From 0a8993bbf19fc368e4f3f04ed5daf73062712203 Mon Sep 17 00:00:00 2001 From: yqrashawn Date: Tue, 20 Dec 2022 22:45:37 +0800 Subject: [PATCH] refactor: reformat all clojure code with zprint (#14589) Co-authored-by: refactor-only --- src/i18n/i18n.cljs | 28 +- src/mocks/js_dependencies.cljs | 504 +++++++------ src/quo/animated.cljs | 146 ++-- src/quo/components/animated/pressable.cljs | 62 +- src/quo/components/animated_header.cljs | 103 +-- src/quo/components/bottom_sheet/style.cljs | 15 +- src/quo/components/bottom_sheet/view.cljs | 235 +++--- src/quo/components/button/view.cljs | 102 +-- src/quo/components/controls/styles.cljs | 28 +- src/quo/components/controls/view.cljs | 96 +-- src/quo/components/header.cljs | 216 +++--- src/quo/components/list/footer.cljs | 17 +- src/quo/components/list/header.cljs | 24 +- src/quo/components/list/index.cljs | 26 +- src/quo/components/list/item.cljs | 338 +++++---- src/quo/components/safe_area.cljs | 10 +- src/quo/components/separator.cljs | 7 +- src/quo/components/text.cljs | 16 +- src/quo/components/text_input.cljs | 277 ++++--- src/quo/components/tooltip.cljs | 64 +- src/quo/core.cljs | 18 +- src/quo/design_system/colors.cljs | 73 +- src/quo/design_system/spacing.cljs | 33 +- src/quo/design_system/typography.cljs | 35 +- src/quo/gesture_handler.cljs | 40 +- src/quo/haptic.cljs | 23 +- src/quo/previews/bottom_sheet.cljs | 97 +-- src/quo/previews/button.cljs | 126 ++-- src/quo/previews/controls.cljs | 77 +- src/quo/previews/header.cljs | 79 +- src/quo/previews/icons.cljs | 8 +- src/quo/previews/lists.cljs | 181 ++--- src/quo/previews/main.cljs | 135 ++-- src/quo/previews/preview.clj | 8 +- src/quo/previews/preview.cljs | 155 ++-- src/quo/previews/text.cljs | 156 ++-- src/quo/previews/text_input.cljs | 138 ++-- src/quo/previews/tooltip.cljs | 50 +- src/quo/react.clj | 6 +- src/quo/react.cljs | 81 +- src/quo/react_native.cljs | 129 ++-- src/quo/theme.cljs | 9 +- .../components/avatars/account_avatar.cljs | 33 +- .../components/avatars/channel_avatar.cljs | 70 +- src/quo2/components/avatars/group_avatar.cljs | 28 +- src/quo2/components/avatars/icon_avatar.cljs | 34 +- src/quo2/components/avatars/user_avatar.cljs | 189 ++--- .../avatars/wallet_user_avatar.cljs | 64 +- src/quo2/components/buttons/button.cljs | 458 ++++++------ .../components/buttons/dynamic_button.cljs | 65 +- src/quo2/components/code/snippet.cljs | 169 +++-- .../community/community_card_view.cljs | 23 +- .../community/community_list_view.cljs | 208 +++--- .../components/community/community_view.cljs | 83 ++- .../components/community/discover_card.cljs | 145 ++-- src/quo2/components/community/style.cljs | 132 ++-- .../components/community/token_gating.cljs | 252 ++++--- .../__tests__/counter_component_spec.cljs | 10 +- src/quo2/components/counter/counter.cljs | 77 +- src/quo2/components/dividers/date.cljs | 29 +- .../components/dividers/divider_label.cljs | 66 +- .../components/dividers/new_messages.cljs | 17 +- .../action_drawers_component_spec.cljs | 38 +- .../components/drawers/action_drawers.cljs | 104 +-- src/quo2/components/dropdowns/dropdown.cljs | 201 ++--- src/quo2/components/header.cljs | 210 +++--- src/quo2/components/icon.cljs | 16 +- src/quo2/components/icons/icons.clj | 15 +- src/quo2/components/icons/icons.cljs | 3 +- src/quo2/components/info/info_message.cljs | 52 +- src/quo2/components/info/information_box.cljs | 125 ++-- src/quo2/components/list_items/channel.cljs | 47 +- src/quo2/components/list_items/menu_item.cljs | 60 +- .../components/list_items/preview_list.cljs | 166 +++-- src/quo2/components/loaders/skeleton.cljs | 109 +-- src/quo2/components/markdown/text.cljs | 17 +- .../components/messages/author/style.cljs | 12 +- src/quo2/components/messages/author/view.cljs | 95 +-- src/quo2/components/messages/gap.cljs | 135 ++-- .../components/messages/system_message.cljs | 210 +++--- .../components/navigation/bottom_nav_tab.cljs | 72 +- .../navigation/floating_shell_button.cljs | 29 +- src/quo2/components/navigation/page_nav.cljs | 220 +++--- .../notifications/activity_log/style.cljs | 3 +- .../notifications/activity_log/view.cljs | 130 ++-- .../notifications/count_down_circle.cljs | 13 +- .../components/notifications/info_count.cljs | 32 +- .../notifications/notification_dot.cljs | 22 +- src/quo2/components/notifications/toast.cljs | 15 +- src/quo2/components/reactions/reaction.cljs | 80 +- .../record_audio/record_audio/style.cljs | 52 +- .../record_audio/record_audio/view.cljs | 672 +++++++++++------ .../__tests__/selectors_component_spec.cljs | 16 +- src/quo2/components/selectors/disclaimer.cljs | 29 +- src/quo2/components/selectors/selectors.cljs | 207 +++--- src/quo2/components/separator.cljs | 7 +- .../components/settings/privacy_option.cljs | 32 +- .../components/tabs/account_selector.cljs | 86 +-- src/quo2/components/tabs/segmented_tab.cljs | 38 +- src/quo2/components/tabs/tab.cljs | 121 +-- src/quo2/components/tabs/tabs.cljs | 198 ++--- src/quo2/components/tags/base_tag.cljs | 41 +- src/quo2/components/tags/context_tags.cljs | 70 +- src/quo2/components/tags/permission_tag.cljs | 178 +++-- src/quo2/components/tags/status_tags.cljs | 112 +-- src/quo2/components/tags/tag.cljs | 141 ++-- src/quo2/components/tags/tags.cljs | 35 +- src/quo2/components/tags/token_tag.cljs | 95 +-- src/quo2/components/wallet/lowest_price.cljs | 27 +- .../components/wallet/network_amount.cljs | 70 +- .../components/wallet/network_breakdown.cljs | 74 +- .../components/wallet/token_overview.cljs | 137 ++-- src/quo2/core.cljs | 98 +-- src/quo2/core_spec.cljs | 6 +- src/quo2/foundations/colors.cljs | 22 +- src/quo2/foundations/typography.cljs | 35 +- src/quo2/theme.cljs | 9 +- src/react_native/background_timer.cljs | 12 +- src/react_native/core.cljs | 46 +- src/react_native/fast_image.cljs | 31 +- src/react_native/flat_list.cljs | 38 +- src/react_native/hole_view.cljs | 4 +- src/react_native/languages.cljs | 6 +- src/react_native/mail.cljs | 3 +- src/react_native/navigation.cljs | 42 +- src/react_native/reanimated.cljs | 96 ++- src/react_native/safe_area.cljs | 7 +- src/react_native/section_list.cljs | 22 +- src/react_native/shake.cljs | 3 +- src/react_native/svg.cljs | 5 +- src/status_im/add_new/core.cljs | 44 +- src/status_im/add_new/db.cljs | 17 +- src/status_im/async_storage/core.cljs | 18 +- src/status_im/async_storage/transit.cljs | 8 +- src/status_im/audio/core.cljs | 139 ++-- src/status_im/backup/core.cljs | 6 +- src/status_im/bootnodes/core.cljs | 75 +- src/status_im/bootnodes/core_test.cljs | 101 +-- src/status_im/bottom_sheet/core.cljs | 10 +- src/status_im/browser/core.cljs | 243 +++--- src/status_im/browser/core_test.cljs | 83 ++- src/status_im/browser/eip3085.cljs | 87 ++- src/status_im/browser/eip3326.cljs | 59 +- src/status_im/browser/permissions.cljs | 72 +- src/status_im/browser/permissions_test.cljs | 47 +- src/status_im/chat/db.cljs | 56 +- src/status_im/chat/db_test.cljs | 50 +- src/status_im/chat/default_chats.cljs | 3 +- src/status_im/chat/models.cljs | 212 +++--- src/status_im/chat/models/images.cljs | 59 +- src/status_im/chat/models/input.cljs | 155 ++-- src/status_im/chat/models/input_test.cljs | 58 +- src/status_im/chat/models/link_preview.cljs | 38 +- src/status_im/chat/models/loading.cljs | 68 +- src/status_im/chat/models/mentions.cljs | 398 +++++----- src/status_im/chat/models/mentions_test.cljs | 142 ++-- src/status_im/chat/models/message.cljs | 108 +-- .../chat/models/message_content.cljs | 7 +- src/status_im/chat/models/message_list.cljs | 73 +- .../chat/models/message_list_test.cljs | 206 +++--- src/status_im/chat/models/message_test.cljs | 122 +-- src/status_im/chat/models/reactions.cljs | 43 +- src/status_im/chat/models/transport.cljs | 4 +- src/status_im/chat/models_test.cljs | 25 +- src/status_im/commands/core.cljs | 28 +- src/status_im/communities/core.cljs | 201 +++-- src/status_im/constants.cljs | 48 +- src/status_im/contact/block.cljs | 55 +- src/status_im/contact/chat.cljs | 8 +- src/status_im/contact/core.cljs | 69 +- src/status_im/contact/db.cljs | 53 +- src/status_im/contact/db_test.cljs | 84 ++- src/status_im/currency/core.cljs | 6 +- src/status_im/currency/core_test.cljs | 10 +- src/status_im/data_store/activities.cljs | 22 +- src/status_im/data_store/activities_test.cljs | 4 +- src/status_im/data_store/chats.cljs | 89 +-- src/status_im/data_store/chats_test.cljs | 68 +- src/status_im/data_store/contacts.cljs | 67 +- src/status_im/data_store/contacts_test.cljs | 12 +- src/status_im/data_store/invitations.cljs | 3 +- src/status_im/data_store/messages.cljs | 139 ++-- src/status_im/data_store/messages_test.cljs | 95 +-- src/status_im/data_store/pin_messages.cljs | 33 +- src/status_im/data_store/reactions.cljs | 19 +- src/status_im/data_store/settings.cljs | 32 +- .../data_store/visibility_status_updates.cljs | 19 +- src/status_im/ens/core.cljs | 118 +-- src/status_im/ethereum/core.cljs | 102 ++- src/status_im/ethereum/core_test.cljs | 9 +- src/status_im/ethereum/eip681.cljs | 47 +- src/status_im/ethereum/eip681_test.cljs | 150 ++-- src/status_im/ethereum/ens.cljs | 20 +- src/status_im/ethereum/json_rpc.cljs | 11 +- src/status_im/ethereum/macros.clj | 26 +- src/status_im/ethereum/mnemonic.cljs | 203 ++++- src/status_im/ethereum/mnemonic_test.cljs | 9 +- src/status_im/ethereum/stateofus.cljs | 22 +- src/status_im/ethereum/subscriptions.cljs | 44 +- src/status_im/ethereum/tokens.cljs | 28 +- src/status_im/ethereum/transactions/core.cljs | 166 +++-- src/status_im/events.cljs | 211 +++--- src/status_im/fleet/core.cljs | 36 +- src/status_im/fleet/core_test.cljs | 30 +- src/status_im/fleet/default_fleet.cljs | 3 +- src/status_im/goog/i18n.cljs | 492 +++++++------ src/status_im/group_chats/core.cljs | 29 +- src/status_im/group_chats/db.cljs | 3 +- src/status_im/http/core.cljs | 6 +- src/status_im/i18n/i18n.cljs | 29 +- src/status_im/i18n/i18n_resources.cljs | 85 ++- src/status_im/i18n/i18n_test.cljs | 108 +-- src/status_im/integration_test.cljs | 95 ++- src/status_im/keycard/backup_key.cljs | 63 +- src/status_im/keycard/card.cljs | 130 ++-- src/status_im/keycard/change_pin.cljs | 111 +-- src/status_im/keycard/common.cljs | 158 ++-- src/status_im/keycard/core.cljs | 215 +++--- src/status_im/keycard/core_test.cljs | 53 +- src/status_im/keycard/delete_key.cljs | 24 +- src/status_im/keycard/export_key.cljs | 60 +- src/status_im/keycard/fx.cljs | 13 +- src/status_im/keycard/login.cljs | 74 +- src/status_im/keycard/mnemonic.cljs | 20 +- src/status_im/keycard/nfc.cljs | 6 +- src/status_im/keycard/onboarding.cljs | 84 ++- src/status_im/keycard/real_keycard.cljs | 222 +++--- src/status_im/keycard/recovery.cljs | 214 +++--- src/status_im/keycard/sign.cljs | 97 +-- src/status_im/keycard/simulated_keycard.cljs | 694 ++++++++++-------- src/status_im/keycard/test_menu.cljs | 12 +- src/status_im/keycard/unpair.cljs | 142 ++-- src/status_im/keycard/wallet.cljs | 21 +- src/status_im/log_level/core.cljs | 5 +- src/status_im/mailserver/constants.cljs | 3 +- src/status_im/mailserver/core.cljs | 195 ++--- src/status_im/mobile_sync_settings/core.cljs | 43 +- .../multiaccounts/biometric/core.cljs | 39 +- src/status_im/multiaccounts/core.cljs | 65 +- src/status_im/multiaccounts/core_test.cljs | 3 +- src/status_im/multiaccounts/create/core.cljs | 156 ++-- src/status_im/multiaccounts/db.cljs | 5 +- .../multiaccounts/key_storage/core.cljs | 106 +-- .../multiaccounts/key_storage/core_test.cljs | 22 +- src/status_im/multiaccounts/login/core.cljs | 285 +++---- .../multiaccounts/login/data_test.cljs | 107 +-- .../multiaccounts/login/flow_test.cljs | 51 +- src/status_im/multiaccounts/logout/core.cljs | 16 +- src/status_im/multiaccounts/model.cljs | 6 +- src/status_im/multiaccounts/recover/core.cljs | 95 ++- .../multiaccounts/recover/core_test.cljs | 78 +- .../multiaccounts/reset_password/core.cljs | 42 +- src/status_im/multiaccounts/update/core.cljs | 72 +- .../multiaccounts/update/core_test.cljs | 13 +- src/status_im/native_module/core.cljs | 166 +++-- src/status_im/native_module/core_test.cljs | 5 +- src/status_im/navigation/core.cljs | 429 ++++++----- src/status_im/network/core.cljs | 100 +-- src/status_im/network/core_test.cljs | 146 ++-- src/status_im/network/net_info.cljs | 19 +- src/status_im/node/core.cljs | 100 +-- src/status_im/notifications/android.cljs | 26 +- src/status_im/notifications/core.cljs | 129 ++-- src/status_im/notifications/local.cljs | 86 ++- src/status_im/pairing/core.cljs | 118 +-- src/status_im/popover/core.cljs | 4 +- src/status_im/profile/core.cljs | 24 +- src/status_im/profile/db.cljs | 18 +- src/status_im/qr_scanner/core.cljs | 37 +- src/status_im/react_native/resources.cljs | 21 +- src/status_im/router/core.cljs | 134 ++-- src/status_im/router/core_test.cljs | 113 +-- src/status_im/search/core_test.cljs | 19 +- src/status_im/signals/core.cljs | 115 +-- src/status_im/signing/core.cljs | 347 +++++---- src/status_im/signing/core_test.cljs | 58 +- src/status_im/signing/eip1559.cljs | 9 +- src/status_im/signing/gas.cljs | 196 +++-- src/status_im/signing/keycard.cljs | 25 +- src/status_im/stickers/core.cljs | 20 +- src/status_im/test_helpers.clj | 4 +- src/status_im/test_helpers.cljs | 10 +- src/status_im/test_runner.cljs | 52 +- src/status_im/theme/core.cljs | 3 +- src/status_im/transport/core.cljs | 24 +- src/status_im/transport/message/core.cljs | 107 +-- src/status_im/transport/message/protocol.cljs | 61 +- src/status_im/transport/shh.cljs | 6 +- src/status_im/transport/utils.cljs | 9 +- src/status_im/ui/components/accordion.cljs | 47 +- src/status_im/ui/components/action_sheet.cljs | 18 +- src/status_im/ui/components/animation.cljs | 67 +- src/status_im/ui/components/badge.cljs | 34 +- .../ui/components/bottom_panel/views.cljs | 96 ++- .../ui/components/chat_icon/screen.cljs | 87 ++- .../ui/components/chat_icon/styles.cljs | 57 +- .../ui/components/checkbox/styles.cljs | 13 +- .../ui/components/checkbox/view.cljs | 11 +- .../ui/components/common/common.cljs | 49 +- .../ui/components/common/styles.cljs | 18 +- .../ui/components/connectivity/view.cljs | 173 +++-- .../ui/components/copyable_text.cljs | 25 +- src/status_im/ui/components/dialog.cljs | 13 +- .../emoji_thumbnail/color_picker.cljs | 44 +- .../components/emoji_thumbnail/preview.cljs | 15 +- .../ui/components/emoji_thumbnail/styles.cljs | 85 ++- .../ui/components/emoji_thumbnail/utils.cljs | 10 +- src/status_im/ui/components/fast_image.cljs | 31 +- src/status_im/ui/components/icons/icons.clj | 6 +- src/status_im/ui/components/icons/icons.cljs | 53 +- .../ui/components/invite/events.cljs | 8 +- src/status_im/ui/components/invite/views.cljs | 18 +- .../keyboard_avoid_presentation.cljs | 19 +- src/status_im/ui/components/list/styles.cljs | 12 +- src/status_im/ui/components/list/views.cljs | 50 +- .../ui/components/list_selection.cljs | 19 +- src/status_im/ui/components/permissions.cljs | 7 +- src/status_im/ui/components/plus_button.cljs | 26 +- .../ui/components/profile_header/view.cljs | 101 +-- .../ui/components/qr_code_viewer/styles.cljs | 21 +- .../ui/components/qr_code_viewer/views.cljs | 21 +- src/status_im/ui/components/react.cljs | 156 ++-- .../ui/components/search_input/view.cljs | 161 ++-- src/status_im/ui/components/slider.cljs | 4 +- src/status_im/ui/components/tabbar/core.cljs | 3 +- src/status_im/ui/components/tabs.cljs | 41 +- .../ui/components/toastable_highlight.cljs | 15 +- src/status_im/ui/components/toolbar.cljs | 26 +- .../ui/components/tooltip/animations.cljs | 19 +- .../ui/components/tooltip/styles.cljs | 31 +- .../ui/components/tooltip/views.cljs | 37 +- src/status_im/ui/components/topbar.cljs | 44 +- src/status_im/ui/components/typography.cljs | 15 +- .../ui/components/unviewed_indicator.cljs | 10 +- src/status_im/ui/components/webview.cljs | 12 +- src/status_im/ui/screens/about_app/views.cljs | 41 +- .../ui/screens/add_new/new_chat/views.cljs | 310 ++++---- .../screens/add_new/new_public_chat/view.cljs | 96 ++- .../ui/screens/advanced_settings/views.cljs | 48 +- .../ui/screens/appearance/views.cljs | 28 +- .../ui/screens/backup_settings/view.cljs | 46 +- src/status_im/ui/screens/biometric/views.cljs | 80 +- .../edit_bootnode/styles.cljs | 13 +- .../edit_bootnode/views.cljs | 39 +- .../ui/screens/bootnodes_settings/styles.cljs | 8 +- .../ui/screens/bootnodes_settings/views.cljs | 33 +- .../ui/screens/bottom_sheets/views.cljs | 3 +- .../ui/screens/browser/accounts.cljs | 27 +- .../ui/screens/browser/bookmarks/views.cljs | 31 +- .../ui/screens/browser/eip3085/sheet.cljs | 35 +- .../ui/screens/browser/eip3326/sheet.cljs | 41 +- .../ui/screens/browser/empty_tab/styles.cljs | 9 +- .../ui/screens/browser/empty_tab/views.cljs | 192 ++--- .../ui/screens/browser/options/views.cljs | 72 +- .../ui/screens/browser/permissions/views.cljs | 84 ++- .../screens/browser/site_blocked/styles.cljs | 10 +- .../screens/browser/site_blocked/views.cljs | 34 +- src/status_im/ui/screens/browser/styles.cljs | 12 +- .../ui/screens/browser/tabs/views.cljs | 97 +-- src/status_im/ui/screens/browser/views.cljs | 214 +++--- src/status_im/ui/screens/bug_report.cljs | 14 +- .../ui/screens/chat/audio_message/styles.cljs | 22 +- .../ui/screens/chat/audio_message/views.cljs | 250 ++++--- .../ui/screens/chat/components/accessory.cljs | 164 +++-- .../chat/components/contact_request.cljs | 74 +- .../ui/screens/chat/components/edit.cljs | 52 +- .../ui/screens/chat/components/hooks.cljs | 27 +- .../ui/screens/chat/components/input.cljs | 333 +++++---- .../ui/screens/chat/components/reply.cljs | 98 ++- .../ui/screens/chat/components/style.cljs | 71 +- .../ui/screens/chat/extensions/views.cljs | 56 +- src/status_im/ui/screens/chat/group.cljs | 137 ++-- .../ui/screens/chat/image/preview/views.cljs | 137 ++-- .../ui/screens/chat/image/views.cljs | 120 +-- .../ui/screens/chat/message/audio.cljs | 196 +++-- .../ui/screens/chat/message/audio_old.cljs | 200 ++--- .../ui/screens/chat/message/command.cljs | 254 ++++--- .../ui/screens/chat/message/datemark.cljs | 18 +- .../ui/screens/chat/message/datemark_old.cljs | 7 +- .../ui/screens/chat/message/gap.cljs | 18 +- .../ui/screens/chat/message/link_preview.cljs | 170 +++-- .../ui/screens/chat/message/message.cljs | 652 +++++++++------- .../screens/chat/message/pinned_message.cljs | 120 +-- .../ui/screens/chat/message/reactions.cljs | 103 +-- .../screens/chat/message/reactions_old.cljs | 103 +-- .../chat/message/reactions_picker.cljs | 116 +-- .../screens/chat/message/reactions_row.cljs | 34 +- .../chat/message/reactions_row_old.cljs | 31 +- .../ui/screens/chat/message/styles.cljs | 47 +- src/status_im/ui/screens/chat/photos.cljs | 46 +- .../ui/screens/chat/pinned_messages.cljs | 118 +-- src/status_im/ui/screens/chat/sheets.cljs | 67 +- src/status_im/ui/screens/chat/state.cljs | 9 +- .../ui/screens/chat/stickers/styles.cljs | 6 +- .../ui/screens/chat/stickers/views.cljs | 161 ++-- .../ui/screens/chat/styles/input/gap.cljs | 6 +- .../ui/screens/chat/styles/main.cljs | 36 +- .../ui/screens/chat/styles/message/audio.cljs | 32 +- .../chat/styles/message/audio_old.cljs | 36 +- .../screens/chat/styles/message/datemark.cljs | 24 +- .../chat/styles/message/datemark_old.cljs | 3 +- .../screens/chat/styles/message/message.cljs | 328 +++++---- .../chat/styles/message/message_old.cljs | 333 +++++---- .../ui/screens/chat/styles/photos.cljs | 6 +- .../ui/screens/chat/toolbar_content.cljs | 41 +- src/status_im/ui/screens/chat/utils.cljs | 85 ++- src/status_im/ui/screens/chat/views.cljs | 442 ++++++----- .../screens/communities/channel_details.cljs | 100 +-- .../ui/screens/communities/community.cljs | 189 +++-- .../community_emoji_thumbnail_picker.cljs | 48 +- .../ui/screens/communities/create.cljs | 254 ++++--- .../screens/communities/create_category.cljs | 55 +- .../screens/communities/create_channel.cljs | 57 +- .../ui/screens/communities/edit.cljs | 18 +- .../ui/screens/communities/edit_channel.cljs | 23 +- .../ui/screens/communities/icon.cljs | 31 +- .../ui/screens/communities/import.cljs | 30 +- .../ui/screens/communities/invite.cljs | 89 ++- .../ui/screens/communities/members.cljs | 158 ++-- .../ui/screens/communities/membership.cljs | 52 +- .../ui/screens/communities/profile.cljs | 167 +++-- .../communities/reorder_categories.cljs | 160 ++-- .../screens/communities/requests_to_join.cljs | 88 ++- .../screens/communities/select_category.cljs | 72 +- .../ui/screens/communities/views.cljs | 211 +++--- .../ui/screens/contacts_list/views.cljs | 37 +- .../ui/screens/currency_settings/views.cljs | 34 +- .../ui/screens/dapps_permissions/views.cljs | 46 +- .../default_sync_period_settings/view.cljs | 27 +- src/status_im/ui/screens/ens/views.cljs | 482 +++++++----- .../ui/screens/fleet_settings/styles.cljs | 8 +- .../ui/screens/fleet_settings/views.cljs | 28 +- src/status_im/ui/screens/glossary/view.cljs | 72 +- src/status_im/ui/screens/group/styles.cljs | 16 +- src/status_im/ui/screens/group/views.cljs | 234 +++--- .../ui/screens/help_center/views.cljs | 11 +- .../ui/screens/home/sheet/views.cljs | 47 +- src/status_im/ui/screens/home/styles.cljs | 19 +- src/status_im/ui/screens/home/views.cljs | 359 +++++---- .../ui/screens/home/views/inner_item.cljs | 210 +++--- .../keycard/authentication_method/views.cljs | 45 +- .../keycard/components/description.cljs | 19 +- .../keycard/components/keycard_animation.cljs | 160 ++-- .../ui/screens/keycard/components/style.cljs | 26 +- .../screens/keycard/components/turn_nfc.cljs | 28 +- .../ui/screens/keycard/frozen_card/view.cljs | 53 +- .../screens/keycard/keycard_interaction.cljs | 73 +- .../ui/screens/keycard/onboarding/views.cljs | 490 +++++++------ .../ui/screens/keycard/pairing/views.cljs | 114 +-- .../ui/screens/keycard/pin/styles.cljs | 76 +- .../ui/screens/keycard/pin/views.cljs | 235 +++--- .../ui/screens/keycard/recovery/views.cljs | 346 +++++---- .../ui/screens/keycard/settings/views.cljs | 175 +++-- src/status_im/ui/screens/keycard/styles.cljs | 4 +- src/status_im/ui/screens/keycard/views.cljs | 496 +++++++------ .../screens/link_previews_settings/views.cljs | 50 +- .../ui/screens/log_level_settings/styles.cljs | 8 +- .../ui/screens/log_level_settings/views.cljs | 37 +- .../mobile_network_settings/sheets.cljs | 76 +- .../sheets_styles.cljs | 4 +- .../mobile_network_settings/style.cljs | 15 +- .../screens/mobile_network_settings/view.cljs | 33 +- .../multiaccounts/key_storage/views.cljs | 457 +++++++----- .../screens/multiaccounts/login/styles.cljs | 22 +- .../ui/screens/multiaccounts/login/views.cljs | 91 ++- .../screens/multiaccounts/recover/views.cljs | 135 ++-- .../ui/screens/multiaccounts/sheets.cljs | 20 +- .../ui/screens/multiaccounts/styles.cljs | 4 +- .../ui/screens/multiaccounts/views.cljs | 102 +-- .../screens/network/edit_network/views.cljs | 39 +- .../network/network_details/views.cljs | 33 +- src/status_im/ui/screens/network/styles.cljs | 18 +- src/status_im/ui/screens/network/views.cljs | 76 +- .../ui/screens/network_info/views.cljs | 37 +- .../screens/notifications_settings/views.cljs | 104 +-- .../edit_mailserver/styles.cljs | 16 +- .../edit_mailserver/views.cljs | 65 +- .../offline_messaging_settings/styles.cljs | 22 +- .../offline_messaging_settings/views.cljs | 54 +- .../ui/screens/onboarding/intro/styles.cljs | 20 +- .../ui/screens/onboarding/intro/views.cljs | 217 +++--- .../ui/screens/onboarding/keys/views.cljs | 99 +-- .../onboarding/notifications/views.cljs | 50 +- .../ui/screens/onboarding/password/views.cljs | 155 ++-- .../ui/screens/onboarding/phrase/view.cljs | 145 ++-- .../ui/screens/onboarding/storage/views.cljs | 49 +- .../ui/screens/onboarding/styles.cljs | 16 +- .../ui/screens/onboarding/views.cljs | 46 +- .../ui/screens/onboarding/welcome/views.cljs | 46 +- src/status_im/ui/screens/pairing/styles.cljs | 28 +- src/status_im/ui/screens/pairing/views.cljs | 134 ++-- src/status_im/ui/screens/peers_stats.cljs | 8 +- src/status_im/ui/screens/popover/views.cljs | 115 +-- .../delete_profile.cljs | 104 +-- .../privacy_and_security_settings/events.cljs | 15 +- .../messages_from_contacts_only.cljs | 27 +- .../privacy_and_security_settings/views.cljs | 202 ++--- .../ui/screens/profile/components/sheets.cljs | 35 +- .../ui/screens/profile/components/styles.cljs | 2 +- .../ui/screens/profile/components/views.cljs | 35 +- .../ui/screens/profile/contact/styles.cljs | 3 +- .../ui/screens/profile/contact/views.cljs | 235 +++--- .../ui/screens/profile/group_chat/views.cljs | 131 ++-- .../ui/screens/profile/seed/styles.cljs | 22 +- .../ui/screens/profile/seed/views.cljs | 132 ++-- .../ui/screens/profile/user/edit_picture.cljs | 55 +- .../ui/screens/profile/user/views.cljs | 68 +- .../profile/visibility_status/styles.cljs | 42 +- .../profile/visibility_status/utils.cljs | 25 +- .../profile/visibility_status/views.cljs | 193 ++--- src/status_im/ui/screens/progress/views.cljs | 16 +- .../ui/screens/qr_scanner/views.cljs | 72 +- .../ui/screens/reset_password/views.cljs | 130 ++-- src/status_im/ui/screens/rpc_usage_info.cljs | 21 +- src/status_im/ui/screens/screens.cljs | 311 ++++---- src/status_im/ui/screens/signing/sheets.cljs | 225 +++--- src/status_im/ui/screens/signing/styles.cljs | 12 +- src/status_im/ui/screens/signing/views.cljs | 392 ++++++---- .../ui/screens/status/new/styles.cljs | 3 +- .../ui/screens/status/new/views.cljs | 82 ++- src/status_im/ui/screens/status/styles.cljs | 3 +- src/status_im/ui/screens/status/views.cljs | 233 +++--- src/status_im/ui/screens/stickers/styles.cljs | 8 +- src/status_im/ui/screens/stickers/views.cljs | 78 +- .../ui/screens/sync_settings/views.cljs | 126 ++-- .../ui/screens/terms_of_service/views.cljs | 91 ++- .../wakuv2_settings/edit_node/styles.cljs | 13 +- .../wakuv2_settings/edit_node/views.cljs | 42 +- .../ui/screens/wakuv2_settings/styles.cljs | 8 +- .../ui/screens/wakuv2_settings/views.cljs | 51 +- .../ui/screens/wallet/account/styles.cljs | 24 +- .../ui/screens/wallet/account/views.cljs | 235 +++--- .../wallet/account_settings/views.cljs | 128 ++-- .../ui/screens/wallet/accounts/common.cljs | 48 +- .../ui/screens/wallet/accounts/sheets.cljs | 21 +- .../ui/screens/wallet/accounts/styles.cljs | 97 +-- .../ui/screens/wallet/accounts/views.cljs | 326 ++++---- .../screens/wallet/accounts_manage/views.cljs | 16 +- .../ui/screens/wallet/add_new/views.cljs | 80 +- .../ui/screens/wallet/buy_crypto/sheets.cljs | 36 +- .../ui/screens/wallet/buy_crypto/views.cljs | 182 +++-- .../ui/screens/wallet/collectibles/views.cljs | 342 +++++---- .../ui/screens/wallet/components/styles.cljs | 6 +- .../ui/screens/wallet/components/views.cljs | 20 +- .../screens/wallet/custom_tokens/views.cljs | 78 +- .../wallet/manage_connections/styles.cljs | 21 +- .../wallet/manage_connections/views.cljs | 51 +- .../ui/screens/wallet/recipient/views.cljs | 205 +++--- .../ui/screens/wallet/request/views.cljs | 27 +- .../ui/screens/wallet/send/sheets.cljs | 45 +- .../ui/screens/wallet/send/styles.cljs | 12 +- .../ui/screens/wallet/send/views.cljs | 162 ++-- .../ui/screens/wallet/settings/views.cljs | 40 +- .../screens/wallet/signing_phrase/views.cljs | 55 +- .../ui/screens/wallet/swap/views.cljs | 579 ++++++++------- .../screens/wallet/transactions/styles.cljs | 17 +- .../ui/screens/wallet/transactions/views.cljs | 182 ++--- .../session_proposal/styles.cljs | 178 ++--- .../session_proposal/views.cljs | 311 ++++---- .../screens/chat/components/edit/style.cljs | 4 +- .../screens/chat/components/edit/view.cljs | 44 +- .../chat/components/reaction_drawer.cljs | 52 +- .../chat/components/received_cr_item.cljs | 143 ++-- .../ui2/screens/chat/components/reply.cljs | 66 +- .../ui2/screens/chat/composer/edit/view.cljs | 10 +- .../screens/chat/composer/images/view.cljs | 34 +- .../ui2/screens/chat/composer/input.cljs | 279 ++++--- .../ui2/screens/chat/composer/mentions.cljs | 70 +- .../ui2/screens/chat/composer/reply.cljs | 15 +- .../ui2/screens/chat/composer/style.cljs | 36 +- .../ui2/screens/chat/composer/view.cljs | 273 ++++--- .../ui2/screens/chat/group_details/style.cljs | 12 +- .../ui2/screens/chat/group_details/view.cljs | 237 +++--- .../ui2/screens/chat/messages/message.cljs | 625 +++++++++------- .../screens/chat/photo_selector/style.cljs | 24 +- .../ui2/screens/chat/photo_selector/view.cljs | 88 ++- .../screens/chat/pin_limit_popover/style.cljs | 6 +- .../screens/chat/pin_limit_popover/view.cljs | 72 +- .../ui2/screens/chat/pinned_banner/view.cljs | 36 +- .../ui2/screens/common/contact_list/view.cljs | 16 +- src/status_im/utils/async.cljs | 68 +- src/status_im/utils/async_test.cljs | 38 +- src/status_im/utils/build.clj | 16 +- src/status_im/utils/build.cljs | 3 +- src/status_im/utils/clocks.cljs | 18 +- src/status_im/utils/config.cljs | 83 ++- src/status_im/utils/datetime.cljs | 5 +- src/status_im/utils/datetime_test.cljs | 58 +- src/status_im/utils/db.cljs | 3 +- src/status_im/utils/dimensions.cljs | 3 +- src/status_im/utils/fs.cljs | 27 +- src/status_im/utils/fx.clj | 50 +- src/status_im/utils/fx.cljs | 14 +- src/status_im/utils/fx_test.cljs | 16 +- src/status_im/utils/gfycat/core.cljs | 10 +- src/status_im/utils/handlers.cljs | 6 +- src/status_im/utils/hex.cljs | 6 +- src/status_im/utils/http.cljs | 78 +- src/status_im/utils/http_test.cljs | 39 +- src/status_im/utils/identicon.cljs | 7 +- src/status_im/utils/image.cljs | 3 +- src/status_im/utils/image_processing.cljs | 3 +- src/status_im/utils/image_server.cljs | 56 +- src/status_im/utils/js_resources.cljs | 7 +- src/status_im/utils/keychain/core.cljs | 45 +- src/status_im/utils/label.cljs | 3 +- src/status_im/utils/logging/core.cljs | 90 +-- src/status_im/utils/mobile_sync.cljs | 8 +- src/status_im/utils/money.cljs | 93 ++- src/status_im/utils/name.cljs | 6 +- src/status_im/utils/pairing.cljs | 6 +- src/status_im/utils/platform.cljs | 9 +- src/status_im/utils/prices.cljs | 24 +- src/status_im/utils/priority_map.cljs | 290 ++++---- src/status_im/utils/random.cljs | 15 +- src/status_im/utils/random_test.cljs | 30 +- src/status_im/utils/react_native.cljs | 3 +- src/status_im/utils/share.cljs | 3 +- src/status_im/utils/signing_phrase/core.cljs | 13 +- .../utils/signing_phrase/core_test.cljs | 4 +- src/status_im/utils/slurp.clj | 6 +- src/status_im/utils/snoopy.cljs | 66 +- src/status_im/utils/styles.clj | 8 +- src/status_im/utils/test.cljs | 176 ++--- src/status_im/utils/transducers.cljs | 14 +- src/status_im/utils/transducers_test.cljs | 47 +- src/status_im/utils/types.cljs | 27 +- src/status_im/utils/universal_links/core.cljs | 88 ++- .../utils/universal_links/core_test.cljs | 4 +- .../utils/universal_links/utils.cljs | 27 +- src/status_im/utils/utils.cljs | 53 +- src/status_im/utils/utils_test.cljs | 13 +- src/status_im/utils/views.clj | 65 +- src/status_im/utils/wallet_connect.cljs | 18 +- .../utils/wallet_connect_legacy.cljs | 10 +- .../visibility_status_popover/core.cljs | 4 +- .../visibility_status_updates/core.cljs | 90 ++- src/status_im/waku/core.cljs | 75 +- src/status_im/wallet/accounts/core.cljs | 117 +-- .../wallet/choose_recipient/core.cljs | 48 +- src/status_im/wallet/core.cljs | 482 ++++++------ src/status_im/wallet/custom_tokens/core.cljs | 93 +-- src/status_im/wallet/db.cljs | 8 +- src/status_im/wallet/db_test.cljs | 22 +- src/status_im/wallet/prices.cljs | 23 +- src/status_im/wallet/recipient/core.cljs | 51 +- src/status_im/wallet/swap/core.cljs | 6 +- src/status_im/wallet/utils.cljs | 15 +- src/status_im/wallet_connect/core.cljs | 197 +++-- src/status_im/wallet_connect_legacy/core.cljs | 325 ++++---- src/status_im2/common/alert/events.cljs | 18 +- .../common/confirmation_drawer/style.cljs | 3 +- .../common/confirmation_drawer/view.cljs | 75 +- src/status_im2/common/constants.cljs | 35 +- .../common/contact_list_item/view.cljs | 88 ++- src/status_im2/common/home/actions/view.cljs | 274 ++++--- src/status_im2/common/home/view.cljs | 112 +-- src/status_im2/common/plus_button/view.cljs | 14 +- src/status_im2/common/scroll_page/style.cljs | 18 +- src/status_im2/common/scroll_page/view.cljs | 61 +- src/status_im2/common/theme/core.cljs | 6 +- src/status_im2/common/toasts/events.cljs | 5 +- src/status_im2/common/toasts/view.cljs | 67 +- .../contexts/activity_center/events.cljs | 89 ++- .../contexts/activity_center/events_test.cljs | 71 +- .../notification/common/view.cljs | 3 +- .../notification/contact_request/view.cljs | 69 +- .../contact_verification/view.cljs | 103 +-- .../notification/mentions/view.cljs | 31 +- .../contexts/activity_center/view.cljs | 125 ++-- .../chat/home/chat_list_item/style.cljs | 9 +- .../chat/home/chat_list_item/view.cljs | 108 +-- .../chat/home/contact_request/style.cljs | 6 +- .../chat/home/contact_request/view.cljs | 45 +- src/status_im2/contexts/chat/home/view.cljs | 79 +- .../contexts/chat/messages/list/view.cljs | 176 +++-- .../message/delete_message/events.cljs | 13 +- .../message/delete_message_for_me/events.cljs | 13 +- .../chat/messages/pin/banner/view.cljs | 5 +- .../contexts/chat/messages/pin/events.cljs | 58 +- .../contexts/chat/messages/pin/list/view.cljs | 75 +- .../contexts/chat/messages/view.cljs | 43 +- .../contexts/communities/discover/view.cljs | 139 ++-- .../communities/home/actions/view.cljs | 162 ++-- .../contexts/communities/home/view.cljs | 79 +- .../contexts/communities/overview/style.cljs | 14 +- .../contexts/communities/overview/view.cljs | 449 +++++------ .../communities/requests/actions/style.cljs | 8 +- .../communities/requests/actions/view.cljs | 178 +++-- .../quo_preview/avatars/account_avatar.cljs | 78 +- .../quo_preview/avatars/channel_avatar.cljs | 74 +- .../quo_preview/avatars/group_avatar.cljs | 76 +- .../quo_preview/avatars/icon_avatar.cljs | 90 +-- .../quo_preview/avatars/user_avatar.cljs | 118 +-- .../avatars/wallet_user_avatar.cljs | 87 +-- .../contexts/quo_preview/buttons/button.cljs | 155 ++-- .../quo_preview/buttons/dynamic_button.cljs | 76 +- .../contexts/quo_preview/code/snippet.cljs | 82 ++- .../community/community_card_view.cljs | 82 ++- .../community/community_list_view.cljs | 106 +-- .../community_membership_list_view.cljs | 108 +-- .../contexts/quo_preview/community/data.cljs | 21 +- .../quo_preview/community/discover_card.cljs | 56 +- .../quo_preview/community/token_gating.cljs | 273 +++---- .../contexts/quo_preview/counter/counter.cljs | 62 +- .../contexts/quo_preview/dividers/date.cljs | 41 +- .../quo_preview/dividers/divider_label.cljs | 81 +- .../quo_preview/dividers/new_messages.cljs | 99 +-- .../quo_preview/drawers/action_drawers.cljs | 116 +-- .../quo_preview/dropdowns/dropdown.cljs | 118 +-- .../quo_preview/info/info_message.cljs | 72 +- .../quo_preview/info/information_box.cljs | 76 +- .../quo_preview/list_items/channel.cljs | 131 ++-- .../quo_preview/list_items/preview_lists.cljs | 80 +- src/status_im2/contexts/quo_preview/main.cljs | 474 ++++++------ .../contexts/quo_preview/markdown/text.cljs | 77 +- .../contexts/quo_preview/messages/author.cljs | 82 ++- .../contexts/quo_preview/messages/gap.cljs | 49 +- .../quo_preview/messages/system_message.cljs | 97 +-- .../navigation/bottom_nav_tab.cljs | 99 +-- .../navigation/floating_shell_button.cljs | 77 +- .../quo_preview/navigation/page_nav.cljs | 155 ++-- .../quo_preview/navigation/top_nav.cljs | 78 +- .../notifications/activity_logs.cljs | 214 +++--- .../quo_preview/notifications/toast.cljs | 11 +- .../messages_skeleton.cljs | 16 +- .../contexts/quo_preview/preview.clj | 5 +- .../contexts/quo_preview/preview.cljs | 172 +++-- .../contexts/quo_preview/reactions/react.cljs | 72 +- .../record_audio/record_audio.cljs | 28 +- .../quo_preview/selectors/disclaimer.cljs | 42 +- .../quo_preview/selectors/selectors.cljs | 110 +-- .../quo_preview/settings/privacy_option.cljs | 87 ++- .../quo_preview/switcher/switcher_cards.cljs | 202 ++--- .../quo_preview/tabs/account_selector.cljs | 106 +-- .../quo_preview/tabs/segmented_tab.cljs | 71 +- .../contexts/quo_preview/tabs/tabs.cljs | 61 +- .../quo_preview/tags/context_tags.cljs | 125 ++-- .../quo_preview/tags/permission_tag.cljs | 343 +++++---- .../quo_preview/tags/status_tags.cljs | 68 +- .../contexts/quo_preview/tags/tags.cljs | 168 +++-- .../contexts/quo_preview/tags/token_tag.cljs | 118 +-- .../quo_preview/wallet/lowest_price.cljs | 80 +- .../quo_preview/wallet/network_amount.cljs | 49 +- .../quo_preview/wallet/network_breakdown.cljs | 105 +-- .../quo_preview/wallet/token_overview.cljs | 105 +-- src/status_im2/contexts/shell/animation.cljs | 171 +++-- .../contexts/shell/bottom_tabs.cljs | 16 +- .../contexts/shell/cards/style.cljs | 41 +- src/status_im2/contexts/shell/cards/view.cljs | 173 +++-- src/status_im2/contexts/shell/constants.cljs | 18 +- src/status_im2/contexts/shell/events.cljs | 23 +- src/status_im2/contexts/shell/home_stack.cljs | 35 +- src/status_im2/contexts/shell/style.cljs | 15 +- src/status_im2/contexts/shell/view.cljs | 95 +-- src/status_im2/contexts/syncing/events.cljs | 27 +- .../syncing/sheets/enter_password/view.cljs | 63 +- .../sheets/sync_device_notice/styles.cljs | 18 +- .../sheets/sync_device_notice/view.cljs | 83 ++- src/status_im2/contexts/syncing/styles.cljs | 14 +- src/status_im2/contexts/syncing/view.cljs | 68 +- src/status_im2/navigation/core.cljs | 195 ++--- src/status_im2/navigation/events.cljs | 22 +- src/status_im2/navigation/roots.cljs | 39 +- src/status_im2/navigation/screens.cljs | 16 +- src/status_im2/navigation/view.cljs | 61 +- src/status_im2/setup/config.cljs | 84 ++- src/status_im2/setup/core.cljs | 38 +- src/status_im2/setup/db.cljs | 81 +- src/status_im2/setup/dev.cljs | 32 +- src/status_im2/setup/events.cljs | 48 +- src/status_im2/setup/global_error.cljs | 24 +- src/status_im2/setup/hot_reload.cljs | 43 +- src/status_im2/setup/i18n_resources.cljs | 79 +- src/status_im2/setup/i18n_test.cljs | 104 +-- src/status_im2/setup/log.cljs | 12 +- src/status_im2/subs/chat/chats.cljs | 76 +- src/status_im2/subs/chat/messages.cljs | 20 +- src/status_im2/subs/communities.cljs | 27 +- src/status_im2/subs/communities_test.cljs | 96 +-- src/status_im2/subs/contact.cljs | 79 +- src/status_im2/subs/ens.cljs | 30 +- src/status_im2/subs/general.cljs | 42 +- src/status_im2/subs/home.cljs | 14 +- src/status_im2/subs/keycard.cljs | 22 +- src/status_im2/subs/mailservers.cljs | 4 +- src/status_im2/subs/multiaccount.cljs | 61 +- src/status_im2/subs/networks.cljs | 15 +- src/status_im2/subs/onboarding.cljs | 13 +- src/status_im2/subs/root.cljs | 8 +- src/status_im2/subs/search.cljs | 33 +- src/status_im2/subs/shell.cljs | 24 +- src/status_im2/subs/subs_test.cljs | 34 +- src/status_im2/subs/toasts.cljs | 3 +- src/status_im2/subs/wallet/signing.cljs | 90 ++- src/status_im2/subs/wallet/transactions.cljs | 95 +-- src/status_im2/subs/wallet/wallet.cljs | 39 +- src/status_im2/subs/wallet/wallet_test.cljs | 52 +- src/utils/address.cljs | 7 +- src/utils/debounce.cljs | 6 +- src/utils/re_frame.clj | 50 +- src/utils/re_frame.cljs | 19 +- src/utils/security/core.cljs | 26 +- src/utils/security/security_html.cljs | 247 ++++--- 804 files changed, 37007 insertions(+), 28310 deletions(-) diff --git a/src/i18n/i18n.cljs b/src/i18n/i18n.cljs index 1ca5ef181e..af608c6e9a 100644 --- a/src/i18n/i18n.cljs +++ b/src/i18n/i18n.cljs @@ -1,22 +1,25 @@ (ns i18n.i18n - (:require - ["i18n-js" :as i18n] - [clojure.string :as string] - [status-im.goog.i18n :as goog.i18n])) + (:require ["i18n-js" :as i18n] + [clojure.string :as string] + [status-im.goog.i18n :as goog.i18n])) -(defn init [default-device-language translations-by-locale] +(defn init + [default-device-language translations-by-locale] (set! (.-fallbacks i18n) true) (set! (.-defaultSeparator i18n) "/") (set! (.-locale i18n) default-device-language) (set! (.-translations i18n) translations-by-locale)) -(defn get-translations [] +(defn get-translations + [] (.-translations i18n)) -(defn set-language [lang] +(defn set-language + [lang] (set! (.-locale i18n) lang)) -;;:zh, :zh-hans-xx, :zh-hant-xx have been added until this bug will be fixed https://github.com/fnando/i18n-js/issues/460 +;;:zh, :zh-hans-xx, :zh-hant-xx have been added until this bug will be fixed +;;https://github.com/fnando/i18n-js/issues/460 (def delimeters "This function is a hack: mobile Safari doesn't support toLocaleString(), so we need to pass @@ -29,7 +32,8 @@ {:delimiter "" :separator (subs n 4 5)}))) -(defn label-number [number] +(defn label-number + [number] (when number (let [{:keys [delimiter separator]} delimeters] (.toNumber i18n @@ -41,7 +45,8 @@ (def default-option-value "") -(defn label-options [options] +(defn label-options + [options] ;; i18n ignores nil value, leading to misleading messages (into {} (for [[k v] options] [k (or v default-option-value)]))) @@ -55,7 +60,8 @@ (def label (memoize label-fn)) -(defn label-pluralize [count path & options] +(defn label-pluralize + [count path & options] (if (exists? (.t i18n)) (.p i18n count (name path) (clj->js options)) (name path))) diff --git a/src/mocks/js_dependencies.cljs b/src/mocks/js_dependencies.cljs index 9c58117db5..c3ea317c4b 100644 --- a/src/mocks/js_dependencies.cljs +++ b/src/mocks/js_dependencies.cljs @@ -4,11 +4,13 @@ (:require [status-im.utils.test :as utils.test]) (:require [status-im.chat.default-chats :refer (default-chats)])) -;; to generate a js Proxy at js/__STATUS_MOBILE_JS_IDENTITY_PROXY__ that accept any (.xxx) call and return itself +;; to generate a js Proxy at js/__STATUS_MOBILE_JS_IDENTITY_PROXY__ that accept any (.xxx) call and +;; return itself ;; For the convenience to mock eg. ;; (-> reanimated/slide-out-up-animation .springify (.damping 20) (.stiffness 300)) ;; (-> reanimated/slide-out-up-animation (.damping 20) .springify (.stiffness 300)) -(js/eval " +(js/eval + " var globalThis if (typeof window === \"undefined\") { globalThis = global @@ -18,24 +20,24 @@ if (typeof window === \"undefined\") { globalThis.__STATUS_MOBILE_JS_IDENTITY_PROXY__ = new Proxy({}, {get() { return () => globalThis.__STATUS_MOBILE_JS_IDENTITY_PROXY__}}) ") -(def action-button #js {:default #js {:Item #js {}}}) -(def config #js {:default #js {}}) -(def camera #js {:RNCamera #js {:Constants #js {}}}) -(def dialogs #js {}) -(def dismiss-keyboard #js {}) -(def emoji-picker #js {:default #js {}}) -(def fs #js {}) -(def i18n #js {:locale "en"}) -(def image-crop-picker #js {}) -(def image-resizer #js {}) -(def qr-code #js {}) -(def svg #js {}) +(def action-button #js {:default #js {:Item #js {}}}) +(def config #js {:default #js {}}) +(def camera #js {:RNCamera #js {:Constants #js {}}}) +(def dialogs #js {}) +(def dismiss-keyboard #js {}) +(def emoji-picker #js {:default #js {}}) +(def fs #js {}) +(def i18n #js {:locale "en"}) +(def image-crop-picker #js {}) +(def image-resizer #js {}) +(def qr-code #js {}) +(def svg #js {}) (def react-native - (clj->js {:NativeModules {:RNGestureHandlerModule {:Direction (fn [])} - :PushNotifications {} - :Status utils.test/status - :ReanimatedModule {:configureProps (fn [])}} + (clj->js {:NativeModules {:RNGestureHandlerModule {:Direction (fn [])} + :PushNotifications {} + :Status utils.test/status + :ReanimatedModule {:configureProps (fn [])}} :View {} :RefreshControl {} @@ -78,9 +80,10 @@ globalThis.__STATUS_MOBILE_JS_IDENTITY_PROXY__ = new Proxy({}, {get() { return ( :Platform {:select (fn [])} :I18nManager {:isRTL ""} :NativeEventEmitter (fn []) - :LayoutAnimation {:Presets #js {:easeInEaseOut nil - :linear nil - :spring nil} + :LayoutAnimation {:Presets #js + {:easeInEaseOut nil + :linear nil + :spring nil} :Types #js {} :Properties #{} :create (fn []) @@ -91,168 +94,199 @@ globalThis.__STATUS_MOBILE_JS_IDENTITY_PROXY__ = new Proxy({}, {get() { return ( (set! js/ReactNative react-native) -(def reanimated-bottom-sheet #js {:default #js {}}) +(def reanimated-bottom-sheet #js {:default #js {}}) -(def icons #js {:default #js {}}) -(def webview #js {:WebView #js {}}) -(def status-keycard #js {:default #js {:nfcIsSupported (fn [] #js {:then identity}) - :nfcIsEnabled (fn [] #js {:then identity})}}) +(def icons #js {:default #js {}}) +(def webview #js {:WebView #js {}}) +(def status-keycard + #js + {:default #js + {:nfcIsSupported (fn [] #js {:then identity}) + :nfcIsEnabled (fn [] #js {:then identity})}}) -(def snoopy #js {:default #js {}}) -(def snoopy-filter #js {:default #js {}}) -(def snoopy-bars #js {:default #js {}}) -(def snoopy-buffer #js {:default #js {}}) -(def fetch #js {}) +(def snoopy #js {:default #js {}}) +(def snoopy-filter #js {:default #js {}}) +(def snoopy-bars #js {:default #js {}}) +(def snoopy-buffer #js {:default #js {}}) +(def fetch #js {}) (def async-storage-atom (atom {})) -(def async-storage #js {:default #js {:setItem #(js/Promise.resolve) - :multiGet #(js/Promise.resolve) - :getItem #(js/Promise.resolve)}}) +(def async-storage + #js + {:default #js + {:setItem #(js/Promise.resolve) + :multiGet #(js/Promise.resolve) + :getItem #(js/Promise.resolve)}}) -(def background-timer (clj->js {:default {:setTimeout js/setTimeout - :setInterval js/setInterval - :clearTimeout js/clearTimeout - :clearInterval js/clearInterval}})) +(def background-timer + (clj->js {:default {:setTimeout js/setTimeout + :setInterval js/setInterval + :clearTimeout js/clearTimeout + :clearInterval js/clearInterval}})) -(def keychain #js {:setGenericPassword (constantly (.resolve js/Promise true)) - :setInternetCredentials #(js/Promise.resolve) - :resetInternetCredentials #(js/Promise.resolve) - "ACCESSIBLE" {} - "ACCESS_CONTROL" {}}) +(def keychain + #js + {:setGenericPassword (constantly (.resolve js/Promise true)) + :setInternetCredentials #(js/Promise.resolve) + :resetInternetCredentials #(js/Promise.resolve) + "ACCESSIBLE" {} + "ACCESS_CONTROL" {}}) (def react-native-mail #js {:mail #js {}}) -(def react-native-screens #js {}) -(def react-native-shake #js {}) +(def react-native-screens #js {}) +(def react-native-shake #js {}) (def react-native-share #js {:default {}}) -(def react-native-svg #js {:SvgUri #js {:render identity} - :SvgXml #js {:render identity} - :default #js {:render identity} - :Path #js {:render identity}}) +(def react-native-svg + #js + {:SvgUri #js {:render identity} + :SvgXml #js {:render identity} + :default #js {:render identity} + :Path #js {:render identity}}) (def react-native-webview #js {:default {}}) (def react-native-audio-toolkit #js {:MediaStates {}}) -(def net-info #js {}) -(def touchid #js {}) +(def net-info #js {}) +(def touchid #js {}) (def react-native-image-viewing #js {:default {}}) -(def safe-area-context (clj->js {:SafeAreaProvider {:_reactNativeIphoneXHelper {:getStatusBarHeight (fn [])}} - :SafeAreaInsetsContext {:Consumer (fn [])} - :SafeAreaView {}})) +(def safe-area-context + (clj->js {:SafeAreaProvider {:_reactNativeIphoneXHelper {:getStatusBarHeight (fn [])}} + :SafeAreaInsetsContext {:Consumer (fn [])} + :SafeAreaView {}})) -(def back-handler #js {:addEventListener identity - :removeEventListener identity}) -(def react #js {:useCallback nil - :useEffect nil - :useRef nil - :createRef nil - :Fragment identity}) -(def react-navigation-native #js {:NavigationContainer #js {} - :useFocusEffect identity - :CommonActions #js {} - :StackActions #js {}}) +(def back-handler + #js + {:addEventListener identity + :removeEventListener identity}) +(def react + #js + {:useCallback nil + :useEffect nil + :useRef nil + :createRef nil + :Fragment identity}) +(def react-navigation-native + #js + {:NavigationContainer #js {} + :useFocusEffect identity + :CommonActions #js {} + :StackActions #js {}}) -(def react-native-navigation #js {:Navigation #js {:constants (fn [] #js {:then identity}) - :setDefaultOptions identity - :setRoot identity - :dismissOverlay #(js/Promise.resolve) - :showOverlay identity - :setLazyComponentRegistrator identity - :pop identity - :push identity - :mergeOptions identity - :popToRoot identity - :showModal identity - :dismissModal identity - :registerComponent identity - :events - (fn [] - #js {:registerModalDismissedListener identity - :registerAppLaunchedListener identity - :registerBottomTabSelectedListener identity - :registerComponentDidDisappearListener identity - :registerComponentDidAppearListener identity - :registerNavigationButtonPressedListener identity})}}) +(def react-native-navigation + #js + {:Navigation #js + {:constants (fn [] #js {:then identity}) + :setDefaultOptions identity + :setRoot identity + :dismissOverlay #(js/Promise.resolve) + :showOverlay identity + :setLazyComponentRegistrator identity + :pop identity + :push identity + :mergeOptions identity + :popToRoot identity + :showModal identity + :dismissModal identity + :registerComponent identity + :events + (fn [] + #js + {:registerModalDismissedListener identity + :registerAppLaunchedListener identity + :registerBottomTabSelectedListener identity + :registerComponentDidDisappearListener identity + :registerComponentDidAppearListener identity + :registerNavigationButtonPressedListener identity})}}) -(def react-navigation-stack #js {:createStackNavigator identity - :TransitionPresets #js {:ModalPresentationIOS #js {}}}) +(def react-navigation-stack + #js + {:createStackNavigator identity + :TransitionPresets #js {:ModalPresentationIOS #js {}}}) (def react-navigation-bottom-tabs #js {:createBottomTabNavigator identity}) (def react-native-haptic-feedback #js {:default #js {:trigger nil}}) -(def react-native-reanimated #js {:default #js {:createAnimatedComponent identity - :eq nil - :greaterOrEq nil - :greaterThan nil - :lessThan nil - :lessOrEq nil - :add nil - :diff nil - :divide nil - :sub nil - :multiply nil - :abs nil - :min nil - :max nil - :neq nil - :and nil - :or nil - :not nil - :set nil - :startClock nil - :stopClock nil - :Value nil - :Clock nil - :debug nil - :log nil - :event nil - :cond nil - :block nil - :interpolateNode nil - :call nil - :timing nil - :onChange nil - :View #js {} - :Image #js {} - :ScrollView #js {} - :Text #js {} - :Extrapolate #js {:CLAMP nil} - :Code #js {}} - :EasingNode #js {:bezier identity - :linear identity} - :clockRunning nil - :useSharedValue (fn []) - :useAnimatedStyle (fn []) - :withTiming (fn []) - :withDelay (fn []) - :Easing #js {:bezier identity} - :Keyframe (fn []) - :SlideOutUp js/__STATUS_MOBILE_JS_IDENTITY_PROXY__ - :SlideInUp js/__STATUS_MOBILE_JS_IDENTITY_PROXY__ - :LinearTransition js/__STATUS_MOBILE_JS_IDENTITY_PROXY__}) -(def react-native-gesture-handler #js {:default #js {} - :State #js {:BEGAN nil - :ACTIVE nil - :CANCELLED nil - :END nil - :FAILED nil - :UNDETERMINED nil} - :PureNativeButton #js {} - :TapGestureHandler #js {} - :PanGestureHandler #js {} - :TouchableHighlight #js {} - :LongPressGestureHandler #js {} - :TouchableWithoutFeedback #js {} - :NativeViewGestureHandler #js {} - :FlatList #js {} - :ScrollView #js {} - :TouchableOpacity #js {} - :GestureDetector #js {} - :Gesture #js {:Pan nil} - :createNativeWrapper identity}) +(def react-native-reanimated + #js + {:default #js + {:createAnimatedComponent identity + :eq nil + :greaterOrEq nil + :greaterThan nil + :lessThan nil + :lessOrEq nil + :add nil + :diff nil + :divide nil + :sub nil + :multiply nil + :abs nil + :min nil + :max nil + :neq nil + :and nil + :or nil + :not nil + :set nil + :startClock nil + :stopClock nil + :Value nil + :Clock nil + :debug nil + :log nil + :event nil + :cond nil + :block nil + :interpolateNode nil + :call nil + :timing nil + :onChange nil + :View #js {} + :Image #js {} + :ScrollView #js {} + :Text #js {} + :Extrapolate #js {:CLAMP nil} + :Code #js {}} + :EasingNode #js + {:bezier identity + :linear identity} + :clockRunning nil + :useSharedValue (fn []) + :useAnimatedStyle (fn []) + :withTiming (fn []) + :withDelay (fn []) + :Easing #js {:bezier identity} + :Keyframe (fn []) + :SlideOutUp js/__STATUS_MOBILE_JS_IDENTITY_PROXY__ + :SlideInUp js/__STATUS_MOBILE_JS_IDENTITY_PROXY__ + :LinearTransition js/__STATUS_MOBILE_JS_IDENTITY_PROXY__}) +(def react-native-gesture-handler + #js + {:default #js {} + :State #js + {:BEGAN nil + :ACTIVE nil + :CANCELLED nil + :END nil + :FAILED nil + :UNDETERMINED nil} + :PureNativeButton #js {} + :TapGestureHandler #js {} + :PanGestureHandler #js {} + :TouchableHighlight #js {} + :LongPressGestureHandler #js {} + :TouchableWithoutFeedback #js {} + :NativeViewGestureHandler #js {} + :FlatList #js {} + :ScrollView #js {} + :TouchableOpacity #js {} + :GestureDetector #js {} + :Gesture #js {:Pan nil} + :createNativeWrapper identity}) (def react-native-redash #js {:clamp nil}) (def react-native-languages - (clj->js {:default {:language "en", - :addEventListener (fn []), + (clj->js {:default {:language "en" + :addEventListener (fn []) :removeEventListener (fn [])}})) (def react-native-device-info @@ -262,8 +296,9 @@ globalThis.__STATUS_MOBILE_JS_IDENTITY_PROXY__ = new Proxy({}, {get() { return ( #js {:CameraKitCamera #js {}}) (def react-native-push-notification - #js {:localNotification identity - :requestPermission identity}) + #js + {:localNotification identity + :requestPermission identity}) (def react-native-gradien #js {:default #js {}}) @@ -291,12 +326,16 @@ globalThis.__STATUS_MOBILE_JS_IDENTITY_PROXY__ = new Proxy({}, {get() { return ( (def react-native-camera-roll (clj->js {:default #js {}})) -(def wallet-connect-client #js {:default #js {} - :CLIENT_EVENTS #js {:session #js {:request nil - :created nil - :deleted nil - :proposal nil - :updated nil}}}) +(def wallet-connect-client + #js + {:default #js {} + :CLIENT_EVENTS #js + {:session #js + {:request nil + :created nil + :deleted nil + :proposal nil + :updated nil}}}) (def worklet-factory #js {:applyAnimationsToStyle (fn [])}) @@ -306,70 +345,71 @@ globalThis.__STATUS_MOBILE_JS_IDENTITY_PROXY__ = new Proxy({}, {get() { return ( (def record-audio-worklets #js {}) ;; Update i18n_resources.cljs -(defn mock [module] +(defn mock + [module] (case module - "react-native-languages" react-native-languages - "react-native-background-timer" background-timer - "react-native-image-crop-picker" image-crop-picker - "react-native-gesture-handler" react-native-gesture-handler - "react-native-safe-area-context" safe-area-context - "react-native-config" config - "react-native-iphone-x-helper" (clj->js {:getStatusBarHeight (fn []) - :getBottomSpace (fn [])}) - "react-native-screens" (clj->js {}) - "react-native-reanimated" react-native-reanimated - "react-native-redash/lib/module/v1" react-native-redash - "react-native-fetch-polyfill" fetch - "react-native-status-keycard" status-keycard - "react-native-keychain" keychain - "react-native-touch-id" touchid - "@react-native-community/netinfo" net-info - "react-native-dialogs" dialogs - "react-native" react-native - "react-native-fs" fs - "react-native-mail" react-native-mail - "react-native-image-resizer" image-resizer - "react-native-haptic-feedback" react-native-haptic-feedback - "react-native-device-info" react-native-device-info - "react-native-push-notification" react-native-push-notification - "react-native-linear-gradient" react-native-gradien - "@react-native-community/masked-view" masked-view - "react-native-blob-util" react-native-blob-util - "react-native-navigation" react-native-navigation + "react-native-languages" react-native-languages + "react-native-background-timer" background-timer + "react-native-image-crop-picker" image-crop-picker + "react-native-gesture-handler" react-native-gesture-handler + "react-native-safe-area-context" safe-area-context + "react-native-config" config + "react-native-iphone-x-helper" (clj->js {:getStatusBarHeight (fn []) + :getBottomSpace (fn [])}) + "react-native-screens" (clj->js {}) + "react-native-reanimated" react-native-reanimated + "react-native-redash/lib/module/v1" react-native-redash + "react-native-fetch-polyfill" fetch + "react-native-status-keycard" status-keycard + "react-native-keychain" keychain + "react-native-touch-id" touchid + "@react-native-community/netinfo" net-info + "react-native-dialogs" dialogs + "react-native" react-native + "react-native-fs" fs + "react-native-mail" react-native-mail + "react-native-image-resizer" image-resizer + "react-native-haptic-feedback" react-native-haptic-feedback + "react-native-device-info" react-native-device-info + "react-native-push-notification" react-native-push-notification + "react-native-linear-gradient" react-native-gradien + "@react-native-community/masked-view" masked-view + "react-native-blob-util" react-native-blob-util + "react-native-navigation" react-native-navigation "@react-native-community/push-notification-ios" push-notification-ios - "@react-native-community/blur" react-native-blur - "@react-native-community/cameraroll" react-native-camera-roll - "react-native-camera-kit" react-native-camera-kit - "react-native-permissions" react-native-permissions - "rn-emoji-keyboard" rn-emoji-keyboard - "react-native-hole-view" react-native-hole-view - "react-native-draggable-flatlist" react-native-draggable-flatlist - "react-native-webview" react-native-webview - "@react-native-community/audio-toolkit" react-native-audio-toolkit - "react-native-image-viewing" react-native-image-viewing - "react-native-share" react-native-share - "@react-native-async-storage/async-storage" async-storage - "react-native-svg" react-native-svg - "../src/js/worklet_factory.js" worklet-factory - "../src/js/shell_worklets.js" shell-worklets - "../src/js/record_audio_worklets.js" record-audio-worklets - "./fleets.js" default-fleets - "./chats.js" default-chats - "@walletconnect/client" wallet-connect-client - "../translations/ar.json" (js/JSON.parse (slurp "./translations/ar.json")) - "../translations/de.json" (js/JSON.parse (slurp "./translations/de.json")) - "../translations/en.json" (js/JSON.parse (slurp "./translations/en.json")) - "../translations/es.json" (js/JSON.parse (slurp "./translations/es.json")) - "../translations/es_419.json" (js/JSON.parse (slurp "./translations/es_419.json")) - "../translations/fil.json" (js/JSON.parse (slurp "./translations/fil.json")) - "../translations/fr.json" (js/JSON.parse (slurp "./translations/fr.json")) - "../translations/id.json" (js/JSON.parse (slurp "./translations/id.json")) - "../translations/it.json" (js/JSON.parse (slurp "./translations/it.json")) - "../translations/ko.json" (js/JSON.parse (slurp "./translations/ko.json")) - "../translations/pt_BR.json" (js/JSON.parse (slurp "./translations/pt_BR.json")) - "../translations/ru.json" (js/JSON.parse (slurp "./translations/ru.json")) - "../translations/tr.json" (js/JSON.parse (slurp "./translations/tr.json")) - "../translations/zh.json" (js/JSON.parse (slurp "./translations/zh.json")) - "../translations/zh_hant.json" (js/JSON.parse (slurp "./translations/zh_hant.json")) - "../translations/zh_TW.json" (js/JSON.parse (slurp "./translations/zh_TW.json")) + "@react-native-community/blur" react-native-blur + "@react-native-community/cameraroll" react-native-camera-roll + "react-native-camera-kit" react-native-camera-kit + "react-native-permissions" react-native-permissions + "rn-emoji-keyboard" rn-emoji-keyboard + "react-native-hole-view" react-native-hole-view + "react-native-draggable-flatlist" react-native-draggable-flatlist + "react-native-webview" react-native-webview + "@react-native-community/audio-toolkit" react-native-audio-toolkit + "react-native-image-viewing" react-native-image-viewing + "react-native-share" react-native-share + "@react-native-async-storage/async-storage" async-storage + "react-native-svg" react-native-svg + "../src/js/worklet_factory.js" worklet-factory + "../src/js/shell_worklets.js" shell-worklets + "../src/js/record_audio_worklets.js" record-audio-worklets + "./fleets.js" default-fleets + "./chats.js" default-chats + "@walletconnect/client" wallet-connect-client + "../translations/ar.json" (js/JSON.parse (slurp "./translations/ar.json")) + "../translations/de.json" (js/JSON.parse (slurp "./translations/de.json")) + "../translations/en.json" (js/JSON.parse (slurp "./translations/en.json")) + "../translations/es.json" (js/JSON.parse (slurp "./translations/es.json")) + "../translations/es_419.json" (js/JSON.parse (slurp "./translations/es_419.json")) + "../translations/fil.json" (js/JSON.parse (slurp "./translations/fil.json")) + "../translations/fr.json" (js/JSON.parse (slurp "./translations/fr.json")) + "../translations/id.json" (js/JSON.parse (slurp "./translations/id.json")) + "../translations/it.json" (js/JSON.parse (slurp "./translations/it.json")) + "../translations/ko.json" (js/JSON.parse (slurp "./translations/ko.json")) + "../translations/pt_BR.json" (js/JSON.parse (slurp "./translations/pt_BR.json")) + "../translations/ru.json" (js/JSON.parse (slurp "./translations/ru.json")) + "../translations/tr.json" (js/JSON.parse (slurp "./translations/tr.json")) + "../translations/zh.json" (js/JSON.parse (slurp "./translations/zh.json")) + "../translations/zh_hant.json" (js/JSON.parse (slurp "./translations/zh_hant.json")) + "../translations/zh_TW.json" (js/JSON.parse (slurp "./translations/zh_TW.json")) nil)) diff --git a/src/quo/animated.cljs b/src/quo/animated.cljs index d57cf254d2..ad80dbfc6f 100644 --- a/src/quo/animated.cljs +++ b/src/quo/animated.cljs @@ -1,12 +1,12 @@ (ns quo.animated (:refer-clojure :exclude [abs set delay divide]) - (:require [reagent.core :as reagent] - [quo.react-native :as rn] - [quo.gesture-handler :as gh] - [oops.core :refer [oget ocall]] - ["react-native-reanimated" :default animated :refer (clockRunning EasingNode)] + (:require ["react-native-reanimated" :default animated :refer (clockRunning EasingNode)] ["react-native-redash/lib/module/v1" :as redash] - quo.react) + [oops.core :refer [ocall oget]] + [quo.gesture-handler :as gh] + quo.react + [quo.react-native :as rn] + [reagent.core :as reagent]) (:require-macros [quo.react :refer [maybe-js-deps]])) (def create-animated-component (comp reagent/adapt-react-class (.-createAnimatedComponent animated))) @@ -15,9 +15,10 @@ (def text (reagent/adapt-react-class (.-Text animated))) (def scroll-view (reagent/adapt-react-class (.-ScrollView animated))) (def code (reagent/adapt-react-class (.-Code animated))) -(def animated-flat-list (create-animated-component gh/flat-list-raw)) +(def animated-flat-list (create-animated-component gh/flat-list-raw)) -(defn flat-list [props] +(defn flat-list + [props] [animated-flat-list (rn/base-list-props props)]) (def useCode (.-useCode animated)) @@ -58,37 +59,42 @@ (def bezier (.-bezier ^js EasingNode)) (def linear (.-linear ^js EasingNode)) -(def easings {:linear linear - :ease-in (bezier 0.42 0 1 1) - :ease-out (bezier 0 0 0.58 1) - :ease-in-out (bezier 0.42 0 0.58 1) - :cubic (bezier 0.55 0.055 0.675 0.19) - :keyboard (bezier 0.17 0.59 0.4 0.77)}) +(def easings + {:linear linear + :ease-in (bezier 0.42 0 1 1) + :ease-out (bezier 0 0 0.58 1) + :ease-in-out (bezier 0.42 0 0.58 1) + :cubic (bezier 0.55 0.055 0.675 0.19) + :keyboard (bezier 0.17 0.59 0.4 0.77)}) -(def springs {:lazy {:damping 50 - :mass 0.3 - :stiffness 120 - :overshootClamping true - :bouncyFactor 1} - :jump {:damping 13 - :mass 0.5 - :stiffness 170 - :overshootClamping false - :bouncyFactor 1 - :restSpeedThreshold 0.001 - :restDisplacementThreshold 0.001}}) +(def springs + {:lazy {:damping 50 + :mass 0.3 + :stiffness 120 + :overshootClamping true + :bouncyFactor 1} + :jump {:damping 13 + :mass 0.5 + :stiffness 170 + :overshootClamping false + :bouncyFactor 1 + :restSpeedThreshold 0.001 + :restDisplacementThreshold 0.001}}) -(defn set-value [anim val] +(defn set-value + [anim val] (ocall anim "setValue" val)) (def Value (oget animated "Value")) -(defn value [x] +(defn value + [x] (new Value x)) (def Clock (oget animated "Clock")) -(defn clock [] +(defn clock + [] (new Clock)) (def debug (oget animated "debug")) @@ -100,8 +106,10 @@ ([config options] (ocall animated "event" (clj->js config) (clj->js options)))) -(defn on-change [state node] - (ocall animated "onChange" +(defn on-change + [state node] + (ocall animated + "onChange" state (if (vector? node) (clj->js node) @@ -124,24 +132,31 @@ (clj->js else-node) else-node)))) -(defn block [opts] +(defn block + [opts] (.block ^js animated (to-array opts))) -(defn interpolate [anim-value config] +(defn interpolate + [anim-value config] (.interpolateNode ^js animated anim-value (clj->js config))) -(defn call* [args callback] +(defn call* + [args callback] (.call ^js animated (to-array args) callback)) -(defn timing [clock-value opts config] +(defn timing + [clock-value opts config] (.timing ^js animated clock-value (clj->js opts) (clj->js config))) -(defn spring [clock-value opts config] - (.spring ^js animated clock-value - (clj->js opts) (clj->js config))) +(defn spring + [clock-value opts config] + (.spring ^js animated + clock-value + (clj->js opts) + (clj->js config))) (def extrapolate {:clamp (oget animated "Extrapolate" "CLAMP")}) @@ -150,37 +165,48 @@ (def clamp (oget redash "clamp")) (def diff-clamp (.-diffClamp ^js redash)) -(defn with-spring [config] +(defn with-spring + [config] (ocall redash "withSpring" (clj->js config))) -(defn with-decay [config] +(defn with-decay + [config] (.withDecay ^js redash (clj->js config))) -(defn with-offset [config] +(defn with-offset + [config] (.withOffset ^js redash (clj->js config))) -(defn with-spring-transition [val config] +(defn with-spring-transition + [val config] (.withSpringTransition ^js redash val (clj->js config))) -(defn with-timing-transition [val config] +(defn with-timing-transition + [val config] (.withTimingTransition ^js redash val (clj->js config))) -(defn use-spring-transition [val config] +(defn use-spring-transition + [val config] (.useSpringTransition ^js redash val (clj->js config))) -(defn use-timing-transition [val config] +(defn use-timing-transition + [val config] (.useTimingTransition ^js redash val (clj->js config))) -(defn re-timing [config] +(defn re-timing + [config] (.timing ^js redash (clj->js config))) -(defn re-spring [config] +(defn re-spring + [config] (.spring ^js redash (clj->js config))) -(defn on-scroll [opts] +(defn on-scroll + [opts] (ocall redash "onScrollEvent" (clj->js opts))) -(defn on-gesture [opts] +(defn on-gesture + [opts] (let [gesture-event (event #js [#js {:nativeEvent (clj->js opts)}])] {:onHandlerStateChange gesture-event :onGestureEvent gesture-event})) @@ -191,19 +217,22 @@ (def delay (.-delay ^js redash)) -(defn loop* [opts] +(defn loop* + [opts] (ocall redash "loop" (clj->js opts))) (def use-value (.-useValue ^js redash)) (def use-clock (.-useClock ^js redash)) -(defn use-gesture [opts] +(defn use-gesture + [opts] (let [gesture (.useGestureHandler ^js redash (clj->js opts))] {:onHandlerStateChange (.-onHandlerStateChange ^js gesture) :onGestureEvent (.-onGestureEvent ^js gesture)})) -(defn snap-point [value velocity snap-points] +(defn snap-point + [value velocity snap-points] (.snapPoint ^js redash value velocity (to-array snap-points))) (defn cancelable-loop @@ -256,12 +285,13 @@ (set position (add offset val))]) (cond* (and* (eq state (:end gh/states)) (not* animation-over)) - [(set position (re-timing - {:clock c - :easing easing - :duration duration - :from position - :to to})) + [(set position + (re-timing + {:clock c + :easing easing + :duration duration + :from position + :to to})) (cond* (not* (clock-running c)) finish-animation)]) position]))) diff --git a/src/quo/components/animated/pressable.cljs b/src/quo/components/animated/pressable.cljs index 6c69afde1d..5ea788a208 100644 --- a/src/quo/components/animated/pressable.cljs +++ b/src/quo/components/animated/pressable.cljs @@ -1,9 +1,9 @@ (ns quo.components.animated.pressable - (:require [quo.animated :as animated] + (:require [cljs-bean.core :as bean] + [quo.animated :as animated] + [quo.gesture-handler :as gesture-handler] [quo.react :as react] - [reagent.core :as reagent] - [cljs-bean.core :as bean] - [quo.gesture-handler :as gesture-handler])) + [reagent.core :as reagent])) (def long-press-duration 500) (def scale-down-small 0.95) @@ -54,7 +54,8 @@ :right 0 :position :absolute}) -(defn pressable-hooks [props] +(defn pressable-hooks + [props] (let [{background-color :bgColor border-radius :borderRadius border-color :borderColor @@ -69,23 +70,24 @@ :or {border-radius 0 type "primary"}} (bean/bean props) - long-press-ref (react/create-ref) - state (animated/use-value (:undetermined gesture-handler/states)) - active (animated/eq state (:began gesture-handler/states)) - gesture-handler (animated/use-gesture {:state state}) - animation (react/use-memo - (fn [] - (animated/with-timing-transition active - {:duration (animated/cond* active time-in time-out) - :easing (:ease-in animated/easings)})) - []) + long-press-ref (react/create-ref) + state (animated/use-value (:undetermined gesture-handler/states)) + active (animated/eq state (:began gesture-handler/states)) + gesture-handler (animated/use-gesture {:state state}) + animation (react/use-memo + (fn [] + (animated/with-timing-transition active + {:duration (animated/cond* active time-in time-out) + :easing (:ease-in animated/easings)})) + []) {:keys [background - foreground]} (react/use-memo - (fn [] - (type->animation {:type (keyword type) - :animation animation})) - [type]) - handle-press (fn [] (when on-press (on-press))) + foreground]} + (react/use-memo + (fn [] + (type->animation {:type (keyword type) + :animation animation})) + [type]) + handle-press (fn [] (when on-press (on-press))) long-gesture-handler (react/callback (fn [^js evt] (let [gesture-state (-> evt .-nativeEvent .-state)] @@ -111,8 +113,9 @@ :min-duration-ms long-press-duration :max-dist 22 :ref long-press-ref} - [animated/view {:accessible true - :accessibility-label accessibility-label} + [animated/view + {:accessible true + :accessibility-label accessibility-label} [gesture-handler/tap-gesture-handler (merge gesture-handler {:shouldCancelWhenOutside true @@ -120,12 +123,13 @@ :enabled (boolean (and (or on-press on-long-press on-press-start) (not disabled)))}) [animated/view - [animated/view {:style (merge absolute-fill - background - {:background-color background-color - :border-radius border-radius - :border-color border-color - :border-width border-width})}] + [animated/view + {:style (merge absolute-fill + background + {:background-color background-color + :border-radius border-radius + :border-color border-color + :border-width border-width})}] (into [animated/view {:style foreground}] (react/get-children children))]]]]))) diff --git a/src/quo/components/animated_header.cljs b/src/quo/components/animated_header.cljs index 7e7bab5e6f..826b54241a 100644 --- a/src/quo/components/animated_header.cljs +++ b/src/quo/components/animated_header.cljs @@ -7,7 +7,8 @@ [quo.platform :as platform] [reagent.core :as reagent])) -(defn header-wrapper-style [{:keys [value offset]}] +(defn header-wrapper-style + [{:keys [value offset]}] (merge {:background-color (:ui-background @colors/theme)} (when (and offset platform/android?) @@ -27,66 +28,78 @@ :shadow-color (:shadow-01 @colors/theme) :shadow-offset {:width 0 :height 4}}))) -(defn title-style [layout] - {:flex 1 +(defn title-style + [layout] + {:flex 1 :justify-content :center - :padding-right (get-in layout [:right :width])}) + :padding-right (get-in layout [:right :width])}) -(defn header-container [] +(defn header-container + [] (let [y (animated/value 0) animation-value (animated/value 0) animation (animated/with-timing-transition - animation-value - {:duration 250 - :easing (:ease-in animated/easings)}) + animation-value + {:duration 250 + :easing (:ease-in animated/easings)}) on-scroll (animated/on-scroll {:y y}) layout (reagent/atom {}) offset (reagent/atom 0) on-layout (fn [evt] (reset! offset (oget evt "nativeEvent" "layout" "height")))] (fn [{:keys [extended-header refresh-control refreshing-sub refreshing-counter] :as props} children] - [animated/view {:flex 1 - :pointer-events :box-none} - [animated/code {:key (str @offset) - :exec (animated/cond* - (animated/and* (animated/greater-or-eq y @offset) - (animated/greater-or-eq y 1)) - (animated/set animation-value 1) - (animated/set animation-value 0))}] - [animated/view {:pointer-events :box-none - :style (header-wrapper-style {:value y - :offset @offset})} - [header/header (merge - {:get-layout (fn [el l] (swap! layout assoc el l)) - :border-bottom false - :title-component [animated/view {:style (title-style @layout)} - [extended-header {:value y - :animation animation - :minimized true - :offset @offset}]] - :title-align :left} - (dissoc props :extended-header))]] - (into [animated/scroll-view {:on-scroll on-scroll - :refreshControl (when refresh-control - (refresh-control - (and @refreshing-sub - @refreshing-counter))) - :style {:z-index 1} - :scrollEventThrottle 16} + [animated/view + {:flex 1 + :pointer-events :box-none} + [animated/code + {:key (str @offset) + :exec (animated/cond* + (animated/and* (animated/greater-or-eq y @offset) + (animated/greater-or-eq y 1)) + (animated/set animation-value 1) + (animated/set animation-value 0))}] + [animated/view + {:pointer-events :box-none + :style (header-wrapper-style {:value y + :offset @offset})} + [header/header + (merge + {:get-layout (fn [el l] (swap! layout assoc el l)) + :border-bottom false + :title-component [animated/view {:style (title-style @layout)} + [extended-header + {:value y + :animation animation + :minimized true + :offset @offset}]] + :title-align :left} + (dissoc props :extended-header))]] + (into [animated/scroll-view + {:on-scroll on-scroll + :refreshControl (when refresh-control + (refresh-control + (and @refreshing-sub + @refreshing-counter))) + :style {:z-index 1} + :scrollEventThrottle 16} [animated/view {:pointer-events :box-none} - [animated/view {:pointer-events :box-none - :on-layout on-layout} - [extended-header {:value y - :animation animation - :offset @offset}]]]] + [animated/view + {:pointer-events :box-none + :on-layout on-layout} + [extended-header + {:value y + :animation animation + :offset @offset}]]]] children)]))) -(defn header [{:keys [use-insets] :as props} & children] +(defn header + [{:keys [use-insets] :as props} & children] (if use-insets [safe-area/consumer (fn [insets] - [header-container (-> props - (dissoc :use-insets) - (assoc :insets insets)) + [header-container + (-> props + (dissoc :use-insets) + (assoc :insets insets)) children])] [header-container props children])) diff --git a/src/quo/components/bottom_sheet/style.cljs b/src/quo/components/bottom_sheet/style.cljs index 49a7c442ef..c1980cad0e 100644 --- a/src/quo/components/bottom_sheet/style.cljs +++ b/src/quo/components/bottom_sheet/style.cljs @@ -15,13 +15,14 @@ :flex 1 :justify-content :flex-end}) -(defn backdrop [] - {:flex 1 - :position :absolute - :left 0 - :top 0 - :right 0 - :bottom 0}) +(defn backdrop + [] + {:flex 1 + :position :absolute + :left 0 + :top 0 + :right 0 + :bottom 0}) (defn content-container [window-height] diff --git a/src/quo/components/bottom_sheet/view.cljs b/src/quo/components/bottom_sheet/view.cljs index a26b37fdcb..624d937dcd 100644 --- a/src/quo/components/bottom_sheet/view.cljs +++ b/src/quo/components/bottom_sheet/view.cljs @@ -1,25 +1,27 @@ (ns quo.components.bottom-sheet.view - (:require [reagent.core :as reagent] + (:require [cljs-bean.core :as bean] [quo.animated :as animated] - [quo.react-native :as rn] - [quo.react :as react] - [quo.platform :as platform] - [cljs-bean.core :as bean] - [quo.components.safe-area :as safe-area] [quo.components.bottom-sheet.style :as styles] + [quo.components.safe-area :as safe-area] + [quo.design-system.colors :as colors] [quo.gesture-handler :as gesture-handler] - [quo.design-system.colors :as colors])) + [quo.platform :as platform] + [quo.react :as react] + [quo.react-native :as rn] + [reagent.core :as reagent])) (def opacity-coeff 0.8) (def close-duration 150) -(def spring-config {:damping 15 - :mass 0.7 - :stiffness 150 - :overshootClamping false - :restSpeedThreshold 0.1 - :restDisplacementThreshold 0.1}) +(def spring-config + {:damping 15 + :mass 0.7 + :stiffness 150 + :overshootClamping false + :restSpeedThreshold 0.1 + :restDisplacementThreshold 0.1}) -(defn bottom-sheet-hooks [props] +(defn bottom-sheet-hooks + [props] (let [{on-cancel :onCancel disable-drag? :disableDrag? show-handle? :showHandle? @@ -31,77 +33,83 @@ backdrop-dismiss? true back-button-cancel true}} (bean/bean props) - body-ref (react/create-ref) + body-ref (react/create-ref) master-ref (react/create-ref) - height (react/state 0) - {window-height :height} (rn/use-window-dimensions) + height (react/state 0) + {window-height :height} (rn/use-window-dimensions) {:keys [keyboard-shown - keyboard-height]} (rn/use-keyboard) - keyboard-height-android-delta (if (and platform/android? keyboard-shown) (+ keyboard-height 20) 0) - safe-area (safe-area/use-safe-area) - window-height (- window-height (if platform/android? - (+ 50 keyboard-height-android-delta) ;; TODO : remove 50 when react-native-navigation v8 will be implemented https://github.com/wix/react-native-navigation/issues/7225 - 0)) - min-height (+ (* styles/vertical-padding 2) (:bottom safe-area)) - max-height (- window-height (:top safe-area)) - visible (react/state false) + keyboard-height]} + (rn/use-keyboard) + keyboard-height-android-delta + (if (and platform/android? keyboard-shown) (+ keyboard-height 20) 0) + safe-area (safe-area/use-safe-area) + window-height (- window-height + (if platform/android? + (+ 50 keyboard-height-android-delta) ;; TODO : remove 50 when + ;; react-native-navigation v8 will be + ;; implemented + ;; https://github.com/wix/react-native-navigation/issues/7225 + 0)) + min-height (+ (* styles/vertical-padding 2) (:bottom safe-area)) + max-height (- window-height (:top safe-area)) + visible (react/state false) master-translation-y (animated/use-value 0) - master-velocity-y (animated/use-value (:undetermined gesture-handler/states)) - master-state (animated/use-value (:undetermined gesture-handler/states)) - tap-state (animated/use-value 0) - manual-open (animated/use-value 0) - manual-close (animated/use-value 0) - offset (animated/use-value 0) - drag-over (animated/use-value 1) - clock (animated/use-clock) - tap-gesture-handler (animated/use-gesture {:state tap-state}) - on-master-event (animated/use-gesture - {:translationY master-translation-y - :state master-state - :velocityY master-velocity-y}) - on-body-event on-master-event - sheet-height (min max-height - (+ styles/border-radius @height)) + master-velocity-y (animated/use-value (:undetermined gesture-handler/states)) + master-state (animated/use-value (:undetermined gesture-handler/states)) + tap-state (animated/use-value 0) + manual-open (animated/use-value 0) + manual-close (animated/use-value 0) + offset (animated/use-value 0) + drag-over (animated/use-value 1) + clock (animated/use-clock) + tap-gesture-handler (animated/use-gesture {:state tap-state}) + on-master-event (animated/use-gesture + {:translationY master-translation-y + :state master-state + :velocityY master-velocity-y}) + on-body-event on-master-event + sheet-height (min max-height + (+ styles/border-radius @height)) - open-snap-point (animated/use-value 0) + open-snap-point (animated/use-value 0) close-snap-point 0 - on-close (fn [] - (when @visible - (reset! visible false) - (reset! height 0) - (when on-cancel - (on-cancel)))) - close-sheet (fn [] - (animated/set-value manual-close 1)) - on-snap (fn [pos] - (when (= close-snap-point (aget pos 0)) - (on-close))) - interrupted (animated/and* (animated/eq master-state (:began gesture-handler/states)) - (animated/clock-running clock)) - translate-y (react/use-memo - (fn [] - (animated/with-easing - {:value (animated/cond* (animated/less-or-eq master-translation-y 0) - (animated/divide master-translation-y 2) - master-translation-y) - :velocity master-velocity-y - :offset offset - :state master-state - :animation-over drag-over - :snap-points [open-snap-point close-snap-point]})) - []) - opacity (react/use-memo - (fn [] - (animated/cond* - open-snap-point - (animated/interpolate - translate-y - {:inputRange [(animated/multiply open-snap-point opacity-coeff) 0] - :outputRange [1 0] - :extrapolate (:clamp animated/extrapolate)}))) - [])] + on-close (fn [] + (when @visible + (reset! visible false) + (reset! height 0) + (when on-cancel + (on-cancel)))) + close-sheet (fn [] + (animated/set-value manual-close 1)) + on-snap (fn [pos] + (when (= close-snap-point (aget pos 0)) + (on-close))) + interrupted (animated/and* (animated/eq master-state (:began gesture-handler/states)) + (animated/clock-running clock)) + translate-y (react/use-memo + (fn [] + (animated/with-easing + {:value (animated/cond* (animated/less-or-eq master-translation-y 0) + (animated/divide master-translation-y 2) + master-translation-y) + :velocity master-velocity-y + :offset offset + :state master-state + :animation-over drag-over + :snap-points [open-snap-point close-snap-point]})) + []) + opacity (react/use-memo + (fn [] + (animated/cond* + open-snap-point + (animated/interpolate + translate-y + {:inputRange [(animated/multiply open-snap-point opacity-coeff) 0] + :outputRange [1 0] + :extrapolate (:clamp animated/extrapolate)}))) + [])] (animated/code! (fn [] (animated/cond* (animated/and* (animated/eq master-state (:end gesture-handler/states)) @@ -177,39 +185,48 @@ (close-sheet))) [visible?]) (reagent/as-element - [rn/view {:style styles/container - :pointer-events :box-none} - [gesture-handler/tap-gesture-handler (merge {:enabled backdrop-dismiss?} - tap-gesture-handler) - [animated/view {:style (merge (styles/backdrop) - (when platform/ios? - {:opacity opacity - :background-color (:backdrop @colors/theme)}))}]] - [animated/view {:style (merge (styles/content-container window-height) - {:transform [{:translateY (if (= sheet-height max-height) - (animated/add translate-y keyboard-height-android-delta) - translate-y)} - {:translateY (* window-height 2)}]})} - [gesture-handler/pan-gesture-handler (merge on-master-event - {:ref master-ref - :wait-for body-ref - :enabled (not disable-drag?)}) - [animated/view {:style styles/content-header} + [rn/view + {:style styles/container + :pointer-events :box-none} + [gesture-handler/tap-gesture-handler + (merge {:enabled backdrop-dismiss?} + tap-gesture-handler) + [animated/view + {:style (merge (styles/backdrop) + (when platform/ios? + {:opacity opacity + :background-color (:backdrop @colors/theme)}))}]] + [animated/view + {:style (merge (styles/content-container window-height) + {:transform [{:translateY (if (= sheet-height max-height) + (animated/add translate-y + keyboard-height-android-delta) + translate-y)} + {:translateY (* window-height 2)}]})} + [gesture-handler/pan-gesture-handler + (merge on-master-event + {:ref master-ref + :wait-for body-ref + :enabled (not disable-drag?)}) + [animated/view {:style styles/content-header} (when show-handle? [rn/view {:style styles/handle}])]] - [gesture-handler/pan-gesture-handler (merge on-body-event - {:ref body-ref - :wait-for master-ref - :enabled (and (not disable-drag?) - (not= sheet-height max-height))}) - [animated/view {:height sheet-height - :flex 1} - [animated/view {:style {:padding-top styles/vertical-padding - :padding-bottom (+ styles/vertical-padding - (if (and platform/ios? keyboard-shown) - keyboard-height - (:bottom safe-area)))} - :on-layout #(reset! height (.-nativeEvent.layout.height ^js %))} + [gesture-handler/pan-gesture-handler + (merge on-body-event + {:ref body-ref + :wait-for master-ref + :enabled (and (not disable-drag?) + (not= sheet-height max-height))}) + [animated/view + {:height sheet-height + :flex 1} + [animated/view + {:style {:padding-top styles/vertical-padding + :padding-bottom (+ styles/vertical-padding + (if (and platform/ios? keyboard-shown) + keyboard-height + (:bottom safe-area)))} + :on-layout #(reset! height (.-nativeEvent.layout.height ^js %))} (into [:<>] (react/get-children children))]]]]]))) diff --git a/src/quo/components/button/view.cljs b/src/quo/components/button/view.cljs index ea5d3ff926..a35aea044d 100644 --- a/src/quo/components/button/view.cljs +++ b/src/quo/components/button/view.cljs @@ -1,14 +1,14 @@ (ns quo.components.button.view (:require [quo.components.animated.pressable :as animation] - [quo.react-native :as rn] - [quo.haptic :as haptic] + [quo.components.text :as text] ;; FIXME: [quo.design-system.colors :as colors] [quo.design-system.spacing :as spacing] - [quo.components.text :as text] - ;; FIXME: + [quo.haptic :as haptic] + [quo.react-native :as rn] [status-im.ui.components.icons.icons :as icons])) -(defn style-container [type] +(defn style-container + [type] (merge {:height 44 :align-items :center :justify-content :center @@ -19,14 +19,16 @@ :icon {} nil))) -(defn content-style [type] +(defn content-style + [type] (case type :primary (:base spacing/padding-horizontal) :secondary (:x-tiny spacing/padding-horizontal) :icon (:tiny spacing/padding-horizontal) nil)) -(defn themes [theme] +(defn themes + [theme] (case theme :main {:icon-color (:icon-04 @colors/theme) :background-color (:interactive-02 @colors/theme) @@ -54,44 +56,46 @@ :text-color (:text-01 @colors/theme) :border-color (:ui-01 @colors/theme)})) -(defn button [{:keys [on-press disabled type theme before after - haptic-feedback haptic-type on-long-press on-press-start - accessibility-label loading border-radius style test-ID] - :or {theme :main - type :primary - haptic-feedback true - border-radius 8 - haptic-type :selection}} - children] - (let [theme' (cond - disabled :disabled - :else theme) +(defn button + [{:keys [on-press disabled type theme before after + haptic-feedback haptic-type on-long-press on-press-start + accessibility-label loading border-radius style test-ID] + :or {theme :main + type :primary + haptic-feedback true + border-radius 8 + haptic-type :selection}} + children] + (let [theme' (cond + disabled :disabled + :else theme) {:keys [icon-color background-color text-color border-color]} (themes theme') - optional-haptic (fn [] - (when haptic-feedback - (haptic/trigger haptic-type)))] - [animation/pressable (merge {:bg-color background-color - :border-radius border-radius - :type type - :disabled disabled - :accessibility-label accessibility-label} - (when border-color - {:border-color border-color - :border-width 1}) - (when on-press - {:on-press (fn [] - (optional-haptic) - (on-press))}) - (when on-long-press - {:on-long-press (fn [] - (optional-haptic) - (on-long-press))}) - (when on-press-start - {:on-press-start (fn [] - (optional-haptic) - (on-press-start))})) + optional-haptic (fn [] + (when haptic-feedback + (haptic/trigger haptic-type)))] + [animation/pressable + (merge {:bg-color background-color + :border-radius border-radius + :type type + :disabled disabled + :accessibility-label accessibility-label} + (when border-color + {:border-color border-color + :border-width 1}) + (when on-press + {:on-press (fn [] + (optional-haptic) + (on-press))}) + (when on-long-press + {:on-long-press (fn [] + (optional-haptic) + (on-long-press))}) + (when on-press-start + {:on-press-start (fn [] + (optional-haptic) + (on-press-start))})) [rn/view {:test-ID test-ID :style (merge (style-container type) style)} (when before [rn/view @@ -99,17 +103,19 @@ (when loading [rn/view {:style {:position :absolute}} [rn/activity-indicator]]) - [rn/view {:style (merge (content-style type) - (when loading - {:opacity 0}))} + [rn/view + {:style (merge (content-style type) + (when loading + {:opacity 0}))} (cond (= type :icon) [icons/icon children {:color icon-color}] (string? children) - [text/text {:weight :medium - :number-of-lines 1 - :style {:color text-color}} + [text/text + {:weight :medium + :number-of-lines 1 + :style {:color text-color}} children] (vector? children) diff --git a/src/quo/components/controls/styles.cljs b/src/quo/components/controls/styles.cljs index 9f8cc2cb45..4116ef4eeb 100644 --- a/src/quo/components/controls/styles.cljs +++ b/src/quo/components/controls/styles.cljs @@ -2,7 +2,8 @@ (:require [quo.animated :as animated] [quo.design-system.colors :as colors])) -(defn switch-style [state disabled] +(defn switch-style + [state disabled] {:width 52 :height 28 :border-radius 14 @@ -13,7 +14,8 @@ (:interactive-04 @colors/theme) (:interactive-01 @colors/theme)))}) -(defn switch-bullet-style [state hold] +(defn switch-bullet-style + [state hold] {:width 20 :height 20 :border-radius 10 @@ -26,7 +28,8 @@ :shadow-color (:shadow-01 @colors/theme) :shadow-offset {:width 0 :height 4}}) -(defn radio-style [state disabled] +(defn radio-style + [state disabled] {:width 20 :height 20 :border-radius 10 @@ -37,7 +40,8 @@ (:interactive-04 @colors/theme) (:interactive-01 @colors/theme)))}) -(defn radio-bullet-style [state hold] +(defn radio-bullet-style + [state hold] {:width 12 :height 12 :border-radius 6 @@ -50,7 +54,8 @@ :shadow-color (:shadow-01 @colors/theme) :shadow-offset {:width 0 :height 4}}) -(defn animated-checkbox-style [state disabled] +(defn animated-checkbox-style + [state disabled] {:width 18 :height 18 :border-radius 4 @@ -62,7 +67,8 @@ (:interactive-04 @colors/theme) (:interactive-01 @colors/theme)))}) -(defn checkbox-style [value disabled] +(defn checkbox-style + [value disabled] {:width 18 :height 18 :border-radius 4 @@ -74,9 +80,11 @@ (:interactive-01 @colors/theme)) (:ui-01 @colors/theme))}) -(defn animated-check-icon-style [state hold] - {:opacity (animated/mix hold 1 0.6) - :transform [{:scale (animated/mix state 0.0001 1)}]}) +(defn animated-check-icon-style + [state hold] + {:opacity (animated/mix hold 1 0.6) + :transform [{:scale (animated/mix state 0.0001 1)}]}) -(defn check-icon-style [value] +(defn check-icon-style + [value] {:opacity (if value 1 0)}) diff --git a/src/quo/components/controls/view.cljs b/src/quo/components/controls/view.cljs index e7ad70cafe..6c2e525fb7 100644 --- a/src/quo/components/controls/view.cljs +++ b/src/quo/components/controls/view.cljs @@ -1,34 +1,37 @@ (ns quo.components.controls.view - (:require [reagent.core :as reagent] - [cljs-bean.core :as bean] + (:require [cljs-bean.core :as bean] + [quo.animated :as animated] + [quo.components.controls.styles :as styles] + [quo.design-system.colors :as colors] + [quo.gesture-handler :as gh] [quo.react :as react] [quo.react-native :as rn] - [quo.animated :as animated] - [quo.gesture-handler :as gh] - [quo.design-system.colors :as colors] - [quo.components.controls.styles :as styles] + [reagent.core :as reagent] [status-im.ui.components.icons.icons :as icons])) -(defn control-builder [component] +(defn control-builder + [component] (fn [props] (let [{:keys [value onChange disabled]} (bean/bean props) - state (animated/use-value 0) - tap-state (animated/use-value (:undetermined gh/states)) - tap-handler (animated/on-gesture {:state tap-state}) - hold (react/use-memo - (fn [] - (animated/with-timing-transition - (animated/eq tap-state (:began gh/states)) - {})) - []) - transition (react/use-memo - (fn [] - (animated/with-spring-transition state (:lazy animated/springs))) - []) - press-end (fn [] - (when (and (not disabled) onChange) - (onChange (not value))))] + state (animated/use-value 0) + tap-state (animated/use-value (:undetermined gh/states)) + tap-handler (animated/on-gesture {:state tap-state}) + hold (react/use-memo + (fn [] + (animated/with-timing-transition + (animated/eq tap-state (:began gh/states)) + {})) + []) + transition (react/use-memo + (fn [] + (animated/with-spring-transition state + (:lazy + animated/springs))) + []) + press-end (fn [] + (when (and (not disabled) onChange) + (onChange (not value))))] (animated/code! (fn [] (animated/cond* (animated/eq tap-state (:end gh/states)) @@ -41,38 +44,47 @@ (animated/set state (if (true? value) 1 0))) [value]) (reagent/as-element - [gh/tap-gesture-handler (merge tap-handler - {:shouldCancelWhenOutside true - :enabled (boolean (and onChange (not disabled)))}) + [gh/tap-gesture-handler + (merge tap-handler + {:shouldCancelWhenOutside true + :enabled (boolean (and onChange (not disabled)))}) [animated/view - [component {:transition transition - :hold hold - :disabled disabled}]]])))) + [component + {:transition transition + :hold hold + :disabled disabled}]]])))) -(defn switch-view [{:keys [transition hold disabled value]}] - [animated/view {:style (styles/switch-style transition disabled) - :accessibility-label (str "switch-" (if value "on" "off")) - :accessibility-role :switch} +(defn switch-view + [{:keys [transition hold disabled value]}] + [animated/view + {:style (styles/switch-style transition disabled) + :accessibility-label (str "switch-" (if value "on" "off")) + :accessibility-role :switch} [animated/view {:style (styles/switch-bullet-style transition hold)}]]) -(defn radio-view [{:keys [transition hold disabled value]}] - [animated/view {:style (styles/radio-style transition disabled) - :accessibility-label (str "radio-" (if value "on" "off")) - :accessibility-role :radio} +(defn radio-view + [{:keys [transition hold disabled value]}] + [animated/view + {:style (styles/radio-style transition disabled) + :accessibility-label (str "radio-" (if value "on" "off")) + :accessibility-role :radio} [animated/view {:style (styles/radio-bullet-style transition hold)}]]) -(defn checkbox-view [props] +(defn checkbox-view + [props] (let [{:keys [value onChange disabled]} (bean/bean props)] (reagent/as-element [rn/touchable-without-feedback {:on-press (when (and onChange (not disabled)) onChange)} - [rn/view {:style (styles/checkbox-style value disabled) - :accessibility-label (str "checkbox-" (if value "on" "off")) - :accessibility-role :checkbox} + [rn/view + {:style (styles/checkbox-style value disabled) + :accessibility-label (str "checkbox-" (if value "on" "off")) + :accessibility-role :checkbox} [rn/view {:style (styles/check-icon-style value)} [icons/tiny-icon :tiny-icons/tiny-check {:color colors/white}]]]]))) -(defn animated-checkbox-view [{:keys [transition hold disabled value]}] +(defn animated-checkbox-view + [{:keys [transition hold disabled value]}] [animated/view {:style (styles/animated-checkbox-style transition disabled) :accessibility-label (str "checkbox-" (if value "on" "off")) diff --git a/src/quo/components/header.cljs b/src/quo/components/header.cljs index 94469d9dcb..eac11ec57c 100644 --- a/src/quo/components/header.cljs +++ b/src/quo/components/header.cljs @@ -10,7 +10,8 @@ (def header-height 56) -(defn header-wrapper-style [{:keys [height border-bottom background]}] +(defn header-wrapper-style + [{:keys [height border-bottom background]}] (merge (:x-tiny spacing/padding-horizontal) {:background-color (:ui-background @colors/theme) @@ -21,32 +22,37 @@ {:border-bottom-width 1 :border-bottom-color (:ui-01 @colors/theme)}))) -(def absolute-fill {:position :absolute - :top 0 - :bottom 0 - :left 0 - :right 0}) +(def absolute-fill + {:position :absolute + :top 0 + :bottom 0 + :left 0 + :right 0}) -(def content {:flex 1 - :flex-direction :row - :align-items :center - :justify-content :center}) +(def content + {:flex 1 + :flex-direction :row + :align-items :center + :justify-content :center}) -(def left {:position :absolute - :left 0 - :top 0 - :bottom 0 - :justify-content :center - :align-items :flex-start}) +(def left + {:position :absolute + :left 0 + :top 0 + :bottom 0 + :justify-content :center + :align-items :flex-start}) -(def right {:position :absolute - :right 0 - :top 0 - :bottom 0 - :justify-content :center - :align-items :flex-end}) +(def right + {:position :absolute + :right 0 + :top 0 + :bottom 0 + :justify-content :center + :align-items :flex-end}) -(defn title-style [{:keys [left right]} title-align] +(defn title-style + [{:keys [left right]} title-align] (merge absolute-fill (case title-align @@ -59,66 +65,75 @@ (def header-actions-style (merge - {:flex 1 - :flex-direction :row - :align-items :center - :justify-content :center} + {:flex 1 + :flex-direction :row + :align-items :center + :justify-content :center} (:x-tiny spacing/padding-horizontal))) (def header-action-placeholder {:width (:base spacing/spacing)}) -(def element {:align-items :center - :justify-content :center - :flex 1}) +(def element + {:align-items :center + :justify-content :center + :flex 1}) -(defn header-action [{:keys [icon label on-press disabled accessibility-label]}] - [button/button (merge {:on-press on-press - :disabled disabled} - (cond - icon {:type :icon - :theme :icon} - label {:type :secondary}) - (when accessibility-label - {:accessibility-label accessibility-label})) +(defn header-action + [{:keys [icon label on-press disabled accessibility-label]}] + [button/button + (merge {:on-press on-press + :disabled disabled} + (cond + icon {:type :icon + :theme :icon} + label {:type :secondary}) + (when accessibility-label + {:accessibility-label accessibility-label})) (cond icon icon label label)]) -(defn header-actions [{:keys [accessories component]}] +(defn header-actions + [{:keys [accessories component]}] [rn/view {:style element} (cond (seq accessories) (into [rn/view {:style header-actions-style}] (map header-action accessories)) - component component + component component :else [rn/view {:style header-action-placeholder}])]) -(defn header-title [{:keys [title subtitle component title-align]}] +(defn header-title + [{:keys [title subtitle component title-align]}] [:<> (cond - component component + component component (and title subtitle) [:<> - [text/text {:weight :medium - :number-of-lines 1} + [text/text + {:weight :medium + :number-of-lines 1} title] - [text/text {:weight :regular - :color :secondary - :number-of-lines 1} + [text/text + {:weight :regular + :color :secondary + :number-of-lines 1} subtitle]] - title [text/text {:weight :bold - :number-of-lines 0 - :align title-align - :size :large} - title])]) + title [text/text + {:weight :bold + :number-of-lines 0 + :align title-align + :size :large} + title])]) -(defn header [{:keys [left-width right-width]}] +(defn header + [{:keys [left-width right-width]}] (let [layout (reagent/atom {:left {:width (or left-width 8) :height header-height} :right {:width (or right-width 8) @@ -130,46 +145,61 @@ (let [width (oget evt "nativeEvent" "layout" "width") height (oget evt "nativeEvent" "layout" "height")] (when get-layout - (get-layout el {:width width - :height height})) - (swap! layout assoc el {:width width - :height height}))))] - (fn [{:keys [left-accessories left-component border-bottom - right-accessories right-component insets get-layout - title subtitle title-component style title-align - background] - :or {title-align :center - border-bottom true}}] + (get-layout el + {:width width + :height height})) + (swap! layout assoc + el + {:width width + :height height}))))] + (fn + [{:keys [left-accessories left-component border-bottom + right-accessories right-component insets get-layout + title subtitle title-component style title-align + background] + :or {title-align :center + border-bottom true}}] (let [status-bar-height (get insets :top 0) height (+ header-height status-bar-height)] - [animated/view {:style (header-wrapper-style {:height height - :background background - :border-bottom border-bottom})} - [rn/view {:pointer-events :box-none - :height status-bar-height}] - [rn/view {:style (merge {:height header-height} - style) - :pointer-events :box-none} - [rn/view {:style absolute-fill - :pointer-events :box-none} - [rn/view {:style content - :pointer-events :box-none} - [rn/view {:style left - :on-layout (handle-layout :left get-layout) - :pointer-events :box-none} - [header-actions {:accessories left-accessories - :component left-component}]] + [animated/view + {:style (header-wrapper-style {:height height + :background background + :border-bottom border-bottom})} + [rn/view + {:pointer-events :box-none + :height status-bar-height}] + [rn/view + {:style (merge {:height header-height} + style) + :pointer-events :box-none} + [rn/view + {:style absolute-fill + :pointer-events :box-none} + [rn/view + {:style content + :pointer-events :box-none} + [rn/view + {:style left + :on-layout (handle-layout :left get-layout) + :pointer-events :box-none} + [header-actions + {:accessories left-accessories + :component left-component}]] - [rn/view {:style (title-style @layout title-align) - :on-layout (handle-layout :title get-layout) - :pointer-events :box-none} - [header-title {:title title - :subtitle subtitle - :title-align title-align - :component title-component}]] + [rn/view + {:style (title-style @layout title-align) + :on-layout (handle-layout :title get-layout) + :pointer-events :box-none} + [header-title + {:title title + :subtitle subtitle + :title-align title-align + :component title-component}]] - [rn/view {:style right - :on-layout (handle-layout :right get-layout) - :pointer-events :box-none} - [header-actions {:accessories right-accessories - :component right-component}]]]]]])))) + [rn/view + {:style right + :on-layout (handle-layout :right get-layout) + :pointer-events :box-none} + [header-actions + {:accessories right-accessories + :component right-component}]]]]]])))) diff --git a/src/quo/components/list/footer.cljs b/src/quo/components/list/footer.cljs index dd3058ad94..9b5d89f132 100644 --- a/src/quo/components/list/footer.cljs +++ b/src/quo/components/list/footer.cljs @@ -1,14 +1,17 @@ (ns quo.components.list.footer - (:require [quo.react-native :as rn] + (:require [quo.components.text :as text] [quo.design-system.spacing :as spacing] - [quo.components.text :as text] + [quo.react-native :as rn] [reagent.core :as reagent])) -(defn footer [] - (let [this (reagent/current-component) +(defn footer + [] + (let [this (reagent/current-component) {:keys [color] - :or {color :secondary}} (reagent/props this)] - [rn/view {:style (merge (:base spacing/padding-horizontal) - (:small spacing/padding-vertical))} + :or {color :secondary}} + (reagent/props this)] + [rn/view + {:style (merge (:base spacing/padding-horizontal) + (:small spacing/padding-vertical))} (into [text/text {:color color}] (reagent/children this))])) diff --git a/src/quo/components/list/header.cljs b/src/quo/components/list/header.cljs index 05f8a758f1..773aba4b53 100644 --- a/src/quo/components/list/header.cljs +++ b/src/quo/components/list/header.cljs @@ -1,15 +1,19 @@ (ns quo.components.list.header - (:require [reagent.core :as reagent] - [quo.react-native :as rn] + (:require [quo.components.text :as text] [quo.design-system.spacing :as spacing] - [quo.components.text :as text])) + [quo.react-native :as rn] + [reagent.core :as reagent])) -(defn header [] - (let [this (reagent/current-component) +(defn header + [] + (let [this (reagent/current-component) {:keys [color] - :or {color :secondary}} (reagent/props this)] - [rn/view {:style (merge (:base spacing/padding-horizontal) - (:x-tiny spacing/padding-vertical))} - (into [text/text {:color color - :style {:margin-top 10}}] + :or {color :secondary}} + (reagent/props this)] + [rn/view + {:style (merge (:base spacing/padding-horizontal) + (:x-tiny spacing/padding-vertical))} + (into [text/text + {:color color + :style {:margin-top 10}}] (reagent/children this))])) diff --git a/src/quo/components/list/index.cljs b/src/quo/components/list/index.cljs index 90d99d6da7..d45fdcfcdd 100644 --- a/src/quo/components/list/index.cljs +++ b/src/quo/components/list/index.cljs @@ -1,16 +1,18 @@ (ns quo.components.list.index - (:require [quo.react-native :as rn] - [quo.components.text :as text] - [quo.design-system.colors :as colors])) + (:require [quo.components.text :as text] + [quo.design-system.colors :as colors] + [quo.react-native :as rn])) -(defn index [{:keys [title]}] +(defn index + [{:keys [title]}] [rn/view {:style {:padding-right 16}} - [rn/view {:style {:border-top-width 1 - :border-bottom-width 1 - :border-right-width 1 - :border-color (colors/get-color :border-01) - :padding-vertical 3 - :padding-horizontal 16 - :border-top-right-radius 16 - :border-bottom-right-radius 16}} + [rn/view + {:style {:border-top-width 1 + :border-bottom-width 1 + :border-right-width 1 + :border-color (colors/get-color :border-01) + :padding-vertical 3 + :padding-horizontal 16 + :border-top-right-radius 16 + :border-bottom-right-radius 16}} [text/text title]]]) diff --git a/src/quo/components/list/item.cljs b/src/quo/components/list/item.cljs index b8d43d6104..bbefa99937 100644 --- a/src/quo/components/list/item.cljs +++ b/src/quo/components/list/item.cljs @@ -1,28 +1,29 @@ (ns quo.components.list.item - (:require [quo.react-native :as rn] - [quo.platform :as platform] - [quo.haptic :as haptic] - [quo.gesture-handler :as gh] - [quo.design-system.spacing :as spacing] - [quo.design-system.colors :as colors] - [quo.components.text :as text] + (:require [quo.components.animated.pressable :as animated] [quo.components.controls.view :as controls] + [quo.components.text :as text] [quo.components.tooltip :as tooltip] - [status-im.ui.components.icons.icons :as icons] - [quo.components.animated.pressable :as animated])) + [quo.design-system.colors :as colors] + [quo.design-system.spacing :as spacing] + [quo.gesture-handler :as gh] + [quo.haptic :as haptic] + [quo.platform :as platform] + [quo.react-native :as rn] + [status-im.ui.components.icons.icons :as icons])) -(defn themes [theme] +(defn themes + [theme] (case theme - :main {:icon-color (:icon-04 @colors/theme) - :icon-bg-color (:interactive-02 @colors/theme) - :active-background (:interactive-02 @colors/theme) - :passive-background (:ui-background @colors/theme) - :text-color (:text-01 @colors/theme)} - :accent {:icon-color (:icon-04 @colors/theme) - :icon-bg-color (:interactive-02 @colors/theme) - :active-background (:interactive-02 @colors/theme) - :passive-background (:ui-background @colors/theme) - :text-color (:text-04 @colors/theme)} + :main {:icon-color (:icon-04 @colors/theme) + :icon-bg-color (:interactive-02 @colors/theme) + :active-background (:interactive-02 @colors/theme) + :passive-background (:ui-background @colors/theme) + :text-color (:text-01 @colors/theme)} + :accent {:icon-color (:icon-04 @colors/theme) + :icon-bg-color (:interactive-02 @colors/theme) + :active-background (:interactive-02 @colors/theme) + :passive-background (:ui-background @colors/theme) + :text-color (:text-04 @colors/theme)} :negative {:icon-color (:negative-01 @colors/theme) :icon-bg-color (:negative-02 @colors/theme) :active-background (:negative-02 @colors/theme) @@ -39,28 +40,34 @@ :passive-background (:ui-background @colors/theme) :text-color (:text-02 @colors/theme)})) -(defn size->icon-size [size] +(defn size->icon-size + [size] (case size :small 36 40)) -(defn size->container-size [size] +(defn size->container-size + [size] (case size :small 52 64)) -(defn size->single-title-size [size] +(defn size->single-title-size + [size] (case size :small :base :large)) -(defn container [{:keys [size container-style]} & children] - (into [rn/view {:style (merge (:tiny spacing/padding-horizontal) - {:min-height (size->container-size size) - :padding-vertical 8 - :flex-direction :row - :align-items :center - :justify-content :space-between} container-style)}] +(defn container + [{:keys [size container-style]} & children] + (into [rn/view + {:style (merge (:tiny spacing/padding-horizontal) + {:min-height (size->container-size size) + :padding-vertical 8 + :flex-direction :row + :align-items :center + :justify-content :space-between} + container-style)}] children)) (defn icon-column @@ -72,103 +79,118 @@ (vector? icon) icon (keyword? icon) - [rn/view {:style {:width icon-size - :height icon-size - :align-items :center - :justify-content :center - :border-radius (/ icon-size 2) - :background-color icon-bg-color}} + [rn/view + {:style {:width icon-size + :height icon-size + :align-items :center + :justify-content :center + :border-radius (/ icon-size 2) + :background-color icon-bg-color}} [icons/icon icon {:color icon-color}]])]))) (defn title-column [{:keys [title text-color subtitle subtitle-max-lines subtitle-secondary title-accessibility-label size text-size title-text-weight right-side-present?]}] - [rn/view {:style (merge (:tiny spacing/padding-horizontal) - ;; make left-side title grow if nothing is present on right-side - (when-not right-side-present? - {:flex 1 - :justify-content :center}))} + [rn/view + {:style (merge (:tiny spacing/padding-horizontal) + ;; make left-side title grow if nothing is present on right-side + (when-not right-side-present? + {:flex 1 + :justify-content :center}))} (cond (and title subtitle) [:<> - ;; FIXME(Ferossgp): ReactNative 63 will support view inside text on andrid, remove thess if when migrating + ;; FIXME(Ferossgp): ReactNative 63 will support view inside text on andrid, remove thess if when + ;; migrating (if (string? title) - [text/text {:weight (or title-text-weight :medium) - :style {:color text-color} - :accessibility-label title-accessibility-label - :ellipsize-mode :tail - :number-of-lines 1 - :size text-size} + [text/text + {:weight (or title-text-weight :medium) + :style {:color text-color} + :accessibility-label title-accessibility-label + :ellipsize-mode :tail + :number-of-lines 1 + :size text-size} title] title) (if (string? subtitle-secondary) [rn/view {:flex-direction :row} - [text/text {:style {:max-width "56.5%"} - :weight :regular - :color :secondary - :ellipsize-mode :tail - :number-of-lines subtitle-max-lines - :size text-size} + [text/text + {:style {:max-width "56.5%"} + :weight :regular + :color :secondary + :ellipsize-mode :tail + :number-of-lines subtitle-max-lines + :size text-size} subtitle] - [text/text {:style {:width "7%" :text-align :center} - :weight :regular - :color :secondary - :ellipsize-mode :middle - :number-of-lines subtitle-max-lines - :size text-size} + [text/text + {:style {:width "7%" :text-align :center} + :weight :regular + :color :secondary + :ellipsize-mode :middle + :number-of-lines subtitle-max-lines + :size text-size} "•"] - [text/text {:style {:max-width "36.5%"} - :weight :regular - :color :secondary - :ellipsize-mode :middle - :number-of-lines subtitle-max-lines - :size text-size} + [text/text + {:style {:max-width "36.5%"} + :weight :regular + :color :secondary + :ellipsize-mode :middle + :number-of-lines subtitle-max-lines + :size text-size} subtitle-secondary]] (if (string? subtitle) - [text/text {:weight :regular - :color :secondary - :ellipsize-mode :tail - :number-of-lines subtitle-max-lines - :size text-size} + [text/text + {:weight :regular + :color :secondary + :ellipsize-mode :tail + :number-of-lines subtitle-max-lines + :size text-size} subtitle] subtitle))] title (if (string? title) - [text/text {:weight (or title-text-weight :regular) - :number-of-lines 1 - :style {:color text-color} - :title-accessibility-label title-accessibility-label - :ellipsize-mode :tail - :size (or text-size (size->single-title-size size))} + [text/text + {:weight (or title-text-weight :regular) + :number-of-lines 1 + :style {:color text-color} + :title-accessibility-label title-accessibility-label + :ellipsize-mode :tail + :size (or text-size (size->single-title-size size))} title] title))]) -(defn left-side [props] - [rn/view {:style {:flex-direction :row - ;; Occupy only content width, never grow, but shrink if need be - :flex-grow 0 - :flex-shrink 1 - :padding-right 16 - :align-items (or (:left-side-alignment props) :center)}} +(defn left-side + [props] + [rn/view + {:style {:flex-direction :row + ;; Occupy only content width, never grow, but shrink if need be + :flex-grow 0 + :flex-shrink 1 + :padding-right 16 + :align-items (or (:left-side-alignment props) :center)}} [icon-column props] [title-column props]]) -(defn right-side [{:keys [chevron active disabled accessory accessory-text accessory-style animated-accessory?]}] +(defn right-side + [{:keys [chevron active disabled accessory accessory-text accessory-style animated-accessory?]}] (when (or chevron accessory) - [rn/view {:style (merge {:align-items :center - :justify-content :flex-end - :flex-direction :row - ;; Grow to occupy full space, shrink when need be, but always maitaining 16px left gutter - :flex-grow 1 - :flex-shrink 0 - :margin-left 16 - ;; When the left-side leaves no room for right-side, the rendered element is pushed out. A flex-basis ensures that there is some room reserved. - ;; The number 80px was determined by trial and error. - :flex-basis 80} - accessory-style)} + [rn/view + {:style (merge {:align-items :center + :justify-content :flex-end + :flex-direction :row + ;; Grow to occupy full space, shrink when need be, but always maitaining 16px left + ;; gutter + :flex-grow 1 + :flex-shrink 0 + :margin-left 16 + ;; When the left-side leaves no room for right-side, the rendered element is pushed + ;; out. A flex-basis ensures that there is some room reserved. + ;; The number 80px was determined by trial and error. + :flex-basis 80} + accessory-style)} [rn/view {:style (:tiny spacing/padding-horizontal)} (case accessory :radio [controls/radio {:value active :disabled disabled}] @@ -177,25 +199,28 @@ controls/checkbox) {:value active :disabled disabled}] :switch [controls/switch {:value active :disabled disabled}] - :text [text/text {:color :secondary - :ellipsize-mode :middle - :number-of-lines 1} + :text [text/text + {:color :secondary + :ellipsize-mode :middle + :number-of-lines 1} accessory-text] accessory)] (when (and chevron platform/ios?) [rn/view {:style {:padding-right (:tiny spacing/spacing)}} - [icons/icon :main-icons/next {:container-style {:opacity 0.4 - :align-items :center - :justify-content :center} - :resize-mode :center - :color (:icon-02 @colors/theme)}]])])) + [icons/icon :main-icons/next + {:container-style {:opacity 0.4 + :align-items :center + :justify-content :center} + :resize-mode :center + :color (:icon-02 @colors/theme)}]])])) (defn list-item [{:keys [theme accessory disabled subtitle-max-lines icon icon-container-style left-side-alignment icon-color icon-bg-color title subtitle subtitle-secondary active on-press on-long-press chevron size text-size accessory-text accessibility-label title-accessibility-label accessory-style - haptic-feedback haptic-type error animated animated-accessory? title-text-weight container-style + haptic-feedback haptic-type error animated animated-accessory? title-text-weight + container-style active-background-enabled background-color] :or {subtitle-max-lines 1 theme :main @@ -203,26 +228,29 @@ animated platform/ios? active-background-enabled true haptic-type :selection}}] - (let [theme (if disabled :disabled theme) + (let [theme (if disabled :disabled theme) {:keys [text-color active-background passive-background]} (themes theme) - icon-color (or icon-color (:icon-color (themes theme))) - icon-bg-color (or icon-bg-color (:icon-bg-color (themes theme))) - optional-haptic (fn [] - (when haptic-feedback - (haptic/trigger haptic-type))) - component (cond - (and (not on-press) - (not on-long-press)) - rn/view - animated animated/pressable - :else gh/touchable-highlight)] - [rn/view {:background-color (cond (not= background-color nil) - background-color - (and (= accessory :radio) active) - active-background - :else - passive-background)} + icon-color (or icon-color + (:icon-color (themes theme))) + icon-bg-color (or icon-bg-color + (:icon-bg-color (themes theme))) + optional-haptic (fn [] + (when haptic-feedback + (haptic/trigger haptic-type))) + component (cond + (and (not on-press) + (not on-long-press)) + rn/view + animated animated/pressable + :else gh/touchable-highlight)] + [rn/view + {:background-color (cond (not= background-color nil) + background-color + (and (= accessory :radio) active) + active-background + :else + passive-background)} [component (merge {:type :list-item :disabled disabled @@ -237,35 +265,39 @@ (optional-haptic) (on-long-press))})) [container {:size size :container-style container-style} - [left-side {:icon-color icon-color - :text-color (if on-press - text-color - (:text-color (themes :main))) - :left-side-alignment left-side-alignment - :icon-bg-color icon-bg-color - :title-accessibility-label title-accessibility-label - :icon icon - :icon-container-style icon-container-style - :title title - :title-text-weight title-text-weight - :size size - :text-size text-size - :subtitle subtitle - :subtitle-max-lines subtitle-max-lines - :subtitle-secondary subtitle-secondary - :right-side-present? (or accessory chevron)}] - [right-side {:chevron chevron - :active active - :disabled disabled - :on-press on-press - :accessory-text accessory-text - :animated-accessory? animated-accessory? - :accessory-style accessory-style - :accessory accessory}]]] + [left-side + {:icon-color icon-color + :text-color (if on-press + text-color + (:text-color (themes :main))) + :left-side-alignment left-side-alignment + :icon-bg-color icon-bg-color + :title-accessibility-label title-accessibility-label + :icon icon + :icon-container-style icon-container-style + :title title + :title-text-weight title-text-weight + :size size + :text-size text-size + :subtitle subtitle + :subtitle-max-lines subtitle-max-lines + :subtitle-secondary subtitle-secondary + :right-side-present? (or accessory chevron)}] + [right-side + {:chevron chevron + :active active + :disabled disabled + :on-press on-press + :accessory-text accessory-text + :animated-accessory? animated-accessory? + :accessory-style accessory-style + :accessory accessory}]]] (when error - [tooltip/tooltip (merge {:bottom-value 0} - (when accessibility-label - {:accessibility-label (str (name accessibility-label) "-error")})) - [text/text {:color :negative - :size :small} + [tooltip/tooltip + (merge {:bottom-value 0} + (when accessibility-label + {:accessibility-label (str (name accessibility-label) "-error")})) + [text/text + {:color :negative + :size :small} error]])])) diff --git a/src/quo/components/safe_area.cljs b/src/quo/components/safe_area.cljs index a1fa415ec5..03e808cb87 100644 --- a/src/quo/components/safe_area.cljs +++ b/src/quo/components/safe_area.cljs @@ -1,19 +1,21 @@ (ns quo.components.safe-area - (:require ["react-native-safe-area-context" :as safe-area-context - :refer (SafeAreaView SafeAreaProvider SafeAreaInsetsContext useSafeAreaInsets)] + (:require ["react-native-safe-area-context" :as safe-area-context :refer + (SafeAreaView SafeAreaProvider SafeAreaInsetsContext useSafeAreaInsets)] [reagent.core :as reagent])) (def provider (reagent/adapt-react-class SafeAreaProvider)) (def ^:private consumer-raw (reagent/adapt-react-class (.-Consumer ^js SafeAreaInsetsContext))) (def view (reagent/adapt-react-class SafeAreaView)) -(defn consumer [component] +(defn consumer + [component] [consumer-raw (fn [insets] (reagent/as-element [component (js->clj insets :keywordize-keys true)]))]) -(defn use-safe-area [] +(defn use-safe-area + [] (let [insets (useSafeAreaInsets)] {:top (.-top ^js insets) :bottom (.-bottom ^js insets) diff --git a/src/quo/components/separator.cljs b/src/quo/components/separator.cljs index 5e7731a750..f175666641 100644 --- a/src/quo/components/separator.cljs +++ b/src/quo/components/separator.cljs @@ -1,8 +1,9 @@ (ns quo.components.separator - (:require [quo.react-native :as react] - [quo.design-system.colors :as colors])) + (:require [quo.design-system.colors :as colors] + [quo.react-native :as react])) -(defn separator [{:keys [color style]}] +(defn separator + [{:keys [color style]}] [react/view {:style (merge diff --git a/src/quo/components/text.cljs b/src/quo/components/text.cljs index 1434a0b7eb..47e893916e 100644 --- a/src/quo/components/text.cljs +++ b/src/quo/components/text.cljs @@ -5,7 +5,8 @@ [quo.react-native :as rn] [reagent.core :as reagent])) -(defn text-style [{:keys [size align weight monospace color style]}] +(defn text-style + [{:keys [size align weight monospace color style]}] ;; NOTE(Ferossgo): or in destructoring will keep nil as a value (merge (if monospace ;; TODO(Ferossgp): Add all weights for monospace @@ -38,12 +39,15 @@ {:text-align (or align :auto)} style)) -(defn text [] +(defn text + [] (let [this (reagent/current-component) props (reagent/props this) component (if (:animated? props) animated/text rn/text)] - (into [component (merge {:style (text-style props)} - (dissoc props - :style :size :weight :color - :align :animated?))] + (into [component + (merge {:style (text-style props)} + (dissoc props + :style :size + :weight :color + :align :animated?))] (reagent/children this)))) diff --git a/src/quo/components/text_input.cljs b/src/quo/components/text_input.cljs index 8af35f6291..ebfe48cce5 100644 --- a/src/quo/components/text_input.cljs +++ b/src/quo/components/text_input.cljs @@ -1,16 +1,15 @@ (ns quo.components.text-input (:require [clojure.spec.alpha :as s] - [reagent.core :as reagent] [oops.core :refer [ocall]] - [quo.react-native :as rn] - ;; TODO(Ferossgp): Move icon component to lib - [status-im.ui.components.icons.icons :as icons] + [quo.components.text :as text] [quo.components.tooltip :as tooltip] - [quo.platform :as platform] - [quo.design-system.typography :as typography] - [quo.design-system.spacing :as spacing] [quo.design-system.colors :as colors] - [quo.components.text :as text])) + [quo.design-system.spacing :as spacing] + [quo.design-system.typography :as typography] + [quo.platform :as platform] + [quo.react-native :as rn] ;; TODO(Ferossgp): Move icon component to lib + [reagent.core :as reagent] + [status-im.ui.components.icons.icons :as icons])) ;; NOTE(Ferossgp): Refactor with hooks when available ;; We track all currently mounted text input refs @@ -22,19 +21,26 @@ (s/def ::multiline boolean?) (s/def ::secure-text-entry boolean?) (s/def ::show-cancel boolean?) -(s/def ::label (s/nilable (s/or :string string? :component vector?))) +(s/def ::label + (s/nilable (s/or :string string? + :component vector?))) (s/def ::cancel-label (s/nilable string?)) (s/def ::default-value (s/nilable string?)) (s/def ::placeholder (s/nilable string?)) -(s/def ::keyboard-type (s/nilable (s/or :string string? :keyword keyword?))) ; TODO: make set -(s/def ::accessibility-label (s/nilable (s/or :string string? :keyword keyword?))) +(s/def ::keyboard-type + (s/nilable (s/or :string string? + :keyword keyword?))) ; TODO: make set +(s/def ::accessibility-label + (s/nilable (s/or :string string? + :keyword keyword?))) (s/def ::on-focus fn?) (s/def ::on-blur fn?) (s/def ::on-press fn?) -(s/def ::accessory (s/keys :opt-un [::on-press - ::icon - ::component])) +(s/def ::accessory + (s/keys :opt-un [::on-press + ::icon + ::component])) (s/def ::after (s/nilable ::accessory)) (s/def ::before (s/nilable ::accessory)) @@ -42,25 +48,27 @@ (s/def ::input-style ::style) (s/def ::container-style ::style) -(s/def ::text-input (s/keys :opt-un - [::label - ::multiline - ::error - ::style - ::input-style - ::keyboard-type - ::before - ::after - ::cancel-label - ::on-focus - ::on-blur - ::container-style - ::show-cancel - ::accessibility-label - ::bottom-value - ::secure-text-entry])) +(s/def ::text-input + (s/keys :opt-un + [::label + ::multiline + ::error + ::style + ::input-style + ::keyboard-type + ::before + ::after + ::cancel-label + ::on-focus + ::on-blur + ::container-style + ::show-cancel + ::accessibility-label + ::bottom-value + ::secure-text-entry])) -(defn check-spec [spec prop] +(defn check-spec + [spec prop] (if (s/valid? spec prop) true (do @@ -70,14 +78,17 @@ (def height 44) ; 22 line-height + 11*2 vertical padding (def multiline-height 88) ; 3 * 22 three line-height + 11* vertical padding -(defn label-style [] +(defn label-style + [] {:margin-bottom (:tiny spacing/spacing)}) -(defn text-input-row-style [] +(defn text-input-row-style + [] {:flex-direction :row :align-items :center}) -(defn text-input-view-style [style] +(defn text-input-view-style + [style] (merge {:border-radius 8 :flex-direction :row :flex 1 @@ -85,7 +96,8 @@ :background-color (:ui-01 @colors/theme)} style)) -(defn text-input-style [multiline input-style monospace before after] +(defn text-input-style + [multiline input-style monospace before after] (merge (if monospace typography/monospace typography/font-regular) @@ -107,66 +119,76 @@ :height multiline-height}) input-style)) -(defn cancel-style [] +(defn cancel-style + [] {:margin-left (:tiny spacing/spacing) :padding-left (:tiny spacing/spacing) :justify-content :center :align-self :stretch}) -(defn accessory-style [] +(defn accessory-style + [] (merge (:base spacing/padding-horizontal) {:flex 1 :justify-content :center})) -(defn accessory-element [{:keys [icon component icon-opts style accessibility-label on-press]}] +(defn accessory-element + [{:keys [icon component icon-opts style accessibility-label on-press]}] (let [el (if on-press rn/touchable-opacity rn/view)] - [el (merge {:style {:align-self :stretch}} - (when on-press - {:on-press on-press})) - [rn/view (merge {:style (merge (accessory-style) - style)} - (when accessibility-label - {:accessibility-label accessibility-label})) + [el + (merge {:style {:align-self :stretch}} + (when on-press + {:on-press on-press})) + [rn/view + (merge {:style (merge (accessory-style) + style)} + (when accessibility-label + {:accessibility-label accessibility-label})) (cond icon - [icons/icon icon (merge {:color (:icon-01 @colors/theme)} - icon-opts)] + [icons/icon icon + (merge {:color (:icon-01 @colors/theme)} + icon-opts)] component component :else nil)]])) -(defn text-input-raw [] +(defn text-input-raw + [] (let [focused (reagent/atom nil) visible (reagent/atom false) ref (atom nil) blur (fn [] - (some-> @ref (ocall "blur")))] - (fn [{:keys [label multiline error style input-style keyboard-type before after - cancel-label on-focus on-blur show-cancel accessibility-label - bottom-value secure-text-entry container-style get-ref on-cancel - monospace auto-complete-type auto-correct] - :or {cancel-label "Cancel"} - :as props}] + (some-> @ref + (ocall "blur")))] + (fn + [{:keys [label multiline error style input-style keyboard-type before after + cancel-label on-focus on-blur show-cancel accessibility-label + bottom-value secure-text-entry container-style get-ref on-cancel + monospace auto-complete-type auto-correct] + :or {cancel-label "Cancel"} + :as props}] {:pre [(check-spec ::text-input props)]} - (let [show-cancel (if (nil? show-cancel) - ;; Enabled by default on iOs and disabled on Android - platform/ios? - show-cancel) - after (cond - (and secure-text-entry @visible) - {:icon :main-icons/hide - :on-press #(reset! visible false)} + (let [show-cancel (if (nil? show-cancel) + ;; Enabled by default on iOs and disabled on Android + platform/ios? + show-cancel) + after (cond + (and secure-text-entry @visible) + {:icon :main-icons/hide + :on-press #(reset! visible false)} - (and secure-text-entry (not @visible)) - {:icon :main-icons/show - :on-press #(reset! visible true)} + (and secure-text-entry (not @visible)) + {:icon :main-icons/show + :on-press #(reset! visible true)} - :else after) - secure (boolean (and secure-text-entry (not @visible))) ; must be a boolean to work on iOS + :else after) + secure (boolean (and secure-text-entry (not @visible))) ; must be a boolean to work on + ; iOS auto-complete (cond (= keyboard-type :visible-password) :off @@ -176,7 +198,9 @@ :else auto-complete-type) - auto-correct (and (not= keyboard-type :visible-password) (not secure-text-entry) auto-correct) + auto-correct (and (not= keyboard-type :visible-password) + (not secure-text-entry) + auto-correct) on-cancel (fn [] (when on-cancel (on-cancel)) @@ -185,8 +209,10 @@ (and platform/ios? (= keyboard-type :visible-password)) :default - ; the correct approach on Android would be keep secure-text-entry on set keyboard type - ; to visible-password. But until https://github.com/facebook/react-native/issues/27946 + ; the correct approach on Android would be keep secure-text-entry on set + ; keyboard type + ; to visible-password. But until + ; https://github.com/facebook/react-native/issues/27946 ; is solved that's the second best way. (and platform/android? secure-text-entry) :default @@ -198,59 +224,71 @@ [text/text {:style (label-style)} label]) [rn/view {:style (text-input-row-style)} - [rn/view {:style (text-input-view-style style) - :important-for-accessibility (if secure-text-entry - :no-hide-descendants - :auto)} + [rn/view + {:style (text-input-view-style style) + :important-for-accessibility (if secure-text-entry + :no-hide-descendants + :auto)} (when before [accessory-element before]) [rn/text-input - (merge {:style (text-input-style multiline input-style monospace before after) - :ref (fn [r] - (reset! ref r) - (when get-ref (get-ref r))) - :placeholder-text-color (:text-02 @colors/theme) - :underline-color-android :transparent - :auto-capitalize :none - :secure-text-entry secure - :auto-correct auto-correct - :auto-complete-type auto-complete - :on-focus (fn [evt] - (when on-focus (on-focus evt)) - (when show-cancel - (rn/configure-next (:ease-in-ease-out rn/layout-animation-presets))) - (reset! focused true)) - :on-blur (fn [evt] - (when on-blur (on-blur evt)) - (when show-cancel - (rn/configure-next (:ease-in-ease-out rn/layout-animation-presets))) - (reset! focused false)) - :keyboard-type keyboard-type} - (when (and platform/ios? (not after)) - {:clear-button-mode :while-editing}) - (dissoc props - :style :keyboard-type :on-focus :on-blur - :secure-text-entry :ref :get-ref :auto-correct :auto-complete-type))] + (merge + {:style (text-input-style multiline input-style monospace before after) + :ref (fn [r] + (reset! ref r) + (when get-ref (get-ref r))) + :placeholder-text-color (:text-02 @colors/theme) + :underline-color-android :transparent + :auto-capitalize :none + :secure-text-entry secure + :auto-correct auto-correct + :auto-complete-type auto-complete + :on-focus (fn [evt] + (when on-focus (on-focus evt)) + (when show-cancel + (rn/configure-next (:ease-in-ease-out + rn/layout-animation-presets))) + (reset! focused true)) + :on-blur (fn [evt] + (when on-blur (on-blur evt)) + (when show-cancel + (rn/configure-next (:ease-in-ease-out + rn/layout-animation-presets))) + (reset! focused false)) + :keyboard-type keyboard-type} + (when (and platform/ios? (not after)) + {:clear-button-mode :while-editing}) + (dissoc props + :style + :keyboard-type :on-focus + :on-blur + :secure-text-entry + :ref :get-ref + :auto-correct :auto-complete-type))] (when after [accessory-element after])] (when (and show-cancel (not multiline) @focused) - [rn/touchable-opacity {:style (cancel-style) - :on-press on-cancel} + [rn/touchable-opacity + {:style (cancel-style) + :on-press on-cancel} [text/text {:color :link} cancel-label]]) (when error - [tooltip/tooltip (merge {:bottom-value (if bottom-value bottom-value 0)} - (when accessibility-label - {:accessibility-label (str (name accessibility-label) "-error")})) - [text/text {:color :negative - :align :center - :size :small} + [tooltip/tooltip + (merge {:bottom-value (if bottom-value bottom-value 0)} + (when accessibility-label + {:accessibility-label (str (name accessibility-label) "-error")})) + [text/text + {:color :negative + :align :center + :size :small} error]])]])))) ;; TODO(Ferossgp): Refactor me when hooks available -(defn text-input [{:keys [preserve-input?] - :as props}] +(defn text-input + [{:keys [preserve-input?] + :as props}] (if preserve-input? [text-input-raw props] (let [id (random-uuid)] @@ -261,10 +299,11 @@ :reagent-render (fn [{:keys [get-ref default-value] :as props}] - [text-input-raw (merge props - {:get-ref (fn [r] - ;; Store input and its defaultValue - ;; one we receive a non-nil ref - (when r - (swap! text-input-refs assoc id {:ref r :value default-value})) - (when get-ref (get-ref r)))})])})))) + [text-input-raw + (merge props + {:get-ref (fn [r] + ;; Store input and its defaultValue + ;; one we receive a non-nil ref + (when r + (swap! text-input-refs assoc id {:ref r :value default-value})) + (when get-ref (get-ref r)))})])})))) diff --git a/src/quo/components/tooltip.cljs b/src/quo/components/tooltip.cljs index 04c906c1ce..19e576379e 100644 --- a/src/quo/components/tooltip.cljs +++ b/src/quo/components/tooltip.cljs @@ -1,17 +1,17 @@ (ns quo.components.tooltip - (:require [reagent.core :as reagent] - [oops.core :refer [oget]] + (:require [oops.core :refer [oget]] [quo.animated :as animated] - [quo.react-native :as rn] [quo.design-system.colors :as colors] [quo.design-system.spacing :as spacing] - [quo.platform :as platform] - ;; FIXME(Ferossgp): Dependecy on status + [quo.platform :as platform] ;; FIXME(Ferossgp): Dependecy on status + [quo.react-native :as rn] + [reagent.core :as reagent] [status-im.ui.components.icons.icons :as icons])) (def ^:private initial-height 22) -(defn tooltip-style [{:keys [bottom-value animation]}] +(defn tooltip-style + [{:keys [bottom-value animation]}] (merge (:base spacing/padding-horizontal) {:position :absolute @@ -22,7 +22,8 @@ :opacity animation :transform [{:translateY (animated/mix animation 10 0)}]})) -(defn container-style [] +(defn container-style + [] {:z-index 2 :align-items :center :shadow-radius 16 @@ -30,43 +31,50 @@ :shadow-color (:shadow-01 @colors/theme) :shadow-offset {:width 0 :height 4}}) -(defn content-style [] +(defn content-style + [] (merge (:base spacing/padding-horizontal) {:padding-vertical 6 :elevation 2 :background-color (:ui-background @colors/theme) :border-radius 8})) -(defn tooltip [] +(defn tooltip + [] (let [layout (reagent/atom {:height initial-height}) animation-v (animated/value 0) animation (animated/with-timing-transition - animation-v - {:easing (:ease-in animated/easings)}) + animation-v + {:easing (:ease-in animated/easings)}) on-layout (fn [evt] (let [width (oget evt "nativeEvent" "layout" "width") height (oget evt "nativeEvent" "layout" "height")] (reset! layout {:width width :height height})))] - (fn [{:keys [bottom-value accessibility-label]} & children] + (fn [{:keys [bottom-value accessibility-label]} & children] [:<> - [animated/code {:exec (animated/cond* (animated/not* animation-v) - (animated/set animation-v 1))}] - [animated/view {:style (tooltip-style {:bottom-value (- (get @layout :height) - bottom-value) - :animation animation}) - :pointer-events :box-none} - [animated/view {:style (container-style) - :pointer-events :box-none} - (into [rn/view {:style (content-style) - :pointer-events :box-none - :accessibility-label accessibility-label - :on-layout on-layout}] + [animated/code + {:exec (animated/cond* (animated/not* animation-v) + (animated/set animation-v 1))}] + [animated/view + {:style (tooltip-style {:bottom-value (- (get @layout :height) + bottom-value) + :animation animation}) + :pointer-events :box-none} + [animated/view + {:style (container-style) + :pointer-events :box-none} + (into [rn/view + {:style (content-style) + :pointer-events :box-none + :accessibility-label accessibility-label + :on-layout on-layout}] children) (when platform/ios? ;; NOTE(Ferossgp): Android does not show elevation for tooltip making it lost on white bg - [icons/icon :icons/tooltip-tip {:width 18 - :height 8 - :container-style {:elevation 3} - :color (:ui-background @colors/theme)}])]]]))) + [icons/icon :icons/tooltip-tip + {:width 18 + :height 8 + :container-style {:elevation 3} + :color (:ui-background @colors/theme)}])]]]))) diff --git a/src/quo/core.cljs b/src/quo/core.cljs index 5b932daf50..985e0fb1af 100644 --- a/src/quo/core.cljs +++ b/src/quo/core.cljs @@ -1,18 +1,18 @@ (ns quo.core (:require [quo.components.animated-header :as animated-header] + [quo.components.bottom-sheet.view :as bottom-sheet] + [quo.components.button.view :as button] + [quo.components.controls.view :as controls] [quo.components.header :as header] + [quo.components.list.footer :as list-footer] + [quo.components.list.header :as list-header] + [quo.components.list.index :as list-index] + [quo.components.list.item :as list-item] [quo.components.safe-area :as safe-area] + [quo.components.separator :as separator] + [quo.components.text :as text] [quo.components.text-input :as text-input] [quo.components.tooltip :as tooltip] - [quo.components.text :as text] - [quo.components.button.view :as button] - [quo.components.list.header :as list-header] - [quo.components.list.footer :as list-footer] - [quo.components.list.item :as list-item] - [quo.components.list.index :as list-index] - [quo.components.controls.view :as controls] - [quo.components.bottom-sheet.view :as bottom-sheet] - [quo.components.separator :as separator] [quo.design-system.colors :as colors])) (def text text/text) diff --git a/src/quo/design_system/colors.cljs b/src/quo/design_system/colors.cljs index 6a2ac73a98..a4e4113625 100644 --- a/src/quo/design_system/colors.cljs +++ b/src/quo/design_system/colors.cljs @@ -1,6 +1,6 @@ (ns quo.design-system.colors - (:require [reagent.core :as reagent] - [clojure.string :as string])) + (:require [clojure.string :as string] + [reagent.core :as reagent])) ;; Colors mapping from figma to code, note that theme is more extended and ;; one can follow the comments from the light theme to choose what to use in a component. @@ -19,14 +19,17 @@ (def light-theme {:positive-01 "rgba(68,208,88,1)" ; Primary Positive, text, icons color - :positive-02 "rgba(78,188,96,0.1)" ; Secondary Positive, Supporting color for success illustrations + :positive-02 "rgba(78,188,96,0.1)" ; Secondary Positive, Supporting color for success + ; illustrations :positive-03 "rgba(78,188,96,1)" ; Lighter Positive, Supporting color for success illustrations :negative-01 "rgba(255,45,85,1)" ; Primary Negative, text, icons color - :negative-02 "rgba(255,45,85,0.1))" ; Secondary Negative, Supporting color for errors illustrations + :negative-02 "rgba(255,45,85,0.1))" ; Secondary Negative, Supporting color for errors + ; illustrations :warning-01 "rgba(255, 202, 15, 1)" :warning-02 "rgba(255, 202, 15, 0.1)" :interactive-01 "rgba(67,96,223,1)" ; Accent color, buttons, own message, actions,active state - :interactive-02 "rgba(236,239,252,1)" ; Light Accent, buttons background, actions background, messages + :interactive-02 "rgba(236,239,252,1)" ; Light Accent, buttons background, actions background, + ; messages :interactive-03 "rgba(255,255,255,0.1)" ; Background for interactive above accent :interactive-04 "rgba(147,155,161,1)" ; Disabled state :ui-background "rgba(255,255,255,1)" ; Default view background @@ -85,12 +88,14 @@ (def theme (reagent/atom light-theme)) -(defn get-color [color] +(defn get-color + [color] (get @theme color)) ;; LEGACY COLORS -(defn alpha [value opacity] +(defn alpha + [value opacity] (if (string/starts-with? value "#") (let [hex (string/replace value #"#" "") r (js/parseInt (subs hex 0 2) 16) @@ -100,13 +105,15 @@ (let [rgb (string/split value #",")] (str (string/join "," (butlast rgb)) "," opacity ")")))) -(def old-colors-mapping-light {:mentioned-background "#def6fc" - :mentioned-border "#b8ecf9" - :pin-background "#FFEECC"}) +(def old-colors-mapping-light + {:mentioned-background "#def6fc" + :mentioned-border "#b8ecf9" + :pin-background "#FFEECC"}) -(def old-colors-mapping-dark {:mentioned-background "#2a4046" - :mentioned-border "#2a4046" - :pin-background "#34232B"}) +(def old-colors-mapping-dark + {:mentioned-background "#2a4046" + :mentioned-border "#2a4046" + :pin-background "#34232B"}) (def old-colors-mapping-themes {:dark old-colors-mapping-dark :light old-colors-mapping-light}) @@ -127,7 +134,8 @@ ;; BLACK (def black (:text-01 light-theme)) ;; Used as the default text color (def black-persist (:ui-background dark-theme)) ;; this doesn't with theme -(def black-transparent (:ui-02 light-theme)) ;; Used as background color for rounded button on dark background and as background color for containers like "Backup recovery phrase" +(def black-transparent (:ui-02 light-theme)) ;; Used as background color for rounded button on dark background and as background + ;; color for containers like "Backup recovery phrase" (def black-transparent-20 (:backdrop light-theme)) ; accounts divider (def black-transparent-40 (:backdrop light-theme)) (def black-transparent-40-persist (:backdrop light-theme)) @@ -136,7 +144,8 @@ (def black-transparent-86 (:ui-03 light-theme)) ;; DARK GREY -(def gray (:text-02 light-theme)) ;; Dark grey, used as a background for a light foreground and as section header and secondary text color +(def gray (:text-02 light-theme)) ;; Dark grey, used as a background for a light foreground and as + ;; section header and secondary text color (def gray-transparent-10 (alpha gray 0.1)) (def gray-transparent-40 (alpha gray 0.4)) ;; LIGHT GREY @@ -164,20 +173,22 @@ (def purple "#887af9") (def orange "#FE8F59") -(def chat-colors ["#fa6565" - "#7cda00" - purple - "#51d0f0" - orange - "#d37ef4"]) +(def chat-colors + ["#fa6565" + "#7cda00" + purple + "#51d0f0" + orange + "#d37ef4"]) -(def account-colors ["#9B832F" - "#D37EF4" - "#1D806F" - "#FA6565" - "#7CDA00" - purple - "#8B3131"]) +(def account-colors + ["#9B832F" + "#D37EF4" + "#1D806F" + "#FA6565" + "#7CDA00" + purple + "#8B3131"]) (def mention-incoming "#0DA4C9") (def mention-outgoing "#9EE8FA") @@ -191,10 +202,12 @@ (def theme-type (reagent/atom :light)) -(defn dark? [] +(defn dark? + [] (= :dark @theme-type)) -(defn set-legacy-theme-type [type] +(defn set-legacy-theme-type + [type] (when-not (= type @theme-type) (let [old-colors-mapping-colors (get old-colors-mapping-themes type)] (set! white (:ui-background @theme)) diff --git a/src/quo/design_system/spacing.cljs b/src/quo/design_system/spacing.cljs index 3885074274..3d6197a23e 100644 --- a/src/quo/design_system/spacing.cljs +++ b/src/quo/design_system/spacing.cljs @@ -1,19 +1,22 @@ (ns quo.design-system.spacing) -(def spacing {:x-tiny 4 - :tiny 8 - :small 12 - :base 16 - :large 24 - :x-large 32 - :xx-large 48}) +(def spacing + {:x-tiny 4 + :tiny 8 + :small 12 + :base 16 + :large 24 + :x-large 32 + :xx-large 48}) -(def padding-horizontal (reduce-kv (fn [m k v] - (assoc m k {:padding-horizontal v})) - {} - spacing)) +(def padding-horizontal + (reduce-kv (fn [m k v] + (assoc m k {:padding-horizontal v})) + {} + spacing)) -(def padding-vertical (reduce-kv (fn [m k v] - (assoc m k {:padding-vertical v})) - {} - spacing)) +(def padding-vertical + (reduce-kv (fn [m k v] + (assoc m k {:padding-vertical v})) + {} + spacing)) diff --git a/src/quo/design_system/typography.cljs b/src/quo/design_system/typography.cljs index dddf0bd86f..5863bf1e25 100644 --- a/src/quo/design_system/typography.cljs +++ b/src/quo/design_system/typography.cljs @@ -1,25 +1,32 @@ (ns quo.design-system.typography) -(def tiny {:font-size 10 - :line-height 14}) +(def tiny + {:font-size 10 + :line-height 14}) -(def x-small {:font-size 12 - :line-height 16}) +(def x-small + {:font-size 12 + :line-height 16}) -(def small {:font-size 13 - :line-height 18}) +(def small + {:font-size 13 + :line-height 18}) -(def base {:font-size 15 - :line-height 22}) +(def base + {:font-size 15 + :line-height 22}) -(def large {:font-size 17 - :line-height 24}) +(def large + {:font-size 17 + :line-height 24}) -(def x-large {:font-size 22 - :line-height 30}) +(def x-large + {:font-size 22 + :line-height 30}) -(def xx-large {:font-size 28 - :line-height 38}) +(def xx-large + {:font-size 28 + :line-height 38}) (def font-regular {:font-family "Inter-Regular"}) ; 400 diff --git a/src/quo/gesture_handler.cljs b/src/quo/gesture_handler.cljs index 8b177e2e5c..5ebba7480c 100644 --- a/src/quo/gesture_handler.cljs +++ b/src/quo/gesture_handler.cljs @@ -1,12 +1,17 @@ (ns quo.gesture-handler - (:require [oops.core :refer [oget]] - [reagent.core :as reagent] + (:require ["react-native-gesture-handler" :refer + (TapGestureHandler PanGestureHandler + LongPressGestureHandler + TouchableWithoutFeedback + TouchableOpacity + TouchableHighlight + State + NativeViewGestureHandler + FlatList + ScrollView)] + [oops.core :refer [oget]] [quo.design-system.colors :as colors] - ["react-native-gesture-handler" - :refer (TapGestureHandler PanGestureHandler LongPressGestureHandler - TouchableWithoutFeedback TouchableOpacity - TouchableHighlight State NativeViewGestureHandler - FlatList ScrollView)])) + [reagent.core :as reagent])) (def flat-list-raw FlatList) @@ -30,9 +35,11 @@ (def touchable-highlight-class (reagent/adapt-react-class TouchableHighlight)) -(defn touchable-highlight [props & children] - (into [touchable-highlight-class (merge {:underlay-color (:interactive-02 @colors/theme)} - props)] +(defn touchable-highlight + [props & children] + (into [touchable-highlight-class + (merge {:underlay-color (:interactive-02 @colors/theme)} + props)] children)) (def touchable-opacity @@ -40,9 +47,10 @@ (def native-view-gesture-handler (reagent/adapt-react-class NativeViewGestureHandler)) -(def states {:began (oget State "BEGAN") - :active (oget State "ACTIVE") - :cancelled (oget State "CANCELLED") - :end (oget State "END") - :failed (oget State "FAILED") - :undetermined (oget State "UNDETERMINED")}) +(def states + {:began (oget State "BEGAN") + :active (oget State "ACTIVE") + :cancelled (oget State "CANCELLED") + :end (oget State "END") + :failed (oget State "FAILED") + :undetermined (oget State "UNDETERMINED")}) diff --git a/src/quo/haptic.cljs b/src/quo/haptic.cljs index 3bf6cb68e9..deabdb5817 100644 --- a/src/quo/haptic.cljs +++ b/src/quo/haptic.cljs @@ -9,16 +9,17 @@ :notification-success "notificationSuccess" :notification-warning "notificationWarning" :notification-error "notificationError" - :clock-tick "clockTick" ; (Android only) - :context-click "contextClick" ; (Android only) - :keyboard-press "keyboardPress" ; (Android only) - :keyboard-release "keyboardRelease" ; (Android only) - :keyboard-tap "keyboardTap" ; (Android only) - :long-press "longPress" ; (Android only) - :text-handle-move "textHandleMove" ; (Android only) - :virtual-key "virtualKey" ; (Android only) - :virtual-key-release "virtualKeyRelease" ; (Android only) - }) + :clock-tick "clockTick" ; (Android only) + :context-click "contextClick" ; (Android only) + :keyboard-press "keyboardPress" ; (Android only) + :keyboard-release "keyboardRelease" ; (Android only) + :keyboard-tap "keyboardTap" ; (Android only) + :long-press "longPress" ; (Android only) + :text-handle-move "textHandleMove" ; (Android only) + :virtual-key "virtualKey" ; (Android only) + :virtual-key-release "virtualKeyRelease" ; (Android only) + }) -(defn trigger [method] +(defn trigger + [method] (.trigger ^js react-native-haptic-feedback (get haptic-methods method))) diff --git a/src/quo/previews/bottom_sheet.cljs b/src/quo/previews/bottom_sheet.cljs index 3c2a12bccb..4940c1a30f 100644 --- a/src/quo/previews/bottom_sheet.cljs +++ b/src/quo/previews/bottom_sheet.cljs @@ -1,27 +1,29 @@ (ns quo.previews.bottom-sheet - (:require [reagent.core :as reagent] - [quo.core :as quo] - [quo.react-native :as rn] + (:require [quo.core :as quo] [quo.design-system.colors :as colors] - [quo.previews.preview :as preview])) + [quo.previews.preview :as preview] + [quo.react-native :as rn] + [reagent.core :as reagent])) -(def descriptor [{:label "Show handle:" - :key :show-handle? - :type :boolean} - {:label "Backdrop dismiss:" - :key :backdrop-dismiss? - :type :boolean} - {:label "Disable drag:" - :key :disable-drag? - :type :boolean} - {:label "Android back cancel:" - :key :back-button-cancel - :type :boolean} - {:label "Scrollable:" - :key :scrollable - :type :boolean}]) +(def descriptor + [{:label "Show handle:" + :key :show-handle? + :type :boolean} + {:label "Backdrop dismiss:" + :key :backdrop-dismiss? + :type :boolean} + {:label "Disable drag:" + :key :disable-drag? + :type :boolean} + {:label "Android back cancel:" + :key :back-button-cancel + :type :boolean} + {:label "Scrollable:" + :key :scrollable + :type :boolean}]) -(defn cool-preview [] +(defn cool-preview + [] (let [state (reagent/atom {:show-handle? true :backdrop-dismiss? true :disable-drag? false @@ -29,38 +31,47 @@ visible (reagent/atom false) scrollable (reagent/cursor state [:scrollable])] (fn [] - [rn/view {:margin-bottom 50 - :padding 16} + [rn/view + {:margin-bottom 50 + :padding 16} [preview/customizer state descriptor] [:<> - [rn/view {:style {:align-items :center - :padding 16}} + [rn/view + {:style {:align-items :center + :padding 16}} [rn/touchable-opacity {:on-press #(reset! visible true)} - [rn/view {:style {:padding-horizontal 16 - :padding-vertical 8 - :border-radius 4 - :background-color (:interactive-01 @colors/theme)}} + [rn/view + {:style {:padding-horizontal 16 + :padding-vertical 8 + :border-radius 4 + :background-color (:interactive-01 @colors/theme)}} [quo/text {:color :secondary-inverse} (str "Open sheet: " @visible)]]]] - [quo/bottom-sheet (merge @state - {:visible? @visible - :on-cancel #(reset! visible false)}) - [rn/view {:style {:height (if @scrollable 1200 400) - :justify-content :center - :align-items :center}} + [quo/bottom-sheet + (merge @state + {:visible? @visible + :on-cancel #(reset! visible false)}) + [rn/view + {:style {:height (if @scrollable 1200 400) + :justify-content :center + :align-items :center}} [rn/touchable-opacity {:on-press #(reset! visible false)} [quo/text {:color :link} "Close"]] - [rn/touchable-opacity {:on-press #(swap! scrollable not) - :style {:padding-vertical 16}} + [rn/touchable-opacity + {:on-press #(swap! scrollable not) + :style {:padding-vertical 16}} [quo/text {:color :link} "Toggle size"]] [quo/text "Hello world!"]]]]]))) -(defn preview [] +(defn preview + [] (fn [] - [rn/view {:background-color (:ui-background @colors/theme) - :flex 1} - [rn/flat-list {:flex 1 - :keyboardShouldPersistTaps :always - :header [cool-preview] - :key-fn str}]])) + [rn/view + {:background-color (:ui-background @colors/theme) + :flex 1} + [rn/flat-list + {:flex 1 + :keyboardShouldPersistTaps :always + :header [cool-preview] + :key-fn str}]])) diff --git a/src/quo/previews/button.cljs b/src/quo/previews/button.cljs index 1f5002f5ac..b7d54cfac5 100644 --- a/src/quo/previews/button.cljs +++ b/src/quo/previews/button.cljs @@ -1,44 +1,46 @@ (ns quo.previews.button - (:require [reagent.core :as reagent] - [quo.core :as quo] - [quo.react-native :as rn] + (:require [quo.core :as quo] [quo.design-system.colors :as colors] - [quo.previews.preview :as preview])) + [quo.previews.preview :as preview] + [quo.react-native :as rn] + [reagent.core :as reagent])) -(def descriptor [{:label "Type:" - :key :type - :type :select - :options [{:key :primary - :value "Primary"} - {:key :secondary - :value "Secondary"} - {:key :icon - :value "Icon"}]} - {:label "Theme:" - :key :theme - :type :select - :options [{:key :main - :value "Main"} - {:key :negative - :value "Negative"} - {:key :positive - :value "Positive"} - {:key :accent - :value "Accent"}]} - {:label "After icon:" - :key :after - :type :boolean} - {:label "Before icon:" - :key :before - :type :boolean} - {:label "Disabled:" - :key :disabled - :type :boolean} - {:label "Label" - :key :label - :type :text}]) +(def descriptor + [{:label "Type:" + :key :type + :type :select + :options [{:key :primary + :value "Primary"} + {:key :secondary + :value "Secondary"} + {:key :icon + :value "Icon"}]} + {:label "Theme:" + :key :theme + :type :select + :options [{:key :main + :value "Main"} + {:key :negative + :value "Negative"} + {:key :positive + :value "Positive"} + {:key :accent + :value "Accent"}]} + {:label "After icon:" + :key :after + :type :boolean} + {:label "Before icon:" + :key :before + :type :boolean} + {:label "Disabled:" + :key :disabled + :type :boolean} + {:label "Label" + :key :label + :type :text}]) -(defn cool-preview [] +(defn cool-preview + [] (let [state (reagent/atom {:label "Press Me" :type :primary :theme :main @@ -48,28 +50,36 @@ before (reagent/cursor state [:before]) after (reagent/cursor state [:after])] (fn [] - [rn/view {:margin-bottom 50 - :padding 16} + [rn/view + {:margin-bottom 50 + :padding 16} [rn/view {:flex 1} [preview/customizer state descriptor]] - [rn/view {:padding-vertical 16 - :flex-direction :row - :justify-content :center} - [quo/button (merge (dissoc @state - :theme :before :after) - {:on-press #(println "Hello world!")} - (when @theme - {:theme @theme}) - (when @before - {:before :main-icons/back}) - (when @after - {:after :main-icons/next})) + [rn/view + {:padding-vertical 16 + :flex-direction :row + :justify-content :center} + [quo/button + (merge (dissoc @state + :theme + :before + :after) + {:on-press #(println "Hello world!")} + (when @theme + {:theme @theme}) + (when @before + {:before :main-icons/back}) + (when @after + {:after :main-icons/next})) @label]]]))) -(defn preview-button [] - [rn/view {:background-color (:ui-background @colors/theme) - :flex 1} - [rn/flat-list {:flex 1 - :keyboardShouldPersistTaps :always - :header [cool-preview] - :key-fn str}]]) +(defn preview-button + [] + [rn/view + {:background-color (:ui-background @colors/theme) + :flex 1} + [rn/flat-list + {:flex 1 + :keyboardShouldPersistTaps :always + :header [cool-preview] + :key-fn str}]]) diff --git a/src/quo/previews/controls.cljs b/src/quo/previews/controls.cljs index a2e6999668..ce7cf33212 100644 --- a/src/quo/previews/controls.cljs +++ b/src/quo/previews/controls.cljs @@ -1,44 +1,55 @@ (ns quo.previews.controls - (:require [reagent.core :as reagent] - [quo.core :as quo] + (:require [quo.core :as quo] + [quo.design-system.colors :as colors] [quo.react-native :as rn] - [quo.design-system.colors :as colors])) + [reagent.core :as reagent])) -(defn preview [] +(defn preview + [] (let [switch-state (reagent/atom true) radio-state (reagent/atom true) checkbox-state (reagent/atom true)] (fn [] - [rn/view {:background-color (:ui-background @colors/theme) - :flex 1} - [rn/view {:padding 20 - :flex-direction :row - :align-items :center - :justify-content :space-between} - [rn/touchable-opacity {:style {:margin-vertical 10 - :padding 10} - :on-press #(swap! switch-state not)} + [rn/view + {:background-color (:ui-background @colors/theme) + :flex 1} + [rn/view + {:padding 20 + :flex-direction :row + :align-items :center + :justify-content :space-between} + [rn/touchable-opacity + {:style {:margin-vertical 10 + :padding 10} + :on-press #(swap! switch-state not)} [quo/text (str "Switch state: " @switch-state)]] - [quo/switch {:value @switch-state - :on-change #(reset! switch-state %)}]] + [quo/switch + {:value @switch-state + :on-change #(reset! switch-state %)}]] - [rn/view {:padding 20 - :flex-direction :row - :align-items :center - :justify-content :space-between} - [rn/touchable-opacity {:style {:margin-vertical 10 - :padding 10} - :on-press #(swap! radio-state not)} + [rn/view + {:padding 20 + :flex-direction :row + :align-items :center + :justify-content :space-between} + [rn/touchable-opacity + {:style {:margin-vertical 10 + :padding 10} + :on-press #(swap! radio-state not)} [quo/text (str "Radio state: " @radio-state)]] - [quo/radio {:value @radio-state - :on-change #(reset! radio-state %)}]] - [rn/view {:padding 20 - :flex-direction :row - :align-items :center - :justify-content :space-between} - [rn/touchable-opacity {:style {:margin-vertical 10 - :padding 10} - :on-press #(swap! checkbox-state not)} + [quo/radio + {:value @radio-state + :on-change #(reset! radio-state %)}]] + [rn/view + {:padding 20 + :flex-direction :row + :align-items :center + :justify-content :space-between} + [rn/touchable-opacity + {:style {:margin-vertical 10 + :padding 10} + :on-press #(swap! checkbox-state not)} [quo/text (str "Checkbox state: " @checkbox-state)]] - [quo/checkbox {:value @checkbox-state - :on-change #(reset! checkbox-state %)}]]]))) + [quo/checkbox + {:value @checkbox-state + :on-change #(reset! checkbox-state %)}]]]))) diff --git a/src/quo/previews/header.cljs b/src/quo/previews/header.cljs index 3df2b3b590..2817031d0f 100644 --- a/src/quo/previews/header.cljs +++ b/src/quo/previews/header.cljs @@ -1,44 +1,51 @@ (ns quo.previews.header (:require [quo.core :as quo] - [quo.react-native :as rn] - [quo.design-system.colors :as colors]) + [quo.design-system.colors :as colors] + [quo.react-native :as rn]) (:require-macros [quo.previews.preview :as preview])) -(def accessories [nil - [{:icon :main-icons/close - :on-press identity}] - [{:icon :main-icons/close - :on-press identity} - {:icon :main-icons/add - :on-press identity}] - [{:icon :main-icons/add - :on-press identity} - {:label "Text" - :on-press identity}] - [{:label "Text" - :on-press identity}]]) +(def accessories + [nil + [{:icon :main-icons/close + :on-press identity}] + [{:icon :main-icons/close + :on-press identity} + {:icon :main-icons/add + :on-press identity}] + [{:icon :main-icons/add + :on-press identity} + {:label "Text" + :on-press identity}] + [{:label "Text" + :on-press identity}]]) -(def all-props (preview/list-comp [left-accessories accessories - right-accessories accessories - title [nil "This is a title" "This is a very long super title"] - subtitle [nil "This is a subtitle"] - title-align [:left :center]] - {:left-accessories left-accessories - :right-accessories right-accessories - :title title - :subtitle subtitle - :title-align title-align})) +(def all-props + (preview/list-comp [left-accessories accessories + right-accessories accessories + title [nil "This is a title" "This is a very long super title"] + subtitle [nil "This is a subtitle"] + title-align [:left :center]] + {:left-accessories left-accessories + :right-accessories right-accessories + :title title + :subtitle subtitle + :title-align title-align})) -(defn render-item [props] - [rn/view {:border-bottom-color "#EEF2F5" - :border-bottom-width 2} +(defn render-item + [props] + [rn/view + {:border-bottom-color "#EEF2F5" + :border-bottom-width 2} [quo/header props]]) -(defn preview-header [] - [rn/view {:background-color (:ui-background @colors/theme) - :flex 1} - [rn/flat-list {:flex 1 - :keyboardShouldPersistTaps :always - :data all-props - :render-fn render-item - :key-fn str}]]) +(defn preview-header + [] + [rn/view + {:background-color (:ui-background @colors/theme) + :flex 1} + [rn/flat-list + {:flex 1 + :keyboardShouldPersistTaps :always + :data all-props + :render-fn render-item + :key-fn str}]]) diff --git a/src/quo/previews/icons.cljs b/src/quo/previews/icons.cljs index 618da1e065..77318534f8 100644 --- a/src/quo/previews/icons.cljs +++ b/src/quo/previews/icons.cljs @@ -3,9 +3,11 @@ [quo.react-native :as rn] [status-im.ui.components.icons.icons :as icons])) -(defn preview [] - [rn/scroll-view {:background-color (:ui-background @colors/theme) - :flex 1} +(defn preview + [] + [rn/scroll-view + {:background-color (:ui-background @colors/theme) + :flex 1} (for [i (keys icons/icons)] [rn/view {:flex-direction :row} [icons/icon (keyword i)] diff --git a/src/quo/previews/lists.cljs b/src/quo/previews/lists.cljs index 8b05387882..55dd9ca4ae 100644 --- a/src/quo/previews/lists.cljs +++ b/src/quo/previews/lists.cljs @@ -1,87 +1,94 @@ (ns quo.previews.lists - (:require [reagent.core :as reagent] - [quo.core :as quo] - [quo.react-native :as rn] + (:require [quo.core :as quo] [quo.design-system.colors :as colors] - [quo.previews.preview :as preview])) + [quo.previews.preview :as preview] + [quo.react-native :as rn] + [reagent.core :as reagent])) (def all-props (preview/list-comp [] {})) -(defn avatar [] - [rn/view {:border-radius 20 - :width 40 - :height 40 - :justify-content :center - :align-items :center - :background-color :red} - [quo/text {:weight :bold - :size :large} +(defn avatar + [] + [rn/view + {:border-radius 20 + :width 40 + :height 40 + :justify-content :center + :align-items :center + :background-color :red} + [quo/text + {:weight :bold + :size :large} "T"]]) -(defn icon-element [type] +(defn icon-element + [type] (case type :icon :main-icons/add-contact :component [avatar] nil)) -(def descriptor [{:label "Accessory:" - :key :accessory - :type :select - :options [{:key :radio - :value "Radio"} - {:key :checkbox - :value "Checkbox"} - {:key :switch - :value "Switch"} - {:key :text - :value "Text"} - {:key :default - :value "Default"}]} - {:label "Size:" - :key :size - :type :select - :options [{:key :small - :value "Small"} - {:key :default - :value "Default"}]} - {:label "Icon:" - :key :icon - :type :select - :options [{:key :icon - :value "Icon"} - {:key :component - :value "Component"}]} - {:label "Theme:" - :key :theme - :type :select - :options [{:key :main - :value "Main"} - {:key :accent - :value "Accent"} - {:key :negative - :value "Negative"} - {:key :positive - :value "Positive"}]} - {:label "Selectable" - :key :selectable - :type :boolean} - {:label "Chevron" - :key :chevron - :type :boolean} - {:label "Disabled:" - :key :disabled - :type :boolean} - {:label "Title" - :key :title - :type :text} - {:label "Subtitle" - :key :subtitle - :type :text}]) +(def descriptor + [{:label "Accessory:" + :key :accessory + :type :select + :options [{:key :radio + :value "Radio"} + {:key :checkbox + :value "Checkbox"} + {:key :switch + :value "Switch"} + {:key :text + :value "Text"} + {:key :default + :value "Default"}]} + {:label "Size:" + :key :size + :type :select + :options [{:key :small + :value "Small"} + {:key :default + :value "Default"}]} + {:label "Icon:" + :key :icon + :type :select + :options [{:key :icon + :value "Icon"} + {:key :component + :value "Component"}]} + {:label "Theme:" + :key :theme + :type :select + :options [{:key :main + :value "Main"} + {:key :accent + :value "Accent"} + {:key :negative + :value "Negative"} + {:key :positive + :value "Positive"}]} + {:label "Selectable" + :key :selectable + :type :boolean} + {:label "Chevron" + :key :chevron + :type :boolean} + {:label "Disabled:" + :key :disabled + :type :boolean} + {:label "Title" + :key :title + :type :text} + {:label "Subtitle" + :key :subtitle + :type :text}]) -(defn render-item [_] - [rn/view {:style {:padding-vertical 24}}]) +(defn render-item + [_] + [rn/view {:style {:padding-vertical 24}}]) -(defn cool-preview [] +(defn cool-preview + [] (let [state (reagent/atom {:title "Title" :active false}) icon (reagent/cursor state [:icon]) @@ -92,19 +99,23 @@ [rn/view {:padding-horizontal 16} [preview/customizer state descriptor]] [rn/view {:padding-vertical 16} - [quo/list-item (merge (dissoc @state :active :selectable) - (when @selectable - {:active @active - :on-press #(swap! active not)}) - {:accessory-text "Accessory" - :icon (icon-element @icon)})]]]))) + [quo/list-item + (merge (dissoc @state :active :selectable) + (when @selectable + {:active @active + :on-press #(swap! active not)}) + {:accessory-text "Accessory" + :icon (icon-element @icon)})]]]))) -(defn preview [] - [rn/view {:background-color (:ui-background @colors/theme) - :flex 1} - [rn/flat-list {:flex 1 - :keyboardShouldPersistTaps :always - :header [cool-preview] - :data all-props - :render-fn render-item - :key-fn str}]]) +(defn preview + [] + [rn/view + {:background-color (:ui-background @colors/theme) + :flex 1} + [rn/flat-list + {:flex 1 + :keyboardShouldPersistTaps :always + :header [cool-preview] + :data all-props + :render-fn render-item + :key-fn str}]]) diff --git a/src/quo/previews/main.cljs b/src/quo/previews/main.cljs index 7905ea19ec..0dbfaa99e0 100644 --- a/src/quo/previews/main.cljs +++ b/src/quo/previews/main.cljs @@ -1,75 +1,83 @@ (ns quo.previews.main - (:require [quo.previews.header :as header] + (:require [quo.core :as quo] + [quo.design-system.colors :as colors] + [quo.previews.bottom-sheet :as bottom-sheet] + [quo.previews.button :as button] + [quo.previews.controls :as controls] + [quo.previews.header :as header] + [quo.previews.icons :as icons] + [quo.previews.lists :as lists] [quo.previews.text :as text] [quo.previews.text-input :as text-input] [quo.previews.tooltip :as tooltip] - [quo.previews.button :as button] - [quo.previews.lists :as lists] - [quo.previews.bottom-sheet :as bottom-sheet] - [quo.previews.controls :as controls] [quo.react-native :as rn] - [quo.core :as quo] - [quo.design-system.colors :as colors] [quo.theme :as theme] - [quo.previews.icons :as icons] [re-frame.core :as re-frame])) -(def screens [{:name :texts - :insets {:top false} - :component text/preview-text} - {:name :tooltip - :insets {:top false} - :component tooltip/preview-tooltip} - {:name :text-input - :insets {:top false} - :component text-input/preview-text} - {:name :headers - :insets {:top false} - :component header/preview-header} - {:name :button - :insets {:top false} - :component button/preview-button} - {:name :lists - :instes {:top false} - :component lists/preview} - {:name :bottom-sheet - :insets {:top false} - :component bottom-sheet/preview} - {:name :controls - :insets {:top false} - :component controls/preview} - {:name :icons - :insets {:top false} - :component icons/preview}]) +(def screens + [{:name :texts + :insets {:top false} + :component text/preview-text} + {:name :tooltip + :insets {:top false} + :component tooltip/preview-tooltip} + {:name :text-input + :insets {:top false} + :component text-input/preview-text} + {:name :headers + :insets {:top false} + :component header/preview-header} + {:name :button + :insets {:top false} + :component button/preview-button} + {:name :lists + :instes {:top false} + :component lists/preview} + {:name :bottom-sheet + :insets {:top false} + :component bottom-sheet/preview} + {:name :controls + :insets {:top false} + :component controls/preview} + {:name :icons + :insets {:top false} + :component icons/preview}]) -(defn theme-switcher [] - [rn/view {:style {:flex-direction :row - :margin-vertical 8 - :border-radius 4 - :background-color (:ui-01 @colors/theme) - :border-width 1 - :border-color (:ui-02 @colors/theme)}} - [rn/touchable-opacity {:style {:padding 8 - :flex 1 - :justify-content :center - :align-items :center} - :on-press #(theme/set-theme :light)} +(defn theme-switcher + [] + [rn/view + {:style {:flex-direction :row + :margin-vertical 8 + :border-radius 4 + :background-color (:ui-01 @colors/theme) + :border-width 1 + :border-color (:ui-02 @colors/theme)}} + [rn/touchable-opacity + {:style {:padding 8 + :flex 1 + :justify-content :center + :align-items :center} + :on-press #(theme/set-theme :light)} [quo/text "Set light theme"]] - [rn/view {:width 1 - :margin-vertical 4 - :background-color (:ui-02 @colors/theme)}] - [rn/touchable-opacity {:style {:padding 8 - :flex 1 - :justify-content :center - :align-items :center} - :on-press #(theme/set-theme :dark)} + [rn/view + {:width 1 + :margin-vertical 4 + :background-color (:ui-02 @colors/theme)}] + [rn/touchable-opacity + {:style {:padding 8 + :flex 1 + :justify-content :center + :align-items :center} + :on-press #(theme/set-theme :dark)} [quo/text "Set dark theme"]]]) -(defn main-screen [] - [rn/scroll-view {:flex 1 - :padding-vertical 8 - :padding-horizontal 16 - :background-color (:ui-background @colors/theme)} +(defn main-screen + [] + [rn/scroll-view + {:flex 1 + :padding-vertical 8 + :padding-horizontal 16 + :background-color (:ui-background @colors/theme)} [theme-switcher] [rn/view (for [{:keys [name]} screens] @@ -78,6 +86,7 @@ [rn/view {:style {:padding-vertical 8}} [quo/text (str "Preview " name)]]])]]) -(def main-screens [{:name :quo-preview - :insets {:top false} - :component main-screen}]) +(def main-screens + [{:name :quo-preview + :insets {:top false} + :component main-screen}]) diff --git a/src/quo/previews/preview.clj b/src/quo/previews/preview.clj index c57e450337..bac44c99b2 100644 --- a/src/quo/previews/preview.clj +++ b/src/quo/previews/preview.clj @@ -1,15 +1,17 @@ (ns quo.previews.preview) -(defn descriptor->values [{:keys [key options type]}] +(defn descriptor->values + [{:keys [key options type]}] {key (case type :boolean [false true] :text [nil "Just simple text"] ; NOTE(Ferossgp): add example with long text? :select (mapv :key options))}) -(defmacro list-comp [[binding seq-expr & bindings] body-expr] +(defmacro list-comp + [[binding seq-expr & bindings] body-expr] (cond (not binding) `(list ~body-expr) :else `(mapcat (fn [~binding] (list-comp ~bindings ~body-expr)) - ~seq-expr))) + ~seq-expr))) diff --git a/src/quo/previews/preview.cljs b/src/quo/previews/preview.cljs index 82582c6c84..57a7cd8141 100644 --- a/src/quo/previews/preview.cljs +++ b/src/quo/previews/preview.cljs @@ -1,23 +1,26 @@ (ns quo.previews.preview - (:require [reagent.core :as reagent] + (:require [quo.core :as quo] + [quo.design-system.colors :as colors] [quo.react-native :as rn] - [quo.core :as quo] - [quo.design-system.colors :as colors]) + [reagent.core :as reagent]) (:require-macros quo.previews.preview)) -(def container {:flex-direction :row - :padding-vertical 8 - :flex 1 - :align-items :center}) +(def container + {:flex-direction :row + :padding-vertical 8 + :flex 1 + :align-items :center}) -(defn touchable-style [] +(defn touchable-style + [] {:flex 1 :align-items :center :justify-content :center :padding-horizontal 16 :height 44}) -(defn select-style [] +(defn select-style + [] {:flex 1 :flex-direction :row :align-items :center @@ -28,7 +31,8 @@ :border-width 1 :border-color (:ui-02 @colors/theme)}) -(defn select-option-style [selected] +(defn select-option-style + [selected] (merge (select-style) {:margin-vertical 8 :justify-content :center} @@ -36,23 +40,27 @@ {:background-color (:interactive-02 @colors/theme)} {:background-color (:ui-01 @colors/theme)}))) -(def label-style {:flex 0.4 - :padding-right 8}) +(def label-style + {:flex 0.4 + :padding-right 8}) -(defn label-view [state label] +(defn label-view + [state label] [rn/view {:style label-style} [quo/text (when-let [label-color (:preview-label-color @state)] {:style {:color label-color}}) label]]) -(defn modal-container [] +(defn modal-container + [] {:flex 1 :justify-content :center :padding-horizontal 24 :background-color "rgba(0,0,0,0.4)"}) -(defn modal-view [] +(defn modal-view + [] {:padding-horizontal 16 :padding-vertical 8 :border-radius 8 @@ -64,21 +72,25 @@ (let [state* (reagent/cursor state [key])] [rn/view {:style container} [label-view state label] - [rn/view {:style {:flex-direction :row - :flex 0.6 - :border-radius 4 - :background-color (:ui-01 @colors/theme) - :border-width 1 - :border-color (:ui-02 @colors/theme)}} - [rn/touchable-opacity {:style (touchable-style) - :on-press #(reset! state* true)} - [quo/text {:color (if @state* :link :secondary)} + [rn/view + {:style {:flex-direction :row + :flex 0.6 + :border-radius 4 + :background-color (:ui-01 @colors/theme) + :border-width 1 + :border-color (:ui-02 @colors/theme)}} + [rn/touchable-opacity + {:style (touchable-style) + :on-press #(reset! state* true)} + [quo/text {:color (if @state* :link :secondary)} "True"]] - [rn/view {:width 1 - :margin-vertical 4 - :background-color (:ui-02 @colors/theme)}] - [rn/touchable-opacity {:style (touchable-style) - :on-press #(reset! state* false)} + [rn/view + {:width 1 + :margin-vertical 4 + :background-color (:ui-02 @colors/theme)}] + [rn/touchable-opacity + {:style (touchable-style) + :on-press #(reset! state* false)} [quo/text {:color (if (not @state*) :link :secondary)} "False"]]]])) @@ -88,20 +100,22 @@ [rn/view {:style container} [label-view state label] [rn/view {:style {:flex 0.6}} - [quo/text-input {:value @state* - :show-cancel false - :style {:border-radius 4 - :border-width 1 - :border-color (:ui-02 @colors/theme)} - :on-change-text #(do - (reset! state* %) - (reagent/flush))}]]])) + [quo/text-input + {:value @state* + :show-cancel false + :style {:border-radius 4 + :border-width 1 + :border-color (:ui-02 @colors/theme)} + :on-change-text #(do + (reset! state* %) + (reagent/flush))}]]])) (defn value-for-key [id v] (:value (first (filter #(= (:key %) id) v)))) -(defn customizer-select [] +(defn customizer-select + [] (let [open (reagent/atom nil)] (fn [{:keys [label key state options]}] (let [state* (reagent/cursor state [key]) @@ -109,54 +123,63 @@ [rn/view {:style container} [label-view state label] [rn/view {:style {:flex 0.6}} - [rn/modal {:visible @open - :on-request-close #(reset! open false) - :statusBarTranslucent true - :transparent true - :animation :slide} + [rn/modal + {:visible @open + :on-request-close #(reset! open false) + :statusBarTranslucent true + :transparent true + :animation :slide} [rn/view {:style (modal-container)} [rn/view {:style (modal-view)} [rn/scroll-view (doall (for [{:keys [key value]} options] ^{:key key} - [rn/touchable-opacity {:style (select-option-style (= @state* key)) - :on-press #(do - (reset! open false) - (reset! state* key))} + [rn/touchable-opacity + {:style (select-option-style (= @state* key)) + :on-press #(do + (reset! open false) + (reset! state* key))} [quo/text {:color (if (= @state* key) :link :secondary)} value]])) [rn/view {:flex-direction :row} - [rn/touchable-opacity {:style (select-option-style false) - :on-press #(do - (reset! state* nil) - (reset! open false))} + [rn/touchable-opacity + {:style (select-option-style false) + :on-press #(do + (reset! state* nil) + (reset! open false))} [quo/text "Clear"]] [rn/view {:width 16}] - [rn/touchable-opacity {:style (select-option-style false) - :on-press #(reset! open false)} + [rn/touchable-opacity + {:style (select-option-style false) + :on-press #(reset! open false)} [quo/text "Close"]]]]]]] - [rn/touchable-opacity {:style (select-style) - :on-press #(reset! open true)} + [rn/touchable-opacity + {:style (select-style) + :on-press #(reset! open true)} (if selected [quo/text {:color :link} selected] [quo/text "Select option"]) - [rn/view {:position :absolute - :right 16 - :top 0 - :bottom 0 - :justify-content :center} + [rn/view + {:position :absolute + :right 16 + :top 0 + :bottom 0 + :justify-content :center} [quo/text "↓"]]]]])))) -(defn customizer [state descriptors] - [rn/view {:style {:flex 1} - :padding-horizontal 16} +(defn customizer + [state descriptors] + [rn/view + {:style {:flex 1} + :padding-horizontal 16} (doall (for [{:keys [key type] - :as desc} descriptors - :let [descriptor (merge desc - {:state state})]] + :as desc} + descriptors + :let [descriptor (merge desc + {:state state})]] ^{:key key} [:<> (case type diff --git a/src/quo/previews/text.cljs b/src/quo/previews/text.cljs index cf912d923d..cac6c903fa 100644 --- a/src/quo/previews/text.cljs +++ b/src/quo/previews/text.cljs @@ -1,89 +1,99 @@ (ns quo.previews.text - (:require [reagent.core :as reagent] + (:require [quo.animated :as animated] [quo.core :as quo] - [quo.animated :as animated] - [quo.react-native :as rn] [quo.design-system.colors :as colors] - [quo.previews.preview :as preview])) + [quo.previews.preview :as preview] + [quo.react-native :as rn] + [reagent.core :as reagent])) -(def all-props (preview/list-comp [size [:tiny :small :base :large :x-large :xx-large] - weight [:regular :medium :semi-bold :bold :monospace]] - {:weight weight - :size size})) +(def all-props + (preview/list-comp [size [:tiny :small :base :large :x-large :xx-large] + weight [:regular :medium :semi-bold :bold :monospace]] + {:weight weight + :size size})) -(def descriptor [{:label "Size:" - :key :size - :type :select - :options [{:key :tiny - :value "Tiny"} - {:key :small - :value "Small"} - {:key :base - :value "Base"} - {:key :large - :value "Large"} - {:key :x-large - :value "X-Large"} - {:key :xx-large - :value "XX-Large"}]} - {:label "Weight:" - :key :weight - :type :select - :options [{:key :regular - :value "Regular"} - {:key :medium - :value "Medium"} - {:key :semi-bold - :value "Semi-bold"} - {:key :bold - :value "Bold"} - {:key :monospace - :value "Monospace"}]} - {:label "Color:" - :key :color - :type :select - :options [{:key :main - :value "main"} - {:key :secondary - :value "secondary"} - {:key :secondary-inverse - :value "secondary-inverse"} - {:key :link - :value "link"} - {:key :negative - :value "negative"} - {:key :positive - :value "positive"}]} - {:label "Animated:" - :key :animated? - :type :boolean}]) +(def descriptor + [{:label "Size:" + :key :size + :type :select + :options [{:key :tiny + :value "Tiny"} + {:key :small + :value "Small"} + {:key :base + :value "Base"} + {:key :large + :value "Large"} + {:key :x-large + :value "X-Large"} + {:key :xx-large + :value "XX-Large"}]} + {:label "Weight:" + :key :weight + :type :select + :options [{:key :regular + :value "Regular"} + {:key :medium + :value "Medium"} + {:key :semi-bold + :value "Semi-bold"} + {:key :bold + :value "Bold"} + {:key :monospace + :value "Monospace"}]} + {:label "Color:" + :key :color + :type :select + :options [{:key :main + :value "main"} + {:key :secondary + :value "secondary"} + {:key :secondary-inverse + :value "secondary-inverse"} + {:key :link + :value "link"} + {:key :negative + :value "negative"} + {:key :positive + :value "positive"}]} + {:label "Animated:" + :key :animated? + :type :boolean}]) -(defn render-item [props] - [rn/view {:style {:padding-vertical 24 - :padding-horizontal 16}} +(defn render-item + [props] + [rn/view + {:style {:padding-vertical 24 + :padding-horizontal 16}} [quo/text props (str "Text size " props " number 0 1x2")]]) -(defn cool-preview [] +(defn cool-preview + [] (let [state (reagent/atom {}) - animation (animated/value 0)] + animation (animated/value 0)] (fn [] - [rn/view {:margin-bottom 50 - :padding 16} + [rn/view + {:margin-bottom 50 + :padding 16} [animated/code {:exec (animated/set animation (animated/loop* {:duration 1000}))}] [preview/customizer state descriptor] [rn/view {:padding-vertical 16} - [quo/text (merge @state - (when (:animated? @state) - {:opacity animation})) + [quo/text + (merge @state + (when (:animated? @state) + {:opacity animation})) "This is a demo text 1 2 0 2x2 0x0"]]]))) -(defn preview-text [] - [rn/view {:background-color (:ui-background @colors/theme) - :flex 1} - [rn/flat-list {:flex 1 - :keyboardShouldPersistTaps :always - :header [cool-preview] - :data all-props - :render-fn render-item - :key-fn str}]]) +(defn preview-text + [] + [rn/view + {:background-color (:ui-background @colors/theme) + :flex 1} + [rn/flat-list + {:flex 1 + :keyboardShouldPersistTaps :always + :header [cool-preview] + :data all-props + :render-fn render-item + :key-fn str}]]) diff --git a/src/quo/previews/text_input.cljs b/src/quo/previews/text_input.cljs index bd54946008..f5453a3841 100644 --- a/src/quo/previews/text_input.cljs +++ b/src/quo/previews/text_input.cljs @@ -1,57 +1,62 @@ (ns quo.previews.text-input - (:require [reagent.core :as reagent] - [quo.core :as quo] - [quo.react-native :as rn] + (:require [quo.core :as quo] [quo.design-system.colors :as colors] - [quo.previews.preview :as preview])) + [quo.previews.preview :as preview] + [quo.react-native :as rn] + [reagent.core :as reagent])) -(def all-props (preview/list-comp [multiline [false true] - label [nil "Input label"] - default-value [nil "Test initial value"] - placeholder [nil "Placeholder value"] - before [nil {:icon :main-icons/search}] - after [nil {:icon :main-icons/close}] - error [nil "Something went wrong!"] - secure [false true] - show-cancel [false true]] - {:label label - :default-value default-value - :placeholder placeholder - :multiline multiline - :before before - :after after - :error error - :show-cancel show-cancel - :secure-text-entry secure})) +(def all-props + (preview/list-comp [multiline [false true] + label [nil "Input label"] + default-value [nil "Test initial value"] + placeholder [nil "Placeholder value"] + before [nil {:icon :main-icons/search}] + after [nil {:icon :main-icons/close}] + error [nil "Something went wrong!"] + secure [false true] + show-cancel [false true]] + {:label label + :default-value default-value + :placeholder placeholder + :multiline multiline + :before before + :after after + :error error + :show-cancel show-cancel + :secure-text-entry secure})) -(def descriptor [{:label "Multiline:" - :key :multiline - :type :boolean} - {:label "Show cancel:" - :key :show-cancel - :type :boolean} - {:label "Secure:" - :key :secure-text-entry - :type :boolean} - {:label "After icon:" - :key :after - :type :boolean} - {:label "Before icon:" - :key :before - :type :boolean} - {:label "Show error:" - :key :error - :type :boolean} - {:label "Label" - :key :label - :type :text}]) +(def descriptor + [{:label "Multiline:" + :key :multiline + :type :boolean} + {:label "Show cancel:" + :key :show-cancel + :type :boolean} + {:label "Secure:" + :key :secure-text-entry + :type :boolean} + {:label "After icon:" + :key :after + :type :boolean} + {:label "Before icon:" + :key :before + :type :boolean} + {:label "Show error:" + :key :error + :type :boolean} + {:label "Label" + :key :label + :type :text}]) -(defn render-item [props] - [rn/view {:style {:padding-horizontal 16 - :padding-vertical 24}} +(defn render-item + [props] + [rn/view + {:style {:padding-horizontal 16 + :padding-vertical 24}} [quo/text-input props]]) -(defn cool-preview [] +(defn cool-preview + [] (let [state (reagent/atom {:secure false :show-cancel false :multiline false @@ -60,22 +65,27 @@ after (reagent/cursor state [:after]) error (reagent/cursor state [:error])] (fn [] - [rn/view {:margin-bottom 50 - :padding 16} + [rn/view + {:margin-bottom 50 + :padding 16} [preview/customizer state descriptor] - [quo/text-input (merge @state - {:default-value nil - :placeholder "I'm a cool placeholder" - :before (when @before {:icon :main-icons/search}) - :after (when @after {:icon :main-icons/close}) - :error (when @error "Something went wrong!")})]]))) + [quo/text-input + (merge @state + {:default-value nil + :placeholder "I'm a cool placeholder" + :before (when @before {:icon :main-icons/search}) + :after (when @after {:icon :main-icons/close}) + :error (when @error "Something went wrong!")})]]))) -(defn preview-text [] - [rn/view {:background-color (:ui-background @colors/theme) - :flex 1} - [rn/flat-list {:flex 1 - :keyboardShouldPersistTaps :always - :header [cool-preview] - :data all-props - :render-fn render-item - :key-fn str}]]) +(defn preview-text + [] + [rn/view + {:background-color (:ui-background @colors/theme) + :flex 1} + [rn/flat-list + {:flex 1 + :keyboardShouldPersistTaps :always + :header [cool-preview] + :data all-props + :render-fn render-item + :key-fn str}]]) diff --git a/src/quo/previews/tooltip.cljs b/src/quo/previews/tooltip.cljs index d618ba6288..16696e0b65 100644 --- a/src/quo/previews/tooltip.cljs +++ b/src/quo/previews/tooltip.cljs @@ -1,30 +1,38 @@ (ns quo.previews.tooltip (:require [quo.core :as quo] - [quo.react-native :as rn] - [quo.design-system.colors :as colors]) + [quo.design-system.colors :as colors] + [quo.react-native :as rn]) (:require-macros [quo.previews.preview :as preview])) -(def all-props (preview/list-comp - [child [[quo/text {:size :small} "Simple text"] - [quo/text {:color :negative - :size :small} - "Error text"] - [rn/view {:width 100 :height 20 :background-color :red}] - [quo/text "Just text, but long. Officia autem est repellendus ad quia exercitationem veniam."]]] - child)) +(def all-props + (preview/list-comp + [child [[quo/text {:size :small} "Simple text"] + [quo/text + {:color :negative + :size :small} + "Error text"] + [rn/view {:width 100 :height 20 :background-color :red}] + [quo/text + "Just text, but long. Officia autem est repellendus ad quia exercitationem veniam."]]] + child)) -(defn render-item [children] +(defn render-item + [children] [rn/view {:margin-vertical 50} - [rn/view {:height 20 - :background-color "rgba(0,0,0,0.1)"}] + [rn/view + {:height 20 + :background-color "rgba(0,0,0,0.1)"}] [quo/tooltip {} children]]) -(defn preview-tooltip [] - [rn/view {:background-color (:ui-background @colors/theme) - :flex 1} - [rn/flat-list {:flex 1 - :keyboardShouldPersistTaps :always - :data all-props - :render-fn render-item - :key-fn str}]]) +(defn preview-tooltip + [] + [rn/view + {:background-color (:ui-background @colors/theme) + :flex 1} + [rn/flat-list + {:flex 1 + :keyboardShouldPersistTaps :always + :data all-props + :render-fn render-item + :key-fn str}]]) diff --git a/src/quo/react.clj b/src/quo/react.clj index 4e5ffc9582..7947dd34eb 100644 --- a/src/quo/react.clj +++ b/src/quo/react.clj @@ -1,9 +1,11 @@ (ns quo.react) -(defmacro maybe-js-deps [deps] +(defmacro maybe-js-deps + [deps] `(if ~deps (into-array ~deps) js/undefined)) -(defmacro with-deps-check [[prev-deps] f deps] +(defmacro with-deps-check + [[prev-deps] f deps] `(let [~prev-deps (quo.react/ref ~deps)] (when (not= @~prev-deps ~deps) (reset! ~prev-deps ~deps)) diff --git a/src/quo/react.cljs b/src/quo/react.cljs index 414f5d23c4..8cc88c51d7 100644 --- a/src/quo/react.cljs +++ b/src/quo/react.cljs @@ -2,12 +2,12 @@ (:refer-clojure :exclude [ref]) (:require ["react" :as react] [oops.core :refer [oget oset!]]) - (:require-macros [quo.react :refer [with-deps-check - maybe-js-deps]])) + (:require-macros [quo.react :refer [with-deps-check maybe-js-deps]])) (def create-ref react/createRef) -(defn current-ref [ref] +(defn current-ref + [ref] (oget ref "current")) ;; Inspired from UIX, Rum and Rumext @@ -16,33 +16,35 @@ (oset! ref "current" val) val) -(defn set-native-props [^js ref ^js props] +(defn set-native-props + [^js ref ^js props] (when-let [curr-ref ^js (current-ref ref)] (.setNativeProps curr-ref props))) (deftype StateHook [value set-value] cljs.core/IHash - (-hash [o] (goog/getUid o)) + (-hash [o] (goog/getUid o)) cljs.core/IDeref - (-deref [_o] - value) + (-deref [_o] + value) cljs.core/IReset - (-reset! [_o new-value] - (set-value new-value)) + (-reset! [_o new-value] + (set-value new-value)) cljs.core/ISwap - (-swap! [_o f] - (set-value f)) - (-swap! [_o f a] - (set-value #(f % a))) - (-swap! [_o f a b] - (set-value #(f % a b))) - (-swap! [_o f a b xs] - (set-value #(apply f % a b xs)))) + (-swap! [_o f] + (set-value f)) + (-swap! [_o f a] + (set-value #(f % a))) + (-swap! [_o f a b] + (set-value #(f % a b))) + (-swap! [_o f a b xs] + (set-value #(apply f % a b xs)))) -(defn state [value] +(defn state + [value] (let [[value set-value] (react/useState value) sh (react/useMemo #(StateHook. value set-value) #js [])] (react/useMemo (fn [] @@ -51,31 +53,33 @@ sh) #js [value set-value]))) -(defn use-ref [val] +(defn use-ref + [val] (let [ref (react/useRef val)] (reify - cljs.core/IHash - (-hash [_] (goog/getUid ref)) + cljs.core/IHash + (-hash [_] (goog/getUid ref)) - cljs.core/IDeref - (-deref [_] - (current-ref ref)) + cljs.core/IDeref + (-deref [_] + (current-ref ref)) - cljs.core/IReset - (-reset! [_ new-value] - (set-ref-val! ref new-value)) + cljs.core/IReset + (-reset! [_ new-value] + (set-ref-val! ref new-value)) - cljs.core/ISwap - (-swap! [_ f] - (-reset! ref (f (current-ref ref)))) - (-swap! [_ f a] - (-reset! ref (f (current-ref ref) a))) - (-swap! [_ f a b] - (-reset! ref (f (current-ref ref) a b))) - (-swap! [_ f a b xs] - (-reset! ref (apply f (current-ref ref) a b xs)))))) + cljs.core/ISwap + (-swap! [_ f] + (-reset! ref (f (current-ref ref)))) + (-swap! [_ f a] + (-reset! ref (f (current-ref ref) a))) + (-swap! [_ f a b] + (-reset! ref (f (current-ref ref) a b))) + (-swap! [_ f a b xs] + (-reset! ref (apply f (current-ref ref) a b xs)))))) -(defn ref [value] +(defn ref + [value] (let [vref (use-ref value)] (react/useMemo (fn [] vref) #js []))) @@ -125,7 +129,8 @@ (def memo react/memo) -(defn get-children [^js children] +(defn get-children + [^js children] (->> children (react/Children.toArray) (into []))) diff --git a/src/quo/react_native.cljs b/src/quo/react_native.cljs index f71a5fba5d..1b1df5c451 100644 --- a/src/quo/react_native.cljs +++ b/src/quo/react_native.cljs @@ -1,13 +1,13 @@ (ns quo.react-native - (:require [reagent.core :as reagent] + (:require ["@react-native-community/hooks" :as hooks] + ["react-native" :as rn] + ["react-native-draggable-flatlist" :default DraggableFlatList] + ["react-native-hole-view" :refer (RNHoleView)] + ["react-native-navigation" :refer (Navigation)] + ["rn-emoji-keyboard" :refer (EmojiKeyboard)] [cljs-bean.core :as bean] [quo.platform :as platform] - ["react-native" :as rn] - ["@react-native-community/hooks" :as hooks] - ["react-native-navigation" :refer (Navigation)] - ["react-native-hole-view" :refer (RNHoleView)] - ["rn-emoji-keyboard" :refer (EmojiKeyboard)] - ["react-native-draggable-flatlist" :default DraggableFlatList])) + [reagent.core :as reagent])) (def hole-view (reagent/adapt-react-class RNHoleView)) @@ -25,7 +25,9 @@ (def image (reagent/adapt-react-class (.-Image rn))) (def text (reagent/adapt-react-class (.-Text ^js rn))) -(defn resolve-asset-source [uri] (js->clj (.resolveAssetSource ^js (.-Image ^js rn) uri) :keywordize-keys true)) +(defn resolve-asset-source + [uri] + (js->clj (.resolveAssetSource ^js (.-Image ^js rn) uri) :keywordize-keys true)) (def scroll-view (reagent/adapt-react-class (.-ScrollView ^js rn))) (def modal (reagent/adapt-react-class (.-Modal ^js rn))) @@ -42,11 +44,12 @@ (.then (.constants Navigation) (fn [^js consts] - (reset! navigation-const {:top-bar-height (.-topBarHeight consts) + (reset! navigation-const {:top-bar-height (.-topBarHeight consts) :bottom-tabs-height (.-bottomTabsHeight consts) - :status-bar-height (.-statusBarHeight consts)}))) + :status-bar-height (.-statusBarHeight consts)}))) -(defn keyboard-avoiding-view [] +(defn keyboard-avoiding-view + [] (let [this (reagent/current-component) props (reagent/props this)] (into [keyboard-avoiding-view-class @@ -66,7 +69,8 @@ (def pan-responder (.-PanResponder ^js rn)) -(defn create-pan-responder [opts] +(defn create-pan-responder + [opts] (.create ^js pan-responder (clj->js opts))) (def animated (.-Animated rn)) @@ -79,82 +83,99 @@ (def animated-view (reagent/adapt-react-class (.-View ^js animated))) -(def ui-manager (.-UIManager ^js rn)) +(def ui-manager (.-UIManager ^js rn)) (def layout-animation (.-LayoutAnimation ^js rn)) (def configure-next (.-configureNext ^js layout-animation)) (def create-animation (.-create ^js layout-animation)) -(def layout-animation-presets {:ease-in-ease-out (-> ^js layout-animation .-Presets .-easeInEaseOut) - :linear (-> ^js layout-animation .-Presets .-linear) - :spring (-> ^js layout-animation .-Presets .-spring)}) +(def layout-animation-presets + {:ease-in-ease-out (-> ^js layout-animation .-Presets .-easeInEaseOut) + :linear (-> ^js layout-animation .-Presets .-linear) + :spring (-> ^js layout-animation .-Presets .-spring)}) -(def layout-animation-types {:spring (-> ^js layout-animation .-Types .-spring) - :linear (-> ^js layout-animation .-Types .-linear) - :ease-in-ease-out (-> ^js layout-animation .-Types .-easeInEaseOut) - :ease-in (-> ^js layout-animation .-Types .-easeIn) - :ease-out (-> ^js layout-animation .-Types .-easeOut)}) +(def layout-animation-types + {:spring (-> ^js layout-animation .-Types .-spring) + :linear (-> ^js layout-animation .-Types .-linear) + :ease-in-ease-out (-> ^js layout-animation .-Types .-easeInEaseOut) + :ease-in (-> ^js layout-animation .-Types .-easeIn) + :ease-out (-> ^js layout-animation .-Types .-easeOut)}) -(def layout-animation-properties {:opacity (-> ^js layout-animation .-Properties .-opacity) - :scale-x (-> ^js layout-animation .-Properties .-scaleX) - :scale-y (-> ^js layout-animation .-Properties .-scaleY) - :scale-xy (-> ^js layout-animation .-Properties .-scaleXY)}) +(def layout-animation-properties + {:opacity (-> ^js layout-animation .-Properties .-opacity) + :scale-x (-> ^js layout-animation .-Properties .-scaleX) + :scale-y (-> ^js layout-animation .-Properties .-scaleY) + :scale-xy (-> ^js layout-animation .-Properties .-scaleXY)}) -(def custom-animations {:ease-opacity-200 #js {:duration 200 - :create #js {:type (:ease-in-ease-out layout-animation-types) - :property (:opacity layout-animation-properties)} - :update #js {:type (:ease-in-ease-out layout-animation-types) - :property (:opacity layout-animation-properties)} - :delete #js {:type (:ease-in-ease-out layout-animation-types) - :property (:opacity layout-animation-properties)}}}) +(def custom-animations + {:ease-opacity-200 #js + {:duration 200 + :create #js + {:type (:ease-in-ease-out layout-animation-types) + :property (:opacity layout-animation-properties)} + :update #js + {:type (:ease-in-ease-out layout-animation-types) + :property (:opacity layout-animation-properties)} + :delete #js + {:type (:ease-in-ease-out layout-animation-types) + :property (:opacity layout-animation-properties)}}}) (defonce enable-layout-animations - (when platform/android? - (.setLayoutAnimationEnabledExperimental ^js ui-manager true))) + (when platform/android? + (.setLayoutAnimationEnabledExperimental ^js ui-manager true))) (def activity-indicator (reagent/adapt-react-class (.-ActivityIndicator ^js rn))) ;; Flat-list (def ^:private rn-flat-list (reagent/adapt-react-class (.-FlatList ^js rn))) -(defn- wrap-render-fn [f render-data] +(defn- wrap-render-fn + [f render-data] (fn [data] (reagent/as-element [f (.-item ^js data) (.-index ^js data) (.-separators ^js data) render-data (.-isActive ^js data) (.-drag ^js data)]))) -(defn- wrap-on-drag-end-fn [f] +(defn- wrap-on-drag-end-fn + [f] (fn [data] (f (.-from ^js data) (.-to ^js data) (.-data ^js data)))) -(defn- wrap-key-fn [f] +(defn- wrap-key-fn + [f] (fn [data index] {:post [(some? %)]} (f data index))) (defn base-list-props - [{:keys [key-fn render-fn empty-component header footer separator data render-data on-drag-end-fn] :as props}] - (merge {:data (to-array data)} - (when key-fn {:keyExtractor (wrap-key-fn key-fn)}) - (when render-fn {:renderItem (wrap-render-fn render-fn render-data)}) - (when separator {:ItemSeparatorComponent (fn [] (reagent/as-element separator))}) - (when empty-component {:ListEmptyComponent (fn [] (reagent/as-element empty-component))}) - (when header {:ListHeaderComponent (reagent/as-element header)}) - (when footer {:ListFooterComponent (reagent/as-element footer)}) - (when on-drag-end-fn {:onDragEnd (wrap-on-drag-end-fn on-drag-end-fn)}) - (dissoc props :data :header :footer :empty-component :separator :render-fn :key-fn :on-drag-end-fn))) + [{:keys [key-fn render-fn empty-component header footer separator data render-data on-drag-end-fn] + :as props}] + (merge + {:data (to-array data)} + (when key-fn {:keyExtractor (wrap-key-fn key-fn)}) + (when render-fn {:renderItem (wrap-render-fn render-fn render-data)}) + (when separator {:ItemSeparatorComponent (fn [] (reagent/as-element separator))}) + (when empty-component {:ListEmptyComponent (fn [] (reagent/as-element empty-component))}) + (when header {:ListHeaderComponent (reagent/as-element header)}) + (when footer {:ListFooterComponent (reagent/as-element footer)}) + (when on-drag-end-fn {:onDragEnd (wrap-on-drag-end-fn on-drag-end-fn)}) + (dissoc props :data :header :footer :empty-component :separator :render-fn :key-fn :on-drag-end-fn))) -(defn flat-list [props] +(defn flat-list + [props] [rn-flat-list (base-list-props props)]) -(defn draggable-flat-list [props] +(defn draggable-flat-list + [props] [rn-draggable-flatlist (base-list-props props)]) -(defn animated-flat-list [props] +(defn animated-flat-list + [props] [animated-flat-list-class (base-list-props props)]) ;; Hooks -(defn use-window-dimensions [] +(defn use-window-dimensions + [] (let [window (rn/useWindowDimensions)] {:font-scale (.-fontScale window) :height (.-height ^js window) @@ -163,12 +184,14 @@ (def use-back-handler (.-useBackHandler hooks)) -(defn use-keyboard [] +(defn use-keyboard + [] (let [kb (.useKeyboard hooks)] {:keyboard-shown (.-keyboardShown ^js kb) :keyboard-height (.-keyboardHeight ^js kb)})) -(defn use-layout [] +(defn use-layout + [] (let [{:keys [onLayout x y height width]} (bean/bean (.useLayout hooks))] {:on-layout onLayout :x x diff --git a/src/quo/theme.cljs b/src/quo/theme.cljs index 212e55a2c5..b07cf19e50 100644 --- a/src/quo/theme.cljs +++ b/src/quo/theme.cljs @@ -4,13 +4,16 @@ (def theme (reagent/atom nil)) -(defn dark? [] +(defn dark? + [] (= :dark @theme)) -(defn get-theme [] +(defn get-theme + [] @theme) -(defn set-theme [value] +(defn set-theme + [value] (reset! theme value) (reset! colors/theme (case value :dark colors/dark-theme diff --git a/src/quo2/components/avatars/account_avatar.cljs b/src/quo2/components/avatars/account_avatar.cljs index fb226276c2..2a22f3dd83 100644 --- a/src/quo2/components/avatars/account_avatar.cljs +++ b/src/quo2/components/avatars/account_avatar.cljs @@ -1,14 +1,15 @@ (ns quo2.components.avatars.account-avatar - (:require [react-native.core :as rn] + (:require [quo2.components.icon :as icons] [quo2.foundations.colors :as colors] - [quo2.components.icon :as icons] - [quo2.theme :as theme])) + [quo2.theme :as theme] + [react-native.core :as rn])) (def icon-color-value {:dark (get-in colors/customization [:dark :purple]) :light (get-in colors/customization [:light :purple])}) -(defn get-border-radius [size] +(defn get-border-radius + [size] (case size 80 16 48 12 @@ -16,7 +17,8 @@ 24 8 20 6)) -(defn get-inner-icon-sizes [size] +(defn get-inner-icon-sizes + [size] (case size 80 36 48 24 @@ -28,17 +30,18 @@ [{:keys [size icon] :or {size 80 icon :main-icons/placeholder}}] - (let [icon-color (if (theme/dark?) - (:dark icon-color-value) - (:light icon-color-value)) + (let [icon-color (if (theme/dark?) + (:dark icon-color-value) + (:light icon-color-value)) avatar-border-radius (get-border-radius size) - inner-icon-size (get-inner-icon-sizes size)] - [rn/view {:style {:width size - :background-color icon-color - :height size - :border-radius avatar-border-radius - :justify-content :center - :align-items :center}} + inner-icon-size (get-inner-icon-sizes size)] + [rn/view + {:style {:width size + :background-color icon-color + :height size + :border-radius avatar-border-radius + :justify-content :center + :align-items :center}} [icons/icon icon {:no-color true :size inner-icon-size}]])) diff --git a/src/quo2/components/avatars/channel_avatar.cljs b/src/quo2/components/avatars/channel_avatar.cljs index 4a63c5781d..01d9395d14 100644 --- a/src/quo2/components/avatars/channel_avatar.cljs +++ b/src/quo2/components/avatars/channel_avatar.cljs @@ -1,41 +1,47 @@ (ns quo2.components.avatars.channel-avatar - (:require [quo2.foundations.colors :as colors] - [react-native.core :as rn] - [quo2.components.icon :as icons] + (:require [quo2.components.icon :as icons] [quo2.components.markdown.text :as text] - [quo2.theme :as theme])) + [quo2.foundations.colors :as colors] + [quo2.theme :as theme] + [react-native.core :as rn])) -(defn channel-avatar [{:keys [big? locked? emoji-background-color emoji]}] +(defn channel-avatar + [{:keys [big? locked? emoji-background-color emoji]}] (let [lock-exists? (some? locked?) dark? (theme/dark?)] - [rn/view {:style {:width (if big? 32 24) - :height (if big? 32 24) - :border-radius (if big? 32 24) - :justify-content :center - :align-items :center - :background-color emoji-background-color}} - [rn/view {:style {:display :flex - :justify-content :center - :align-items :center}} - [text/text {:size (if big? - :paragraph-1 - :label)} emoji] + [rn/view + {:style {:width (if big? 32 24) + :height (if big? 32 24) + :border-radius (if big? 32 24) + :justify-content :center + :align-items :center + :background-color emoji-background-color}} + [rn/view + {:style {:display :flex + :justify-content :center + :align-items :center}} + [text/text + {:size (if big? + :paragraph-1 + :label)} emoji] (when lock-exists? - [rn/view {:style {:position :absolute - :left (if big? - 14 - 8) - :top (if big? - 15 - 8) - :background-color (if dark? - colors/neutral-90 - colors/white) - :border-radius 15 - :padding 2}} - [icons/icon (if locked? - :main-icons/locked - :main-icons/unlocked) + [rn/view + {:style {:position :absolute + :left (if big? + 14 + 8) + :top (if big? + 15 + 8) + :background-color (if dark? + colors/neutral-90 + colors/white) + :border-radius 15 + :padding 2}} + [icons/icon + (if locked? + :main-icons/locked + :main-icons/unlocked) {:color (if dark? colors/neutral-40 colors/neutral-50) diff --git a/src/quo2/components/avatars/group_avatar.cljs b/src/quo2/components/avatars/group_avatar.cljs index d5fab5ac39..b4f70be3e8 100644 --- a/src/quo2/components/avatars/group_avatar.cljs +++ b/src/quo2/components/avatars/group_avatar.cljs @@ -1,6 +1,6 @@ (ns quo2.components.avatars.group-avatar - (:require [quo2.foundations.colors :as colors] - [quo2.components.icon :as icon] + (:require [quo2.components.icon :as icon] + [quo2.foundations.colors :as colors] [react-native.core :as rn])) (def sizes @@ -11,16 +11,20 @@ :medium 32 :large 48}}) -;; TODO: this implementation does not support group display picture (can only display default group icon). -(defn group-avatar [_] +;; TODO: this implementation does not support group display picture (can only display default group +;; icon). +(defn group-avatar + [_] (fn [{:keys [color size]}] (let [container-size (get-in sizes [:container size]) icon-size (get-in sizes [:icon size])] - [rn/view {:width container-size - :height container-size - :align-items :center - :justify-content :center - :border-radius (/ container-size 2) - :background-color (colors/custom-color-by-theme color 50 60)} - [icon/icon :i/group {:size icon-size - :color colors/white-opa-70}]]))) + [rn/view + {:width container-size + :height container-size + :align-items :center + :justify-content :center + :border-radius (/ container-size 2) + :background-color (colors/custom-color-by-theme color 50 60)} + [icon/icon :i/group + {:size icon-size + :color colors/white-opa-70}]]))) diff --git a/src/quo2/components/avatars/icon_avatar.cljs b/src/quo2/components/avatars/icon_avatar.cljs index c8ed2c0f5f..e62b5fa60f 100644 --- a/src/quo2/components/avatars/icon_avatar.cljs +++ b/src/quo2/components/avatars/icon_avatar.cljs @@ -1,7 +1,7 @@ (ns quo2.components.avatars.icon-avatar - (:require [react-native.core :as rn] + (:require [quo2.components.icon :as icons] [quo2.foundations.colors :as colors] - [quo2.components.icon :as icons])) + [react-native.core :as rn])) (def sizes {:big 48 @@ -12,17 +12,19 @@ [{:keys [size icon color opacity] :or {opacity 20}}] (let [component-size (size sizes) - circle-color (colors/custom-color color 50 opacity) - icon-color (colors/custom-color-by-theme color 50 60) - icon-size (case size - :big 20 - :medium 16 - :small 12)] - [rn/view {:style {:width component-size - :height component-size - :border-radius component-size - :background-color circle-color - :justify-content :center - :align-items :center}} - [icons/icon icon {:size icon-size - :color icon-color}]])) + circle-color (colors/custom-color color 50 opacity) + icon-color (colors/custom-color-by-theme color 50 60) + icon-size (case size + :big 20 + :medium 16 + :small 12)] + [rn/view + {:style {:width component-size + :height component-size + :border-radius component-size + :background-color circle-color + :justify-content :center + :align-items :center}} + [icons/icon icon + {:size icon-size + :color icon-color}]])) diff --git a/src/quo2/components/avatars/user_avatar.cljs b/src/quo2/components/avatars/user_avatar.cljs index 8a5718bcd5..01b115b902 100644 --- a/src/quo2/components/avatars/user_avatar.cljs +++ b/src/quo2/components/avatars/user_avatar.cljs @@ -1,91 +1,96 @@ (ns quo2.components.avatars.user-avatar - (:require [react-native.core :as rn] - [react-native.fast-image :as fast-image] + (:require [clojure.string :refer [blank? split upper-case]] + [quo2.components.icon :as icons] [quo2.components.markdown.text :as text] [quo2.foundations.colors :as colors] - [quo2.components.icon :as icons] [quo2.theme :refer [dark?]] - [clojure.string :refer [upper-case split blank?]])) + [react-native.core :as rn] + [react-native.fast-image :as fast-image])) -(def sizes {:big {:outer 80 - :inner 72 - :status-indicator 20 - :status-indicator-border 4 - :font-size :heading-1} - :medium {:outer 48 - :inner 44 - :status-indicator 12 - :status-indicator-border 2 - :font-size :paragraph-1} - :small {:outer 32 - :inner 28 - :status-indicator 12 - :status-indicator-border 2 - :font-size :paragraph-2} - :xs {:outer 24 - :inner 24 - :status-indicator 0 - :status-indicator-border 0 - :font-size :paragraph-2} - :xxs {:outer 20 - :inner 20 - :status-indicator 0 - :status-indicator-border 0 - :font-size :label} - :xxxs {:outer 16 - :inner 16 - :status-indicator 0 - :status-indicator-border 0 - :font-size :label}}) +(def sizes + {:big {:outer 80 + :inner 72 + :status-indicator 20 + :status-indicator-border 4 + :font-size :heading-1} + :medium {:outer 48 + :inner 44 + :status-indicator 12 + :status-indicator-border 2 + :font-size :paragraph-1} + :small {:outer 32 + :inner 28 + :status-indicator 12 + :status-indicator-border 2 + :font-size :paragraph-2} + :xs {:outer 24 + :inner 24 + :status-indicator 0 + :status-indicator-border 0 + :font-size :paragraph-2} + :xxs {:outer 20 + :inner 20 + :status-indicator 0 + :status-indicator-border 0 + :font-size :label} + :xxxs {:outer 16 + :inner 16 + :status-indicator 0 + :status-indicator-border 0 + :font-size :label}}) (defn dot-indicator [size status-indicator? online? ring? dark?] (when status-indicator? - (let [dimensions (get-in sizes [size :status-indicator]) + (let [dimensions (get-in sizes [size :status-indicator]) border-width (get-in sizes [size :status-indicator-border]) - right (case size - :big 2 - :medium 0 - :small -2 - 0) - bottom (case size - :big (if ring? - -1 - 2) - :medium (if ring? - 0 - -2) - :small (if ring? - -2 - -2) - 0)] - [rn/view {:style {:background-color (if online? - colors/success-50 - colors/neutral-40) - :width dimensions - :height dimensions - :border-width border-width - :border-radius dimensions - :border-color (if dark? - colors/neutral-100 - colors/white) - :position :absolute - :bottom bottom - :right right}}]))) + right (case size + :big 2 + :medium 0 + :small -2 + 0) + bottom (case size + :big (if ring? + -1 + 2) + :medium (if ring? + 0 + -2) + :small (if ring? + -2 + -2) + 0)] + [rn/view + {:style {:background-color (if online? + colors/success-50 + colors/neutral-40) + :width dimensions + :height dimensions + :border-width border-width + :border-radius dimensions + :border-color (if dark? + colors/neutral-100 + colors/white) + :position :absolute + :bottom bottom + :right right}}]))) -(defn container-styling [inner-dimensions outer-dimensions] - {:width inner-dimensions - :position :absolute - :top (/ (- outer-dimensions inner-dimensions) 2) - :left (/ (- outer-dimensions inner-dimensions) 2) - :height inner-dimensions +(defn container-styling + [inner-dimensions outer-dimensions] + {:width inner-dimensions + :position :absolute + :top (/ (- outer-dimensions inner-dimensions) 2) + :left (/ (- outer-dimensions inner-dimensions) 2) + :height inner-dimensions :border-radius inner-dimensions}) -(defn container [inner-dimensions outer-dimensions & children] - [rn/view {:style (merge {:background-color (colors/custom-color-by-theme :turquoise 50 60) - :justify-content :center - :align-items :center} - (container-styling inner-dimensions outer-dimensions))} +(defn container + [inner-dimensions outer-dimensions & children] + [rn/view + {:style (merge {:background-color (colors/custom-color-by-theme :turquoise 50 60) + :justify-content :center + :align-items :center} + (container-styling inner-dimensions outer-dimensions))} children]) (def small-sizes #{:xs :xxs :xxxs}) @@ -112,9 +117,11 @@ identicon? (contains? identicon-sizes size) small? (contains? small-sizes size) outer-dimensions (get-in sizes [size :outer]) - inner-dimensions (get-in sizes [size (if ring? - :inner - :outer)]) + inner-dimensions (get-in sizes + [size + (if ring? + :inner + :outer)]) font-size (get-in sizes [size :font-size]) icon-text (if-not (or (blank? first-initial-letter) (blank? initials)) @@ -122,22 +129,26 @@ first-initial-letter initials) "")] - [rn/view {:accessibility-label :user-avatar - :style {:width outer-dimensions - :height outer-dimensions - :border-radius outer-dimensions}} + [rn/view + {:accessibility-label :user-avatar + :style {:width outer-dimensions + :height outer-dimensions + :border-radius outer-dimensions}} (when (and ring? identicon?) - [icons/icon :i/identicon-ring {:size outer-dimensions - :no-color true}]) + [icons/icon :i/identicon-ring + {:size outer-dimensions + :no-color true}]) (if profile-picture ;; display image - [fast-image/fast-image {:source {:uri profile-picture} - :style (container-styling inner-dimensions outer-dimensions)}] + [fast-image/fast-image + {:source {:uri profile-picture} + :style (container-styling inner-dimensions outer-dimensions)}] ;; else display initials [container inner-dimensions outer-dimensions ^{:key :icon-text} - [text/text {:weight :semi-bold - :size font-size - :style {:color colors/white-opa-70}} + [text/text + {:weight :semi-bold + :size font-size + :style {:color colors/white-opa-70}} (upper-case icon-text)]]) [dot-indicator size status-indicator? online? ring? (dark?)]])) diff --git a/src/quo2/components/avatars/wallet_user_avatar.cljs b/src/quo2/components/avatars/wallet_user_avatar.cljs index e910f6686d..47874af004 100644 --- a/src/quo2/components/avatars/wallet_user_avatar.cljs +++ b/src/quo2/components/avatars/wallet_user_avatar.cljs @@ -1,31 +1,35 @@ (ns quo2.components.avatars.wallet-user-avatar - (:require [react-native.core :as rn] - [quo2.foundations.colors :as colors] + (:require [clojure.string :as string] [quo2.components.markdown.text :as text] - [clojure.string :as string])) + [quo2.foundations.colors :as colors] + [react-native.core :as rn])) -(def circle-sizes {:small 20 - :medium 32 - :large 48 - :x-large 80}) +(def circle-sizes + {:small 20 + :medium 32 + :large 48 + :x-large 80}) -(def font-sizes {:small :label - :medium :paragraph-2 - :large :paragraph-1 - :x-large :heading-1}) +(def font-sizes + {:small :label + :medium :paragraph-2 + :large :paragraph-1 + :x-large :heading-1}) -(def font-weights {:small :medium - :medium :semi-bold - :large :semi-bold - :x-large :medium}) +(def font-weights + {:small :medium + :medium :semi-bold + :large :semi-bold + :x-large :medium}) (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 color size] + :or {f-name "John" + l-name "Doe" + color :red + size :x-large}}] (let [circle-size (size circle-sizes) small? (= size :small) f-name-initial (-> f-name @@ -36,16 +40,18 @@ (subs 0 1)) circle-color (colors/custom-color color 50 20) text-color (colors/custom-color-by-theme color 50 60)] - [rn/view {:style {:width circle-size - :height circle-size - :border-radius circle-size - :text-align :center - :justify-content :center - :align-items :center - :background-color circle-color}} - [text/text {:size (size font-sizes) - :weight (size font-weights) - :style {:color text-color}} + [rn/view + {:style {:width circle-size + :height circle-size + :border-radius circle-size + :text-align :center + :justify-content :center + :align-items :center + :background-color circle-color}} + [text/text + {:size (size font-sizes) + :weight (size font-weights) + :style {:color text-color}} (if small? (str f-name-initial) (str f-name-initial l-name-initial))]])) diff --git a/src/quo2/components/buttons/button.cljs b/src/quo2/components/buttons/button.cljs index 602eeff049..852afbdf87 100644 --- a/src/quo2/components/buttons/button.cljs +++ b/src/quo2/components/buttons/button.cljs @@ -1,142 +1,143 @@ (ns quo2.components.buttons.button - (:require [react-native.core :as rn] - [quo2.foundations.colors :as colors] + (:require [quo2.components.icon :as quo2.icons] [quo2.components.markdown.text :as text] + [quo2.foundations.colors :as colors] [quo2.theme :as theme] - [reagent.core :as reagent] - [quo2.components.icon :as quo2.icons])) + [react-native.core :as rn] + [reagent.core :as reagent])) (def themes - {:light {:primary {:icon-color colors/white - :label-color colors/white - :background-color {:default colors/primary-50 - :pressed colors/primary-60 - :disabled colors/primary-50}} - :secondary {:icon-color colors/primary-50 - :label-color colors/primary-50 - :background-color {:default colors/primary-50-opa-20 - :pressed colors/primary-50-opa-40 - :disabled colors/primary-50-opa-20}} - :grey {:icon-color colors/neutral-100 - :icon-secondary-color colors/neutral-50 - :label-color colors/neutral-100 - :background-color {:default colors/neutral-10 - :pressed colors/neutral-20 - :disabled colors/neutral-10}} - :dark-grey {:icon-color colors/neutral-100 - :icon-secondary-color colors/neutral-50 - :label-color colors/neutral-100 - :background-color {:default colors/neutral-20 - :pressed colors/neutral-30 - :disabled colors/neutral-20}} - :outline {:icon-color colors/neutral-50 - :icon-secondary-color colors/neutral-50 - :label-color colors/neutral-100 - :border-color {:default colors/neutral-20 - :pressed colors/neutral-40 - :disabled colors/neutral-20}} - :ghost {:icon-color colors/neutral-50 - :icon-secondary-color colors/neutral-50 - :label-color colors/neutral-100 - :background-color {:pressed colors/neutral-10}} - :danger {:icon-color colors/white - :label-color colors/white - :background-color {:default colors/danger-50 - :pressed colors/danger-60 - :disabled colors/danger-50}} - :positive {:icon-color colors/white - :label-color colors/white - :background-color {:default colors/success-50 - :pressed colors/success-60 - :disabled colors/success-50-opa-30}} - :photo-bg {:icon-color colors/neutral-100 - :icon-secondary-color colors/neutral-80-opa-40 - :label-color colors/neutral-100 - :background-color {:default colors/white-opa-40 - :pressed colors/white-opa-50 - :disabled colors/white-opa-40}} - :blur-bg {:icon-color colors/neutral-100 - :icon-secondary-color colors/neutral-80-opa-40 - :label-color colors/neutral-100 - :background-color {:default colors/neutral-80-opa-5 - :pressed colors/neutral-80-opa-10 - :disabled colors/neutral-80-opa-5}} - :blur-bg-outline {:icon-color colors/neutral-100 - :icon-secondary-color colors/neutral-80-opa-40 - :label-color colors/neutral-100 - :border-color {:default colors/neutral-80-opa-10 - :pressed colors/neutral-80-opa-20 - :disabled colors/neutral-80-opa-10}} - :shell {:icon-color colors/white - :label-color colors/white - :background-color {:default colors/neutral-95 - :pressed colors/neutral-95 - :disabled colors/neutral-95}}} - :dark {:primary {:icon-color colors/white - :label-color colors/white - :background-color {:default colors/primary-60 - :pressed colors/primary-50 - :disabled colors/primary-60}} - :secondary {:icon-color colors/primary-50 - :label-color colors/primary-50 - :background-color {:default colors/primary-50-opa-20 - :pressed colors/primary-50-opa-30 - :disabled colors/primary-50-opa-20}} - :grey {:icon-color colors/white - :icon-secondary-color colors/neutral-40 - :label-color colors/white - :background-color {:default colors/neutral-80 - :pressed colors/neutral-60 - :disabled colors/neutral-80}} - :dark-grey {:icon-color colors/white - :icon-secondary-color colors/neutral-40 - :label-color colors/white - :background-color {:default colors/neutral-70 - :pressed colors/neutral-60 - :disabled colors/neutral-70}} - :outline {:icon-color colors/neutral-40 - :icon-secondary-color colors/neutral-40 - :label-color colors/white - :border-color {:default colors/neutral-70 - :pressed colors/neutral-60 - :disabled colors/neutral-70}} - :ghost {:icon-color colors/neutral-40 - :icon-secondary-color colors/neutral-40 - :label-color colors/white - :background-color {:pressed colors/neutral-80}} - :danger {:icon-color colors/white - :label-color colors/white - :background-color {:default colors/danger-60 - :pressed colors/danger-50 - :disabled colors/danger-60}} - :positive {:icon-color colors/white - :label-color colors/white - :background-color {:default colors/success-60 - :pressed colors/success-50 - :disabled colors/success-60-opa-30}} - :photo-bg {:icon-color colors/white - :icon-secondary-color colors/neutral-30 - :label-color colors/white - :background-color {:default colors/neutral-80-opa-40 - :pressed colors/neutral-80-opa-50 - :disabled colors/neutral-80-opa-40}} - :blur-bg {:icon-color colors/white - :icon-secondary-color colors/white-opa-40 - :label-color colors/white - :background-color {:default colors/white-opa-5 - :pressed colors/white-opa-10 - :disabled colors/white-opa-5}} - :blur-bg-outline {:icon-color colors/white - :icon-secondary-color colors/white-opa-40 - :label-color colors/white - :border-color {:default colors/white-opa-10 - :pressed colors/white-opa-20 - :disabled colors/white-opa-5}} - :shell {:icon-color colors/white - :label-color colors/white - :background-color {:default colors/neutral-95}}}}) + {:light {:primary {:icon-color colors/white + :label-color colors/white + :background-color {:default colors/primary-50 + :pressed colors/primary-60 + :disabled colors/primary-50}} + :secondary {:icon-color colors/primary-50 + :label-color colors/primary-50 + :background-color {:default colors/primary-50-opa-20 + :pressed colors/primary-50-opa-40 + :disabled colors/primary-50-opa-20}} + :grey {:icon-color colors/neutral-100 + :icon-secondary-color colors/neutral-50 + :label-color colors/neutral-100 + :background-color {:default colors/neutral-10 + :pressed colors/neutral-20 + :disabled colors/neutral-10}} + :dark-grey {:icon-color colors/neutral-100 + :icon-secondary-color colors/neutral-50 + :label-color colors/neutral-100 + :background-color {:default colors/neutral-20 + :pressed colors/neutral-30 + :disabled colors/neutral-20}} + :outline {:icon-color colors/neutral-50 + :icon-secondary-color colors/neutral-50 + :label-color colors/neutral-100 + :border-color {:default colors/neutral-20 + :pressed colors/neutral-40 + :disabled colors/neutral-20}} + :ghost {:icon-color colors/neutral-50 + :icon-secondary-color colors/neutral-50 + :label-color colors/neutral-100 + :background-color {:pressed colors/neutral-10}} + :danger {:icon-color colors/white + :label-color colors/white + :background-color {:default colors/danger-50 + :pressed colors/danger-60 + :disabled colors/danger-50}} + :positive {:icon-color colors/white + :label-color colors/white + :background-color {:default colors/success-50 + :pressed colors/success-60 + :disabled colors/success-50-opa-30}} + :photo-bg {:icon-color colors/neutral-100 + :icon-secondary-color colors/neutral-80-opa-40 + :label-color colors/neutral-100 + :background-color {:default colors/white-opa-40 + :pressed colors/white-opa-50 + :disabled colors/white-opa-40}} + :blur-bg {:icon-color colors/neutral-100 + :icon-secondary-color colors/neutral-80-opa-40 + :label-color colors/neutral-100 + :background-color {:default colors/neutral-80-opa-5 + :pressed colors/neutral-80-opa-10 + :disabled colors/neutral-80-opa-5}} + :blur-bg-outline {:icon-color colors/neutral-100 + :icon-secondary-color colors/neutral-80-opa-40 + :label-color colors/neutral-100 + :border-color {:default colors/neutral-80-opa-10 + :pressed colors/neutral-80-opa-20 + :disabled colors/neutral-80-opa-10}} + :shell {:icon-color colors/white + :label-color colors/white + :background-color {:default colors/neutral-95 + :pressed colors/neutral-95 + :disabled colors/neutral-95}}} + :dark {:primary {:icon-color colors/white + :label-color colors/white + :background-color {:default colors/primary-60 + :pressed colors/primary-50 + :disabled colors/primary-60}} + :secondary {:icon-color colors/primary-50 + :label-color colors/primary-50 + :background-color {:default colors/primary-50-opa-20 + :pressed colors/primary-50-opa-30 + :disabled colors/primary-50-opa-20}} + :grey {:icon-color colors/white + :icon-secondary-color colors/neutral-40 + :label-color colors/white + :background-color {:default colors/neutral-80 + :pressed colors/neutral-60 + :disabled colors/neutral-80}} + :dark-grey {:icon-color colors/white + :icon-secondary-color colors/neutral-40 + :label-color colors/white + :background-color {:default colors/neutral-70 + :pressed colors/neutral-60 + :disabled colors/neutral-70}} + :outline {:icon-color colors/neutral-40 + :icon-secondary-color colors/neutral-40 + :label-color colors/white + :border-color {:default colors/neutral-70 + :pressed colors/neutral-60 + :disabled colors/neutral-70}} + :ghost {:icon-color colors/neutral-40 + :icon-secondary-color colors/neutral-40 + :label-color colors/white + :background-color {:pressed colors/neutral-80}} + :danger {:icon-color colors/white + :label-color colors/white + :background-color {:default colors/danger-60 + :pressed colors/danger-50 + :disabled colors/danger-60}} + :positive {:icon-color colors/white + :label-color colors/white + :background-color {:default colors/success-60 + :pressed colors/success-50 + :disabled colors/success-60-opa-30}} + :photo-bg {:icon-color colors/white + :icon-secondary-color colors/neutral-30 + :label-color colors/white + :background-color {:default colors/neutral-80-opa-40 + :pressed colors/neutral-80-opa-50 + :disabled colors/neutral-80-opa-40}} + :blur-bg {:icon-color colors/white + :icon-secondary-color colors/white-opa-40 + :label-color colors/white + :background-color {:default colors/white-opa-5 + :pressed colors/white-opa-10 + :disabled colors/white-opa-5}} + :blur-bg-outline {:icon-color colors/white + :icon-secondary-color colors/white-opa-40 + :label-color colors/white + :border-color {:default colors/white-opa-10 + :pressed colors/white-opa-20 + :disabled colors/white-opa-5}} + :shell {:icon-color colors/white + :label-color colors/white + :background-color {:default colors/neutral-95}}}}) -(defn shape-style-container [type icon size] +(defn shape-style-container + [type icon size] {:border-radius (if (and icon (#{:primary :secondary :danger} type)) 24 (case size @@ -145,22 +146,43 @@ 32 10 24 8))}) -(defn style-container [type size disabled background-color border-color icon above width before after] +(defn style-container + [type size disabled background-color border-color icon above width before after] (merge {:height size :align-items :center :justify-content :center :flex-direction (if above :column :row) :background-color background-color :padding-horizontal (when-not (or icon before after) - (case size 56 16 40 16 32 12 24 8)) + (case size + 56 16 + 40 16 + 32 12 + 24 8)) :padding-left (when-not (or icon before) - (case size 56 16 40 16 32 12 24 8)) + (case size + 56 16 + 40 16 + 32 12 + 24 8)) :padding-right (when-not (or icon after) - (case size 56 16 40 16 32 12 24 8)) + (case size + 56 16 + 40 16 + 32 12 + 24 8)) :padding-top (when-not (or icon before after) - (case size 56 0 40 9 32 5 24 3)) + (case size + 56 0 + 40 9 + 32 5 + 24 3)) :padding-bottom (when-not (or icon before after) - (case size 56 0 40 9 32 5 24 4))} + (case size + 56 0 + 40 9 + 32 5 + 24 4))} (shape-style-container type icon size) (when width {:width width}) @@ -172,7 +194,8 @@ (when disabled {:opacity 0.3}))) -(defn community-themed? [type community-color] +(defn community-themed? + [type community-color] (and (= type :community) (string? community-color))) (defn button @@ -192,85 +215,97 @@ [button {:icon true} :main-icons/close-circle]" [_ _] (let [pressed (reagent/atom false)] - (fn [{:keys [on-press disabled type size community-color community-text-color before after above width - override-theme override-background-color - on-long-press accessibility-label icon icon-no-color style test-ID] - :or {type :primary - size 40}} - children] + (fn + [{:keys [on-press disabled type size community-color community-text-color before after above + width + override-theme override-background-color + on-long-press accessibility-label icon icon-no-color style test-ID] + :or {type :primary + size 40}} + children] (let [{:keys [icon-color icon-secondary-color background-color label-color border-color]} - (get-in themes [(or - override-theme - (theme/get-theme)) type]) - state (cond disabled :disabled @pressed :pressed :else :default) - icon-size (when (= 24 size) 12) + (get-in themes + [(or + override-theme + (theme/get-theme)) type]) + state (cond disabled :disabled + @pressed :pressed + :else :default) + icon-size (when (= 24 size) 12) icon-secondary-color (or icon-secondary-color icon-color)] - [rn/touchable-without-feedback (merge {:test-ID test-ID - :disabled disabled - :accessibility-label accessibility-label} - (when on-press - {:on-press (fn [] - (on-press))}) - (when on-long-press - {:on-long-press (fn [] - (on-long-press))}) - {:on-press-in (fn [] - (reset! pressed true))} - {:on-press-out (fn [] - (reset! pressed nil))}) + [rn/touchable-without-feedback + (merge {:test-ID test-ID + :disabled disabled + :accessibility-label accessibility-label} + (when on-press + {:on-press (fn [] + (on-press))}) + (when on-long-press + {:on-long-press (fn [] + (on-long-press))}) + {:on-press-in (fn [] + (reset! pressed true))} + {:on-press-out (fn [] + (reset! pressed nil))}) - [rn/view {:style (merge - (shape-style-container type icon size) - {:background-color - (if (= state :pressed) - (colors/theme-colors colors/neutral-100 colors/white) - :transparent)} - style)} - [rn/view {:style (merge - (style-container - type - size - disabled - (or override-background-color (get background-color state)) - (get border-color state) - icon - above - width - before - after) - (when - (community-themed? type community-color) - (merge - {:background-color community-color} - (when (= state :pressed) {:opacity 0.9}))))} + [rn/view + {:style (merge + (shape-style-container type icon size) + {:background-color + (if (= state :pressed) + (colors/theme-colors colors/neutral-100 colors/white) + :transparent)} + style)} + [rn/view + {:style (merge + (style-container + type + size + disabled + (or override-background-color (get background-color state)) + (get border-color state) + icon + above + width + before + after) + (when + (community-themed? type community-color) + (merge + {:background-color community-color} + (when (= state :pressed) {:opacity 0.9}))))} (when above [rn/view - [quo2.icons/icon above {:container-style {:margin-bottom 2} - :color icon-secondary-color - :size icon-size}]]) + [quo2.icons/icon above + {:container-style {:margin-bottom 2} + :color icon-secondary-color + :size icon-size}]]) (when before [rn/view - [quo2.icons/icon before {:container-style {:margin-left (if (= size 40) 12 8) - :margin-right 4} - :color icon-secondary-color - :size icon-size}]]) + [quo2.icons/icon before + {:container-style {:margin-left (if (= size 40) 12 8) + :margin-right 4} + :color icon-secondary-color + :size icon-size}]]) [rn/view (cond (or icon icon-no-color) - [quo2.icons/icon children {:color icon-color - :no-color icon-no-color - :size icon-size}] + [quo2.icons/icon children + {:color icon-color + :no-color icon-no-color + :size icon-size}] (string? children) - [text/text {:size (when (#{56 24} size) :paragraph-2) - :weight :medium - :number-of-lines 1 - :style {:color (if - (and - (community-themed? type community-color) - (string? community-text-color)) - community-text-color - label-color)}} + [text/text + {:size (when (#{56 24} size) :paragraph-2) + :weight :medium + :number-of-lines 1 + :style {:color (if + (and + (community-themed? type community-color) + (string? community-text-color)) + community-text-color + label-color)}} children] @@ -278,7 +313,8 @@ children)] (when after [rn/view - [quo2.icons/icon after {:container-style {:margin-left 4 - :margin-right (if (= size 40) 12 8)} - :color icon-secondary-color - :size icon-size}]])]]])))) + [quo2.icons/icon after + {:container-style {:margin-left 4 + :margin-right (if (= size 40) 12 8)} + :color icon-secondary-color + :size icon-size}]])]]])))) diff --git a/src/quo2/components/buttons/dynamic_button.cljs b/src/quo2/components/buttons/dynamic_button.cljs index 36cf7c5733..4751594a65 100644 --- a/src/quo2/components/buttons/dynamic_button.cljs +++ b/src/quo2/components/buttons/dynamic_button.cljs @@ -1,11 +1,12 @@ (ns quo2.components.buttons.dynamic-button - (:require [react-native.core :as rn] - [reagent.core :as reagent] - [quo2.components.icon :as icon] + (:require [quo2.components.icon :as icon] + [quo2.components.markdown.text :as text] [quo2.foundations.colors :as colors] - [quo2.components.markdown.text :as text])) + [react-native.core :as rn] + [reagent.core :as reagent])) -(defn- get-button-color [type pressed? customization-color] +(defn- get-button-color + [type pressed? customization-color] (if (#{:jump-to :mention} type) (if pressed? (colors/custom-color-by-theme customization-color 60 50) @@ -14,12 +15,14 @@ (colors/theme-colors colors/neutral-80-opa-80 colors/white-opa-80) (colors/theme-colors colors/neutral-80-opa-70 colors/white-opa-70)))) -(defn- get-icon-and-text-color [type] +(defn- get-icon-and-text-color + [type] (if (#{:jump-to :mention} type) colors/white (colors/theme-colors colors/white colors/neutral-100))) -(defn- icon-view [type] +(defn- icon-view + [type] [icon/icon (case type :jump-to :i/jump-to @@ -67,32 +70,34 @@ :active-opacity 1 :style {:padding 5} :accessibility-label type} - [rn/view {:style (merge - {:flex-direction :row - :height 24 - :border-radius 12 - :background-color (get-button-color type @pressed? (or customization-color :primary))} - style)} + [rn/view + {:style (merge + {:flex-direction :row + :height 24 + :border-radius 12 + :background-color (get-button-color type @pressed? (or customization-color :primary))} + style)} (when (#{:mention :search :search-with-label :bottom} type) [icon-view type]) (when (#{:jump-to :mention :notification-down :notification-up :search-with-label} type) - [text/text {:weight :medium - :size :paragraph-2 - :style {:color (get-icon-and-text-color type) - :margin-top 2.5 - :margin-bottom 3.5 - :margin-left (case type - :jump-to 8 - :mention 0 - :notification-down 8 - :notification-up 8 - :search-with-label 0) - :margin-right (case type - :jump-to 0 - :mention 8 - :notification-down 0 - :notification-up 0 - :search-with-label 8)}} + [text/text + {:weight :medium + :size :paragraph-2 + :style {:color (get-icon-and-text-color type) + :margin-top 2.5 + :margin-bottom 3.5 + :margin-left (case type + :jump-to 8 + :mention 0 + :notification-down 8 + :notification-up 8 + :search-with-label 0) + :margin-right (case type + :jump-to 0 + :mention 8 + :notification-down 0 + :notification-up 0 + :search-with-label 8)}} (case type :jump-to label :search-with-label label diff --git a/src/quo2/components/code/snippet.cljs b/src/quo2/components/code/snippet.cljs index 9e25206ab4..e09d160786 100644 --- a/src/quo2/components/code/snippet.cljs +++ b/src/quo2/components/code/snippet.cljs @@ -3,35 +3,36 @@ [cljs-bean.core :as bean] [clojure.string :as string] [oops.core :as oops] - [react-native.core :as rn] - [quo2.theme :as theme] [quo2.components.buttons.button :as button] [quo2.components.markdown.text :as text] [quo2.foundations.colors :as colors] - [reagent.core :as reagent] + [quo2.theme :as theme] + [react-native.core :as rn] [react-native.linear-gradient :as linear-gradient] [react-native.masked-view :as masked-view] - [react-native.syntax-highlighter :as highlighter])) + [react-native.syntax-highlighter :as highlighter] + [reagent.core :as reagent])) ;; Example themes: ;; https://github.com/react-syntax-highlighter/react-syntax-highlighter/tree/master/src/styles/hljs (def ^:private themes - {:light {:hljs-comment {:color colors/neutral-40}, - :hljs-title {:color (colors/custom-color :blue 50)}, - :hljs-keyword {:color (colors/custom-color :green 50)}, - :hljs-string {:color (colors/custom-color :turquoise 50)}, + {:light {:hljs-comment {:color colors/neutral-40} + :hljs-title {:color (colors/custom-color :blue 50)} + :hljs-keyword {:color (colors/custom-color :green 50)} + :hljs-string {:color (colors/custom-color :turquoise 50)} :hljs-literal {:color (colors/custom-color :turquoise 50)} :hljs-number {:color (colors/custom-color :turquoise 50)} :line-number {:color colors/neutral-40}} - :dark {:hljs-comment {:color colors/neutral-60}, - :hljs-title {:color (colors/custom-color :blue 60)}, - :hljs-keyword {:color (colors/custom-color :green 60)}, - :hljs-string {:color (colors/custom-color :turquoise 60)}, + :dark {:hljs-comment {:color colors/neutral-60} + :hljs-title {:color (colors/custom-color :blue 60)} + :hljs-keyword {:color (colors/custom-color :green 60)} + :hljs-string {:color (colors/custom-color :turquoise 60)} :hljs-literal {:color (colors/custom-color :turquoise 60)} :hljs-number {:color (colors/custom-color :turquoise 60)} :line-number {:color colors/neutral-40}}}) -(defn- text-style [class-names] +(defn- text-style + [class-names] (->> class-names (map keyword) (reduce #(merge %1 (get-in themes [(theme/get-theme) %2])) @@ -43,35 +44,41 @@ ;; layout. :line-height 18}))) -(defn- render-nodes [nodes] +(defn- render-nodes + [nodes] (map (fn [{:keys [children value] :as node}] ;; Node can have :children or a :value. (if children - (into [text/text {:weight :code - :size :paragraph-2 - :style (text-style (get-in node [:properties :className]))}] + (into [text/text + {:weight :code + :size :paragraph-2 + :style (text-style (get-in node [:properties :className]))}] (render-nodes children)) ;; Remove newlines as we already render each line separately. (-> value string/trim-newline))) nodes)) -(defn- code-block [{:keys [rows line-number-width]}] +(defn- code-block + [{:keys [rows line-number-width]}] [into [:<>] (->> rows (render-nodes) ;; Line numbers (map-indexed (fn [idx row] (conj [rn/view {:style {:flex-direction :row}} - [rn/view {:style {:width line-number-width - ;; 8+12 margin - :margin-right 20}} - [text/text {:weight :code - :size :paragraph-2 - :style (text-style ["line-number"])} + [rn/view + {:style {:width line-number-width + ;; 8+12 margin + :margin-right 20}} + [text/text + {:weight :code + :size :paragraph-2 + :style (text-style ["line-number"])} (inc idx)]]] row))))]) -(defn- native-renderer [] +(defn- native-renderer + [] (let [text-height (reagent/atom nil)] (fn [{:keys [rows max-lines on-copy-press]}] (let [background-color (colors/theme-colors @@ -89,67 +96,81 @@ max-line-digits (-> rows count (min max-rows) str count) ;; ~ 9 is char width, 18 is width used in Figma. line-number-width (* font-scale (max 18 (* 9 max-line-digits))) - max-text-height (some-> max-lines (* font-scale 18)) ;; 18 is font's line height. + max-text-height (some-> max-lines + (* font-scale 18)) ;; 18 is font's line height. truncated? (and max-text-height (< max-text-height @text-height)) maybe-mask-wrapper (if truncated? [masked-view/masked-view {:mask-element (reagent/as-element - [linear-gradient/linear-gradient {:colors ["black" "transparent"] - :locations [0.75 1] - :style {:flex 1}}])}] + [linear-gradient/linear-gradient + {:colors ["black" "transparent"] + :locations [0.75 1] + :style {:flex 1}}])}] [:<>])] - [rn/view {:style {:overflow :hidden - :padding 8 - :background-color background-color - :border-color border-color - :border-width 1 - :border-radius 8 - ;; Hide on intial render to avoid flicker when mask-wrapper is shown. - :opacity (if @text-height 1 0)}} + [rn/view + {:style {:overflow :hidden + :padding 8 + :background-color background-color + :border-color border-color + :border-width 1 + :border-radius 8 + ;; Hide on intial render to avoid flicker when mask-wrapper is shown. + :opacity (if @text-height 1 0)}} ;; Line number container - [rn/view {:style {:position :absolute - :bottom 0 - :top 0 - :left 0 - :width (+ line-number-width 8 8) - :background-color background-color-left - :border-right-color border-color - :border-right-width 1}}] - (conj maybe-mask-wrapper [rn/view {:max-height max-text-height} - [rn/view {:on-layout (fn [evt] - (let [height (oops/oget evt "nativeEvent.layout.height")] - (reset! text-height height)))} - [code-block {:rows (take max-rows rows) - :line-number-width line-number-width}]]]) + [rn/view + {:style {:position :absolute + :bottom 0 + :top 0 + :left 0 + :width (+ line-number-width 8 8) + :background-color background-color-left + :border-right-color border-color + :border-right-width 1}}] + (conj maybe-mask-wrapper + [rn/view {:max-height max-text-height} + [rn/view + {:on-layout (fn [evt] + (let [height (oops/oget evt "nativeEvent.layout.height")] + (reset! text-height height)))} + [code-block + {:rows (take max-rows rows) + :line-number-width line-number-width}]]]) ;; Copy button - [rn/view {:style {:position :absolute - :bottom 8 - :right 8}} - [button/button {:icon true - :type :grey - :size 24 - :on-press on-copy-press} + [rn/view + {:style {:position :absolute + :bottom 8 + :right 8}} + [button/button + {:icon true + :type :grey + :size 24 + :on-press on-copy-press} :main-icons/copy]]])))) -(defn- wrap-renderer-fn [f {:keys [max-lines on-copy-press]}] +(defn- wrap-renderer-fn + [f {:keys [max-lines on-copy-press]}] (fn [^js props] - (reagent/as-element [:f> f {:rows (.-rows props) - :max-lines max-lines - :on-copy-press on-copy-press}]))) + (reagent/as-element [:f> f + {:rows (.-rows props) + :max-lines max-lines + :on-copy-press on-copy-press}]))) -(defn snippet [{:keys [language max-lines on-copy-press]} children] - [highlighter/highlighter {:language language - :renderer (wrap-renderer-fn - native-renderer {:max-lines max-lines - :on-copy-press #(when on-copy-press - (on-copy-press children))}) - ;; Default props to adapt Highlighter for react-native. - :CodeTag react-native/View - :PreTag react-native/View - :show-line-numbers false - :style #js {} - :custom-style #js {:backgroundColor nil}} +(defn snippet + [{:keys [language max-lines on-copy-press]} children] + [highlighter/highlighter + {:language language + :renderer (wrap-renderer-fn + native-renderer + {:max-lines max-lines + :on-copy-press #(when on-copy-press + (on-copy-press children))}) + ;; Default props to adapt Highlighter for react-native. + :CodeTag react-native/View + :PreTag react-native/View + :show-line-numbers false + :style #js {} + :custom-style #js {:backgroundColor nil}} children]) diff --git a/src/quo2/components/community/community_card_view.cljs b/src/quo2/components/community/community_card_view.cljs index 570a9dc9b2..3ceb9e4efd 100644 --- a/src/quo2/components/community/community_card_view.cljs +++ b/src/quo2/components/community/community_card_view.cljs @@ -1,18 +1,18 @@ (ns quo2.components.community.community-card-view - (:require - [quo2.components.community.community-view :as community-view] - [quo2.components.community.style :as style] - [react-native.core :as rn])) + (:require [quo2.components.community.community-view :as community-view] + [quo2.components.community.style :as style] + [react-native.core :as rn])) (defn community-card-view-item [{:keys [name description locked status tokens cover tags width]} on-press] [rn/touchable-opacity {:on-press on-press} [rn/view {:style (style/community-card 20)} - [rn/view {:style {:width width - :height 230 - :border-radius 20} - :on-press on-press} + [rn/view + {:style {:width width + :height 230 + :border-radius 20} + :on-press on-press} [rn/view {:flex 1} [rn/view (style/community-cover-container 40) @@ -27,9 +27,10 @@ ;[communities.icon/community-icon-redesign community 48]] (when (= status :gated) [rn/view (style/permission-tag-styles) - [community-view/permission-tag-container {:locked locked - :status status - :tokens tokens}]]) + [community-view/permission-tag-container + {:locked locked + :status status + :tokens tokens}]]) [community-view/community-title {:title name :description description}] diff --git a/src/quo2/components/community/community_list_view.cljs b/src/quo2/components/community/community_list_view.cljs index 23049136cd..d22724e30a 100644 --- a/src/quo2/components/community/community_list_view.cljs +++ b/src/quo2/components/community/community_list_view.cljs @@ -1,112 +1,131 @@ (ns quo2.components.community.community-list-view - (:require - [quo2.components.community.community-view :as community-view] - [quo2.components.markdown.text :as text] - [quo2.foundations.colors :as colors] - [quo2.components.counter.counter :as counter] - [quo2.components.icon :as icons] - [quo2.components.community.style :as style] - [react-native.fast-image :as fast-image] - [react-native.core :as rn])) + (:require [quo2.components.community.community-view :as community-view] + [quo2.components.community.style :as style] + [quo2.components.counter.counter :as counter] + [quo2.components.icon :as icons] + [quo2.components.markdown.text :as text] + [quo2.foundations.colors :as colors] + [react-native.core :as rn] + [react-native.fast-image :as fast-image])) -(defn community-icon-view [community-icon] - [rn/view {:width 32 - :height 32} - [fast-image/fast-image {:source {:uri community-icon} - :style {:height 32 - :border-radius 16 - :width 32}}]]) +(defn community-icon-view + [community-icon] + [rn/view + {:width 32 + :height 32} + [fast-image/fast-image + {:source {:uri community-icon} + :style {:height 32 + :border-radius 16 + :width 32}}]]) -(defn notification-view [{:keys [muted? - unread-messages? - unread-mentions-count]}] +(defn notification-view + [{:keys [muted? + unread-messages? + unread-mentions-count]}] (cond muted? - [icons/icon :i/muted {:container-style {:align-items :center - :justify-content :center} - :resize-mode :center - :size 20 - :color (colors/theme-colors - colors/neutral-40 - colors/neutral-50)}] + [icons/icon :i/muted + {:container-style {:align-items :center + :justify-content :center} + :resize-mode :center + :size 20 + :color (colors/theme-colors + colors/neutral-40 + colors/neutral-50)}] (pos? unread-mentions-count) [counter/counter {:type :default} unread-mentions-count] unread-messages? - [rn/view {:style {:width 8 - :height 8 - :border-radius 4 - :background-color (colors/theme-colors - colors/neutral-40 - colors/neutral-60)}}])) + [rn/view + {:style {:width 8 + :height 8 + :border-radius 4 + :background-color (colors/theme-colors + colors/neutral-40 + colors/neutral-60)}}])) -(defn communities-list-view-item [props {:keys [name - locked? - status - muted? - unread-messages? - unread-mentions-count - community-icon - tokens - background-color]}] - [rn/view {:style (merge (style/community-card 16) - {:margin-bottom 12 - :margin-horizontal 20})} - [rn/touchable-highlight (merge {:style {:height 56 - :border-radius 16}} - props) +(defn communities-list-view-item + [props + {:keys [name + locked? + status + muted? + unread-messages? + unread-mentions-count + community-icon + tokens + background-color]}] + [rn/view + {:style (merge (style/community-card 16) + {:margin-bottom 12 + :margin-horizontal 20})} + [rn/touchable-highlight + (merge {:style {:height 56 + :border-radius 16}} + props) [rn/view {:flex 1} - [rn/view {:flex-direction :row - :border-radius 16 - :padding-horizontal 12 - :align-items :center - :padding-vertical 8 - :background-color background-color} + [rn/view + {:flex-direction :row + :border-radius 16 + :padding-horizontal 12 + :align-items :center + :padding-vertical 8 + :background-color background-color} [rn/view] (when community-icon [community-icon-view community-icon]) - [rn/view {:flex 1 - :margin-horizontal 12} - [text/text {:weight :semi-bold - :size :paragraph-1 - :accessibility-label :community-name-text - :number-of-lines 1 - :ellipsize-mode :tail - :style {:color (when muted? - (colors/theme-colors - colors/neutral-40 - colors/neutral-60))}} + [rn/view + {:flex 1 + :margin-horizontal 12} + [text/text + {:weight :semi-bold + :size :paragraph-1 + :accessibility-label :community-name-text + :number-of-lines 1 + :ellipsize-mode :tail + :style {:color (when muted? + (colors/theme-colors + colors/neutral-40 + colors/neutral-60))}} name] [community-view/community-stats-column :list-view]] (if (= status :gated) - [community-view/permission-tag-container {:locked? locked? - :tokens tokens}] - [notification-view {:muted? muted? - :unread-mentions-count unread-mentions-count - :unread-messages? unread-messages?}])]]]]) + [community-view/permission-tag-container + {:locked? locked? + :tokens tokens}] + [notification-view + {:muted? muted? + :unread-mentions-count unread-mentions-count + :unread-messages? unread-messages?}])]]]]) -(defn communities-membership-list-item [props {:keys [name - muted? - unread-messages? - unread-mentions-count - status - community-icon - tokens - locked?]}] +(defn communities-membership-list-item + [props + {:keys [name + muted? + unread-messages? + unread-mentions-count + status + community-icon + tokens + locked?]}] [rn/view {:margin-bottom 20} - [rn/touchable-highlight (merge {:underlay-color colors/primary-50-opa-5 - :style {:border-radius 12}} - props) + [rn/touchable-highlight + (merge {:underlay-color colors/primary-50-opa-5 + :style {:border-radius 12}} + props) [rn/view {:flex 1} - [rn/view {:flex-direction :row - :border-radius 16 - :align-items :center} + [rn/view + {:flex-direction :row + :border-radius 16 + :align-items :center} (when community-icon [community-icon-view community-icon]) - [rn/view {:flex 1 - :margin-left 12 - :justify-content :center} + [rn/view + {:flex 1 + :margin-left 12 + :justify-content :center} [text/text {:accessibility-label :chat-name-text :number-of-lines 1 @@ -115,11 +134,14 @@ :size :paragraph-1} name]] - [rn/view {:justify-content :center - :margin-right 16} + [rn/view + {:justify-content :center + :margin-right 16} (if (= status :gated) - [community-view/permission-tag-container {:locked? locked? - :tokens tokens}] - [notification-view {:muted? muted? - :unread-mentions-count unread-mentions-count - :unread-messages? unread-messages?}])]]]]]) + [community-view/permission-tag-container + {:locked? locked? + :tokens tokens}] + [notification-view + {:muted? muted? + :unread-mentions-count unread-mentions-count + :unread-messages? unread-messages?}])]]]]]) diff --git a/src/quo2/components/community/community_view.cljs b/src/quo2/components/community/community_view.cljs index 105c986b42..fbe6c02036 100644 --- a/src/quo2/components/community/community_view.cljs +++ b/src/quo2/components/community/community_view.cljs @@ -1,38 +1,46 @@ (ns quo2.components.community.community-view - (:require - [quo2.components.markdown.text :as text] - [quo2.components.icon :as icons] - [quo2.foundations.colors :as colors] - [quo2.components.tags.permission-tag :as permission] - [quo2.components.tags.tag :as tag] - [quo2.components.community.style :as style] - [react-native.core :as rn])) + (:require [quo2.components.community.style :as style] + [quo2.components.icon :as icons] + [quo2.components.markdown.text :as text] + [quo2.components.tags.permission-tag :as permission] + [quo2.components.tags.tag :as tag] + [quo2.foundations.colors :as colors] + [react-native.core :as rn])) -(defn community-stats [{:keys [icon members-count icon-color]}] +(defn community-stats + [{:keys [icon members-count icon-color]}] [rn/view (style/stats-count-container) [rn/view {:margin-right 4} - [icons/icon icon {:container-style {:align-items :center - :justify-content :center} - :resize-mode :center - :size 16 - :color icon-color}]] - [text/text {:weight :regular - :size :paragraph-1} + [icons/icon icon + {:container-style {:align-items :center + :justify-content :center} + :resize-mode :center + :size 16 + :color icon-color}]] + [text/text + {:weight :regular + :size :paragraph-1} members-count]]) -(defn community-stats-column [type] +(defn community-stats-column + [type] (let [icon-color (colors/theme-colors colors/neutral-50 colors/neutral-40)] - [rn/view (if (= type :card-view) - (style/card-stats-container) - (style/list-stats-container)) - [community-stats {:icon :i/group - :members-count "629.2K" ;;TODO here should be formatted value, use money/format-members from outside this component - :icon-color icon-color}] - [community-stats {:icon :i/lightning - :members-count "112.1K" - :icon-color icon-color}]])) + [rn/view + (if (= type :card-view) + (style/card-stats-container) + (style/list-stats-container)) + [community-stats + {:icon :i/group + :members-count "629.2K" ;;TODO here should be formatted value, use money/format-members from + ;;outside this component + :icon-color icon-color}] + [community-stats + {:icon :i/lightning + :members-count "112.1K" + :icon-color icon-color}]])) -(defn community-tags [tags] +(defn community-tags + [tags] [rn/view (style/community-tags-container) (for [{:keys [id tag-label resource]} tags] ^{:key id} @@ -45,7 +53,8 @@ :labelled true :resource resource}]])]) -(defn community-title [{:keys [title description size] :or {size :small}}] +(defn community-title + [{:keys [title description size] :or {size :small}}] [rn/view (style/community-title-description-container (if (= size :large) 56 32)) (when title [text/text @@ -65,12 +74,14 @@ :style {:margin-top (if (= size :large) 8 2)}} description])]) -(defn permission-tag-container [{:keys [locked? tokens on-press]}] - [permission/tag {:background-color (colors/theme-colors - colors/neutral-10 - colors/neutral-80) - :locked? locked? - :tokens tokens +(defn permission-tag-container + [{:keys [locked? tokens on-press]}] + [permission/tag + {:background-color (colors/theme-colors + colors/neutral-10 + colors/neutral-80) + :locked? locked? + :tokens tokens - :size 24 - :on-press on-press}]) + :size 24 + :on-press on-press}]) diff --git a/src/quo2/components/community/discover_card.cljs b/src/quo2/components/community/discover_card.cljs index 241b5c8f4e..10149faa0e 100644 --- a/src/quo2/components/community/discover_card.cljs +++ b/src/quo2/components/community/discover_card.cljs @@ -1,94 +1,107 @@ (ns quo2.components.community.discover-card - (:require [quo2.components.markdown.text :as text] + (:require [quo2.components.community.style :as style] + [quo2.components.markdown.text :as text] [quo2.foundations.colors :as colors] - [quo2.components.community.style :as style] [react-native.core :as rn])) ;; Discover card placeholders images. ;; TODO replaced when real data is available (def images - {:images [{:id 1 :column-images [{:id 1 :image ""}]} + {:images [{:id 1 :column-images [{:id 1 :image ""}]} {:id 2 :column-images [{}]}]}) -(defn card-title-and-description [title description] +(defn card-title-and-description + [title description] [rn/view - {:flex 1 - :padding-top 8 - :padding-bottom 8 - :border-radius 12} - [rn/view {:flex 1 - :padding-horizontal 12} - [text/text {:accessibility-label :community-name-text - :ellipsize-mode :tail - :number-of-lines 1 - :weight :semi-bold - :size :paragraph-1} + {:flex 1 + :padding-top 8 + :padding-bottom 8 + :border-radius 12} + [rn/view + {:flex 1 + :padding-horizontal 12} + [text/text + {:accessibility-label :community-name-text + :ellipsize-mode :tail + :number-of-lines 1 + :weight :semi-bold + :size :paragraph-1} title] - [text/text {:accessibility-label :community-name-text - :ellipsize-mode :tail - :number-of-lines 1 - :color (colors/theme-colors - colors/neutral-50 - colors/neutral-40) - :weight :regular - :size :paragraph-2} + [text/text + {:accessibility-label :community-name-text + :ellipsize-mode :tail + :number-of-lines 1 + :color (colors/theme-colors + colors/neutral-50 + colors/neutral-40) + :weight :regular + :size :paragraph-2} description]]]) -(defn placeholder-list-images [{:keys [images width height border-radius]}] +(defn placeholder-list-images + [{:keys [images width height border-radius]}] [rn/view - [rn/view {:justify-content :center} + [rn/view {:justify-content :center} (for [{:keys [id]} images] ^{:key id} - [rn/view {:border-radius border-radius - :margin-top 4 - :margin-right 4 - :width width - :height height - :background-color colors/neutral-10}])]]) + [rn/view + {:border-radius border-radius + :margin-top 4 + :margin-right 4 + :width width + :height height + :background-color colors/neutral-10}])]]) -(defn placeholder-row-images [{:keys [first-image last-image images width height - border-radius]}] +(defn placeholder-row-images + [{:keys [first-image last-image images width height + border-radius]}] [rn/view (when first-image - [rn/view {:border-bottom-right-radius 6 - :border-bottom-left-radius 6 - :margin-right 4 - :width width - :height height - :background-color colors/neutral-10}]) + [rn/view + {:border-bottom-right-radius 6 + :border-bottom-left-radius 6 + :margin-right 4 + :width width + :height height + :background-color colors/neutral-10}]) (when images - [placeholder-list-images {:images images - :width 32 - :height 32 - :border-radius 6}]) + [placeholder-list-images + {:images images + :width 32 + :height 32 + :border-radius 6}]) (when last-image - [rn/view {:border-top-left-radius border-radius - :border-top-right-radius 6 - :margin-top 4 - :width width - :height height - :background-color colors/neutral-10}])]) + [rn/view + {:border-top-left-radius border-radius + :border-top-right-radius 6 + :margin-top 4 + :width width + :height height + :background-color colors/neutral-10}])]) -(defn discover-card [{:keys [title description on-press accessibility-label]}] - (let [on-joined-images (get images :images)] +(defn discover-card + [{:keys [title description on-press accessibility-label]}] + (let [on-joined-images (get images :images)] [rn/touchable-without-feedback {:on-press on-press :accessibility-label accessibility-label} - [rn/view (merge (style/community-card 16) - {:background-color (colors/theme-colors - colors/white - colors/neutral-90)} - {:flex-direction :row - :margin-horizontal 20 - :height 56 - :padding-right 12}) + [rn/view + (merge (style/community-card 16) + {:background-color (colors/theme-colors + colors/white + colors/neutral-90)} + {:flex-direction :row + :margin-horizontal 20 + :height 56 + :padding-right 12}) [card-title-and-description title description] (for [{:keys [id column-images]} on-joined-images] ^{:key id} - [placeholder-row-images {:images (when (= id 1) - column-images) - :width 32 - :height (if (= id 1) 8 26) - :border-radius 6 - :first-image "" ; TODO replace with real data - :last-image ""}])]])) ; TODO replace with real data + [placeholder-row-images + {:images (when (= id 1) + column-images) + :width 32 + :height (if (= id 1) 8 26) + :border-radius 6 + :first-image "" ; TODO replace with real data + :last-image ""}])]])) ; TODO replace with real data diff --git a/src/quo2/components/community/style.cljs b/src/quo2/components/community/style.cljs index a44574bb22..96d375810b 100644 --- a/src/quo2/components/community/style.cljs +++ b/src/quo2/components/community/style.cljs @@ -8,57 +8,66 @@ :height 52 :padding-left 18}) -(defn community-card [radius] - {:shadow-offset {:width 0 +(defn community-card + [radius] + {:shadow-offset {:width 0 :height 2} - :shadow-radius radius - :shadow-opacity 1 - :shadow-color colors/shadow - :border-radius radius - :justify-content :space-between - :elevation 2 - :background-color (colors/theme-colors - colors/white - colors/neutral-90)}) - -(defn stats-count-container [] - {:flex-direction :row - :align-items :center - :margin-right 16}) - -(defn card-stats-container [] - {:flex-direction :row}) - -(defn list-stats-container [] - {:flex-direction :row - :align-items :center}) - -(defn community-tags-container [] - {:flex-direction :row}) - -(defn card-stats-position [] - {:position :absolute - :top 116 - :right 12 - :left 12}) - -(defn community-tags-position [] - {:position :absolute - :top 154 - :right 12 - :left 12}) - -(defn card-view-content-container [padding-horizontal] - {:flex 1 - :height 20 - :padding-left padding-horizontal - :padding-right padding-horizontal - :border-radius 16 + :shadow-radius radius + :shadow-opacity 1 + :shadow-color colors/shadow + :border-radius radius + :justify-content :space-between + :elevation 2 :background-color (colors/theme-colors colors/white colors/neutral-90)}) -(defn card-view-chat-icon [icon-size] +(defn stats-count-container + [] + {:flex-direction :row + :align-items :center + :margin-right 16}) + +(defn card-stats-container + [] + {:flex-direction :row}) + +(defn list-stats-container + [] + {:flex-direction :row + :align-items :center}) + +(defn community-tags-container + [] + {:flex-direction :row}) + +(defn card-stats-position + [] + {:position :absolute + :top 116 + :right 12 + :left 12}) + +(defn community-tags-position + [] + {:position :absolute + :top 154 + :right 12 + :left 12}) + +(defn card-view-content-container + [padding-horizontal] + {:flex 1 + :height 20 + :padding-left padding-horizontal + :padding-right padding-horizontal + :border-radius 16 + :background-color (colors/theme-colors + colors/white + colors/neutral-90)}) + +(defn card-view-chat-icon + [icon-size] {:border-radius 48 :position :absolute :top (- (/ icon-size 2)) @@ -68,29 +77,34 @@ colors/white colors/neutral-90)}) -(defn list-view-content-container [] - {:flex-direction :row - :border-radius 16 - :align-items :center +(defn list-view-content-container + [] + {:flex-direction :row + :border-radius 16 + :align-items :center :background-color (colors/theme-colors colors/white colors/neutral-90)}) -(defn list-view-chat-icon [] - {:border-radius 32 - :padding 12}) +(defn list-view-chat-icon + [] + {:border-radius 32 + :padding 12}) -(defn community-title-description-container [margin-top] +(defn community-title-description-container + [margin-top] {:margin-top margin-top}) -(defn community-cover-container [height] +(defn community-cover-container + [height] {:flex-direction :row :height height :border-top-right-radius 20 :border-top-left-radius 20 :background-color colors/primary-50-opa-20}) -(defn permission-tag-styles [] - {:position :absolute - :top 8 - :right 8}) \ No newline at end of file +(defn permission-tag-styles + [] + {:position :absolute + :top 8 + :right 8}) \ No newline at end of file diff --git a/src/quo2/components/community/token_gating.cljs b/src/quo2/components/community/token_gating.cljs index f7cd692086..e2de3dc51b 100644 --- a/src/quo2/components/community/token_gating.cljs +++ b/src/quo2/components/community/token_gating.cljs @@ -13,75 +13,83 @@ (def ^:private token-tag-horizontal-spacing 7) (def token-tag-vertical-spacing 5) -(def styles {:container {:flex 1 - :padding-horizontal 20} - :header-container {:flex-direction :row - :align-items :center} - :header-spacing-community {:margin-bottom 16} - :header-spacing-channel {:margin-bottom 24} - :header-avatar {:margin-right 8} - :header-community-avatar-image {:width 32 :height 32} - :header-title-container {:flex 1 - :flex-direction :row - :align-items :center} - :header-title-lock {:margin-left 6} - :header-info-button {:margin-left 8} - :token-requirement-text-spacing {:margin-vertical 8} - :token-requirement-list-spacing {:padding-vertical (- 8 token-tag-vertical-spacing)} - :token-row {:flex-direction :row - :flex-wrap :wrap - :align-items :center} - :token-row-container {:flex-direction :row - :align-items :center - :margin-horizontal (- token-tag-horizontal-spacing)} - :token-row-container-no-shift {:margin-left 0} - :token-row-or-text {:margin-right 3} - :token-tag-spacing {:margin-horizontal token-tag-horizontal-spacing - :margin-vertical token-tag-vertical-spacing} - :divider {:height 1 - :margin-horizontal -20 - :margin-top (- 12 token-tag-vertical-spacing) - :margin-bottom 8} - :membership-request-denied {:margin-top 13} - :enter-button {:margin-top 16} - :info-text {:margin-top 12 - :text-align :center - :padding-horizontal 20}}) +(def styles + {:container {:flex 1 + :padding-horizontal 20} + :header-container {:flex-direction :row + :align-items :center} + :header-spacing-community {:margin-bottom 16} + :header-spacing-channel {:margin-bottom 24} + :header-avatar {:margin-right 8} + :header-community-avatar-image {:width 32 :height 32} + :header-title-container {:flex 1 + :flex-direction :row + :align-items :center} + :header-title-lock {:margin-left 6} + :header-info-button {:margin-left 8} + :token-requirement-text-spacing {:margin-vertical 8} + :token-requirement-list-spacing {:padding-vertical (- 8 token-tag-vertical-spacing)} + :token-row {:flex-direction :row + :flex-wrap :wrap + :align-items :center} + :token-row-container {:flex-direction :row + :align-items :center + :margin-horizontal (- token-tag-horizontal-spacing)} + :token-row-container-no-shift {:margin-left 0} + :token-row-or-text {:margin-right 3} + :token-tag-spacing {:margin-horizontal token-tag-horizontal-spacing + :margin-vertical token-tag-vertical-spacing} + :divider {:height 1 + :margin-horizontal -20 + :margin-top (- 12 token-tag-vertical-spacing) + :margin-bottom 8} + :membership-request-denied {:margin-top 13} + :enter-button {:margin-top 16} + :info-text {:margin-top 12 + :text-align :center + :padding-horizontal 20}}) -(defn multiple-token-requirements? [token-requirement-lists] +(defn multiple-token-requirements? + [token-requirement-lists] (when (vector? (first token-requirement-lists)) (> (count token-requirement-lists) 1))) -(defn is-token-requirement-met? [token-requirement-list] +(defn is-token-requirement-met? + [token-requirement-list] (and (seq token-requirement-list) (every? (fn [token] (get token :is-sufficient?)) token-requirement-list))) -(defn are-multiple-token-requirements-met? [token-requirement-lists] +(defn are-multiple-token-requirements-met? + [token-requirement-lists] (if (multiple-token-requirements? token-requirement-lists) (some is-token-requirement-met? token-requirement-lists) (is-token-requirement-met? token-requirement-lists))) -(defn token-requirement-list-row [tokens community-color] - [rn/view {:style (merge - (get styles :token-row) - (get styles :token-requirement-list-spacing))} +(defn token-requirement-list-row + [tokens community-color] + [rn/view + {:style (merge + (get styles :token-row) + (get styles :token-requirement-list-spacing))} (map-indexed (fn [token-index token] (let [{:keys [token-img-src token amount is-sufficient? is-purchasable?]} token] ^{:key token-index} [rn/view {:style (get styles :token-tag-spacing)} - [token-tag/token-tag {:token token - :value amount - :size 24 - :border-color community-color - :is-required is-sufficient? - :is-purchasable is-purchasable? - :token-img-src token-img-src}]])) + [token-tag/token-tag + {:token token + :value amount + :size 24 + :border-color community-color + :is-required is-sufficient? + :is-purchasable is-purchasable? + :token-img-src token-img-src}]])) tokens)]) -(defn token-requirement-list [props community-color] +(defn token-requirement-list + [props community-color] (let [{:keys [gate token-requirements-changed? required-tokens-lost?]} props [gate-type token-requirement-lists] gate multiple-token-requirements? (multiple-token-requirements? token-requirement-lists) @@ -89,15 +97,17 @@ you-must-hold-label (if (= gate-type :join) (cond token-requirements-changed? :t/you-must-now-hold - required-tokens-lost? :t/you-must-always-hold - :else :t/you-must-hold) + required-tokens-lost? :t/you-must-always-hold + :else :t/you-must-hold) :t/you-must-hold) message-label (cond (= gate-type :join) (cond token-requirements-changed? :t/community-join-requirements-changed - required-tokens-lost? :t/community-join-requirements-tokens-lost - :else (if is-sufficient? :t/community-join-requirements-met :t/community-join-requirements-not-met)) + required-tokens-lost? :t/community-join-requirements-tokens-lost + :else (if is-sufficient? + :t/community-join-requirements-met + :t/community-join-requirements-not-met)) (= gate-type :read) (if is-sufficient? :t/community-channel-read-requirements-met @@ -108,12 +118,14 @@ :t/community-channel-write-requirements-not-met))] [rn/view [rn/view {:style (get styles :token-requirement-text-spacing)} - [text/text {:weight :medium - :number-of-lines 1} + [text/text + {:weight :medium + :number-of-lines 1} (i18n/label message-label)] - [text/text {:size :paragraph-2 - :number-of-lines 1} + [text/text + {:size :paragraph-2 + :number-of-lines 1} (i18n/label you-must-hold-label)]] @@ -121,22 +133,26 @@ (map-indexed (fn [token-requirement-index tokens] ^{:key token-requirement-index} - [rn/view {:style (merge - (get styles :token-row-container) - (when-not (= token-requirement-index 0) - (get styles :token-row-container-no-shift)))} + [rn/view + {:style (merge + (get styles :token-row-container) + (when-not (= token-requirement-index 0) + (get styles :token-row-container-no-shift)))} (when-not (= token-requirement-index 0) - [text/text {:style (get styles :token-row-or-text) - :size :paragraph-2} "or"]) + [text/text + {:style (get styles :token-row-or-text) + :size :paragraph-2} "or"]) [token-requirement-list-row tokens community-color]]) token-requirement-lists) [rn/view {:style (get styles :token-row-container)} [token-requirement-list-row token-requirement-lists community-color]])])) -(defn is-community-locked? [community] +(defn is-community-locked? + [community] (not (are-multiple-token-requirements-met? (get-in community [:gates :join])))) -(defn is-channel-locked? [channel] +(defn is-channel-locked? + [channel] (not (are-multiple-token-requirements-met? (or (get-in channel [:gates :read]) (get-in channel [:gates :write]))))) @@ -174,82 +190,98 @@ membership-request-denied? token-requirements-changed? required-tokens-lost? - gates]} (if (= type :community) community channel) + gates]} + (if (= type :community) community channel) locked? (if - (= type :community) + (= type :community) (is-community-locked? community) (is-channel-locked? channel))] - [rn/view {:style (merge (get styles :container) {:background-color (colors/theme-colors colors/white colors/neutral-90)})} - [rn/view {:style (merge - (get styles :header-container) - (if - (= type :community) - (get styles :header-spacing-community) - (get styles :header-spacing-channel)))} + [rn/view + {:style (merge (get styles :container) + {:background-color (colors/theme-colors colors/white colors/neutral-90)})} + [rn/view + {:style (merge + (get styles :header-container) + (if + (= type :community) + (get styles :header-spacing-community) + (get styles :header-spacing-channel)))} [rn/view {:style (get styles :header-avatar)} (if (= type :community) - [fast-image/fast-image {:source community-avatar-img-src - :style (get styles :header-community-avatar-image)}] + [fast-image/fast-image + {:source community-avatar-img-src + :style (get styles :header-community-avatar-image)}] - [channel-avatar/channel-avatar {:big? true - :locked? locked? - :emoji emoji - :emoji-background-color emoji-background-color}])] + [channel-avatar/channel-avatar + {:big? true + :locked? locked? + :emoji emoji + :emoji-background-color emoji-background-color}])] [rn/view {:style (get styles :header-title-container)} - [text/text {:weight :semi-bold - :number-of-lines 1 - :size :heading-1 - :style (get styles :header-text)} (if (= type :community) name (str "# " name))] + [text/text + {:weight :semi-bold + :number-of-lines 1 + :size :heading-1 + :style (get styles :header-text)} (if (= type :community) name (str "# " name))] (when (= type :community) - [icon/icon (if locked? - :main-icons2/locked - :main-icons2/unlocked) + [icon/icon + (if locked? + :main-icons2/locked + :main-icons2/unlocked) {:container-style (get styles :header-title-lock) :color (colors/theme-colors colors/neutral-50 colors/neutral-40)}])] - [button/button {:type :outline - :size 32 - :icon true - :style (get styles :header-info-button)} :main-icons2/info]] + [button/button + {:type :outline + :size 32 + :icon true + :style (get styles :header-info-button)} :main-icons2/info]] (map-indexed (fn [gate-index gate] ^{:key gate-index} [:<> (when-not (= gate-index 0) - [rn/view {:style (merge - (get styles :divider) - {:background-color (colors/theme-colors - colors/neutral-10 - colors/neutral-80)})}]) - [token-requirement-list {:gate gate :token-requirements-changed? token-requirements-changed? :required-tokens-lost? required-tokens-lost?} community-color]]) + [rn/view + {:style (merge + (get styles :divider) + {:background-color (colors/theme-colors + colors/neutral-10 + colors/neutral-80)})}]) + [token-requirement-list + {:gate gate + :token-requirements-changed? token-requirements-changed? + :required-tokens-lost? required-tokens-lost?} community-color]]) gates) (when (= type :channel) (if membership-request-denied? - [information-box/information-box {:type :error - :icon :main-icons2/untrustworthy - :no-icon-color? true - :style (get styles :membership-request-denied)} + [information-box/information-box + {:type :error + :icon :main-icons2/untrustworthy + :no-icon-color? true + :style (get styles :membership-request-denied)} (i18n/label :t/membership-request-denied)] [:<> - [button/button {:type :community - :community-color community-color - :community-text-color community-text-color - :style (get styles :enter-button) - :disabled locked? - :on-press on-enter-channel} - (str "# " (i18n/label - :t/enter-channel))] + [button/button + {:type :community + :community-color community-color + :community-text-color community-text-color + :style (get styles :enter-button) + :disabled locked? + :on-press on-enter-channel} + (str "# " + (i18n/label + :t/enter-channel))] [text/text - {:size :paragraph-2 + {:size :paragraph-2 :style (merge (get styles :info-text) {:color (colors/theme-colors diff --git a/src/quo2/components/counter/__tests__/counter_component_spec.cljs b/src/quo2/components/counter/__tests__/counter_component_spec.cljs index ef00cabbd3..2d06142a51 100644 --- a/src/quo2/components/counter/__tests__/counter_component_spec.cljs +++ b/src/quo2/components/counter/__tests__/counter_component_spec.cljs @@ -7,28 +7,28 @@ ([] (render-counter {} nil)) ([opts value] - (rtl/render (reagent/as-element [counter/counter opts value])))) + (rtl/render (reagent/as-element [counter/counter opts value])))) (js/global.test "default render of counter component" (fn [] (render-counter) - (-> (js/expect (rtl/screen.getByTestId "counter-component")) + (-> (js/expect (rtl/screen.getByTestId "counter-component")) (.toBeTruthy)))) (js/global.test "renders counter with a string value" (fn [] (render-counter {} "1") - (-> (js/expect (rtl/screen.getByText "1")) + (-> (js/expect (rtl/screen.getByText "1")) (.toBeTruthy)))) (js/global.test "renders counter with an integer value" (fn [] (render-counter {} 1) - (-> (js/expect (rtl/screen.getByText "1")) + (-> (js/expect (rtl/screen.getByText "1")) (.toBeTruthy)))) (js/global.test "renders counter with value 99+ when the value is greater than 99" (fn [] (render-counter {} "100") - (-> (js/expect (rtl/screen.getByText "99+")) + (-> (js/expect (rtl/screen.getByText "99+")) (.toBeTruthy)))) \ No newline at end of file diff --git a/src/quo2/components/counter/counter.cljs b/src/quo2/components/counter/counter.cljs index 51334f9e27..7948020d33 100644 --- a/src/quo2/components/counter/counter.cljs +++ b/src/quo2/components/counter/counter.cljs @@ -1,20 +1,21 @@ (ns quo2.components.counter.counter - (:require [quo2.theme :as theme] - [react-native.core :as rn] - [quo2.components.markdown.text :as text] - [quo2.foundations.colors :as colors])) + (:require [quo2.components.markdown.text :as text] + [quo2.foundations.colors :as colors] + [quo2.theme :as theme] + [react-native.core :as rn])) (def themes - {:light {:default colors/primary-50 - :secondary colors/neutral-80-opa-5 - :grey colors/neutral-10 - :outline colors/neutral-20} - :dark {:default colors/primary-60 - :secondary colors/white-opa-5 - :grey colors/neutral-70 - :outline colors/neutral-70}}) + {:light {:default colors/primary-50 + :secondary colors/neutral-80-opa-5 + :grey colors/neutral-10 + :outline colors/neutral-20} + :dark {:default colors/primary-60 + :secondary colors/white-opa-5 + :grey colors/neutral-70 + :outline colors/neutral-70}}) -(defn get-color [key] +(defn get-color + [key] (get-in themes [(theme/get-theme) key])) (defn counter @@ -35,30 +36,32 @@ "99+" (str value)) width (case (count label) - 1 16 - 2 20 + 1 16 + 2 20 28)] - [rn/view {:test-ID :counter-component - :accessible true - :accessibility-label accessibility-label - :style (cond-> (merge - {:align-items :center - :justify-content :center - :border-radius 6 - :width width - :height 16} - style) - (= type :outline) - (merge {:border-width 1 - :border-color (get-color type)}) + [rn/view + {:test-ID :counter-component + :accessible true + :accessibility-label accessibility-label + :style (cond-> (merge + {:align-items :center + :justify-content :center + :border-radius 6 + :width width + :height 16} + style) + (= type :outline) + (merge {:border-width 1 + :border-color (get-color type)}) - (not= type :outline) - (assoc :background-color - (or override-bg-color - (get-color type))) + (not= type :outline) + (assoc :background-color + (or override-bg-color + (get-color type))) - (> value 99) - (assoc :padding-left 0.5))} - [text/text {:weight :medium - :size :label - :style {:color text-color}} label]])) + (> value 99) + (assoc :padding-left 0.5))} + [text/text + {:weight :medium + :size :label + :style {:color text-color}} label]])) diff --git a/src/quo2/components/dividers/date.cljs b/src/quo2/components/dividers/date.cljs index 598eb43f96..8e40d45f18 100644 --- a/src/quo2/components/dividers/date.cljs +++ b/src/quo2/components/dividers/date.cljs @@ -1,18 +1,21 @@ (ns quo2.components.dividers.date - (:require [react-native.core :as rn] + (:require [quo2.components.markdown.text :as text] [quo2.components.separator :as separator] - [quo2.components.markdown.text :as text] - [quo2.foundations.colors :as colors])) + [quo2.foundations.colors :as colors] + [react-native.core :as rn])) -(defn date [value] - [rn/view {:margin-vertical 16 - :padding-right 8 - :padding-left 62} - [text/text {:weight :medium - :accessibility-label :divider-date-text - :size :label - :style {:color (colors/theme-colors colors/neutral-50 colors/neutral-40) - :text-transform :capitalize - :margin-bottom 4}} +(defn date + [value] + [rn/view + {:margin-vertical 16 + :padding-right 8 + :padding-left 62} + [text/text + {:weight :medium + :accessibility-label :divider-date-text + :size :label + :style {:color (colors/theme-colors colors/neutral-50 colors/neutral-40) + :text-transform :capitalize + :margin-bottom 4}} value] [separator/separator]]) diff --git a/src/quo2/components/dividers/divider_label.cljs b/src/quo2/components/dividers/divider_label.cljs index 3a4bf7d9de..790c0fe8cd 100644 --- a/src/quo2/components/dividers/divider_label.cljs +++ b/src/quo2/components/dividers/divider_label.cljs @@ -1,8 +1,8 @@ (ns quo2.components.dividers.divider-label - (:require [react-native.core :as rn] + (:require [quo2.components.icon :as icons] [quo2.components.markdown.text :as markdown.text] [quo2.foundations.colors :as colors] - [quo2.components.icon :as icons])) + [react-native.core :as rn])) (def chevron-icon-container-width 20) @@ -15,17 +15,20 @@ increase-padding-top? -> boolean blur? -> boolean" [{:keys [label chevron-position counter-value increase-padding-top? blur?]}] - (let [dark? (colors/dark?) - border-and-counter-bg-color (if dark? (if blur? colors/white-opa-5 colors/neutral-70) colors/neutral-10) - padding-top (if increase-padding-top? 16 8) - text-and-icon-color (if dark? colors/neutral-40 colors/neutral-50) - counter-text-color (if dark? colors/white colors/neutral-100)] - [rn/view {:style {:border-top-width 1 - :border-top-color border-and-counter-bg-color - :padding-top padding-top - :padding-horizontal 16 - :align-items :center - :flex-direction :row}} + (let [dark? (colors/dark?) + border-and-counter-bg-color (if dark? + (if blur? colors/white-opa-5 colors/neutral-70) + colors/neutral-10) + padding-top (if increase-padding-top? 16 8) + text-and-icon-color (if dark? colors/neutral-40 colors/neutral-50) + counter-text-color (if dark? colors/white colors/neutral-100)] + [rn/view + {:style {:border-top-width 1 + :border-top-color border-and-counter-bg-color + :padding-top padding-top + :padding-horizontal 16 + :align-items :center + :flex-direction :row}} (when (= chevron-position :left) [rn/view {:style {:margin-right 4}} [icons/icon @@ -33,27 +36,30 @@ {:color text-and-icon-color :width chevron-icon-container-width :height chevron-icon-container-height}]]) - [markdown.text/text {:size :paragraph-2 - :weight :medium - :style {:color text-and-icon-color - :flex 1}} + [markdown.text/text + {:size :paragraph-2 + :weight :medium + :style {:color text-and-icon-color + :flex 1}} label] (when (= chevron-position :right) [icons/icon :main-icons/chevron-down - {:color text-and-icon-color + {:color text-and-icon-color :size chevron-icon-container-width}]) (when (pos? counter-value) - [rn/view {:style {:border-radius 6 - :height 16 - :width (case (count counter-value) - 1 16 - 2 20 - 28) - :background-color border-and-counter-bg-color - :align-items :center - :justify-content :center}} - [markdown.text/text {:size :label - :weight :medium - :style {:color counter-text-color}} + [rn/view + {:style {:border-radius 6 + :height 16 + :width (case (count counter-value) + 1 16 + 2 20 + 28) + :background-color border-and-counter-bg-color + :align-items :center + :justify-content :center}} + [markdown.text/text + {:size :label + :weight :medium + :style {:color counter-text-color}} counter-value]])])) diff --git a/src/quo2/components/dividers/new_messages.cljs b/src/quo2/components/dividers/new_messages.cljs index 62d46eb488..e3cd266a13 100644 --- a/src/quo2/components/dividers/new_messages.cljs +++ b/src/quo2/components/dividers/new_messages.cljs @@ -1,7 +1,7 @@ (ns quo2.components.dividers.new-messages - (:require [react-native.core :as rn] - [quo2.components.markdown.text :as text] + (:require [quo2.components.markdown.text :as text] [quo2.foundations.colors :as colors] + [react-native.core :as rn] [react-native.linear-gradient :as linear-gradient])) (defn new-messages @@ -9,11 +9,14 @@ [{:keys [label color] :or {color :primary}}] (let [bg-color (colors/custom-color-by-theme color 50 60 5 5) text-color (colors/custom-color-by-theme color 50 60)] - [linear-gradient/linear-gradient {:colors [bg-color "rgba(0,0,0,0)"] - :start {:x 0 :y 0} :end {:x 0 :y 1}} - [rn/view {:style {:padding-left 60 - :padding-vertical 12 - :padding-right 24}} + [linear-gradient/linear-gradient + {:colors [bg-color "rgba(0,0,0,0)"] + :start {:x 0 :y 0} + :end {:x 0 :y 1}} + [rn/view + {:style {:padding-left 60 + :padding-vertical 12 + :padding-right 24}} [text/text {:size :paragraph-2 :weight :medium diff --git a/src/quo2/components/drawers/__tests__/action_drawers_component_spec.cljs b/src/quo2/components/drawers/__tests__/action_drawers_component_spec.cljs index d8dc290976..fab1d5a5d9 100644 --- a/src/quo2/components/drawers/__tests__/action_drawers_component_spec.cljs +++ b/src/quo2/components/drawers/__tests__/action_drawers_component_spec.cljs @@ -5,21 +5,21 @@ (defn render-action-drawer ([options] - (rtl/render (reagent/as-element [action-drawer/action-drawer options])))) + (rtl/render (reagent/as-element [action-drawer/action-drawer options])))) (js/global.test "action-drawer renders with elements label displaying" (fn [] - (render-action-drawer [[{:icon :i/friend - :label "a sample label"}]]) - (-> (js/expect (rtl/screen.getByText "a sample label")) + (render-action-drawer [[{:icon :i/friend + :label "a sample label"}]]) + (-> (js/expect (rtl/screen.getByText "a sample label")) (.toBeTruthy)))) (js/global.test "action-drawer renders with elements sub-label displaying" (fn [] - (render-action-drawer [[{:icon :i/friend - :label "a sample label" + (render-action-drawer [[{:icon :i/friend + :label "a sample label" :sub-label "a sample sub label"}]]) - (-> (js/expect (rtl/screen.getByText "a sample sub label")) + (-> (js/expect (rtl/screen.getByText "a sample sub label")) (.toBeTruthy)))) (js/global.test "action-drawer on click action works on element" @@ -29,14 +29,14 @@ :label "a sample label" :on-press event}]]) (rtl/fireEvent.press (rtl/screen.getByText "a sample label")) - (-> (js/expect event) + (-> (js/expect event) (.toHaveBeenCalled))))) (js/global.test "action-drawer renders two icons when set" (fn [] - (render-action-drawer [[{:icon :i/friend - :label "a sample label" - :right-icon :i/friend + (render-action-drawer [[{:icon :i/friend + :label "a sample label" + :right-icon :i/friend :accessibility-label :first-element}]]) (-> (js/expect (rtl/screen.getByLabelText "right-icon-for-action")) (.toBeTruthy)) @@ -45,19 +45,19 @@ (js/global.test "action-drawer does not render a divider when the add-divider? prop is false" (fn [] - (render-action-drawer [[{:icon :i/friend - :label "a sample label" - :add-divider? false + (render-action-drawer [[{:icon :i/friend + :label "a sample label" + :add-divider? false :accessibility-label :first-element}]]) - (-> (js/expect (rtl/screen.getAllByLabelText "divider")) + (-> (js/expect (rtl/screen.getAllByLabelText "divider")) (.not) (.toBeTruthy)))) (js/global.test "action-drawer renders a divider when the add-divider? prop is true" (fn [] - (render-action-drawer [[{:icon :i/friend - :label "a sample label" - :add-divider? true + (render-action-drawer [[{:icon :i/friend + :label "a sample label" + :add-divider? true :accessibility-label :first-element}]]) - (-> (js/expect (rtl/screen.getAllByLabelText "divider")) + (-> (js/expect (rtl/screen.getAllByLabelText "divider")) (.toBeTruthy)))) \ No newline at end of file diff --git a/src/quo2/components/drawers/action_drawers.cljs b/src/quo2/components/drawers/action_drawers.cljs index c25d22832b..96fd03724f 100644 --- a/src/quo2/components/drawers/action_drawers.cljs +++ b/src/quo2/components/drawers/action_drawers.cljs @@ -1,54 +1,62 @@ (ns quo2.components.drawers.action-drawers - (:require [react-native.core :as rn] + (:require [quo2.components.icon :as icon] [quo2.components.markdown.text :as text] - [quo2.components.icon :as icon] - [quo2.foundations.colors :as colors])) + [quo2.foundations.colors :as colors] + [react-native.core :as rn])) -(defn- get-icon-color [danger?] +(defn- get-icon-color + [danger?] (if danger? colors/danger-50 (colors/theme-colors colors/neutral-50 colors/neutral-40))) (def divider - [rn/view {:style {:border-top-width 1 - :border-top-color (colors/theme-colors - colors/neutral-10 colors/neutral-90) - :margin-top 8 - :margin-bottom 7 - :align-items :center - :flex-direction :row} - :accessible true - :accessibility-label :divider}]) + [rn/view + {:style {:border-top-width 1 + :border-top-color (colors/theme-colors + colors/neutral-10 + colors/neutral-90) + :margin-top 8 + :margin-bottom 7 + :align-items :center + :flex-direction :row} + :accessible true + :accessibility-label :divider}]) -(defn action [{:keys [icon - label - sub-label - right-icon - danger? - on-press - add-divider? - accessibility-label] :as action-props}] +(defn action + [{:keys [icon + label + sub-label + right-icon + danger? + on-press + add-divider? + accessibility-label] + :as action-props}] (when action-props [:<> {:key label} (when add-divider? divider) - [rn/touchable-highlight {:accessibility-label accessibility-label - :style {:border-radius 12 - :height (if sub-label 58 50) - :margin-horizontal 8} - :underlay-color (colors/theme-colors colors/neutral-5 colors/neutral-90) - :on-press on-press} - [rn/view {:style - {:height (if sub-label 58 50) - :margin-horizontal 12 - :flex-direction :row}} - [rn/view {:accessibility-label :left-icon-for-action - :accessible true - :style - {:height 20 - :margin-top :auto - :margin-bottom :auto - :margin-right 12 - :width 20}} + [rn/touchable-highlight + {:accessibility-label accessibility-label + :style {:border-radius 12 + :height (if sub-label 58 50) + :margin-horizontal 8} + :underlay-color (colors/theme-colors colors/neutral-5 colors/neutral-90) + :on-press on-press} + [rn/view + {:style + {:height (if sub-label 58 50) + :margin-horizontal 12 + :flex-direction :row}} + [rn/view + {:accessibility-label :left-icon-for-action + :accessible true + :style + {:height 20 + :margin-top :auto + :margin-bottom :auto + :margin-right 12 + :width 20}} [icon/icon icon {:color (get-icon-color danger?) :size 20}]] @@ -70,18 +78,20 @@ (colors/theme-colors colors/neutral-50 colors/neutral-40)}} sub-label])] (when right-icon - [rn/view {:style - {:height 20 - :margin-top :auto - :margin-bottom :auto - :width 20} - :accessible true - :accessibility-label :right-icon-for-action} + [rn/view + {:style + {:height 20 + :margin-top :auto + :margin-bottom :auto + :width 20} + :accessible true + :accessibility-label :right-icon-for-action} [icon/icon right-icon {:color (get-icon-color danger?) :size 20}]])]]])) -(defn action-drawer [sections] +(defn action-drawer + [sections] [:<> (doall (for [actions sections] diff --git a/src/quo2/components/dropdowns/dropdown.cljs b/src/quo2/components/dropdowns/dropdown.cljs index 8f89924708..edd6567627 100644 --- a/src/quo2/components/dropdowns/dropdown.cljs +++ b/src/quo2/components/dropdowns/dropdown.cljs @@ -1,97 +1,108 @@ (ns quo2.components.dropdowns.dropdown - (:require [react-native.core :as rn] - [quo2.components.icon :as icons] + (:require [quo2.components.icon :as icons] [quo2.components.markdown.text :as text] [quo2.foundations.colors :as colors] + [react-native.core :as rn] [react-native.reanimated :as reanimated] [reagent.core :as reagent])) -(defn apply-anim [dd-height val] +(defn apply-anim + [dd-height val] (reanimated/animate-shared-value-with-delay dd-height val 300 :easing1 0)) -(def sizes {:big {:icon-size 20 - :font {:font-size :paragraph-1} - :height 40 - :padding {:padding-with-icon {:padding-vertical 9 - :padding-horizontal 12} - :padding-with-no-icon {:padding-vertical 9 - :padding-left 12 - :padding-right 8}}} - :medium {:icon-size 20 - :font {:font-size :paragraph-1} - :height 32 - :padding {:padding-with-icon {:padding-vertical 5 - :padding-horizontal 8} - :padding-with-no-icon {:padding-vertical 5 - :padding-left 12 - :padding-right 8}}} - :small {:icon-size 15 - :font {:font-size :paragraph-2} - :height 24 - :padding {:padding-with-icon {:padding-vertical 3 - :padding-horizontal 6} - :padding-with-no-icon {:padding-vertical 3 - :padding-horizontal 6}}}}) +(def sizes + {:big {:icon-size 20 + :font {:font-size :paragraph-1} + :height 40 + :padding {:padding-with-icon {:padding-vertical 9 + :padding-horizontal 12} + :padding-with-no-icon {:padding-vertical 9 + :padding-left 12 + :padding-right 8}}} + :medium {:icon-size 20 + :font {:font-size :paragraph-1} + :height 32 + :padding {:padding-with-icon {:padding-vertical 5 + :padding-horizontal 8} + :padding-with-no-icon {:padding-vertical 5 + :padding-left 12 + :padding-right 8}}} + :small {:icon-size 15 + :font {:font-size :paragraph-2} + :height 24 + :padding {:padding-with-icon {:padding-vertical 3 + :padding-horizontal 6} + :padding-with-no-icon {:padding-vertical 3 + :padding-horizontal 6}}}}) (defn color-by-10 [color] (colors/alpha color 0.6)) -(defn dropdown-comp [{:keys [icon open? dd-height size disabled? dd-color use-border? border-color]}] +(defn dropdown-comp + [{:keys [icon open? dd-height size disabled? dd-color use-border? border-color]}] (let [dark? (colors/dark?) {:keys [width height width-with-icon padding font icon-size]} (size sizes) {:keys [padding-with-icon padding-with-no-icon]} padding font-size (:font-size font) spacing (case size - :big 4 + :big 4 :medium 2 - :small 2)] - [rn/touchable-opacity (cond-> {:on-press (fn [] - (if (swap! open? not) - (apply-anim dd-height 120) - (apply-anim dd-height 0))) - :style (cond-> (merge - (if icon - padding-with-icon - padding-with-no-icon) - {:width (if icon - width-with-icon - width) - :height height - :border-radius (case size - :big 12 - :medium 10 - :small 8) - :flex-direction :row - :align-items :center - :background-color (if @open? - dd-color - (color-by-10 dd-color))}) - use-border? (assoc :border-width 1 - :border-color (if @open? border-color (color-by-10 border-color))))} - disabled? (assoc-in [:style :opacity] 0.3) - disabled? (assoc :disabled true)) + :small 2)] + [rn/touchable-opacity + (cond-> + {:on-press (fn [] + (if (swap! open? not) + (apply-anim dd-height 120) + (apply-anim dd-height 0))) + :style (cond-> + (merge + (if icon + padding-with-icon + padding-with-no-icon) + {:width (if icon + width-with-icon + width) + :height height + :border-radius (case size + :big 12 + :medium 10 + :small 8) + :flex-direction :row + :align-items :center + :background-color (if @open? + dd-color + (color-by-10 dd-color))}) + use-border? (assoc :border-width 1 + :border-color (if @open? + border-color + (color-by-10 border-color))))} + disabled? (assoc-in [:style :opacity] 0.3) + disabled? (assoc :disabled true)) (when icon - [icons/icon icon {:no-color true - :size 20 - :container-style {:margin-right spacing - :margin-top 1 - :width icon-size - :height icon-size}}]) - [text/text {:size font-size - :weight :medium - :font :font-medium - :color :main} "Dropdown"] - [icons/icon (if @open? - (if dark? - :main-icons/pullup-dark - :main-icons/pullup) - (if dark? - :main-icons/dropdown-dark - :main-icons/dropdown)) + [icons/icon icon + {:no-color true + :size 20 + :container-style {:margin-right spacing + :margin-top 1 + :width icon-size + :height icon-size}}]) + [text/text + {:size font-size + :weight :medium + :font :font-medium + :color :main} "Dropdown"] + [icons/icon + (if @open? + (if dark? + :main-icons/pullup-dark + :main-icons/pullup) + (if dark? + :main-icons/dropdown-dark + :main-icons/dropdown)) {:size 20 :no-color true :container-style {:width (+ icon-size 3) @@ -105,9 +116,10 @@ (defn items-comp [{:keys [items on-select]}] (let [items-count (count items)] - [rn/scroll-view {:style {:height "100%"} - :horizontal false - :nestedScrollEnabled true} + [rn/scroll-view + {:style {:height "100%"} + :horizontal false + :nestedScrollEnabled true} (doall (map-indexed (fn [index item] [rn/touchable-opacity @@ -121,27 +133,32 @@ colors/white) :text-align :center} :on-press #(on-select item)} - [text/text {:style {:text-align :center}} item]]) items))])) + [text/text {:style {:text-align :center}} item]]) + items))])) -(defn dropdown [{:keys [items icon text default-item on-select size disabled? border-color use-border? dd-color]}] +(defn dropdown + [{:keys [items icon text default-item on-select size disabled? border-color use-border? dd-color]}] [:f> (fn [] (let [open? (reagent/atom false) dd-height (reanimated/use-shared-value 0)] [rn/view {:style {:flex-grow 1}} - [dropdown-comp {:items items - :icon icon - :disabled? disabled? - :size size - :dd-color dd-color - :text text - :border-color (colors/custom-color-by-theme border-color 50 60) - :use-border? use-border? - :default-item default-item - :open? open? - :dd-height dd-height}] - [reanimated/view {:style (reanimated/apply-animations-to-style - {:height dd-height} - {:height dd-height})} - [items-comp {:items items - :on-select on-select}]]]))]) + [dropdown-comp + {:items items + :icon icon + :disabled? disabled? + :size size + :dd-color dd-color + :text text + :border-color (colors/custom-color-by-theme border-color 50 60) + :use-border? use-border? + :default-item default-item + :open? open? + :dd-height dd-height}] + [reanimated/view + {:style (reanimated/apply-animations-to-style + {:height dd-height} + {:height dd-height})} + [items-comp + {:items items + :on-select on-select}]]]))]) diff --git a/src/quo2/components/header.cljs b/src/quo2/components/header.cljs index 084f71c054..307aad9679 100644 --- a/src/quo2/components/header.cljs +++ b/src/quo2/components/header.cljs @@ -1,15 +1,16 @@ (ns quo2.components.header (:require [oops.core :refer [oget]] - [react-native.reanimated :as reanimated] [quo2.components.buttons.button :as button] [quo2.components.markdown.text :as text] [quo2.foundations.colors :as colors] [react-native.core :as rn] + [react-native.reanimated :as reanimated] [reagent.core :as reagent])) (def header-height 56) -(defn header-wrapper-style [{:keys [height border-bottom background]}] +(defn header-wrapper-style + [{:keys [height border-bottom background]}] (merge {:background-color (colors/theme-colors colors/neutral-5 @@ -23,32 +24,37 @@ colors/neutral-5 colors/neutral-95)}))) -(def absolute-fill {:position :absolute - :top 0 - :bottom 0 - :left 0 - :right 0}) +(def absolute-fill + {:position :absolute + :top 0 + :bottom 0 + :left 0 + :right 0}) -(def content {:flex 1 - :flex-direction :row - :align-items :center - :justify-content :center}) +(def content + {:flex 1 + :flex-direction :row + :align-items :center + :justify-content :center}) -(def left {:position :absolute - :left 0 - :top 0 - :bottom 0 - :justify-content :center - :align-items :flex-start}) +(def left + {:position :absolute + :left 0 + :top 0 + :bottom 0 + :justify-content :center + :align-items :flex-start}) -(def right {:position :absolute - :right 0 - :top 0 - :bottom 0 - :justify-content :center - :align-items :flex-end}) +(def right + {:position :absolute + :right 0 + :top 0 + :bottom 0 + :justify-content :center + :align-items :flex-end}) -(defn title-style [{:keys [left right]} title-align] +(defn title-style + [{:keys [left right]} title-align] (merge absolute-fill (case title-align @@ -69,57 +75,66 @@ (def header-action-placeholder {:width 16}) -(def element {:align-items :center - :justify-content :center - :flex 1}) +(def element + {:align-items :center + :justify-content :center + :flex 1}) -(defn header-action [{:keys [icon label on-press disabled accessibility-label]}] - [button/button (merge {:on-press on-press - :disabled disabled} - (cond - icon {:type :icon - :theme :icon} - label {:type :secondary}) - (when accessibility-label - {:accessibility-label accessibility-label})) +(defn header-action + [{:keys [icon label on-press disabled accessibility-label]}] + [button/button + (merge {:on-press on-press + :disabled disabled} + (cond + icon {:type :icon + :theme :icon} + label {:type :secondary}) + (when accessibility-label + {:accessibility-label accessibility-label})) (cond icon icon label label)]) -(defn header-actions [{:keys [accessories component]}] +(defn header-actions + [{:keys [accessories component]}] [rn/view {:style element} (cond (seq accessories) (into [rn/view {:style header-actions-style}] (map header-action accessories)) - component component + component component :else [rn/view {:style header-action-placeholder}])]) -(defn header-title [{:keys [title subtitle component title-align]}] +(defn header-title + [{:keys [title subtitle component title-align]}] [:<> (cond - component component + component component (and title subtitle) [:<> - [text/text {:weight :medium - :number-of-lines 1} + [text/text + {:weight :medium + :number-of-lines 1} title] - [text/text {:weight :regular - :color :secondary - :number-of-lines 1} + [text/text + {:weight :regular + :color :secondary + :number-of-lines 1} subtitle]] - title [text/text {:weight :bold - :number-of-lines 0 - :align title-align - :size :large} - title])]) + title [text/text + {:weight :bold + :number-of-lines 0 + :align title-align + :size :large} + title])]) -(defn header [{:keys [left-width right-width]}] +(defn header + [{:keys [left-width right-width]}] (let [layout (reagent/atom {:left {:width (or left-width 8) :height header-height} :right {:width (or right-width 8) @@ -131,46 +146,61 @@ (let [width (oget evt "nativeEvent" "layout" "width") height (oget evt "nativeEvent" "layout" "height")] (when get-layout - (get-layout el {:width width - :height height})) - (swap! layout assoc el {:width width - :height height}))))] - (fn [{:keys [left-accessories left-component border-bottom - right-accessories right-component insets get-layout - title subtitle title-component style title-align - background] - :or {title-align :center - border-bottom false}}] + (get-layout el + {:width width + :height height})) + (swap! layout assoc + el + {:width width + :height height}))))] + (fn + [{:keys [left-accessories left-component border-bottom + right-accessories right-component insets get-layout + title subtitle title-component style title-align + background] + :or {title-align :center + border-bottom false}}] (let [status-bar-height (get insets :top 0) height (+ header-height status-bar-height)] - [reanimated/view {:style (header-wrapper-style {:height height - :background background - :border-bottom border-bottom})} - [rn/view {:pointer-events :box-none - :height status-bar-height}] - [rn/view {:style (merge {:height header-height} - style) - :pointer-events :box-none} - [rn/view {:style absolute-fill - :pointer-events :box-none} - [rn/view {:style content - :pointer-events :box-none} - [rn/view {:style left - :on-layout (handle-layout :left get-layout) - :pointer-events :box-none} - [header-actions {:accessories left-accessories - :component left-component}]] + [reanimated/view + {:style (header-wrapper-style {:height height + :background background + :border-bottom border-bottom})} + [rn/view + {:pointer-events :box-none + :height status-bar-height}] + [rn/view + {:style (merge {:height header-height} + style) + :pointer-events :box-none} + [rn/view + {:style absolute-fill + :pointer-events :box-none} + [rn/view + {:style content + :pointer-events :box-none} + [rn/view + {:style left + :on-layout (handle-layout :left get-layout) + :pointer-events :box-none} + [header-actions + {:accessories left-accessories + :component left-component}]] - [rn/view {:style (title-style @layout title-align) - :on-layout (handle-layout :title get-layout) - :pointer-events :box-none} - [header-title {:title title - :subtitle subtitle - :title-align title-align - :component title-component}]] + [rn/view + {:style (title-style @layout title-align) + :on-layout (handle-layout :title get-layout) + :pointer-events :box-none} + [header-title + {:title title + :subtitle subtitle + :title-align title-align + :component title-component}]] - [rn/view {:style right - :on-layout (handle-layout :right get-layout) - :pointer-events :box-none} - [header-actions {:accessories right-accessories - :component right-component}]]]]]])))) + [rn/view + {:style right + :on-layout (handle-layout :right get-layout) + :pointer-events :box-none} + [header-actions + {:accessories right-accessories + :component right-component}]]]]]])))) diff --git a/src/quo2/components/icon.cljs b/src/quo2/components/icon.cljs index 8ff11997f1..fdf22d623e 100644 --- a/src/quo2/components/icon.cljs +++ b/src/quo2/components/icon.cljs @@ -1,15 +1,15 @@ (ns quo2.components.icon - (:require - [clojure.string :as string] - [react-native.core :as rn] - [quo2.components.icons.icons :as icons] - [quo2.foundations.colors :as colors])) + (:require [clojure.string :as string] + [quo2.components.icons.icons :as icons] + [quo2.foundations.colors :as colors] + [react-native.core :as rn])) (defn memo-icon-fn ([icon-name] (memo-icon-fn icon-name nil)) - ([icon-name {:keys [color container-style size - accessibility-label no-color] - :or {accessibility-label :icon}}] + ([icon-name + {:keys [color container-style size + accessibility-label no-color] + :or {accessibility-label :icon}}] (let [size (or size 20)] ^{:key icon-name} [rn/image diff --git a/src/quo2/components/icons/icons.clj b/src/quo2/components/icons/icons.clj index 73c6c845b3..09eb83d22b 100644 --- a/src/quo2/components/icons/icons.clj +++ b/src/quo2/components/icons/icons.clj @@ -4,12 +4,14 @@ (def icon-path "./resources/images/icons2/") -(defn combine-path [path el] +(defn combine-path + [path el] (if (System/getenv "COMPONENT_TEST") (str "." path el "@2x.png") (str "." path el ".png"))) -(defn require-icon [size path] +(defn require-icon + [size path] (fn [el] (let [s (combine-path path el) k (-> el @@ -19,18 +21,21 @@ (str size))] [k `(js/require ~s)]))) -(defn get-files [path] +(defn get-files + [path] (->> (io/file path) file-seq (filter #(string/ends-with? % "png")) (map #(first (string/split (.getName %) #"@"))) distinct)) -(defn get-icons [size] +(defn get-icons + [size] (let [path (str icon-path size "x" size "/")] (into {} (map (require-icon size path) (get-files path))))) -(defmacro resolve-icons [] +(defmacro resolve-icons + [] (merge (get-icons 12) (get-icons 16) diff --git a/src/quo2/components/icons/icons.cljs b/src/quo2/components/icons/icons.cljs index 5f49cff15f..8426bacc54 100644 --- a/src/quo2/components/icons/icons.cljs +++ b/src/quo2/components/icons/icons.cljs @@ -4,7 +4,8 @@ (def icons (icons/resolve-icons)) -(defn icon-source [icon] +(defn icon-source + [icon] (if-let [icon (get icons (name icon))] icon (do diff --git a/src/quo2/components/info/info_message.cljs b/src/quo2/components/info/info_message.cljs index c574ca72f2..564c906c45 100644 --- a/src/quo2/components/info/info_message.cljs +++ b/src/quo2/components/info/info_message.cljs @@ -1,19 +1,20 @@ (ns quo2.components.info.info-message - (:require [quo2.theme :as theme] - [react-native.core :as rn] + (:require [quo2.components.icon :as quo2.icons] [quo2.components.markdown.text :as text] [quo2.foundations.colors :as colors] - [quo2.components.icon :as quo2.icons])) + [quo2.theme :as theme] + [react-native.core :as rn])) (def themes - {:light {:default colors/neutral-40 - :success colors/success-50 - :error colors/danger-50} - :dark {:default colors/neutral-60 - :success colors/success-60 - :error colors/danger-60}}) + {:light {:default colors/neutral-40 + :success colors/success-50 + :error colors/danger-50} + :dark {:default colors/neutral-60 + :success colors/success-60 + :error colors/danger-60}}) -(defn get-color [key] +(defn get-color + [key] (get-in themes [(theme/get-theme) key])) (defn info-message @@ -26,17 +27,20 @@ :icon-color colors/white ;; icon color override :no-icon-color? false ;; disable tint color for icon" [{:keys [type size icon text-color icon-color no-icon-color?]} message] - (let [weight (if (= size :default) :regular :medium) - size (if (= size :default) :paragraph-2 :label) - text-color (or text-color (get-color type)) - icon-color (or icon-color text-color)] - [rn/view {:style {:flex-direction :row - :flex 1}} - [quo2.icons/icon icon {:color icon-color - :no-color no-icon-color? - :size 12 - :container-style {:margin-top 3}}] - [text/text {:size size - :weight weight - :style {:color text-color - :margin-horizontal 8}} message]])) + (let [weight (if (= size :default) :regular :medium) + size (if (= size :default) :paragraph-2 :label) + text-color (or text-color (get-color type)) + icon-color (or icon-color text-color)] + [rn/view + {:style {:flex-direction :row + :flex 1}} + [quo2.icons/icon icon + {:color icon-color + :no-color no-icon-color? + :size 12 + :container-style {:margin-top 3}}] + [text/text + {:size size + :weight weight + :style {:color text-color + :margin-horizontal 8}} message]])) diff --git a/src/quo2/components/info/information_box.cljs b/src/quo2/components/info/information_box.cljs index 2562bee985..8c309e26af 100644 --- a/src/quo2/components/info/information_box.cljs +++ b/src/quo2/components/info/information_box.cljs @@ -1,44 +1,46 @@ (ns quo2.components.info.information-box - (:require [quo2.theme :as theme] - [react-native.core :as rn] - [clojure.string :as string] - [quo2.foundations.colors :as colors] - [quo2.components.icon :as quo2.icons] + (:require [clojure.string :as string] [quo2.components.buttons.button :as quo2.button] - [quo2.components.info.info-message :as info-message])) + [quo2.components.icon :as quo2.icons] + [quo2.components.info.info-message :as info-message] + [quo2.foundations.colors :as colors] + [quo2.theme :as theme] + [react-native.core :as rn])) (def themes - {:light {:default {:bg colors/white - :border colors/neutral-20 - :icon colors/neutral-50 - :text colors/neutral-100} - :informative {:bg colors/primary-50-opa-5 - :border colors/primary-50-opa-10 - :icon colors/primary-50 - :text colors/neutral-100} - :error {:bg colors/danger-50-opa-5 - :border colors/danger-50-opa-10 - :icon colors/danger-50 - :text colors/danger-50} + {:light {:default {:bg colors/white + :border colors/neutral-20 + :icon colors/neutral-50 + :text colors/neutral-100} + :informative {:bg colors/primary-50-opa-5 + :border colors/primary-50-opa-10 + :icon colors/primary-50 + :text colors/neutral-100} + :error {:bg colors/danger-50-opa-5 + :border colors/danger-50-opa-10 + :icon colors/danger-50 + :text colors/danger-50} :close-button colors/neutral-100} - :dark {:default {:bg colors/neutral-90 - :border colors/neutral-70 - :icon colors/neutral-40 - :text colors/white} - :informative {:bg colors/primary-50-opa-5 - :border colors/primary-50-opa-10 - :icon colors/white - :text colors/white} - :error {:bg colors/danger-50-opa-5 - :border colors/danger-50-opa-10 - :icon colors/danger-50 - :text colors/danger-50} + :dark {:default {:bg colors/neutral-90 + :border colors/neutral-70 + :icon colors/neutral-40 + :text colors/white} + :informative {:bg colors/primary-50-opa-5 + :border colors/primary-50-opa-10 + :icon colors/white + :text colors/white} + :error {:bg colors/danger-50-opa-5 + :border colors/danger-50-opa-10 + :icon colors/danger-50 + :text colors/danger-50} :close-button colors/white}}) -(defn get-color [key] +(defn get-color + [key] (get-in themes [(theme/get-theme) key])) -(defn get-color-by-type [type key] +(defn get-color-by-type + [type key] (get-in themes [(theme/get-theme) type key])) (defn information-box @@ -54,40 +56,47 @@ :button-label \"PressMe\" ;; add action button with label :on-button-press action ;; (required for information box with button-label) :on-close on-close ;; (optional on-close call)" - [{:keys [type closable? closed? id icon style button-label on-button-press on-close no-icon-color?]} message] + [{:keys [type closable? closed? id icon style button-label on-button-press on-close no-icon-color?]} + message] (let [background-color (get-color-by-type type :bg) border-color (get-color-by-type type :border) icon-color (get-color-by-type type :icon) text-color (get-color-by-type type :text) include-button? (not (string/blank? button-label))] (when-not closed? - [rn/view {:accessibility-label (or id :information-box) - :style (merge {:background-color background-color - :border-color border-color - :border-width 1 - :border-radius 12 - :padding-top (if include-button? 10 11) - :padding-bottom (if include-button? 12 11) - :padding-horizontal 16} style)} - [rn/view {:style {:flex-direction :row - :align-self :flex-start}} - [info-message/info-message {:size :default - :icon icon - :text-color text-color - :icon-color icon-color - :no-icon-color? no-icon-color?} message] + [rn/view + {:accessibility-label (or id :information-box) + :style (merge {:background-color background-color + :border-color border-color + :border-width 1 + :border-radius 12 + :padding-top (if include-button? 10 11) + :padding-bottom (if include-button? 12 11) + :padding-horizontal 16} + style)} + [rn/view + {:style {:flex-direction :row + :align-self :flex-start}} + [info-message/info-message + {:size :default + :icon icon + :text-color text-color + :icon-color icon-color + :no-icon-color? no-icon-color?} message] (when closable? [rn/touchable-opacity {:on-press on-close :accessibility-label (str (or id "information-box") "-close-button")} - [quo2.icons/icon :i/close {:size 12 - :color (get-color :close-button) - :container-style {:margin-top 4 - :margin-left 8}}]])] + [quo2.icons/icon :i/close + {:size 12 + :color (get-color :close-button) + :container-style {:margin-top 4 + :margin-left 8}}]])] (when include-button? - [quo2.button/button {:type :primary - :size 24 - :on-press on-button-press - :style {:margin-left 20 - :margin-top 8 - :align-self :flex-start}} button-label])]))) + [quo2.button/button + {:type :primary + :size 24 + :on-press on-button-press + :style {:margin-left 20 + :margin-top 8 + :align-self :flex-start}} button-label])]))) diff --git a/src/quo2/components/list_items/channel.cljs b/src/quo2/components/list_items/channel.cljs index 570b586321..4162b9ca4f 100644 --- a/src/quo2/components/list_items/channel.cljs +++ b/src/quo2/components/list_items/channel.cljs @@ -7,25 +7,28 @@ [quo2.theme :as theme] [react-native.core :as rn])) -(defn list-item [{:keys [name locked? mentions-count unread-messages? - muted? is-active-channel? emoji channel-color on-press] - :or {channel-color colors/primary-50}}] +(defn list-item + [{:keys [name locked? mentions-count unread-messages? + muted? is-active-channel? emoji channel-color on-press] + :or {channel-color colors/primary-50}}] [rn/touchable-opacity {:on-press on-press} - [rn/view {:style (merge {:height 48 - :display :flex - :border-radius 12 - :flex-direction :row - :justify-content :space-between - :align-items :center - :width "100%" - :padding-left 12 - :padding-right 12} - (when is-active-channel? - {:background-color (colors/theme-alpha channel-color 0.05 0.05)}))} - [rn/view {:display :flex - :flex-direction :row - :justify-content :flex-start - :align-items :center} + [rn/view + {:style (merge {:height 48 + :display :flex + :border-radius 12 + :flex-direction :row + :justify-content :space-between + :align-items :center + :width "100%" + :padding-left 12 + :padding-right 12} + (when is-active-channel? + {:background-color (colors/theme-alpha channel-color 0.05 0.05)}))} + [rn/view + {:display :flex + :flex-direction :row + :justify-content :flex-start + :align-items :center} [channel-avatar/channel-avatar {:big? true :locked? locked? @@ -35,7 +38,8 @@ {:style (merge {:margin-left 12} (when (and (not locked?) muted?) {:color (if (theme/dark?) colors/neutral-60 colors/neutral-40)})) - :weight :medium :size :paragraph-1} (str "# " name)]] + :weight :medium + :size :paragraph-1} (str "# " name)]] [rn/view {:style {:height 20}} (when (and (not locked?) muted?) @@ -45,8 +49,9 @@ (when (and (not locked?) (not muted?) (pos? (int mentions-count))) - [rn/view {:style {:margin-right 2 - :margin-top 2}} + [rn/view + {:style {:margin-right 2 + :margin-top 2}} [quo2.counter/counter {:override-bg-color channel-color} mentions-count]]) (when (and (not locked?) (not muted?) diff --git a/src/quo2/components/list_items/menu_item.cljs b/src/quo2/components/list_items/menu_item.cljs index 115f92e081..a269a1e17e 100644 --- a/src/quo2/components/list_items/menu_item.cljs +++ b/src/quo2/components/list_items/menu_item.cljs @@ -1,17 +1,18 @@ (ns quo2.components.list-items.menu-item - (:require [react-native.core :as rn] - [quo2.foundations.colors :as colors :refer [theme-colors]] + (:require [quo2.components.icon :as icons] [quo2.components.markdown.text :as text] - [quo2.components.icon :as icons])) + [quo2.foundations.colors :as colors :refer [theme-colors]] + [react-native.core :as rn])) -(defn themes [type] +(defn themes + [type] (case type - :main {:icon-color (theme-colors colors/neutral-50 colors/neutral-10) - :background (theme-colors colors/white colors/neutral-90) - :text-color (theme-colors colors/neutral-100 colors/white)} - :danger {:icon-color (theme-colors colors/danger-50 colors/danger-60) - :background (theme-colors colors/white colors/neutral-90) - :text-color (theme-colors colors/danger-50 colors/danger-60)})) + :main {:icon-color (theme-colors colors/neutral-50 colors/neutral-10) + :background (theme-colors colors/white colors/neutral-90) + :text-color (theme-colors colors/neutral-100 colors/white)} + :danger {:icon-color (theme-colors colors/danger-50 colors/danger-60) + :background (theme-colors colors/white colors/neutral-90) + :text-color (theme-colors colors/danger-50 colors/danger-60)})) (defn menu-item [{:keys [type title accessibility-label icon on-press] @@ -19,25 +20,28 @@ (let [{:keys [icon-color text-color background]} (themes type)] [rn/touchable-opacity (merge {:accessibility-label accessibility-label - :style {:background-color background - :height 48 - :flex-direction :row - :align-items :center}} + :style {:background-color background + :height 48 + :flex-direction :row + :align-items :center}} (when on-press {:on-press on-press})) - [rn/view {:style {:flex-direction :row - :flex-grow 0 - :flex-shrink 1 - :padding-horizontal 20}} - [rn/view {:style {:width 20 - :height 20 - :align-items :center - :justify-content :center - :margin-right 12}} + [rn/view + {:style {:flex-direction :row + :flex-grow 0 + :flex-shrink 1 + :padding-horizontal 20}} + [rn/view + {:style {:width 20 + :height 20 + :align-items :center + :justify-content :center + :margin-right 12}} [icons/icon icon {:color icon-color}]] - [text/text {:weight :medium - :style {:color text-color} - :ellipsize-mode :tail - :number-of-lines 1 - :size :paragraph-1} + [text/text + {:weight :medium + :style {:color text-color} + :ellipsize-mode :tail + :number-of-lines 1 + :size :paragraph-1} title]]])) diff --git a/src/quo2/components/list_items/preview_list.cljs b/src/quo2/components/list_items/preview_list.cljs index 39cfa3a312..907c7a78fb 100644 --- a/src/quo2/components/list_items/preview_list.cljs +++ b/src/quo2/components/list_items/preview_list.cljs @@ -1,97 +1,112 @@ (ns quo2.components.list-items.preview-list - (:require [react-native.core :as rn] - [react-native.hole-view :as hole-view] - [react-native.fast-image :as fast-image] - [quo2.foundations.colors :as colors] + (:require [quo2.components.avatars.user-avatar :as user-avatar] [quo2.components.icon :as quo2.icons] - [quo2.components.avatars.user-avatar :as user-avatar] - [quo2.components.markdown.text :as quo2.text])) + [quo2.components.markdown.text :as quo2.text] + [quo2.foundations.colors :as colors] + [react-native.core :as rn] + [react-native.fast-image :as fast-image] + [react-native.hole-view :as hole-view])) -(def params {32 {:border-radius {:circular 16 :rounded 10} - :hole-radius {:circular 18 :rounded 12} - :margin-left -8 - :hole-size 36 - :hole-x 22 - :hole-y -2} - 24 {:border-radius {:circular 12 :rounded 8} - :hole-radius {:circular 13 :rounded 9} - :margin-left -4 - :hole-size 26 - :hole-x 19 - :hole-y -1} - 16 {:border-radius {:circular 8 :rounded 8} - :hole-radius {:circular 9 :rounded 9} - :margin-left -4 - :hole-size 18 - :hole-x 11 - :hole-y -1}}) +(def params + {32 {:border-radius {:circular 16 :rounded 10} + :hole-radius {:circular 18 :rounded 12} + :margin-left -8 + :hole-size 36 + :hole-x 22 + :hole-y -2} + 24 {:border-radius {:circular 12 :rounded 8} + :hole-radius {:circular 13 :rounded 9} + :margin-left -4 + :hole-size 26 + :hole-x 19 + :hole-y -1} + 16 {:border-radius {:circular 8 :rounded 8} + :hole-radius {:circular 9 :rounded 9} + :margin-left -4 + :hole-size 18 + :hole-x 11 + :hole-y -1}}) ;; TODO - Add avatar components for other types once implemented -(defn avatar [item type size border-radius] +(defn avatar + [item type size border-radius] (case type - :user [user-avatar/user-avatar - (merge {:ring? false - :status-indicator? false - :size (case size 32 :small 24 :xs 16 :xxxs)} - item)] - (:photo :collectible) [fast-image/fast-image {:source (:source item) - :style {:width size - :height size - :border-radius border-radius}}])) + :user [user-avatar/user-avatar + (merge {:ring? false + :status-indicator? false + :size (case size + 32 :small + 24 :xs + 16 :xxxs)} + item)] + (:photo :collectible) [fast-image/fast-image + {:source (:source item) + :style {:width size + :height size + :border-radius border-radius}}])) -(defn list-item [index type size item list-size margin-left - hole-size hole-radius hole-x hole-y border-radius] +(defn list-item + [index type size item list-size margin-left + hole-size hole-radius hole-x hole-y border-radius] (let [last-item? (= index (- list-size 1))] - [hole-view/hole-view {:style {:margin-left (if (= index 0) 0 margin-left)} - :holes (if last-item? [] - [{:x hole-x - :y hole-y - :width hole-size - :height hole-size - :borderRadius hole-radius}])} + [hole-view/hole-view + {:style {:margin-left (if (= index 0) 0 margin-left)} + :holes (if last-item? + [] + [{:x hole-x + :y hole-y + :width hole-size + :height hole-size + :borderRadius hole-radius}])} [avatar item type size border-radius]])) -(defn get-overflow-color [transparent? transparent-color light-color dark-color override-theme] +(defn get-overflow-color + [transparent? transparent-color light-color dark-color override-theme] (if transparent? transparent-color (colors/theme-colors light-color dark-color override-theme))) -(defn overflow-label [label size transparent? border-radius margin-left override-theme more-than-99-label] - [rn/view {:style {:width size - :height size - :margin-left margin-left - :border-radius border-radius - :justify-content :center - :align-items :center - :background-color (get-overflow-color - transparent? - colors/white-opa-10 - colors/neutral-20 - colors/neutral-70 - override-theme)}} +(defn overflow-label + [label size transparent? border-radius margin-left override-theme more-than-99-label] + [rn/view + {:style {:width size + :height size + :margin-left margin-left + :border-radius border-radius + :justify-content :center + :align-items :center + :background-color (get-overflow-color + transparent? + colors/white-opa-10 + colors/neutral-20 + colors/neutral-70 + override-theme)}} (if (= size 16) - [quo2.icons/icon :i/more {:size 12 - :color (get-overflow-color - transparent? - colors/white-opa-70 - colors/neutral-50 - colors/neutral-40 - override-theme)}] - [quo2.text/text {:size (if (= size 32) :paragraph-2 :label) - :weight :medium - :style {:color (get-overflow-color - transparent? - colors/white-opa-70 - colors/neutral-60 - colors/neutral-40 - override-theme) - :margin-left -2}} + [quo2.icons/icon :i/more + {:size 12 + :color (get-overflow-color + transparent? + colors/white-opa-70 + colors/neutral-50 + colors/neutral-40 + override-theme)}] + [quo2.text/text + {:size (if (= size 32) :paragraph-2 :label) + :weight :medium + :style {:color (get-overflow-color + transparent? + colors/white-opa-70 + colors/neutral-60 + colors/neutral-40 + override-theme) + :margin-left -2}} ;; If overflow label is below 100, show label as +label (ex. +30), else just show 99+ (if (< label 100) (str "+" label) more-than-99-label)])]) -(defn border-type [type] +(defn border-type + [type] (case type (:account :collectible :photo) :rounded :circular)) @@ -120,4 +135,5 @@ [list-item index type size (get items-arr index) list-size margin-left hole-size hole-radius hole-x hole-y border-radius]) (when (> list-size 4) - [overflow-label (- list-size 3) size transparent? border-radius margin-left override-theme more-than-99-label])])) + [overflow-label (- list-size 3) size transparent? border-radius margin-left override-theme + more-than-99-label])])) diff --git a/src/quo2/components/loaders/skeleton.cljs b/src/quo2/components/loaders/skeleton.cljs index 1a6b0029c6..1a8578844c 100644 --- a/src/quo2/components/loaders/skeleton.cljs +++ b/src/quo2/components/loaders/skeleton.cljs @@ -1,25 +1,27 @@ (ns quo2.components.loaders.skeleton - (:require [react-native.core :as rn] + (:require [quo2.foundations.colors :as colors] + [react-native.core :as rn] [react-native.masked-view :as masked-view] - [reagent.core :as reagent] [react-native.reanimated :as reanimated] - [quo2.foundations.colors :as colors])) + [reagent.core :as reagent])) (def message-skeleton-height 54) (def avatar-skeleton-size 32) -(def message-content-width [{:author 80 - :message 249} - {:author 124 - :message 156} - {:author 96 - :message 212} - {:author 112 - :message 144}]) +(def message-content-width + [{:author 80 + :message 249} + {:author 124 + :message 156} + {:author 96 + :message 212} + {:author 112 + :message 144}]) ;; Standlone message skeleton -(defn message-skeleton [] +(defn message-skeleton + [] [:f> (fn [] (let [color (colors/theme-colors colors/neutral-5 colors/neutral-70) @@ -31,47 +33,56 @@ translate-x (reanimated/use-shared-value (- window-width)) animated-gradient-style (reanimated/apply-animations-to-style {:transform [{:translateX translate-x}]} - {:width window-width - :height "100%"})] + {:width window-width + :height "100%"})] (reanimated/animate-shared-value-with-repeat translate-x window-width 1000 :linear (- 1) false) [masked-view/masked-view - {:style {:height message-skeleton-height} + {:style {:height message-skeleton-height} :maskElement (reagent/as-element - [rn/view {:style {:height message-skeleton-height - :flex-direction :row - :padding-vertical 11 - :background-color :transparent - :padding-left 21}} - [rn/view {:style {:height avatar-skeleton-size - :width avatar-skeleton-size - :border-radius (/ avatar-skeleton-size 2) - :background-color color - :overflow :hidden}}] - [rn/view {:style {:padding-left 8 - :background-color :transparent}} - [rn/view {:style {:height 8 - :width author-width - :border-radius 6 - :background-color color - :margin-bottom 8 - :overflow :hidden}}] - [rn/view {:style {:height 16 - :width message-width - :border-radius 6 - :overflow :hidden - :background-color color}}]]])} - [rn/view {:style {:flex 1 - :background-color color}} - [reanimated/linear-gradient {:colors [color color loading-color color color] - :start {:x 0 :y 0} - :end {:x 1 :y 0} - :style animated-gradient-style}]]]))]) + [rn/view + {:style {:height message-skeleton-height + :flex-direction :row + :padding-vertical 11 + :background-color :transparent + :padding-left 21}} + [rn/view + {:style {:height avatar-skeleton-size + :width avatar-skeleton-size + :border-radius (/ avatar-skeleton-size 2) + :background-color color + :overflow :hidden}}] + [rn/view + {:style {:padding-left 8 + :background-color :transparent}} + [rn/view + {:style {:height 8 + :width author-width + :border-radius 6 + :background-color color + :margin-bottom 8 + :overflow :hidden}}] + [rn/view + {:style {:height 16 + :width message-width + :border-radius 6 + :overflow :hidden + :background-color color}}]]])} + [rn/view + {:style {:flex 1 + :background-color color}} + [reanimated/linear-gradient + {:colors [color color loading-color color color] + :start {:x 0 :y 0} + :end {:x 1 :y 0} + :style animated-gradient-style}]]]))]) -(defn skeleton [parent-height] +(defn skeleton + [parent-height] (let [number-of-skeletons (int (Math/floor (/ parent-height message-skeleton-height)))] - [rn/view {:style {:background-color (colors/theme-colors - colors/white - colors/neutral-90) - :flex 1}} + [rn/view + {:style {:background-color (colors/theme-colors + colors/white + colors/neutral-90) + :flex 1}} (for [n (range number-of-skeletons)] [message-skeleton {:key n}])])) diff --git a/src/quo2/components/markdown/text.cljs b/src/quo2/components/markdown/text.cljs index cd47639bca..cba4031bba 100644 --- a/src/quo2/components/markdown/text.cljs +++ b/src/quo2/components/markdown/text.cljs @@ -1,11 +1,12 @@ (ns quo2.components.markdown.text - (:require [react-native.core :as rn] - [quo2.theme :as theme] - [quo2.foundations.colors :as colors] + (:require [quo2.foundations.colors :as colors] [quo2.foundations.typography :as typography] + [quo2.theme :as theme] + [react-native.core :as rn] [reagent.core :as reagent])) -(defn text-style [{:keys [size align weight style]}] +(defn text-style + [{:keys [size align weight style]}] (merge (case (or weight :regular) :regular typography/font-regular :medium typography/font-medium @@ -26,10 +27,12 @@ style (assoc style :color (if (= (theme/get-theme) :dark) colors/white colors/neutral-100))))) -(defn text [] +(defn text + [] (let [this (reagent/current-component) props (reagent/props this) style (text-style props)] - (into [rn/text (merge {:style style} - (dissoc props :style :size :align :weight :color))] + (into [rn/text + (merge {:style style} + (dissoc props :style :size :align :weight :color))] (reagent/children this)))) diff --git a/src/quo2/components/messages/author/style.cljs b/src/quo2/components/messages/author/style.cljs index 31430d2812..266e5553f9 100644 --- a/src/quo2/components/messages/author/style.cljs +++ b/src/quo2/components/messages/author/style.cljs @@ -8,10 +8,12 @@ :flex-direction :row :align-items :center}) -(defn ens-text [] +(defn ens-text + [] {:color (colors/theme-colors colors/neutral-100 colors/white)}) -(defn nickname-text [] +(defn nickname-text + [] {:color (colors/theme-colors colors/neutral-100 colors/white)}) (def middle-dot-nickname @@ -26,7 +28,8 @@ {:color colors/neutral-50 :margin-left 4}) -(defn profile-name-text [nickname?] +(defn profile-name-text + [nickname?] {:color (if nickname? (colors/theme-colors colors/neutral-60 colors/neutral-40) (colors/theme-colors colors/neutral-100 colors/white))}) @@ -34,6 +37,7 @@ (def icon-container {:margin-left 4}) -(defn time-text [ens?] +(defn time-text + [ens?] {:color colors/neutral-50 :margin-left (if ens? 8 4)}) \ No newline at end of file diff --git a/src/quo2/components/messages/author/view.cljs b/src/quo2/components/messages/author/view.cljs index 12eb339c65..499eb14fd4 100644 --- a/src/quo2/components/messages/author/view.cljs +++ b/src/quo2/components/messages/author/view.cljs @@ -1,60 +1,68 @@ (ns quo2.components.messages.author.view - (:require - [quo2.components.messages.author.style :as style] - [react-native.core :as rn] - [quo2.components.markdown.text :as text] - [quo2.components.icon :as icons] - [status-im.utils.utils :as utils] - [clojure.string :as string])) + (:require [clojure.string :as string] + [quo2.components.icon :as icons] + [quo2.components.markdown.text :as text] + [quo2.components.messages.author.style :as style] + [react-native.core :as rn] + [status-im.utils.utils :as utils])) (def middle-dot "·") -(defn display-name [{:keys [profile-name nickname ens-name text-style]}] +(defn display-name + [{:keys [profile-name nickname ens-name text-style]}] (let [ens? (-> ens-name string/blank? not) nickname? (-> nickname string/blank? not)] (if ens? - [text/text (merge {:weight :semi-bold - :size :paragraph-2 - :style (style/ens-text)} - text-style) + [text/text + (merge {:weight :semi-bold + :size :paragraph-2 + :style (style/ens-text)} + text-style) ens-name] [:<> (if nickname? - [text/text (merge {:weight :semi-bold - :size :paragraph-2 - :style (style/nickname-text)} - text-style) + [text/text + (merge {:weight :semi-bold + :size :paragraph-2 + :style (style/nickname-text)} + text-style) nickname] - [text/text (merge {:weight :semi-bold - :size :paragraph-2 - :style (style/profile-name-text nickname?)} - text-style) + [text/text + (merge {:weight :semi-bold + :size :paragraph-2 + :style (style/profile-name-text nickname?)} + text-style) profile-name])]))) -(defn author [{:keys [profile-name nickname chat-key ens-name time-str contact? verified? untrustworthy?]}] +(defn author + [{:keys [profile-name nickname chat-key ens-name time-str contact? verified? untrustworthy?]}] [:f> (fn [] - (let [ens? (-> ens-name string/blank? not) + (let [ens? (-> ens-name string/blank? not) nickname? (-> nickname string/blank? not)] [rn/view {:style style/container} (if ens? - [text/text {:weight :semi-bold - :size :paragraph-2 - :style (style/ens-text)} + [text/text + {:weight :semi-bold + :size :paragraph-2 + :style (style/ens-text)} ens-name] [:<> (when nickname? [:<> - [text/text {:weight :semi-bold - :size :paragraph-2 - :style (style/nickname-text)} + [text/text + {:weight :semi-bold + :size :paragraph-2 + :style (style/nickname-text)} nickname] - [text/text {:size :paragraph-2 - :style style/middle-dot-nickname} + [text/text + {:size :paragraph-2 + :style style/middle-dot-nickname} middle-dot]]) - [text/text {:weight (if nickname? :medium :semi-bold) - :size :paragraph-2 - :style (style/profile-name-text nickname?)} + [text/text + {:weight (if nickname? :medium :semi-bold) + :size :paragraph-2 + :style (style/profile-name-text nickname?)} profile-name]]) (when contact? [icons/icon :main-icons2/contact @@ -73,16 +81,19 @@ :no-color true :container-style style/icon-container}]) (when-not ens? - [text/text {:monospace true - :size :paragraph-2 - :style style/chat-key-text} + [text/text + {:monospace true + :size :paragraph-2 + :style style/chat-key-text} (utils/get-shortened-address chat-key)]) (when-not ens? - [text/text {:monospace true - :size :paragraph-2 - :style style/middle-dot-chat-key} + [text/text + {:monospace true + :size :paragraph-2 + :style style/middle-dot-chat-key} middle-dot]) - [text/text {:monospace true - :size :paragraph-2 - :style (style/time-text ens?)} + [text/text + {:monospace true + :size :paragraph-2 + :style (style/time-text ens?)} time-str]]))]) diff --git a/src/quo2/components/messages/gap.cljs b/src/quo2/components/messages/gap.cljs index 42631601b7..43869f9ffd 100644 --- a/src/quo2/components/messages/gap.cljs +++ b/src/quo2/components/messages/gap.cljs @@ -1,12 +1,11 @@ (ns quo2.components.messages.gap - (:require - [oops.core :refer [oget]] - [react-native.core :as rn] - [quo2.theme :as theme] - [quo2.components.icon :as icon] - [quo2.components.markdown.text :as text] - [quo2.foundations.colors :as colors] - [reagent.core :as reagent])) + (:require [oops.core :refer [oget]] + [quo2.components.icon :as icon] + [quo2.components.markdown.text :as text] + [quo2.foundations.colors :as colors] + [quo2.theme :as theme] + [react-native.core :as rn] + [reagent.core :as reagent])) ;;; helpers (def themes @@ -17,7 +16,8 @@ :time colors/neutral-40 :background colors/neutral-95}}) -(defn get-color [key] +(defn get-color + [key] (get-in themes [(theme/get-theme) key])) (def ui-images @@ -27,45 +27,51 @@ :dark {:horizontal (js/require "../resources/images/ui/message-gap-hborder-dark.png") :circles (js/require "../resources/images/ui/message-gap-circle-bg-dark.png")}}) -(defn get-image [key] +(defn get-image + [key] (get-in ui-images [(theme/get-theme) key])) ;;; components ;;;; borders -(defn hborder [{:keys [type style]}] - [rn/image {:source (get-image :horizontal) - :resize-mode :repeat - :style (merge {:position :absolute - :left 0 - :padding-horizontal 4 - :overflow :hidden - :width "110%" - :height 8 - :margin-left -4} - (if (= type :top) - {:top 0} - {:transform [{:rotateZ "180deg"}] - :bottom 0}) - style)}]) +(defn hborder + [{:keys [type style]}] + [rn/image + {:source (get-image :horizontal) + :resize-mode :repeat + :style (merge {:position :absolute + :left 0 + :padding-horizontal 4 + :overflow :hidden + :width "110%" + :height 8 + :margin-left -4} + (if (= type :top) + {:top 0} + {:transform [{:rotateZ "180deg"}] + :bottom 0}) + style)}]) -(defn vborder [type body-height] +(defn vborder + [type body-height] (let [height @body-height img (get-image :vertical)] (when (and img height) - [rn/image {:source img - :resize-mode :repeat - :style (merge - {:position :absolute - :top 4 - :height (- height 8) - :width 4} - (if (= type :left) - {:left 0} - {:transform [{:rotate "180deg"}] - :right 0}))}]))) + [rn/image + {:source img + :resize-mode :repeat + :style (merge + {:position :absolute + :top 4 + :height (- height 8) + :width 4} + (if (= type :left) + {:left 0} + {:transform [{:rotate "180deg"}] + :right 0}))}]))) ;;;; others -(defn circle [] +(defn circle + [] [rn/view {:width 9 :height 9 @@ -75,34 +81,40 @@ :border-color (get-color :icon) :border-radius 50}]) -(defn timestamp [str] - [text/text {:size :label - :style {:text-transform :none - :color (get-color :time)}} str]) +(defn timestamp + [str] + [text/text + {:size :label + :style {:text-transform :none + :color (get-color :time)}} str]) -(defn info-button [on-press] +(defn info-button + [on-press] [rn/touchable-without-feedback {:on-press on-press} [icon/icon "message-gap-info" {:size 12 :no-color true :container-style {:padding 4}}]]) ;;;; timeline/body -(defn timeline [] - [rn/view {:flex 0 - :margin-right 20 - :align-items :center - :width 9 - :justify-content :space-between} +(defn timeline + [] + [rn/view + {:flex 0 + :margin-right 20 + :align-items :center + :width 9 + :justify-content :space-between} [circle] [rn/image {:style {:flex 1} :source (get-image :circles) :resize-mode :repeat}] [circle]]) -(defn body [timestamp-far timestamp-near on-info-button-pressed on-press warning-label] +(defn body + [timestamp-far timestamp-near on-info-button-pressed on-press warning-label] [rn/view {:flex 1} [rn/view - {:flex-direction :row - :align-items :center - :justify-content :space-between - :margin-right 2} + {:flex-direction :row + :align-items :center + :justify-content :space-between + :margin-right 2} [timestamp timestamp-far] (when on-info-button-pressed [info-button on-info-button-pressed])] @@ -131,13 +143,14 @@ :flex 1} [hborder {:type :top}] [hborder {:type :bottom}] - [rn/view (merge {:width "100%" - :background-color (get-color :background) - :flex-direction :row - :padding 20 - :padding-left 31 - :margin-vertical 4} - style) + [rn/view + (merge {:width "100%" + :background-color (get-color :background) + :flex-direction :row + :padding 20 + :padding-left 31 + :margin-vertical 4} + style) [timeline] [body timestamp-far timestamp-near on-info-button-pressed on-press warning-label]] diff --git a/src/quo2/components/messages/system_message.cljs b/src/quo2/components/messages/system_message.cljs index ef6f51f0cf..9da88cbe77 100644 --- a/src/quo2/components/messages/system_message.cljs +++ b/src/quo2/components/messages/system_message.cljs @@ -8,9 +8,10 @@ [react-native.reanimated :as reanimated] [utils.string :as utils])) -(def themes-landed {:pinned colors/primary-50-opa-5 - :added colors/primary-50-opa-5 - :deleted colors/danger-50-opa-5}) +(def themes-landed + {:pinned colors/primary-50-opa-5 + :added colors/primary-50-opa-5 + :deleted colors/danger-50-opa-5}) (def themes {:light {:text colors/neutral-100 @@ -24,115 +25,149 @@ :pressed colors/neutral-80 :landed themes-landed}}}) -(defn get-color [& keys] +(defn get-color + [& keys] (reduce (fn [acc k] (get acc k (reduced acc))) - ((theme/get-theme) themes) (vec keys))) + ((theme/get-theme) themes) + (vec keys))) -(defn sm-timestamp [timestamp-str] +(defn sm-timestamp + [timestamp-str] [rn/view {:margin-left 6} - [text/text {:size :label - :style {:color (get-color :time) - :text-transform :none}} + [text/text + {:size :label + :style {:color (get-color :time) + :text-transform :none}} timestamp-str]]) -(defn sm-icon [{:keys [icon color opacity]}] - [rn/view {:align-items :center - :margin-right 8} - [icon-avatar/icon-avatar {:size :medium - :icon icon - :color color - :opacity opacity}]]) +(defn sm-icon + [{:keys [icon color opacity]}] + [rn/view + {:align-items :center + :margin-right 8} + [icon-avatar/icon-avatar + {:size :medium + :icon icon + :color color + :opacity opacity}]]) -(defn sm-user-avatar [image] +(defn sm-user-avatar + [image] [rn/view {:margin-right 4} - [user-avatar/user-avatar {:status-indicator? false - :online? false - :size :xxxs - :profile-picture image - :ring? false}]]) + [user-avatar/user-avatar + {:status-indicator? false + :online? false + :size :xxxs + :profile-picture image + :ring? false}]]) (defmulti sm-render :type) -(defmethod sm-render :deleted [{:keys [label timestamp-str labels]}] - [rn/view {:align-items :center - :justify-content :space-between - :flex 1 - :flex-direction :row} - [rn/view {:align-items :center - :flex-direction :row} - [sm-icon {:icon :main-icons/delete - :color :danger - :opacity 5}] - [text/text {:size :paragraph-2 - :style {:color (get-color :text) - :margin-right 5}} +(defmethod sm-render :deleted + [{:keys [label timestamp-str labels]}] + [rn/view + {:align-items :center + :justify-content :space-between + :flex 1 + :flex-direction :row} + [rn/view + {:align-items :center + :flex-direction :row} + [sm-icon + {:icon :main-icons/delete + :color :danger + :opacity 5}] + [text/text + {:size :paragraph-2 + :style {:color (get-color :text) + :margin-right 5}} (or (get labels label) label (:message-deleted labels))] [sm-timestamp timestamp-str]]]) -(defmethod sm-render :added [{:keys [state mentions timestamp-str labels]}] - [rn/view {:align-items :center - :flex-direction :row} - [sm-icon {:icon :main-icons/add-user - :color :primary - :opacity (if (= state :landed) 0 5)}] +(defmethod sm-render :added + [{:keys [state mentions timestamp-str labels]}] + [rn/view + {:align-items :center + :flex-direction :row} + [sm-icon + {:icon :main-icons/add-user + :color :primary + :opacity (if (= state :landed) 0 5)}] [sm-user-avatar (:image (first mentions))] - [text/text {:weight :semi-bold - :size :paragraph-2} + [text/text + {:weight :semi-bold + :size :paragraph-2} (:name (first mentions))] - [text/text {:size :paragraph-2 - :style {:color (get-color :text) - :margin-left 3 - :margin-right 3}} + [text/text + {:size :paragraph-2 + :style {:color (get-color :text) + :margin-left 3 + :margin-right 3}} (:added labels)] [sm-user-avatar (:image (second mentions))] - [text/text {:weight :semi-bold - :size :paragraph-2} + [text/text + {:weight :semi-bold + :size :paragraph-2} (:name (second mentions))] [sm-timestamp timestamp-str]]) -(defmethod sm-render :pinned [{:keys [state pinned-by content timestamp-str labels]}] - [rn/view {:flex-direction :row - :flex 1 - :align-items :center} - [sm-icon {:icon :main-icons/pin - :color :primary - :opacity (if (= state :landed) 0 5)}] - [rn/view {:flex-direction :column - :flex 1} - [rn/view {:align-items :baseline - :flex-direction :row} - [text/text {:size :paragraph-2 - :weight :semi-bold - :style {:color (get-color :text)}} +(defmethod sm-render :pinned + [{:keys [state pinned-by content timestamp-str labels]}] + [rn/view + {:flex-direction :row + :flex 1 + :align-items :center} + [sm-icon + {:icon :main-icons/pin + :color :primary + :opacity (if (= state :landed) 0 5)}] + [rn/view + {:flex-direction :column + :flex 1} + [rn/view + {:align-items :baseline + :flex-direction :row} + [text/text + {:size :paragraph-2 + :weight :semi-bold + :style {:color (get-color :text)}} (utils/truncate-str pinned-by 18)] - [rn/view {:margin-left 4 - :margin-right 2} - [text/text {:size :paragraph-2 - :style {:color (get-color :text)}} + [rn/view + {:margin-left 4 + :margin-right 2} + [text/text + {:size :paragraph-2 + :style {:color (get-color :text)}} (:pinned-a-message labels)]] [sm-timestamp timestamp-str]] [rn/view {:flex-direction :row} - [rn/view {:flex-direction :row - :margin-right 4} + [rn/view + {:flex-direction :row + :margin-right 4} [sm-user-avatar (:image (:mentions content))] - [text/text {:weight :semi-bold - :size :label} + [text/text + {:weight :semi-bold + :size :label} (:name (:mentions content))]] (when (seq (:text content)) - [rn/view {:margin-right 20 - :flex-direction :row - :flex 1} - [text/text {:size :label - :style {:color (get-color :text)} - :number-of-lines 1 - :ellipsize-mode :tail} + [rn/view + {:margin-right 20 + :flex-direction :row + :flex 1} + [text/text + {:size :label + :style {:color (get-color :text)} + :number-of-lines 1 + :ellipsize-mode :tail} (:text content)]]) - [rn/view {:justify-content :flex-end - :flex-direction :row - :min-width 10} + [rn/view + {:justify-content :flex-end + :flex-direction :row + :min-width 10} (when (seq (:info content)) - [text/text {:size :label - :style {:color (get-color :time)}} + [text/text + {:size :label + :style {:color (get-color :time)}} (utils/truncate-str (:info content) 24)])]]]]) (defn system-message @@ -143,11 +178,16 @@ (get-color :bg (if animate-landing? :landed :default) type))] (when animate-landing? (reanimated/animate-shared-value-with-delay - sv-color (get-color :bg :default type) 0 :linear 1000)) + sv-color + (get-color :bg :default type) + 0 + :linear + 1000)) [reanimated/touchable-opacity {:on-press #(when-not non-pressable? (reanimated/set-shared-value - sv-color (get-color :bg :pressed type))) + sv-color + (get-color :bg :pressed type))) :style (reanimated/apply-animations-to-style {:background-color sv-color} (merge diff --git a/src/quo2/components/navigation/bottom_nav_tab.cljs b/src/quo2/components/navigation/bottom_nav_tab.cljs index ea7b233de2..b04bea359f 100644 --- a/src/quo2/components/navigation/bottom_nav_tab.cljs +++ b/src/quo2/components/navigation/bottom_nav_tab.cljs @@ -1,12 +1,13 @@ (ns quo2.components.navigation.bottom-nav-tab - (:require [react-native.core :as rn] - [react-native.reanimated :as reanimated] - [react-native.hole-view :as hole-view] - [quo2.foundations.colors :as colors] + (:require [quo2.components.counter.counter :as counter] [quo2.components.icons.icons :as icons] - [quo2.components.counter.counter :as counter])) + [quo2.foundations.colors :as colors] + [react-native.core :as rn] + [react-native.hole-view :as hole-view] + [react-native.reanimated :as reanimated])) -(defn toggle-background-color [background-color press-out? pass-through?] +(defn toggle-background-color + [background-color press-out? pass-through?] (reanimated/set-shared-value background-color (cond @@ -36,44 +37,47 @@ background-color (reanimated/use-shared-value "transparent") background-animated-style (reanimated/apply-animations-to-style {:background-color background-color} - {:width 90 - :height 40 - :border-radius 10})] + {:width 90 + :height 40 + :border-radius 10})] [rn/touchable-without-feedback - {:test-ID test-ID + {:test-ID test-ID :on-press on-press :on-press-in #(toggle-background-color background-color false pass-through?) :on-press-out #(toggle-background-color background-color true pass-through?) :accessibility-label accessibility-label} [reanimated/view {:style background-animated-style} - [hole-view/hole-view {:style {:padding-left 33 - :padding-top 8} - :key new-notifications? ;; Key is required to force removal of holes - :holes (cond - (not new-notifications?) ;; No new notifications, remove holes - [] + [hole-view/hole-view + {:style {:padding-left 33 + :padding-top 8} + :key new-notifications? ;; Key is required to force removal of holes + :holes (cond + (not new-notifications?) ;; No new notifications, remove holes + [] - (= notification-indicator :unread-dot) - [{:x 50 :y 5 :width 10 :height 10 :borderRadius 5}] + (= notification-indicator :unread-dot) + [{:x 50 :y 5 :width 10 :height 10 :borderRadius 5}] - :else - [{:x 47 :y 1 :width 18 :height 18 :borderRadius 7}])} + :else + [{:x 47 :y 1 :width 18 :height 18 :borderRadius 7}])} [reanimated/image - {:style icon-animated-style + {:style icon-animated-style :source (icons/icon-source (keyword (str icon 24)))}]] (when new-notifications? (if (= notification-indicator :counter) - [counter/counter {:outline false - :override-text-color colors/white - :override-bg-color colors/primary-50 - :style {:position :absolute - :left 48 - :top 2}} + [counter/counter + {:outline false + :override-text-color colors/white + :override-bg-color colors/primary-50 + :style {:position :absolute + :left 48 + :top 2}} counter-label] - [rn/view {:style {:width 8 - :height 8 - :border-radius 4 - :top 6 - :left 51 - :position :absolute - :background-color colors/primary-50}}]))]]))]) + [rn/view + {:style {:width 8 + :height 8 + :border-radius 4 + :top 6 + :left 51 + :position :absolute + :background-color colors/primary-50}}]))]]))]) diff --git a/src/quo2/components/navigation/floating_shell_button.cljs b/src/quo2/components/navigation/floating_shell_button.cljs index 00f8a3ca0f..595be89516 100644 --- a/src/quo2/components/navigation/floating_shell_button.cljs +++ b/src/quo2/components/navigation/floating_shell_button.cljs @@ -1,9 +1,10 @@ (ns quo2.components.navigation.floating-shell-button - (:require [react-native.core :as rn] - [react-native.reanimated :as reanimated] - [quo2.components.buttons.dynamic-button :as dynamic-button])) + (:require [quo2.components.buttons.dynamic-button :as dynamic-button] + [react-native.core :as rn] + [react-native.reanimated :as reanimated])) -(defn dynamic-button-view [type dynamic-buttons style] +(defn dynamic-button-view + [type dynamic-buttons style] (when-let [{:keys [count on-press customization-color label]} (get dynamic-buttons type)] [dynamic-button/dynamic-button {:type type @@ -25,23 +26,27 @@ (fn [] (let [original-style (merge {:flex-direction :row :margin-horizontal 12 - :pointer-events :box-none} style) + :pointer-events :box-none} + style) animated-style (reanimated/apply-animations-to-style (if opacity-anim - {:opacity opacity-anim} {}) + {:opacity opacity-anim} + {}) original-style)] [reanimated/view {:style animated-style} ;; Left Section [rn/view {:style {:flex 1}} - [dynamic-button-view :search dynamic-buttons {:position :absolute - :right 8}]] + [dynamic-button-view :search dynamic-buttons + {:position :absolute + :right 8}]] ;; Mid Section (jump-to) [dynamic-button-view :jump-to dynamic-buttons nil] - ;; Right Section + ;; Right Section [rn/view {:style {:flex 1}} - [rn/view {:style {:position :absolute - :flex-direction :row - :right 0}} + [rn/view + {:style {:position :absolute + :flex-direction :row + :right 0}} [dynamic-button-view :mention dynamic-buttons {:margin-left 8}] [dynamic-button-view :notification-down dynamic-buttons {:margin-left 8}] [dynamic-button-view :notification-up dynamic-buttons {:margin-left 8}] diff --git a/src/quo2/components/navigation/page_nav.cljs b/src/quo2/components/navigation/page_nav.cljs index 8de818f4a4..dcb7083930 100644 --- a/src/quo2/components/navigation/page_nav.cljs +++ b/src/quo2/components/navigation/page_nav.cljs @@ -1,11 +1,11 @@ (ns quo2.components.navigation.page-nav - (:require [react-native.core :as rn] - [quo2.foundations.colors :as colors] + (:require [clojure.string :as string] + [quo2.components.avatars.user-avatar :as user-avatar] + [quo2.components.buttons.button :as button] [quo2.components.icon :as icons] [quo2.components.markdown.text :as text] - [clojure.string :as string] - [quo2.components.buttons.button :as button] - [quo2.components.avatars.user-avatar :as user-avatar])) + [quo2.foundations.colors :as colors] + [react-native.core :as rn])) (def ^:private centrify-style {:display :flex @@ -16,7 +16,8 @@ (defn- big? [size] (= size :big)) -(defn- icon-props [color size] +(defn- icon-props + [color size] (merge {:size 20 :container-style {:width (if (big? size) 20 @@ -28,22 +29,25 @@ {:color color} {:no-color true}))) -(defn left-section-view [{:keys [on-press icon accessibility-label type] :or {type :grey}} - put-middle-section-on-left?] +(defn left-section-view + [{:keys [on-press icon accessibility-label type] :or {type :grey}} + put-middle-section-on-left?] [rn/view {:style (when put-middle-section-on-left? {:margin-right 5})} - [button/button {:on-press on-press - :icon true - :type type - :size 32 - :accessibility-label accessibility-label} + [button/button + {:on-press on-press + :icon true + :type type + :size 32 + :accessibility-label accessibility-label} icon]]) (defn- mid-section-comp [{:keys [description-img description-user-icon horizontal-description? text-secondary-color align-mid? text-color icon main-text type description]}] - [rn/view {:style (assoc centrify-style - :flex-direction :row - :margin-horizontal 2)} + [rn/view + {:style (assoc centrify-style + :flex-direction :row + :margin-horizontal 2)} (when (or (and (not horizontal-description?) align-mid? (not= :text-with-description type)) @@ -52,85 +56,98 @@ (if description-img [rn/view {:margin-right 8} [description-img]] - [rn/image {:source {:uri description-user-icon} - :style {:width 32 - :height 32 - :border-radius 32 - :margin-right 8}}])) - [rn/view {:style {:flex-direction (if horizontal-description? - :row - :column)}} - [text/text {:size :paragraph-1 - :weight :semi-bold - :style {:color text-color - :line-height 21}} + [rn/image + {:source {:uri description-user-icon} + :style {:width 32 + :height 32 + :border-radius 32 + :margin-right 8}}])) + [rn/view + {:style {:flex-direction (if horizontal-description? + :row + :column)}} + [text/text + {:size :paragraph-1 + :weight :semi-bold + :style {:color text-color + :line-height 21}} main-text] (when description - [text/text {:size :paragraph-2 - :weight :medium - :style (cond-> {:padding-right 4 - :color text-secondary-color - :line-height 18} - horizontal-description? (assoc :margin-left 4 :margin-top 2))} + [text/text + {:size :paragraph-2 + :weight :medium + :style (cond-> {:padding-right 4 + :color text-secondary-color + :line-height 18} + horizontal-description? (assoc :margin-left 4 :margin-top 2))} description])]]) (defn- mid-section-view [{:keys [horizontal-description? one-icon-align-left? type left-align? - main-text right-icon main-text-icon-color left-icon on-press avatar] :as props}] - (let [text-color (if (colors/dark?) colors/neutral-5 colors/neutral-95) + main-text right-icon main-text-icon-color left-icon on-press avatar] + :as props}] + (let [text-color (if (colors/dark?) colors/neutral-5 colors/neutral-95) text-secondary-color (if (colors/dark?) colors/neutral-40 colors/neutral-50) - component-instance [mid-section-comp (assoc props :text-secondary-color text-secondary-color)]] + component-instance [mid-section-comp (assoc props :text-secondary-color text-secondary-color)]] [rn/touchable-opacity {:on-press on-press} - [rn/view {:style (merge - (if left-align? - align-left - centrify-style) - {:flex 1 - :margin-left 4 - :text-align-vertical :center})} + [rn/view + {:style (merge + (if left-align? + align-left + centrify-style) + {:flex 1 + :margin-left 4 + :text-align-vertical :center})} (case type - :text-only [text/text {:size :paragraph-1 - :weight :semi-bold - :style {:color text-color}} - main-text] - :user-avatar [rn/view {:style (assoc centrify-style :flex-direction :row)} - [user-avatar/user-avatar avatar] - [text/text {:size :paragraph-1 + :text-only [text/text + {:size :paragraph-1 + :weight :semi-bold + :style {:color text-color}} + main-text] + :user-avatar [rn/view {:style (assoc centrify-style :flex-direction :row)} + [user-avatar/user-avatar avatar] + [text/text + {:size :paragraph-1 :weight :semi-bold :style {:padding-horizontal 4 :color text-color}} - main-text]] - :text-with-two-icons [rn/view {:style (assoc centrify-style :flex-direction :row)} - [icons/icon left-icon - (icon-props main-text-icon-color :big)] - [text/text {:size :paragraph-1 - :weight :semi-bold - :style {:padding-horizontal 4 - :color text-color}} - main-text] - [icons/icon right-icon - (icon-props main-text-icon-color :big)]] - :text-with-one-icon [rn/view {:style {:flex-direction :row}} - (if one-icon-align-left? - [rn/view {:style {:flex-direction :row - :align-items :center}} - (when horizontal-description? - [icons/icon left-icon - (icon-props main-text-icon-color :big)]) - component-instance] - [rn/view {:style {:flex-direction :row - :align-items :center}} - component-instance - (when horizontal-description? - [icons/icon left-icon - (icon-props main-text-icon-color :big)])])] + main-text]] + :text-with-two-icons [rn/view {:style (assoc centrify-style :flex-direction :row)} + [icons/icon left-icon + (icon-props main-text-icon-color :big)] + [text/text + {:size :paragraph-1 + :weight :semi-bold + :style {:padding-horizontal 4 + :color text-color}} + main-text] + [icons/icon right-icon + (icon-props main-text-icon-color :big)]] + :text-with-one-icon [rn/view {:style {:flex-direction :row}} + (if one-icon-align-left? + [rn/view + {:style {:flex-direction :row + :align-items :center}} + (when horizontal-description? + [icons/icon left-icon + (icon-props main-text-icon-color :big)]) + component-instance] + [rn/view + {:style {:flex-direction :row + :align-items :center}} + component-instance + (when horizontal-description? + [icons/icon left-icon + (icon-props main-text-icon-color :big)])])] :text-with-description component-instance)]])) -(defn- right-section-view [right-section-buttons] - [rn/view {:style (assoc centrify-style - :flex-direction :row - :flex 1 - :justify-content :flex-end)} +(defn- right-section-view + [right-section-buttons] + [rn/view + {:style (assoc centrify-style + :flex-direction :row + :flex 1 + :justify-content :flex-end)} (let [last-icon-index (-> right-section-buttons count dec)] (map-indexed (fn [index {:keys [icon on-press type] :or {type :grey}}] ^{:key index} @@ -194,27 +211,30 @@ :icon (:icon mid-section) :left-icon (:left-icon mid-section) :avatar (:avatar mid-section)}] - [rn/view {:style (cond-> {:display :flex - :flex-direction :row - ;; iPhone 11 Pro's height in Figma divided by Component height 56/1125 - :align-items :center - :padding-horizontal 20 - :height 56 - :justify-content :space-between} - page-nav-background-uri (assoc :background-color page-nav-color) - page-nav-color (assoc :background page-nav-background-uri))} - [rn/view {:style {:flex 1 - :flex-direction :row - :align-items :center}} + [rn/view + {:style (cond-> {:display :flex + :flex-direction :row + ;; iPhone 11 Pro's height in Figma divided by Component height 56/1125 + :align-items :center + :padding-horizontal 20 + :height 56 + :justify-content :space-between} + page-nav-background-uri (assoc :background-color page-nav-color) + page-nav-color (assoc :background page-nav-background-uri))} + [rn/view + {:style {:flex 1 + :flex-direction :row + :align-items :center}} [left-section-view left-section put-middle-section-on-left?] (when put-middle-section-on-left? - [mid-section-view (assoc mid-section-props - :left-align? true - :description (:description mid-section) - :description-color (:description-color mid-section) - :description-icon (:description-icon mid-section) - :align-mid? align-mid? - :description-user-icon (:description-user-icon mid-section))])] + [mid-section-view + (assoc mid-section-props + :left-align? true + :description (:description mid-section) + :description-color (:description-color mid-section) + :description-icon (:description-icon mid-section) + :align-mid? align-mid? + :description-user-icon (:description-user-icon mid-section))])] (when-not put-middle-section-on-left? [mid-section-view mid-section-props]) [right-section-view right-section-buttons]])) diff --git a/src/quo2/components/notifications/activity_log/style.cljs b/src/quo2/components/notifications/activity_log/style.cljs index 6771823a53..292eed955d 100644 --- a/src/quo2/components/notifications/activity_log/style.cljs +++ b/src/quo2/components/notifications/activity_log/style.cljs @@ -43,7 +43,8 @@ :align-items :flex-start :flex 1}) -(defn title [replying?] +(defn title + [replying?] {:color colors/white :flex-shrink 1 :max-width (when-not replying? "60%")}) diff --git a/src/quo2/components/notifications/activity_log/view.cljs b/src/quo2/components/notifications/activity_log/view.cljs index 02fcd151c1..7a774ab8d2 100644 --- a/src/quo2/components/notifications/activity_log/view.cljs +++ b/src/quo2/components/notifications/activity_log/view.cljs @@ -4,9 +4,9 @@ [quo2.components.buttons.button :as button] [quo2.components.icon :as icon] [quo2.components.markdown.text :as text] + [quo2.components.notifications.activity-log.style :as style] [quo2.components.tags.status-tags :as status-tags] [quo2.foundations.colors :as colors] - [quo2.components.notifications.activity-log.style :as style] [react-native.core :as rn] [reagent.core :as reagent] [status-im.i18n.i18n :as i18n])) @@ -21,17 +21,20 @@ (defn- activity-reply-text-input [reply-input on-update-reply] [rn/view - [rn/view {:style {:margin-top 16 - :margin-bottom 8 - :flex-direction :row}} - [text/text {:weight :medium - :style {:flex-grow 1 - :color colors/neutral-40}} + [rn/view + {:style {:margin-top 16 + :margin-bottom 8 + :flex-direction :row}} + [text/text + {:weight :medium + :style {:flex-grow 1 + :color colors/neutral-40}} (i18n/label :t/your-answer)] - [text/text {:style {:flex-shrink 1 - :color (if (valid-reply? @reply-input) - colors/neutral-40 - colors/danger-60)}} + [text/text + {:style {:flex-shrink 1 + :color (if (valid-reply? @reply-input) + colors/neutral-40 + colors/danger-60)}} (str (count @reply-input) "/" max-reply-length)]] [rn/view ;; TODO(@ilmotta): Replace with quo2 component when available. @@ -58,33 +61,39 @@ (let [first-line-offset (if replying? 4 0) gap-between-lines 4] (into [rn/view {:style (assoc style/context-container :margin-top first-line-offset)}] - (mapcat (fn [detail] - ^{:key (hash detail)} - (if (string? detail) - (map (fn [s] - [rn/view {:style {:margin-right 4 - :margin-top 0}} - [text/text {:size :paragraph-2 - :style {:color colors/white}} - s]]) - (string/split detail #"\s+")) - [[rn/view {:margin-right 4 - :margin-top gap-between-lines} - detail]])) - context)))) + (mapcat + (fn [detail] + ^{:key (hash detail)} + (if (string? detail) + (map (fn [s] + [rn/view + {:style {:margin-right 4 + :margin-top 0}} + [text/text + {:size :paragraph-2 + :style {:color colors/white}} + s]]) + (string/split detail #"\s+")) + [[rn/view + {:margin-right 4 + :margin-top gap-between-lines} + detail]])) + context)))) (defn- activity-message [{:keys [title body]}] [rn/view {:style style/message-container} (when title - [text/text {:size :paragraph-2 - :accessibility-label :activity-message-title - :style style/message-title} + [text/text + {:size :paragraph-2 + :accessibility-label :activity-message-title + :style style/message-title} title]) (if (string? body) - [text/text {:style style/message-body - :accessibility-label :activity-message-body - :size :paragraph-1} + [text/text + {:style style/message-body + :accessibility-label :activity-message-body + :size :paragraph-1} body] body)]) @@ -97,44 +106,51 @@ :flex-basis 0})] [rn/view style/buttons-container (when button-1 - [button/button (-> button-1 - (assoc :size size) - (update :style merge common-style {:margin-right 8})) + [button/button + (-> button-1 + (assoc :size size) + (update :style merge common-style {:margin-right 8})) (:label button-1)]) (when button-2 - [button/button (-> button-2 - (assoc :size size) - (assoc :disabled (and replying? (not (valid-reply? @reply-input)))) - (update :style merge common-style)) + [button/button + (-> button-2 + (assoc :size size) + (assoc :disabled (and replying? (not (valid-reply? @reply-input)))) + (update :style merge common-style)) (:label button-2)])])) (defn- activity-status [status] - [rn/view {:style style/status - :accessibility-label :activity-status} - [status-tags/status-tag {:size :small - :label (:label status) - :status status}]]) + [rn/view + {:style style/status + :accessibility-label :activity-status} + [status-tags/status-tag + {:size :small + :label (:label status) + :status status}]]) (defn- activity-title [title replying?] - [text/text {:weight :semi-bold - :accessibility-label :activity-title - :style (style/title replying?) - :size (if replying? :heading-2 :paragraph-1)} + [text/text + {:weight :semi-bold + :accessibility-label :activity-title + :style (style/title replying?) + :size (if replying? :heading-2 :paragraph-1)} title]) (defn- activity-timestamp [timestamp] - [text/text {:size :label - :accessibility-label :activity-timestamp - :style style/timestamp} + [text/text + {:size :label + :accessibility-label :activity-timestamp + :style style/timestamp} timestamp]) (defn- activity-unread-dot [] - [rn/view {:accessibility-label :activity-unread-indicator - :style style/unread-dot-container} + [rn/view + {:accessibility-label :activity-unread-indicator + :style style/unread-dot-container} [rn/view {:style style/unread-dot}]]) (defn- footer @@ -159,12 +175,14 @@ replying? unread?] :as props}] - [rn/view {:accessibility-label :activity - :style style/container} + [rn/view + {:accessibility-label :activity + :style style/container} (when-not replying? [activity-icon icon]) - [rn/view {:style {:padding-left (when-not replying? 8) - :flex 1}} + [rn/view + {:style {:padding-left (when-not replying? 8) + :flex 1}} [rn/view [rn/view {:style style/top-section-container} [activity-title title replying?] diff --git a/src/quo2/components/notifications/count_down_circle.cljs b/src/quo2/components/notifications/count_down_circle.cljs index 2512e9c110..9d4dbfc60a 100644 --- a/src/quo2/components/notifications/count_down_circle.cljs +++ b/src/quo2/components/notifications/count_down_circle.cljs @@ -1,11 +1,10 @@ (ns quo2.components.notifications.count-down-circle - (:require - [goog.string :as gstring] - [quo2.foundations.colors :as colors] - [quo2.theme :as theme] - [react-native.core :as rn] - [react-native.svg :as svg] - [reagent.core :as reagent])) + (:require [goog.string :as gstring] + [quo2.foundations.colors :as colors] + [quo2.theme :as theme] + [react-native.core :as rn] + [react-native.svg :as svg] + [reagent.core :as reagent])) (defn- get-path-props [size stroke-width rotation] diff --git a/src/quo2/components/notifications/info_count.cljs b/src/quo2/components/notifications/info_count.cljs index e0a3a1ca43..8f9851838e 100644 --- a/src/quo2/components/notifications/info_count.cljs +++ b/src/quo2/components/notifications/info_count.cljs @@ -1,17 +1,21 @@ (ns quo2.components.notifications.info-count - (:require [react-native.core :as rn] - [quo2.foundations.colors :as colors] - [quo2.foundations.typography :as typography])) + (:require [quo2.foundations.colors :as colors] + [quo2.foundations.typography :as typography] + [react-native.core :as rn])) -(defn info-count [count style] +(defn info-count + [count style] (when (> count 0) - [rn/view {:style (merge {:width 16 - :height 16 - :position :absolute - :right 22 - :border-radius 6 - :justify-content :center - :align-items :center - :background-color (colors/theme-colors colors/primary-50 colors/primary-60)} - style)} - [rn/text {:style (merge typography/font-medium typography/label {:color colors/white :text-align :center})} count]])) + [rn/view + {:style (merge {:width 16 + :height 16 + :position :absolute + :right 22 + :border-radius 6 + :justify-content :center + :align-items :center + :background-color (colors/theme-colors colors/primary-50 colors/primary-60)} + style)} + [rn/text + {:style (merge typography/font-medium typography/label {:color colors/white :text-align :center})} + count]])) diff --git a/src/quo2/components/notifications/notification_dot.cljs b/src/quo2/components/notifications/notification_dot.cljs index eb652f3ff8..9436f24579 100644 --- a/src/quo2/components/notifications/notification_dot.cljs +++ b/src/quo2/components/notifications/notification_dot.cljs @@ -1,12 +1,14 @@ (ns quo2.components.notifications.notification-dot - (:require [react-native.core :as rn] - [quo2.foundations.colors :as colors])) + (:require [quo2.foundations.colors :as colors] + [react-native.core :as rn])) -(defn notification-dot [style] - [rn/view {:style (merge {:background-color (colors/theme-colors colors/primary-50 colors/primary-60) - :width 8 - :height 8 - :border-radius 4 - :position :absolute - :z-index 1} - style)}]) +(defn notification-dot + [style] + [rn/view + {:style (merge {:background-color (colors/theme-colors colors/primary-50 colors/primary-60) + :width 8 + :height 8 + :border-radius 4 + :position :absolute + :z-index 1} + style)}]) diff --git a/src/quo2/components/notifications/toast.cljs b/src/quo2/components/notifications/toast.cljs index 45ba42d823..a58e24203e 100644 --- a/src/quo2/components/notifications/toast.cljs +++ b/src/quo2/components/notifications/toast.cljs @@ -1,12 +1,11 @@ (ns quo2.components.notifications.toast - (:require - [i18n.i18n :as i18n] - [quo2.components.icon :as icon] - [quo2.components.markdown.text :as text] - [quo2.components.notifications.count-down-circle :as count-down-circle] - [quo2.foundations.colors :as colors] - [quo2.theme :as theme] - [react-native.core :as rn])) + (:require [i18n.i18n :as i18n] + [quo2.components.icon :as icon] + [quo2.components.markdown.text :as text] + [quo2.components.notifications.count-down-circle :as count-down-circle] + [quo2.foundations.colors :as colors] + [quo2.theme :as theme] + [react-native.core :as rn])) (def ^:private themes {:container {:light {:background-color colors/white-opa-70} diff --git a/src/quo2/components/reactions/reaction.cljs b/src/quo2/components/reactions/reaction.cljs index 18f4f259d2..890492631a 100644 --- a/src/quo2/components/reactions/reaction.cljs +++ b/src/quo2/components/reactions/reaction.cljs @@ -1,33 +1,33 @@ (ns quo2.components.reactions.reaction - (:require [quo2.components.markdown.text :as quo2.text] - [react-native.core :as rn] - [quo2.theme :as theme] + (:require [quo2.components.icon :as icons] + [quo2.components.markdown.text :as quo2.text] [quo2.foundations.colors :as colors] - [quo2.components.icon :as icons])) + [quo2.theme :as theme] + [react-native.core :as rn])) (def reaction-styling - {:flex-direction :row - :justify-content :center - :align-items :center + {:flex-direction :row + :justify-content :center + :align-items :center :padding-horizontal 8 - :border-radius 8 - :height 24}) + :border-radius 8 + :height 24}) (defn open-reactions-menu [{:keys [on-press]}] (let [dark? (theme/dark?)] [rn/touchable-opacity - {:on-press on-press + {:on-press on-press :accessibility-label :emoji-reaction-add - :style (merge reaction-styling - {:padding-horizontal 9 - :border-width 1 - :margin-top 5 - :border-color (if dark? - colors/neutral-70 - colors/neutral-30)})} + :style (merge reaction-styling + {:padding-horizontal 9 + :border-width 1 + :margin-top 5 + :border-color (if dark? + colors/neutral-70 + colors/neutral-30)})} [icons/icon :i/add - {:size 20 + {:size 20 :color (if dark? colors/white colors/neutral-100)}]])) @@ -35,37 +35,41 @@ (defn reaction "Add your emoji as a param here" [{:keys [emoji clicks neutral? on-press accessibility-label]}] - (let [dark? (theme/dark?) - text-color (if dark? colors/white - colors/neutral-100) - numeric-value (int clicks) + (let [dark? (theme/dark?) + text-color (if dark? + colors/white + colors/neutral-100) + numeric-value (int clicks) clicks-positive? (pos? numeric-value)] [rn/touchable-opacity {:on-press on-press :accessibility-label accessibility-label :style (merge reaction-styling - (cond-> {:background-color - (if dark? - (if neutral? - colors/neutral-70 - :transparent) - (if neutral? - colors/neutral-30 - :transparent))} + (cond-> + {:background-color + (if dark? + (if neutral? + colors/neutral-70 + :transparent) + (if neutral? + colors/neutral-30 + :transparent))} (and dark? (not neutral?)) (assoc :border-color colors/neutral-70 :border-width 1) (and (not dark?) (not neutral?)) (assoc :border-color colors/neutral-30 :border-width 1)))} - [icons/icon emoji {:no-color true - :size 16}] - [quo2.text/text {:size :paragraph-2 - :weight :semi-bold - :color text-color - :flex-direction :row - :align-items :center - :justify-content :center} + [icons/icon emoji + {:no-color true + :size 16}] + [quo2.text/text + {:size :paragraph-2 + :weight :semi-bold + :color text-color + :flex-direction :row + :align-items :center + :justify-content :center} (if clicks-positive? (str " " numeric-value) "")]])) diff --git a/src/quo2/components/record_audio/record_audio/style.cljs b/src/quo2/components/record_audio/record_audio/style.cljs index 14c6e1ac21..a8c8664794 100644 --- a/src/quo2/components/record_audio/record_audio/style.cljs +++ b/src/quo2/components/record_audio/record_audio/style.cljs @@ -2,7 +2,8 @@ (:require [quo2.foundations.colors :as colors] [react-native.reanimated :as reanimated])) -(defn animated-circle [scale opacity color] +(defn animated-circle + [scale opacity color] (reanimated/apply-animations-to-style {:transform [{:scale scale}] :opacity opacity} @@ -16,11 +17,12 @@ :align-items :center :z-index 0})) -(defn record-button-big-container [translate-x translate-y opacity] +(defn record-button-big-container + [translate-x translate-y opacity] (reanimated/apply-animations-to-style {:transform [{:translateY translate-y} {:translateX translate-x}] - :opacity opacity} + :opacity opacity} {:position :absolute :bottom 0 :right 0 @@ -30,7 +32,8 @@ :justify-content :center :z-index 0})) -(defn record-button-big-body [button-color] +(defn record-button-big-body + [button-color] {:width 56 :height 56 :border-radius 28 @@ -39,7 +42,8 @@ :background-color button-color :overflow :hidden}) -(defn record-button-big-red-overlay [red-overlay-opacity] +(defn record-button-big-red-overlay + [red-overlay-opacity] (reanimated/apply-animations-to-style {:opacity red-overlay-opacity} {:position :absolute @@ -49,7 +53,8 @@ :bottom 0 :background-color colors/danger-50})) -(defn record-button-big-gray-overlay [gray-overlay-opacity] +(defn record-button-big-gray-overlay + [gray-overlay-opacity] (reanimated/apply-animations-to-style {:opacity gray-overlay-opacity} {:position :absolute @@ -59,7 +64,8 @@ :bottom 0 :background-color (colors/theme-colors colors/neutral-80-opa-5-opaque colors/neutral-80)})) -(defn record-button-big-icon-container [icon-opacity] +(defn record-button-big-icon-container + [icon-opacity] (reanimated/apply-animations-to-style {:opacity icon-opacity} {})) @@ -70,7 +76,8 @@ :border-radius 4 :background-color colors/white}) -(defn send-button-container [opacity] +(defn send-button-container + [opacity] (reanimated/apply-animations-to-style {:opacity opacity} {:justify-content :center @@ -81,7 +88,8 @@ :top 0 :right 20})) -(defn send-button-connector [opacity width height border-radius-first-half border-radius-second-half] +(defn send-button-connector + [opacity width height border-radius-first-half border-radius-second-half] (reanimated/apply-animations-to-style {:opacity opacity :width width @@ -96,7 +104,8 @@ :background-color colors/primary-50 :z-index 0})) -(defn send-button [translate-y opacity] +(defn send-button + [translate-y opacity] (reanimated/apply-animations-to-style {:transform [{:translateY translate-y}] :opacity opacity} @@ -114,7 +123,8 @@ (def send-icon-container {:z-index 10}) -(defn lock-button-container [opacity] +(defn lock-button-container + [opacity] (reanimated/apply-animations-to-style {:opacity opacity} {:transform [{:rotate "45deg"}] @@ -126,7 +136,8 @@ :top 20 :left 20})) -(defn lock-button-connector [opacity width height border-radius-first-half border-radius-second-half] +(defn lock-button-connector + [opacity width height border-radius-first-half border-radius-second-half] (reanimated/apply-animations-to-style {:opacity opacity :width width @@ -141,7 +152,8 @@ :background-color (colors/theme-colors colors/neutral-80-opa-5-opaque colors/neutral-80) :overflow :hidden})) -(defn lock-button [translate-x-y opacity] +(defn lock-button + [translate-x-y opacity] (reanimated/apply-animations-to-style {:transform [{:translateX translate-x-y} {:translateY translate-x-y}] @@ -158,7 +170,8 @@ :overflow :hidden :z-index 12})) -(defn delete-button-container [opacity] +(defn delete-button-container + [opacity] (reanimated/apply-animations-to-style {:opacity opacity} {:justify-content :center @@ -169,7 +182,8 @@ :bottom 20 :left 0})) -(defn delete-button-connector [opacity width height border-radius-first-half border-radius-second-half] +(defn delete-button-connector + [opacity width height border-radius-first-half border-radius-second-half] (reanimated/apply-animations-to-style {:opacity opacity :width width @@ -184,7 +198,8 @@ :background-color colors/danger-50 :z-index 0})) -(defn delete-button [translate-x opacity] +(defn delete-button + [translate-x opacity] (reanimated/apply-animations-to-style {:transform [{:translateX translate-x}] :opacity opacity} @@ -199,9 +214,10 @@ :left 0 :z-index 11})) -(defn record-button-container [opacity] +(defn record-button-container + [opacity] (reanimated/apply-animations-to-style - {:opacity opacity} + {:opacity opacity} {:margin-bottom 32 :margin-right 32})) diff --git a/src/quo2/components/record_audio/record_audio/view.cljs b/src/quo2/components/record_audio/record_audio/view.cljs index a85bd0a4a5..e34deb8547 100644 --- a/src/quo2/components/record_audio/record_audio/view.cljs +++ b/src/quo2/components/record_audio/record_audio/view.cljs @@ -1,15 +1,15 @@ (ns quo2.components.record-audio.record-audio.view - (:require [quo.react :refer [memo effect!]] - [react-native.core :as rn] - [quo2.foundations.colors :as colors] - [quo2.components.icon :as icons] + (:require [cljs-bean.core :as bean] + [oops.core :as oops] + [quo.react :refer [effect! memo]] [quo2.components.buttons.button :as button] + [quo2.components.icon :as icons] + [quo2.components.record-audio.record-audio.style :as style] + [quo2.foundations.colors :as colors] + [react-native.core :as rn] [react-native.reanimated :as reanimated] [reagent.core :as reagent] - [cljs-bean.core :as bean] - [quo2.components.record-audio.record-audio.style :as style] - [status-im.utils.utils :as utils] - [oops.core :as oops])) + [status-im.utils.utils :as utils])) (def ^:private scale-to-each 1.8) (def ^:private scale-to-total 2.6) @@ -39,26 +39,30 @@ :x 68 :y 68}) -(defn- delete-button-area [active?] +(defn- delete-button-area + [active?] {:width (if active? 72 82) :height 56 :x (if active? -16 -32) :y (if active? 64 70)}) -(defn- lock-button-area [active?] +(defn- lock-button-area + [active?] {:width (if active? 72 100) :height (if active? 72 102) :x -32 :y -32}) -(defn- send-button-area [active?] +(defn- send-button-area + [active?] {:width 56 :height (if active? 72 92) :x 68 :y (if active? -16 -32)}) -(defn- touch-inside-area? [{:keys [location-x location-y ignore-min-y? ignore-max-y? ignore-min-x? ignore-max-x?]} - {:keys [width height x y]}] +(defn- touch-inside-area? + [{:keys [location-x location-y ignore-min-y? ignore-max-y? ignore-min-x? ignore-max-x?]} + {:keys [width height x y]}] (let [max-x (+ x width) max-y (+ y height)] (and @@ -71,12 +75,16 @@ (def record-audio-worklets (js/require "../src/js/record_audio_worklets.js")) -(defn- ring-scale [scale substract] +(defn- ring-scale + [scale substract] (.ringScale ^js record-audio-worklets scale substract)) -(defn- record-button-big [recording? ready-to-send? ready-to-lock? ready-to-delete? record-button-is-animating? record-button-at-initial-position? locked? reviewing-audio? recording-timer recording-length-ms clear-timeout touch-active?] +(defn- record-button-big + [recording? ready-to-send? ready-to-lock? ready-to-delete? record-button-is-animating? + record-button-at-initial-position? locked? reviewing-audio? recording-timer recording-length-ms + clear-timeout touch-active?] [:f> (fn [] (let [scale (reanimated/use-shared-value 1) @@ -85,13 +93,16 @@ animations (map (fn [index] (let [ring-scale (ring-scale scale (* scale-padding index))] - {:scale ring-scale - :opacity (reanimated/interpolate ring-scale [1 scale-to-each] [opacity-from 0])})) + {:scale ring-scale + :opacity (reanimated/interpolate ring-scale + [1 scale-to-each] + [opacity-from 0])})) (range 0 5)) rings-color (cond - @ready-to-lock? (colors/theme-colors colors/neutral-80-opa-5-opaque colors/neutral-80) + @ready-to-lock? (colors/theme-colors colors/neutral-80-opa-5-opaque + colors/neutral-80) @ready-to-delete? colors/danger-50 - :else colors/primary-50) + :else colors/primary-50) translate-y (reanimated/use-shared-value 0) translate-x (reanimated/use-shared-value 0) button-color colors/primary-50 @@ -115,12 +126,25 @@ (reset! recording-length-ms 0)))) start-animation (fn [] (reanimated/set-shared-value opacity 1) - (reanimated/animate-shared-value-with-timing scale 2.6 signal-anim-duration :linear) - ;; TODO: Research if we can implement this with withSequence method from Reanimated 2 - ;; GitHub issue [#14561]: https://github.com/status-im/status-mobile/issues/14561 - (reset! clear-timeout (utils/set-timeout #(do (reanimated/set-shared-value scale scale-to-each) - (reanimated/animate-shared-value-with-delay-repeat scale scale-to-total signal-anim-duration-2 :linear 0 -1)) - signal-anim-duration))) + (reanimated/animate-shared-value-with-timing scale + 2.6 + signal-anim-duration + :linear) + ;; TODO: Research if we can implement this with withSequence method + ;; from Reanimated 2 + ;; GitHub issue [#14561]: + ;; https://github.com/status-im/status-mobile/issues/14561 + (reset! clear-timeout + (utils/set-timeout + #(do (reanimated/set-shared-value scale scale-to-each) + (reanimated/animate-shared-value-with-delay-repeat + scale + scale-to-total + signal-anim-duration-2 + :linear + 0 + -1)) + signal-anim-duration))) stop-animation (fn [] (reanimated/set-shared-value opacity 0) (reanimated/cancel-animation scale) @@ -129,48 +153,105 @@ start-y-animation (fn [] (reset! record-button-at-initial-position? false) (reset! record-button-is-animating? true) - (reanimated/animate-shared-value-with-timing translate-y -64 250 :easing1) - (reanimated/animate-shared-value-with-delay icon-opacity 0 33.33 :linear 76.66) + (reanimated/animate-shared-value-with-timing translate-y + -64 + 250 + :easing1) + (reanimated/animate-shared-value-with-delay icon-opacity + 0 33.33 + :linear 76.66) (utils/set-timeout (fn [] (reset! record-button-is-animating? false) (when-not @touch-active? (complete-animation))) 250)) reset-y-animation (fn [] - (reanimated/animate-shared-value-with-timing translate-y 0 300 :easing1) - (reanimated/animate-shared-value-with-timing icon-opacity 1 500 :linear) - (utils/set-timeout (fn [] (reset! record-button-at-initial-position? true)) 500)) + (reanimated/animate-shared-value-with-timing translate-y + 0 + 300 + :easing1) + (reanimated/animate-shared-value-with-timing icon-opacity + 1 + 500 + :linear) + (utils/set-timeout (fn [] + (reset! record-button-at-initial-position? true)) + 500)) start-x-animation (fn [] (reset! record-button-at-initial-position? false) (reset! record-button-is-animating? true) - (reanimated/animate-shared-value-with-timing translate-x -64 250 :easing1) - (reanimated/animate-shared-value-with-delay icon-opacity 0 33.33 :linear 76.66) - (reanimated/animate-shared-value-with-timing red-overlay-opacity 1 33.33 :linear) + (reanimated/animate-shared-value-with-timing translate-x + -64 + 250 + :easing1) + (reanimated/animate-shared-value-with-delay icon-opacity + 0 33.33 + :linear 76.66) + (reanimated/animate-shared-value-with-timing red-overlay-opacity + 1 + 33.33 + :linear) (utils/set-timeout (fn [] (reset! record-button-is-animating? false) (when-not @touch-active? (complete-animation))) 250)) reset-x-animation (fn [] - (reanimated/animate-shared-value-with-timing translate-x 0 300 :easing1) - (reanimated/animate-shared-value-with-timing icon-opacity 1 500 :linear) - (reanimated/animate-shared-value-with-timing red-overlay-opacity 0 100 :linear) - (utils/set-timeout (fn [] (reset! record-button-at-initial-position? true)) 500)) + (reanimated/animate-shared-value-with-timing translate-x + 0 + 300 + :easing1) + (reanimated/animate-shared-value-with-timing icon-opacity + 1 + 500 + :linear) + (reanimated/animate-shared-value-with-timing red-overlay-opacity + 0 + 100 + :linear) + (utils/set-timeout (fn [] + (reset! record-button-at-initial-position? true)) + 500)) start-x-y-animation (fn [] (reset! record-button-at-initial-position? false) (reset! record-button-is-animating? true) - (reanimated/animate-shared-value-with-timing translate-y -44 200 :easing1) - (reanimated/animate-shared-value-with-timing translate-x -44 200 :easing1) - (reanimated/animate-shared-value-with-delay icon-opacity 0 33.33 :linear 33.33) - (reanimated/animate-shared-value-with-timing gray-overlay-opacity 1 33.33 :linear) + (reanimated/animate-shared-value-with-timing translate-y + -44 + 200 + :easing1) + (reanimated/animate-shared-value-with-timing translate-x + -44 + 200 + :easing1) + (reanimated/animate-shared-value-with-delay icon-opacity + 0 33.33 + :linear 33.33) + (reanimated/animate-shared-value-with-timing gray-overlay-opacity + 1 + 33.33 + :linear) (utils/set-timeout (fn [] (reset! record-button-is-animating? false) (when-not @touch-active? (complete-animation))) 200)) reset-x-y-animation (fn [] - (reanimated/animate-shared-value-with-timing translate-y 0 300 :easing1) - (reanimated/animate-shared-value-with-timing translate-x 0 300 :easing1) - (reanimated/animate-shared-value-with-timing icon-opacity 1 500 :linear) - (reanimated/animate-shared-value-with-timing gray-overlay-opacity 0 800 :linear) - (utils/set-timeout (fn [] (reset! record-button-at-initial-position? true)) 800))] + (reanimated/animate-shared-value-with-timing translate-y + 0 + 300 + :easing1) + (reanimated/animate-shared-value-with-timing translate-x + 0 + 300 + :easing1) + (reanimated/animate-shared-value-with-timing icon-opacity + 1 + 500 + :linear) + (reanimated/animate-shared-value-with-timing gray-overlay-opacity + 0 + 800 + :linear) + (utils/set-timeout (fn [] + (reset! record-button-at-initial-position? true)) + 800))] (effect! #(cond @recording? (start-animation) @@ -189,15 +270,17 @@ (start-x-animation) (reset-x-animation)) [@ready-to-delete?]) - [reanimated/view {:style (style/record-button-big-container translate-x translate-y opacity) - :pointer-events :none} + [reanimated/view + {:style (style/record-button-big-container translate-x translate-y opacity) + :pointer-events :none} [:<> (map-indexed (fn [id animation] ^{:key id} - [animated-ring {:scale (:scale animation) - :opacity (:opacity animation) - :color rings-color}]) + [animated-ring + {:scale (:scale animation) + :opacity (:opacity animation) + :color rings-color}]) animations)] [rn/view {:style (style/record-button-big-body button-color)} [reanimated/view {:style (style/record-button-big-red-overlay red-overlay-opacity)}] @@ -207,7 +290,8 @@ [rn/view {:style style/stop-icon}] [icons/icon :i/audio {:color icon-color}])]]]))]) -(defn- send-button [recording? ready-to-send? reviewing-audio?] +(defn- send-button + [recording? ready-to-send? reviewing-audio?] [:f> (fn [] (let [opacity (reanimated/use-shared-value 0) @@ -218,33 +302,70 @@ border-radius-first-half (reanimated/use-shared-value 16) border-radius-second-half (reanimated/use-shared-value 8) start-y-animation (fn [] - (reanimated/animate-shared-value-with-delay translate-y 12 50 :linear 133.33) - (reanimated/animate-shared-value-with-delay connector-opacity 1 0 :easing1 93.33) - (reanimated/animate-shared-value-with-delay width 56 83.33 :easing1 80) - (reanimated/animate-shared-value-with-delay height 56 83.33 :easing1 80) - (reanimated/animate-shared-value-with-delay border-radius-first-half 28 83.33 :easing1 80) - (reanimated/animate-shared-value-with-delay border-radius-second-half 28 83.33 :easing1 80)) + (reanimated/animate-shared-value-with-delay translate-y + 12 50 + :linear 133.33) + (reanimated/animate-shared-value-with-delay connector-opacity + 1 0 + :easing1 93.33) + (reanimated/animate-shared-value-with-delay width + 56 83.33 + :easing1 80) + (reanimated/animate-shared-value-with-delay height + 56 83.33 + :easing1 80) + (reanimated/animate-shared-value-with-delay + border-radius-first-half + 28 83.33 + :easing1 80) + (reanimated/animate-shared-value-with-delay + border-radius-second-half + 28 83.33 + :easing1 80)) reset-y-animation (fn [] - (reanimated/animate-shared-value-with-timing translate-y 0 100 :linear) + (reanimated/animate-shared-value-with-timing translate-y + 0 + 100 + :linear) (reanimated/set-shared-value connector-opacity 0) (reanimated/set-shared-value width 12) (reanimated/set-shared-value height 24) (reanimated/set-shared-value border-radius-first-half 16) (reanimated/set-shared-value border-radius-second-half 8)) fade-in-animation (fn [] - (reanimated/animate-shared-value-with-timing translate-y 0 200 :linear) - (reanimated/animate-shared-value-with-timing opacity 1 200 :linear)) + (reanimated/animate-shared-value-with-timing translate-y + 0 + 200 + :linear) + (reanimated/animate-shared-value-with-timing opacity + 1 + 200 + :linear)) fade-out-animation (fn [] - (reanimated/animate-shared-value-with-timing translate-y (if @reviewing-audio? 76 20) 200 :linear) - (when-not @reviewing-audio? (reanimated/animate-shared-value-with-timing opacity 0 200 :linear)) + (reanimated/animate-shared-value-with-timing translate-y + (if @reviewing-audio? + 76 + 20) + 200 + :linear) + (when-not @reviewing-audio? + (reanimated/animate-shared-value-with-timing opacity + 0 + 200 + :linear)) (reanimated/set-shared-value connector-opacity 0) (reanimated/set-shared-value width 24) (reanimated/set-shared-value height 12) (reanimated/set-shared-value border-radius-first-half 8) (reanimated/set-shared-value border-radius-second-half 16)) fade-out-reset-animation (fn [] - (reanimated/animate-shared-value-with-timing opacity 0 200 :linear) - (reanimated/animate-shared-value-with-delay translate-y 20 0 :linear 200) + (reanimated/animate-shared-value-with-timing opacity + 0 + 200 + :linear) + (reanimated/animate-shared-value-with-delay translate-y + 20 0 + :linear 200) (reanimated/set-shared-value connector-opacity 0) (reanimated/set-shared-value width 24) (reanimated/set-shared-value height 12) @@ -260,18 +381,26 @@ (effect! #(cond @ready-to-send? (start-y-animation) - @recording? (reset-y-animation)) + @recording? (reset-y-animation)) [@ready-to-send?]) [:<> [reanimated/view {:style (style/send-button-container opacity)} - [reanimated/view {:style (style/send-button-connector connector-opacity width height border-radius-first-half border-radius-second-half)}]] - [reanimated/view {:style (style/send-button translate-y opacity) - :pointer-events :none} - [icons/icon :i/arrow-up {:color colors/white - :size 20 - :container-style style/send-icon-container}]]]))]) + [reanimated/view + {:style (style/send-button-connector connector-opacity + width + height + border-radius-first-half + border-radius-second-half)}]] + [reanimated/view + {:style (style/send-button translate-y opacity) + :pointer-events :none} + [icons/icon :i/arrow-up + {:color colors/white + :size 20 + :container-style style/send-icon-container}]]]))]) -(defn- lock-button [recording? ready-to-lock? locked?] +(defn- lock-button + [recording? ready-to-lock? locked?] [:f> (fn [] (let [translate-x-y (reanimated/use-shared-value 20) @@ -282,25 +411,54 @@ border-radius-first-half (reanimated/use-shared-value 8) border-radius-second-half (reanimated/use-shared-value 8) start-x-y-animation (fn [] - (reanimated/animate-shared-value-with-delay translate-x-y 8 50 :linear 116.66) - (reanimated/animate-shared-value-with-delay connector-opacity 1 0 :easing1 80) - (reanimated/animate-shared-value-with-delay width 56 83.33 :easing1 63.33) - (reanimated/animate-shared-value-with-delay height 56 83.33 :easing1 63.33) - (reanimated/animate-shared-value-with-delay border-radius-first-half 28 83.33 :easing1 63.33) - (reanimated/animate-shared-value-with-delay border-radius-second-half 28 83.33 :easing1 63.33)) + (reanimated/animate-shared-value-with-delay translate-x-y + 8 50 + :linear 116.66) + (reanimated/animate-shared-value-with-delay connector-opacity + 1 0 + :easing1 80) + (reanimated/animate-shared-value-with-delay width + 56 83.33 + :easing1 63.33) + (reanimated/animate-shared-value-with-delay height + 56 83.33 + :easing1 63.33) + (reanimated/animate-shared-value-with-delay + border-radius-first-half + 28 83.33 + :easing1 63.33) + (reanimated/animate-shared-value-with-delay + border-radius-second-half + 28 83.33 + :easing1 63.33)) reset-x-y-animation (fn [] - (reanimated/animate-shared-value-with-timing translate-x-y 0 100 :linear) + (reanimated/animate-shared-value-with-timing translate-x-y + 0 + 100 + :linear) (reanimated/set-shared-value connector-opacity 0) (reanimated/set-shared-value width 24) (reanimated/set-shared-value height 12) (reanimated/set-shared-value border-radius-first-half 8) (reanimated/set-shared-value border-radius-second-half 16)) fade-in-animation (fn [] - (reanimated/animate-shared-value-with-timing translate-x-y 0 220 :linear) - (reanimated/animate-shared-value-with-timing opacity 1 220 :linear)) + (reanimated/animate-shared-value-with-timing translate-x-y + 0 + 220 + :linear) + (reanimated/animate-shared-value-with-timing opacity + 1 + 220 + :linear)) fade-out-animation (fn [] - (reanimated/animate-shared-value-with-timing translate-x-y 20 200 :linear) - (reanimated/animate-shared-value-with-timing opacity 0 200 :linear) + (reanimated/animate-shared-value-with-timing translate-x-y + 20 + 200 + :linear) + (reanimated/animate-shared-value-with-timing opacity + 0 + 200 + :linear) (reanimated/set-shared-value connector-opacity 0) (reanimated/set-shared-value width 24) (reanimated/set-shared-value height 12) @@ -322,14 +480,21 @@ [@locked?]) [:<> [reanimated/view {:style (style/lock-button-container opacity)} - [reanimated/view {:style (style/lock-button-connector connector-opacity width height border-radius-first-half border-radius-second-half)}]] - [reanimated/view {:style (style/lock-button translate-x-y opacity) - :pointer-events :none} + [reanimated/view + {:style (style/lock-button-connector connector-opacity + width + height + border-radius-first-half + border-radius-second-half)}]] + [reanimated/view + {:style (style/lock-button translate-x-y opacity) + :pointer-events :none} [icons/icon (if @ready-to-lock? :i/locked :i/unlocked) {:color (colors/theme-colors colors/black colors/white) :size 20}]]]))]) -(defn- delete-button [recording? ready-to-delete?] +(defn- delete-button + [recording? ready-to-delete?] [:f> (fn [] (let [opacity (reanimated/use-shared-value 0) @@ -340,25 +505,54 @@ border-radius-first-half (reanimated/use-shared-value 8) border-radius-second-half (reanimated/use-shared-value 8) start-x-animation (fn [] - (reanimated/animate-shared-value-with-delay translate-x 12 50 :linear 133.33) - (reanimated/animate-shared-value-with-delay connector-opacity 1 0 :easing1 93.33) - (reanimated/animate-shared-value-with-delay width 56 83.33 :easing1 80) - (reanimated/animate-shared-value-with-delay height 56 83.33 :easing1 80) - (reanimated/animate-shared-value-with-delay border-radius-first-half 28 83.33 :easing1 80) - (reanimated/animate-shared-value-with-delay border-radius-second-half 28 83.33 :easing1 80)) + (reanimated/animate-shared-value-with-delay translate-x + 12 50 + :linear 133.33) + (reanimated/animate-shared-value-with-delay connector-opacity + 1 0 + :easing1 93.33) + (reanimated/animate-shared-value-with-delay width + 56 83.33 + :easing1 80) + (reanimated/animate-shared-value-with-delay height + 56 83.33 + :easing1 80) + (reanimated/animate-shared-value-with-delay + border-radius-first-half + 28 83.33 + :easing1 80) + (reanimated/animate-shared-value-with-delay + border-radius-second-half + 28 83.33 + :easing1 80)) reset-x-animation (fn [] - (reanimated/animate-shared-value-with-timing translate-x 0 100 :linear) + (reanimated/animate-shared-value-with-timing translate-x + 0 + 100 + :linear) (reanimated/set-shared-value connector-opacity 0) (reanimated/set-shared-value width 24) (reanimated/set-shared-value height 12) (reanimated/set-shared-value border-radius-first-half 8) (reanimated/set-shared-value border-radius-second-half 16)) fade-in-animation (fn [] - (reanimated/animate-shared-value-with-timing translate-x 0 200 :linear) - (reanimated/animate-shared-value-with-timing opacity 1 200 :linear)) + (reanimated/animate-shared-value-with-timing translate-x + 0 + 200 + :linear) + (reanimated/animate-shared-value-with-timing opacity + 1 + 200 + :linear)) fade-out-animation (fn [] - (reanimated/animate-shared-value-with-timing translate-x 20 200 :linear) - (reanimated/animate-shared-value-with-timing opacity 0 200 :linear) + (reanimated/animate-shared-value-with-timing translate-x + 20 + 200 + :linear) + (reanimated/animate-shared-value-with-timing opacity + 0 + 200 + :linear) (reanimated/set-shared-value connector-opacity 0) (reanimated/set-shared-value width 24) (reanimated/set-shared-value height 12) @@ -376,13 +570,21 @@ [@ready-to-delete?]) [:<> [reanimated/view {:style (style/delete-button-container opacity)} - [reanimated/view {:style (style/delete-button-connector connector-opacity width height border-radius-first-half border-radius-second-half)}]] - [reanimated/view {:style (style/delete-button translate-x opacity) - :pointer-events :none} - [icons/icon :i/delete {:color colors/white - :size 20}]]]))]) + [reanimated/view + {:style (style/delete-button-connector connector-opacity + width + height + border-radius-first-half + border-radius-second-half)}]] + [reanimated/view + {:style (style/delete-button translate-x opacity) + :pointer-events :none} + [icons/icon :i/delete + {:color colors/white + :size 20}]]]))]) -(defn- record-button [recording? reviewing-audio?] +(defn- record-button + [recording? reviewing-audio?] [:f> (fn [] (let [opacity (reanimated/use-shared-value 1) @@ -393,13 +595,15 @@ (show-animation)) [@recording? @reviewing-audio?]) [reanimated/view {:style (style/record-button-container opacity)} - [button/button {:type :outline - :size 32 - :width 32 - :accessibility-label :mic-button} + [button/button + {:type :outline + :size 32 + :width 32 + :accessibility-label :mic-button} [icons/icon :i/audio {:color (colors/theme-colors colors/neutral-100 colors/white)}]]]))]) -(defn input-view [] +(defn input-view + [] [:f> (fn [] (let [recording? (reagent/atom false) @@ -414,126 +618,136 @@ touch-active? (atom false) recording-timer (atom nil) recording-length-ms (atom 0) - on-start-should-set-responder (fn [^js e] - (when-not @locked? - (let [pressed-record-button? (touch-inside-area? - {:location-x (oops/oget e "nativeEvent.locationX") - :location-y (oops/oget e "nativeEvent.locationY") - :ignore-min-y? false - :ignore-max-y? false - :ignore-min-x? false - :ignore-max-x? false} - record-button-area)] - (when-not @reviewing-audio? - (reset! recording? pressed-record-button?) - (when pressed-record-button? - ;; TODO: By now we just track recording length, we need to add actual audio recording logic - ;; GitHub issue [#14558]: https://github.com/status-im/status-mobile/issues/14558 - (reset! recording-timer (utils/set-interval #(reset! recording-length-ms (+ @recording-length-ms 500)) 500)))) - (reset! touch-active? true))) - true) - on-responder-move (fn [^js e] - (when-not @locked? - (let [location-x (oops/oget e "nativeEvent.locationX") - location-y (oops/oget e "nativeEvent.locationY") - page-x (oops/oget e "nativeEvent.pageX") - page-y (oops/oget e "nativeEvent.pageY") - moved-to-send-button? (touch-inside-area? - {:location-x location-x - :location-y location-y - :ignore-min-y? true - :ignore-max-y? false - :ignore-min-x? false - :ignore-max-x? true} - (send-button-area @ready-to-send?)) - moved-to-delete-button? (touch-inside-area? - {:location-x location-x - :location-y location-y - :ignore-min-y? false - :ignore-max-y? true - :ignore-min-x? true - :ignore-max-x? false} - (delete-button-area @ready-to-delete?)) - moved-to-lock-button? (touch-inside-area? - {:location-x location-x - :location-y location-y - :ignore-min-y? false - :ignore-max-y? false - :ignore-min-x? false - :ignore-max-x? false} - (lock-button-area @ready-to-lock?)) - moved-to-record-button? (and - (touch-inside-area? - {:location-x location-x - :location-y location-y - :ignore-min-y? false - :ignore-max-y? false - :ignore-min-x? false - :ignore-max-x? false} - record-button-area-big) - (not= location-x page-x) - (not= location-y page-y))] - (cond - (and - (or - (and moved-to-record-button? @ready-to-lock?) - (and (not @locked?) moved-to-lock-button? @record-button-at-initial-position?)) - (not @ready-to-delete?) - (not @ready-to-send?) - @recording?) (reset! ready-to-lock? moved-to-lock-button?) - (and - (or - (and moved-to-record-button? @ready-to-delete?) - (and moved-to-delete-button? @record-button-at-initial-position?)) - (not @ready-to-lock?) - (not @ready-to-send?) - @recording?) (reset! ready-to-delete? moved-to-delete-button?) - (and - (or - (and moved-to-record-button? @ready-to-send?) - (and moved-to-send-button? @record-button-at-initial-position?)) - (not @ready-to-lock?) - (not @ready-to-delete?) - @recording?) (reset! ready-to-send? moved-to-send-button?))))) - on-responder-release (fn [^js e] - (let [on-record-button? (touch-inside-area? - {:location-x (oops/oget e "nativeEvent.locationX") - :location-y (oops/oget e "nativeEvent.locationY") - :ignore-min-y? false - :ignore-max-y? false - :ignore-min-x? false - :ignore-max-x? false} - (if @reviewing-audio? record-button-area record-button-area-big))] - (cond - (and @reviewing-audio? on-record-button?) - (reset! reviewing-audio? false) - (and @ready-to-lock? (not @record-button-is-animating?)) - (do - (reset! locked? true) - (reset! ready-to-lock? false)) - (and (not @reviewing-audio?) on-record-button?) - (do - (when (>= @recording-length-ms 500) (reset! reviewing-audio? true)) - (reset! locked? false) - (reset! recording? false) - (reset! ready-to-lock? false) - (utils/clear-interval @recording-timer) - (reset! recording-length-ms 0)) - (and (not @locked?) (not @reviewing-audio?) (not @record-button-is-animating?)) - (do - (reset! recording? false) - (reset! ready-to-send? false) - (reset! ready-to-delete? false) - (reset! ready-to-lock? false) - (utils/clear-interval @recording-timer) - (reset! recording-length-ms 0)))) - (reset! touch-active? false))] + on-start-should-set-responder + (fn [^js e] + (when-not @locked? + (let [pressed-record-button? (touch-inside-area? + {:location-x (oops/oget e "nativeEvent.locationX") + :location-y (oops/oget e "nativeEvent.locationY") + :ignore-min-y? false + :ignore-max-y? false + :ignore-min-x? false + :ignore-max-x? false} + record-button-area)] + (when-not @reviewing-audio? + (reset! recording? pressed-record-button?) + (when pressed-record-button? + ;; TODO: By now we just track recording length, we need to add actual audio + ;; recording logic + ;; GitHub issue [#14558]: https://github.com/status-im/status-mobile/issues/14558 + (reset! recording-timer (utils/set-interval #(reset! recording-length-ms + (+ @recording-length-ms 500)) + 500)))) + (reset! touch-active? true))) + true) + on-responder-move + (fn [^js e] + (when-not @locked? + (let [location-x (oops/oget e "nativeEvent.locationX") + location-y (oops/oget e "nativeEvent.locationY") + page-x (oops/oget e "nativeEvent.pageX") + page-y (oops/oget e "nativeEvent.pageY") + moved-to-send-button? (touch-inside-area? + {:location-x location-x + :location-y location-y + :ignore-min-y? true + :ignore-max-y? false + :ignore-min-x? false + :ignore-max-x? true} + (send-button-area @ready-to-send?)) + moved-to-delete-button? (touch-inside-area? + {:location-x location-x + :location-y location-y + :ignore-min-y? false + :ignore-max-y? true + :ignore-min-x? true + :ignore-max-x? false} + (delete-button-area @ready-to-delete?)) + moved-to-lock-button? (touch-inside-area? + {:location-x location-x + :location-y location-y + :ignore-min-y? false + :ignore-max-y? false + :ignore-min-x? false + :ignore-max-x? false} + (lock-button-area @ready-to-lock?)) + moved-to-record-button? (and + (touch-inside-area? + {:location-x location-x + :location-y location-y + :ignore-min-y? false + :ignore-max-y? false + :ignore-min-x? false + :ignore-max-x? false} + record-button-area-big) + (not= location-x page-x) + (not= location-y page-y))] + (cond + (and + (or + (and moved-to-record-button? @ready-to-lock?) + (and (not @locked?) moved-to-lock-button? @record-button-at-initial-position?)) + (not @ready-to-delete?) + (not @ready-to-send?) + @recording?) + (reset! ready-to-lock? moved-to-lock-button?) + (and + (or + (and moved-to-record-button? @ready-to-delete?) + (and moved-to-delete-button? @record-button-at-initial-position?)) + (not @ready-to-lock?) + (not @ready-to-send?) + @recording?) + (reset! ready-to-delete? moved-to-delete-button?) + (and + (or + (and moved-to-record-button? @ready-to-send?) + (and moved-to-send-button? @record-button-at-initial-position?)) + (not @ready-to-lock?) + (not @ready-to-delete?) + @recording?) + (reset! ready-to-send? moved-to-send-button?))))) + on-responder-release + (fn [^js e] + (let [on-record-button? (touch-inside-area? + {:location-x (oops/oget e "nativeEvent.locationX") + :location-y (oops/oget e "nativeEvent.locationY") + :ignore-min-y? false + :ignore-max-y? false + :ignore-min-x? false + :ignore-max-x? false} + (if @reviewing-audio? record-button-area record-button-area-big))] + (cond + (and @reviewing-audio? on-record-button?) + (reset! reviewing-audio? false) + (and @ready-to-lock? (not @record-button-is-animating?)) + (do + (reset! locked? true) + (reset! ready-to-lock? false)) + (and (not @reviewing-audio?) on-record-button?) + (do + (when (>= @recording-length-ms 500) (reset! reviewing-audio? true)) + (reset! locked? false) + (reset! recording? false) + (reset! ready-to-lock? false) + (utils/clear-interval @recording-timer) + (reset! recording-length-ms 0)) + (and (not @locked?) (not @reviewing-audio?) (not @record-button-is-animating?)) + (do + (reset! recording? false) + (reset! ready-to-send? false) + (reset! ready-to-delete? false) + (reset! ready-to-lock? false) + (utils/clear-interval @recording-timer) + (reset! recording-length-ms 0)))) + (reset! touch-active? false))] (fn [] - [rn/view {:style style/input-container - :pointer-events :box-only - :on-start-should-set-responder on-start-should-set-responder - :on-responder-move on-responder-move - :on-responder-release on-responder-release} + [rn/view + {:style style/input-container + :pointer-events :box-only + :on-start-should-set-responder on-start-should-set-responder + :on-responder-move on-responder-move + :on-responder-release on-responder-release} [delete-button recording? ready-to-delete?] [lock-button recording? ready-to-lock? locked?] [send-button recording? ready-to-send? reviewing-audio?] diff --git a/src/quo2/components/selectors/__tests__/selectors_component_spec.cljs b/src/quo2/components/selectors/__tests__/selectors_component_spec.cljs index be61f7c3e8..3a62db31c5 100644 --- a/src/quo2/components/selectors/__tests__/selectors_component_spec.cljs +++ b/src/quo2/components/selectors/__tests__/selectors_component_spec.cljs @@ -7,30 +7,30 @@ ([] (render-toggle {})) ([opts] - (rtl/render (reagent/as-element [selectors/toggle opts])))) + (rtl/render (reagent/as-element [selectors/toggle opts])))) (defn render-checkbox ([] (render-checkbox {})) ([opts] - (rtl/render (reagent/as-element [selectors/checkbox opts])))) + (rtl/render (reagent/as-element [selectors/checkbox opts])))) (defn render-checkbox-prefill ([] (render-checkbox-prefill {})) ([opts] - (rtl/render (reagent/as-element [selectors/checkbox-prefill opts])))) + (rtl/render (reagent/as-element [selectors/checkbox-prefill opts])))) (defn render-radio ([] (render-radio {})) ([opts] - (rtl/render (reagent/as-element [selectors/radio opts])))) + (rtl/render (reagent/as-element [selectors/radio opts])))) (js/global.test "default render of toggle component" (fn [] (render-toggle) - (-> (js/expect (rtl/screen.getByTestId "toggle-component")) + (-> (js/expect (rtl/screen.getByTestId "toggle-component")) (.toBeTruthy)))) (js/global.test "toggle component on change is working" @@ -44,7 +44,7 @@ (js/global.test "default render of radio component" (fn [] (render-radio) - (-> (js/expect (rtl/screen.getByTestId "radio-component")) + (-> (js/expect (rtl/screen.getByTestId "radio-component")) (.toBeTruthy)))) (js/global.test "radio component on change is working" @@ -58,7 +58,7 @@ (js/global.test "default render of checkbox component" (fn [] (render-checkbox) - (-> (js/expect (rtl/screen.getByTestId "checkbox-component")) + (-> (js/expect (rtl/screen.getByTestId "checkbox-component")) (.toBeTruthy)))) (js/global.test "checkbox component on change is working" @@ -72,7 +72,7 @@ (js/global.test "default render of checkbox-prefill component" (fn [] (render-checkbox-prefill) - (-> (js/expect (rtl/screen.getByTestId "checkbox-prefill-component")) + (-> (js/expect (rtl/screen.getByTestId "checkbox-prefill-component")) (.toBeTruthy)))) (js/global.test "checkbox-prefill component on change is working" diff --git a/src/quo2/components/selectors/disclaimer.cljs b/src/quo2/components/selectors/disclaimer.cljs index 49c997d826..3d4a22e82d 100644 --- a/src/quo2/components/selectors/disclaimer.cljs +++ b/src/quo2/components/selectors/disclaimer.cljs @@ -1,31 +1,32 @@ (ns quo2.components.selectors.disclaimer - (:require [react-native.core :as rn] + (:require [quo2.components.markdown.text :as text] + [quo2.components.selectors.selectors :as selectors] [quo2.foundations.colors :as colors] - [quo2.components.markdown.text :as text] - [quo2.components.selectors.selectors :as selectors])) + [react-native.core :as rn])) -(defn disclaimer [{:keys [on-change accessibility-label container-style]} label] +(defn disclaimer + [{:keys [on-change accessibility-label container-style]} label] [rn/view {:style (merge container-style - {:flex 1 - :flex-direction :row + {:flex 1 + :flex-direction :row :background-color (colors/theme-colors colors/neutral-5 colors/neutral-80-opa-40) - :padding 11 - :align-self :stretch - :border-radius 12 - :border-width 1 - :border-color (colors/theme-colors - colors/neutral-20 - colors/neutral-70)})} + :padding 11 + :align-self :stretch + :border-radius 12 + :border-width 1 + :border-color (colors/theme-colors + colors/neutral-20 + colors/neutral-70)})} [selectors/checkbox {:on-change on-change}] [text/text {:accessibility-label accessibility-label :size :paragraph-2 - :style {:margin-left 8}} + :style {:margin-left 8}} label]]) diff --git a/src/quo2/components/selectors/selectors.cljs b/src/quo2/components/selectors/selectors.cljs index c50753acc0..801ece74b0 100644 --- a/src/quo2/components/selectors/selectors.cljs +++ b/src/quo2/components/selectors/selectors.cljs @@ -1,14 +1,19 @@ (ns quo2.components.selectors.selectors - (:require [react-native.core :as rn] + (:require [quo2.components.icon :as icons] [quo2.foundations.colors :as colors] - [reagent.core :as reagent] - [quo2.components.icon :as icons])) + [react-native.core :as rn] + [reagent.core :as reagent])) -(defn- get-color [checked? disabled? blurred-background?] +(defn- get-color + [checked? disabled? blurred-background?] (cond checked? (colors/custom-color-by-theme - :primary 50 60 (when disabled? 30) (when disabled? 30)) + :primary + 50 + 60 + (when disabled? 30) + (when disabled? 30)) blurred-background? (colors/theme-colors (colors/alpha colors/neutral-80 (if disabled? 0.05 0.1)) @@ -18,138 +23,152 @@ (colors/alpha colors/neutral-20 (if disabled? 0.4 1)) (colors/alpha colors/neutral-70 (if disabled? 0.3 1))))) -(defn- handle-press [disabled? on-change checked?] +(defn- handle-press + [disabled? on-change checked?] (when (not disabled?) (fn [] (swap! checked? not) (when on-change (on-change @checked?))))) -(defn checkbox-prefill [{:keys [default-checked?]}] +(defn checkbox-prefill + [{:keys [default-checked?]}] (let [checked? (reagent/atom (or default-checked? false))] (fn [{:keys [on-change disabled? blurred-background? container-style]}] [rn/touchable-without-feedback {:on-press (handle-press disabled? on-change checked?)} [rn/view - {:style (merge - container-style - {:height 21 - :width 21 - :border-radius 6 - :background-color (if blurred-background? - (colors/theme-colors - (colors/alpha colors/neutral-80 (if disabled? 0.05 0.1)) - (colors/alpha colors/white (if disabled? 0.05 0.1))) - (colors/theme-colors - (colors/alpha colors/neutral-20 (if disabled? 0.3 1)) - (colors/alpha colors/neutral-70 (if disabled? 0.3 1))))}) + {:style (merge + container-style + {:height 21 + :width 21 + :border-radius 6 + :background-color (if blurred-background? + (colors/theme-colors + (colors/alpha colors/neutral-80 + (if disabled? 0.05 0.1)) + (colors/alpha colors/white (if disabled? 0.05 0.1))) + (colors/theme-colors + (colors/alpha colors/neutral-20 + (if disabled? 0.3 1)) + (colors/alpha colors/neutral-70 + (if disabled? 0.3 1))))}) :accessibility-label (str "checkbox-" (if @checked? "on" "off")) :accessibility-role :checkbox - :testID "checkbox-prefill-component"} + :testID "checkbox-prefill-component"} (when @checked? - [rn/view {:style - {:height 20 - :width 20}} + [rn/view + {:style + {:height 20 + :width 20}} [icons/icon :i/check-small - {:size 20 + {:size 20 :color (colors/theme-colors (colors/alpha colors/neutral-100 (if disabled? 0.3 1)) (colors/alpha colors/white (if disabled? 0.3 1)))}]])]]))) -(defn checkbox [{:keys [default-checked?]}] +(defn checkbox + [{:keys [default-checked?]}] (let [checked? (reagent/atom (or default-checked? false))] (fn [{:keys [on-change disabled? blurred-background? container-style]}] [rn/touchable-without-feedback {:on-press (handle-press disabled? on-change checked?)} [rn/view - {:style (merge - container-style - {:height 20 - :width 20})} + {:style (merge + container-style + {:height 20 + :width 20})} [rn/view - {:style {:flex 1 - :border-radius 6 - :border-width (if @checked? 0 1) - :background-color (cond - @checked? - (get-color @checked? disabled? blurred-background?) - blurred-background? - (colors/theme-colors - colors/white-opa-5 - colors/white-opa-10) - :else - (colors/theme-colors - colors/white - colors/neutral-80-opa-40)) - :border-color (if @checked? :none - (get-color @checked? disabled? blurred-background?))} + {:style {:flex 1 + :border-radius 6 + :border-width (if @checked? 0 1) + :background-color (cond + @checked? + (get-color @checked? disabled? blurred-background?) + blurred-background? + (colors/theme-colors + colors/white-opa-5 + colors/white-opa-10) + :else + (colors/theme-colors + colors/white + colors/neutral-80-opa-40)) + :border-color (if @checked? + :none + (get-color @checked? disabled? blurred-background?))} :accessibility-label (str "checkbox-" (if @checked? "on" "off")) :accessibility-role :checkbox - :testID "checkbox-component"} + :testID "checkbox-component"} (when @checked? - [rn/view {:style - {:height 20 - :width 20}} + [rn/view + {:style + {:height 20 + :width 20}} [icons/icon :i/check-small - {:size 20 + {:size 20 :color colors/white}]])]]]))) -(defn radio [{:keys [default-checked?]}] +(defn radio + [{:keys [default-checked?]}] (let [checked? (reagent/atom (or default-checked? false))] (fn [{:keys [on-change disabled? blurred-background? container-style]}] [rn/touchable-without-feedback {:on-press (handle-press disabled? on-change checked?)} [rn/view - {:style (merge - container-style - {:height 20 - :width 20 - :border-radius 20 - :border-width 1 - :border-color (get-color @checked? disabled? blurred-background?) - :background-color (when-not blurred-background? - (colors/theme-colors colors/white - (colors/alpha colors/neutral-80 0.4)))}) + {:style (merge + container-style + {:height 20 + :width 20 + :border-radius 20 + :border-width 1 + :border-color (get-color @checked? disabled? blurred-background?) + :background-color (when-not blurred-background? + (colors/theme-colors colors/white + (colors/alpha colors/neutral-80 + 0.4)))}) :accessibility-label (str "radio-" (if @checked? "on" "off")) :accessibility-role :checkbox - :testID "radio-component"} + :testID "radio-component"} - [rn/view {:style - {:margin-left :auto - :height 14 - :width 14 - :background-color (when @checked? (get-color @checked? disabled? blurred-background?)) - :border-radius 20 - :margin-right :auto - :margin-top :auto - :margin-bottom :auto}}]]]))) + [rn/view + {:style + {:margin-left :auto + :height 14 + :width 14 + :background-color (when @checked? (get-color @checked? disabled? blurred-background?)) + :border-radius 20 + :margin-right :auto + :margin-top :auto + :margin-bottom :auto}}]]]))) -(defn toggle [{:keys [default-checked?]}] +(defn toggle + [{:keys [default-checked?]}] (let [checked? (reagent/atom (or default-checked? false))] (fn [{:keys [on-change disabled? blurred-background? container-style]}] [rn/touchable-without-feedback {:on-press (handle-press disabled? on-change checked?)} [rn/view - {:style (merge - container-style - {:height 20 - :width 30 - :border-radius 20 - :background-color (get-color @checked? disabled? blurred-background?)}) + {:style (merge + container-style + {:height 20 + :width 30 + :border-radius 20 + :background-color (get-color @checked? disabled? blurred-background?)}) :accessibility-label (str "toggle-" (if @checked? "on" "off")) :accessibility-role :checkbox - :testID "toggle-component"} - [rn/view {:style - {:margin-left (if @checked? 12 2) - :height 16 - :width 16 - :background-color (if blurred-background? - (colors/theme-colors - (colors/alpha colors/white (if disabled? 0.4 1)) - (colors/alpha colors/white (if disabled? 0.3 1))) - (colors/theme-colors - (colors/alpha colors/white 1) - (colors/alpha colors/white (if disabled? 0.4 1)))) - :border-radius 20 - :margin-right :auto - :margin-top :auto - :margin-bottom :auto}}]]]))) + :testID "toggle-component"} + [rn/view + {:style + {:margin-left (if @checked? 12 2) + :height 16 + :width 16 + :background-color (if blurred-background? + (colors/theme-colors + (colors/alpha colors/white (if disabled? 0.4 1)) + (colors/alpha colors/white (if disabled? 0.3 1))) + (colors/theme-colors + (colors/alpha colors/white 1) + (colors/alpha colors/white (if disabled? 0.4 1)))) + :border-radius 20 + :margin-right :auto + :margin-top :auto + :margin-bottom :auto}}]]]))) diff --git a/src/quo2/components/separator.cljs b/src/quo2/components/separator.cljs index fe32ed6cd2..a7520c09eb 100644 --- a/src/quo2/components/separator.cljs +++ b/src/quo2/components/separator.cljs @@ -1,8 +1,9 @@ (ns quo2.components.separator - (:require [react-native.core :as rn] - [quo2.foundations.colors :as quo2.colors])) + (:require [quo2.foundations.colors :as quo2.colors] + [react-native.core :as rn])) -(defn separator [{:keys [style]}] +(defn separator + [{:keys [style]}] [rn/view {:style (merge diff --git a/src/quo2/components/settings/privacy_option.cljs b/src/quo2/components/settings/privacy_option.cljs index 503bfa2646..2bc31b05ff 100644 --- a/src/quo2/components/settings/privacy_option.cljs +++ b/src/quo2/components/settings/privacy_option.cljs @@ -1,10 +1,10 @@ (ns quo2.components.settings.privacy-option - (:require [react-native.core :as rn] - [quo2.components.icon :as icons] + (:require [quo2.components.icon :as icons] + [quo2.components.markdown.text :as text] [quo2.components.selectors.selectors :as selectors] [quo2.components.settings.style :as style] - [quo2.components.markdown.text :as text] - [quo2.foundations.colors :as colors])) + [quo2.foundations.colors :as colors] + [react-native.core :as rn])) (defn- bullet [] @@ -27,8 +27,9 @@ [rn/view {:style style/card-footer-label-container} [text/text {:size :paragraph-2} label]] [rn/view {:style style/card-footer-toggle-container} - [selectors/toggle {:disabled? (not active?) - :on-change on-toggle}]]]]) + [selectors/toggle + {:disabled? (not active?) + :on-change on-toggle}]]]]) (defn- selection-indicator [active?] @@ -38,8 +39,9 @@ (defn- card-header [{:keys [icon label active?]}] [rn/view {:style style/card-header-container} - [icons/icon icon {:size 20 - :color (colors/theme-colors colors/neutral-50 colors/neutral-40)}] + [icons/icon icon + {:size 20 + :color (colors/theme-colors colors/neutral-50 colors/neutral-40)}] [rn/view {:style style/card-header-label-container} [text/text {:weight :semi-bold} label]] [selection-indicator active?]]) @@ -53,11 +55,13 @@ :accessibility-label :privacy-option-card :testID :privacy-option-card} [rn/view (style/privacy-option-card active?) - [card-header {:active? active? - :icon icon - :label header}] + [card-header + {:active? active? + :icon icon + :label header}] [unordered-list (when-not footer {:margin-bottom 8}) list-items] (when footer - [card-footer {:active? active? - :label footer - :on-toggle on-toggle}])]]) + [card-footer + {:active? active? + :label footer + :on-toggle on-toggle}])]]) diff --git a/src/quo2/components/tabs/account_selector.cljs b/src/quo2/components/tabs/account_selector.cljs index 0f0b851d98..6491f17fa7 100644 --- a/src/quo2/components/tabs/account_selector.cljs +++ b/src/quo2/components/tabs/account_selector.cljs @@ -1,46 +1,47 @@ (ns quo2.components.tabs.account-selector - (:require - [quo2.theme :as theme] - [react-native.core :as rn] - [quo2.foundations.colors :as colors] - [quo2.components.markdown.text :as quo2])) + (:require [quo2.components.markdown.text :as quo2] + [quo2.foundations.colors :as colors] + [quo2.theme :as theme] + [react-native.core :as rn])) (def themes - {:light {:default {:bg colors/neutral-10 - :account-text colors/neutral-100 - :label-text colors/neutral-50} - :transparent {:bg colors/neutral-80-opa-5 - :account-text colors/neutral-100 - :label-text colors/neutral-80-opa-40}} + {:light {:default {:bg colors/neutral-10 + :account-text colors/neutral-100 + :label-text colors/neutral-50} + :transparent {:bg colors/neutral-80-opa-5 + :account-text colors/neutral-100 + :label-text colors/neutral-80-opa-40}} - :dark {:default {:bg colors/neutral-80-opa-80 - :account-text colors/white - :label-text colors/neutral-40} - :transparent {:bg colors/white-opa-5 - :account-text colors/white - :label-text colors/neutral-40}}}) + :dark {:default {:bg colors/neutral-80-opa-80 + :account-text colors/white + :label-text colors/neutral-40} + :transparent {:bg colors/white-opa-5 + :account-text colors/white + :label-text colors/neutral-40}}}) -(defn account-container-row [background-color] - {:padding-vertical 4 - :flex-direction :row - :align-items :center - :background-color background-color - :border-radius 12}) +(defn account-container-row + [background-color] + {:padding-vertical 4 + :flex-direction :row + :align-items :center + :background-color background-color + :border-radius 12}) (def account-emoji - {:height 16 - :width 16}) + {:height 16 + :width 16}) (def account-emoji-container - {:background-color (colors/custom-color :purple 50) - :padding 8 - :justify-content :center - :align-items :center - :border-radius 10 - :margin-left 4 - :margin-right 8}) + {:background-color (colors/custom-color :purple 50) + :padding 8 + :justify-content :center + :align-items :center + :border-radius 10 + :margin-left 4 + :margin-right 8}) -(defn get-color-by-type [type key] +(defn get-color-by-type + [type key] (get-in themes [(theme/get-theme) type key])) (defn account-selector @@ -57,16 +58,19 @@ account-text-color (get-color-by-type (if transparent? :transparent :default) :account-text) label-text-color (get-color-by-type (if transparent? :transparent :default) :label-text)] [rn/view {:style style} - (when show-label? [quo2/text {:weight :medium - :size :paragraph-2 - :style {:color label-text-color - :margin-bottom 8}} - label-text]) + (when show-label? + [quo2/text + {:weight :medium + :size :paragraph-2 + :style {:color label-text-color + :margin-bottom 8}} + label-text]) [rn/view {:style (account-container-row background-color)} [rn/view {:style account-emoji-container} [quo2/text account-emoji]] - [quo2/text {:weight :medium - :size :paragraph-1 - :style {:color account-text-color}} + [quo2/text + {:weight :medium + :size :paragraph-1 + :style {:color account-text-color}} account-text]]])) diff --git a/src/quo2/components/tabs/segmented_tab.cljs b/src/quo2/components/tabs/segmented_tab.cljs index 229779782e..8d3002f5ba 100644 --- a/src/quo2/components/tabs/segmented_tab.cljs +++ b/src/quo2/components/tabs/segmented_tab.cljs @@ -1,29 +1,33 @@ (ns quo2.components.tabs.segmented-tab - (:require [reagent.core :as reagent] - [react-native.core :as rn] - [quo2.components.tabs.tab :as tab] + (:require [quo2.components.tabs.tab :as tab] [quo2.foundations.colors :as colors] - [quo2.theme :as theme])) + [quo2.theme :as theme] + [react-native.core :as rn] + [reagent.core :as reagent])) -(def themes {:light {:background-color colors/neutral-20} - :dark {:background-color colors/neutral-80}}) +(def themes + {:light {:background-color colors/neutral-20} + :dark {:background-color colors/neutral-80}}) -(defn segmented-control [{:keys [default-active on-change]}] +(defn segmented-control + [{:keys [default-active on-change]}] (let [active-tab-id (reagent/atom default-active)] (fn [{:keys [data size]}] (let [active-id @active-tab-id] - [rn/view {:flex-direction :row - :background-color (get-in themes [(theme/get-theme) :background-color]) - :border-radius (case size - 32 10 - 28 8 - 24 8 - 20 6) - :padding 2} + [rn/view + {:flex-direction :row + :background-color (get-in themes [(theme/get-theme) :background-color]) + :border-radius (case size + 32 10 + 28 8 + 24 8 + 20 6) + :padding 2} (for [[indx {:keys [label id]}] (map-indexed vector data)] ^{:key id} - [rn/view {:margin-left (if (= 0 indx) 0 2) - :flex 1} + [rn/view + {:margin-left (if (= 0 indx) 0 2) + :flex 1} [tab/tab {:id id :segmented true diff --git a/src/quo2/components/tabs/tab.cljs b/src/quo2/components/tabs/tab.cljs index b7f1677ff1..3a7959f17d 100644 --- a/src/quo2/components/tabs/tab.cljs +++ b/src/quo2/components/tabs/tab.cljs @@ -1,49 +1,52 @@ (ns quo2.components.tabs.tab - (:require [quo2.foundations.colors :as colors] - [react-native.core :as rn] - [quo2.theme :as theme] + (:require [quo2.components.icon :as icons] [quo2.components.markdown.text :as text] - [quo2.components.icon :as icons])) + [quo2.foundations.colors :as colors] + [quo2.theme :as theme] + [react-native.core :as rn])) -(def themes {:light {:default {:background-color colors/neutral-20 - :icon-color colors/neutral-50 - :label {:style {:color colors/neutral-100}}} - :active {:background-color colors/neutral-50 - :icon-color colors/white - :label {:style {:color colors/white}}} - :disabled {:background-color colors/neutral-20 - :icon-color colors/neutral-50 - :label {:style {:color colors/neutral-100}}}} - :dark {:default {:background-color colors/neutral-80 - :icon-color colors/neutral-40 - :label {:style {:color colors/white}}} - :active {:background-color colors/neutral-60 - :icon-color colors/white - :label {:style {:color colors/white}}} - :disabled {:background-color colors/neutral-80 - :icon-color colors/neutral-40 - :label {:style {:color colors/white}}}}}) +(def themes + {:light {:default {:background-color colors/neutral-20 + :icon-color colors/neutral-50 + :label {:style {:color colors/neutral-100}}} + :active {:background-color colors/neutral-50 + :icon-color colors/white + :label {:style {:color colors/white}}} + :disabled {:background-color colors/neutral-20 + :icon-color colors/neutral-50 + :label {:style {:color colors/neutral-100}}}} + :dark {:default {:background-color colors/neutral-80 + :icon-color colors/neutral-40 + :label {:style {:color colors/white}}} + :active {:background-color colors/neutral-60 + :icon-color colors/white + :label {:style {:color colors/white}}} + :disabled {:background-color colors/neutral-80 + :icon-color colors/neutral-40 + :label {:style {:color colors/white}}}}}) -(def themes-for-blur-background {:light {:default {:background-color colors/neutral-80-opa-5 - :icon-color colors/neutral-80-opa-40 - :label {:style {:color colors/neutral-100}}} - :active {:background-color colors/neutral-80-opa-60 - :icon-color colors/white - :label {:style {:color colors/white}}} - :disabled {:background-color colors/neutral-80-opa-5 - :icon-color colors/neutral-80-opa-40 - :label {:style {:color colors/neutral-100}}}} - :dark {:default {:background-color colors/white-opa-5 - :icon-color colors/white - :label {:style {:color colors/white}}} - :active {:background-color colors/white-opa-20 - :icon-color colors/white - :label {:style {:color colors/white}}} - :disabled {:background-color colors/white-opa-5 - :icon-color colors/neutral-40 - :label {:style {:color colors/white}}}}}) +(def themes-for-blur-background + {:light {:default {:background-color colors/neutral-80-opa-5 + :icon-color colors/neutral-80-opa-40 + :label {:style {:color colors/neutral-100}}} + :active {:background-color colors/neutral-80-opa-60 + :icon-color colors/white + :label {:style {:color colors/white}}} + :disabled {:background-color colors/neutral-80-opa-5 + :icon-color colors/neutral-80-opa-40 + :label {:style {:color colors/neutral-100}}}} + :dark {:default {:background-color colors/white-opa-5 + :icon-color colors/white + :label {:style {:color colors/white}}} + :active {:background-color colors/white-opa-20 + :icon-color colors/white + :label {:style {:color colors/white}}} + :disabled {:background-color colors/white-opa-5 + :icon-color colors/neutral-40 + :label {:style {:color colors/white}}}}}) -(defn style-container [size disabled background-color] +(defn style-container + [size disabled background-color] (merge {:height size :align-items :center :justify-content :center @@ -54,7 +57,11 @@ 24 8 20 6) :background-color background-color - :padding-horizontal (case size 32 12 28 12 24 8 20 8)} + :padding-horizontal (case size + 32 12 + 28 12 + 24 8 + 20 8)} (when disabled {:opacity 0.3}))) @@ -70,14 +77,18 @@ (fn [{:keys [id on-press disabled size before active accessibility-label blur? override-theme] :or {size 32}} children] - (let [state (cond disabled :disabled active :active :else :default) + (let [state (cond disabled :disabled + active :active + :else :default) {:keys [icon-color background-color label]} - (get-in (if blur? themes-for-blur-background themes) [(or override-theme (theme/get-theme)) state])] - [rn/touchable-without-feedback (merge {:disabled disabled - :accessibility-label accessibility-label} - (when on-press - {:on-press (fn [] - (on-press id))})) + (get-in (if blur? themes-for-blur-background themes) + [(or override-theme (theme/get-theme)) state])] + [rn/touchable-without-feedback + (merge {:disabled disabled + :accessibility-label accessibility-label} + (when on-press + {:on-press (fn [] + (on-press id))})) [rn/view {:style (style-container size disabled background-color)} (when before [rn/view @@ -85,10 +96,14 @@ [rn/view (cond (string? children) - [text/text (merge {:size (case size 24 :paragraph-2 20 :label nil) - :weight :medium - :number-of-lines 1} - label) + [text/text + (merge {:size (case size + 24 :paragraph-2 + 20 :label + nil) + :weight :medium + :number-of-lines 1} + label) children] (vector? children) children)]]]))) diff --git a/src/quo2/components/tabs/tabs.cljs b/src/quo2/components/tabs/tabs.cljs index 824561e9e6..ae32aefb9a 100644 --- a/src/quo2/components/tabs/tabs.cljs +++ b/src/quo2/components/tabs/tabs.cljs @@ -1,31 +1,34 @@ (ns quo2.components.tabs.tabs (:require [oops.core :refer [oget]] - [react-native.core :as rn] - [quo2.components.tabs.tab :as tab] - [reagent.core :as reagent] - [utils.number :as utils.number] - [utils.collection :as utils.collection] - [quo2.foundations.colors :as colors] [quo2.components.notifications.notification-dot :refer [notification-dot]] + [quo2.components.tabs.tab :as tab] + [quo2.foundations.colors :as colors] + [react-native.core :as rn] + [react-native.linear-gradient :as linear-gradient] [react-native.masked-view :as masked-view] - [react-native.linear-gradient :as linear-gradient])) + [reagent.core :as reagent] + [utils.collection :as utils.collection] + [utils.number :as utils.number])) (def default-tab-size 32) -(defn indicator [] - [rn/view {:position :absolute - :z-index 1 - :right -2 - :top -2 - :width 10 - :height 10 - :border-radius 5 - :justify-content :center - :align-items :center - :background-color (colors/theme-colors colors/neutral-5 colors/neutral-95)} +(defn indicator + [] + [rn/view + {:position :absolute + :z-index 1 + :right -2 + :top -2 + :width 10 + :height 10 + :border-radius 5 + :justify-content :center + :align-items :center + :background-color (colors/theme-colors colors/neutral-5 colors/neutral-95)} [notification-dot]]) -(defn tabs [{:keys [default-active on-change style]}] +(defn tabs + [{:keys [default-active on-change style]}] (let [active-tab-id (reagent/atom default-active)] (fn [{:keys [data size] :or {size default-tab-size}}] [rn/view (merge {:flex-direction :row} style) @@ -85,22 +88,23 @@ (let [active-tab-id (reagent/atom default-active) fading (reagent/atom {:fade-end-percentage fade-end-percentage}) flat-list-ref (atom nil)] - (fn [{:keys [data - fade-end-percentage - fade-end? - on-change - on-scroll - scroll-event-throttle - scroll-on-press? - size - blur? - override-theme] - :or {fade-end-percentage fade-end-percentage - fade-end? false - scroll-event-throttle 64 - scroll-on-press? false - size default-tab-size} - :as props}] + (fn + [{:keys [data + fade-end-percentage + fade-end? + on-change + on-scroll + scroll-event-throttle + scroll-on-press? + size + blur? + override-theme] + :or {fade-end-percentage fade-end-percentage + fade-end? false + scroll-event-throttle 64 + scroll-on-press? false + size default-tab-size} + :as props}] (let [maybe-mask-wrapper (if fade-end? [masked-view/masked-view {:mask-element @@ -114,54 +118,76 @@ :style {:width "100%" :height "100%"}}])}] [:<>])] - (conj maybe-mask-wrapper - [rn/flat-list - (merge (dissoc props - :default-active - :fade-end-percentage - :fade-end? - :on-change - :scroll-on-press? - :size) - (when scroll-on-press? - {:initial-scroll-index (utils.collection/first-index #(= @active-tab-id (:id %)) data)}) - {:ref #(reset! flat-list-ref %) - :extra-data (str @active-tab-id) - :horizontal true - :scroll-event-throttle scroll-event-throttle - :shows-horizontal-scroll-indicator false - :data data - :key-fn (comp str :id) - :on-scroll (fn [^js e] - (when fade-end? - (let [offset-x (oget e "nativeEvent.contentOffset.x") - content-width (oget e "nativeEvent.contentSize.width") - layout-width (oget e "nativeEvent.layoutMeasurement.width") - new-percentage (calculate-fade-end-percentage {:offset-x offset-x - :content-width content-width - :layout-width layout-width - :max-fade-percentage fade-end-percentage})] - ;; Avoid unnecessary re-rendering. - (when (not= new-percentage (get @fading :fade-end-percentage)) - (swap! fading assoc :fade-end-percentage new-percentage)))) - (when on-scroll - (on-scroll e))) - :render-fn (fn [{:keys [id label]} index] - [rn/view {:style {:margin-right (if (= size default-tab-size) 12 8) - :padding-right (when (= index (dec (count data))) - (get-in props [:style :padding-left]))}} - [tab/tab {:id id - :size size - :override-theme override-theme - :blur? blur? - :active (= id @active-tab-id) - :on-press (fn [id] - (reset! active-tab-id id) - (when scroll-on-press? - (.scrollToIndex ^js @flat-list-ref - #js {:animated true - :index index - :viewPosition 0.5})) - (when on-change - (on-change id)))} - label]])})]))))) + (conj + maybe-mask-wrapper + [rn/flat-list + (merge + (dissoc props + :default-active + :fade-end-percentage + :fade-end? + :on-change + :scroll-on-press? + :size) + (when scroll-on-press? + {:initial-scroll-index (utils.collection/first-index #(= @active-tab-id (:id %)) data)}) + {:ref #(reset! flat-list-ref %) + :extra-data (str @active-tab-id) + :horizontal true + :scroll-event-throttle scroll-event-throttle + :shows-horizontal-scroll-indicator false + :data data + :key-fn (comp str :id) + :on-scroll (fn [^js e] + (when fade-end? + (let [offset-x (oget + e + "nativeEvent.contentOffset.x") + content-width (oget + e + "nativeEvent.contentSize.width") + layout-width + (oget e "nativeEvent.layoutMeasurement.width") + new-percentage + (calculate-fade-end-percentage + {:offset-x offset-x + :content-width content-width + :layout-width layout-width + :max-fade-percentage fade-end-percentage})] + ;; Avoid unnecessary re-rendering. + (when (not= new-percentage + (get @fading :fade-end-percentage)) + (swap! fading assoc + :fade-end-percentage + new-percentage)))) + (when on-scroll + (on-scroll e))) + :render-fn (fn [{:keys [id label]} index] + [rn/view + {:style {:margin-right (if (= size default-tab-size) + 12 + 8) + :padding-right (when (= index + (dec (count data))) + (get-in props + [:style + :padding-left]))}} + [tab/tab + {:id id + :size size + :override-theme override-theme + :blur? blur? + :active (= id @active-tab-id) + :on-press (fn [id] + (reset! active-tab-id id) + (when scroll-on-press? + (.scrollToIndex ^js + @flat-list-ref + #js + {:animated true + :index index + :viewPosition + 0.5})) + (when on-change + (on-change id)))} + label]])})]))))) diff --git a/src/quo2/components/tags/base_tag.cljs b/src/quo2/components/tags/base_tag.cljs index af3180ba18..6329a445d6 100644 --- a/src/quo2/components/tags/base_tag.cljs +++ b/src/quo2/components/tags/base_tag.cljs @@ -1,18 +1,19 @@ (ns quo2.components.tags.base-tag (:require [react-native.core :as rn])) -(defn style-container [size disabled border-color border-width background-color label type] - (merge {:height size - :border-color border-color - :background-color background-color - :border-width border-width - :border-radius size - :align-items :center - :justify-content :center} +(defn style-container + [size disabled border-color border-width background-color label type] + (merge {:height size + :border-color border-color + :background-color background-color + :border-width border-width + :border-radius size + :align-items :center + :justify-content :center} (when disabled {:opacity 0.3}) (when (and (or (= type :icon) (= type :emoji)) (not label)) - {:width size}))) + {:width size}))) (defn base-tag "opts @@ -21,11 +22,19 @@ :labelled true" [_] (fn [{:keys [id size disabled border-color border-width background-color on-press - accessibility-label label type] :or {size 32}} children] - [rn/touchable-without-feedback (merge {:disabled disabled - :accessibility-label accessibility-label} - (when on-press - {:on-press #(on-press id)})) - [rn/view {:style (merge (style-container size disabled border-color border-width - background-color label type))} + accessibility-label label type] + :or {size 32}} children] + [rn/touchable-without-feedback + (merge {:disabled disabled + :accessibility-label accessibility-label} + (when on-press + {:on-press #(on-press id)})) + [rn/view + {:style (merge (style-container size + disabled + border-color + border-width + background-color + label + type))} children]])) diff --git a/src/quo2/components/tags/context_tags.cljs b/src/quo2/components/tags/context_tags.cljs index 7fa09e18c6..da89372f00 100644 --- a/src/quo2/components/tags/context_tags.cljs +++ b/src/quo2/components/tags/context_tags.cljs @@ -1,66 +1,78 @@ (ns quo2.components.tags.context-tags - (:require [quo2.foundations.colors :as colors] - [quo2.theme :as quo2.theme] + (:require [quo2.components.avatars.group-avatar :as group-avatar] [quo2.components.markdown.text :as text] - [quo2.components.avatars.group-avatar :as group-avatar] + [quo2.foundations.colors :as colors] + [quo2.theme :as quo2.theme] [react-native.core :as rn])) -(defn padding-left-for-type [type] +(defn padding-left-for-type + [type] (case type :group-avatar 3 8)) -(defn trim-public-key [pk] +(defn trim-public-key + [pk] (str (subs pk 0 6) "..." (subs pk (- (count pk) 3)))) -(defn base-tag [_ _] +(defn base-tag + [_ _] (fn [{:keys [override-theme style]} & children] (let [theme (or override-theme (quo2.theme/get-theme))] (into [rn/view (merge - {:border-radius 100 + {:border-radius 100 :padding-vertical 3 - :flex-direction :row - :padding-right 8 - :padding-left 8 + :flex-direction :row + :padding-right 8 + :padding-left 8 :background-color (if (= theme :light) colors/neutral-10 - colors/neutral-80)} style)] + colors/neutral-80)} + style)] children)))) -(defn group-avatar-tag [_ _] +(defn group-avatar-tag + [_ _] (fn [label opts] - [base-tag (-> opts - (select-keys [:override-theme :style]) - (assoc-in [:style :padding-left] 3)) + [base-tag + (-> opts + (select-keys [:override-theme :style]) + (assoc-in [:style :padding-left] 3)) [group-avatar/group-avatar opts] - [text/text {:weight :medium - :size :paragraph-2 - :style (:text-style opts)} + [text/text + {:weight :medium + :size :paragraph-2 + :style (:text-style opts)} (str " " label)]])) -(defn public-key-tag [_ _] +(defn public-key-tag + [_ _] (fn [params public-key] [base-tag params - [text/text {:weight :monospace - :size :paragraph-2} + [text/text + {:weight :monospace + :size :paragraph-2} (trim-public-key public-key)]])) -(defn context-tag [params photo name] +(defn context-tag + [params photo name] (let [text-style (params :text-style)] [base-tag (assoc-in params [:style :padding-left] 3) - [rn/image {:style {:width 20 - :border-radius 10 - :background-color :white - :height 20} - :source photo}] + [rn/image + {:style {:width 20 + :border-radius 10 + :background-color :white + :height 20} + :source photo}] [text/text (merge {:weight :medium - :size :paragraph-2} + :size :paragraph-2} {:style text-style}) (str " " name)]])) -(defn user-avatar-tag [] +(defn user-avatar-tag + [] (fn [params username photo] [context-tag params {:uri photo} username])) diff --git a/src/quo2/components/tags/permission_tag.cljs b/src/quo2/components/tags/permission_tag.cljs index f39706a892..25d8aca170 100644 --- a/src/quo2/components/tags/permission_tag.cljs +++ b/src/quo2/components/tags/permission_tag.cljs @@ -1,49 +1,61 @@ (ns quo2.components.tags.permission-tag - (:require [react-native.core :as rn] + (:require [quo2.components.icon :as icons] [quo2.components.markdown.text :as text] + [quo2.components.tags.base-tag :as base-tag] [quo2.foundations.colors :as colors] - [quo2.components.icon :as icons] - [quo2.components.tags.base-tag :as base-tag])) + [react-native.core :as rn])) -(defn outer-resource-container [size background-color] +(defn outer-resource-container + [size background-color] {:background-color background-color :border-radius size :width size :height size - :margin-left (case size 32 -12 24 -8) + :margin-left (case size + 32 -12 + 24 -8) :align-items :center :justify-content :center}) -(defn extra-count-styles [size] +(defn extra-count-styles + [size] {:background-color (colors/theme-colors colors/neutral-20 colors/neutral-70) - :height (case size 32 28 24 20) - :width (case size 32 28 24 20) + :height (case size + 32 28 + 24 20) + :width (case size + 32 28 + 24 20) :border-radius size :justify-content :center :align-items :center}) -(defn extra-count [total-group-count selected-count size background-color] +(defn extra-count + [total-group-count selected-count size background-color] (let [extra-group-count (- total-group-count selected-count)] (when (> extra-group-count 0) [rn/view (outer-resource-container size background-color) [rn/view (extra-count-styles size) (if (< extra-group-count 4) - [text/text {:size :label - :style {:align-items :center - :color (colors/theme-colors - colors/neutral-50 - colors/neutral-40)}} + [text/text + {:size :label + :style {:align-items :center + :color (colors/theme-colors + colors/neutral-50 + colors/neutral-40)}} (str "+" extra-group-count)] - [icons/icon :i/pending-default {:container-style {:align-items :center - :justify-content :center} - :color (colors/theme-colors - colors/neutral-50 - colors/neutral-40) - :size 12}])]]))) + [icons/icon :i/pending-default + {:container-style {:align-items :center + :justify-content :center} + :color (colors/theme-colors + colors/neutral-50 + colors/neutral-40) + :size 12}])]]))) -(defn selected-token-count [group] +(defn selected-token-count + [group] (cond (= (count group) 3) 3 @@ -52,76 +64,104 @@ :else (count group))) -(defn token-group [] +(defn token-group + [] (fn [{:keys [group size last-group background-color] - :or {size 24}}] - (let [tokens-count (count group) + :or {size 24}}] + (let [tokens-count (count group) selected-tokens (take (selected-token-count group) group)] - [rn/view {:flex-direction :row - :align-items :center} + [rn/view + {:flex-direction :row + :align-items :center} (for [{:keys [token-icon]} selected-tokens] ^{:key token-icon} [rn/view {:flex-direction :row} [rn/view (outer-resource-container size background-color) - [rn/image {:source token-icon - :style {:height (case size 32 28 24 20) - :width (case size 32 28 24 20) - :border-radius size}}]]]) + [rn/image + {:source token-icon + :style {:height (case size + 32 28 + 24 20) + :width (case size + 32 28 + 24 20) + :border-radius size}}]]]) [extra-count tokens-count (count selected-tokens) size] (when-not last-group - [rn/view {:align-items :center} - [text/text {:weight :medium - :size (case size 32 :paragraph-2 24 :label) - :style {:color (colors/theme-colors - colors/neutral-50 - colors/neutral-40) - :padding-left 4 - :text-transform :lowercase - :padding-right (case size 32 16 24 12)}} + [rn/view {:align-items :center} + [text/text + {:weight :medium + :size (case size + 32 :paragraph-2 + 24 :label) + :style {:color (colors/theme-colors + colors/neutral-50 + colors/neutral-40) + :padding-left 4 + :text-transform :lowercase + :padding-right (case size + 32 16 + 24 12)}} "or"]])]))) -(defn selected-group-count [tokens] +(defn selected-group-count + [tokens] (cond (> (count tokens) 3) 3 :else (count tokens))) -(defn tag-tokens [] +(defn tag-tokens + [] (fn [{:keys [tokens size background-color] - :or {size 24}}] + :or {size 24}}] (let [selected-groups (take (selected-group-count tokens) tokens) - last-group-id ((last selected-groups) :id)] - [rn/view {:flex-direction :row - :align-items :center} + last-group-id ((last selected-groups) :id)] + [rn/view + {:flex-direction :row + :align-items :center} (for [{:keys [id group]} selected-groups] ^{:key id} - [token-group {:group group - :size size - :last-group (if (= id last-group-id) true false) - :background-color background-color}])]))) + [token-group + {:group group + :size size + :last-group (if (= id last-group-id) true false) + :background-color background-color}])]))) (defn tag [_ _] (fn [{:keys [locked tokens size background-color on-press] - :or {size 24}}] - [base-tag/base-tag {:background-color background-color - :size size - :type :permission - :on-press on-press} - [rn/view {:flex-direction :row - :align-items :center - :justify-content :flex-end} - [rn/view {:padding-left (case 32 8 24 6) - :padding-right (case size 32 16 24 12)} - [icons/icon (if locked :i/locked - :i/unlocked) - {:resize-mode :center - :size (case size 32 20 24 16) - :color (colors/theme-colors - colors/neutral-50 - colors/neutral-40)}]] - [tag-tokens {:tokens tokens - :size size - :background-color background-color}]]])) + :or {size 24}}] + [base-tag/base-tag + {:background-color background-color + :size size + :type :permission + :on-press on-press} + [rn/view + {:flex-direction :row + :align-items :center + :justify-content :flex-end} + [rn/view + {:padding-left (case 32 + 8 24 + 6) + :padding-right (case size + 32 16 + 24 12)} + [icons/icon + (if locked + :i/locked + :i/unlocked) + {:resize-mode :center + :size (case size + 32 20 + 24 16) + :color (colors/theme-colors + colors/neutral-50 + colors/neutral-40)}]] + [tag-tokens + {:tokens tokens + :size size + :background-color background-color}]]])) diff --git a/src/quo2/components/tags/status_tags.cljs b/src/quo2/components/tags/status_tags.cljs index 2eee606906..e2a7b67b87 100644 --- a/src/quo2/components/tags/status_tags.cljs +++ b/src/quo2/components/tags/status_tags.cljs @@ -1,87 +1,97 @@ (ns quo2.components.tags.status-tags - (:require [react-native.core :as rn] - [quo2.theme :as quo2.theme] - [quo2.components.icon :as icon] + (:require [quo2.components.icon :as icon] [quo2.components.markdown.text :as text] - [quo2.foundations.colors :as colors])) + [quo2.foundations.colors :as colors] + [quo2.theme :as quo2.theme] + [react-native.core :as rn])) (def default-container-style {:border-radius 20 - :border-width 1}) + :border-width 1}) (def small-container-style (merge default-container-style {:padding-horizontal 8 - :padding-vertical 3})) + :padding-vertical 3})) (def large-container-style (merge default-container-style {:padding-horizontal 11 - :padding-vertical 4})) + :padding-vertical 4})) -(defn base-tag [_] - (fn [{:keys [size - border-color - background-color - icon - text-color - label]}] +(defn base-tag + [_] + (fn + [{:keys [size + border-color + background-color + icon + text-color + label]}] (let [paragraph-size (if (= size :small) :paragraph-2 :paragraph-1)] [rn/view (assoc (if (= size :small) small-container-style large-container-style) - :border-width 1 - :border-color border-color + :border-width 1 + :border-color border-color :background-color background-color) - [rn/view {:flex-direction :row - :flex 1} - [rn/view {:style {:justify-content :center - :align-items :center}} + [rn/view + {:flex-direction :row + :flex 1} + [rn/view + {:style {:justify-content :center + :align-items :center}} [icon/icon icon {:no-color true - :size 12}]] - [text/text {:size paragraph-size - :weight :medium - :style {:padding-left 5 - :color text-color}} label]]]))) + :size 12}]] + [text/text + {:size paragraph-size + :weight :medium + :style {:padding-left 5 + :color text-color}} label]]]))) (defn- positive [size theme label] - [base-tag {:size size - :background-color colors/success-50-opa-10 - :icon :verified - :border-color colors/success-50-opa-20 - :label label - :text-color (if (= theme :light) colors/success-50 - colors/success-60)}]) + [base-tag + {:size size + :background-color colors/success-50-opa-10 + :icon :verified + :border-color colors/success-50-opa-20 + :label label + :text-color (if (= theme :light) + colors/success-50 + colors/success-60)}]) (defn- negative [size theme label] - [base-tag {:size size - :icon :untrustworthy - :background-color colors/danger-50-opa-10 - :border-color colors/danger-50-opa-20 - :label label - :text-color (if (= theme :light) - colors/danger-50 - colors/danger-60)}]) + [base-tag + {:size size + :icon :untrustworthy + :background-color colors/danger-50-opa-10 + :border-color colors/danger-50-opa-20 + :label label + :text-color (if (= theme :light) + colors/danger-50 + colors/danger-60)}]) (defn- pending [size theme label] - [base-tag {:size size - :icon :pending - :label label - :background-color (if (= theme :light) - colors/neutral-10 - colors/neutral-80) - :border-color (if (= theme :light) - colors/neutral-20 - colors/neutral-70) - :text-color colors/neutral-50}]) + [base-tag + {:size size + :icon :pending + :label label + :background-color (if (= theme :light) + colors/neutral-10 + colors/neutral-80) + :border-color (if (= theme :light) + colors/neutral-20 + colors/neutral-70) + :text-color colors/neutral-50}]) -(defn status-tag [{:keys [status size override-theme label]}] +(defn status-tag + [{:keys [status size override-theme label]}] (when status (when-let [status-component (case (:type status) :positive positive diff --git a/src/quo2/components/tags/tag.cljs b/src/quo2/components/tags/tag.cljs index 0243fa777d..e99663343b 100644 --- a/src/quo2/components/tags/tag.cljs +++ b/src/quo2/components/tags/tag.cljs @@ -1,80 +1,93 @@ (ns quo2.components.tags.tag - (:require [quo2.foundations.colors :as colors] - [quo2.theme :as theme] - [react-native.core :as rn] - [quo2.components.icon :as icons] + (:require [quo2.components.icon :as icons] [quo2.components.markdown.text :as text] - [quo2.components.tags.base-tag :as base-tag])) + [quo2.components.tags.base-tag :as base-tag] + [quo2.foundations.colors :as colors] + [quo2.theme :as theme] + [react-native.core :as rn])) -(def themes {:light {:default {:border-color colors/neutral-20 - :blurred-border-color colors/neutral-80-opa-5 - :text-color {:style {:color colors/neutral-100}}} - :active {:border-color colors/neutral-30 - :blurred-border-color colors/neutral-80-opa-10 - :text-color {:style {:color colors/neutral-100}}} - :disabled {:border-color colors/neutral-20 - :blurred-border-color colors/neutral-80-opa-5 - :text-color {:style {:color colors/neutral-100}}}} - :dark {:default {:border-color colors/neutral-70 - :blurred-border-color colors/white-opa-10 - :text-color {:style {:color colors/white}}} - :active {:border-color colors/neutral-60 - :blurred-border-color colors/white-opa-20 - :text-color {:style {:color colors/white}}} - :disabled {:border-color colors/neutral-70 - :blurred-border-color colors/white-opa-10 - :text-color {:style {:color colors/white}}}}}) +(def themes + {:light {:default {:border-color colors/neutral-20 + :blurred-border-color colors/neutral-80-opa-5 + :text-color {:style {:color colors/neutral-100}}} + :active {:border-color colors/neutral-30 + :blurred-border-color colors/neutral-80-opa-10 + :text-color {:style {:color colors/neutral-100}}} + :disabled {:border-color colors/neutral-20 + :blurred-border-color colors/neutral-80-opa-5 + :text-color {:style {:color colors/neutral-100}}}} + :dark {:default {:border-color colors/neutral-70 + :blurred-border-color colors/white-opa-10 + :text-color {:style {:color colors/white}}} + :active {:border-color colors/neutral-60 + :blurred-border-color colors/white-opa-20 + :text-color {:style {:color colors/white}}} + :disabled {:border-color colors/neutral-70 + :blurred-border-color colors/white-opa-10 + :text-color {:style {:color colors/white}}}}}) -(defn tag-resources [size type resource icon-color label text-color labelled] - [rn/view {:style (merge {:flex-direction :row - :align-items :center - :justify-content :center} - (when label - {:padding-horizontal (case size 32 12 24 8)}))} +(defn tag-resources + [size type resource icon-color label text-color labelled] + [rn/view + {:style (merge {:flex-direction :row + :align-items :center + :justify-content :center} + (when label + {:padding-horizontal (case size + 32 12 + 24 8)}))} (when (= type :icon) - [icons/icon resource {:container-style (when label - {:margin-right 4}) - :resize-mode :center - :size (case size - 32 20 - 24 12) - :color icon-color}]) + [icons/icon resource + {:container-style (when label + {:margin-right 4}) + :resize-mode :center + :size (case size + 32 20 + 24 12) + :color icon-color}]) (when (= type :emoji) - [rn/image {:source resource - :style (merge (case size - 32 {:height 20 - :width 20} - 24 {:height 12 - :width 12}) - (when label - {:margin-right 4}))}]) + [rn/image + {:source resource + :style (merge (case size + 32 {:height 20 + :width 20} + 24 {:height 12 + :width 12}) + (when label + {:margin-right 4}))}]) (when labelled - [text/text (merge {:size (case size - 32 :paragraph-1 - 24 :paragraph-2 - 20 :label nil) - :weight :medium - :number-of-lines 1} - text-color) + [text/text + (merge {:size (case size + 32 :paragraph-1 + 24 :paragraph-2 + 20 :label + nil) + :weight :medium + :number-of-lines 1} + text-color) label])]) (defn tag [_ _] (fn [{:keys [id on-press disabled size resource active accessibility-label - label type labelled blurred icon-color] :or {size 32}}] - (let [state (cond disabled :disabled active :active :else :default) + label type labelled blurred icon-color] + :or {size 32}}] + (let [state (cond disabled :disabled + active :active + :else :default) {:keys [border-color blurred-border-color text-color]} (get-in themes [(theme/get-theme) state])] - [base-tag/base-tag {:id id - :size size - :border-width 1 - :border-color (if blurred - blurred-border-color - border-color) - :on-press on-press - :accessibility-label accessibility-label - :disabled disabled - :type type - :label label} + [base-tag/base-tag + {:id id + :size size + :border-width 1 + :border-color (if blurred + blurred-border-color + border-color) + :on-press on-press + :accessibility-label accessibility-label + :disabled disabled + :type type + :label label} [tag-resources size type resource icon-color label text-color labelled]]))) diff --git a/src/quo2/components/tags/tags.cljs b/src/quo2/components/tags/tags.cljs index 4a5af4bb14..f5fc322d8e 100644 --- a/src/quo2/components/tags/tags.cljs +++ b/src/quo2/components/tags/tags.cljs @@ -1,9 +1,10 @@ (ns quo2.components.tags.tags - (:require [reagent.core :as reagent] + (:require [quo2.components.tags.tag :as tag] [react-native.core :as rn] - [quo2.components.tags.tag :as tag])) + [reagent.core :as reagent])) -(defn tags [{:keys [default-active on-change]}] +(defn tags + [{:keys [default-active on-change]}] (let [active-tab-id (reagent/atom default-active)] (fn [{:keys [data size type labelled disabled blurred icon-color] :or {size 32}}] (let [active-id @active-tab-id] @@ -12,17 +13,17 @@ ^{:key id} [rn/view {:margin-right 8} [tag/tag - (merge {:id id - :size size - :type type - :label (if labelled tag-label (when (= type :label) tag-label)) - :active (= id active-id) - :disabled disabled - :blurred blurred - :icon-color icon-color - :labelled (if (= type :label) true labelled) - :resource (if (= type :icon) - :i/placeholder - resource) - :on-press #(do (reset! active-tab-id %) - (when on-change (on-change %)))})]])])))) + (merge {:id id + :size size + :type type + :label (if labelled tag-label (when (= type :label) tag-label)) + :active (= id active-id) + :disabled disabled + :blurred blurred + :icon-color icon-color + :labelled (if (= type :label) true labelled) + :resource (if (= type :icon) + :i/placeholder + resource) + :on-press #(do (reset! active-tab-id %) + (when on-change (on-change %)))})]])])))) diff --git a/src/quo2/components/tags/token_tag.cljs b/src/quo2/components/tags/token_tag.cljs index 99b0a0521b..c201050a3a 100644 --- a/src/quo2/components/tags/token_tag.cljs +++ b/src/quo2/components/tags/token_tag.cljs @@ -1,31 +1,35 @@ (ns quo2.components.tags.token-tag - (:require [quo2.foundations.colors :as colors] - [react-native.core :as rn] - [quo2.theme :as theme] + (:require [quo2.components.icon :as icons] [quo2.components.markdown.text :as text] - [quo2.components.icon :as icons])) + [quo2.foundations.colors :as colors] + [quo2.theme :as theme] + [react-native.core :as rn])) -(def themes {:light {:background-color colors/neutral-20} - :dark {:background-color colors/neutral-80}}) +(def themes + {:light {:background-color colors/neutral-20} + :dark {:background-color colors/neutral-80}}) -(defn get-value-from-size [size big-option small-option] - (if (= size :big) big-option small-option)) +(defn get-value-from-size + [size big-option small-option] + (if (= size :big) big-option small-option)) (def icon-container-styles - {:display :flex - :align-items :center + {:display :flex + :align-items :center :justify-content :center - :position :absolute - :border-radius 20 - :margin-left 2}) + :position :absolute + :border-radius 20 + :margin-left 2}) -(defn tag-container [size] - {:height (get-value-from-size size 32 24) - :align-items :center +(defn tag-container + [size] + {:height (get-value-from-size size 32 24) + :align-items :center :flex-direction :row - :border-radius 20}) + :border-radius 20}) -(defn tag "[tag opts \"label\"]] +(defn tag + "[tag opts \"label\"]] opts { :size :small/:big @@ -36,27 +40,28 @@ }" [_ _] (fn [{:keys [size token-img-src token-img-style border-color overlay] - :or {size :small}} label] + :or {size :small}} label] [rn/view {:style (when border-color - {:border-color border-color + {:border-color border-color :border-radius 20 - :border-width 1})} + :border-width 1})} [rn/view - {:style (merge (tag-container size) (get themes (theme/get-theme)))} + {:style (merge (tag-container size) (get themes (theme/get-theme)))} [rn/image {:source token-img-src - :style (merge - {:height (get-value-from-size size 28 20) - :width (get-value-from-size size 28 20) - :margin-left 2 - :margin-right (get-value-from-size size 8 6)} token-img-style)}] + :style (merge + {:height (get-value-from-size size 28 20) + :width (get-value-from-size size 28 20) + :margin-left 2 + :margin-right (get-value-from-size size 8 6)} + token-img-style)}] [text/text - {:weight :medium + {:weight :medium :number-of-lines 1 :style {:margin-right (get-value-from-size size 12 11)} - :size (get-value-from-size size :paragraph-2 :label)} label] + :size (get-value-from-size size :paragraph-2 :label)} label] overlay]])) (defn token-tag @@ -76,19 +81,21 @@ :or {size :small border-color (colors/custom-color-by-theme :purple 50 60)}}] - [tag {:size size - :token-img-src token-img-src - :border-color (when is-required border-color) - :overlay - (when (or is-required is-purchasable) - [rn/view - {:style (merge icon-container-styles - {:background-color border-color - :border-color (if (= (theme/get-theme) :dark) colors/neutral-100 colors/white) - :border-width 1 - :right (get-value-from-size size -3.75 -5.75) - :bottom (get-value-from-size size (- 32 7.75 4) (- 24 7.75 2))})} - [icons/icon (if is-required :i/hold :i/add) - {:no-color true - :size 12}]])} + [tag + {:size size + :token-img-src token-img-src + :border-color (when is-required border-color) + :overlay + (when (or is-required is-purchasable) + [rn/view + {:style (merge + icon-container-styles + {:background-color border-color + :border-color (if (= (theme/get-theme) :dark) colors/neutral-100 colors/white) + :border-width 1 + :right (get-value-from-size size -3.75 -5.75) + :bottom (get-value-from-size size (- 32 7.75 4) (- 24 7.75 2))})} + [icons/icon (if is-required :i/hold :i/add) + {:no-color true + :size 12}]])} (str value " " token)])) diff --git a/src/quo2/components/wallet/lowest_price.cljs b/src/quo2/components/wallet/lowest_price.cljs index f29c46c99e..b09b3b05db 100644 --- a/src/quo2/components/wallet/lowest_price.cljs +++ b/src/quo2/components/wallet/lowest_price.cljs @@ -1,12 +1,14 @@ (ns quo2.components.wallet.lowest-price - (:require [react-native.core :as rn] - [clojure.string :as string])) + (:require [clojure.string :as string] + [react-native.core :as rn])) -(def centrify {:style {:flex-direction :row - :justify-content :center - :align-items :center}}) +(def centrify + {:style {:flex-direction :row + :justify-content :center + :align-items :center}}) -(defn border-alignment [value-bg-color] +(defn border-alignment + [value-bg-color] {:style {:align-self :flex-start :padding-horizontal 16 :padding-vertical 2 @@ -34,18 +36,21 @@ (defn dots-comp "Returns the dots adding their styles" [few-of-dots?] - [rn/view {:style {:align-items :center - :bottom 5}} + [rn/view + {:style {:align-items :center + :bottom 5}} [rn/text line-dots-props (dots few-of-dots?)]]) (defn lowest-price-value-comp "Component responsible for the top and bottom values" [top-value top-value-bg-color top-value-text-color] [rn/view (border-alignment top-value-bg-color) - [rn/text {:style {:color top-value-text-color - :text-align :center}} top-value]]) + [rn/text + {:style {:color top-value-text-color + :text-align :center}} top-value]]) -(defn lowest-price-styles [margin-top] +(defn lowest-price-styles + [margin-top] {:style {:flex-direction :column :width "100%" :overflow :hidden diff --git a/src/quo2/components/wallet/network_amount.cljs b/src/quo2/components/wallet/network_amount.cljs index 7d4c5c7d9d..9b87f604ec 100644 --- a/src/quo2/components/wallet/network_amount.cljs +++ b/src/quo2/components/wallet/network_amount.cljs @@ -1,8 +1,8 @@ (ns quo2.components.wallet.network-amount - (:require [react-native.core :as rn] - [quo2.components.icon :as icon] + (:require [quo2.components.icon :as icon] [quo2.components.markdown.text :as text] - [quo2.foundations.colors :as colors])) + [quo2.foundations.colors :as colors] + [react-native.core :as rn])) (defn network-amount "[network-amount opts] @@ -12,34 +12,42 @@ :icon :i/ethereum ;; key of icon belonging to the network :eth-value 1.2345678 ;; shown ETH value}" [{:keys [show-right-border? style network-name icon eth-value labels] :as _opts}] - [rn/view (merge {:accessibility-label :network-amount - :background-color (colors/theme-colors colors/white colors/neutral-95) - :border-radius 16 - :padding 6} - style) - [rn/view {:style {:flex-direction :row - :align-items :center}} - [rn/view {:flex-direction :column - :align-self :flex-start - :padding-top 3 - :padding-right 4} - [icon/icon icon {:size 40 - :no-color true - :container-style {:width 12 - :height 12}}]] + [rn/view + (merge {:accessibility-label :network-amount + :background-color (colors/theme-colors colors/white colors/neutral-95) + :border-radius 16 + :padding 6} + style) + [rn/view + {:style {:flex-direction :row + :align-items :center}} [rn/view - [rn/view {:style {:flex-direction :row - :align-items :center}} - [text/text {:weight :medium - :size :paragraph-2 - :style {:color (colors/theme-colors colors/neutral-100 colors/white)}} + {:flex-direction :column + :align-self :flex-start + :padding-top 3 + :padding-right 4} + [icon/icon icon + {:size 40 + :no-color true + :container-style {:width 12 + :height 12}}]] + [rn/view + [rn/view + {:style {:flex-direction :row + :align-items :center}} + [text/text + {:weight :medium + :size :paragraph-2 + :style {:color (colors/theme-colors colors/neutral-100 colors/white)}} eth-value \space (:eth labels)] - [rn/view {:style {:border-right-width (when show-right-border? 1) - :border-right-color (colors/theme-colors colors/neutral-40 colors/neutral-50) - :padding-left 8 - :align-self :center - :height 8}}]] - [text/text {:weight :medium - :size :label - :style {:color colors/neutral-50}} + [rn/view + {:style {:border-right-width (when show-right-border? 1) + :border-right-color (colors/theme-colors colors/neutral-40 colors/neutral-50) + :padding-left 8 + :align-self :center + :height 8}}]] + [text/text + {:weight :medium + :size :label + :style {:color colors/neutral-50}} (:on labels) \space network-name]]]]) diff --git a/src/quo2/components/wallet/network_breakdown.cljs b/src/quo2/components/wallet/network_breakdown.cljs index 2cd59ffa4c..324c45a14d 100644 --- a/src/quo2/components/wallet/network_breakdown.cljs +++ b/src/quo2/components/wallet/network_breakdown.cljs @@ -1,46 +1,52 @@ (ns quo2.components.wallet.network-breakdown - (:require [react-native.core :as rn] + (:require [quo2.components.markdown.text :as text] + [quo2.components.wallet.network-amount :refer [network-amount]] [quo2.foundations.colors :as colors] - [quo2.components.markdown.text :as text] - [quo2.components.wallet.network-amount :refer [network-amount]])) + [react-native.core :as rn])) (defn network-breakdown [{:keys [top-value network-conversions labels]}] - [rn/view {:style {:background-color (colors/theme-colors - colors/white - colors/neutral-95) - :padding-horizontal 40 - :padding-top 24 - :border-radius 16 - :height 126 - :width 390 - :overflow :hidden}} - [rn/view {:style {:border-bottom-width 1 - :border-bottom-color (colors/theme-colors - colors/neutral-20 - colors/neutral-70) - :padding-vertical 6}} - [text/text {:weight :medium - :style {:color (colors/theme-colors - colors/neutral-100 - colors/white) - :font-size 19}} + [rn/view + {:style {:background-color (colors/theme-colors + colors/white + colors/neutral-95) + :padding-horizontal 40 + :padding-top 24 + :border-radius 16 + :height 126 + :width 390 + :overflow :hidden}} + [rn/view + {:style {:border-bottom-width 1 + :border-bottom-color (colors/theme-colors + colors/neutral-20 + colors/neutral-70) + :padding-vertical 6}} + [text/text + {:weight :medium + :style {:color (colors/theme-colors + colors/neutral-100 + colors/white) + :font-size 19}} (str top-value)]] - [rn/scroll-view {:horizontal true - :style {:text-align :center - :margin-top 6 - :width 340}} + [rn/scroll-view + {:horizontal true + :style {:text-align :center + :margin-top 6 + :width 340}} (let [last-item-idx (-> network-conversions count dec)] (map-indexed (fn [idx {:keys [conversion network icon]}] - [rn/view {:style (cond-> {:flex-direction :row} - (not= idx 0) (assoc :margin-left -4)) - :key idx} - [network-amount {:show-right-border? (not= idx last-item-idx) - :icon icon - :network-name network - :eth-value conversion - :labels labels}]]) + [rn/view + {:style (cond-> {:flex-direction :row} + (not= idx 0) (assoc :margin-left -4)) + :key idx} + [network-amount + {:show-right-border? (not= idx last-item-idx) + :icon icon + :network-name network + :eth-value conversion + :labels labels}]]) network-conversions))]]) diff --git a/src/quo2/components/wallet/token_overview.cljs b/src/quo2/components/wallet/token_overview.cljs index 8f6f7608a0..ba8ea3b9a0 100644 --- a/src/quo2/components/wallet/token_overview.cljs +++ b/src/quo2/components/wallet/token_overview.cljs @@ -1,34 +1,45 @@ (ns quo2.components.wallet.token-overview - (:require - [quo2.foundations.colors :as colors] - [react-native.core :as rn] - [clojure.string :as string] - [quo2.components.markdown.text :as text] - [quo2.components.icon :as icons])) + (:require [clojure.string :as string] + [quo2.components.icon :as icons] + [quo2.components.markdown.text :as text] + [quo2.foundations.colors :as colors] + [react-native.core :as rn])) -(def container-style {:display :flex :width "100%" :padding-left 20 :padding-right 20 :padding-top 12 :padding-bottom 12}) +(def container-style + {:display :flex :width "100%" :padding-left 20 :padding-right 20 :padding-top 12 :padding-bottom 12}) -(defn price-direction [price-change increase decrease neutral] +(defn price-direction + [price-change increase decrease neutral] (cond (pos? price-change) increase (neg? price-change) decrease - :else neutral)) + :else neutral)) -(defn price-color [direction] +(defn price-color + [direction] (price-direction direction colors/success-50 colors/danger-50 colors/neutral-50)) -(defn divider [direction] - [rn/view {:style {:height 10 - :margin-left 4 - :margin-right 4 - :width 1 - :background-color (price-direction direction colors/success-50-opa-40 colors/danger-50-opa-40 colors/divider-light)}}]) +(defn divider + [direction] + [rn/view + {:style {:height 10 + :margin-left 4 + :margin-right 4 + :width 1 + :background-color (price-direction direction + colors/success-50-opa-40 + colors/danger-50-opa-40 + colors/divider-light)}}]) -(defn get-direction [percentage-change] - (if (zero? (js/parseInt percentage-change)) 0 - (/ (js/parseInt percentage-change) (js/Math.abs (js/parseInt percentage-change))))) +(defn get-direction + [percentage-change] + (if (zero? (js/parseInt percentage-change)) + 0 + (/ (js/parseInt percentage-change) (js/Math.abs (js/parseInt percentage-change))))) -(defn trim-minus [percentage-change] (if (= (first percentage-change) "-") (string/join (rest percentage-change)) percentage-change)) +(defn trim-minus + [percentage-change] + (if (= (first percentage-change) "-") (string/join (rest percentage-change)) percentage-change)) (defn token-price "[token-price opts \"label\"] @@ -43,23 +54,27 @@ [{:keys [currency price percentage-change label] :or {price "0.00" percentage-change "0.0"}}] (let [direction (get-direction percentage-change)] [rn/view {:style container-style} - [text/text {:number-of-lines 1 - :size :paragraph-2} label] - [text/text {:style {:margin-top 4} - :weight :semi-bold - :number-of-lines 1 - :size :heading-2} (str currency price)] + [text/text + {:number-of-lines 1 + :size :paragraph-2} label] + [text/text + {:style {:margin-top 4} + :weight :semi-bold + :number-of-lines 1 + :size :heading-2} (str currency price)] [rn/view {:style {:display :flex :flex-direction :row :margin-top 6 :align-items :center}} - (when (not (zero? direction)) [icons/icon (if (>= direction 0) :i/price-increase12 :i/price-decrease12) - {:no-color true - :width 14 - :height 14 - :container-style {:margin-right 4}}]) - [text/text {:number-of-lines 1 - :weight :medium - :size :paragraph-2 - :style {:color (price-color direction)}} + (when (not (zero? direction)) + [icons/icon (if (>= direction 0) :i/price-increase12 :i/price-decrease12) + {:no-color true + :width 14 + :height 14 + :container-style {:margin-right 4}}]) + [text/text + {:number-of-lines 1 + :weight :medium + :size :paragraph-2 + :style {:color (price-color direction)}} (str (trim-minus percentage-change) "%")]]]))) (defn token-balance @@ -74,31 +89,39 @@ :percentage-change :string }" [] - (fn [{:keys [token token-img-src currency account-balance price percentage-change] :or {account-balance "0.00" price "0.00" percentage-change "0.0"}}] + (fn [{:keys [token token-img-src currency account-balance price percentage-change] + :or {account-balance "0.00" price "0.00" percentage-change "0.0"}}] (let [direction (get-direction percentage-change)] [rn/view {:style container-style} - [text/text {:weight :regular - :number-of-lines 1 - :size :paragraph-1} (str "Account " token " Balance")] + [text/text + {:weight :regular + :number-of-lines 1 + :size :paragraph-1} (str "Account " token " Balance")] [rn/view {:style {:display :flex :flex-direction :row :flex 1 :justify-content :space-between}} - [text/text {:number-of-lines 1 - :weight :semi-bold - :size :heading-1} (str currency account-balance)] - [rn/image {:source token-img-src - :style {:height 32 - :width 32}}]] + [text/text + {:number-of-lines 1 + :weight :semi-bold + :size :heading-1} (str currency account-balance)] + [rn/image + {:source token-img-src + :style {:height 32 + :width 32}}]] [rn/view {:style {:display :flex :flex-direction :row :margin-top 6 :align-items :center}} - (when (not (zero? direction)) [icons/icon (if (pos? direction) :i/price-increase :i/price-decrease) - {:no-color true - :size 12 - :container-style {:margin-right 4}}]) - [text/text {:number-of-lines 1 - :weight :medium - :size :paragraph-2 - :style {:color (price-color direction)}} (str currency price)] + (when (not (zero? direction)) + [icons/icon (if (pos? direction) :i/price-increase :i/price-decrease) + {:no-color true + :size 12 + :container-style {:margin-right 4}}]) + [text/text + {:number-of-lines 1 + :weight :medium + :size :paragraph-2 + :style {:color (price-color direction)}} (str currency price)] [divider direction] - [text/text {:number-of-lines 1 - :weight :medium - :size :paragraph-2 - :style {:color (price-color direction)}} (str (trim-minus percentage-change) "%")]]]))) + [text/text + {:number-of-lines 1 + :weight :medium + :size :paragraph-2 + :style {:color (price-color direction)}} + (str (trim-minus percentage-change) "%")]]]))) diff --git a/src/quo2/core.cljs b/src/quo2/core.cljs index 8e742dc27a..4c54ac7f6d 100644 --- a/src/quo2/core.cljs +++ b/src/quo2/core.cljs @@ -1,51 +1,52 @@ (ns quo2.core - (:require quo2.components.buttons.button - quo2.components.buttons.dynamic-button - quo2.components.markdown.text - quo2.components.icon - quo2.components.separator - quo2.components.header - quo2.components.avatars.account-avatar - quo2.components.avatars.channel-avatar - quo2.components.avatars.group-avatar - quo2.components.avatars.icon-avatar - quo2.components.avatars.user-avatar - quo2.components.avatars.wallet-user-avatar - quo2.components.community.community-card-view - quo2.components.community.community-list-view - quo2.components.community.community-view - quo2.components.community.discover-card - quo2.components.community.token-gating - quo2.components.counter.counter - quo2.components.dividers.divider-label - quo2.components.dividers.date - quo2.components.dividers.new-messages - quo2.components.drawers.action-drawers - quo2.components.dropdowns.dropdown - quo2.components.info.info-message - quo2.components.info.information-box - quo2.components.list-items.channel - quo2.components.list-items.menu-item - quo2.components.list-items.preview-list - quo2.components.messages.gap - quo2.components.messages.system-message - quo2.components.reactions.reaction - quo2.components.notifications.activity-log.view - quo2.components.notifications.info-count - quo2.components.notifications.notification-dot - quo2.components.tags.tags - quo2.components.tags.context-tags - quo2.components.tabs.tabs - quo2.components.notifications.toast - quo2.components.tabs.account-selector - quo2.components.navigation.floating-shell-button - quo2.components.tags.status-tags - quo2.components.navigation.page-nav - quo2.components.selectors.disclaimer - quo2.components.selectors.selectors - quo2.components.settings.privacy-option - quo2.components.loaders.skeleton - quo2.components.notifications.count-down-circle)) + (:require + quo2.components.avatars.account-avatar + quo2.components.avatars.channel-avatar + quo2.components.avatars.group-avatar + quo2.components.avatars.icon-avatar + quo2.components.avatars.user-avatar + quo2.components.avatars.wallet-user-avatar + quo2.components.buttons.button + quo2.components.buttons.dynamic-button + quo2.components.community.community-card-view + quo2.components.community.community-list-view + quo2.components.community.community-view + quo2.components.community.discover-card + quo2.components.community.token-gating + quo2.components.counter.counter + quo2.components.dividers.date + quo2.components.dividers.divider-label + quo2.components.dividers.new-messages + quo2.components.drawers.action-drawers + quo2.components.dropdowns.dropdown + quo2.components.header + quo2.components.icon + quo2.components.info.info-message + quo2.components.info.information-box + quo2.components.list-items.channel + quo2.components.list-items.menu-item + quo2.components.list-items.preview-list + quo2.components.loaders.skeleton + quo2.components.markdown.text + quo2.components.messages.gap + quo2.components.messages.system-message + quo2.components.navigation.floating-shell-button + quo2.components.navigation.page-nav + quo2.components.notifications.activity-log.view + quo2.components.notifications.count-down-circle + quo2.components.notifications.info-count + quo2.components.notifications.notification-dot + quo2.components.notifications.toast + quo2.components.reactions.reaction + quo2.components.selectors.disclaimer + quo2.components.selectors.selectors + quo2.components.separator + quo2.components.settings.privacy-option + quo2.components.tabs.account-selector + quo2.components.tabs.tabs + quo2.components.tags.context-tags + quo2.components.tags.status-tags + quo2.components.tags.tags)) (def toast quo2.components.notifications.toast/toast) (def button quo2.components.buttons.button/button) @@ -87,7 +88,8 @@ ;;;; COMMUNITY (def community-card-view-item quo2.components.community.community-card-view/community-card-view-item) (def communities-list-view-item quo2.components.community.community-list-view/communities-list-view-item) -(def communities-membership-list-item quo2.components.community.community-list-view/communities-membership-list-item) +(def communities-membership-list-item + quo2.components.community.community-list-view/communities-membership-list-item) (def community-stats-column quo2.components.community.community-view/community-stats-column) (def community-stats quo2.components.community.community-view/community-stats) (def community-tags quo2.components.community.community-view/community-tags) diff --git a/src/quo2/core_spec.cljs b/src/quo2/core_spec.cljs index ddab67d566..a3197966fc 100644 --- a/src/quo2/core_spec.cljs +++ b/src/quo2/core_spec.cljs @@ -1,4 +1,4 @@ (ns quo2.core-spec - (:require [quo2.components.selectors.--tests--.selectors-component-spec] - [quo2.components.counter.--tests--.counter-component-spec] - [quo2.components.drawers.--tests--.action-drawers-component-spec])) + (:require [quo2.components.counter.--tests--.counter-component-spec] + [quo2.components.drawers.--tests--.action-drawers-component-spec] + [quo2.components.selectors.--tests--.selectors-component-spec])) diff --git a/src/quo2/foundations/colors.cljs b/src/quo2/foundations/colors.cljs index b92545e970..db550317b5 100644 --- a/src/quo2/foundations/colors.cljs +++ b/src/quo2/foundations/colors.cljs @@ -2,7 +2,8 @@ (:require [clojure.string :as string] [quo2.theme :as theme])) -(defn alpha [value opacity] +(defn alpha + [value opacity] (when value (if (string/starts-with? value "#") (let [hex (string/replace value #"#" "") @@ -13,7 +14,8 @@ (let [rgb (string/split value #",")] (str (string/join "," (butlast rgb)) "," opacity ")"))))) -(defn- alpha-opaque [value opacity] +(defn- alpha-opaque + [value opacity] (when value (if (string/starts-with? value "#") (let [hex (string/replace value #"#" "") @@ -57,11 +59,11 @@ (def neutral-100 "#09101C") ;;Blur -(def neutral-5-opa-70 (alpha neutral-5 0.7)) +(def neutral-5-opa-70 (alpha neutral-5 0.7)) (def neutral-90-opa-70 (alpha neutral-90 0.7)) ;;80 with transparency -(def neutral-80-opa-5 (alpha neutral-80 0.05)) +(def neutral-80-opa-5 (alpha neutral-80 0.05)) (def neutral-80-opa-10 (alpha neutral-80 0.1)) (def neutral-80-opa-15 (alpha neutral-80 0.15)) (def neutral-80-opa-20 (alpha neutral-80 0.2)) @@ -190,10 +192,12 @@ :beige {50 "#CAAE93" 60 "#AA927C"}}) -(def colors-map (merge {:danger {50 danger-50 - 60 danger-60} - :success {50 success-50 - 60 success-60}} customization)) +(def colors-map + (merge {:danger {50 danger-50 + 60 danger-60} + :success {50 success-50 + 60 success-60}} + customization)) (def custom-color "(custom-color color suffix opacity) @@ -222,7 +226,7 @@ (custom-color color suffix-dark opacity-dark) (custom-color color suffix-light opacity-light)))) -(def shadow "rgba(9,16,28,0.04)") +(def shadow "rgba(9,16,28,0.04)") ;;General diff --git a/src/quo2/foundations/typography.cljs b/src/quo2/foundations/typography.cljs index da26e0b16a..a251560afb 100644 --- a/src/quo2/foundations/typography.cljs +++ b/src/quo2/foundations/typography.cljs @@ -16,25 +16,30 @@ (def tracking (memoize tracking-fn)) -(def heading-1 {:font-size 27 - :line-height 32 - :letter-spacing (tracking 27)}) +(def heading-1 + {:font-size 27 + :line-height 32 + :letter-spacing (tracking 27)}) -(def heading-2 {:font-size 19 - :line-height 25.65 - :letter-spacing (tracking 19)}) +(def heading-2 + {:font-size 19 + :line-height 25.65 + :letter-spacing (tracking 19)}) -(def paragraph-1 {:font-size 15 - :line-height 21.75 - :letter-spacing (tracking 15)}) +(def paragraph-1 + {:font-size 15 + :line-height 21.75 + :letter-spacing (tracking 15)}) -(def paragraph-2 {:font-size 13 - :line-height 18.2 - :letter-spacing (tracking 13)}) +(def paragraph-2 + {:font-size 13 + :line-height 18.2 + :letter-spacing (tracking 13)}) -(def label {:font-size 11 - :line-height 15.62 - :letter-spacing (tracking 11)}) +(def label + {:font-size 11 + :line-height 15.62 + :letter-spacing (tracking 11)}) (def font-regular {:font-family "Inter-Regular"}) ; 400 diff --git a/src/quo2/theme.cljs b/src/quo2/theme.cljs index 3ee34565d8..e1109e7169 100644 --- a/src/quo2/theme.cljs +++ b/src/quo2/theme.cljs @@ -3,11 +3,14 @@ (def theme (reagent/atom :light)) -(defn dark? [] +(defn dark? + [] (= :dark @theme)) -(defn get-theme [] +(defn get-theme + [] @theme) -(defn set-theme [value] +(defn set-theme + [value] (reset! theme value)) \ No newline at end of file diff --git a/src/react_native/background_timer.cljs b/src/react_native/background_timer.cljs index a0ea87bf7b..75b129b9de 100644 --- a/src/react_native/background_timer.cljs +++ b/src/react_native/background_timer.cljs @@ -1,14 +1,18 @@ (ns react-native.background-timer (:require ["react-native-background-timer" :default background-timer])) -(defn set-timeout [cb ms] +(defn set-timeout + [cb ms] (.setTimeout background-timer cb ms)) -(defn clear-timeout [id] +(defn clear-timeout + [id] (.clearTimeout background-timer id)) -(defn set-interval [cb ms] +(defn set-interval + [cb ms] (.setInterval background-timer cb ms)) -(defn clear-interval [id] +(defn clear-interval + [id] (.clearInterval background-timer id)) diff --git a/src/react_native/core.cljs b/src/react_native/core.cljs index 945ba5b4f2..261ee5aeb3 100644 --- a/src/react_native/core.cljs +++ b/src/react_native/core.cljs @@ -1,13 +1,13 @@ (ns react-native.core - (:require ["react" :as react] + (:require ["@react-native-community/blur" :as blur] + ["react" :as react] ["react-native" :as react-native] [cljs-bean.core :as bean] - ["@react-native-community/blur" :as blur] [oops.core :as oops] [react-native.flat-list :as flat-list] + [react-native.platform :as platform] [react-native.section-list :as section-list] - [reagent.core :as reagent] - [react-native.platform :as platform])) + [reagent.core :as reagent])) (def app-state ^js (.-AppState ^js react-native)) (def blur-view (reagent/adapt-react-class (.-BlurView blur))) @@ -20,7 +20,8 @@ (def touchable-opacity (reagent/adapt-react-class (.-TouchableOpacity ^js react-native))) (def touchable-highlight (reagent/adapt-react-class (.-TouchableHighlight ^js react-native))) -(def touchable-without-feedback (reagent/adapt-react-class (.-TouchableWithoutFeedback ^js react-native))) +(def touchable-without-feedback + (reagent/adapt-react-class (.-TouchableWithoutFeedback ^js react-native))) (def flat-list flat-list/flat-list) @@ -34,44 +35,54 @@ (def dismiss-keyboard! #(.dismiss keyboard)) -(defn use-window-dimensions [] +(defn use-window-dimensions + [] (let [window ^js (react-native/useWindowDimensions)] {:font-scale (.-fontScale window) :height (.-height window) :scale (.-scale window) :width (.-width window)})) -(defn hide-splash-screen [] +(defn hide-splash-screen + [] (.hide ^js (-> react-native .-NativeModules .-SplashScreen))) -(defn alert [title message buttons options] +(defn alert + [title message buttons options] (.alert (.-Alert ^js react-native) title message (clj->js buttons) (clj->js options))) (def appearance ^js (.-Appearance ^js react-native)) -(defn get-color-scheme [] +(defn get-color-scheme + [] (.getColorScheme appearance)) -(defn appearance-add-change-listener [handler] +(defn appearance-add-change-listener + [handler] (.addChangeListener appearance handler)) -(defn get-window [] +(defn get-window + [] (js->clj (.get (.-Dimensions ^js react-native) "window") :keywordize-keys true)) (def status-bar (.-StatusBar ^js react-native)) -(defn status-bar-height [] +(defn status-bar-height + [] (.-currentHeight ^js status-bar)) -(defn hw-back-add-listener [callback] +(defn hw-back-add-listener + [callback] (.addEventListener (.-BackHandler ^js react-native) "hardwareBackPress" callback)) -(defn hw-back-remove-listener [callback] +(defn hw-back-remove-listener + [callback] (.removeEventListener (.-BackHandler ^js react-native) "hardwareBackPress" callback)) (def keyboard-avoiding-view-class (reagent/adapt-react-class (.-KeyboardAvoidingView react-native))) -(defn keyboard-avoiding-view [props & children] +(defn keyboard-avoiding-view + [props & children] (into [keyboard-avoiding-view-class (merge (when platform/ios? {:behavior :padding}) props)] @@ -80,11 +91,12 @@ (defn use-effect ([effect] (use-effect effect [])) ([effect deps] - (react/useEffect effect (bean/->js deps)))) + (react/useEffect effect (bean/->js deps)))) (def use-ref react/useRef) (defn use-effect-once [effect] (use-effect effect)) -(defn use-unmount [f] +(defn use-unmount + [f] (let [fn-ref (use-ref f)] (oops/oset! fn-ref "current" f) (use-effect-once (fn [] #((oops/oget fn-ref "current")))))) diff --git a/src/react_native/fast_image.cljs b/src/react_native/fast_image.cljs index 378d2f798c..3f86b501bd 100644 --- a/src/react_native/fast_image.cljs +++ b/src/react_native/fast_image.cljs @@ -5,25 +5,28 @@ (def fast-image-class (reagent/adapt-react-class ^js FastImage)) -(defn placeholder [style child] +(defn placeholder + [style child] [rn/view {:style (merge style {:flex 1 :justify-content :center :align-items :center})} child]) -(defn fast-image [_] +(defn fast-image + [_] (let [loaded? (reagent/atom false) - error? (reagent/atom false)] + error? (reagent/atom false)] (fn [props] - [fast-image-class (merge - props - {:on-error (fn [e] - (when-let [on-error (:on-error props)] - (on-error e)) - (reset! error? true)) - :on-load (fn [e] - (when-let [on-load (:on-load props)] - (on-load e)) - (reset! loaded? true) - (reset! error? false))}) + [fast-image-class + (merge + props + {:on-error (fn [e] + (when-let [on-error (:on-error props)] + (on-error e)) + (reset! error? true)) + :on-load (fn [e] + (when-let [on-load (:on-load props)] + (on-load e)) + (reset! loaded? true) + (reset! error? false))}) (when (or @error? (not @loaded?)) [placeholder (:style props) (if @error? diff --git a/src/react_native/flat_list.cljs b/src/react_native/flat_list.cljs index 67a368f08e..78192d582b 100644 --- a/src/react_native/flat_list.cljs +++ b/src/react_native/flat_list.cljs @@ -1,35 +1,41 @@ (ns react-native.flat-list - (:require [reagent.core :as reagent] - ["react-native" :as react-native])) + (:require ["react-native" :as react-native] + [reagent.core :as reagent])) (def react-native-flat-list (reagent/adapt-react-class (.-FlatList ^js react-native))) -(defn- wrap-render-fn [f render-data] +(defn- wrap-render-fn + [f render-data] (fn [data] (reagent/as-element [f (.-item ^js data) (.-index ^js data) (.-separators ^js data) render-data (.-isActive ^js data) (.-drag ^js data)]))) -(defn- wrap-on-drag-end-fn [f] +(defn- wrap-on-drag-end-fn + [f] (fn [data] (f (.-from ^js data) (.-to ^js data) (.-data ^js data)))) -(defn- wrap-key-fn [f] +(defn- wrap-key-fn + [f] (fn [data index] {:post [(some? %)]} (f data index))) (defn base-list-props - [{:keys [key-fn render-fn empty-component header footer separator data render-data on-drag-end-fn] :as props}] - (merge {:data (to-array data)} - (when key-fn {:keyExtractor (wrap-key-fn key-fn)}) - (when render-fn {:renderItem (wrap-render-fn render-fn render-data)}) - (when separator {:ItemSeparatorComponent (fn [] (reagent/as-element separator))}) - (when empty-component {:ListEmptyComponent (fn [] (reagent/as-element empty-component))}) - (when header {:ListHeaderComponent (reagent/as-element header)}) - (when footer {:ListFooterComponent (reagent/as-element footer)}) - (when on-drag-end-fn {:onDragEnd (wrap-on-drag-end-fn on-drag-end-fn)}) - (dissoc props :data :header :footer :empty-component :separator :render-fn :key-fn :on-drag-end-fn))) + [{:keys [key-fn render-fn empty-component header footer separator data render-data on-drag-end-fn] + :as props}] + (merge + {:data (to-array data)} + (when key-fn {:keyExtractor (wrap-key-fn key-fn)}) + (when render-fn {:renderItem (wrap-render-fn render-fn render-data)}) + (when separator {:ItemSeparatorComponent (fn [] (reagent/as-element separator))}) + (when empty-component {:ListEmptyComponent (fn [] (reagent/as-element empty-component))}) + (when header {:ListHeaderComponent (reagent/as-element header)}) + (when footer {:ListFooterComponent (reagent/as-element footer)}) + (when on-drag-end-fn {:onDragEnd (wrap-on-drag-end-fn on-drag-end-fn)}) + (dissoc props :data :header :footer :empty-component :separator :render-fn :key-fn :on-drag-end-fn))) -(defn flat-list [props] +(defn flat-list + [props] [react-native-flat-list (base-list-props props)]) diff --git a/src/react_native/hole_view.cljs b/src/react_native/hole_view.cljs index eb60711d20..89c0053304 100644 --- a/src/react_native/hole_view.cljs +++ b/src/react_native/hole_view.cljs @@ -1,5 +1,5 @@ (ns react-native.hole-view - (:require [reagent.core :as reagent] - ["react-native-hole-view" :refer (RNHoleView)])) + (:require ["react-native-hole-view" :refer (RNHoleView)] + [reagent.core :as reagent])) (def hole-view (reagent/adapt-react-class RNHoleView)) diff --git a/src/react_native/languages.cljs b/src/react_native/languages.cljs index c291dabdc9..fd9d0e9f0a 100644 --- a/src/react_native/languages.cljs +++ b/src/react_native/languages.cljs @@ -1,8 +1,10 @@ (ns react-native.languages (:require ["react-native-languages" :default react-native-languages])) -(defn add-change-listener [handler] +(defn add-change-listener + [handler] (.addEventListener ^js react-native-languages "change" (fn [^js event] (handler (.-language event))))) -(defn get-lang-keyword [] +(defn get-lang-keyword + [] (keyword (.-language ^js react-native-languages))) diff --git a/src/react_native/mail.cljs b/src/react_native/mail.cljs index 62d1f86e95..6fe44f8378 100644 --- a/src/react_native/mail.cljs +++ b/src/react_native/mail.cljs @@ -1,5 +1,6 @@ (ns react-native.mail (:require ["react-native-mail" :default react-native-mail])) -(defn mail [opts callback] +(defn mail + [opts callback] (.mail react-native-mail (clj->js opts) callback)) diff --git a/src/react_native/navigation.cljs b/src/react_native/navigation.cljs index 9c756455b3..8d86e574f3 100644 --- a/src/react_native/navigation.cljs +++ b/src/react_native/navigation.cljs @@ -2,60 +2,74 @@ (:refer-clojure :exclude [pop]) (:require ["react-native-navigation" :refer (Navigation)])) -(defn set-default-options [opts] +(defn set-default-options + [opts] (.setDefaultOptions ^js Navigation (clj->js opts))) (defn register-component [arg1 arg2 arg3] (.registerComponent ^js Navigation arg1 arg2 arg3)) (defn set-lazy-component-registrator [handler] (.setLazyComponentRegistrator ^js Navigation handler)) -(defn set-root [root] +(defn set-root + [root] (.setRoot ^js Navigation (clj->js root))) -(defn set-stack-root [stack comp] +(defn set-stack-root + [stack comp] (.setStackRoot ^js Navigation stack (clj->js comp))) -(defn push [arg1 arg2] +(defn push + [arg1 arg2] (.push ^js Navigation arg1 (clj->js arg2))) (defn pop [comp] (.pop ^js Navigation comp)) -(defn show-modal [arg] +(defn show-modal + [arg] (.showModal ^js Navigation (clj->js arg))) (defn dismiss-modal [comp] (.dismissModal ^js Navigation comp)) -(defn show-overlay [comp] +(defn show-overlay + [comp] (.showOverlay Navigation (clj->js comp))) -(defn pop-to-root [tab] +(defn pop-to-root + [tab] (.popToRoot Navigation (clj->js tab))) -(defn dissmiss-overlay [comp] +(defn dissmiss-overlay + [comp] (.catch (.dismissOverlay Navigation comp) #())) -(defn reg-app-launched-listener [handler] +(defn reg-app-launched-listener + [handler] (.registerAppLaunchedListener ^js (.events ^js Navigation) handler)) -(defn reg-button-pressed-listener [handler] +(defn reg-button-pressed-listener + [handler] (.registerNavigationButtonPressedListener (.events Navigation) (fn [^js evn] (handler (.-buttonId evn))))) -(defn reg-modal-dismissed-listener [handler] +(defn reg-modal-dismissed-listener + [handler] (.registerModalDismissedListener ^js (.events ^js Navigation) handler)) -(defn reg-component-did-appear-listener [handler] +(defn reg-component-did-appear-listener + [handler] (.registerComponentDidAppearListener ^js (.events ^js Navigation) (fn [^js evn] (handler (keyword (.-componentName evn)))))) -(defn reg-component-did-disappear-listener [handler] +(defn reg-component-did-disappear-listener + [handler] (.registerComponentDidDisappearListener ^js (.events ^js Navigation) (fn [^js evn] (handler (.-componentName evn))))) -(defn merge-options [id opts] +(defn merge-options + [id opts] (.mergeOptions Navigation id (clj->js opts))) diff --git a/src/react_native/reanimated.cljs b/src/react_native/reanimated.cljs index 3d24fe5d9c..f397ff67f6 100644 --- a/src/react_native/reanimated.cljs +++ b/src/react_native/reanimated.cljs @@ -1,8 +1,18 @@ (ns react-native.reanimated (:require ["react-native" :as rn] ["react-native-linear-gradient" :default LinearGradient] - ["react-native-reanimated" :default reanimated - :refer (useSharedValue useAnimatedStyle withTiming withDelay withSpring withRepeat Easing Keyframe cancelAnimation SlideInUp SlideOutUp LinearTransition)] + ["react-native-reanimated" :default reanimated :refer + (useSharedValue useAnimatedStyle + withTiming + withDelay + withSpring + withRepeat + Easing + Keyframe + cancelAnimation + SlideInUp + SlideOutUp + LinearTransition)] [clojure.string :as string] [reagent.core :as reagent])) @@ -20,7 +30,7 @@ (def linear-gradient (create-animated-component LinearGradient)) -;; Hooks +;; Hooks (def use-shared-value useSharedValue) (def use-animated-style useAnimatedStyle) @@ -35,32 +45,38 @@ ;; Easings (def bezier (.-bezier ^js Easing)) -(def easings {:linear (bezier 0 0 1 1) - :easing1 (bezier 0.25 0.1 0.25 1) ;; TODO(parvesh) - rename easing functions, (design team input) - :easing2 (bezier 0 0.3 0.6 0.9) - :easing3 (bezier 0.3 0.3 0.3 0.9)}) +(def easings + {:linear (bezier 0 0 1 1) + :easing1 (bezier 0.25 0.1 0.25 1) ;; TODO(parvesh) - rename easing functions, (design team input) + :easing2 (bezier 0 0.3 0.6 0.9) + :easing3 (bezier 0.3 0.3 0.3 0.9)}) ;; Helper functions -(defn get-shared-value [anim] +(defn get-shared-value + [anim] (.-value anim)) -(defn set-shared-value [anim val] +(defn set-shared-value + [anim val] (set! (.-value anim) val)) -(defn kebab-case->camelCase [k] +(defn kebab-case->camelCase + [k] (let [words (string/split (name k) #"-")] (->> (map string/capitalize (rest words)) (apply str (first words)) keyword))) -(defn map-keys [f m] +(defn map-keys + [f m] (->> (map (fn [[k v]] [(f k) v]) m) (into {}))) ;; Worklets (def worklet-factory (js/require "../src/js/worklet_factory.js")) -(defn interpolate [shared-value input-range output-range] +(defn interpolate + [shared-value input-range output-range] (.interpolateValue ^js worklet-factory shared-value (clj->js input-range) @@ -70,24 +86,37 @@ ;; kebab-case styles are not working for worklets ;; so first convert kebab case styles into camel case styles -(defn apply-animations-to-style [animations style] +(defn apply-animations-to-style + [animations style] (let [animations (map-keys kebab-case->camelCase animations) style (apply dissoc (map-keys kebab-case->camelCase style) (keys animations))] (use-animated-style (.applyAnimationsToStyle ^js worklet-factory (clj->js animations) (clj->js style))))) ;; Animators -(defn animate-shared-value-with-timing [anim val duration easing] - (set-shared-value anim (with-timing val (js-obj "duration" duration - "easing" (get easings easing))))) +(defn animate-shared-value-with-timing + [anim val duration easing] + (set-shared-value anim + (with-timing val + (js-obj "duration" duration + "easing" (get easings easing))))) -(defn animate-shared-value-with-delay [anim val duration easing delay] - (set-shared-value anim (with-delay delay (with-timing val (js-obj "duration" duration - "easing" (get easings easing)))))) +(defn animate-shared-value-with-delay + [anim val duration easing delay] + (set-shared-value anim + (with-delay delay + (with-timing val + (js-obj "duration" duration + "easing" (get easings easing)))))) -(defn animate-shared-value-with-repeat [anim val duration easing number-of-repetitions reverse?] - (set-shared-value anim (with-repeat (with-timing val (js-obj "duration" duration - "easing" (get easings easing))) number-of-repetitions reverse?))) +(defn animate-shared-value-with-repeat + [anim val duration easing number-of-repetitions reverse?] + (set-shared-value anim + (with-repeat (with-timing val + (js-obj "duration" duration + "easing" (get easings easing))) + number-of-repetitions + reverse?))) (defn animate-shared-value-with-delay-repeat ([anim val duration easing delay number-of-repetitions] @@ -95,13 +124,18 @@ ([anim val duration easing delay number-of-repetitions reverse?] (set-shared-value anim (with-delay delay - (with-repeat - (with-timing val - #js {:duration duration - :easing (get easings easing)}) - number-of-repetitions reverse?))))) + (with-repeat + (with-timing val + #js + {:duration duration + :easing (get easings easing)}) + number-of-repetitions + reverse?))))) -(defn animate-shared-value-with-spring [anim val {:keys [mass stiffness damping]}] - (set-shared-value anim (with-spring val (js-obj "mass" mass - "damping" damping - "stiffness" stiffness)))) +(defn animate-shared-value-with-spring + [anim val {:keys [mass stiffness damping]}] + (set-shared-value anim + (with-spring val + (js-obj "mass" mass + "damping" damping + "stiffness" stiffness)))) diff --git a/src/react_native/safe_area.cljs b/src/react_native/safe_area.cljs index 2b5fc2ef1b..2893f1abb1 100644 --- a/src/react_native/safe_area.cljs +++ b/src/react_native/safe_area.cljs @@ -1,11 +1,12 @@ (ns react-native.safe-area - (:require ["react-native-safe-area-context" :as safe-area-context - :refer (SafeAreaProvider SafeAreaInsetsContext)] + (:require ["react-native-safe-area-context" :as safe-area-context :refer + (SafeAreaProvider SafeAreaInsetsContext)] [reagent.core :as reagent])) (def ^:private consumer-raw (reagent/adapt-react-class (.-Consumer ^js SafeAreaInsetsContext))) -(defn consumer [component] +(defn consumer + [component] [consumer-raw (fn [^js insets] (reagent/as-element diff --git a/src/react_native/section_list.cljs b/src/react_native/section_list.cljs index 7a96a73a2f..f8293b117d 100644 --- a/src/react_native/section_list.cljs +++ b/src/react_native/section_list.cljs @@ -1,7 +1,7 @@ (ns react-native.section-list - (:require [reagent.core :as reagent] + (:require ["react-native" :as react-native] [react-native.flat-list :as flat-list] - ["react-native" :as react-native])) + [reagent.core :as reagent])) (def section-list-class (reagent/adapt-react-class (.-SectionList react-native))) @@ -11,18 +11,24 @@ (fn [^js data] (reagent/as-element [f (.-item data) (.-index data) (.-separators data) render-data]))))) -(defn- wrap-render-section-header-fn [f] +(defn- wrap-render-section-header-fn + [f] (fn [^js data] (let [^js section (.-section data)] - (reagent/as-element [f {:title (.-title section) - :data (.-data section)}])))) + (reagent/as-element [f + {:title (.-title section) + :data (.-data section)}])))) -(defn- wrap-per-section-render-fn [props] +(defn- wrap-per-section-render-fn + [props] (update (if-let [f (:render-fn props)] - (assoc (dissoc props :render-fn :render-data) :renderItem (memo-wrap-render-fn f (:render-data props))) + (assoc (dissoc props :render-fn :render-data) + :renderItem + (memo-wrap-render-fn f (:render-data props))) props) - :data to-array)) + :data + to-array)) (defn section-list "A wrapper for SectionList. diff --git a/src/react_native/shake.cljs b/src/react_native/shake.cljs index ef1b9c91a1..822ed76197 100644 --- a/src/react_native/shake.cljs +++ b/src/react_native/shake.cljs @@ -1,5 +1,6 @@ (ns react-native.shake (:require ["react-native-shake" :as react-native-shake])) -(defn add-shake-listener [handler] +(defn add-shake-listener + [handler] (.addEventListener react-native-shake "ShakeEvent" handler)) diff --git a/src/react_native/svg.cljs b/src/react_native/svg.cljs index 720745bb06..ac5eb48cf2 100644 --- a/src/react_native/svg.cljs +++ b/src/react_native/svg.cljs @@ -1,7 +1,6 @@ (ns react-native.svg - (:require - ["react-native-svg" :as Svg] - [reagent.core :as reagent])) + (:require ["react-native-svg" :as Svg] + [reagent.core :as reagent])) (def svg (reagent/adapt-react-class Svg/default)) (def path (reagent/adapt-react-class Svg/Path)) diff --git a/src/status_im/add_new/core.cljs b/src/status_im/add_new/core.cljs index 4e3f3648ed..eb93fa633e 100644 --- a/src/status_im/add_new/core.cljs +++ b/src/status_im/add_new/core.cljs @@ -1,19 +1,19 @@ (ns status-im.add-new.core (:require [clojure.string :as string] [re-frame.core :as re-frame] + [status-im.add-new.db :as db] + [status-im.chat.models :as chat] + [status-im.contact.core :as contact] [status-im.ethereum.core :as ethereum] [status-im.ethereum.ens :as ens] - [status-im.add-new.db :as db] + [status-im.ethereum.stateofus :as stateofus] + [status-im.i18n.i18n :as i18n] + [status-im.router.core :as router] + [status-im.utils.db :as utils.db] + [status-im.utils.fx :as fx] [status-im.utils.random :as random] [status-im.utils.utils :as utils] - [status-im.utils.fx :as fx] - [status-im.chat.models :as chat] - [status-im.i18n.i18n :as i18n] - [status-im.contact.core :as contact] - [status-im.router.core :as router] - [status-im2.navigation.events :as navigation] - [status-im.ethereum.stateofus :as stateofus] - [status-im.utils.db :as utils.db])) + [status-im2.navigation.events :as navigation])) (re-frame/reg-fx :resolve-public-key @@ -31,12 +31,12 @@ (when (or (not id) (= id @resolve-last-id)) (if ens-error {:db (assoc-in db [:contacts/new-identity :state] :error)} - (let [new-identity (utils/safe-trim new-identity-raw) + (let [new-identity (utils/safe-trim new-identity-raw) is-public-key? (and (string? new-identity) (utils.db/valid-public-key? new-identity)) - is-ens? (and (not is-public-key?) - (ens/valid-eth-name-prefix? new-identity)) - error (db/validate-pub-key db new-identity)] + is-ens? (and (not is-public-key?) + (ens/valid-eth-name-prefix? new-identity)) + error (db/validate-pub-key db new-identity)] (reset! resolve-last-id nil) (merge {:db (assoc db :contacts/new-identity @@ -68,14 +68,16 @@ (fx/defn qr-code-handled {:events [::qr-code-handled]} - [{:keys [db] :as cofx} {:keys [type public-key chat-id data ens-name]} {:keys [new-contact? nickname] :as opts}] - (let [public-key? (and (string? data) - (string/starts-with? data "0x")) - chat-key (cond - (= type :private-chat) chat-id - (= type :contact) public-key - (and (= type :undefined) - public-key?) data) + [{:keys [db] :as cofx} {:keys [type public-key chat-id data ens-name]} + {:keys [new-contact? nickname] :as opts}] + (let [public-key? (and (string? data) + (string/starts-with? data "0x")) + chat-key (cond + (= type :private-chat) chat-id + (= type :contact) public-key + (and (= type :undefined) + public-key?) + data) validation-result (db/validate-pub-key db chat-key)] (if-not validation-result (if new-contact? diff --git a/src/status_im/add_new/db.cljs b/src/status_im/add_new/db.cljs index d0fc062a32..863ad45e1f 100644 --- a/src/status_im/add_new/db.cljs +++ b/src/status_im/add_new/db.cljs @@ -1,13 +1,14 @@ (ns status-im.add-new.db - (:require [status-im.ethereum.ens :as ens] - [cljs.spec.alpha :as spec] + (:require [cljs.spec.alpha :as spec] + [status-im.ethereum.ens :as ens] [status-im.utils.db :as utils.db])) (defn own-public-key? [{:keys [multiaccount]} public-key] (= (:public-key multiaccount) public-key)) -(defn validate-pub-key [db public-key] +(defn validate-pub-key + [db public-key] (cond (or (not (utils.db/valid-public-key? public-key)) (= public-key ens/default-key)) @@ -17,11 +18,13 @@ (spec/def ::name (spec/and string? not-empty)) -(spec/def ::topic (spec/and string? - not-empty - (partial re-matches #"[a-z0-9\-]+"))) +(spec/def ::topic + (spec/and string? + not-empty + (partial re-matches #"[a-z0-9\-]+"))) -(defn valid-topic? [topic] +(defn valid-topic? + [topic] (and topic (spec/valid? ::topic topic) (not (utils.db/valid-public-key? topic)))) \ No newline at end of file diff --git a/src/status_im/async_storage/core.cljs b/src/status_im/async_storage/core.cljs index 73f6e71a2b..95657d00d3 100644 --- a/src/status_im/async_storage/core.cljs +++ b/src/status_im/async_storage/core.cljs @@ -1,15 +1,16 @@ (ns status-im.async-storage.core - (:require [re-frame.core :as re-frame] - [taoensso.timbre :as log] + (:require ["@react-native-async-storage/async-storage" :default async-storage] [goog.functions :as f] + [re-frame.core :as re-frame] [status-im.async-storage.transit :refer [clj->transit transit->clj]] - ["@react-native-async-storage/async-storage" :default async-storage])) + [taoensso.timbre :as log])) (def ^:private debounce-ms 250) (def key->string str) -(defn set-item! [key value] +(defn set-item! + [key value] (-> ^js async-storage (.setItem (key->string key) (clj->transit value)) @@ -22,12 +23,14 @@ debounced (f/debounce (fn [] (doseq [[k v] @tmp-storage] (swap! tmp-storage dissoc k) - (set-item! k v))) debounce-ms)] + (set-item! k v))) + debounce-ms)] (fn [items] (swap! tmp-storage merge items) (debounced)))) -(defn get-items [keys cb] +(defn get-items + [keys cb] (-> ^js async-storage (.multiGet (to-array (map key->string keys))) (.then (fn [^js data] @@ -38,7 +41,8 @@ (cb nil) (log/error "[async-storage]" error))))) -(defn get-item [k cb] +(defn get-item + [k cb] (-> ^js async-storage (.getItem (key->string k)) (.then (fn [^js data] diff --git a/src/status_im/async_storage/transit.cljs b/src/status_im/async_storage/transit.cljs index b9275b263f..841dd94cb5 100644 --- a/src/status_im/async_storage/transit.cljs +++ b/src/status_im/async_storage/transit.cljs @@ -6,6 +6,8 @@ (def writer (transit/writer :json)) (defn clj->transit [o] (transit/write writer o)) -(defn transit->clj [o] (try (transit/read reader o) - (catch :default e - (log/error e)))) +(defn transit->clj + [o] + (try (transit/read reader o) + (catch :default e + (log/error e)))) diff --git a/src/status_im/audio/core.cljs b/src/status_im/audio/core.cljs index a318f7fea3..8dad5bcc09 100644 --- a/src/status_im/audio/core.cljs +++ b/src/status_im/audio/core.cljs @@ -11,18 +11,21 @@ (def DESTROYED (.-DESTROYED ^js MediaStates)) (def SEEKING (.-SEEKING ^js MediaStates)) -(def default-recorder-options {:filename "recording.aac" - :bitrate 32000 - :channels 1 - :sampleRate 22050 - :quality "medium" ; ios only - :meteringInterval 50}) +(def default-recorder-options + {:filename "recording.aac" + :bitrate 32000 + :channels 1 + :sampleRate 22050 + :quality "medium" ; ios only + :meteringInterval 50}) -(defn get-state [player-recorder] +(defn get-state + [player-recorder] (when player-recorder (.-state ^js player-recorder))) -(defn new-recorder [options on-meter on-ended] +(defn new-recorder + [options on-meter on-ended] (let [recorder (new ^js Recorder (:filename options) (clj->js options))] @@ -31,98 +34,124 @@ (when on-ended (.on ^js recorder "ended" on-ended)))) -(defn new-player [audio options on-ended] +(defn new-player + [audio options on-ended] (let [player (new ^js Player audio (clj->js options))] (when on-ended (.on ^js player "ended" on-ended)))) -(defn prepare-player [player on-prepared on-error] +(defn prepare-player + [player on-prepared on-error] (when (and player (.-canPrepare ^js player)) - (.prepare ^js player #(if % - (on-error {:error (.-err %) :message (.-message %)}) - (on-prepared))))) + (.prepare ^js player + #(if % + (on-error {:error (.-err %) :message (.-message %)}) + (on-prepared))))) -(defn prepare-recorder [recorder on-prepared on-error] +(defn prepare-recorder + [recorder on-prepared on-error] (when (and recorder (.-canPrepare ^js recorder)) - (.prepare ^js recorder (fn [err _] - (if err - (on-error {:error (.-err err) :message (.-message err)}) - (on-prepared)))))) + (.prepare ^js recorder + (fn [err _] + (if err + (on-error {:error (.-err err) :message (.-message err)}) + (on-prepared)))))) -(defn start-recording [recorder on-start on-error] +(defn start-recording + [recorder on-start on-error] (when (and recorder (or (.-canRecord ^js recorder) (.-canPrepare ^js recorder))) - (.record ^js recorder #(if % - (on-error {:error (.-err %) :message (.-message %)}) - (on-start))))) + (.record ^js recorder + #(if % + (on-error {:error (.-err %) :message (.-message %)}) + (on-start))))) -(defn stop-recording [recorder on-stop on-error] +(defn stop-recording + [recorder on-stop on-error] (if (and recorder (#{RECORDING PAUSED} (get-state recorder))) - (.stop ^js recorder #(if % - (on-error {:error (.-err %) :message (.-message %)}) - (on-stop))) + (.stop ^js recorder + #(if % + (on-error {:error (.-err %) :message (.-message %)}) + (on-stop))) (on-stop))) -(defn pause-recording [recorder on-pause on-error] +(defn pause-recording + [recorder on-pause on-error] (when (and recorder (.-isRecording ^js recorder)) - (.pause ^js recorder #(if % - (on-error {:error (.-err %) :message (.-message %)}) - (on-pause))))) + (.pause ^js recorder + #(if % + (on-error {:error (.-err %) :message (.-message %)}) + (on-pause))))) -(defn start-playing [player on-start on-error] +(defn start-playing + [player on-start on-error] (when (and player (.-canPlay ^js player)) - (.play ^js player #(if % - (on-error {:error (.-err %) :message (.-message %)}) - (on-start))))) + (.play ^js player + #(if % + (on-error {:error (.-err %) :message (.-message %)}) + (on-start))))) -(defn stop-playing [player on-stop on-error] +(defn stop-playing + [player on-stop on-error] (if (and player (.-isPlaying ^js player)) - (.stop ^js player #(if % - (on-error {:error (.-err %) :message (.-message %)}) - (on-stop))) + (.stop ^js player + #(if % + (on-error {:error (.-err %) :message (.-message %)}) + (on-stop))) (on-stop))) -(defn get-recorder-file-path [recorder] +(defn get-recorder-file-path + [recorder] (when recorder (.-fsPath ^js recorder))) -(defn get-player-duration [player] +(defn get-player-duration + [player] (when (and player (.-canPlay ^js player)) (.-duration ^js player))) -(defn get-player-current-time [player] +(defn get-player-current-time + [player] (when (and player (.-canPlay ^js player)) (.-currentTime ^js player))) -(defn toggle-playpause-player [player on-play on-pause on-error] +(defn toggle-playpause-player + [player on-play on-pause on-error] (when (and player (.-canPlay ^js player)) - (.playPause ^js player (fn [error pause?] - (if error - (on-error {:error (.-err error) :message (.-message error)}) - (if pause? - (on-pause) - (on-play))))))) + (.playPause ^js player + (fn [error pause?] + (if error + (on-error {:error (.-err error) :message (.-message error)}) + (if pause? + (on-pause) + (on-play))))))) -(defn seek-player [player value on-seek on-error] +(defn seek-player + [player value on-seek on-error] (when (and player (.-canPlay ^js player)) - (.seek ^js player value #(if % - (on-error {:error (.-err %) :message (.-message %)}) - (on-seek))))) + (.seek ^js player + value + #(if % + (on-error {:error (.-err %) :message (.-message %)}) + (on-seek))))) -(defn can-play? [player] +(defn can-play? + [player] (and player (.-canPlay ^js player))) -(defn destroy-recorder [recorder] +(defn destroy-recorder + [recorder] (stop-recording recorder #(when (and recorder (not= (get-state recorder) DESTROYED)) (.destroy ^js recorder)) #())) -(defn destroy-player [player] +(defn destroy-player + [player] (stop-playing player #(when (and player (not= (get-state player) IDLE)) (.destroy ^js player)) diff --git a/src/status_im/backup/core.cljs b/src/status_im/backup/core.cljs index 29de285791..64c93bea06 100644 --- a/src/status_im/backup/core.cljs +++ b/src/status_im/backup/core.cljs @@ -1,8 +1,8 @@ (ns status-im.backup.core (:require [re-frame.core :as re-frame] - [taoensso.timbre :as log] [status-im.ethereum.json-rpc :as json-rpc] - [status-im.utils.fx :as fx])) + [status-im.utils.fx :as fx] + [taoensso.timbre :as log])) (fx/defn handle-backup-failed {:events [::backup-failed]} @@ -17,7 +17,7 @@ (fx/defn handle-perform-backup-pressed {:events [:multiaccounts.ui/perform-backup-pressed]} [{:keys [db]}] - {:db (assoc db :backup/performing-backup true) + {:db (assoc db :backup/performing-backup true) ::json-rpc/call [{:method "wakuext_backupData" :params [] :on-error #(do diff --git a/src/status_im/bootnodes/core.cljs b/src/status_im/bootnodes/core.cljs index fc2bbcab00..9bcf6632d3 100644 --- a/src/status_im/bootnodes/core.cljs +++ b/src/status_im/bootnodes/core.cljs @@ -3,21 +3,24 @@ [re-frame.core :as re-frame] [status-im.i18n.i18n :as i18n] [status-im.multiaccounts.update.core :as multiaccounts.update] - [status-im2.navigation.events :as navigation] - [status-im.utils.fx :as fx])) + [status-im.utils.fx :as fx] + [status-im2.navigation.events :as navigation])) (def address-regex #"enode://[a-zA-Z0-9]+:?(.*)\@\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b:(\d{1,5})") -(defn valid-address? [address] +(defn valid-address? + [address] (re-matches address-regex address)) -(defn- build [id bootnode-name address chain] +(defn- build + [id bootnode-name address chain] {:address address :chain chain :id (string/replace id "-" "") :name bootnode-name}) -(fx/defn fetch [cofx id] +(fx/defn fetch + [cofx id] (let [network (get-in cofx [:db :networks/current-network])] (get-in cofx [:db :multiaccount :custom-bootnodes network id]))) @@ -40,11 +43,12 @@ [{:keys [db] :as cofx} id] (let [{:keys [id address - name]} (fetch cofx id) - fxs (fx/merge cofx - (set-input :id id) - (set-input :url (str address)) - (set-input :name (str name)))] + name]} + (fetch cofx id) + fxs (fx/merge cofx + (set-input :id id) + (set-input :url (str address)) + (set-input :name (str name)))] (assoc fxs :dispatch [:navigate-to :edit-bootnode]))) (defn custom-bootnodes-in-use? @@ -52,64 +56,71 @@ (let [network (:networks/current-network db)] (get-in db [:multiaccount :custom-bootnodes-enabled? network]))) -(fx/defn delete [{{:keys [multiaccount] :as db} :db :as cofx} id] - (let [network (:networks/current-network db) +(fx/defn delete + [{{:keys [multiaccount] :as db} :db :as cofx} id] + (let [network (:networks/current-network db) new-multiaccount (update-in multiaccount [:custom-bootnodes network] dissoc id)] (fx/merge cofx {:db (assoc db :multiaccount new-multiaccount)} (multiaccounts.update/multiaccount-update - :custom-bootnodes (:custom-bootnodes new-multiaccount) + :custom-bootnodes + (:custom-bootnodes new-multiaccount) {:success-event (when (custom-bootnodes-in-use? cofx) [:multiaccounts.update.callback/save-settings-success])})))) (fx/defn upsert - {:events [:bootnodes.ui/save-pressed] + {:events [:bootnodes.ui/save-pressed] :interceptors [(re-frame/inject-cofx :random-id-generator)]} [{{:bootnodes/keys [manage] :keys [multiaccount] :as db} :db - random-id-generator :random-id-generator :as cofx}] + random-id-generator :random-id-generator + :as cofx}] (let [{:keys [name id url]} manage - network (:networks/current-network db) - bootnode (build - (or (:value id) (random-id-generator)) - (:value name) - (:value url) - network) - new-bootnodes (assoc-in - (:custom-bootnodes multiaccount) - [network (:id bootnode)] - bootnode)] + network (:networks/current-network db) + bootnode (build + (or (:value id) (random-id-generator)) + (:value name) + (:value url) + network) + new-bootnodes (assoc-in + (:custom-bootnodes multiaccount) + [network (:id bootnode)] + bootnode)] (fx/merge cofx {:db (dissoc db :bootnodes/manage) :dispatch [:navigate-back]} (multiaccounts.update/multiaccount-update - :custom-bootnodes new-bootnodes + :custom-bootnodes + new-bootnodes {:success-event (when (custom-bootnodes-in-use? cofx) [:multiaccounts.update.callback/save-settings-success])})))) (fx/defn toggle-custom-bootnodes {:events [:bootnodes.ui/custom-bootnodes-switch-toggled]} [{:keys [db] :as cofx} value] - (let [current-network (:networks/current-network db) + (let [current-network (:networks/current-network db) bootnodes-settings (get-in db [:multiaccount :custom-bootnodes-enabled?])] (multiaccounts.update/multiaccount-update cofx - :custom-bootnodes-enabled? (assoc bootnodes-settings current-network value) + :custom-bootnodes-enabled? + (assoc bootnodes-settings current-network value) {:success-event [:multiaccounts.update.callback/save-settings-success]}))) (fx/defn set-bootnodes-from-qr {:events [:bootnodes.callback/qr-code-scanned]} [cofx url] (assoc (set-input cofx :url (string/trim url)) - :dispatch [:navigate-back])) + :dispatch + [:navigate-back])) (fx/defn show-delete-bootnode-confirmation {:events [:bootnodes.ui/delete-pressed]} [_ bootnode-id] - {:ui/show-confirmation {:title (i18n/label :t/delete-bootnode-title) - :content (i18n/label :t/delete-bootnode-are-you-sure) + {:ui/show-confirmation {:title (i18n/label :t/delete-bootnode-title) + :content (i18n/label :t/delete-bootnode-are-you-sure) :confirm-button-text (i18n/label :t/delete-bootnode) - :on-accept #(re-frame/dispatch [:bootnodes.ui/delete-confirmed bootnode-id])}}) + :on-accept #(re-frame/dispatch [:bootnodes.ui/delete-confirmed + bootnode-id])}}) (fx/defn delete-bootnode {:events [:bootnodes.ui/delete-confirmed]} diff --git a/src/status_im/bootnodes/core_test.cljs b/src/status_im/bootnodes/core_test.cljs index 6c3b9f8292..8852b38793 100644 --- a/src/status_im/bootnodes/core_test.cljs +++ b/src/status_im/bootnodes/core_test.cljs @@ -2,7 +2,8 @@ (:require [cljs.test :refer-macros [deftest is testing]] [status-im.bootnodes.core :as model])) -(def bootnode-id "1da276e34126e93babf24ec88aac1a7602b4cbb2e11b0961d0ab5e989ca9c261aa7f7c1c85f15550a5f1e5a5ca2305b53b9280cf5894d5ecf7d257b173136d40") +(def bootnode-id + "1da276e34126e93babf24ec88aac1a7602b4cbb2e11b0961d0ab5e989ca9c261aa7f7c1c85f15550a5f1e5a5ca2305b53b9280cf5894d5ecf7d257b173136d40") (def host "167.99.209.61:30504") (def valid-bootnode-address (str "enode://" bootnode-id "@" host)) @@ -16,10 +17,11 @@ :chain "mainnet_rpc" :id "someid"}}} actual (model/upsert - {:random-id-generator (constantly "some-id") - :db {:bootnodes/manage new-bootnode - :networks/current-network "mainnet_rpc" - :multiaccount {:not-empty "would throw an error if was empty"}}})] + {:random-id-generator (constantly "some-id") + :db {:bootnodes/manage new-bootnode + :networks/current-network "mainnet_rpc" + :multiaccount {:not-empty + "would throw an error if was empty"}}})] (is (= expected (get-in actual [:db :multiaccount :custom-bootnodes]))))) (testing "adding an existing bootnode" (let [new-bootnode {:id {:value "a"} @@ -30,15 +32,15 @@ :chain "mainnet_rpc" :id "a"}}} actual (model/upsert - {:random-id-generator (constantly "some-id") - :db {:bootnodes/manage new-bootnode - :networks/current-network "mainnet_rpc" - :multiaccount {:custom-bootnodes - {"mainnet_rpc" - {"a" {:name "name" - :address "url" - :chain "mainnet_rpc" - :id "a"}}}}}})] + {:random-id-generator (constantly "some-id") + :db {:bootnodes/manage new-bootnode + :networks/current-network "mainnet_rpc" + :multiaccount {:custom-bootnodes + {"mainnet_rpc" + {"a" {:name "name" + :address "url" + :chain "mainnet_rpc" + :id "a"}}}}}})] (is (= expected (get-in actual [:db :multiaccount :custom-bootnodes])))))) (deftest set-input-bootnode @@ -64,18 +66,18 @@ (deftest set-bootnode-from-qr (testing "correct name" (is (= {:dispatch [:navigate-back] - :db {:bootnodes/manage {:url {:value valid-bootnode-address - :error false}}}} + :db {:bootnodes/manage {:url {:value valid-bootnode-address + :error false}}}} (model/set-bootnodes-from-qr {:db {}} (str valid-bootnode-address " ")))))) (deftest edit-bootnode - (let [db {:networks/current-network "mainnet_rpc" - :multiaccount - {:custom-bootnodes - {"mainnet_rpc" - {"a" {:id "a" - :name "name" - :address valid-bootnode-address}}}}} + (let [db {:networks/current-network "mainnet_rpc" + :multiaccount + {:custom-bootnodes + {"mainnet_rpc" + {"a" {:id "a" + :name "name" + :address valid-bootnode-address}}}}} cofx {:db db}] (testing "when no id is given" (let [actual (model/edit cofx nil)] @@ -121,11 +123,12 @@ (deftest fetch-bootnode (testing "it fetches the bootnode from the db" (let [cofx {:db {:networks/current-network "mainnet_rpc" - :multiaccount {:custom-bootnodes - {"mainnet_rpc" - {"a" {:id "a" - :name "name" - :address "enode://old-id:old-password@url:port"}}}}}}] + :multiaccount {:custom-bootnodes + {"mainnet_rpc" + {"a" {:id "a" + :name "name" + :address + "enode://old-id:old-password@url:port"}}}}}}] (is (model/fetch cofx "a"))))) (deftest custom-bootnodes-in-use? @@ -135,39 +138,41 @@ (testing "it returns true when enabled" (is (model/custom-bootnodes-in-use? {:db {:networks/current-network "mainnet_rpc" - :multiaccount {:custom-bootnodes-enabled? - {"mainnet_rpc" true}}}})))) + :multiaccount {:custom-bootnodes-enabled? + {"mainnet_rpc" true}}}})))) (testing "is on a different network" (testing "it returns false when not enabled" (is (not (model/custom-bootnodes-in-use? {:db {:networks/current-network "goerli_rpc"}})))) (testing "it returns true when enabled" (is (not (model/custom-bootnodes-in-use? {:db {:networks/current-network "goerli_rpc" - :multiaccount {:custom-bootnodes-enabled? - {"mainnnet_rpc" true}}}})))))) + :multiaccount {:custom-bootnodes-enabled? + {"mainnnet_rpc" true}}}})))))) (deftest delete-bootnode (testing "non existing bootnode" - (let [cofx {:db {:networks/current-network "mainnet_rpc" - :multiaccount {:custom-bootnodes - {"mainnet_rpc" - {"a" {:id "a" - :name "name" - :address "enode://old-id:old-password@url:port"}}} - :custom-bootnodes-enabled? - {"mainnnet_rpc" true}}}} + (let [cofx {:db {:networks/current-network "mainnet_rpc" + :multiaccount {:custom-bootnodes + {"mainnet_rpc" + {"a" {:id "a" + :name "name" + :address + "enode://old-id:old-password@url:port"}}} + :custom-bootnodes-enabled? + {"mainnnet_rpc" true}}}} actual (model/delete cofx "b")] (testing "it does not removes the bootnode" (is (model/fetch actual "a"))))) (testing "existing bootnode" - (let [cofx {:db {:networks/current-network "mainnet_rpc" - :multiaccount {:custom-bootnodes - {"mainnet_rpc" - {"a" {:id "a" - :name "name" - :address "enode://old-id:old-password@url:port"}}} - :custom-bootnodes-enabled? - {"mainnnet_rpc" true}}}} + (let [cofx {:db {:networks/current-network "mainnet_rpc" + :multiaccount {:custom-bootnodes + {"mainnet_rpc" + {"a" {:id "a" + :name "name" + :address + "enode://old-id:old-password@url:port"}}} + :custom-bootnodes-enabled? + {"mainnnet_rpc" true}}}} actual (model/delete cofx "a")] (testing "it removes the bootnode" diff --git a/src/status_im/bottom_sheet/core.cljs b/src/status_im/bottom_sheet/core.cljs index 588b8ed9b8..a04e43e899 100644 --- a/src/status_im/bottom_sheet/core.cljs +++ b/src/status_im/bottom_sheet/core.cljs @@ -4,10 +4,10 @@ (fx/defn show-bottom-sheet [{:keys [db]} {:keys [view options]}] {:show-bottom-sheet nil - :db (assoc db - :bottom-sheet/show? true - :bottom-sheet/view view - :bottom-sheet/options options)}) + :db (assoc db + :bottom-sheet/show? true + :bottom-sheet/view view + :bottom-sheet/options options)}) (fx/defn show-bottom-sheet-event {:events [:bottom-sheet/show-sheet]} @@ -21,4 +21,4 @@ {:events [:bottom-sheet/hide]} [{:keys [db]}] {:hide-bottom-sheet nil - :db (assoc db :bottom-sheet/show? false)}) + :db (assoc db :bottom-sheet/show? false)}) diff --git a/src/status_im/browser/core.cljs b/src/status_im/browser/core.cljs index 9ce5697981..f0ab46bbf3 100644 --- a/src/status_im/browser/core.cljs +++ b/src/status_im/browser/core.cljs @@ -1,30 +1,30 @@ (ns status-im.browser.core - (:require [clojure.string :as string] + (:require ["eth-phishing-detect" :as eth-phishing-detect] + [clojure.string :as string] [re-frame.core :as re-frame] + [status-im.bottom-sheet.core :as bottom-sheet] + [status-im.browser.eip3085 :as eip3085] + [status-im.browser.eip3326 :as eip3326] [status-im.browser.permissions :as browser.permissions] + [status-im.browser.webview-ref :as webview-ref] [status-im.constants :as constants] [status-im.ethereum.core :as ethereum] [status-im.ethereum.ens :as ens] [status-im.ethereum.json-rpc :as json-rpc] [status-im.i18n.i18n :as i18n] + [status-im.multiaccounts.update.core :as multiaccounts.update] [status-im.native-module.core :as status] + [status-im.signing.core :as signing] [status-im.ui.components.list-selection :as list-selection] - [status-im2.navigation.events :as navigation] [status-im.utils.fx :as fx] [status-im.utils.http :as http] [status-im.utils.platform :as platform] [status-im.utils.random :as random] [status-im.utils.types :as types] [status-im.utils.universal-links.utils :as links] + [status-im2.navigation.events :as navigation] [taoensso.timbre :as log] - [status-im.signing.core :as signing] - [status-im.multiaccounts.update.core :as multiaccounts.update] - [status-im.bottom-sheet.core :as bottom-sheet] - [status-im.browser.webview-ref :as webview-ref] - ["eth-phishing-detect" :as eth-phishing-detect] - [utils.debounce :as debounce] - [status-im.browser.eip3085 :as eip3085] - [status-im.browser.eip3326 :as eip3326])) + [utils.debounce :as debounce])) (fx/defn update-browser-option [{:keys [db]} option-key option-value] @@ -34,15 +34,18 @@ [{:keys [db]} options] {:db (update db :browser/options merge options)}) -(defn get-current-browser [db] +(defn get-current-browser + [db] (get-in db [:browser/browsers (get-in db [:browser/options :browser-id])])) -(defn get-current-url [{:keys [history history-index] - :or {history-index 0}}] +(defn get-current-url + [{:keys [history history-index] + :or {history-index 0}}] (when history (nth history history-index))) -(defn secure? [{:keys [error? dapp?]} {:keys [url]}] +(defn secure? + [{:keys [error? dapp?]} {:keys [url]}] (or dapp? (and (not error?) (when url @@ -51,9 +54,9 @@ (fx/defn remove-browser {:events [:browser.ui/remove-browser-pressed]} [{:keys [db]} browser-id] - {:db (update-in db [:browser/browsers] dissoc browser-id) - ::json-rpc/call [{:method "wakuext_deleteBrowser" - :params [browser-id] + {:db (update-in db [:browser/browsers] dissoc browser-id) + ::json-rpc/call [{:method "wakuext_deleteBrowser" + :params [browser-id] :on-success #()}]}) (fx/defn clear-all-browsers @@ -65,14 +68,17 @@ :params [browser-id] :on-success #()})}) -(defn update-dapp-name [{:keys [name] :as browser}] +(defn update-dapp-name + [{:keys [name] :as browser}] (assoc browser :dapp? false :name (or name (i18n/label :t/browser)))) -(defn check-if-phishing-url [{:keys [history history-index] :as browser}] +(defn check-if-phishing-url + [{:keys [history history-index] :as browser}] (let [history-host (http/url-host (try (nth history history-index) (catch js/Error _)))] (cond-> browser history-host (assoc :unsafe? (eth-phishing-detect history-host))))) -(defn resolve-ens-contenthash-callback [url] +(defn resolve-ens-contenthash-callback + [url] (if (not (string/blank? url)) (re-frame/dispatch [:browser.callback/resolve-ens-multihash-success url]) (re-frame/dispatch [:browser.callback/resolve-ens-multihash-error]))) @@ -81,9 +87,9 @@ [{:keys [db]} {:keys [error? resolved-url]}] (when (not error?) (let [current-url (get-current-url (get-current-browser db)) - host (http/url-host current-url)] + host (http/url-host current-url)] (if (and (not resolved-url) (ens/is-valid-eth-name? host)) - {:db (update db :browser/options assoc :resolving? true) + {:db (update db :browser/options assoc :resolving? true) :browser/resolve-ens-contenthash {:chain-id (ethereum/chain-id db) :ens-name host :cb resolve-ens-contenthash-callback}} @@ -95,20 +101,23 @@ (let [updated-browser (-> browser (update-dapp-name) (check-if-phishing-url))] - {:db (update-in db - [:browser/browsers browser-id] - merge updated-browser) - ::json-rpc/call [{:method "wakuext_addBrowser" - :params [(select-keys updated-browser [:browser-id :timestamp :name :dapp? :history :history-index])] + {:db (update-in db + [:browser/browsers browser-id] + merge + updated-browser) + ::json-rpc/call [{:method "wakuext_addBrowser" + :params [(select-keys updated-browser + [:browser-id :timestamp :name :dapp? :history + :history-index])] :on-success #()}]})) (fx/defn store-bookmark {:events [:browser/store-bookmark]} [{:keys [db]} {:keys [url] :as bookmark}] - {:db (assoc-in db [:bookmarks/bookmarks url] bookmark) - ::json-rpc/call [{:method "wakuext_addBookmark" - :params [bookmark] + {:db (assoc-in db [:bookmarks/bookmarks url] bookmark) + ::json-rpc/call [{:method "wakuext_addBookmark" + :params [bookmark] :on-success #()}]}) (fx/defn update-bookmark @@ -117,23 +126,26 @@ {:keys [url] :as bookmark}] (let [old-bookmark (get-in db [:bookmarks/bookmarks url]) new-bookmark (merge old-bookmark bookmark)] - (fx/merge cofx {:db (assoc-in db [:bookmarks/bookmarks url] new-bookmark) - ::json-rpc/call [{:method "wakuext_updateBookmark" - :params [url bookmark] - :on-success #()}]}))) + (fx/merge cofx + {:db (assoc-in db [:bookmarks/bookmarks url] new-bookmark) + ::json-rpc/call [{:method "wakuext_updateBookmark" + :params [url bookmark] + :on-success #()}]}))) (fx/defn delete-bookmark {:events [:browser/delete-bookmark]} [{:keys [db] :as cofx} url] - (let [old-bookmark (get-in db [:bookmarks/bookmarks url]) + (let [old-bookmark (get-in db [:bookmarks/bookmarks url]) removed-bookmark (merge old-bookmark {:removed true})] - (fx/merge cofx {:db (update db :bookmarks/bookmarks dissoc url) - ::json-rpc/call [{:method "wakuext_removeBookmark" - :params [url] - :on-success #()}]}))) + (fx/merge cofx + {:db (update db :bookmarks/bookmarks dissoc url) + ::json-rpc/call [{:method "wakuext_removeBookmark" + :params [url] + :on-success #()}]}))) -(defn can-go-back? [{:keys [history-index]}] +(defn can-go-back? + [{:keys [history-index]}] (pos? history-index)) (fx/defn navigate-to-previous-page @@ -149,10 +161,11 @@ {:events [:browser/ignore-unsafe]} [cofx] (let [browser (get-current-browser (:db cofx)) - host (http/url-host (get-current-url browser))] + host (http/url-host (get-current-url browser))] (update-browser cofx (assoc browser :ignore-unsafe host)))) -(defn can-go-forward? [{:keys [history-index history]}] +(defn can-go-forward? + [{:keys [history-index history]}] (< history-index (dec (count history)))) (fx/defn navigate-to-next-page @@ -173,7 +186,7 @@ new-index (dec (count new-history))] (update-browser cofx (assoc browser - :history new-history + :history new-history :history-index new-index)))))) (fx/defn resolve-ens-multihash-success @@ -184,18 +197,20 @@ path (subs current-url (+ (.indexOf ^js current-url host) (count host))) gateway url] (fx/merge cofx - {:db (-> (update db :browser/options + {:db (-> (update db + :browser/options assoc - :url (str gateway path) - :resolving? false) + :url (str gateway path) + :resolving? false) (assoc-in [:browser/options :resolved-ens host] gateway))}))) (fx/defn resolve-ens-multihash-error {:events [:browser.callback/resolve-ens-multihash-error]} [{:keys [db] :as cofx}] - (update-browser-options cofx {:url (get-current-url (get-current-browser db)) - :resolving? false - :error? true})) + (update-browser-options cofx + {:url (get-current-url (get-current-browser db)) + :resolving? false + :error? true})) (fx/defn handle-browser-error {:events [:browser/error-occured]} @@ -218,15 +233,21 @@ (fx/defn update-browser-on-nav-change [cofx url error?] - (let [browser (get-current-browser (:db cofx)) - options (get-in cofx [:db :browser/options]) + (let [browser (get-current-browser (:db cofx)) + options (get-in cofx [:db :browser/options]) current-url (:url options)] - (when (and browser (not (string/blank? url)) (not= "about:blank" url) (not= current-url url) (not= (str current-url "/") url)) + (when (and browser + (not (string/blank? url)) + (not= "about:blank" url) + (not= current-url url) + (not= (str current-url "/") url)) (let [resolved-ens (first (filter (fn [v] (not= (.indexOf ^js url (second v)) -1)) (:resolved-ens options))) resolved-url (if resolved-ens - (http/normalize-url (string/replace url (second resolved-ens) (first resolved-ens))) + (http/normalize-url (string/replace url + (second resolved-ens) + (first resolved-ens))) url)] (fx/merge cofx (update-browser-history browser resolved-url) @@ -258,7 +279,7 @@ origin url" {:events [:browser.ui/url-submitted]} [cofx url] - (let [browser (get-current-browser (:db cofx)) + (let [browser (get-current-browser (:db cofx)) normalized-url (http/normalize-and-decode-url url)] (if (links/universal-link? normalized-url) {:dispatch [:universal-links/handle-url normalized-url]} @@ -274,13 +295,14 @@ {:events [:browser.ui/open-url]} [{:keys [db] :as cofx} url] (let [normalized-url (http/normalize-and-decode-url url) - browser {:browser-id (random/id) - :history-index 0 - :history [normalized-url]}] + browser {:browser-id (random/id) + :history-index 0 + :history [normalized-url]}] (if (links/universal-link? normalized-url) {:dispatch [:universal-links/handle-url normalized-url]} (fx/merge cofx - {:db (assoc db :browser/options + {:db (assoc db + :browser/options {:browser-id (:browser-id browser)})} (navigation/change-tab :browser) (navigation/set-stack-root :browser-stack :browser) @@ -293,7 +315,8 @@ [{:keys [db] :as cofx} browser-id] (let [browser (get-in db [:browser/browsers browser-id])] (fx/merge cofx - {:db (assoc db :browser/options + {:db (assoc db + :browser/options {:browser-id browser-id})} (update-browser browser) (navigation/set-stack-root :browser-stack :browser) @@ -341,16 +364,19 @@ [_ message] {:browser/send-to-bridge message}) -(defn web3-sign-message? [method] +(defn web3-sign-message? + [method] (#{constants/web3-sign-typed-data constants/web3-sign-typed-data-v3 constants/web3-sign-typed-data-v4 constants/web3-personal-sign - constants/web3-eth-sign constants/web3-keycard-sign-typed-data} method)) + constants/web3-eth-sign constants/web3-keycard-sign-typed-data} + method)) (fx/defn web3-send-async [cofx dapp-name {:keys [method params id] :as payload} message-id] (let [message? (web3-sign-message? method) dapps-address (get-in cofx [:db :multiaccount :dapps-address]) - typed? (and (not= constants/web3-personal-sign method) (not= constants/web3-eth-sign method))] + typed? (and (not= constants/web3-personal-sign method) + (not= constants/web3-eth-sign method))] (if (or message? (= constants/web3-send-transaction method)) (let [[address data] (cond (and (= method constants/web3-keycard-sign-typed-data) (not (vector? params))) @@ -359,34 +385,38 @@ message? (normalize-sign-message-params params typed?) :else [nil nil])] (when (or (not message?) (and address data)) - (signing/sign cofx (merge - (if message? - {:message {:address address - :data data - :v4 (= constants/web3-sign-typed-data-v4 method) - :typed? typed? - :pinless? (= method constants/web3-keycard-sign-typed-data) - :from dapps-address}} - {:tx-obj (-> params - first - (update :from #(or % dapps-address)) - (dissoc :gasPrice))}) - {:on-result [:browser.dapp/transaction-on-result message-id id] - :on-error [:browser.dapp/transaction-on-error message-id]})))) + (signing/sign cofx + (merge + (if message? + {:message {:address address + :data data + :v4 (= constants/web3-sign-typed-data-v4 method) + :typed? typed? + :pinless? (= method constants/web3-keycard-sign-typed-data) + :from dapps-address}} + {:tx-obj (-> params + first + (update :from #(or % dapps-address)) + (dissoc :gasPrice))}) + {:on-result [:browser.dapp/transaction-on-result message-id id] + :on-error [:browser.dapp/transaction-on-error message-id]})))) (cond (#{"eth_accounts" "eth_coinbase"} method) - (send-to-bridge cofx {:type constants/web3-send-async-callback - :messageId message-id - :result {:jsonrpc "2.0" - :id (int id) - :result (if (= method "eth_coinbase") dapps-address [dapps-address])}}) + (send-to-bridge + cofx + {:type constants/web3-send-async-callback + :messageId message-id + :result {:jsonrpc "2.0" + :id (int id) + :result (if (= method "eth_coinbase") dapps-address [dapps-address])}}) (= method "personal_ecRecover") - {:signing.fx/recover-message {:params {:message (first params) + {:signing.fx/recover-message {:params {:message (first params) :signature (second params)} - :on-completed #(re-frame/dispatch [:browser.callback/call-rpc - {:type constants/web3-send-async-callback - :messageId message-id - :result (types/json->clj %)}])}} + :on-completed #(re-frame/dispatch + [:browser.callback/call-rpc + {:type constants/web3-send-async-callback + :messageId message-id + :result (types/json->clj %)}])}} (= method "wallet_switchEthereumChain") (eip3326/handle-switch-ethereum-chain cofx dapp-name id message-id (first params)) @@ -401,7 +431,8 @@ :error %1 :result %2}])]})))) -(fx/defn handle-no-permissions [cofx {:keys [method id]} message-id] +(fx/defn handle-no-permissions + [cofx {:keys [method id]} message-id] (if (= method "eth_accounts") ;; eth_accounts returns empty array for compatibility with meta-mask (send-to-bridge cofx @@ -420,7 +451,8 @@ "keycard_signTypedData" "eth_signTypedData" "personal_sign" "personal_ecRecover"}) -(defn has-permissions? [{:dapps/keys [permissions]} dapp-name method] +(defn has-permissions? + [{:dapps/keys [permissions]} dapp-name method] (boolean (and (permissioned-method method) (not (some #{constants/dapp-permission-web3} (get-in permissions [dapp-name :permissions])))))) @@ -449,12 +481,18 @@ (fx/defn process-bridge-message {:events [:browser/bridge-message-received]} [{:keys [db] :as cofx} message] - (let [browser (get-current-browser db) - url-original (get-current-url browser) - data (types/json->clj message) + (let [browser (get-current-browser + db) + url-original (get-current-url + browser) + data (types/json->clj + message) {{:keys [url]} :navState :keys [type permission payload messageId params]} data - {:keys [dapp? name]} browser - dapp-name (if dapp? name (http/url-host url-original))] + {:keys [dapp? name]} browser + dapp-name (if dapp? + name + (http/url-host + url-original))] (cond (and (= type constants/history-state-changed) (not= "about:blank" url)) @@ -474,10 +512,13 @@ (re-frame/reg-fx :browser/send-to-bridge (fn [message] - (let [^js webview @webview-ref/webview-ref - msg (str "(function() { var __send = function() { if (ReactNativeWebView.onMessage) { ReactNativeWebView.onMessage('" - (types/clj->json message) - "');} else {setTimeout(__send, 0)}}; __send();})();")] + (let + [^js webview @webview-ref/webview-ref + msg + (str + "(function() { var __send = function() { if (ReactNativeWebView.onMessage) { ReactNativeWebView.onMessage('" + (types/clj->json message) + "');} else {setTimeout(__send, 0)}}; __send();})();")] (when (and message webview) (.injectJavaScript webview msg))))) @@ -508,7 +549,8 @@ (fn [] (status/clear-web-data))) -(defn share-link [url] +(defn share-link + [url] (let [link (links/generate-link :browse :external url) message (i18n/label :t/share-dapp-text {:link link})] (list-selection/open-share {:message message}))) @@ -523,7 +565,8 @@ (multiaccounts.update/multiaccount-update :dapps-address address {}) #(when (= (:view-id db) :browser) (merge (navigation/navigate-back %) - {:dispatch [:browser.ui/browser-item-selected (get-in db [:browser/options :browser-id])]})))) + {:dispatch [:browser.ui/browser-item-selected + (get-in db [:browser/options :browser-id])]})))) (fx/defn open-empty-tab {:events [:browser.ui/open-empty-tab]} @@ -562,5 +605,5 @@ (assoc acc url bookmark)) {} bookmarks) - stored-bookmarks (get-in db [:bookmarks/bookmarks])] + stored-bookmarks (get-in db [:bookmarks/bookmarks])] {:db (assoc-in db [:bookmarks/bookmarks] (merge stored-bookmarks changed-bookmarks))})) diff --git a/src/status_im/browser/core_test.cljs b/src/status_im/browser/core_test.cljs index 34cf4bfb6a..bc5bf31f88 100644 --- a/src/status_im/browser/core_test.cljs +++ b/src/status_im/browser/core_test.cljs @@ -14,7 +14,8 @@ nil (keys expected-browser)))) -(defn get-dapp-id [result dapp-url] +(defn get-dapp-id + [result dapp-url] (some #(when (= (http/normalize-and-decode-url dapp-url) (first (:history %))) (:browser-id %)) (vals (get-in result [:db :browser/browsers])))) @@ -24,31 +25,31 @@ (testing "user opens a dapp" (let [result-open (browser/open-url {:db {} :now 1} dapp1-url) - dapp1-id (get-dapp-id result-open dapp1-url)] + dapp1-id (get-dapp-id result-open dapp1-url)] (is (= dapp1-id (get-in result-open [:db :browser/options :browser-id])) "browser-id should be dapp1-url") (is (not (has-wrong-properties? result-open dapp1-id - {:browser-id dapp1-id + {:browser-id dapp1-id :history-index 0 - :history ["https://cryptokitties.co"] - :dapp? false - :name "Browser"})) + :history ["https://cryptokitties.co"] + :dapp? false + :name "Browser"})) "some properties of the browser are not correct") (testing "then a second dapp" - (let [result-open-2 (browser/open-url {:db (:db result-open) + (let [result-open-2 (browser/open-url {:db (:db result-open) :now 2} dapp2-url) - dapp2-id (get-dapp-id result-open-2 dapp2-url)] + dapp2-id (get-dapp-id result-open-2 dapp2-url)] (is (= dapp2-id (get-in result-open-2 [:db :browser/options :browser-id])) "browser-id should be dapp2 host") (is (not (has-wrong-properties? result-open-2 dapp2-id - {:browser-id dapp2-id + {:browser-id dapp2-id :history-index 0 - :history ["http://test2.com"] - :dapp? false})) + :history ["http://test2.com"] + :dapp? false})) "some properties of the browser are not correct") (testing "then removes the second dapp" @@ -58,17 +59,17 @@ "the second dapp shouldn't be in the browser list anymore"))))) (testing "then opens the dapp again" - (let [result-open-existing (browser/open-existing-browser {:db (:db result-open) + (let [result-open-existing (browser/open-existing-browser {:db (:db result-open) :now 2} dapp1-id) - dapp1-url2 (str "https://" dapp1-url "/nav2")] + dapp1-url2 (str "https://" dapp1-url "/nav2")] (is (not (has-wrong-properties? result-open-existing dapp1-id - {:browser-id dapp1-id + {:browser-id dapp1-id :history-index 0 - :history ["https://cryptokitties.co"] - :dapp? false - :name "Browser"})) + :history ["https://cryptokitties.co"] + :dapp? false + :name "Browser"})) "some properties of the browser are not correct") (is (nil? (browser/navigate-to-next-page result-open-existing)) "nothing should happen if user tries to navigate to next page") @@ -77,40 +78,42 @@ (testing "then navigates to a new url in the dapp" (let [result-navigate (browser/navigation-state-changed - {:db (:db result-open-existing) + {:db (:db result-open-existing) :now 4} - (clj->js {"url" dapp1-url2 + (clj->js {"url" dapp1-url2 "loading" false}) false)] (is (not (has-wrong-properties? result-navigate dapp1-id - {:browser-id dapp1-id + {:browser-id dapp1-id :history-index 1 - :history ["https://cryptokitties.co" dapp1-url2] - :dapp? false - :name "Browser"})) + :history ["https://cryptokitties.co" dapp1-url2] + :dapp? false + :name "Browser"})) "some properties of the browser are not correct") (testing "then navigates to previous page" - (let [result-previous (browser/navigate-to-previous-page {:db (:db result-navigate) + (let [result-previous (browser/navigate-to-previous-page {:db (:db result-navigate) :now 5})] - (is (not (has-wrong-properties? result-previous - dapp1-id - {:browser-id dapp1-id - :history-index 0 - :history ["https://cryptokitties.co" dapp1-url2] - :dapp? false - :name "Browser"})) - "some properties of the browser are not correct") + (is + (not (has-wrong-properties? result-previous + dapp1-id + {:browser-id dapp1-id + :history-index 0 + :history ["https://cryptokitties.co" dapp1-url2] + :dapp? false + :name "Browser"})) + "some properties of the browser are not correct") (testing "then navigates to next page") - (let [result-next (browser/navigate-to-next-page {:db (:db result-previous) + (let [result-next (browser/navigate-to-next-page {:db (:db result-previous) :now 6})] - (is (not (has-wrong-properties? result-next - dapp1-id - {:browser-id dapp1-id - :history-index 1 - :history ["https://cryptokitties.co" dapp1-url2] - :dapp? false - :name "Browser"})) + (is (not + (has-wrong-properties? result-next + dapp1-id + {:browser-id dapp1-id + :history-index 1 + :history ["https://cryptokitties.co" dapp1-url2] + :dapp? false + :name "Browser"})) "some properties of the browser are not correct")))))))))))) diff --git a/src/status_im/browser/eip3085.cljs b/src/status_im/browser/eip3085.cljs index 74175f2a32..3f5ca861f8 100644 --- a/src/status_im/browser/eip3085.cljs +++ b/src/status_im/browser/eip3085.cljs @@ -1,69 +1,76 @@ -;reference https://eips.ethereum.org/EIPS/eip-3085 EIP-3085: Wallet Add Ethereum Chain RPC Method (`wallet_addEthereumChain`) +;reference https://eips.ethereum.org/EIPS/eip-3085 EIP-3085: Wallet Add Ethereum Chain RPC Method +;(`wallet_addEthereumChain`) (ns status-im.browser.eip3085 - (:require [status-im.constants :as constants] - [clojure.string :as string] + (:require [clojure.string :as string] [re-frame.core :as re-frame] - [status-im.utils.fx :as fx] - [status-im.network.core :as network] - [taoensso.timbre :as log] - [status-im.utils.random :as random] + [status-im.constants :as constants] [status-im.ethereum.json-rpc :as json-rpc] - [status-im.ui.screens.browser.eip3085.sheet :as sheet])) + [status-im.network.core :as network] + [status-im.ui.screens.browser.eip3085.sheet :as sheet] + [status-im.utils.fx :as fx] + [status-im.utils.random :as random] + [taoensso.timbre :as log])) (fx/defn send-success-call-to-bridge {:events [:eip3085/send-success-call-to-bridge]} [_ id messageId] - {:browser/send-to-bridge {:type constants/web3-send-async-callback + {:browser/send-to-bridge {:type constants/web3-send-async-callback :messageId messageId - :result {:jsonrpc "2.0" - :id (int id) - :result nil}}}) + :result {:jsonrpc "2.0" + :id (int id) + :result nil}}}) (fx/defn allow-permission {:events [:eip3085.ui/dapp-permission-allowed]} [{:keys [db] :as cofx} message-id {:keys [new-networks id]}] - {:db (assoc db :networks/networks new-networks) - ::json-rpc/call [{:method "settings_saveSetting" - :params [:networks/networks (vals new-networks)] - :on-success #(re-frame/dispatch [:eip3085/send-success-call-to-bridge cofx id message-id]) + {:db (assoc db :networks/networks new-networks) + ::json-rpc/call [{:method "settings_saveSetting" + :params [:networks/networks (vals new-networks)] + :on-success #(re-frame/dispatch [:eip3085/send-success-call-to-bridge cofx id + message-id]) :on-error #(log/error "failed to perform settings_saveSetting" %)}] - :dispatch [:bottom-sheet/hide]}) + :dispatch [:bottom-sheet/hide]}) (fx/defn deny-permission {:events [:eip3085.ui/dapp-permission-denied]} [_ message-id _] - {:browser/send-to-bridge {:type constants/web3-send-async-callback + {:browser/send-to-bridge {:type constants/web3-send-async-callback :messageId message-id - :error {:code 4001 - :message "User rejected the request."}} - :dispatch [:bottom-sheet/hide]}) + :error {:code 4001 + :message "User rejected the request."}} + :dispatch [:bottom-sheet/hide]}) (fx/defn handle-add-ethereum-chain {:events [:eip3085/handle-add-ethereum-chain]} [{{:networks/keys [networks] :as db} :db :as cofx} - dapp-name id message-id {:keys [chainId blockExplorerUrls chainName iconUrls nativeCurrency rpcUrls] :as params}] - (let [manage {:name {:value chainName} - :symbol {:value (:symbol nativeCurrency)} - :url {:value (first rpcUrls)} + dapp-name id message-id + {:keys [chainId blockExplorerUrls chainName iconUrls nativeCurrency rpcUrls] :as params}] + (let [manage {:name {:value chainName} + :symbol {:value (:symbol nativeCurrency)} + :url {:value (first rpcUrls)} :network-id {:value chainId} - :chain {:value :custom}}] + :chain {:value :custom}}] (if (network/valid-manage? manage) (let [{:keys [name url chain network-id symbol]} manage - random-id (string/replace (random/id) "-" "") - network (network/new-network random-id - (:value name) - (:value symbol) - (:value url) - (:value chain) - (:value network-id)) - new-networks (assoc networks random-id network) - params (assoc params :new-networks new-networks :id id :new-network network)] + random-id (string/replace (random/id) "-" "") + network (network/new-network random-id + (:value name) + (:value symbol) + (:value url) + (:value chain) + (:value network-id)) + new-networks (assoc networks random-id network) + params (assoc params + :new-networks new-networks + :id id + :new-network network)] (if (network/chain-id-available? networks network) - {:dispatch [:bottom-sheet/show-sheet {:content (fn [] - [sheet/permissions-panel dapp-name message-id params])}]} + {:dispatch [:bottom-sheet/show-sheet + {:content (fn [] + [sheet/permissions-panel dapp-name message-id params])}]} (send-success-call-to-bridge cofx id message-id))) - {:browser/send-to-bridge {:type constants/web3-send-async-callback + {:browser/send-to-bridge {:type constants/web3-send-async-callback :messageId message-id - :error {:code -32602 - :message "invalid network parameters"}}}))) + :error {:code -32602 + :message "invalid network parameters"}}}))) diff --git a/src/status_im/browser/eip3326.cljs b/src/status_im/browser/eip3326.cljs index 22d1419e4b..a10f277c62 100644 --- a/src/status_im/browser/eip3326.cljs +++ b/src/status_im/browser/eip3326.cljs @@ -1,43 +1,54 @@ -;reference https://eips.ethereum.org/EIPS/eip-3326 EIP-3326: Wallet Switch Ethereum Chain RPC Method (`wallet_switchEthereumChain`) +;reference https://eips.ethereum.org/EIPS/eip-3326 EIP-3326: Wallet Switch Ethereum Chain RPC Method +;(`wallet_switchEthereumChain`) (ns status-im.browser.eip3326 (:require [status-im.constants :as constants] - [status-im.utils.fx :as fx] [status-im.ethereum.core :as ethereum] - [status-im.ui.screens.browser.eip3326.sheet :as sheet])) + [status-im.ui.screens.browser.eip3326.sheet :as sheet] + [status-im.utils.fx :as fx])) (fx/defn deny-permission {:events [:eip3326.ui/dapp-permission-denied]} [{:keys [db] :as cofx} message-id _] - {:browser/send-to-bridge {:type constants/web3-send-async-callback + {:browser/send-to-bridge {:type constants/web3-send-async-callback :messageId message-id - :error {:code 4001 - :message "User rejected the request."}} - :dispatch [:bottom-sheet/hide]}) + :error {:code 4001 + :message "User rejected the request."}} + :dispatch [:bottom-sheet/hide]}) (fx/defn handle-switch-ethereum-chain {:events [:eip3326/handle-switch-ethereum-chain]} [{:keys [db] :as cofx} dapp-name id message-id {:keys [chainId] :as params}] - (let [target-chain-id (js/parseInt chainId 16) - networks (vals (get-in db [:networks/networks])) - exist-chain-ids (set (map ethereum/network->chain-id networks)) + (let [target-chain-id (js/parseInt chainId 16) + networks (vals (get-in db [:networks/networks])) + exist-chain-ids (set (map ethereum/network->chain-id networks)) current-chain-id (ethereum/chain-id db)] (if (exist-chain-ids target-chain-id) (if (= current-chain-id target-chain-id) - {:browser/send-to-bridge {:type constants/web3-send-async-callback + {:browser/send-to-bridge {:type constants/web3-send-async-callback :messageId message-id - :result {:jsonrpc "2.0" - :id (int id) - :result nil}}} - (let [target-network (first (filter #(= (ethereum/network->chain-id %1) target-chain-id) networks)) + :result {:jsonrpc "2.0" + :id (int id) + :result nil}}} + (let [target-network (first (filter #(= (ethereum/network->chain-id %1) target-chain-id) + networks)) target-network-id (:id target-network) - current-network (ethereum/current-network db) - network-from (:name current-network) - network-to (:name target-network) - params (assoc params :target-network-id target-network-id :network-from network-from :network-to network-to)] - {:dispatch [:bottom-sheet/show-sheet {:content (fn [] - [sheet/permissions-panel dapp-name message-id params])}]})) - {:browser/send-to-bridge {:type constants/web3-send-async-callback + current-network (ethereum/current-network db) + network-from (:name current-network) + network-to (:name target-network) + params (assoc params + :target-network-id target-network-id + :network-from network-from + :network-to network-to)] + {:dispatch [:bottom-sheet/show-sheet + {:content (fn [] + [sheet/permissions-panel dapp-name message-id params])}]})) + {:browser/send-to-bridge {:type constants/web3-send-async-callback :messageId message-id - :error {:code 4902 - :message (str "Unrecognized chain ID: " target-chain-id ". Try adding the chain using wallet_addEthereumChain first.")}}}))) + :error + {:code 4902 + :message + (str + "Unrecognized chain ID: " + target-chain-id + ". Try adding the chain using wallet_addEthereumChain first.")}}}))) diff --git a/src/status_im/browser/permissions.cljs b/src/status_im/browser/permissions.cljs index f83c1c7210..612ac2dd2b 100644 --- a/src/status_im/browser/permissions.cljs +++ b/src/status_im/browser/permissions.cljs @@ -2,24 +2,24 @@ (:require [status-im.constants :as constants] [status-im.ethereum.json-rpc :as json-rpc] [status-im.i18n.i18n :as i18n] - [status-im2.navigation.events :as navigation] [status-im.qr-scanner.core :as qr-scanner] - [status-im.utils.fx :as fx])) + [status-im.utils.fx :as fx] + [status-im2.navigation.events :as navigation])) (declare process-next-permission) (declare send-response-to-bridge) (def supported-permissions - {constants/dapp-permission-qr-code {:yield-control? true - :allowed? true} - constants/dapp-permission-contact-code {:type :profile - :title (i18n/label :t/wants-to-access-profile) - :description (i18n/label :t/your-contact-code) - :icon :main-icons/profile} - constants/dapp-permission-web3 {:type :wallet - :title (i18n/label :t/dapp-would-like-to-connect-wallet) - :description (i18n/label :t/allowing-authorizes-this-dapp) - :icon :main-icons/wallet}}) + {constants/dapp-permission-qr-code {:yield-control? true + :allowed? true} + constants/dapp-permission-contact-code {:type :profile + :title (i18n/label :t/wants-to-access-profile) + :description (i18n/label :t/your-contact-code) + :icon :main-icons/profile} + constants/dapp-permission-web3 {:type :wallet + :title (i18n/label :t/dapp-would-like-to-connect-wallet) + :description (i18n/label :t/allowing-authorizes-this-dapp) + :icon :main-icons/wallet}}) (fx/defn permission-yield-control [{:keys [db] :as cofx} dapp-name permission message-id params] @@ -34,13 +34,15 @@ (fx/defn permission-show-permission [{:keys [db] :as cofx} dapp-name permission message-id yield-control?] - {:db (assoc-in db [:browser/options :show-permission] - {:requested-permission permission - :message-id message-id - :dapp-name dapp-name - :yield-control? yield-control?})}) + {:db (assoc-in db + [:browser/options :show-permission] + {:requested-permission permission + :message-id message-id + :dapp-name dapp-name + :yield-control? yield-control?})}) -(defn get-permission-data [cofx allowed-permission] +(defn get-permission-data + [cofx allowed-permission] (let [multiaccount (get-in cofx [:db :multiaccount])] (get {constants/dapp-permission-contact-code (:public-key multiaccount) constants/dapp-permission-web3 [(:dapps-address multiaccount)]} @@ -65,9 +67,9 @@ (disj dapp-permissions-set permission)) allowed-permissions {:dapp dapp-name :permissions (vec allowed-permissions-set)}] - {:db (assoc-in db [:dapps/permissions dapp-name] allowed-permissions) - ::json-rpc/call [{:method "permissions_addDappPermissions" - :params [allowed-permissions] + {:db (assoc-in db [:dapps/permissions dapp-name] allowed-permissions) + ::json-rpc/call [{:method "permissions_addDappPermissions" + :params [allowed-permissions] :on-success #()}]})) (fx/defn revoke-permissions @@ -75,8 +77,8 @@ [{:keys [db] :as cofx} dapp] (fx/merge cofx {:db (update-in db [:dapps/permissions] dissoc dapp) - ::json-rpc/call [{:method "permissions_deleteDappPermissions" - :params [dapp] + ::json-rpc/call [{:method "permissions_deleteDappPermissions" + :params [dapp] :on-success #()}]})) (fx/defn revoke-dapp-permissions @@ -105,7 +107,7 @@ {:db db} (let [pending-permissions (get-in db [:browser/options :pending-permissions]) next-permission (last pending-permissions) - new-cofx (update-in cofx [:db :browser/options :pending-permissions] butlast)] + new-cofx (update-in cofx [:db :browser/options :pending-permissions] butlast)] (when-let [{:keys [yield-control? permission message-id allowed? params]} next-permission] (if (and yield-control? allowed?) (permission-yield-control new-cofx dapp-name permission message-id params) @@ -136,7 +138,8 @@ "Add permission to set of allowed permission and process next permission" {:events [:browser.permissions.ui/dapp-permission-denied]} [{:keys [db] :as cofx}] - (let [{:keys [requested-permission message-id dapp-name]} (get-in db [:browser/options :show-permission])] + (let [{:keys [requested-permission message-id dapp-name]} (get-in db + [:browser/options :show-permission])] (fx/merge (assoc-in cofx [:db :browser/options :show-permission] nil) (send-response-to-bridge requested-permission message-id @@ -156,15 +159,18 @@ (not supported-permission) (send-response-to-bridge cofx permission message-id false nil) - (and (or permission-allowed? (:allowed? supported-permission)) (not (:yield-control? supported-permission))) + (and (or permission-allowed? (:allowed? supported-permission)) + (not (:yield-control? supported-permission))) (send-response-to-bridge cofx permission message-id true (get-permission-data cofx permission)) :else - (process-next-permission (update-in cofx [:db :browser/options :pending-permissions] - conj {:permission permission - :allowed? (or permission-allowed? - (:allowed? supported-permission)) - :yield-control? (:yield-control? supported-permission) - :params params - :message-id message-id}) + (process-next-permission (update-in cofx + [:db :browser/options :pending-permissions] + conj + {:permission permission + :allowed? (or permission-allowed? + (:allowed? supported-permission)) + :yield-control? (:yield-control? supported-permission) + :params params + :message-id message-id}) dapp-name)))) diff --git a/src/status_im/browser/permissions_test.cljs b/src/status_im/browser/permissions_test.cljs index be0cf2beb3..9b538bd66e 100644 --- a/src/status_im/browser/permissions_test.cljs +++ b/src/status_im/browser/permissions_test.cljs @@ -1,26 +1,28 @@ (ns status-im.browser.permissions-test (:require [cljs.test :refer-macros [deftest is testing]] - [status-im.browser.permissions :as permissions] - [status-im.utils.types :as types] [status-im.browser.core :as browser] - [status-im.browser.core-test :as core.tests])) + [status-im.browser.core-test :as core.tests] + [status-im.browser.permissions :as permissions] + [status-im.utils.types :as types])) (deftest permissions-test (let [dapp-name "test.com" dapp-name2 "test2.org" cofx {:db (assoc-in (:db (browser/open-url {:db {}} dapp-name)) - [:multiaccount :public-key] "public-key")} - dapp-id (core.tests/get-dapp-id cofx dapp-name)] + [:multiaccount :public-key] + "public-key")} + dapp-id (core.tests/get-dapp-id cofx dapp-name)] (testing "dapps permissions are initialized" (is (zero? (count (get-in cofx [:db :dapps/permissions])))) (is (= dapp-id (get-in cofx [:db :browser/options :browser-id])))) (testing "receiving an unsupported permission" (let [result-ask (browser/process-bridge-message cofx - (types/clj->json {:type "api-request" - :host dapp-name - :messageId 0 - :permission "FAKE_PERMISSION"}))] + (types/clj->json + {:type "api-request" + :host dapp-name + :messageId 0 + :permission "FAKE_PERMISSION"}))] (is (not (get-in result-ask [:browser/send-to-bridge :isAllowed]))))) (testing "receiving a supported permission" @@ -30,7 +32,10 @@ :messageId 1 :permission "contact-code"}))] (is (= (get-in result-ask [:db :browser/options :show-permission]) - {:requested-permission "contact-code" :dapp-name "test.com" :message-id 1 :yield-control? nil})) + {:requested-permission "contact-code" + :dapp-name "test.com" + :message-id 1 + :yield-control? nil})) (is (zero? (count (get-in result-ask [:db :dapps/permissions])))) (testing "then user accepts the supported permission" @@ -43,15 +48,16 @@ :permission "contact-code"}) "the data should have been sent to the bridge") (is (= (get-in accept-result [:db :dapps/permissions]) - {"test.com" {:dapp "test.com", :permissions ["contact-code"]}}) + {"test.com" {:dapp "test.com" :permissions ["contact-code"]}}) "the dapp should now have CONTACT_CODE permission") (testing "then dapp asks for permission again" (let [result-ask-again (browser/process-bridge-message {:db (:db accept-result)} - (types/clj->json {:type "api-request" - :host dapp-name - :messageId 2 - :permission "contact-code"}))] + (types/clj->json + {:type "api-request" + :host dapp-name + :messageId 2 + :permission "contact-code"}))] (is (= (get result-ask-again :browser/send-to-bridge) {:type "api-response" :isAllowed true @@ -63,12 +69,13 @@ (testing "then user switch to another dapp that asks for permissions" (let [new-dapp (browser/open-url {:db (:db accept-result)} dapp-name2) result-ask2 (browser/process-bridge-message {:db (:db new-dapp)} - (types/clj->json {:type "api-request" - :host dapp-name2 - :messageId 3 - :permission "contact-code"}))] + (types/clj->json + {:type "api-request" + :host dapp-name2 + :messageId 3 + :permission "contact-code"}))] (is (= (get-in result-ask2 [:db :dapps/permissions]) - {"test.com" {:dapp "test.com", :permissions ["contact-code"]}}) + {"test.com" {:dapp "test.com" :permissions ["contact-code"]}}) "there should only be permissions for dapp-name at that point") (is (nil? (get result-ask2 :browser/send-to-bridge)) "no message should be sent to the bridge") diff --git a/src/status_im/chat/db.cljs b/src/status_im/chat/db.cljs index 762876e9b9..62c37b17db 100644 --- a/src/status_im/chat/db.cljs +++ b/src/status_im/chat/db.cljs @@ -5,7 +5,8 @@ [{:keys [public? name]}] (str (when public? "#") name)) -(defn datemark? [{:keys [type]}] +(defn datemark? + [{:keys [type]}] (= type :datemark)) (defn intersperse-datemark @@ -20,22 +21,24 @@ M1 needs to be displayed before M2 so we bucket both in 1999-12-31" [{:keys [acc last-timestamp last-datemark]} {:keys [whisper-timestamp datemark] :as msg}] - (cond (empty? acc) ; initial element - {:last-timestamp whisper-timestamp - :last-datemark datemark - :acc (conj acc msg)} + (cond + (empty? acc) ; initial element + {:last-timestamp whisper-timestamp + :last-datemark datemark + :acc (conj acc msg)} - (and (not= last-datemark datemark) ; not the same day - (< whisper-timestamp last-timestamp)) ; not out-of-order - {:last-timestamp whisper-timestamp - :last-datemark datemark - :acc (conj acc {:value last-datemark ; intersperse datemark message - :type :datemark} - msg)} - :else - {:last-timestamp (min whisper-timestamp last-timestamp) ; use last datemark - :last-datemark last-datemark - :acc (conj acc (assoc msg :datemark last-datemark))})) + (and (not= last-datemark datemark) ; not the same day + (< whisper-timestamp last-timestamp)) ; not out-of-order + {:last-timestamp whisper-timestamp + :last-datemark datemark + :acc (conj acc + {:value last-datemark ; intersperse datemark message + :type :datemark} + msg)} + :else + {:last-timestamp (min whisper-timestamp last-timestamp) ; use last datemark + :last-datemark last-datemark + :acc (conj acc (assoc msg :datemark last-datemark))})) (defn add-datemarks "Add a datemark in between an ordered seq of messages when two datemarks are not @@ -44,17 +47,18 @@ (when (seq messages) (let [messages-with-datemarks (:acc (reduce intersperse-datemark {:acc []} messages))] ; Append last datemark - (conj messages-with-datemarks {:value (:datemark (peek messages-with-datemarks)) - :type :datemark})))) + (conj messages-with-datemarks + {:value (:datemark (peek messages-with-datemarks)) + :type :datemark})))) (defn last-gap "last-gap is a special gap that is put last in the message stream" [chat-id synced-from] - {:message-id "0x123" - :message-type constants/message-type-gap - :chat-id chat-id - :content-type constants/content-type-gap - :gap-ids #{:first-gap} + {:message-id "0x123" + :message-type constants/message-type-gap + :chat-id chat-id + :content-type constants/content-type-gap + :gap-ids #{:first-gap} :gap-parameters {:from synced-from}}) (defn collapse-gaps @@ -66,17 +70,17 @@ (fn [acc {:keys [gap-parameters message-id] :as message}] (let [last-element (peek acc)] (cond - ;; If it's a message, just add + ;; If it's a message, just add (empty? gap-parameters) (conj acc message) - ;; Both are gaps, merge them + ;; Both are gaps, merge them (and (seq (:gap-parameters last-element)) (seq gap-parameters)) (conj (pop acc) (update last-element :gap-ids conj message-id)) - ;; it's a gap + ;; it's a gap :else (conj acc (assoc message :gap-ids #{message-id}))))) [] diff --git a/src/status_im/chat/db_test.cljs b/src/status_im/chat/db_test.cljs index d05fffa67e..edecf0bbc5 100644 --- a/src/status_im/chat/db_test.cljs +++ b/src/status_im/chat/db_test.cljs @@ -4,37 +4,41 @@ (deftest group-chat-name (testing "it prepends # if it's a public chat" - (is (= "#withhash" (db/group-chat-name {:group-chat true - :chat-id "1" - :public? true - :name "withhash"})))) + (is (= "#withhash" + (db/group-chat-name {:group-chat true + :chat-id "1" + :public? true + :name "withhash"})))) (testing "it leaves the name unchanged if it's a group chat" - (is (= "unchanged" (db/group-chat-name {:group-chat true - :chat-id "1" - :name "unchanged"}))))) + (is (= "unchanged" + (db/group-chat-name {:group-chat true + :chat-id "1" + :name "unchanged"}))))) (deftest intersperse-datemarks (testing "it mantains the order even when timestamps are across days" - (let [message-1 {:datemark "Dec 31, 1999" - :whisper-timestamp 946641600000} ; 1999} - message-2 {:datemark "Jan 1, 2000" - :whisper-timestamp 946728000000} ; 2000 this will displayed in 1999 - message-3 {:datemark "Dec 31, 1999" - :whisper-timestamp 946641600000} ; 1999 - message-4 {:datemark "Jan 1, 2000" - :whisper-timestamp 946728000000} ; 2000 - ordered-messages [message-4 - message-3 - message-2 - message-1] + (let [message-1 {:datemark "Dec 31, 1999" + :whisper-timestamp 946641600000} ; 1999} + message-2 {:datemark "Jan 1, 2000" + :whisper-timestamp 946728000000} ; 2000 this will displayed in 1999 + message-3 {:datemark "Dec 31, 1999" + :whisper-timestamp 946641600000} ; 1999 + message-4 {:datemark "Jan 1, 2000" + :whisper-timestamp 946728000000} ; 2000 + ordered-messages [message-4 + message-3 + message-2 + message-1] [m1 d1 m2 m3 m4 d2] (db/add-datemarks ordered-messages)] (is (= "Jan 1, 2000" (:datemark m1))) - (is (= {:type :datemark - :value "Jan 1, 2000"} d1)) + (is (= {:type :datemark + :value "Jan 1, 2000"} + d1)) (is (= "Dec 31, 1999" (:datemark m2) (:datemark m3) (:datemark m4))) - (is (= {:type :datemark - :value "Dec 31, 1999"} d2))))) + (is (= {:type :datemark + :value "Dec 31, 1999"} + d2))))) diff --git a/src/status_im/chat/default_chats.cljs b/src/status_im/chat/default_chats.cljs index 18532d7282..3b982c1569 100644 --- a/src/status_im/chat/default_chats.cljs +++ b/src/status_im/chat/default_chats.cljs @@ -1,5 +1,4 @@ -(ns status-im.chat.default-chats - (:require-macros [status-im.utils.slurp :refer [slurp]])) +(ns status-im.chat.default-chats (:require-macros [status-im.utils.slurp :refer [slurp]])) (def default-chats (slurp "resources/chats.json")) \ No newline at end of file diff --git a/src/status_im/chat/models.cljs b/src/status_im/chat/models.cljs index 638d77185f..8a3598298e 100644 --- a/src/status_im/chat/models.cljs +++ b/src/status_im/chat/models.cljs @@ -1,31 +1,33 @@ (ns status-im.chat.models - (:require [re-frame.core :as re-frame] - [taoensso.timbre :as log] - [status-im.multiaccounts.model :as multiaccounts.model] + (:require [clojure.set :as set] + [quo.design-system.colors :as colors] + [re-frame.core :as re-frame] + [status-im.add-new.db :as new-public-chat.db] + [status-im.chat.models.loading :as loading] [status-im.chat.models.message-list :as message-list] - [status-im2.contexts.chat.messages.message.delete-message.events :as delete-message] - [status-im2.contexts.chat.messages.message.delete-message-for-me.events :as delete-for-me] + [status-im.constants :as constants] [status-im.data-store.chats :as chats-store] - [status-im.mailserver.core :as mailserver] [status-im.data-store.contacts :as contacts-store] [status-im.ethereum.json-rpc :as json-rpc] [status-im.i18n.i18n :as i18n] - [quo.design-system.colors :as colors] - [status-im.constants :as constants] + [status-im.mailserver.core :as mailserver] + [status-im.multiaccounts.model :as multiaccounts.model] + [status-im.ui.screens.chat.state :as chat.state] [status-im.utils.clocks :as utils.clocks] [status-im.utils.fx :as fx] - [status-im.utils.utils :as utils] [status-im.utils.types :as types] - [status-im.add-new.db :as new-public-chat.db] - [status-im.chat.models.loading :as loading] - [status-im.ui.screens.chat.state :as chat.state] - [clojure.set :as set] - [status-im2.navigation.events :as navigation])) + [status-im.utils.utils :as utils] + [status-im2.contexts.chat.messages.message.delete-message-for-me.events :as delete-for-me] + [status-im2.contexts.chat.messages.message.delete-message.events :as delete-message] + [status-im2.navigation.events :as navigation] + [taoensso.timbre :as log])) -(defn chats [] +(defn chats + [] (:chats (types/json->clj (js/require "./chats.js")))) -(defn- get-chat [cofx chat-id] +(defn- get-chat + [cofx chat-id] (get-in cofx [:db :chats chat-id])) (defn multi-user-chat? @@ -49,7 +51,8 @@ ([cofx chat-id] (community-chat? (get-chat cofx chat-id)))) -(defn active-chat? [cofx chat-id] +(defn active-chat? + [cofx chat-id] (let [chat (get-chat cofx chat-id)] (:active chat))) @@ -85,31 +88,34 @@ (defn- create-new-chat [chat-id {:keys [db now]}] (let [name (get-in db [:contacts/contacts chat-id :name])] - {:chat-id chat-id - :name (or name "") - :color (rand-nth colors/chat-colors) - :chat-type constants/one-to-one-chat-type - :group-chat false - :timestamp now - :contacts #{chat-id} - :last-clock-value 0})) + {:chat-id chat-id + :name (or name "") + :color (rand-nth colors/chat-colors) + :chat-type constants/one-to-one-chat-type + :group-chat false + :timestamp now + :contacts #{chat-id} + :last-clock-value 0})) -(defn map-chats [{:keys [db] :as cofx}] +(defn map-chats + [{:keys [db] :as cofx}] (fn [val] (assoc (merge (or (get (:chats db) (:chat-id val)) (create-new-chat (:chat-id val) cofx)) val) - :invitation-admin (:invitation-admin val)))) + :invitation-admin + (:invitation-admin val)))) -(defn filter-chats [db] +(defn filter-chats + [db] (fn [val] (and (not (get-in db [:chats (:chat-id val)])) (:public? val)))) (fx/defn leave-removed-chat [{{:keys [view-id current-chat-id chats]} :db - :as cofx}] + :as cofx}] (when (and (= view-id :chat) (not (contains? chats current-chat-id))) (navigation/navigate-back cofx))) @@ -118,18 +124,19 @@ "Add chats to db and update" [{: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}] - (if (not (or active muted)) - (update acc :removed-chats conj chat-id) - (cond-> acc - (and (not profile-public-key) (not timeline?) (not community-id) active) - (update :chats-home-list conj chat-id) - :always - (assoc-in [:all-chats chat-id] chat)))) - {:all-chats {} - :chats-home-list #{} - :removed-chats #{}} - (map (map-chats cofx) chats))] + (reduce + (fn [acc {:keys [chat-id profile-public-key timeline? community-id active muted] :as chat}] + (if (not (or active muted)) + (update acc :removed-chats conj chat-id) + (cond-> acc + (and (not profile-public-key) (not timeline?) (not community-id) active) + (update :chats-home-list conj chat-id) + :always + (assoc-in [:all-chats chat-id] chat)))) + {:all-chats {} + :chats-home-list #{} + :removed-chats #{}} + (map (map-chats cofx) chats))] (fx/merge cofx (merge {:db (-> db @@ -147,34 +154,38 @@ "Clears history of the particular chat" [{:keys [db] :as cofx} chat-id remove-chat?] (let [{:keys [last-message public? - deleted-at-clock-value]} (get-in db [:chats chat-id]) + deleted-at-clock-value]} + (get-in db [:chats chat-id]) last-message-clock-value (if (and public? remove-chat?) 0 (or (:clock-value last-message) deleted-at-clock-value (utils.clocks/send 0)))] - {:db (-> db - (assoc-in [:messages chat-id] {}) - (update-in [:message-lists] dissoc chat-id) - (update :chats (fn [chats] - (if (contains? chats chat-id) - (update chats chat-id merge - {:last-message nil - :unviewed-messages-count 0 - :unviewed-mentions-count 0 - :deleted-at-clock-value last-message-clock-value}) - chats))))})) + {:db (-> db + (assoc-in [:messages chat-id] {}) + (update-in [:message-lists] dissoc chat-id) + (update :chats + (fn [chats] + (if (contains? chats chat-id) + (update chats + chat-id + merge + {:last-message nil + :unviewed-messages-count 0 + :unviewed-mentions-count 0 + :deleted-at-clock-value last-message-clock-value}) + chats))))})) (fx/defn clear-history-handler "Clears history of the particular chat" {:events [:chat.ui/clear-history]} [{:keys [db] :as cofx} chat-id remove-chat?] (fx/merge cofx - {:db db - ::json-rpc/call [{:method "wakuext_clearHistory" - :params [{:id chat-id}] + {:db db + ::json-rpc/call [{:method "wakuext_clearHistory" + :params [{:id chat-id}] :on-success #(re-frame/dispatch [::history-cleared chat-id %]) - :on-error #(log/error "failed to clear history " chat-id %)}]} + :on-error #(log/error "failed to clear history " chat-id %)}]} (clear-history chat-id remove-chat?))) (fx/defn chat-deactivated @@ -187,15 +198,15 @@ [{:keys [db now] :as cofx} chat-id] (fx/merge cofx - {:db (-> (if (get-in db [:chats chat-id :muted]) - (assoc-in db [:chats chat-id :active] false) - (update db :chats dissoc chat-id)) - (update :chats-home-list disj chat-id) - (assoc :current-chat-id nil)) - ::json-rpc/call [{:method "wakuext_deactivateChat" - :params [{:id chat-id}] + {:db (-> (if (get-in db [:chats chat-id :muted]) + (assoc-in db [:chats chat-id :active] false) + (update db :chats dissoc chat-id)) + (update :chats-home-list disj chat-id) + (assoc :current-chat-id nil)) + ::json-rpc/call [{:method "wakuext_deactivateChat" + :params [{:id chat-id}] :on-success #(re-frame/dispatch [::chat-deactivated chat-id]) - :on-error #(log/error "failed to create public chat" chat-id %)}]} + :on-error #(log/error "failed to create public chat" chat-id %)}]} (clear-history chat-id true))) (fx/defn offload-messages @@ -291,16 +302,16 @@ (fx/defn handle-one-to-one-chat-created {:events [::one-to-one-chat-created]} [{:keys [db]} chat-id response] - (let [chat (chats-store/<-rpc (first (:chats response))) + (let [chat (chats-store/<-rpc (first (:chats response))) contact-rpc (first (:contacts response)) - contact (when contact-rpc (contacts-store/<-rpc contact-rpc))] - {:db (cond-> db - contact - (assoc-in [:contacts/contacts chat-id] contact) - :always - (assoc-in [:chats chat-id] chat) - :always - (update :chats-home-list conj chat-id)) + contact (when contact-rpc (contacts-store/<-rpc contact-rpc))] + {:db (cond-> db + contact + (assoc-in [:contacts/contacts chat-id] contact) + :always + (assoc-in [:chats chat-id] chat) + :always + (update :chats-home-list conj chat-id)) :dispatch [:chat.ui/navigate-to-chat chat-id]})) (fx/defn navigate-to-user-pinned-messages @@ -315,30 +326,33 @@ [{:keys [db] :as cofx} chat-id ens-name] ;; don't allow to open chat with yourself (when (not= (multiaccounts.model/current-public-key cofx) chat-id) - {::json-rpc/call [{:method "wakuext_createOneToOneChat" - :params [{:id chat-id :ensName ens-name}] + {::json-rpc/call [{:method "wakuext_createOneToOneChat" + :params [{:id chat-id :ensName ens-name}] :on-success #(re-frame/dispatch [::one-to-one-chat-created chat-id %]) - :on-error #(log/error "failed to create one-to-on chat" chat-id %)}]})) + :on-error #(log/error "failed to create one-to-on chat" chat-id %)}]})) -(defn profile-chat-topic [public-key] +(defn profile-chat-topic + [public-key] (str "@" public-key)) -(defn my-profile-chat-topic [db] +(defn my-profile-chat-topic + [db] (profile-chat-topic (get-in db [:multiaccount :public-key]))) (fx/defn handle-public-chat-created {:events [::public-chat-created]} [{:keys [db]} chat-id response] - {:db (-> db - (assoc-in [:chats chat-id] (chats-store/<-rpc (first (:chats response)))) - (update :chats-home-list conj chat-id)) + {:db (-> db + (assoc-in [:chats chat-id] (chats-store/<-rpc (first (:chats response)))) + (update :chats-home-list conj chat-id)) :dispatch [:chat.ui/navigate-to-chat chat-id]}) -(fx/defn create-public-chat-go [_ chat-id] - {::json-rpc/call [{:method "wakuext_createPublicChat" - :params [{:id chat-id}] +(fx/defn create-public-chat-go + [_ chat-id] + {::json-rpc/call [{:method "wakuext_createPublicChat" + :params [{:id chat-id}] :on-success #(re-frame/dispatch [::public-chat-created chat-id %]) - :on-error #(log/error "failed to create public chat" chat-id %)}]}) + :on-error #(log/error "failed to create public chat" chat-id %)}]}) (fx/defn start-public-chat "Starts a new public chat" @@ -373,10 +387,10 @@ (let [chat-id (profile-chat-topic profile-public-key)] (if (active-chat? cofx chat-id) {:dispatch [::profile-chat-created chat-id nil navigate-to?]} - {::json-rpc/call [{:method "wakuext_createProfileChat" - :params [{:id profile-public-key}] + {::json-rpc/call [{:method "wakuext_createProfileChat" + :params [{:id profile-public-key}] :on-success #(re-frame/dispatch [::profile-chat-created chat-id % navigate-to?]) - :on-error #(log/error "failed to create profile chat" chat-id %)}]}))) + :on-error #(log/error "failed to create profile chat" chat-id %)}]}))) (fx/defn disable-chat-cooldown "Turns off chat cooldown (protection against message spamming)" @@ -407,10 +421,10 @@ {:events [::mute-chat-toggled]} [{:keys [db] :as cofx} chat-id muted?] (let [method (if muted? "wakuext_muteChat" "wakuext_unmuteChat")] - {:db (assoc-in db [:chats chat-id :muted] muted?) - ::json-rpc/call [{:method method - :params [chat-id] - :on-error #(re-frame/dispatch [::mute-chat-failed chat-id muted? %]) + {:db (assoc-in db [:chats chat-id :muted] muted?) + ::json-rpc/call [{:method method + :params [chat-id] + :on-error #(re-frame/dispatch [::mute-chat-failed chat-id muted? %]) :on-success #(re-frame/dispatch [::mute-chat-toggled-successfully chat-id])}]})) (fx/defn show-profile @@ -469,18 +483,18 @@ (fx/defn fill-gaps [cofx chat-id gap-ids] - {::json-rpc/call [{:method "wakuext_fillGaps" - :params [chat-id gap-ids] + {::json-rpc/call [{:method "wakuext_fillGaps" + :params [chat-id gap-ids] :on-success #(re-frame/dispatch [::gaps-filled chat-id gap-ids %]) - :on-error #(re-frame/dispatch [::gaps-failed chat-id gap-ids %])}]}) + :on-error #(re-frame/dispatch [::gaps-failed chat-id gap-ids %])}]}) (fx/defn sync-chat-from-sync-from [cofx chat-id] (log/debug "syncing chat from sync from") - {::json-rpc/call [{:method "wakuext_syncChatFromSyncedFrom" - :params [chat-id] + {::json-rpc/call [{:method "wakuext_syncChatFromSyncedFrom" + :params [chat-id] :on-success #(re-frame/dispatch [::sync-chat-from-sync-from-success chat-id %]) - :on-error #(re-frame/dispatch [::sync-chat-from-sync-from-failed chat-id %])}]}) + :on-error #(re-frame/dispatch [::sync-chat-from-sync-from-failed chat-id %])}]}) (fx/defn chat-ui-fill-gaps {:events [:chat.ui/fill-gaps]} diff --git a/src/status_im/chat/models/images.cljs b/src/status_im/chat/models/images.cljs index 998dc9ea6e..e6aaa613fb 100644 --- a/src/status_im/chat/models/images.cljs +++ b/src/status_im/chat/models/images.cljs @@ -1,24 +1,25 @@ (ns status-im.chat.models.images - (:require [re-frame.core :as re-frame] - [status-im.utils.fx :as fx] - ["@react-native-community/cameraroll" :as CameraRoll] + (:require ["@react-native-community/cameraroll" :as CameraRoll] ["react-native-blob-util" :default ReactNativeBlobUtil] - [status-im.utils.types :as types] - [status-im.utils.config :as config] + [clojure.string :as string] + [re-frame.core :as re-frame] + [status-im.chat.models :as chat] + [status-im.i18n.i18n :as i18n] [status-im.ui.components.permissions :as permissions] [status-im.ui.components.react :as react] - [status-im.utils.image-processing :as image-processing] - [taoensso.timbre :as log] - [clojure.string :as string] - [status-im.i18n.i18n :as i18n] - [status-im.utils.utils :as utils] - [status-im.utils.platform :as platform] + [status-im.utils.config :as config] [status-im.utils.fs :as fs] - [status-im.chat.models :as chat])) + [status-im.utils.fx :as fx] + [status-im.utils.image-processing :as image-processing] + [status-im.utils.platform :as platform] + [status-im.utils.types :as types] + [status-im.utils.utils :as utils] + [taoensso.timbre :as log])) (def maximum-image-size-px 2000) -(defn- resize-and-call [uri cb] +(defn- resize-and-call + [uri cb] (react/image-get-size uri (fn [width height] @@ -34,16 +35,19 @@ (cb path))) #(log/error "could not resize image" %)))))) -(defn result->id [^js result] +(defn result->id + [^js result] (if platform/ios? (.-localIdentifier result) (.-path result))) (def temp-image-url (str (fs/cache-dir) "/StatusIm_Image.jpeg")) -(defn download-image-http [base64-uri on-success] - (-> (.config ReactNativeBlobUtil (clj->js {:trusty platform/ios? - :path temp-image-url})) +(defn download-image-http + [base64-uri on-success] + (-> (.config ReactNativeBlobUtil + (clj->js {:trusty platform/ios? + :path temp-image-url})) (.fetch "GET" base64-uri) (.then #(on-success (.path %))) (.catch #(log/error "could not save image")))) @@ -70,15 +74,16 @@ ::chat-open-image-picker-camera (fn [current-chat-id] (react/show-image-picker-camera - #(re-frame/dispatch [:chat.ui/image-captured current-chat-id (.-path %)]) {}))) + #(re-frame/dispatch [:chat.ui/image-captured current-chat-id (.-path %)]) + {}))) (re-frame/reg-fx ::chat-open-image-picker (fn [chat-id] (react/show-image-picker (fn [^js images] - ;; NOTE(Ferossgp): Because we can't highlight the already selected images inside - ;; gallery, we just clean previous state and set all newly picked images + ;; NOTE(Ferossgp): Because we can't highlight the already selected images inside + ;; gallery, we just clean previous state and set all newly picked images (when (and platform/ios? (pos? (count images))) (re-frame/dispatch [:chat.ui/clear-sending-images chat-id])) (doseq [^js result (if platform/ios? @@ -86,8 +91,8 @@ [images])] (resize-and-call (.-path result) #(re-frame/dispatch [:chat.ui/image-selected chat-id (result->id result) %])))) - ;; NOTE(Ferossgp): On android you cannot set max limit on images, when a user - ;; selects too many images the app crashes. + ;; NOTE(Ferossgp): On android you cannot set max limit on images, when a user + ;; selects too many images the app crashes. {:media-type "photo" :multiple platform/ios?}))) @@ -105,10 +110,14 @@ {:permissions [:read-external-storage] :on-allowed (fn [] (-> (if end-cursor - (.getPhotos CameraRoll #js {:first num :after end-cursor :assetType "Photos" :groupTypes "All"}) - (.getPhotos CameraRoll #js {:first num :assetType "Photos" :groupTypes "All"})) + (.getPhotos + CameraRoll + #js {:first num :after end-cursor :assetType "Photos" :groupTypes "All"}) + (.getPhotos CameraRoll + #js {:first num :assetType "Photos" :groupTypes "All"})) (.then #(let [response (types/js->clj %)] - (re-frame/dispatch [:on-camera-roll-get-photos (:edges response) (:page_info response) end-cursor]))) + (re-frame/dispatch [:on-camera-roll-get-photos (:edges response) + (:page_info response) end-cursor]))) (.catch #(log/warn "could not get camera roll photos"))))}))) (fx/defn image-captured diff --git a/src/status_im/chat/models/input.cljs b/src/status_im/chat/models/input.cljs index 846a7d3f5d..f17a9bf90f 100644 --- a/src/status_im/chat/models/input.cljs +++ b/src/status_im/chat/models/input.cljs @@ -37,8 +37,9 @@ (fx/defn set-timeline-input-text {:events [:chat.ui/set-timeline-input-text]} [{db :db} new-input] - {:db (assoc-in db [:chat/inputs (chat/my-profile-chat-topic db) :input-text] - (text->emoji new-input))}) + {:db (assoc-in db + [:chat/inputs (chat/my-profile-chat-topic db) :input-text] + (text->emoji new-input))}) (fx/defn select-mention {:events [:chat.ui/select-mention]} @@ -49,9 +50,9 @@ cursor (+ at-sign-idx (count name) 2)] (fx/merge cofx - {:db (-> db - (assoc-in [:chats/cursor chat-id] cursor) - (assoc-in [:chats/mention-suggestions chat-id] nil)) + {:db (-> db + (assoc-in [:chats/cursor chat-id] cursor) + (assoc-in [:chats/mention-suggestions chat-id] nil)) :set-text-input-value [chat-id new-text text-input-ref]} (set-chat-input-text new-text chat-id) ;; NOTE(rasom): Some keyboards do not react on selection property passed to @@ -74,34 +75,42 @@ :end end})) (mentions/recheck-at-idxs {alias user})))) -(defn- start-cooldown [{:keys [db]} cooldowns] +(defn- start-cooldown + [{:keys [db]} cooldowns] {:dispatch-later [{:dispatch [:chat/disable-cooldown] :ms (chat.constants/cooldown-periods-ms cooldowns chat.constants/default-cooldown-period-ms)}] :show-cooldown-warning nil :db (assoc db - :chat/cooldowns (if (= chat.constants/cooldown-reset-threshold cooldowns) - 0 - cooldowns) + :chat/cooldowns (if + (= + chat.constants/cooldown-reset-threshold + cooldowns) + 0 + cooldowns) :chat/spam-messages-frequency 0 - :chat/cooldown-enabled? true)}) + :chat/cooldown-enabled? true)}) (fx/defn process-cooldown "Process cooldown to protect against message spammers" [{{:keys [chat/last-outgoing-message-sent-at chat/cooldowns chat/spam-messages-frequency - current-chat-id] :as db} :db :as cofx}] + current-chat-id] + :as db} + :db + :as cofx}] (when (chat/public-chat? cofx current-chat-id) - (let [spamming-fast? (< (- (datetime/timestamp) last-outgoing-message-sent-at) - (+ chat.constants/spam-interval-ms (* 1000 cooldowns))) - spamming-frequently? (= chat.constants/spam-message-frequency-threshold spam-messages-frequency)] + (let [spamming-fast? (< (- (datetime/timestamp) last-outgoing-message-sent-at) + (+ chat.constants/spam-interval-ms (* 1000 cooldowns))) + spamming-frequently? (= chat.constants/spam-message-frequency-threshold + spam-messages-frequency)] (cond-> {:db (assoc db :chat/last-outgoing-message-sent-at (datetime/timestamp) - :chat/spam-messages-frequency (if spamming-fast? - (inc spam-messages-frequency) - 0))} + :chat/spam-messages-frequency (if spamming-fast? + (inc spam-messages-frequency) + 0))} (and spamming-fast? spamming-frequently?) (start-cooldown (inc cooldowns)))))) @@ -117,7 +126,8 @@ message) (assoc-in [:chat/inputs current-chat-id :metadata :editing-message] nil) (update-in [:chat/inputs current-chat-id :metadata] - dissoc :sending-image))}))) + dissoc + :sending-image))}))) (fx/defn edit-message "Sets reference to previous chat message and focuses on input" @@ -125,14 +135,15 @@ [{:keys [db] :as cofx} message] (let [current-chat-id (:current-chat-id db) - text (get-in message [:content :text])] + text (get-in message [:content :text])] {:dispatch [:chat.ui.input/set-chat-input-text text current-chat-id] - :db (-> db - (assoc-in [:chat/inputs current-chat-id :metadata :editing-message] - message) - (assoc-in [:chat/inputs current-chat-id :metadata :responding-to-message] nil) - (update-in [:chat/inputs current-chat-id :metadata] - dissoc :sending-image))})) + :db (-> db + (assoc-in [:chat/inputs current-chat-id :metadata :editing-message] + message) + (assoc-in [:chat/inputs current-chat-id :metadata :responding-to-message] nil) + (update-in [:chat/inputs current-chat-id :metadata] + dissoc + :sending-image))})) (fx/defn show-contact-request-input "Sets reference to previous chat message and focuses on input" @@ -146,7 +157,8 @@ nil) (assoc-in [:chat/inputs current-chat-id :metadata :editing-message] nil) (update-in [:chat/inputs current-chat-id :metadata] - dissoc :sending-image))})) + dissoc + :sending-image))})) (fx/defn cancel-message-reply "Cancels stage message reply" @@ -160,16 +172,16 @@ (when-not (string/blank? input-text) (let [{:keys [message-id]} (get-in db [:chat/inputs current-chat-id :metadata :responding-to-message]) - preferred-name (get-in db [:multiaccount :preferred-name]) - emoji? (message-content/emoji-only-content? {:text input-text - :response-to message-id})] + preferred-name (get-in db [:multiaccount :preferred-name]) + emoji? (message-content/emoji-only-content? {:text input-text + :response-to message-id})] {:chat-id current-chat-id :content-type (if emoji? constants/content-type-emoji constants/content-type-text) - :text input-text - :response-to message-id - :ens-name preferred-name}))) + :text input-text + :response-to message-id + :ens-name preferred-name}))) (defn build-image-messages [{db :db} chat-id] @@ -181,7 +193,8 @@ :text (i18n/label :t/update-to-see-image {"locale" "en"})}) images))) -(fx/defn clean-input [{:keys [db] :as cofx} current-chat-id] +(fx/defn clean-input + [{:keys [db] :as cofx} current-chat-id] (fx/merge cofx {:db (-> db (assoc-in [:chat/inputs current-chat-id :metadata :sending-contact-request] nil) @@ -201,10 +214,11 @@ (mentions/clear-mentions) (mentions/clear-cursor)))) -(fx/defn send-messages [{:keys [db] :as cofx} input-text current-chat-id] +(fx/defn send-messages + [{:keys [db] :as cofx} input-text current-chat-id] (let [image-messages (build-image-messages cofx current-chat-id) - text-message (build-text-message cofx input-text current-chat-id) - messages (keep identity (conj image-messages text-message))] + text-message (build-text-message cofx input-text current-chat-id) + messages (keep identity (conj image-messages text-message))] (when (seq messages) (fx/merge cofx (clean-input (:current-chat-id db)) @@ -215,11 +229,11 @@ "when not empty, proceed by sending text message with public key topic" {:events [:profile.ui/send-my-status-message]} [{db :db :as cofx}] - (let [current-chat-id (chat/my-profile-chat-topic db) + (let [current-chat-id (chat/my-profile-chat-topic db) {:keys [input-text]} (get-in db [:chat/inputs current-chat-id]) - image-messages (build-image-messages cofx current-chat-id) - text-message (build-text-message cofx input-text current-chat-id) - messages (keep identity (conj image-messages text-message))] + image-messages (build-image-messages cofx current-chat-id) + text-message (build-text-message cofx input-text current-chat-id) + messages (keep identity (conj image-messages text-message))] (when (seq messages) (fx/merge cofx (clean-input current-chat-id) @@ -228,33 +242,38 @@ (fx/defn send-audio-message [cofx audio-path duration current-chat-id] (when-not (string/blank? audio-path) - (chat.message/send-message cofx {:chat-id current-chat-id - :content-type constants/content-type-audio - :audio-path audio-path - :audio-duration-ms duration - :text (i18n/label :t/update-to-listen-audio {"locale" "en"})}))) + (chat.message/send-message + cofx + {:chat-id current-chat-id + :content-type constants/content-type-audio + :audio-path audio-path + :audio-duration-ms duration + :text (i18n/label :t/update-to-listen-audio {"locale" "en"})}))) (fx/defn send-sticker-message [cofx {:keys [hash packID pack]} current-chat-id] (when-not (or (string/blank? hash) (and (string/blank? packID) (string/blank? pack))) - (chat.message/send-message cofx {:chat-id current-chat-id - :content-type constants/content-type-sticker - :sticker {:hash hash + (chat.message/send-message cofx + {:chat-id current-chat-id + :content-type constants/content-type-sticker + :sticker {:hash hash :pack (int (if (string/blank? packID) pack packID))} - :text (i18n/label :t/update-to-see-sticker {"locale" "en"})}))) + :text (i18n/label :t/update-to-see-sticker {"locale" "en"})}))) -(fx/defn send-edited-message [{:keys [db] :as cofx} text {:keys [message-id quoted-message]}] +(fx/defn send-edited-message + [{:keys [db] :as cofx} text {:keys [message-id quoted-message]}] (fx/merge cofx - {::json-rpc/call [{:method "wakuext_editMessage" - :params [{:id message-id - :text text - :content-type (if (message-content/emoji-only-content? {:text text :response-to quoted-message}) - constants/content-type-emoji - constants/content-type-text)}] + {::json-rpc/call [{:method "wakuext_editMessage" + :params [{:id message-id + :text text + :content-type (if (message-content/emoji-only-content? + {:text text :response-to quoted-message}) + constants/content-type-emoji + constants/content-type-text)}] :js-response true - :on-error #(log/error "failed to edit message " %) - :on-success #(re-frame/dispatch [:sanitize-messages-and-process-response %])}]} + :on-error #(log/error "failed to edit message " %) + :on-success #(re-frame/dispatch [:sanitize-messages-and-process-response %])}]} (cancel-message-edit) (process-cooldown))) @@ -263,8 +282,8 @@ {:events [:chat.ui/send-current-message]} [{{:keys [current-chat-id] :as db} :db :as cofx}] (let [{:keys [input-text metadata]} (get-in db [:chat/inputs current-chat-id]) - editing-message (:editing-message metadata) - input-text-with-mentions (mentions/check-mentions cofx input-text)] + editing-message (:editing-message metadata) + input-text-with-mentions (mentions/check-mentions cofx input-text)] (fx/merge cofx (if editing-message (send-edited-message input-text-with-mentions editing-message) @@ -278,11 +297,11 @@ (fx/merge cofx {:chat.ui/clear-inputs nil :chat.ui/clear-inputs-old nil - ::json-rpc/call [{:method "wakuext_sendContactRequest" + ::json-rpc/call [{:method "wakuext_sendContactRequest" :js-response true - :params [{:id public-key :message message}] - :on-error #(log/warn "failed to send a contact request" %) - :on-success #(re-frame/dispatch [:transport/message-sent %])}]} + :params [{:id public-key :message message}] + :on-error #(log/warn "failed to send a contact request" %) + :on-success #(re-frame/dispatch [:transport/message-sent %])}]} (mentions/clear-mentions) (mentions/clear-cursor) (clean-input (:current-chat-id db)) @@ -305,10 +324,10 @@ [{{:keys [current-chat-id] :as db} :db :as cofx} {:keys [hash packID pack] :as sticker}] (fx/merge cofx - {:db (update db - :stickers/recent-stickers - (fn [recent] - (conj (remove #(= hash (:hash %)) recent) sticker))) + {:db (update db + :stickers/recent-stickers + (fn [recent] + (conj (remove #(= hash (:hash %)) recent) sticker))) ::json-rpc/call [{:method "stickers_addRecent" :params [(int (if (string/blank? packID) pack packID)) hash] :on-success #()}]} diff --git a/src/status_im/chat/models/input_test.cljs b/src/status_im/chat/models/input_test.cljs index da94b77012..ae5380de68 100644 --- a/src/status_im/chat/models/input_test.cljs +++ b/src/status_im/chat/models/input_test.cljs @@ -19,61 +19,61 @@ (with-redefs [datetime/timestamp (constantly 1527675198542)] (testing "no spamming detected" (let [expected {:db (assoc db :chat/last-outgoing-message-sent-at 1527675198542)} - actual (input/process-cooldown {:db db})] + actual (input/process-cooldown {:db db})] (is (= expected actual)))) (testing "spamming detected in 1-1" - (let [db (assoc db - :chats {"chat" {:public? false}} - :chat/spam-messages-frequency constants/spam-message-frequency-threshold - :chat/last-outgoing-message-sent-at (- 1527675198542 900)) + (let [db (assoc db + :chats {"chat" {:public? false}} + :chat/spam-messages-frequency constants/spam-message-frequency-threshold + :chat/last-outgoing-message-sent-at (- 1527675198542 900)) expected nil - actual (input/process-cooldown {:db db})] + actual (input/process-cooldown {:db db})] (is (= expected actual)))) (testing "spamming detected" - (let [db (assoc db - :chat/last-outgoing-message-sent-at (- 1527675198542 900) - :chat/spam-messages-frequency constants/spam-message-frequency-threshold) + (let [db (assoc db + :chat/last-outgoing-message-sent-at (- 1527675198542 900) + :chat/spam-messages-frequency constants/spam-message-frequency-threshold) expected {:db (assoc db :chat/last-outgoing-message-sent-at 1527675198542 - :chat/cooldowns 1 - :chat/spam-messages-frequency 0 - :chat/cooldown-enabled? true) + :chat/cooldowns 1 + :chat/spam-messages-frequency 0 + :chat/cooldown-enabled? true) :show-cooldown-warning nil :dispatch-later [{:dispatch [:chat/disable-cooldown] :ms (constants/cooldown-periods-ms 1)}]} - actual (input/process-cooldown {:db db})] + actual (input/process-cooldown {:db db})] (is (= expected actual)))) (testing "spamming detected twice" - (let [db (assoc db - :chat/cooldowns 1 - :chat/last-outgoing-message-sent-at (- 1527675198542 900) - :chat/spam-messages-frequency constants/spam-message-frequency-threshold) + (let [db (assoc db + :chat/cooldowns 1 + :chat/last-outgoing-message-sent-at (- 1527675198542 900) + :chat/spam-messages-frequency constants/spam-message-frequency-threshold) expected {:db (assoc db :chat/last-outgoing-message-sent-at 1527675198542 - :chat/cooldowns 2 - :chat/spam-messages-frequency 0 - :chat/cooldown-enabled? true) + :chat/cooldowns 2 + :chat/spam-messages-frequency 0 + :chat/cooldown-enabled? true) :show-cooldown-warning nil :dispatch-later [{:dispatch [:chat/disable-cooldown] :ms (constants/cooldown-periods-ms 2)}]} - actual (input/process-cooldown {:db db})] + actual (input/process-cooldown {:db db})] (is (= expected actual)))) (testing "spamming reaching cooldown threshold" - (let [db (assoc db - :chat/cooldowns (dec constants/cooldown-reset-threshold) - :chat/last-outgoing-message-sent-at (- 1527675198542 900) - :chat/spam-messages-frequency constants/spam-message-frequency-threshold) + (let [db (assoc db + :chat/cooldowns (dec constants/cooldown-reset-threshold) + :chat/last-outgoing-message-sent-at (- 1527675198542 900) + :chat/spam-messages-frequency constants/spam-message-frequency-threshold) expected {:db (assoc db :chat/last-outgoing-message-sent-at 1527675198542 - :chat/cooldowns 0 - :chat/spam-messages-frequency 0 - :chat/cooldown-enabled? true) + :chat/cooldowns 0 + :chat/spam-messages-frequency 0 + :chat/cooldown-enabled? true) :show-cooldown-warning nil :dispatch-later [{:dispatch [:chat/disable-cooldown] :ms (constants/cooldown-periods-ms 3)}]} - actual (input/process-cooldown {:db db})] + actual (input/process-cooldown {:db db})] (is (= expected actual))))))) diff --git a/src/status_im/chat/models/link_preview.cljs b/src/status_im/chat/models/link_preview.cljs index 26cf90d468..61f30bbb3b 100644 --- a/src/status_im/chat/models/link_preview.cljs +++ b/src/status_im/chat/models/link_preview.cljs @@ -1,9 +1,9 @@ (ns status-im.chat.models.link-preview (:require [re-frame.core :as re-frame] - [status-im.utils.fx :as fx] - [status-im.multiaccounts.update.core :as multiaccounts.update] - [status-im.ethereum.json-rpc :as json-rpc] [status-im.communities.core :as models.communities] + [status-im.ethereum.json-rpc :as json-rpc] + [status-im.multiaccounts.update.core :as multiaccounts.update] + [status-im.utils.fx :as fx] [taoensso.timbre :as log])) (fx/defn enable @@ -28,13 +28,16 @@ #{}) {}))) -(defn community-resolved [db community-id] +(defn community-resolved + [db community-id] (update db :communities/resolve-community-info dissoc community-id)) -(defn community-failed-to-resolve [db community-id] +(defn community-failed-to-resolve + [db community-id] (update db :communities/resolve-community-info dissoc community-id)) -(defn community-resolving [db community-id] +(defn community-resolving + [db community-id] (assoc-in db [:communities/resolve-community-info community-id] true)) (fx/defn handle-community-failed-to-resolve @@ -42,7 +45,8 @@ [{:keys [db]} community-id] {:db (community-failed-to-resolve db community-id)}) -(defn community-link [id] +(defn community-link + [id] (str "https://join.status.im/c/" id)) (fx/defn handle-community-resolved @@ -51,14 +55,15 @@ (fx/merge cofx (cond-> {:db (community-resolved db community-id)} (some? community) - (assoc :dispatch [::cache-link-preview-data - (community-link community-id) community])) + (assoc :dispatch + [::cache-link-preview-data + (community-link community-id) community])) (models.communities/handle-community community))) (fx/defn resolve-community-info {:events [::resolve-community-info]} [{:keys [db]} community-id] - {:db (community-resolving db community-id) + {:db (community-resolving db community-id) ::json-rpc/call [{:method "wakuext_requestCommunityInfoFromMailserver" :params [community-id] :on-success #(re-frame/dispatch [::community-resolved community-id %]) @@ -72,9 +77,10 @@ {::json-rpc/call [{:method "wakuext_getLinkPreviewData" :params [link] :on-success #(re-frame/dispatch [::cache-link-preview-data link %]) - :on-error #(re-frame/dispatch [::cache-link-preview-data - link - {:error (str "Can't get preview data for " link)}])}]}) + :on-error #(re-frame/dispatch + [::cache-link-preview-data + link + {:error (str "Can't get preview data for " link)}])}]}) (fx/defn cache-link-preview-data {:events [::cache-link-preview-data]} @@ -95,7 +101,8 @@ [{:keys [db] :as cofx} enabled?] (multiaccounts.update/multiaccount-update cofx - :link-preview-request-enabled (boolean enabled?) + :link-preview-request-enabled + (boolean enabled?) {})) (fx/defn request-link-preview-whitelist @@ -108,5 +115,6 @@ (fx/defn save-link-preview-whitelist {:events [::link-preview-whitelist-received]} [{:keys [db]} whitelist] - {:db (assoc db :link-previews-whitelist + {:db (assoc db + :link-previews-whitelist whitelist)}) diff --git a/src/status_im/chat/models/loading.cljs b/src/status_im/chat/models/loading.cljs index b5edfe4147..990d3c6edf 100644 --- a/src/status_im/chat/models/loading.cljs +++ b/src/status_im/chat/models/loading.cljs @@ -1,19 +1,20 @@ (ns status-im.chat.models.loading (:require [re-frame.core :as re-frame] + [status-im.chat.models.message-list :as message-list] [status-im.constants :as constants] [status-im.data-store.chats :as data-store.chats] [status-im.data-store.messages :as data-store.messages] - [status-im.utils.fx :as fx] - [status-im.chat.models.message-list :as message-list] - [taoensso.timbre :as log] [status-im.ethereum.json-rpc :as json-rpc] - [status-im2.contexts.activity-center.events :as activity-center])) + [status-im.utils.fx :as fx] + [status-im2.contexts.activity-center.events :as activity-center] + [taoensso.timbre :as log])) (defn cursor->clock-value [^js cursor] (js/parseInt (.substring cursor 51 64))) -(defn clock-value->cursor [clock-value] +(defn clock-value->cursor + [clock-value] (str "000000000000000000000000000000000000000000000000000" clock-value "0x0000000000000000000000000000000000000000000000000000000000000000")) @@ -30,12 +31,13 @@ (update :chats-home-list conj chat-id) :always (assoc-in [:all-chats chat-id] chat)))) - {:all-chats {} + {:all-chats {} :chats-home-list #{}} new-chats-js)] - {:db (assoc db :chats all-chats + {:db (assoc db + :chats all-chats :chats-home-list chats-home-list - :chats/loading? false)})) + :chats/loading? false)})) (fx/defn load-chat-success {:events [:chats-list/load-chat-success]} @@ -45,10 +47,10 @@ (fx/defn load-chat [_ chat-id] - {::json-rpc/call [{:method "wakuext_chat" - :params [chat-id] + {::json-rpc/call [{:method "wakuext_chat" + :params [chat-id] :on-success #(re-frame/dispatch [:chats-list/load-chat-success %]) - :on-error #(log/error "failed to fetch chats" 0 -1 %)}]}) + :on-error #(log/error "failed to fetch chats" 0 -1 %)}]}) (fx/defn handle-failed-loading-messages {:events [::failed-loading-messages]} @@ -59,10 +61,12 @@ (defn mark-chat-all-read [db chat-id] - (update-in db [:chats chat-id] assoc + (update-in db + [:chats chat-id] + assoc :unviewed-messages-count 0 :unviewed-mentions-count 0 - :highlight false)) + :highlight false)) (fx/defn handle-mark-all-read-successful {:events [::mark-all-read-successful]} @@ -79,23 +83,24 @@ (fx/defn handle-mark-all-read {:events [:chat.ui/mark-all-read-pressed :chat/mark-all-as-read]} [{db :db} chat-id] - {:db (mark-chat-all-read db chat-id) - :clear-message-notifications [[chat-id] - (get-in db [:multiaccount :remote-push-notifications-enabled?])] - ::json-rpc/call [{:method "wakuext_markAllRead" - :params [chat-id] - :on-success #(re-frame/dispatch [::mark-all-read-successful])}]}) + {:db (mark-chat-all-read db chat-id) + :clear-message-notifications [[chat-id] + (get-in db [:multiaccount :remote-push-notifications-enabled?])] + ::json-rpc/call [{:method "wakuext_markAllRead" + :params [chat-id] + :on-success #(re-frame/dispatch [::mark-all-read-successful])}]}) (fx/defn handle-mark-mark-all-read-in-community {:events [:chat.ui/mark-all-read-in-community-pressed]} [{db :db} community-id] (let [community-chat-ids (map #(str community-id %) (keys (get-in db [:communities community-id :chats])))] - {:clear-message-notifications [community-chat-ids - (get-in db [:multiaccount :remote-push-notifications-enabled?])] - ::json-rpc/call [{:method "wakuext_markAllReadInCommunity" - :params [community-id] - :on-success #(re-frame/dispatch [::mark-all-read-in-community-successful %])}]})) + {:clear-message-notifications [community-chat-ids + (get-in db [:multiaccount :remote-push-notifications-enabled?])] + ::json-rpc/call [{:method "wakuext_markAllReadInCommunity" + :params [community-id] + :on-success #(re-frame/dispatch + [::mark-all-read-in-community-successful %])}]})) (fx/defn messages-loaded "Loads more messages for current chat" @@ -104,7 +109,7 @@ (when-not (and (get-in db [:pagination-info chat-id :messages-initialized?]) (not= session-id (get-in db [:pagination-info chat-id :messages-initialized?]))) - (let [already-loaded-messages (get-in db [:messages chat-id]) + (let [already-loaded-messages (get-in db [:messages chat-id]) ;; We remove those messages that are already loaded, as we might get some duplicates {:keys [all-messages new-messages senders contacts]} (reduce (fn [{:keys [all-messages] :as acc} @@ -124,8 +129,11 @@ :contacts {} :new-messages []} messages) - current-clock-value (get-in db [:pagination-info chat-id :cursor-clock-value]) - clock-value (when cursor (cursor->clock-value cursor))] + current-clock-value (get-in db + [:pagination-info chat-id + :cursor-clock-value]) + clock-value (when cursor + (cursor->clock-value cursor))] {:dispatch [:chat/add-senders-to-chat-users (vals senders)] :db (-> db (update-in [:pagination-info chat-id :cursor-clock-value] @@ -134,7 +142,9 @@ %)) (update-in [:pagination-info chat-id :cursor] - #(if (or (empty? cursor) (not current-clock-value) (< clock-value current-clock-value)) + #(if (or (empty? cursor) + (not current-clock-value) + (< clock-value current-clock-value)) cursor %)) (assoc-in [:pagination-info chat-id :loading-messages?] false) @@ -172,7 +182,7 @@ [{:keys [db now] :as cofx} chat-id] (when-not (get-in db [:pagination-info chat-id :messages-initialized?]) (fx/merge cofx - {:db (assoc-in db [:pagination-info chat-id :messages-initialized?] now) + {:db (assoc-in db [:pagination-info chat-id :messages-initialized?] now) :utils/dispatch-later [{:ms 50 :dispatch [:chat.ui/mark-all-read-pressed chat-id]} (when-not (get-in cofx [:db :chats chat-id :public?]) {:ms 100 :dispatch [:pin-message/load-pin-messages chat-id]})]} diff --git a/src/status_im/chat/models/mentions.cljs b/src/status_im/chat/models/mentions.cljs index 221696dd49..72f9113296 100644 --- a/src/status_im/chat/models/mentions.cljs +++ b/src/status_im/chat/models/mentions.cljs @@ -1,23 +1,26 @@ (ns status-im.chat.models.mentions (:require [clojure.string :as string] + [quo.react :as react] + [quo.react-native :as rn] [re-frame.core :as re-frame] [status-im.constants :as constants] - [status-im.utils.fx :as fx] [status-im.contact.db :as contact.db] - [status-im.utils.platform :as platform] - [taoensso.timbre :as log] - [status-im.utils.utils :as utils] + [status-im.multiaccounts.core :as multiaccounts] [status-im.native-module.core :as status] - [quo.react-native :as rn] - [quo.react :as react] - [status-im.multiaccounts.core :as multiaccounts])) + [status-im.utils.fx :as fx] + [status-im.utils.platform :as platform] + [status-im.utils.utils :as utils] + [taoensso.timbre :as log])) (def at-sign "@") ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(defn re-pos [re s] - (loop [res [] s s last-idx 0] +(defn re-pos + [re s] + (loop [res [] + s s + last-idx 0] (if-let [m (.exec re s)] (let [new-idx (.-index m) idx (+ last-idx new-idx) @@ -25,7 +28,8 @@ (recur (conj res [idx c]) (subs s (inc new-idx)) (inc idx))) res))) -(defn check-style-tag [text idxs idx] +(defn check-style-tag + [text idxs idx] (let [[pos c] (get idxs idx) [pos2 c2] (get idxs (inc idx)) [pos3 c3] (get idxs (+ 2 idx)) @@ -63,7 +67,8 @@ (doall ((fnil concat []) checked new-idxs))))))) -(defn apply-style-tag [data idx pos c len start? end?] +(defn apply-style-tag + [data idx pos c len start? end?] (let [was-started? (get data c) tripple-tilde? (and (= "~" c) (= 3 len))] (cond @@ -87,8 +92,9 @@ start? {:data (-> data - (assoc c {:len len - :idx pos}) + (assoc c + {:len len + :idx pos}) (clear-pending-at-signs pos)) :next-idx (+ idx len)} @@ -96,7 +102,8 @@ {:data data :next-idx (+ idx len)}))) -(defn code-tag-len [idxs idx] +(defn code-tag-len + [idxs idx] (let [[pos c] (get idxs idx) [pos2 c2] (get idxs (inc idx)) [pos3 c3] (get idxs (+ 2 idx))] @@ -147,7 +154,8 @@ (string/blank? (subs text (first prev-newlines) (dec pos))))) (recur (-> data (dissoc :newline "*" "_" "~" "`") - (assoc ">" {:idx pos})) (inc idx)) + (assoc ">" {:idx pos})) + (inc idx)) (recur data (inc idx)))) at-sign? @@ -155,7 +163,7 @@ (inc idx)) code-tag? - (let [len (code-tag-len idxs idx) + (let [len (code-tag-len idxs idx) {:keys [data next-idx]} (apply-style-tag data idx pos c len true true)] (recur data next-idx)) @@ -167,7 +175,7 @@ (apply-style-tag data idx pos c len can-be-start? can-be-end?)] (recur data next-idx)) - :else (recur data (inc idx))))))))) + :else (recur data (inc idx))))))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -201,7 +209,8 @@ :ens-verified ens-verified :public-key public-key}))) -(defn mentionable-contacts [contacts] +(defn mentionable-contacts + [contacts] (reduce (fn [acc [key contact]] (let [mentionable-contact (add-searchable-phrases-to-contact contact false)] @@ -211,38 +220,49 @@ {} contacts)) -(defn mentionable-contacts-from-identites [contacts my-public-key identities] +(defn mentionable-contacts-from-identites + [contacts my-public-key identities] (reduce (fn [acc identity] (let [contact (multiaccounts/contact-by-identity - contacts identity) + contacts + identity) contact (if (string/blank? (:alias contact)) - (assoc contact :alias + (assoc contact + :alias (get-in contact [:names :three-words-name])) contact) mentionable-contact (add-searchable-phrases-to-contact - contact true)] - (if (nil? mentionable-contact) acc - (assoc acc identity mentionable-contact)))) + contact + true)] + (if (nil? mentionable-contact) + acc + (assoc acc identity mentionable-contact)))) {} (remove #(= my-public-key %) identities))) -(defn get-mentionable-users [chat all-contacts current-multiaccount community-members] - (let [{:keys [name preferred-name public-key]} current-multiaccount +(defn get-mentionable-users + [chat all-contacts current-multiaccount community-members] + (let [{:keys [name preferred-name public-key]} current-multiaccount {:keys [chat-id users contacts chat-type]} chat - mentionable-contacts (mentionable-contacts all-contacts) - mentionable-users (assoc users public-key {:alias name - :name (or preferred-name name) - :public-key public-key})] + mentionable-contacts (mentionable-contacts all-contacts) + mentionable-users (assoc users + public-key + {:alias name + :name (or preferred-name name) + :public-key public-key})] (cond (= chat-type constants/private-group-chat-type) (merge mentionable-users (mentionable-contacts-from-identites all-contacts public-key contacts)) (= chat-type constants/one-to-one-chat-type) - (assoc mentionable-users chat-id (get mentionable-contacts chat-id - (-> chat-id - contact.db/public-key->new-contact - contact.db/enrich-contact))) + (assoc mentionable-users + chat-id + (get mentionable-contacts + chat-id + (-> chat-id + contact.db/public-key->new-contact + contact.db/enrich-contact))) (= chat-type constants/community-chat-type) (mentionable-contacts-from-identites @@ -253,13 +273,14 @@ (= chat-type constants/public-chat-type) (merge mentionable-users (select-keys mentionable-contacts (keys mentionable-users))) - :else mentionable-users))) + :else mentionable-users))) (def ending-chars "[\\s\\.,;:]") (def ending-chars-regex (re-pattern ending-chars)) (def word-regex (re-pattern (str "^[\\w\\d]*" ending-chars "|^[\\S]*$"))) -(defn mentioned? [{:keys [alias name]} text] +(defn mentioned? + [{:keys [alias name]} text] (let [lcase-name (string/lower-case name) lcase-alias (string/lower-case alias) regex (re-pattern @@ -272,7 +293,8 @@ lcase-text (string/lower-case text)] (re-find regex lcase-text))) -(defn get-user-suggestions [users searched-text] +(defn get-user-suggestions + [users searched-text] (reduce (fn [acc [k {:keys [alias name nickname searchable-phrases] :as user}]] (if-let [match @@ -301,9 +323,11 @@ (string/lower-case name) searched-text) name))] - (assoc acc k (assoc user - :match match - :searched-text searched-text)) + (assoc acc + k + (assoc user + :match match + :searched-text searched-text)) acc)) {} users)) @@ -324,21 +348,25 @@ text)) user-suggestions (get-user-suggestions users searched-text) user-suggestions-cnt (count user-suggestions)] - (cond (zero? user-suggestions-cnt) - nil + (cond + (zero? user-suggestions-cnt) + nil - (and (= 1 user-suggestions-cnt) - (mentioned? (second (first user-suggestions)) - (subs text (inc mention-key-idx)))) - (second (first user-suggestions)) + (and (= 1 user-suggestions-cnt) + (mentioned? (second (first user-suggestions)) + (subs text (inc mention-key-idx)))) + (second (first user-suggestions)) - (> user-suggestions-cnt 1) - (let [word-len (count word) - text-len (count text) - next-word-start (+ next-word-idx word-len)] - (when (> text-len next-word-start) - (match-mention text users mention-key-idx - next-word-start new-words)))))))) + (> user-suggestions-cnt 1) + (let [word-len (count word) + text-len (count text) + next-word-start (+ next-word-idx word-len)] + (when (> text-len next-word-start) + (match-mention text + users + mention-key-idx + next-word-start + new-words)))))))) (defn replace-mentions ([text users] @@ -360,18 +388,25 @@ (let [new-text (string/join [(subs text 0 (inc mention-key-idx)) public-key - (subs text (+ (inc mention-key-idx) - (count match)))])] - (recur new-text users (rest idxs) + (subs text + (+ (inc mention-key-idx) + (count match)))])] + (recur new-text + users + (rest idxs) (+ diff (- (count text) (count new-text))))))))))))) -(defn check-mentions [{:keys [db]} text] +(defn check-mentions + [{:keys [db]} text] (let [current-chat-id (:current-chat-id db) chat (get-in db [:chats current-chat-id]) all-contacts (:contacts/contacts db) current-multiaccount (:multiaccount db) community-members (get-in db [:communities (:community-id chat) :members]) - mentionable-users (get-mentionable-users chat all-contacts current-multiaccount community-members)] + mentionable-users (get-mentionable-users chat + all-contacts + current-multiaccount + community-members)] (replace-mentions text mentionable-users))) (defn get-at-sign-idxs @@ -396,7 +431,7 @@ {:from idx :checked? false}) new-idxs) - (let [diff (- new-text-len old-text-len) + (let [diff (- new-text-len old-text-len) {:keys [state added?]} (->> at-idxs (keep (fn [{:keys [from to] :as entry}] @@ -406,7 +441,7 @@ (>= from old-end) (assoc entry :from (+ from diff) - :to (+ to diff)) + :to (+ to diff)) ;; starts and end before change (and @@ -431,20 +466,22 @@ (> from last-new-idx) (not added?)) {:state (conj - (into state (map (fn [idx] - {:from idx - :checked? false}) - new-idxs)) + (into state + (map (fn [idx] + {:from idx + :checked? false}) + new-idxs)) entry) :added? true} (update acc :state conj entry))) {:state []}))] (if added? state - (into state (map (fn [idx] - {:from idx - :checked? false}) - new-idxs))))))) + (into state + (map (fn [idx] + {:from idx + :checked? false}) + new-idxs))))))) (defn check-entry [text {:keys [from checked?] :as entry} mentionable-users] @@ -453,14 +490,14 @@ (let [{user-match :match} (match-mention (str text "@") mentionable-users from)] (if user-match - {:from from - :to (+ from (count user-match)) + {:from from + :to (+ from (count user-match)) :checked? true :mention? true} - {:from from - :to (count text) + {:from from + :to (count text) :checked? true - :mention false})))) + :mention false})))) (defn check-idx-for-mentions [text idxs mentionable-users] @@ -499,31 +536,34 @@ (if platform/android? previous-text (subs previous-text start end)) - chat-id (:current-chat-id db) - previous-state (get-in db [:chats/mentions chat-id :mentions]) - new-state (-> previous-state - (merge args) - (assoc :previous-text normalized-previous-text)) - new-at-idxs (calc-at-idxs new-state) - new-state (assoc new-state :at-idxs new-at-idxs)] + chat-id (:current-chat-id db) + previous-state (get-in db [:chats/mentions chat-id :mentions]) + new-state (-> previous-state + (merge args) + (assoc :previous-text normalized-previous-text)) + new-at-idxs (calc-at-idxs new-state) + new-state (assoc new-state :at-idxs new-at-idxs)] (log/debug "[mentions] on-text-input state" new-state) {:db (assoc-in db [:chats/mentions chat-id :mentions] new-state)})) -(defn calculate-input [text [first-idx :as idxs]] +(defn calculate-input + [text [first-idx :as idxs]] (if-not first-idx [[:text text]] - (let [idx-cnt (count idxs) + (let [idx-cnt (count idxs) last-from (get-in idxs [(dec idx-cnt) :from])] (reduce (fn [acc {:keys [from to next-at-idx mention?]}] (cond (and mention? next-at-idx) - (into acc [[:mention (subs text from (inc to))] - [:text (subs text (inc to) next-at-idx)]]) + (into acc + [[:mention (subs text from (inc to))] + [:text (subs text (inc to) next-at-idx)]]) (and mention? (= last-from from)) - (into acc [[:mention (subs text from (inc to))] - [:text (subs text (inc to))]]) + (into acc + [[:mention (subs text from (inc to))] + [:text (subs text (inc to))]]) :else (conj acc [:text (subs text from (inc to))]))) @@ -535,77 +575,80 @@ (fx/defn recheck-at-idxs [{:keys [db]} mentionable-users] - (let [chat-id (:current-chat-id db) - text (get-in db [:chat/inputs chat-id :input-text]) - state (get-in db [:chats/mentions chat-id :mentions]) - new-at-idxs (check-idx-for-mentions - text - (:at-idxs state) - mentionable-users) + (let [chat-id (:current-chat-id db) + text (get-in db [:chat/inputs chat-id :input-text]) + state (get-in db [:chats/mentions chat-id :mentions]) + new-at-idxs (check-idx-for-mentions + text + (:at-idxs state) + mentionable-users) calculated-input (calculate-input text new-at-idxs)] (log/debug "[mentions] new-at-idxs" new-at-idxs calculated-input) {:db (-> db (update-in [:chats/mentions chat-id :mentions] assoc - :at-idxs new-at-idxs) + :at-idxs + new-at-idxs) (assoc-in [:chat/inputs-with-mentions chat-id] calculated-input))})) (fx/defn calculate-suggestions {:events [::calculate-suggestions]} [{:keys [db]} mentionable-users] - (let [chat-id (:current-chat-id db) - text (get-in db [:chat/inputs chat-id :input-text]) + (let [chat-id (:current-chat-id db) + text (get-in db [:chat/inputs chat-id :input-text]) {:keys [new-text at-idxs start end] :as state} (get-in db [:chats/mentions chat-id :mentions]) - new-text (or new-text text)] + new-text (or new-text text)] (log/info "[mentions] calculate suggestions" - "state" state) + "state" + state) (if-not (seq at-idxs) {:db (-> db (assoc-in [:chats/mention-suggestions chat-id] nil) (assoc-in [:chats/mentions chat-id :mentions :at-idxs] nil) (assoc-in [:chat/inputs-with-mentions chat-id] [[:text text]]))} - (let [new-at-idxs (check-idx-for-mentions - text - at-idxs - mentionable-users) + (let [new-at-idxs (check-idx-for-mentions + text + at-idxs + mentionable-users) calculated-input (calculate-input text new-at-idxs) - addition? (<= start end) - end (if addition? - (+ start (count new-text)) - start) - at-sign-idx (string/last-index-of text at-sign start) - searched-text (string/lower-case (subs text (inc at-sign-idx) end)) + addition? (<= start end) + end (if addition? + (+ start (count new-text)) + start) + at-sign-idx (string/last-index-of text at-sign start) + searched-text (string/lower-case (subs text (inc at-sign-idx) end)) mentions (when (and (not (> at-sign-idx start)) (not (> (- end at-sign-idx) 100))) (get-user-suggestions mentionable-users searched-text))] (log/debug "[mentions] mention check" - "addition" addition? - "at-sign-idx" at-sign-idx - "start" start - "end" end + "addition" addition? + "at-sign-idx" at-sign-idx + "start" start + "end" end "searched-text" (pr-str searched-text) - "mentions" (count mentions)) + "mentions" (count mentions)) (log/debug "[mentions] new-at-idxs" new-at-idxs calculated-input) {:db (-> db (update-in [:chats/mentions chat-id :mentions] assoc :at-sign-idx at-sign-idx - :at-idxs new-at-idxs + :at-idxs new-at-idxs :mention-end end) (assoc-in [:chat/inputs-with-mentions chat-id] calculated-input) (assoc-in [:chats/mention-suggestions chat-id] mentions))})))) (defn new-input-text-with-mention [{:keys [db]} {:keys [name]}] - (let [chat-id (:current-chat-id db) - text (get-in db [:chat/inputs chat-id :input-text]) + (let [chat-id (:current-chat-id db) + text (get-in db [:chat/inputs chat-id :input-text]) {:keys [mention-end at-sign-idx]} (get-in db [:chats/mentions chat-id :mentions])] (log/debug "[mentions] clear suggestions" - "state" new-input-text-with-mention) + "state" + new-input-text-with-mention) (string/join [(subs text 0 (inc at-sign-idx)) name @@ -645,7 +688,7 @@ [{:keys [db] :as cofx} {:keys [start end] :as selection} mentionable-users] - (let [chat-id (:current-chat-id db) + (let [chat-id (:current-chat-id db) {:keys [mention-end at-idxs]} (get-in db [:chats/mentions chat-id :mentions])] (when (seq at-idxs) @@ -657,10 +700,11 @@ at-idxs) (fx/merge cofx - {:db (update-in db [:chats/mentions chat-id :mentions] + {:db (update-in db + [:chats/mentions chat-id :mentions] assoc - :start end - :end end + :start end + :end end :new-text "")} (calculate-suggestions mentionable-users)) (clear-suggestions cofx))))) @@ -677,22 +721,24 @@ [_ ref cursor] {::reset-text-input-cursor [ref cursor]}) -(defn is-valid-terminating-character? [c] +(defn is-valid-terminating-character? + [c] (case c - "\t" true ; tab + "\t" true ; tab "\n" true ; newline "\f" true ; new page "\r" true ; carriage return - " " true ; whitespace - "," true - "." true - ":" true - ";" true + " " true ; whitespace + "," true + "." true + ":" true + ";" true false)) (def hex-reg #"[0-9a-f]") -(defn is-public-key-character? [c] +(defn is-public-key-character? + [c] (.test hex-reg c)) (def mention-length 133) @@ -710,7 +756,7 @@ current-text current-mention current-mention-length]} character] - (let [is-pk-character (is-public-key-character? character) + (let [is-pk-character (is-public-key-character? character) is-termination-character (is-valid-terminating-character? character)] (cond ;; It's a valid mention. @@ -720,13 +766,13 @@ (and (= current-mention-length mention-length) is-termination-character) {:current-mention-length 0 - :current-mention "" - :current-text character - :text (cond-> text - (seq current-text) - (conj [:text current-text]) - :always - (conj [:mention current-mention]))} + :current-mention "" + :current-text character + :text (cond-> text + (seq current-text) + (conj [:text current-text]) + :always + (conj [:mention current-mention]))} ;; It's either a pk character, or the `x` in the pk @@ -739,9 +785,9 @@ (and (= 2 current-mention-length) (= "x" character))) {:current-mention-length (inc current-mention-length) - :current-text current-text - :current-mention (str current-mention character) - :text text} + :current-text current-text + :current-mention (str current-mention character) + :text text} ;; The beginning of a mention, discard the @ sign @@ -750,29 +796,29 @@ (= "@" character) {:current-mention-length 1 - :current-mention "" - :current-text current-text - :text text} + :current-mention "" + :current-text current-text + :text text} ;; Not a mention character, but we were following a mention ;; discard everything up to know an count as text (and (not is-pk-character) (pos? current-mention-length)) {:current-mention-length 0 - :current-text (str current-text "@" current-mention character) - :current-mention "" - :text text} + :current-text (str current-text "@" current-mention character) + :current-mention "" + :text text} ;; Just a normal text character :else {:current-mention-length 0 - :current-mention "" - :current-text (str current-text character) - :text text}))) + :current-mention "" + :current-text (str current-text character) + :text text}))) {:current-mention-length 0 - :current-text "" - :current-mention "" - :text []} + :current-text "" + :current-mention "" + :text []} text)] ;; Process any remaining mention/text (cond-> text @@ -788,32 +834,32 @@ [m] (reduce (fn [{:keys [start end at-idxs at-sign-idx mention-end]} [t text]] (if (= :mention t) - (let [new-mention {:checked? true - :mention? true - :from mention-end - :to (+ start (count text))} + (let [new-mention {:checked? true + :mention? true + :from mention-end + :to (+ start (count text))} has-previous? (seq at-idxs)] - {:new-text (last text) + {:new-text (last text) :previous-text "" - :start (+ start (count text)) - :end (+ end (count text)) - :at-idxs (cond-> at-idxs - has-previous? - (-> pop - (conj (assoc (peek at-idxs) :next-at-idx mention-end))) - :always - (conj new-mention)) - :at-sign-idx mention-end - :mention-end (+ mention-end (count text))}) - {:new-text (last text) + :start (+ start (count text)) + :end (+ end (count text)) + :at-idxs (cond-> at-idxs + has-previous? + (-> pop + (conj (assoc (peek at-idxs) :next-at-idx mention-end))) + :always + (conj new-mention)) + :at-sign-idx mention-end + :mention-end (+ mention-end (count text))}) + {:new-text (last text) :previous-text "" - :start (+ start (count text)) - :end (+ end (count text)) - :at-idxs at-idxs - :at-sign-idx at-sign-idx - :mention-end (+ mention-end (count text))})) - {:start -1 - :end -1 - :at-idxs [] + :start (+ start (count text)) + :end (+ end (count text)) + :at-idxs at-idxs + :at-sign-idx at-sign-idx + :mention-end (+ mention-end (count text))})) + {:start -1 + :end -1 + :at-idxs [] :mention-end 0} m)) diff --git a/src/status_im/chat/models/mentions_test.cljs b/src/status_im/chat/models/mentions_test.cljs index f6c6190939..1c5a4c12a7 100644 --- a/src/status_im/chat/models/mentions_test.cljs +++ b/src/status_im/chat/models/mentions_test.cljs @@ -1,7 +1,7 @@ (ns status-im.chat.models.mentions-test - (:require [status-im.chat.models.mentions :as mentions] + (:require [cljs.test :as test] [clojure.string :as string] - [cljs.test :as test])) + [status-im.chat.models.mentions :as mentions])) (def ->info-input [[:text "H."] @@ -10,16 +10,16 @@ [:text " "]]) (def ->info-expected - {:at-sign-idx 2 - :mention-end 19 - :new-text " " + {:at-sign-idx 2 + :mention-end 19 + :new-text " " :previous-text "" - :start 18 - :end 18 - :at-idxs [{:mention? true - :from 2 - :to 17 - :checked? true}]}) + :start 18 + :end 18 + :at-idxs [{:mention? true + :from 2 + :to 17 + :checked? true}]}) (test/deftest test->info (test/testing "->info base case" @@ -30,20 +30,36 @@ (def mention-text-result-1 [[:text "parse-text"]]) ;; Mention in the middle -(def mention-text-2 "hey @0x04fbce10971e1cd7253b98c7b7e54de3729ca57ce41a2bfb0d1c4e0a26f72c4b6913c3487fa1b4bb86125770f1743fb4459da05c1cbe31d938814cfaf36e252073 he") -(def mention-text-result-2 [[:text "hey "] [:mention "0x04fbce10971e1cd7253b98c7b7e54de3729ca57ce41a2bfb0d1c4e0a26f72c4b6913c3487fa1b4bb86125770f1743fb4459da05c1cbe31d938814cfaf36e252073"] [:text " he"]]) +(def mention-text-2 + "hey @0x04fbce10971e1cd7253b98c7b7e54de3729ca57ce41a2bfb0d1c4e0a26f72c4b6913c3487fa1b4bb86125770f1743fb4459da05c1cbe31d938814cfaf36e252073 he") +(def mention-text-result-2 + [[:text "hey "] + [:mention + "0x04fbce10971e1cd7253b98c7b7e54de3729ca57ce41a2bfb0d1c4e0a26f72c4b6913c3487fa1b4bb86125770f1743fb4459da05c1cbe31d938814cfaf36e252073"] + [:text " he"]]) ;; Mention at the beginning -(def mention-text-3 "@0x04fbce10971e1cd7253b98c7b7e54de3729ca57ce41a2bfb0d1c4e0a26f72c4b6913c3487fa1b4bb86125770f1743fb4459da05c1cbe31d938814cfaf36e252073 he") -(def mention-text-result-3 [[:mention "0x04fbce10971e1cd7253b98c7b7e54de3729ca57ce41a2bfb0d1c4e0a26f72c4b6913c3487fa1b4bb86125770f1743fb4459da05c1cbe31d938814cfaf36e252073"] [:text " he"]]) +(def mention-text-3 + "@0x04fbce10971e1cd7253b98c7b7e54de3729ca57ce41a2bfb0d1c4e0a26f72c4b6913c3487fa1b4bb86125770f1743fb4459da05c1cbe31d938814cfaf36e252073 he") +(def mention-text-result-3 + [[:mention + "0x04fbce10971e1cd7253b98c7b7e54de3729ca57ce41a2bfb0d1c4e0a26f72c4b6913c3487fa1b4bb86125770f1743fb4459da05c1cbe31d938814cfaf36e252073"] + [:text " he"]]) ;; Mention at the end -(def mention-text-4 "hey @0x04fbce10971e1cd7253b98c7b7e54de3729ca57ce41a2bfb0d1c4e0a26f72c4b6913c3487fa1b4bb86125770f1743fb4459da05c1cbe31d938814cfaf36e252073") -(def mention-text-result-4 [[:text "hey "] [:mention "0x04fbce10971e1cd7253b98c7b7e54de3729ca57ce41a2bfb0d1c4e0a26f72c4b6913c3487fa1b4bb86125770f1743fb4459da05c1cbe31d938814cfaf36e252073"]]) +(def mention-text-4 + "hey @0x04fbce10971e1cd7253b98c7b7e54de3729ca57ce41a2bfb0d1c4e0a26f72c4b6913c3487fa1b4bb86125770f1743fb4459da05c1cbe31d938814cfaf36e252073") +(def mention-text-result-4 + [[:text "hey "] + [:mention + "0x04fbce10971e1cd7253b98c7b7e54de3729ca57ce41a2bfb0d1c4e0a26f72c4b6913c3487fa1b4bb86125770f1743fb4459da05c1cbe31d938814cfaf36e252073"]]) ;; Invalid mention -(def mention-text-5 "invalid @0x04fBce10971e1cd7253b98c7b7e54de3729ca57ce41a2bfb0d1c4e0a26f72c4b6913c3487fa1b4bb86125770f1743fb4459da05c1cbe31d938814cfaf36e252073") -(def mention-text-result-5 [[:text "invalid @0x04fBce10971e1cd7253b98c7b7e54de3729ca57ce41a2bfb0d1c4e0a26f72c4b6913c3487fa1b4bb86125770f1743fb4459da05c1cbe31d938814cfaf36e252073"]]) +(def mention-text-5 + "invalid @0x04fBce10971e1cd7253b98c7b7e54de3729ca57ce41a2bfb0d1c4e0a26f72c4b6913c3487fa1b4bb86125770f1743fb4459da05c1cbe31d938814cfaf36e252073") +(def mention-text-result-5 + [[:text + "invalid @0x04fBce10971e1cd7253b98c7b7e54de3729ca57ce41a2bfb0d1c4e0a26f72c4b6913c3487fa1b4bb86125770f1743fb4459da05c1cbe31d938814cfaf36e252073"]]) (test/deftest test-to-input (test/testing "only text" @@ -113,195 +129,195 @@ (test/testing "starts with mention, some text after mention" (let [text "@User Number One foo" result (mentions/replace-mentions text users)] - (test/is (= result "@0xpk1 foo") (pr-str text)))) + (test/is (= result "@0xpk1 foo") (pr-str text)))) (test/testing "starts with some text, then mention" (let [text "text @User Number One" result (mentions/replace-mentions text users)] - (test/is (= result "text @0xpk1") (pr-str text)))) + (test/is (= result "text @0xpk1") (pr-str text)))) (test/testing "starts with some text, then mention, then more text" (let [text "text @User Number One foo" result (mentions/replace-mentions text users)] - (test/is (= result "text @0xpk1 foo") (pr-str text)))) + (test/is (= result "text @0xpk1 foo") (pr-str text)))) (test/testing "no space before mention" (let [text "text@User Number One" result (mentions/replace-mentions text users)] - (test/is (= result "text@0xpk1") (pr-str text)))) + (test/is (= result "text@0xpk1") (pr-str text)))) (test/testing "two different mentions" (let [text "@User Number One @User Number two" result (mentions/replace-mentions text users)] - (test/is (= result "@0xpk1 @0xpk2") (pr-str text)))) + (test/is (= result "@0xpk1 @0xpk2") (pr-str text)))) (test/testing "two different mentions, separated with comma" (let [text "@User Number One,@User Number two" result (mentions/replace-mentions text users)] - (test/is (= result "@0xpk1,@0xpk2") (pr-str text)))) + (test/is (= result "@0xpk1,@0xpk2") (pr-str text)))) (test/testing "two different mentions inside text" (let [text "foo@User Number One bar @User Number two baz" result (mentions/replace-mentions text users)] - (test/is (= result "foo@0xpk1 bar @0xpk2 baz") (pr-str text)))) + (test/is (= result "foo@0xpk1 bar @0xpk2 baz") (pr-str text)))) (test/testing "ens mention" (let [text "@user2" result (mentions/replace-mentions text users)] - (test/is (= result "@0xpk2") (pr-str text)))) + (test/is (= result "@0xpk2") (pr-str text)))) (test/testing "multiple mentions" - (let [text (string/join - " " - (repeat 1000 "@User Number One @User Number two")) - result (mentions/replace-mentions text users) + (let [text (string/join + " " + (repeat 1000 "@User Number One @User Number two")) + result (mentions/replace-mentions text users) exprected-result (string/join " " (repeat 1000 "@0xpk1 @0xpk2"))] (test/is (= exprected-result result)))) (test/testing "markdown" (test/testing "single * case 1" - (let [text "*@user2*" + (let [text "*@user2*" result (mentions/replace-mentions text users)] (test/is (= result text) (pr-str text)))) (test/testing "single * case 2" - (let [text "*@user2 *" + (let [text "*@user2 *" result (mentions/replace-mentions text users)] - (test/is (= result "*@0xpk2 *") (pr-str text)))) + (test/is (= result "*@0xpk2 *") (pr-str text)))) (test/testing "single * case 3" - (let [text "a*@user2*" + (let [text "a*@user2*" result (mentions/replace-mentions text users)] (test/is (= result text) (pr-str text)))) (test/testing "single * case 4" - (let [text "*@user2 foo*foo" + (let [text "*@user2 foo*foo" result (mentions/replace-mentions text users)] (test/is (= result "*@0xpk2 foo*foo") (pr-str text)))) (test/testing "single * case 5" - (let [text "a *@user2*" + (let [text "a *@user2*" result (mentions/replace-mentions text users)] (test/is (= result text) (pr-str text)))) (test/testing "single * case 6" - (let [text "*@user2 foo*" + (let [text "*@user2 foo*" result (mentions/replace-mentions text users)] (test/is (= result text) (pr-str text)))) (test/testing "single * case 7" - (let [text "@user2 *@user2 foo* @user2" + (let [text "@user2 *@user2 foo* @user2" result (mentions/replace-mentions text users)] (test/is (= result "@0xpk2 *@user2 foo* @0xpk2") (pr-str text)))) (test/testing "single * case 8" - (let [text "*@user2 foo**@user2 foo*" + (let [text "*@user2 foo**@user2 foo*" result (mentions/replace-mentions text users)] (test/is (= result text) (pr-str text)))) (test/testing "single * case 9" - (let [text "*@user2 foo***@user2 foo* @user2" + (let [text "*@user2 foo***@user2 foo* @user2" result (mentions/replace-mentions text users)] (test/is (= result "*@user2 foo***@user2 foo* @0xpk2") (pr-str text)))) (test/testing "double * case 1" - (let [text "**@user2**" + (let [text "**@user2**" result (mentions/replace-mentions text users)] (test/is (= result text) (pr-str text)))) (test/testing "double * case 2" - (let [text "**@user2 **" + (let [text "**@user2 **" result (mentions/replace-mentions text users)] - (test/is (= result "**@0xpk2 **") (pr-str text)))) + (test/is (= result "**@0xpk2 **") (pr-str text)))) (test/testing "double * case 3" - (let [text "a**@user2**" + (let [text "a**@user2**" result (mentions/replace-mentions text users)] (test/is (= result text) (pr-str text)))) (test/testing "double * case 4" - (let [text "**@user2 foo**foo" + (let [text "**@user2 foo**foo" result (mentions/replace-mentions text users)] (test/is (= result "**@user2 foo**foo") (pr-str text)))) (test/testing "double * case 5" - (let [text "a **@user2**" + (let [text "a **@user2**" result (mentions/replace-mentions text users)] (test/is (= result text) (pr-str text)))) (test/testing "double * case 6" - (let [text "**@user2 foo**" + (let [text "**@user2 foo**" result (mentions/replace-mentions text users)] (test/is (= result text) (pr-str text)))) (test/testing "double * case 7" - (let [text "@user2 **@user2 foo** @user2" + (let [text "@user2 **@user2 foo** @user2" result (mentions/replace-mentions text users)] (test/is (= result "@0xpk2 **@user2 foo** @0xpk2") (pr-str text)))) (test/testing "double * case 8" - (let [text "**@user2 foo****@user2 foo**" + (let [text "**@user2 foo****@user2 foo**" result (mentions/replace-mentions text users)] (test/is (= result text) (pr-str text)))) (test/testing "double * case 9" - (let [text "**@user2 foo*****@user2 foo** @user2" + (let [text "**@user2 foo*****@user2 foo** @user2" result (mentions/replace-mentions text users)] (test/is (= result "**@user2 foo*****@user2 foo** @0xpk2") (pr-str text)))) (test/testing "tripple * case 1" - (let [text "***@user2 foo***@user2 foo*" + (let [text "***@user2 foo***@user2 foo*" result (mentions/replace-mentions text users)] (test/is (= result "***@user2 foo***@0xpk2 foo*") (pr-str text)))) (test/testing "tripple ~ case 1" - (let [text "~~~@user2 foo~~~@user2 foo~" + (let [text "~~~@user2 foo~~~@user2 foo~" result (mentions/replace-mentions text users)] (test/is (= result text) (pr-str text)))) (test/testing "quote case 1" - (let [text ">@user2" + (let [text ">@user2" result (mentions/replace-mentions text users)] (test/is (= result text) (pr-str text)))) (test/testing "quote case 2" - (let [text "\n>@user2" + (let [text "\n>@user2" result (mentions/replace-mentions text users)] (test/is (= result text) (pr-str text)))) (test/testing "quote case 3" - (let [text "\n> @user2 \n \n @user2" + (let [text "\n> @user2 \n \n @user2" result (mentions/replace-mentions text users)] (test/is (= result "\n> @user2 \n \n @0xpk2") (pr-str text)))) (test/testing "quote case 4" - (let [text ">@user2\n\n>@user2" + (let [text ">@user2\n\n>@user2" result (mentions/replace-mentions text users)] (test/is (= result text) (pr-str text)))) (test/testing "quote case 5" - (let [text "***hey\n\n>@user2\n\n@user2 foo***" + (let [text "***hey\n\n>@user2\n\n@user2 foo***" result (mentions/replace-mentions text users)] (test/is (= result "***hey\n\n>@user2\n\n@0xpk2 foo***") (pr-str text)))) (test/testing "code case 1" - (let [text "` @user2 `" + (let [text "` @user2 `" result (mentions/replace-mentions text users)] (test/is (= result text) (pr-str text)))) (test/testing "code case 2" - (let [text "` @user2 `" + (let [text "` @user2 `" result (mentions/replace-mentions text users)] (test/is (= result text) (pr-str text)))) (test/testing "code case 3" - (let [text "``` @user2 ```" + (let [text "``` @user2 ```" result (mentions/replace-mentions text users)] (test/is (= result text) (pr-str text)))) (test/testing "code case 4" - (let [text "` ` @user2 ``" + (let [text "` ` @user2 ``" result (mentions/replace-mentions text users)] - (test/is (= result "` ` @0xpk2 ``") (pr-str text))))))) + (test/is (= result "` ` @0xpk2 ``") (pr-str text))))))) diff --git a/src/status_im/chat/models/message.cljs b/src/status_im/chat/models/message.cljs index f567bf9b85..ca1c9b3eec 100644 --- a/src/status_im/chat/models/message.cljs +++ b/src/status_im/chat/models/message.cljs @@ -2,7 +2,6 @@ (:require [clojure.string :as string] [re-frame.core :as re-frame] [status-im.chat.models :as chat-model] - [status-im2.contexts.chat.messages.message.delete-message.events :as delete-message] [status-im.chat.models.loading :as chat.loading] [status-im.chat.models.mentions :as mentions] [status-im.chat.models.message-list :as message-list] @@ -15,6 +14,7 @@ [status-im.utils.gfycat.core :as gfycat] [status-im.utils.platform :as platform] [status-im.utils.types :as types] + [status-im2.contexts.chat.messages.message.delete-message.events :as delete-message] [taoensso.timbre :as log])) (defn- message-loaded? @@ -25,7 +25,8 @@ [db chat-id clock-value] (>= (get-in db [:chats chat-id :deleted-at-clock-value]) clock-value)) -(defn add-timeline-message [acc chat-id message-id message] +(defn add-timeline-message + [acc chat-id message-id message] (-> acc (update-in [:db :messages chat-id] assoc message-id message) (update-in [:db :message-lists chat-id] message-list/add message))) @@ -43,7 +44,9 @@ (let [alias (if (string/blank? alias) (gfycat/generate-gfy from) alias)] - (update-in acc [:db :chats chat-id :users] assoc + (update-in acc + [:db :chats chat-id :users] + assoc from (mentions/add-searchable-phrases {:alias alias @@ -54,7 +57,8 @@ {:db db} messages)) -(defn timeline-message? [db chat-id] +(defn timeline-message? + [db chat-id] (and (get-in db [:pagination-info constants/timeline-chat-id :messages-initialized?]) (or @@ -62,11 +66,13 @@ (when-let [pub-key (get-in db [:chats chat-id :profile-public-key])] (get-in db [:contacts/contacts pub-key :added]))))) -(defn get-timeline-message [db chat-id message-js] +(defn get-timeline-message + [db chat-id message-js] (when (timeline-message? db chat-id) (data-store.messages/<-rpc (types/js->clj message-js)))) -(defn add-message [{:keys [db] :as acc} message-js chat-id message-id cursor-clock-value] +(defn add-message + [{:keys [db] :as acc} message-js chat-id message-id cursor-clock-value] (let [{:keys [replace from clock-value] :as message} (data-store.messages/<-rpc (types/js->clj message-js))] (if (message-loaded? db chat-id message-id) @@ -82,8 +88,9 @@ (update-in [:db :message-lists chat-id] message-list/add message) (or (not cursor-clock-value) (< clock-value cursor-clock-value)) - (update-in [:db :pagination-info chat-id] assoc - :cursor (chat.loading/clock-value->cursor clock-value) + (update-in [:db :pagination-info chat-id] + assoc + :cursor (chat.loading/clock-value->cursor clock-value) :cursor-clock-value clock-value) ;;conj sender for add-sender-to-chat-users @@ -94,11 +101,12 @@ ;;TODO this is expensive (hide-message chat-id replace))))) -(defn reduce-js-messages [{:keys [db] :as acc} ^js message-js] - (let [chat-id (.-localChatId message-js) - clock-value (.-clock message-js) - message-id (.-id message-js) - current-chat-id (:current-chat-id db) +(defn reduce-js-messages + [{:keys [db] :as acc} ^js message-js] + (let [chat-id (.-localChatId message-js) + clock-value (.-clock message-js) + message-id (.-id message-js) + current-chat-id (:current-chat-id db) cursor-clock-value (get-in db [:pagination-info current-chat-id :cursor-clock-value])] ;;ignore not opened chats and earlier clock (if (and (get-in db [:pagination-info chat-id :messages-initialized?]) @@ -110,26 +118,30 @@ (add-message acc message-js chat-id message-id cursor-clock-value) ;; Not in the current view, set all-loaded to false ;; and offload to db and update cursor if necessary - ;;TODO if we'll offload messages , it will conflict with end reached, so probably if we reached the end of visible area, - ;; we need to drop other messages with (< clock-value cursor-clock-value) from response-js so we don't update + ;;TODO if we'll offload messages , it will conflict with end reached, so probably if we reached + ;;the end of visible area, + ;; we need to drop other messages with (< clock-value cursor-clock-value) from response-js so we + ;; don't update ;; :cursor-clock-value because it will be changed when we loadMore message {:db (cond-> (assoc-in db [:pagination-info chat-id :all-loaded?] false) (> clock-value cursor-clock-value) ;;TODO cut older messages from messages-list - (update-in [:pagination-info chat-id] assoc - :cursor (chat.loading/clock-value->cursor clock-value) + (update-in [:pagination-info chat-id] + assoc + :cursor (chat.loading/clock-value->cursor clock-value) :cursor-clock-value clock-value))}) acc))) -(defn receive-many [{:keys [db]} ^js response-js] - (let [messages-js ^js (.splice (.-messages response-js) 0 (if platform/low-device? 3 10)) +(defn receive-many + [{:keys [db]} ^js response-js] + (let [messages-js ^js (.splice (.-messages response-js) 0 (if platform/low-device? 3 10)) {:keys [db senders]} (reduce reduce-js-messages {:db db :chats #{} :senders {} :transactions #{}} messages-js)] ;;we want to render new messages as soon as possible ;;so we dispatch later all other events which can be handled async - {:db db + {:db db :utils/dispatch-later (concat [{:ms 20 :dispatch [:process-response response-js]}] (when (and (:current-chat-id db) (= "active" (:app-state db))) @@ -137,7 +149,8 @@ (when (seq senders) [{:ms 100 :dispatch [:chat/add-senders-to-chat-users (vals senders)]}]))})) -(defn reduce-js-statuses [db ^js message-js] +(defn reduce-js-statuses + [db ^js message-js] (let [chat-id (.-localChatId message-js) profile-initialized (get-in db [:pagination-info chat-id :messages-initialized?]) timeline-message (timeline-message? db chat-id) @@ -165,8 +178,8 @@ (when (get-in db [:messages chat-id message-id]) (fx/merge cofx {:db (assoc-in db - [:messages chat-id message-id :outgoing-status] - status)}))) + [:messages chat-id message-id :outgoing-status] + status)}))) (fx/defn update-message-status [{:keys [db] :as cofx} chat-id message-id status] @@ -176,10 +189,10 @@ (fx/defn resend-message [{:keys [db] :as cofx} chat-id message-id] (fx/merge cofx - {::json-rpc/call [{:method "wakuext_reSendChatMessage" - :params [message-id] + {::json-rpc/call [{:method "wakuext_reSendChatMessage" + :params [message-id] :on-success #(log/debug "re-sent message successfully") - :on-error #(log/error "failed to re-send message" %)}]} + :on-error #(log/error "failed to re-send message" %)}]} (update-message-status chat-id message-id :sending))) (fx/defn send-message @@ -196,19 +209,25 @@ (let [mark-as-deleted-fx (->> removed-messages (map #(assoc % :message-id (:messageId %))) (group-by :chatId) - (mapv (fn [[chat-id messages]] (delete-message/delete-messages-localy messages chat-id)))) - mark-as-seen-fx (mapv (fn [removed-message] - (let [chat-id (:chatId removed-message) - message-id (:messageId removed-message)] - (data-store.messages/mark-messages-seen chat-id - [message-id] - #(re-frame/dispatch [:chat/decrease-unviewed-count chat-id %3])))) - removed-messages) + (mapv (fn [[chat-id messages]] + (delete-message/delete-messages-localy messages chat-id)))) + mark-as-seen-fx (mapv + (fn [removed-message] + (let [chat-id (:chatId removed-message) + message-id (:messageId removed-message)] + (data-store.messages/mark-messages-seen chat-id + [message-id] + #(re-frame/dispatch + [:chat/decrease-unviewed-count + chat-id %3])))) + removed-messages) remove-messages-fx (fn [{:keys [db]}] {:dispatch [:activity-center.notifications/fetch-unread-count]})] - (apply fx/merge cofx (-> mark-as-deleted-fx - (concat mark-as-seen-fx) - (conj remove-messages-fx))))) + (apply fx/merge + cofx + (-> mark-as-deleted-fx + (concat mark-as-seen-fx) + (conj remove-messages-fx))))) (comment (handle-removed-messages @@ -218,15 +237,20 @@ :m4 {:chat-id :c2 :message-id :m4}}}}} [:m1 :m3])) -(defn remove-cleared-message [messages cleared-at] - (into {} (remove #(let [message-clock (:clock-value (second %))] - (<= message-clock cleared-at)) - messages))) +(defn remove-cleared-message + [messages cleared-at] + (into {} + (remove #(let [message-clock (:clock-value (second %))] + (<= message-clock cleared-at)) + messages))) (fx/defn handle-cleared-histories-messages {:events [::handle-cleared-hisotories-messages]} [{:keys [db]} cleared-histories] {:db (reduce (fn [acc current] - (update-in acc [:messages (:chatId current)] remove-cleared-message (:clearedAt current))) + (update-in acc + [:messages (:chatId current)] + remove-cleared-message + (:clearedAt current))) db cleared-histories)}) diff --git a/src/status_im/chat/models/message_content.cljs b/src/status_im/chat/models/message_content.cljs index d978b131d7..35fe027cb8 100644 --- a/src/status_im/chat/models/message_content.cljs +++ b/src/status_im/chat/models/message_content.cljs @@ -1,9 +1,10 @@ (ns status-im.chat.models.message-content (:require [status-im.constants :as constants])) -(def stylings [[:bold constants/regx-bold] - [:italic constants/regx-italic] - [:backquote constants/regx-backquote]]) +(def stylings + [[:bold constants/regx-bold] + [:italic constants/regx-italic] + [:backquote constants/regx-backquote]]) (defn emoji-only-content? "Determines if text is just an emoji" diff --git a/src/status_im/chat/models/message_list.cljs b/src/status_im/chat/models/message_list.cljs index 13b8c2221b..9cc3a6b8be 100644 --- a/src/status_im/chat/models/message_list.cljs +++ b/src/status_im/chat/models/message_list.cljs @@ -4,20 +4,23 @@ [status-im.utils.datetime :as time] [status-im.utils.fx :as fx])) -(defn- add-datemark [{:keys [whisper-timestamp] :as msg}] +(defn- add-datemark + [{:keys [whisper-timestamp] :as msg}] ;;TODO this is slow (assoc msg :datemark (time/day-relative whisper-timestamp))) -(defn- add-timestamp [{:keys [whisper-timestamp] :as msg}] +(defn- add-timestamp + [{:keys [whisper-timestamp] :as msg}] (assoc msg :timestamp-str (time/timestamp->time whisper-timestamp))) -(defn prepare-message [{:keys [message-id - clock-value - message-type - from - outgoing - whisper-timestamp - deleted-for-me?]}] +(defn prepare-message + [{:keys [message-id + clock-value + message-type + from + outgoing + whisper-timestamp + deleted-for-me?]}] (-> {:whisper-timestamp whisper-timestamp :from from :one-to-one? (= constants/message-type-one-to-one message-type) @@ -69,7 +72,8 @@ the most recent message, similarly :first-in-group? is the most recent message in a group." [{:keys [system-message? - one-to-one? outgoing] :as current-message} + one-to-one? outgoing] + :as current-message} {:keys [outgoing-seen?] :as previous-message} next-message] (let [last-in-group? (or (nil? next-message) @@ -87,7 +91,7 @@ (not system-message?) (not outgoing) (not one-to-one?)) - :display-photo? (display-photo? current-message)))) + :display-photo? (display-photo? current-message)))) (defn update-next-message "Update next message in the list, we set :first? to false, and check if it @@ -95,26 +99,28 @@ [current-message next-message] (assoc next-message - :first? false + :first? false :first-outgoing? (and (not (:first-outgoing? current-message)) (:first-outgoing? next-message)) - :outgoing-seen? (:outgoing-seen? current-message) + :outgoing-seen? (:outgoing-seen? current-message) :first-in-group? (not (same-group? current-message next-message)))) (defn update-previous-message "If this is a new group, we mark the previous as the last one in the group" - [current-message {:keys [one-to-one? - system-message? - outgoing] :as previous-message}] + [current-message + {:keys [one-to-one? + system-message? + outgoing] + :as previous-message}] (let [last-in-group? (not (same-group? current-message previous-message))] (assoc previous-message :display-username? (and last-in-group? (not system-message?) (not outgoing) (not one-to-one?)) - :last-in-group? last-in-group?))) + :last-in-group? last-in-group?))) (defn get-prev-element "Get previous item in the iterator, and wind it back to the initial state" @@ -135,11 +141,11 @@ (defn update-message "Update the message and siblings with positional info" [^js tree message] - (let [^js iter (.find tree message) - ^js previous-message (when (.-hasPrev iter) - (get-prev-element iter)) - ^js next-message (when (.-hasNext iter) - (get-next-element iter)) + (let [^js iter (.find tree message) + ^js previous-message (when (.-hasPrev iter) + (get-prev-element iter)) + ^js next-message (when (.-hasNext iter) + (get-next-element iter)) ^js message-with-pos-data (add-group-info message previous-message next-message)] (cond-> (.update iter message-with-pos-data) next-message @@ -157,9 +163,9 @@ (let [iter (.find tree prepared-message)] (if (not iter) tree - (let [^js new-tree (.remove iter) - ^js next-message (when (.-hasNext iter) - (get-next-element iter))] + (let [^js new-tree (.remove iter) + ^js next-message (when (.-hasNext iter) + (get-next-element iter))] (if (not next-message) new-tree (update-message new-tree next-message)))))) @@ -173,21 +179,26 @@ (let [^js tree (.insert old-message-list prepared-message prepared-message)] (update-message tree prepared-message))) -(defn add [message-list message] +(defn add + [message-list message] (insert-message (or message-list (rb-tree compare-fn)) (prepare-message message))) -(defn add-many [message-list messages] +(defn add-many + [message-list messages] (reduce add message-list messages)) -(defn ->seq [^js message-list] +(defn ->seq + [^js message-list] (if message-list (array-seq (.-values message-list)) [])) -;; NOTE(performance): this is too expensive, probably we could mark message somehow and just hide it in the UI +;; NOTE(performance): this is too expensive, probably we could mark message somehow and just hide it in +;; the UI (fx/defn rebuild-message-list [{:keys [db]} chat-id] - {:db (assoc-in db [:message-lists chat-id] - (add-many nil (vals (get-in db [:messages chat-id]))))}) + {:db (assoc-in db + [:message-lists chat-id] + (add-many nil (vals (get-in db [:messages chat-id]))))}) diff --git a/src/status_im/chat/models/message_list_test.cljs b/src/status_im/chat/models/message_list_test.cljs index 0ce4e88c57..5862687e3d 100644 --- a/src/status_im/chat/models/message_list_test.cljs +++ b/src/status_im/chat/models/message_list_test.cljs @@ -5,25 +5,26 @@ (deftest message-stream-tests (testing "building the list" - (let [m1 {:from "1" - :clock-value 1 - :message-id "message-1" + (let [m1 {:from "1" + :clock-value 1 + :message-id "message-1" :whisper-timestamp 1 - :outgoing false} - m2 {:from "2" - :clock-value 2 - :message-id "message-2" + :outgoing false} + m2 {:from "2" + :clock-value 2 + :message-id "message-2" :whisper-timestamp 2 - :outgoing true} - m3 {:from "2" - :clock-value 3 - :message-id "message-3" + :outgoing true} + m3 {:from "2" + :clock-value 3 + :message-id "message-3" :whisper-timestamp 3 - :outgoing true} + :outgoing true} messages (shuffle [m1 m2 m3]) [actual-m1 actual-m2 - actual-m3] (s/->seq (s/add-many nil messages))] + actual-m3] + (s/->seq (s/add-many nil messages))] (testing "it sorts them correclty" (is (= "message-3" (:message-id actual-m1))) (is (= "message-2" (:message-id actual-m2))) @@ -49,92 +50,113 @@ (is (:last-in-group? actual-m2)) (is (:last-in-group? actual-m3)))))) -(def ascending-range (mapv - #(let [i (+ 100000 %)] - {:clock-value i - :whisper-timestamp i - :timestamp i - :message-id (str i)}) - (range 2000))) +(def ascending-range + (mapv + #(let [i (+ 100000 %)] + {:clock-value i + :whisper-timestamp i + :timestamp i + :message-id (str i)}) + (range 2000))) (def descending-range (reverse ascending-range)) (def random-range (shuffle ascending-range)) -(defnp build-message-list [messages] - (s/add-many nil messages)) +(defnp build-message-list + [messages] + (s/add-many nil messages)) -(defnp append-to-message-list [l message] - (s/add l message)) +(defnp append-to-message-list + [l message] + (s/add l message)) -(defnp prepend-to-message-list [l message] - (s/add l message)) +(defnp prepend-to-message-list + [l message] + (s/add l message)) -(defnp insert-close-to-head-message-list [l message] - (s/add l message)) +(defnp insert-close-to-head-message-list + [l message] + (s/add l message)) -(defnp insert-middle-message-list [l message] - (s/add l message)) +(defnp insert-middle-message-list + [l message] + (s/add l message)) -(tufte/add-basic-println-handler! {:format-pstats-opts {:columns [:n-calls :mean :min :max :clock :total] +(tufte/add-basic-println-handler! {:format-pstats-opts {:columns [:n-calls :mean :min :max :clock + :total] :format-id-fn name}}) (deftest ^:benchmark benchmark-list - (let [messages (sort-by :timestamp (mapv (fn [i] (let [i (+ 100000 i 1)] {:timestamp i :clock-value i :message-id (str i) :whisper-timestamp i})) (range 100))) + (let [messages (sort-by + :timestamp + (mapv (fn [i] + (let [i (+ 100000 i 1)] + {:timestamp i :clock-value i :message-id (str i) :whisper-timestamp i})) + (range 100))) built-list (s/add-many nil messages)] (testing "prepending to list" - (profile {} (dotimes [_ 10] (prepend-to-message-list - built-list - {:clock-value 200000 - :message-id "200000" - :whisper-timestamp 21 - :timestamp 21})))) + (profile {} + (dotimes [_ 10] + (prepend-to-message-list + built-list + {:clock-value 200000 + :message-id "200000" + :whisper-timestamp 21 + :timestamp 21})))) (testing "append to list" - (profile {} (dotimes [_ 10] (append-to-message-list - built-list - {:clock-value 100000 - :message-id "100000" - :whisper-timestamp 100000 - :timestamp 100000})))) + (profile {} + (dotimes [_ 10] + (append-to-message-list + built-list + {:clock-value 100000 + :message-id "100000" + :whisper-timestamp 100000 + :timestamp 100000})))) (testing "insert close to head" - (profile {} (dotimes [_ 10] (insert-close-to-head-message-list - built-list - {:clock-value 109970 - :message-id "109970" - :whisper-timestamp 1000 - :timestamp 1000})))) + (profile {} + (dotimes [_ 10] + (insert-close-to-head-message-list + built-list + {:clock-value 109970 + :message-id "109970" + :whisper-timestamp 1000 + :timestamp 1000})))) (testing "insert into the middle list" - (profile {} (dotimes [_ 10] (insert-middle-message-list - built-list - {:clock-value 105000 - :message-id "10500" - :whisper-timestamp 1000 - :timestamp 1000})))))) + (profile {} + (dotimes [_ 10] + (insert-middle-message-list + built-list + {:clock-value 105000 + :message-id "10500" + :whisper-timestamp 1000 + :timestamp 1000})))))) (deftest message-list - (let [current-messages [{:clock-value 109 - :message-id "109" - :timestamp 9 + (let [current-messages [{:clock-value 109 + :message-id "109" + :timestamp 9 :whisper-timestamp 9} - {:clock-value 106 - :message-id "106" - :timestamp 6 + {:clock-value 106 + :message-id "106" + :timestamp 6 :whisper-timestamp 6} - {:clock-value 103 - :message-id "103" - :timestamp 3 + {:clock-value 103 + :message-id "103" + :timestamp 3 :whisper-timestamp 3}] - current-list (s/add-many nil current-messages)] + current-list (s/add-many nil current-messages)] (testing "removing a message" - (let [updated-list (-> (s/remove-message current-list {:clock-value 106 - :message-id "106"}) + (let [updated-list (-> (s/remove-message current-list + {:clock-value 106 + :message-id "106"}) (s/->seq))] (is (= 2 (count updated-list))) (is (= 103 (-> (nth updated-list 1) :clock-value))))) (testing "inserting a newer message" - (let [new-message {:timestamp 12 - :clock-value 112 - :message-id "112" + (let [new-message {:timestamp 12 + :clock-value 112 + :message-id "112" :whisper-timestamp 12}] (is (= 112 (-> (s/add current-list new-message) @@ -142,9 +164,9 @@ first :clock-value))))) (testing "inserting an older message" - (let [new-message {:timestamp 0 - :clock-value 100 - :message-id "100" + (let [new-message {:timestamp 0 + :clock-value 100 + :message-id "100" :whisper-timestamp 0}] (is (= 100 (-> (s/add current-list new-message) @@ -152,9 +174,9 @@ last :clock-value))))) (testing "inserting in the middle of the list" - (let [new-message {:timestamp 7 - :clock-value 107 - :message-id "107" + (let [new-message {:timestamp 7 + :clock-value 107 + :message-id "107" :whisper-timestamp 7}] (is (= 107 (-> (s/add current-list new-message) @@ -162,9 +184,9 @@ (nth 1) :clock-value))))) (testing "inserting in the middle of the list, clock-value clash" - (let [new-message {:timestamp 6 - :clock-value 106 - :message-id "106a" + (let [new-message {:timestamp 6 + :clock-value 106 + :message-id "106a" :whisper-timestamp 6}] (is (= "106a" (-> (s/add current-list new-message) @@ -175,28 +197,28 @@ (deftest same-group-test (testing "two messages from the same author and close together" (is (s/same-group? {:whisper-timestamp 1 - :from "1"} + :from "1"} {:whisper-timestamp 2 - :from "1"}))) + :from "1"}))) (testing "two messages from the same author, close together, first system-message" (is (not (s/same-group? {:whisper-timestamp 1 - :system-message? true - :from "1"} + :system-message? true + :from "1"} {:whisper-timestamp 2 - :from "1"})))) + :from "1"})))) (testing "two messages from the same author, close together, second system-message" (is (not (s/same-group? {:whisper-timestamp 1 - :from "1"} + :from "1"} {:whisper-timestamp 2 - :system-message? true - :from "1"})))) + :system-message? true + :from "1"})))) (testing "two messages from the same author and far apart" (is (not (s/same-group? {:whisper-timestamp 1 - :from "1"} + :from "1"} {:whisper-timestamp 2000000 - :from "1"})))) + :from "1"})))) (testing "two messages not from the same author and close together" (is (not (s/same-group? {:whisper-timestamp 1 - :from "2"} + :from "2"} {:whisper-timestamp 2 - :from "1"}))))) + :from "1"}))))) diff --git a/src/status_im/chat/models/message_test.cljs b/src/status_im/chat/models/message_test.cljs index baa8c30dc9..0978f3a75d 100644 --- a/src/status_im/chat/models/message_test.cljs +++ b/src/status_im/chat/models/message_test.cljs @@ -1,59 +1,66 @@ (ns status-im.chat.models.message-test (:require [cljs.test :refer-macros [deftest is testing]] - [status-im.chat.models.message :as message] [status-im.chat.models.loading :as loading] + [status-im.chat.models.message :as message] [status-im.ui.screens.chat.state :as view.state])) (deftest add-received-message-test (with-redefs [message/add-message #(identity %1)] - (let [chat-id "chat-id" - clock-value 10 + (let [chat-id "chat-id" + clock-value 10 cursor-clock-value (dec clock-value) - cursor (loading/clock-value->cursor cursor-clock-value) - cofx {:now 0 - :db {:current-chat-id chat-id - :pagination-info {chat-id {:messages-initialized? true - :cursor cursor - :cursor-clock-value cursor-clock-value}}}} - message #js {:localChatId chat-id - :clock clock-value - :alias "alias" - :name "name" - :identicon "identicon" - :from "from"}] + cursor (loading/clock-value->cursor cursor-clock-value) + cofx {:now 0 + :db {:current-chat-id chat-id + :pagination-info {chat-id {:messages-initialized? true + :cursor cursor + :cursor-clock-value cursor-clock-value}}}} + message #js + {:localChatId chat-id + :clock clock-value + :alias "alias" + :name "name" + :identicon "identicon" + :from "from"}] ;; <- cursor ;; <- message ;; <- top of the chat (testing "there's no hidden item" (with-redefs [view.state/first-not-visible-item (atom nil)] - (is (= {:db {:current-chat-id "chat-id", - :pagination-info - {"chat-id" - {:messages-initialized? true - :cursor - "00000000000000000000000000000000000000000000000000090x0000000000000000000000000000000000000000000000000000000000000000", - :cursor-clock-value 9}}}} - (dissoc (message/receive-many - cofx - #js {:messages (to-array [message])}) - :utils/dispatch-later))))) + (is + (= + {:db + {:current-chat-id "chat-id" + :pagination-info + {"chat-id" + {:messages-initialized? true + :cursor + "00000000000000000000000000000000000000000000000000090x0000000000000000000000000000000000000000000000000000000000000000" + :cursor-clock-value 9}}}} + (dissoc (message/receive-many + cofx + #js {:messages (to-array [message])}) + :utils/dispatch-later))))) ;; <- cursor ;; <- first-hidden-item ;; <- message ;; <- top of the chat (testing "the hidden item has a clock value less than the current" (with-redefs [view.state/first-not-visible-item (atom {:clock-value (dec clock-value)})] - (is (= {:db {:current-chat-id "chat-id", - :pagination-info - {"chat-id" - {:messages-initialized? true - :cursor - "00000000000000000000000000000000000000000000000000090x0000000000000000000000000000000000000000000000000000000000000000", - :cursor-clock-value 9}}}} - (dissoc (message/receive-many - cofx - #js {:messages (to-array [message])}) - :utils/dispatch-later))))) + (is + (= + {:db + {:current-chat-id "chat-id" + :pagination-info + {"chat-id" + {:messages-initialized? true + :cursor + "00000000000000000000000000000000000000000000000000090x0000000000000000000000000000000000000000000000000000000000000000" + :cursor-clock-value 9}}}} + (dissoc (message/receive-many + cofx + #js {:messages (to-array [message])}) + :utils/dispatch-later))))) ;; <- cursor ;; <- message ;; <- first-hidden-item @@ -68,48 +75,59 @@ (is (not (get-in result [:db :pagination-info chat-id :all-loaded?])))) (testing "it updates cursor-clock-value & cursor" (is (= clock-value (get-in result [:db :pagination-info chat-id :cursor-clock-value]))) - (is (= (loading/clock-value->cursor clock-value) (get-in result [:db :pagination-info chat-id :cursor]))))))) + (is (= (loading/clock-value->cursor clock-value) + (get-in result [:db :pagination-info chat-id :cursor]))))))) ;; <- message ;; <- first-hidden-item ;; <- top of the chat (testing "the message falls between the first-hidden-item and cursor is nil" (with-redefs [view.state/first-not-visible-item (atom {:clock-value (inc clock-value)})] (let [result (dissoc (message/receive-many - (update-in cofx [:db :pagination-info chat-id] dissoc :cursor :cursor-clock-value) + (update-in cofx + [:db :pagination-info chat-id] + dissoc + :cursor + :cursor-clock-value) #js {:messages (to-array [message])}) :utils/dispatch-later)] (testing "it sets all-loaded? to false" (is (not (get-in result [:db :pagination-info chat-id :all-loaded?])))) (testing "it updates cursor-clock-value & cursor" (is (= clock-value (get-in result [:db :pagination-info chat-id :cursor-clock-value]))) - (is (= (loading/clock-value->cursor clock-value) (get-in result [:db :pagination-info chat-id :cursor]))))))) + (is (= (loading/clock-value->cursor clock-value) + (get-in result [:db :pagination-info chat-id :cursor]))))))) ;; <- message ;; <- cursor ;; <- first-hidden-item ;; <- top of the chat (testing "the message falls before both the first-hidden-item and cursor" (with-redefs [view.state/first-not-visible-item (atom {:clock-value (inc clock-value)})] - (let [message #js {:localChatId chat-id - :clock (- clock-value 2) - :alias "alias" - :name "name" - :identicon "identicon" - :from "from"} - result (dissoc (message/receive-many - cofx - #js {:messages (to-array [message])}) - :utils/dispatch-later)] + (let [message #js + {:localChatId chat-id + :clock (- clock-value 2) + :alias "alias" + :name "name" + :identicon "identicon" + :from "from"} + result (dissoc (message/receive-many + cofx + #js {:messages (to-array [message])}) + :utils/dispatch-later)] (testing "it sets all-loaded? to false" (is (not (get-in result [:db :pagination-info chat-id :all-loaded?])))) (testing "it does not update cursor-clock-value & cursor" - (is (= cursor-clock-value (get-in result [:db :pagination-info chat-id :cursor-clock-value]))) + (is (= cursor-clock-value + (get-in result [:db :pagination-info chat-id :cursor-clock-value]))) (is (= cursor (get-in result [:db :pagination-info chat-id :cursor])))))))))) (deftest message-loaded? (testing "it returns false when it's not in loaded message" (is (not (#'status-im.chat.models.message/message-loaded? {:messages {"a" {}}} "a" "message-id")))) (testing "it returns true when it's already in the loaded message" - (is (#'status-im.chat.models.message/message-loaded? {:messages {"a" {"message-id" {}}}} "a" "message-id")))) + (is (#'status-im.chat.models.message/message-loaded? + {:messages {"a" {"message-id" {}}}} + "a" + "message-id")))) (deftest earlier-than-deleted-at? (testing "it returns true when the clock-value is the same as the deleted-clock-value in chat" diff --git a/src/status_im/chat/models/reactions.cljs b/src/status_im/chat/models/reactions.cljs index 7654466520..6776cb02f5 100644 --- a/src/status_im/chat/models/reactions.cljs +++ b/src/status_im/chat/models/reactions.cljs @@ -1,28 +1,36 @@ (ns status-im.chat.models.reactions - (:require [status-im.constants :as constants] - [re-frame.core :as re-frame] - [status-im.utils.fx :as fx] - [taoensso.timbre :as log] + (:require [re-frame.core :as re-frame] + [status-im.constants :as constants] + [status-im.data-store.reactions :as data-store.reactions] [status-im.transport.message.protocol :as message.protocol] - [status-im.data-store.reactions :as data-store.reactions])) + [status-im.utils.fx :as fx] + [taoensso.timbre :as log])) -(defn update-reaction [acc retracted chat-id message-id emoji-id emoji-reaction-id reaction] +(defn update-reaction + [acc retracted chat-id message-id emoji-id emoji-reaction-id reaction] ;; NOTE(Ferossgp): For a better performance, better to not keep in db all retracted reactions ;; retraction will always come after the reaction so there shouldn't be a conflict (if retracted (update-in acc [chat-id message-id emoji-id] dissoc emoji-reaction-id) (assoc-in acc [chat-id message-id emoji-id emoji-reaction-id] reaction))) -(defn process-reactions [chats] +(defn process-reactions + [chats] (fn [reactions new-reactions] ;; TODO(Ferossgp): handling own reaction in subscription could be expensive, ;; for better performance we can here separate own reaction into 2 maps (reduce - (fn [acc {:keys [chat-id message-id emoji-id emoji-reaction-id retracted] - :as reaction}] + (fn [acc + {:keys [chat-id message-id emoji-id emoji-reaction-id retracted] + :as reaction}] (cond-> (update-reaction acc retracted chat-id message-id emoji-id emoji-reaction-id reaction) (get-in chats [chat-id :profile-public-key]) - (update-reaction retracted constants/timeline-chat-id message-id emoji-id emoji-reaction-id reaction))) + (update-reaction retracted + constants/timeline-chat-id + message-id + emoji-id + emoji-reaction-id + reaction))) reactions new-reactions))) @@ -81,16 +89,19 @@ [{:keys [db]} reaction] {:db (update db :reactions (process-reactions (:chats db)) [reaction])}) -(defn message-reactions [current-public-key reactions] +(defn message-reactions + [current-public-key reactions] (reduce (fn [acc [emoji-id reactions]] (if (pos? (count reactions)) (let [own (first (filter (fn [[_ {:keys [from]}]] - (= from current-public-key)) reactions))] - (conj acc {:emoji-id emoji-id - :own (boolean (seq own)) - :emoji-reaction-id (:emoji-reaction-id (second own)) - :quantity (count reactions)})) + (= from current-public-key)) + reactions))] + (conj acc + {:emoji-id emoji-id + :own (boolean (seq own)) + :emoji-reaction-id (:emoji-reaction-id (second own)) + :quantity (count reactions)})) acc)) [] reactions)) diff --git a/src/status_im/chat/models/transport.cljs b/src/status_im/chat/models/transport.cljs index 1c318d09c1..f4398479ba 100644 --- a/src/status_im/chat/models/transport.cljs +++ b/src/status_im/chat/models/transport.cljs @@ -1,8 +1,8 @@ ;;this ns is needed because of cycled deps (ns status-im.chat.models.transport - (:require [status-im.utils.fx :as fx] + (:require [status-im.chat.models.message :as chat.message] [status-im.transport.message.core :as transport.message] - [status-im.chat.models.message :as chat.message])) + [status-im.utils.fx :as fx])) (fx/defn chat-ui-resend-message {:events [:chat.ui/resend-message]} diff --git a/src/status_im/chat/models_test.cljs b/src/status_im/chat/models_test.cljs index 5bc72ec997..f3022faf3f 100644 --- a/src/status_im/chat/models_test.cljs +++ b/src/status_im/chat/models_test.cljs @@ -1,14 +1,14 @@ (ns status-im.chat.models-test (:require [cljs.test :refer-macros [deftest is testing]] - [status-im.utils.clocks :as utils.clocks] [status-im.chat.models :as chat] - [status-im.chat.models.images :as images])) + [status-im.chat.models.images :as images] + [status-im.utils.clocks :as utils.clocks])) (deftest clear-history-test (let [chat-id "1" cofx {:db {:message-lists {chat-id [{:something "a"}]} - :chats {chat-id {:last-message {:clock-value 10} - :unviewed-messages-count 1}}}}] + :chats {chat-id {:last-message {:clock-value 10} + :unviewed-messages-count 1}}}}] (testing "it deletes all the messages" (let [actual (chat/clear-history cofx chat-id true)] (is (= {} (get-in actual [:db :messages chat-id]))))) @@ -25,7 +25,7 @@ (let [actual (chat/clear-history (update-in cofx [:db :chats chat-id] assoc - :last-message nil + :last-message nil :deleted-at-clock-value 100) chat-id true)] @@ -35,7 +35,8 @@ (let [actual (chat/clear-history (update-in cofx [:db :chats chat-id] assoc - :last-message nil) + :last-message + nil) chat-id true)] (is (= 42 (get-in actual [:db :chats chat-id :deleted-at-clock-value])))))))) @@ -45,7 +46,7 @@ cofx {:db {:messages {chat-id {"1" {:clock-value 1} "2" {:clock-value 10} "3" {:clock-value 2}}} - :chats {chat-id {:last-message {:clock-value 10}}}}}] + :chats {chat-id {:last-message {:clock-value 10}}}}}] (testing "it deletes all the messages" (let [actual (chat/remove-chat cofx chat-id)] (is (= nil (get-in actual [:db :messages chat-id]))))) @@ -80,11 +81,11 @@ (def test-db {:multiaccount {:public-key "me"} - :messages {"status" {"4" {} "5" {} "6" {}}} - :chats {"status" {:public? true - :group-chat true} - "opened" {} - "1-1" {}}}) + :messages {"status" {"4" {} "5" {} "6" {}}} + :chats {"status" {:public? true + :group-chat true} + "opened" {} + "1-1" {}}}) (deftest navigate-to-chat-nav2 (let [chat-id "test_chat" diff --git a/src/status_im/commands/core.cljs b/src/status_im/commands/core.cljs index 2f9fae0c91..0f532257e7 100644 --- a/src/status_im/commands/core.cljs +++ b/src/status_im/commands/core.cljs @@ -7,10 +7,10 @@ (fx/defn handle-prepare-accept-request-address-for-transaction {:events [::prepare-accept-request-address-for-transaction]} [{:keys [db]} message] - {:db (assoc db - :commands/select-account - {:message message - :from (ethereum/get-default-account (:multiaccount/accounts db))}) + {:db (assoc db + :commands/select-account + {:message message + :from (ethereum/get-default-account (:multiaccount/accounts db))}) :show-select-acc-sheet nil}) (fx/defn set-selected-account @@ -22,24 +22,24 @@ (fx/defn handle-accept-request-address-for-transaction {:events [::accept-request-address-for-transaction]} [{:keys [db]} message-id address] - {:db (dissoc db :commands/select-account) - ::json-rpc/call [{:method "wakuext_acceptRequestAddressForTransaction" - :params [message-id address] + {:db (dissoc db :commands/select-account) + ::json-rpc/call [{:method "wakuext_acceptRequestAddressForTransaction" + :params [message-id address] :js-response true - :on-success #(re-frame/dispatch [:transport/message-sent %])}]}) + :on-success #(re-frame/dispatch [:transport/message-sent %])}]}) (fx/defn handle-decline-request-address-for-transaction {:events [::decline-request-address-for-transaction]} [_ message-id] - {::json-rpc/call [{:method "wakuext_declineRequestAddressForTransaction" - :params [message-id] + {::json-rpc/call [{:method "wakuext_declineRequestAddressForTransaction" + :params [message-id] :js-response true - :on-success #(re-frame/dispatch [:transport/message-sent %])}]}) + :on-success #(re-frame/dispatch [:transport/message-sent %])}]}) (fx/defn handle-decline-request-transaction {:events [::decline-request-transaction]} [cofx message-id] - {::json-rpc/call [{:method "wakuext_declineRequestTransaction" - :params [message-id] + {::json-rpc/call [{:method "wakuext_declineRequestTransaction" + :params [message-id] :js-response true - :on-success #(re-frame/dispatch [:transport/message-sent %])}]}) + :on-success #(re-frame/dispatch [:transport/message-sent %])}]}) diff --git a/src/status_im/communities/core.cljs b/src/status_im/communities/core.cljs index 687f46811e..08aaadd0ad 100644 --- a/src/status_im/communities/core.cljs +++ b/src/status_im/communities/core.cljs @@ -1,25 +1,25 @@ (ns status-im.communities.core - (:require - [re-frame.core :as re-frame] - [clojure.walk :as walk] - [clojure.string :as string] - [clojure.set :as clojure.set] - [taoensso.timbre :as log] - [status-im.async-storage.core :as async-storage] - [status-im.utils.fx :as fx] - [status-im.constants :as constants] - [status-im.bottom-sheet.core :as bottom-sheet] - [status-im.utils.universal-links.core :as universal-links] - [status-im.ethereum.json-rpc :as json-rpc] - [quo.design-system.colors :as colors] - [status-im2.navigation.events :as navigation] - [status-im.utils.handlers :refer [>evt]] - [status-im.ui.components.emoji-thumbnail.styles :as emoji-thumbnail-styles] - [status-im2.contexts.activity-center.events :as activity-center])) + (:require [clojure.set :as clojure.set] + [clojure.string :as string] + [clojure.walk :as walk] + [quo.design-system.colors :as colors] + [re-frame.core :as re-frame] + [status-im.async-storage.core :as async-storage] + [status-im.bottom-sheet.core :as bottom-sheet] + [status-im.constants :as constants] + [status-im.ethereum.json-rpc :as json-rpc] + [status-im.ui.components.emoji-thumbnail.styles :as emoji-thumbnail-styles] + [status-im.utils.fx :as fx] + [status-im.utils.handlers :refer [>evt]] + [status-im.utils.universal-links.core :as universal-links] + [status-im2.contexts.activity-center.events :as activity-center] + [status-im2.navigation.events :as navigation] + [taoensso.timbre :as log])) (def crop-size 1000) -(defn universal-link [community-id] +(defn universal-link + [community-id] (str (:external universal-links/domains) "/c/" community-id)) @@ -28,18 +28,22 @@ [{:name "Status" :id constants/status-community-id}]) -(defn <-request-to-join-community-rpc [r] - (clojure.set/rename-keys r {:communityId :community-id - :publicKey :public-key - :chatId :chat-id})) +(defn <-request-to-join-community-rpc + [r] + (clojure.set/rename-keys r + {:communityId :community-id + :publicKey :public-key + :chatId :chat-id})) -(defn <-requests-to-join-community-rpc [requests] +(defn <-requests-to-join-community-rpc + [requests] (reduce (fn [acc r] (assoc acc (:id r) (<-request-to-join-community-rpc r))) {} requests)) -(defn <-chats-rpc [chats] +(defn <-chats-rpc + [chats] (reduce-kv (fn [acc k v] (assoc acc (name k) @@ -50,7 +54,8 @@ {} chats)) -(defn <-categories-rpc [categ] +(defn <-categories-rpc + [categ] (reduce-kv (fn [acc k v] (assoc acc (name k) @@ -58,7 +63,8 @@ {} categ)) -(defn <-rpc [c] +(defn <-rpc + [c] (-> c (clojure.set/rename-keys {:canRequestAccess :can-request-access? :canManageUsers :can-manage-users? @@ -70,14 +76,17 @@ (update :chats <-chats-rpc) (update :categories <-categories-rpc))) -(defn fetch-community-id-input [{:keys [db]}] +(defn fetch-community-id-input + [{:keys [db]}] (:communities/community-id-input db)) -(fx/defn handle-request-to-join [{:keys [db]} r] +(fx/defn handle-request-to-join + [{:keys [db]} r] (let [{:keys [id community-id] :as request} (<-request-to-join-community-rpc r)] {:db (assoc-in db [:communities/requests-to-join community-id id] request)})) -(fx/defn handle-removed-chats [{:keys [db]} chat-ids] +(fx/defn handle-removed-chats + [{:keys [db]} chat-ids] {:db (reduce (fn [db chat-id] (update db :chats dissoc chat-id)) db @@ -96,7 +105,8 @@ db communities)}) -(fx/defn handle-response [_ response-js] +(fx/defn handle-response + [_ response-js] {:dispatch [:sanitize-messages-and-process-response response-js]}) (fx/defn left @@ -117,8 +127,9 @@ [cofx community-id] {::json-rpc/call [{:method "wakuext_exportCommunity" :params [community-id] - :on-success #(re-frame/dispatch [:show-popover {:view :export-community - :community-key %}]) + :on-success #(re-frame/dispatch [:show-popover + {:view :export-community + :community-key %}]) :on-error #(do (log/error "failed to export community" community-id %) (re-frame/dispatch [::failed-to-export %]))}]}) @@ -168,10 +179,13 @@ :js-response true :on-success #(re-frame/dispatch [::left %]) :on-error #(do - (log/error "failed to leave community" community-id %) + (log/error "failed to leave community" + community-id + %) (re-frame/dispatch [::failed-to-leave %]))}]})) -(fx/defn fetch [_] +(fx/defn fetch + [_] {::json-rpc/call [{:method "wakuext_communities" :params [] :on-success #(re-frame/dispatch [::fetched %]) @@ -271,7 +285,7 @@ (fx/defn create-channel {:events [::create-channel-confirmation-pressed]} [{:keys [db] :as cofx}] - (let [community-id (fetch-community-id-input cofx) + (let [community-id (fetch-community-id-input cofx) {:keys [name description color emoji]} (get db :communities/create-channel)] {::json-rpc/call [{:method "wakuext_createCommunityChat" :params [community-id @@ -279,7 +293,8 @@ :description description :color color :emoji emoji} - :permissions {:access constants/community-channel-access-no-membership}}] + :permissions {:access + constants/community-channel-access-no-membership}}] :js-response true :on-success #(re-frame/dispatch [::community-channel-created %]) :on-error #(do @@ -288,7 +303,8 @@ (def community-chat-id-length 68) -(defn to-community-chat-id [chat-id] +(defn to-community-chat-id + [chat-id] (subs chat-id community-chat-id-length)) (fx/defn edit-channel @@ -305,24 +321,29 @@ :emoji emoji} :category_id category-id :position position - :permissions {:access constants/community-channel-access-no-membership}}] + :permissions {:access + constants/community-channel-access-no-membership}}] :js-response true :on-success #(re-frame/dispatch [::community-channel-edited %]) :on-error #(do (log/error "failed to edit community channel" %) (re-frame/dispatch [::failed-to-edit-community-channel %]))}]})) -(defn require-membership? [permissions] +(defn require-membership? + [permissions] (not= constants/community-no-membership-access (:access permissions))) -(defn can-post? [community _ local-chat-id] +(defn can-post? + [community _ local-chat-id] (let [chat-id (to-community-chat-id local-chat-id)] (get-in community [:chats chat-id :can-post?]))) -(fx/defn reset-community-id-input [{:keys [db]} id] +(fx/defn reset-community-id-input + [{:keys [db]} id] {:db (assoc db :communities/community-id-input id)}) -(fx/defn reset-channel-info [{:keys [db]}] +(fx/defn reset-channel-info + [{:keys [db]}] {:db (assoc db :communities/create-channel {})}) (fx/defn invite-people-pressed @@ -347,7 +368,8 @@ (fx/merge cofx (reset-community-id-input id) (reset-channel-info) - (>evt [::create-channel-fields (rand-nth emoji-thumbnail-styles/emoji-picker-default-thumbnails)]) + (>evt [::create-channel-fields + (rand-nth emoji-thumbnail-styles/emoji-picker-default-thumbnails)]) (navigation/open-modal :create-community-channel {:community-id id}))) (fx/defn edit-channel-pressed @@ -357,14 +379,16 @@ (rand-nth emoji-thumbnail-styles/emoji-picker-default-thumbnails) {:color color :emoji emoji})] (fx/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})} + {: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)))) (fx/defn community-created @@ -392,15 +416,17 @@ {:events [::open-edit-community]} [{:keys [db] :as cofx} id] (let [{:keys [name description images permissions color]} (get-in db [:communities id]) - {:keys [access]} permissions] + {:keys [access]} permissions] (fx/merge cofx - {:db (assoc db :communities/create {:id id - :name name - :description description - :image (get-in images [:large :uri]) - :membership access - :color color - :editing? true})} + {:db (assoc db + :communities/create + {:id id + :name name + :description description + :image (get-in images [:large :uri]) + :membership access + :color color + :editing? true})} (navigation/navigate-to :community-edit nil)))) (fx/defn community-imported @@ -467,7 +493,10 @@ :user public-key}] :js-response true :on-success #(re-frame/dispatch [::member-banned %]) - :on-error #(log/error "failed to ban user from community" community-id public-key %)}]}) + :on-error #(log/error "failed to ban user from community" + community-id + public-key + %)}]}) (fx/defn member-kicked {:events [::member-kicked]} @@ -483,7 +512,10 @@ :params [community-id public-key] :js-response true :on-success #(re-frame/dispatch [::member-kicked %]) - :on-error #(log/error "failed to remove user from community" community-id public-key %)}]}) + :on-error #(log/error "failed to remove user from community" + community-id + public-key + %)}]}) (fx/defn delete-community {:events [::delete-community]} @@ -493,7 +525,9 @@ (fx/defn requests-to-join-fetched {:events [::requests-to-join-fetched]} [{:keys [db]} community-id requests] - {:db (assoc-in db [:communities/requests-to-join community-id] (<-requests-to-join-community-rpc requests))}) + {:db (assoc-in db + [:communities/requests-to-join community-id] + (<-requests-to-join-community-rpc requests))}) (fx/defn fetch-requests-to-join {:events [::fetch-requests-to-join]} @@ -503,7 +537,8 @@ :on-success #(re-frame/dispatch [::requests-to-join-fetched community-id %]) :on-error #(log/error "failed to fetch requests-to-join" community-id %)}]}) -(defn fetch-requests-to-join! [community-id] +(defn fetch-requests-to-join! + [community-id] (re-frame/dispatch [::fetch-requests-to-join community-id])) (fx/defn request-to-join-accepted @@ -526,8 +561,12 @@ {::json-rpc/call [{:method "wakuext_acceptRequestToJoinCommunity" :params [{:id request-id}] :js-response true - :on-success #(re-frame/dispatch [::request-to-join-accepted community-id request-id %]) - :on-error #(log/error "failed to accept requests-to-join" community-id request-id %)}]}) + :on-success #(re-frame/dispatch [::request-to-join-accepted community-id request-id + %]) + :on-error #(log/error "failed to accept requests-to-join" + community-id + request-id + %)}]}) (fx/defn decline-request-to-join-pressed {:events [:communities.ui/decline-request-to-join-pressed]} @@ -535,8 +574,11 @@ {::json-rpc/call [{:method "wakuext_declineRequestToJoinCommunity" :params [{:id request-id}] :js-response true - :on-success #(re-frame/dispatch [::request-to-join-declined community-id request-id %]) - :on-error #(log/error "failed to decline requests-to-join" community-id request-id)}]}) + :on-success #(re-frame/dispatch [::request-to-join-declined community-id request-id + %]) + :on-error #(log/error "failed to decline requests-to-join" + community-id + request-id)}]}) (fx/defn switch-communities-enabled {:events [:multiaccounts.ui/switch-communities-enabled]} @@ -561,8 +603,9 @@ {:events [:remove-chat-from-community-category]} [{:keys [db]} community-id id categoryID] (let [category (get-in db [:communities community-id :categories categoryID]) - category-chats (map :id (filter #(and (= (:categoryID %) categoryID) (not= id (:id %))) - (vals (get-in db [:communities community-id :chats]))))] + category-chats (map :id + (filter #(and (= (:categoryID %) categoryID) (not= id (:id %))) + (vals (get-in db [:communities community-id :chats]))))] {::json-rpc/call [{:method "wakuext_editCommunityCategory" :params [{:communityId community-id :categoryId categoryID @@ -632,7 +675,8 @@ :on-success #(re-frame/dispatch [:sanitize-messages-and-process-response %]) :on-error #(log/error "failed to reorder community category" %)}]}) -(defn category-hash [public-key community-id category-id] +(defn category-hash + [public-key community-id category-id] (hash (str public-key community-id category-id))) (fx/defn store-category-state @@ -652,7 +696,8 @@ state (get states hash) category-updated (assoc category :state state)] (assoc acc category-id category-updated))) - {} categories-old)] + {} + categories-old)] {:db (update-in db [:communities community-id :categories] merge categories)})) (fx/defn load-category-states @@ -669,7 +714,8 @@ (-> acc (assoc-in [:hashes category-id] hash) (update :keys #(conj % hash))))) - {} categories)] + {} + categories)] {::async-storage/get {:keys keys :cb #(re-frame/dispatch [::category-states-loaded community-id hashes %])}})) @@ -710,8 +756,9 @@ :user public-key :role role-id}]} :on-success #(re-frame/dispatch [:community.member/role-updated %]) - :on-error #(log/error "failed to remove role from member" - {:error % - :community-id community-id - :public-key public-key - :role-id role-id})]}) + :on-error + #(log/error "failed to remove role from member" + {:error % + :community-id community-id + :public-key public-key + :role-id role-id})]}) diff --git a/src/status_im/constants.cljs b/src/status_im/constants.cljs index 20a5723d42..ce751e2e81 100644 --- a/src/status_im/constants.cljs +++ b/src/status_im/constants.cljs @@ -30,8 +30,8 @@ (def ^:const contact-verification-status-untrustworthy 6) (def ^:const emoji-reaction-love 1) -(def ^:const emoji-reaction-thumbs-up 2) -(def ^:const emoji-reaction-thumbs-down 3) +(def ^:const emoji-reaction-thumbs-up 2) +(def ^:const emoji-reaction-thumbs-down 3) (def ^:const emoji-reaction-laugh 4) (def ^:const emoji-reaction-sad 5) (def ^:const emoji-reaction-angry 6) @@ -50,24 +50,26 @@ (def request-to-join-pending-state 1) -(def reactions-old {emoji-reaction-love (:love resources/reactions-old) - emoji-reaction-thumbs-up (:thumbs-up resources/reactions-old) - emoji-reaction-thumbs-down (:thumbs-down resources/reactions-old) - emoji-reaction-laugh (:laugh resources/reactions-old) - emoji-reaction-sad (:sad resources/reactions-old) - emoji-reaction-angry (:angry resources/reactions-old)}) +(def reactions-old + {emoji-reaction-love (:love resources/reactions-old) + emoji-reaction-thumbs-up (:thumbs-up resources/reactions-old) + emoji-reaction-thumbs-down (:thumbs-down resources/reactions-old) + emoji-reaction-laugh (:laugh resources/reactions-old) + emoji-reaction-sad (:sad resources/reactions-old) + emoji-reaction-angry (:angry resources/reactions-old)}) -(def reactions {emoji-reaction-love :i/love - emoji-reaction-thumbs-up :i/thumbs-up - emoji-reaction-thumbs-down :i/thumbs-down - emoji-reaction-laugh :i/laugh - emoji-reaction-sad :i/sad - emoji-reaction-angry :i/angry}) +(def reactions + {emoji-reaction-love :i/love + emoji-reaction-thumbs-up :i/thumbs-up + emoji-reaction-thumbs-down :i/thumbs-down + emoji-reaction-laugh :i/laugh + emoji-reaction-sad :i/sad + emoji-reaction-angry :i/angry}) (def ^:const invitation-state-unknown 0) (def ^:const invitation-state-requested 1) -(def ^:const invitation-state-rejected 2) -(def ^:const invitation-state-approved 3) +(def ^:const invitation-state-rejected 2) +(def ^:const invitation-state-approved 3) (def ^:const invitation-state-granted 4) (def ^:const invitation-state-removed 5) @@ -102,10 +104,10 @@ (def ^:const mailserver-password "status-offline-inbox") (def ^:const send-transaction-failed-parse-response 1) -(def ^:const send-transaction-failed-parse-params 2) -(def ^:const send-transaction-no-account-selected 3) -(def ^:const send-transaction-invalid-tx-sender 4) -(def ^:const send-transaction-err-decrypt 5) +(def ^:const send-transaction-failed-parse-params 2) +(def ^:const send-transaction-no-account-selected 3) +(def ^:const send-transaction-invalid-tx-sender 4) +(def ^:const send-transaction-err-decrypt 5) (def ^:const web3-send-transaction "eth_sendTransaction") (def ^:const web3-personal-sign "personal_sign") @@ -147,7 +149,8 @@ (def ^:const method-id-approve "0x095ea7b3") (def ^:const method-id-approve-and-call "0xcae9ca51") -(def regx-emoji #"^((?:[\u261D\u26F9\u270A-\u270D]|\uD83C[\uDF85\uDFC2-\uDFC4\uDFC7\uDFCA-\uDFCC]|\uD83D[\uDC42\uDC43\uDC46-\uDC50\uDC66-\uDC69\uDC6E\uDC70-\uDC78\uDC7C\uDC81-\uDC83\uDC85-\uDC87\uDCAA\uDD74\uDD75\uDD7A\uDD90\uDD95\uDD96\uDE45-\uDE47\uDE4B-\uDE4F\uDEA3\uDEB4-\uDEB6\uDEC0\uDECC]|\uD83E[\uDD18-\uDD1C\uDD1E\uDD1F\uDD26\uDD30-\uDD39\uDD3D\uDD3E\uDDD1-\uDDDD])(?:\uD83C[\uDFFB-\uDFFF])?|(?:[\u231A\u231B\u23E9-\u23EC\u23F0\u23F3\u25FD\u25FE\u2614\u2615\u2648-\u2653\u267F\u2693\u26A1\u26AA\u26AB\u26BD\u26BE\u26C4\u26C5\u26CE\u26D4\u26EA\u26F2\u26F3\u26F5\u26FA\u26FD\u2705\u270A\u270B\u2728\u274C\u274E\u2753-\u2755\u2757\u2795-\u2797\u27B0\u27BF\u2B1B\u2B1C\u2B50\u2B55]|\uD83C[\uDC04\uDCCF\uDD8E\uDD91-\uDD9A\uDDE6-\uDDFF\uDE01\uDE1A\uDE2F\uDE32-\uDE36\uDE38-\uDE3A\uDE50\uDE51\uDF00-\uDF20\uDF2D-\uDF35\uDF37-\uDF7C\uDF7E-\uDF93\uDFA0-\uDFCA\uDFCF-\uDFD3\uDFE0-\uDFF0\uDFF4\uDFF8-\uDFFF]|\uD83D[\uDC00-\uDC3E\uDC40\uDC42-\uDCFC\uDCFF-\uDD3D\uDD4B-\uDD4E\uDD50-\uDD67\uDD7A\uDD95\uDD96\uDDA4\uDDFB-\uDE4F\uDE80-\uDEC5\uDECC\uDED0-\uDED2\uDEEB\uDEEC\uDEF4-\uDEF8]|\uD83E[\uDD10-\uDD3A\uDD3C-\uDD3E\uDD40-\uDD45\uDD47-\uDD4C\uDD50-\uDD6B\uDD80-\uDD97\uDDC0\uDDD0-\uDDE6])|(?:[#\*0-9\xA9\xAE\u203C\u2049\u2122\u2139\u2194-\u2199\u21A9\u21AA\u231A\u231B\u2328\u23CF\u23E9-\u23F3\u23F8-\u23FA\u24C2\u25AA\u25AB\u25B6\u25C0\u25FB-\u25FE\u2600-\u2604\u260E\u2611\u2614\u2615\u2618\u261D\u2620\u2622\u2623\u2626\u262A\u262E\u262F\u2638-\u263A\u2640\u2642\u2648-\u2653\u2660\u2663\u2665\u2666\u2668\u267B\u267F\u2692-\u2697\u2699\u269B\u269C\u26A0\u26A1\u26AA\u26AB\u26B0\u26B1\u26BD\u26BE\u26C4\u26C5\u26C8\u26CE\u26CF\u26D1\u26D3\u26D4\u26E9\u26EA\u26F0-\u26F5\u26F7-\u26FA\u26FD\u2702\u2705\u2708-\u270D\u270F\u2712\u2714\u2716\u271D\u2721\u2728\u2733\u2734\u2744\u2747\u274C\u274E\u2753-\u2755\u2757\u2763\u2764\u2795-\u2797\u27A1\u27B0\u27BF\u2934\u2935\u2B05-\u2B07\u2B1B\u2B1C\u2B50\u2B55\u3030\u303D\u3297\u3299]|\uD83C[\uDC04\uDCCF\uDD70\uDD71\uDD7E\uDD7F\uDD8E\uDD91-\uDD9A\uDDE6-\uDDFF\uDE01\uDE02\uDE1A\uDE2F\uDE32-\uDE3A\uDE50\uDE51\uDF00-\uDF21\uDF24-\uDF93\uDF96\uDF97\uDF99-\uDF9B\uDF9E-\uDFF0\uDFF3-\uDFF5\uDFF7-\uDFFF]|\uD83D[\uDC00-\uDCFD\uDCFF-\uDD3D\uDD49-\uDD4E\uDD50-\uDD67\uDD6F\uDD70\uDD73-\uDD7A\uDD87\uDD8A-\uDD8D\uDD90\uDD95\uDD96\uDDA4\uDDA5\uDDA8\uDDB1\uDDB2\uDDBC\uDDC2-\uDDC4\uDDD1-\uDDD3\uDDDC-\uDDDE\uDDE1\uDDE3\uDDE8\uDDEF\uDDF3\uDDFA-\uDE4F\uDE80-\uDEC5\uDECB-\uDED2\uDEE0-\uDEE5\uDEE9\uDEEB\uDEEC\uDEF0\uDEF3-\uDEF8]|\uD83E[\uDD10-\uDD3A\uDD3C-\uDD3E\uDD40-\uDD45\uDD47-\uDD4C\uDD50-\uDD6B\uDD80-\uDD97\uDDC0\uDDD0-\uDDE6])\uFE0F|[\t-\r \xA0\u1680\u2000-\u200A\u2028\u2029\u202F\u205F\u3000\uFEFF])+$") +(def regx-emoji + #"^((?:[\u261D\u26F9\u270A-\u270D]|\uD83C[\uDF85\uDFC2-\uDFC4\uDFC7\uDFCA-\uDFCC]|\uD83D[\uDC42\uDC43\uDC46-\uDC50\uDC66-\uDC69\uDC6E\uDC70-\uDC78\uDC7C\uDC81-\uDC83\uDC85-\uDC87\uDCAA\uDD74\uDD75\uDD7A\uDD90\uDD95\uDD96\uDE45-\uDE47\uDE4B-\uDE4F\uDEA3\uDEB4-\uDEB6\uDEC0\uDECC]|\uD83E[\uDD18-\uDD1C\uDD1E\uDD1F\uDD26\uDD30-\uDD39\uDD3D\uDD3E\uDDD1-\uDDDD])(?:\uD83C[\uDFFB-\uDFFF])?|(?:[\u231A\u231B\u23E9-\u23EC\u23F0\u23F3\u25FD\u25FE\u2614\u2615\u2648-\u2653\u267F\u2693\u26A1\u26AA\u26AB\u26BD\u26BE\u26C4\u26C5\u26CE\u26D4\u26EA\u26F2\u26F3\u26F5\u26FA\u26FD\u2705\u270A\u270B\u2728\u274C\u274E\u2753-\u2755\u2757\u2795-\u2797\u27B0\u27BF\u2B1B\u2B1C\u2B50\u2B55]|\uD83C[\uDC04\uDCCF\uDD8E\uDD91-\uDD9A\uDDE6-\uDDFF\uDE01\uDE1A\uDE2F\uDE32-\uDE36\uDE38-\uDE3A\uDE50\uDE51\uDF00-\uDF20\uDF2D-\uDF35\uDF37-\uDF7C\uDF7E-\uDF93\uDFA0-\uDFCA\uDFCF-\uDFD3\uDFE0-\uDFF0\uDFF4\uDFF8-\uDFFF]|\uD83D[\uDC00-\uDC3E\uDC40\uDC42-\uDCFC\uDCFF-\uDD3D\uDD4B-\uDD4E\uDD50-\uDD67\uDD7A\uDD95\uDD96\uDDA4\uDDFB-\uDE4F\uDE80-\uDEC5\uDECC\uDED0-\uDED2\uDEEB\uDEEC\uDEF4-\uDEF8]|\uD83E[\uDD10-\uDD3A\uDD3C-\uDD3E\uDD40-\uDD45\uDD47-\uDD4C\uDD50-\uDD6B\uDD80-\uDD97\uDDC0\uDDD0-\uDDE6])|(?:[#\*0-9\xA9\xAE\u203C\u2049\u2122\u2139\u2194-\u2199\u21A9\u21AA\u231A\u231B\u2328\u23CF\u23E9-\u23F3\u23F8-\u23FA\u24C2\u25AA\u25AB\u25B6\u25C0\u25FB-\u25FE\u2600-\u2604\u260E\u2611\u2614\u2615\u2618\u261D\u2620\u2622\u2623\u2626\u262A\u262E\u262F\u2638-\u263A\u2640\u2642\u2648-\u2653\u2660\u2663\u2665\u2666\u2668\u267B\u267F\u2692-\u2697\u2699\u269B\u269C\u26A0\u26A1\u26AA\u26AB\u26B0\u26B1\u26BD\u26BE\u26C4\u26C5\u26C8\u26CE\u26CF\u26D1\u26D3\u26D4\u26E9\u26EA\u26F0-\u26F5\u26F7-\u26FA\u26FD\u2702\u2705\u2708-\u270D\u270F\u2712\u2714\u2716\u271D\u2721\u2728\u2733\u2734\u2744\u2747\u274C\u274E\u2753-\u2755\u2757\u2763\u2764\u2795-\u2797\u27A1\u27B0\u27BF\u2934\u2935\u2B05-\u2B07\u2B1B\u2B1C\u2B50\u2B55\u3030\u303D\u3297\u3299]|\uD83C[\uDC04\uDCCF\uDD70\uDD71\uDD7E\uDD7F\uDD8E\uDD91-\uDD9A\uDDE6-\uDDFF\uDE01\uDE02\uDE1A\uDE2F\uDE32-\uDE3A\uDE50\uDE51\uDF00-\uDF21\uDF24-\uDF93\uDF96\uDF97\uDF99-\uDF9B\uDF9E-\uDFF0\uDFF3-\uDFF5\uDFF7-\uDFFF]|\uD83D[\uDC00-\uDCFD\uDCFF-\uDD3D\uDD49-\uDD4E\uDD50-\uDD67\uDD6F\uDD70\uDD73-\uDD7A\uDD87\uDD8A-\uDD8D\uDD90\uDD95\uDD96\uDDA4\uDDA5\uDDA8\uDDB1\uDDB2\uDDBC\uDDC2-\uDDC4\uDDD1-\uDDD3\uDDDC-\uDDDE\uDDE1\uDDE3\uDDE8\uDDEF\uDDF3\uDDFA-\uDE4F\uDE80-\uDEC5\uDECB-\uDED2\uDEE0-\uDEE5\uDEE9\uDEEB\uDEEC\uDEF0\uDEF3-\uDEF8]|\uD83E[\uDD10-\uDD3A\uDD3C-\uDD3E\uDD40-\uDD45\uDD47-\uDD4C\uDD50-\uDD6B\uDD80-\uDD97\uDDC0\uDDD0-\uDDE6])\uFE0F|[\t-\r \xA0\u1680\u2000-\u200A\u2028\u2029\u202F\u205F\u3000\uFEFF])+$") (def regx-bold #"\*[^*]+\*") (def regx-italic #"~[^~]+~") (def regx-backquote #"`[^`]+`") @@ -171,7 +174,8 @@ (def ^:const status-community-id "0x039b2da47552aa117a96ea8f1d4d108ba66637c7517a3c94a57b99dbb8a002eda2") -(def ^:const timeline-chat-id "@timeline70bd746ddcc12beb96b2c9d572d0784ab137ffc774f5383e50585a932080b57cca0484b259e61cecbaa33a4c98a300a") +(def ^:const timeline-chat-id + "@timeline70bd746ddcc12beb96b2c9d572d0784ab137ffc774f5383e50585a932080b57cca0484b259e61cecbaa33a4c98a300a") (def ^:const two-mins (* 2 60)) (def ^:const one-day (* 60 60 24)) diff --git a/src/status_im/contact/block.cljs b/src/status_im/contact/block.cljs index 9dbf933c60..ccee28b1dd 100644 --- a/src/status_im/contact/block.cljs +++ b/src/status_im/contact/block.cljs @@ -4,10 +4,10 @@ [status-im.contact.db :as contact.db] [status-im.data-store.chats :as chats-store] [status-im.data-store.contacts :as contacts-store] - [status-im2.navigation.events :as navigation] + [status-im.utils.fx :as fx] [status-im.utils.types :as types] [status-im2.contexts.activity-center.events :as activity-center] - [status-im.utils.fx :as fx])) + [status-im2.navigation.events :as navigation])) (fx/defn clean-up-chat [{:keys [db] :as cofx} @@ -21,30 +21,32 @@ (when (= from public-key) message-id)) (get-in db [:messages chat-id])) - db (-> db - ;; remove messages - (update-in [:messages chat-id] - #(apply dissoc % removed-messages-ids)) - (update-in [:chats chat-id] - assoc - :unviewed-messages-count unviewed-messages-count - :unviewed-mentions-count unviewed-mentions-count - :last-message last-message))] - {:db (assoc-in db [:message-lists chat-id] - (message-list/add-many nil (vals (get-in db [:messages chat-id]))))})) + db (-> db + ;; remove messages + (update-in [:messages chat-id] + #(apply dissoc % removed-messages-ids)) + (update-in [:chats chat-id] + assoc + :unviewed-messages-count unviewed-messages-count + :unviewed-mentions-count unviewed-mentions-count + :last-message last-message))] + {:db (assoc-in db + [:message-lists chat-id] + (message-list/add-many nil (vals (get-in db [:messages chat-id]))))})) (fx/defn contact-blocked {:events [::contact-blocked]} [{:keys [db] :as cofx} {:keys [public-key]} chats] (let [fxs (when chats (map #(->> (chats-store/<-rpc %) - (clean-up-chat public-key)) (types/js->clj chats)))] + (clean-up-chat public-key)) + (types/js->clj chats)))] (apply fx/merge cofx - {:db (-> db - (update :chats dissoc public-key) - (update :chats-home-list disj public-key) - (assoc-in [:contacts/contacts public-key :added] false)) + {:db (-> db + (update :chats dissoc public-key) + (update :chats-home-list disj public-key) + (assoc-in [:contacts/contacts public-key :added] false)) :clear-message-notifications [[public-key] (get-in db [:multiaccount :remote-push-notifications-enabled?])]} (activity-center/notifications-fetch-unread-count) @@ -53,11 +55,11 @@ (fx/defn block-contact {:events [:contact.ui/block-contact-confirmed]} [{:keys [db] :as cofx} public-key] - (let [contact (-> (contact.db/public-key->contact - (:contacts/contacts db) - public-key) - (assoc :blocked true - :added false)) + (let [contact (-> (contact.db/public-key->contact + (:contacts/contacts db) + public-key) + (assoc :blocked true + :added false)) from-one-to-one-chat? (not (get-in db [:chats (:current-chat-id db) :group-chat]))] (fx/merge cofx {:db (-> db @@ -66,9 +68,10 @@ ;; update the contact in contacts list (assoc-in [:contacts/contacts public-key] contact))} (contacts-store/block - public-key #(do (re-frame/dispatch [::contact-blocked contact (.-chats %)]) - (re-frame/dispatch [:sanitize-messages-and-process-response %]) - (re-frame/dispatch [:hide-popover]))) + public-key + #(do (re-frame/dispatch [::contact-blocked contact (.-chats %)]) + (re-frame/dispatch [:sanitize-messages-and-process-response %]) + (re-frame/dispatch [:hide-popover]))) ;; reset navigation to avoid going back to non existing one to one chat (if from-one-to-one-chat? (navigation/pop-to-root-tab :chat-stack) diff --git a/src/status_im/contact/chat.cljs b/src/status_im/contact/chat.cljs index 7b02305cca..4672eed3e8 100644 --- a/src/status_im/contact/chat.cljs +++ b/src/status_im/contact/chat.cljs @@ -1,9 +1,9 @@ (ns status-im.contact.chat (:require [re-frame.core :as re-frame] - [status-im2.navigation.events :as navigation] - [status-im.utils.fx :as fx] [status-im.chat.models :as chat] - [status-im.contact.core :as contact])) + [status-im.contact.core :as contact] + [status-im.utils.fx :as fx] + [status-im2.navigation.events :as navigation])) (fx/defn send-message-pressed {:events [:contact.ui/send-message-pressed] @@ -24,6 +24,6 @@ (navigation/navigate-back %))))) (fx/defn pinned-messages-pressed - {:events [:contact.ui/pinned-messages-pressed]} + {:events [:contact.ui/pinned-messages-pressed]} [cofx public-key] (chat/navigate-to-user-pinned-messages cofx public-key)) diff --git a/src/status_im/contact/core.cljs b/src/status_im/contact/core.cljs index 0272c778e7..24298a8aa1 100644 --- a/src/status_im/contact/core.cljs +++ b/src/status_im/contact/core.cljs @@ -1,30 +1,32 @@ (ns status-im.contact.core (:require [re-frame.core :as re-frame] + [status-im.constants :as constants] + [status-im.contact.block :as contact.block] [status-im.contact.db :as contact.db] [status-im.data-store.contacts :as contacts-store] [status-im.ethereum.json-rpc :as json-rpc] - [status-im2.navigation.events :as navigation] [status-im.multiaccounts.update.core :as multiaccounts.update] [status-im.utils.fx :as fx] - [taoensso.timbre :as log] - [status-im.constants :as constants] - [status-im.contact.block :as contact.block])) + [status-im2.navigation.events :as navigation] + [taoensso.timbre :as log])) (fx/defn load-contacts {:events [::contacts-loaded]} [{:keys [db] :as cofx} all-contacts] - (let [contacts-list (map #(vector (:public-key %) (if (empty? (:address %)) - (dissoc % :address) - %)) + (let [contacts-list (map #(vector (:public-key %) + (if (empty? (:address %)) + (dissoc % :address) + %)) all-contacts) - contacts (into {} contacts-list)] + contacts (into {} contacts-list)] {:db (cond-> (-> db (update :contacts/contacts #(merge contacts %)) (assoc :contacts/blocked (contact.db/get-blocked-contacts all-contacts))))})) (defn build-contact - [{{:keys [multiaccount] - :contacts/keys [contacts]} :db} public-key] + [{{:keys [multiaccount] + :contacts/keys [contacts]} + :db} public-key] (cond-> (contact.db/public-key->contact contacts public-key) (= public-key (:public-key multiaccount)) (assoc :name (:name multiaccount)))) @@ -34,9 +36,9 @@ (let [events (reduce (fn [acc {:keys [public-key] :as contact}] - (let [added (:added contact) - was-added (contact.db/added? db public-key) - blocked (:blocked contact) + (let [added (:added contact) + was-added (contact.db/added? db public-key) + blocked (:blocked contact) was-blocked (contact.db/blocked? db public-key)] (cond-> acc (and added (not was-added)) @@ -51,7 +53,8 @@ [:activity-center.notifications/fetch-unread-count]] contacts)] (merge - {:db (update db :contacts/contacts + {:db (update db + :contacts/contacts #(reduce (fn [acc {:keys [public-key] :as contact}] (-> acc (update public-key merge contact) @@ -72,8 +75,8 @@ {:events [::send-contact-request]} [{:keys [db] :as cofx} public-key] (let [{:keys [name profile-image]} (own-info db)] - {::json-rpc/call [{:method "wakuext_sendContactUpdate" - :params [public-key name profile-image] + {::json-rpc/call [{:method "wakuext_sendContactUpdate" + :params [public-key name profile-image] :on-success #(log/debug "contact request sent" public-key)}]})) (fx/defn add-contact @@ -94,34 +97,36 @@ "Remove a contact from current account's contact list" {:events [:contact.ui/remove-contact-pressed]} [{:keys [db]} {:keys [public-key]}] - {:db (-> db - (assoc-in [:contacts/contacts public-key :added] false) - (assoc-in [:contacts/contacts public-key :contact-request-state] constants/contact-request-state-none)) - ::json-rpc/call [{:method "wakuext_removeContact" - :params [public-key] + {:db (-> db + (assoc-in [:contacts/contacts public-key :added] false) + (assoc-in [:contacts/contacts public-key :contact-request-state] + constants/contact-request-state-none)) + ::json-rpc/call [{:method "wakuext_removeContact" + :params [public-key] :on-success #(log/debug "contact removed successfully")} - {:method "wakuext_retractContactRequest" - :params [{:contactId public-key}] + {:method "wakuext_retractContactRequest" + :params [{:contactId public-key}] :on-success #(log/debug "contact removed successfully")}] - :dispatch [:offload-messages constants/timeline-chat-id]}) + :dispatch [:offload-messages constants/timeline-chat-id]}) (fx/defn accept-contact-request {:events [:contact-requests.ui/accept-request]} [{:keys [db]} id] - {::json-rpc/call [{:method "wakuext_acceptContactRequest" - :params [{:id id}] + {::json-rpc/call [{:method "wakuext_acceptContactRequest" + :params [{:id id}] :js-response true - :on-success #(re-frame/dispatch [:sanitize-messages-and-process-response %])}]}) + :on-success #(re-frame/dispatch [:sanitize-messages-and-process-response %])}]}) (fx/defn decline-contact-request {:events [:contact-requests.ui/decline-request]} [{:keys [db]} id] - {::json-rpc/call [{:method "wakuext_dismissContactRequest" - :params [{:id id}] + {::json-rpc/call [{:method "wakuext_dismissContactRequest" + :params [{:id id}] :js-response true - :on-success #(re-frame/dispatch [:sanitize-messages-and-process-response %])}]}) + :on-success #(re-frame/dispatch [:sanitize-messages-and-process-response %])}]}) -(fx/defn initialize-contacts [cofx] +(fx/defn initialize-contacts + [cofx] (contacts-store/fetch-contacts-rpc cofx #(re-frame/dispatch [::contacts-loaded %]))) (fx/defn open-contact-toggle-list @@ -130,7 +135,7 @@ (fx/merge cofx {:db (assoc db :group/selected-contacts #{} - :new-chat-name "")} + :new-chat-name "")} (navigation/navigate-to-cofx :contact-toggle-list nil))) (fx/defn update-nickname diff --git a/src/status_im/contact/db.cljs b/src/status_im/contact/db.cljs index 152531ab51..019a4613c6 100644 --- a/src/status_im/contact/db.cljs +++ b/src/status_im/contact/db.cljs @@ -1,26 +1,29 @@ (ns status-im.contact.db (:require [clojure.set :as clojure.set] [clojure.string :as string] + [status-im.constants :as constants] [status-im.ethereum.core :as ethereum] - [status-im.utils.gfycat.core :as gfycat] - [status-im.utils.identicon :as identicon] [status-im.multiaccounts.core :as multiaccounts] - [status-im.constants :as constants])) + [status-im.utils.gfycat.core :as gfycat] + [status-im.utils.identicon :as identicon])) -(defn public-key->new-contact [public-key] +(defn public-key->new-contact + [public-key] (let [alias (gfycat/generate-gfy public-key)] - {:alias alias - :name alias - :identicon (identicon/identicon public-key) - :public-key public-key})) + {:alias alias + :name alias + :identicon (identicon/identicon public-key) + :public-key public-key})) -(defn public-key-and-ens-name->new-contact [public-key ens-name] +(defn public-key-and-ens-name->new-contact + [public-key ens-name] (let [contact (public-key->new-contact public-key)] - (if ens-name (-> contact - (assoc :ens-name ens-name) - (assoc :ens-verified true) - (assoc :name ens-name)) - contact))) + (if ens-name + (-> contact + (assoc :ens-name ens-name) + (assoc :ens-verified true) + (assoc :name ens-name)) + contact))) (defn public-key->contact [contacts public-key] @@ -28,11 +31,13 @@ (or (get contacts public-key) (public-key->new-contact public-key)))) -(defn- contact-by-address [[addr contact] address] +(defn- contact-by-address + [[addr contact] address] (when (ethereum/address= addr address) contact)) -(defn find-contact-by-address [contacts address] +(defn find-contact-by-address + [contacts address] (some #(contact-by-address % address) contacts)) (defn sort-contacts @@ -106,10 +111,11 @@ (cond-> (-> contact (dissoc :ens-verified-at :ens-verification-retries) (assoc :blocked? (:blocked contact) - :active? (active? contact) - :added? added) + :active? (active? contact) + :added? added) (multiaccounts/contact-with-names)) - (and setting (not= public-key own-public-key) + (and setting + (not= public-key own-public-key) (or (= setting constants/profile-pictures-visibility-none) (and (= setting constants/profile-pictures-visibility-contacts-only) (not added)))) @@ -117,10 +123,11 @@ (defn enrich-contacts [contacts profile-pictures-visibility own-public-key] - (reduce-kv (fn [acc public-key contact] - (assoc acc public-key (enrich-contact contact profile-pictures-visibility own-public-key))) - {} - contacts)) + (reduce-kv + (fn [acc public-key contact] + (assoc acc public-key (enrich-contact contact profile-pictures-visibility own-public-key))) + {} + contacts)) (defn get-blocked-contacts [contacts] diff --git a/src/status_im/contact/db_test.cljs b/src/status_im/contact/db_test.cljs index 1b6c640b33..d66f10d79e 100644 --- a/src/status_im/contact/db_test.cljs +++ b/src/status_im/contact/db_test.cljs @@ -1,46 +1,56 @@ (ns status-im.contact.db-test (:require [cljs.test :refer-macros [deftest is testing]] + [status-im.contact.db :as contact.db] [status-im.utils.gfycat.core :as gfycat] - [status-im.utils.identicon :as identicon] - [status-im.contact.db :as contact.db])) + [status-im.utils.identicon :as identicon])) (deftest contacts-subs (testing "get-all-contacts-in-group-chat" (with-redefs [gfycat/generate-gfy (constantly "generated") identicon/identicon (constantly "generated")] - (let [chat-contact-ids #{"0x04fcf40c526b09ff9fb22f4a5dbd08490ef9b64af700870f8a0ba2133f4251d5607ed83cd9047b8c2796576bc83fa0de23a13a4dced07654b8ff137fe744047917" - "0x04985040682b77a32bb4bb58268a0719bd24ca4d07c255153fe1eb2ccd5883669627bd1a092d7cc76e8e4b9104327667b19dcda3ac469f572efabe588c38c1985f" - "0x048a2f8b80c60f89a91b4c1316e56f75b087f446e7b8701ceca06a40142d8efe1f5aa36bd0fee9e248060a8d5207b43ae98bef4617c18c71e66f920f324869c09f"} - admins #{"0x04fcf40c526b09ff9fb22f4a5dbd08490ef9b64af700870f8a0ba2133f4251d5607ed83cd9047b8c2796576bc83fa0de23a13a4dced07654b8ff137fe744047917"} + (let + [chat-contact-ids + #{"0x04fcf40c526b09ff9fb22f4a5dbd08490ef9b64af700870f8a0ba2133f4251d5607ed83cd9047b8c2796576bc83fa0de23a13a4dced07654b8ff137fe744047917" + "0x04985040682b77a32bb4bb58268a0719bd24ca4d07c255153fe1eb2ccd5883669627bd1a092d7cc76e8e4b9104327667b19dcda3ac469f572efabe588c38c1985f" + "0x048a2f8b80c60f89a91b4c1316e56f75b087f446e7b8701ceca06a40142d8efe1f5aa36bd0fee9e248060a8d5207b43ae98bef4617c18c71e66f920f324869c09f"} + admins + #{"0x04fcf40c526b09ff9fb22f4a5dbd08490ef9b64af700870f8a0ba2133f4251d5607ed83cd9047b8c2796576bc83fa0de23a13a4dced07654b8ff137fe744047917"} - contacts {"0x04985040682b77a32bb4bb58268a0719bd24ca4d07c255153fe1eb2ccd5883669627bd1a092d7cc76e8e4b9104327667b19dcda3ac469f572efabe588c38c1985f" - {:last-updated 0, - :name "User B", - :identicon "photo1", - :last-online 0, - :public-key - "0x04985040682b77a32bb4bb58268a0719bd24ca4d07c255153fe1eb2ccd5883669627bd1a092d7cc76e8e4b9104327667b19dcda3ac469f572efabe588c38c1985f"}} - current-multiaccount {:last-updated 0, - :signed-up? true, - :sharing-usage-data? false, - :name "User A", - :identicon "photo2", - :public-key - "0x048a2f8b80c60f89a91b4c1316e56f75b087f446e7b8701ceca06a40142d8efe1f5aa36bd0fee9e248060a8d5207b43ae98bef4617c18c71e66f920f324869c09f"}] - (is (= (contact.db/get-all-contacts-in-group-chat chat-contact-ids - admins - contacts - current-multiaccount) - [{:name "generated" - :identicon "generated" - :alias "generated" - :admin? true - :public-key "0x04fcf40c526b09ff9fb22f4a5dbd08490ef9b64af700870f8a0ba2133f4251d5607ed83cd9047b8c2796576bc83fa0de23a13a4dced07654b8ff137fe744047917"} - {:alias "User A" - :identicon "photo2" - :public-key "0x048a2f8b80c60f89a91b4c1316e56f75b087f446e7b8701ceca06a40142d8efe1f5aa36bd0fee9e248060a8d5207b43ae98bef4617c18c71e66f920f324869c09f"} - {:last-updated 0 - :name "User B" - :identicon "photo1" - :last-online 0 - :public-key "0x04985040682b77a32bb4bb58268a0719bd24ca4d07c255153fe1eb2ccd5883669627bd1a092d7cc76e8e4b9104327667b19dcda3ac469f572efabe588c38c1985f"}])))))) + contacts + {"0x04985040682b77a32bb4bb58268a0719bd24ca4d07c255153fe1eb2ccd5883669627bd1a092d7cc76e8e4b9104327667b19dcda3ac469f572efabe588c38c1985f" + {:last-updated 0 + :name "User B" + :identicon "photo1" + :last-online 0 + :public-key + "0x04985040682b77a32bb4bb58268a0719bd24ca4d07c255153fe1eb2ccd5883669627bd1a092d7cc76e8e4b9104327667b19dcda3ac469f572efabe588c38c1985f"}} + current-multiaccount + {:last-updated 0 + :signed-up? true + :sharing-usage-data? false + :name "User A" + :identicon "photo2" + :public-key + "0x048a2f8b80c60f89a91b4c1316e56f75b087f446e7b8701ceca06a40142d8efe1f5aa36bd0fee9e248060a8d5207b43ae98bef4617c18c71e66f920f324869c09f"}] + (is + (= + (contact.db/get-all-contacts-in-group-chat chat-contact-ids + admins + contacts + current-multiaccount) + [{:name "generated" + :identicon "generated" + :alias "generated" + :admin? true + :public-key + "0x04fcf40c526b09ff9fb22f4a5dbd08490ef9b64af700870f8a0ba2133f4251d5607ed83cd9047b8c2796576bc83fa0de23a13a4dced07654b8ff137fe744047917"} + {:alias "User A" + :identicon "photo2" + :public-key + "0x048a2f8b80c60f89a91b4c1316e56f75b087f446e7b8701ceca06a40142d8efe1f5aa36bd0fee9e248060a8d5207b43ae98bef4617c18c71e66f920f324869c09f"} + {:last-updated 0 + :name "User B" + :identicon "photo1" + :last-online 0 + :public-key + "0x04985040682b77a32bb4bb58268a0719bd24ca4d07c255153fe1eb2ccd5883669627bd1a092d7cc76e8e4b9104327667b19dcda3ac469f572efabe588c38c1985f"}])))))) diff --git a/src/status_im/currency/core.cljs b/src/status_im/currency/core.cljs index da18815bc9..f21fe0017e 100644 --- a/src/status_im/currency/core.cljs +++ b/src/status_im/currency/core.cljs @@ -3,7 +3,8 @@ [status-im.utils.fx :as fx] [status-im.wallet.prices :as prices])) -(defn get-currency [db] +(defn get-currency + [db] (get-in db [:multiaccount :currency] :usd)) (fx/defn set-currency @@ -11,6 +12,7 @@ [{:keys [db] :as cofx} currency] (fx/merge cofx (multiaccounts.update/multiaccount-update - :currency currency + :currency + currency {}) (prices/update-prices))) diff --git a/src/status_im/currency/core_test.cljs b/src/status_im/currency/core_test.cljs index c859d7836d..97a27dc181 100644 --- a/src/status_im/currency/core_test.cljs +++ b/src/status_im/currency/core_test.cljs @@ -8,7 +8,11 @@ (is (= :aud (models/get-currency {:multiaccount {:currency :aud}})))) (deftest set-currency - (let [cofx (models/set-currency {:db {:multiaccount {:not-empty "would throw an error if was empty"}}} :usd)] + (let [cofx (models/set-currency {:db {:multiaccount {:not-empty "would throw an error if was empty"}}} + :usd)] (is (= :usd (get-in cofx [:db :multiaccount :currency])))) - (is (= :jpy (get-in (models/set-currency {:db {:multiaccount {:not-empty "would throw an error if was empty"}}} :jpy) - [:db :multiaccount :currency])))) + (is + (= :jpy + (get-in (models/set-currency {:db {:multiaccount {:not-empty "would throw an error if was empty"}}} + :jpy) + [:db :multiaccount :currency])))) diff --git a/src/status_im/data_store/activities.cljs b/src/status_im/data_store/activities.cljs index b681992dd4..7b12561cad 100644 --- a/src/status_im/data_store/activities.cljs +++ b/src/status_im/data_store/activities.cljs @@ -1,10 +1,11 @@ (ns status-im.data-store.activities (:require [clojure.set :as set] [status-im.constants :as constants] - [status-im2.contexts.activity-center.notification-types :as notification-types] - [status-im.data-store.messages :as messages])) + [status-im.data-store.messages :as messages] + [status-im2.contexts.activity-center.notification-types :as notification-types])) -(defn- rpc->type [{:keys [type name] :as chat}] +(defn- rpc->type + [{:keys [type name] :as chat}] (case type notification-types/reply (assoc chat @@ -18,21 +19,22 @@ notification-types/private-group-chat (assoc chat - :chat-type constants/private-group-chat-type - :chat-name name - :public? false + :chat-type constants/private-group-chat-type + :chat-name name + :public? false :group-chat true) notification-types/one-to-one-chat (assoc chat - :chat-type constants/one-to-one-chat-type - :chat-name name - :public? false + :chat-type constants/one-to-one-chat-type + :chat-name name + :public? false :group-chat false) chat)) -(defn <-rpc [item] +(defn <-rpc + [item] (-> item rpc->type (set/rename-keys {:lastMessage :last-message diff --git a/src/status_im/data_store/activities_test.cljs b/src/status_im/data_store/activities_test.cljs index 22dbf55de8..db60db7c11 100644 --- a/src/status_im/data_store/activities_test.cljs +++ b/src/status_im/data_store/activities_test.cljs @@ -1,8 +1,8 @@ (ns status-im.data-store.activities-test (:require [cljs.test :refer [deftest is testing]] [status-im.constants :as constants] - [status-im2.contexts.activity-center.notification-types :as notification-types] - [status-im.data-store.activities :as store])) + [status-im.data-store.activities :as store] + [status-im2.contexts.activity-center.notification-types :as notification-types])) (def chat-id "0x04c66155") diff --git a/src/status_im/data_store/chats.cljs b/src/status_im/data_store/chats.cljs index 3f097f310e..ed37495f11 100644 --- a/src/status_im/data_store/chats.cljs +++ b/src/status_im/data_store/chats.cljs @@ -1,31 +1,34 @@ (ns status-im.data-store.chats (:require [clojure.set :as clojure.set] + [status-im.constants :as constants] [status-im.data-store.messages :as messages] [status-im.ethereum.json-rpc :as json-rpc] - [status-im.constants :as constants] [status-im.utils.fx :as fx] - [taoensso.timbre :as log] - [status-im.utils.types :as types])) + [status-im.utils.types :as types] + [taoensso.timbre :as log])) -(defn rpc->type [{:keys [chat-type name] :as chat}] +(defn rpc->type + [{:keys [chat-type name] :as chat}] (cond (or (= constants/public-chat-type chat-type) (= constants/profile-chat-type chat-type) - (= constants/timeline-chat-type chat-type)) (assoc chat - :chat-name (str "#" name) - :public? true - :group-chat true - :timeline? (= constants/timeline-chat-type chat-type)) + (= constants/timeline-chat-type chat-type)) + (assoc chat + :chat-name (str "#" name) + :public? true + :group-chat true + :timeline? (= constants/timeline-chat-type chat-type)) (= constants/community-chat-type chat-type) (assoc chat - :chat-name name + :chat-name name :group-chat true) (= constants/private-group-chat-type chat-type) (assoc chat - :chat-name name - :public? false + :chat-name name + :public? false :group-chat true) :else (assoc chat :public? false :group-chat false))) -(defn members-reducer [acc member] +(defn members-reducer + [acc member] (cond-> acc (:admin member) (update :admins conj (:id member)) @@ -34,45 +37,48 @@ :always (update :contacts conj (:id member)))) -(defn- unmarshal-members [{:keys [members chat-type] :as chat}] +(defn- unmarshal-members + [{:keys [members chat-type] :as chat}] (cond - (= constants/public-chat-type chat-type) (assoc chat - :contacts #{} - :admins #{} - :members-joined #{}) + (= constants/public-chat-type chat-type) (assoc chat + :contacts #{} + :admins #{} + :members-joined #{}) (= constants/private-group-chat-type chat-type) (merge chat (reduce members-reducer - {:admins #{} + {:admins #{} :members-joined #{} - :contacts #{}} + :contacts #{}} members)) :else (assoc chat - :contacts #{(:id chat)} - :admins #{} + :contacts #{(:id chat)} + :admins #{} :members-joined #{}))) -(defn <-rpc [chat] +(defn <-rpc + [chat] (-> chat - (clojure.set/rename-keys {:id :chat-id - :communityId :community-id - :syncedFrom :synced-from - :syncedTo :synced-to + (clojure.set/rename-keys {:id :chat-id + :communityId :community-id + :syncedFrom :synced-from + :syncedTo :synced-to :membershipUpdateEvents :membership-update-events - :deletedAtClockValue :deleted-at-clock-value - :chatType :chat-type - :unviewedMessagesCount :unviewed-messages-count - :unviewedMentionsCount :unviewed-mentions-count - :lastMessage :last-message - :lastClockValue :last-clock-value - :invitationAdmin :invitation-admin - :profile :profile-public-key}) + :deletedAtClockValue :deleted-at-clock-value + :chatType :chat-type + :unviewedMessagesCount :unviewed-messages-count + :unviewedMentionsCount :unviewed-mentions-count + :lastMessage :last-message + :lastClockValue :last-clock-value + :invitationAdmin :invitation-admin + :profile :profile-public-key}) rpc->type unmarshal-members (update :last-message #(when % (messages/<-rpc %))) (dissoc :members))) -(defn <-rpc-js [^js chat] +(defn <-rpc-js + [^js chat] (-> {:name (.-name chat) :description (.-description chat) :color (.-color chat) @@ -103,9 +109,10 @@ rpc->type unmarshal-members)) -(fx/defn fetch-chats-rpc [_ {:keys [on-success]}] - {::json-rpc/call [{:method "wakuext_chatsPreview" - :params [] +(fx/defn fetch-chats-rpc + [_ {:keys [on-success]}] + {::json-rpc/call [{:method "wakuext_chatsPreview" + :params [] :js-response true - :on-success #(on-success ^js %) - :on-error #(log/error "failed to fetch chats" 0 -1 %)}]}) + :on-success #(on-success ^js %) + :on-error #(log/error "failed to fetch chats" 0 -1 %)}]}) diff --git a/src/status_im/data_store/chats_test.cljs b/src/status_im/data_store/chats_test.cljs index 60ba8021c4..75bdb1ce1b 100644 --- a/src/status_im/data_store/chats_test.cljs +++ b/src/status_im/data_store/chats_test.cljs @@ -3,40 +3,40 @@ [status-im.data-store.chats :as chats])) (deftest normalize-chat-test - (let [chat {:id "chat-id" - :color "color" - :name "name" - :chatType 3 - :members [{:id "a" - :admin true - :joined true} - {:id "b" - :admin true - :joined false} - {:id "c" - :admin false - :joined true} - {:id "d" - :admin false - :joined false}] - :lastClockValue 10 - :membershipUpdateEvents :events - :unviewedMessagesCount 2 - :timestamp 2} - expected-chat {:public? false - :group-chat true - :color "color" - :chat-name "name" - :contacts #{"a" "b" "c" "d"} - :chat-type 3 - :last-clock-value 10 - :last-message nil - :admins #{"a" "b"} - :members-joined #{"a" "c"} - :name "name" + (let [chat {:id "chat-id" + :color "color" + :name "name" + :chatType 3 + :members [{:id "a" + :admin true + :joined true} + {:id "b" + :admin true + :joined false} + {:id "c" + :admin false + :joined true} + {:id "d" + :admin false + :joined false}] + :lastClockValue 10 + :membershipUpdateEvents :events + :unviewedMessagesCount 2 + :timestamp 2} + expected-chat {:public? false + :group-chat true + :color "color" + :chat-name "name" + :contacts #{"a" "b" "c" "d"} + :chat-type 3 + :last-clock-value 10 + :last-message nil + :admins #{"a" "b"} + :members-joined #{"a" "c"} + :name "name" :membership-update-events :events - :unviewed-messages-count 2 - :chat-id "chat-id" - :timestamp 2}] + :unviewed-messages-count 2 + :chat-id "chat-id" + :timestamp 2}] (testing "from-rpc" (is (= expected-chat (chats/<-rpc chat)))))) diff --git a/src/status_im/data_store/contacts.cljs b/src/status_im/data_store/contacts.cljs index 9f54982716..7862395eb8 100644 --- a/src/status_im/data_store/contacts.cljs +++ b/src/status_im/data_store/contacts.cljs @@ -4,7 +4,8 @@ [status-im.utils.fx :as fx] [taoensso.timbre :as log])) -(defn <-rpc [contact] +(defn <-rpc + [contact] (-> contact (clojure.set/rename-keys {:id :public-key @@ -17,48 +18,54 @@ :lastENSClockValue :last-ens-clock-value :lastUpdated :last-updated :localNickname :nickname}) - (assoc :mutual? (and (:added contact) - (:hasAddedUs contact))))) + (assoc :mutual? + (and (:added contact) + (:hasAddedUs contact))))) (fx/defn fetch-contacts-rpc [_ on-success] - {::json-rpc/call [{:method "wakuext_contacts" - :params [] + {::json-rpc/call [{:method "wakuext_contacts" + :params [] :on-success #(on-success (map <-rpc %)) - :on-error #(log/error "failed to fetch contacts" %)}]}) + :on-error #(log/error "failed to fetch contacts" %)}]}) (fx/defn add [_ public-key nickname ens-name on-success] - {::json-rpc/call [{:method "wakuext_addContact" - :params [{:id public-key :nickname nickname :ensName ens-name}] + {::json-rpc/call [{:method "wakuext_addContact" + :params [{:id public-key :nickname nickname :ensName ens-name}] :js-response true - :on-success #(do - (log/info "saved contact" public-key "successfuly") - (when on-success - (on-success %))) - :on-error #(log/error "failed to add contact" public-key %)}]}) + :on-success #(do + (log/info "saved contact" public-key "successfuly") + (when on-success + (on-success %))) + :on-error #(log/error "failed to add contact" public-key %)}]}) (fx/defn set-nickname [_ public-key nickname on-success] - {::json-rpc/call [{:method "wakuext_setContactLocalNickname" - :params [{:id public-key :nickname nickname}] + {::json-rpc/call [{:method "wakuext_setContactLocalNickname" + :params [{:id public-key :nickname nickname}] :js-response true - :on-success #(do - (log/debug "set contact nickname" public-key "successfuly" nickname) - (when on-success - (on-success %))) - :on-error #(log/error "failed to set contact nickname " public-key nickname %)}]}) + :on-success #(do + (log/debug "set contact nickname" public-key "successfuly" nickname) + (when on-success + (on-success %))) + :on-error #(log/error "failed to set contact nickname " + public-key + nickname + %)}]}) -(fx/defn block [_ contact-id on-success] - {::json-rpc/call [{:method "wakuext_blockContact" - :params [contact-id] +(fx/defn block + [_ contact-id on-success] + {::json-rpc/call [{:method "wakuext_blockContact" + :params [contact-id] :js-response true - :on-success on-success - :on-error #(log/error "failed to block contact" % contact-id)}]}) + :on-success on-success + :on-error #(log/error "failed to block contact" % contact-id)}]}) -(fx/defn unblock [_ contact-id on-success] - {::json-rpc/call [{:method "wakuext_unblockContact" - :params [contact-id] - :on-success on-success +(fx/defn unblock + [_ contact-id on-success] + {::json-rpc/call [{:method "wakuext_unblockContact" + :params [contact-id] + :on-success on-success :js-response true - :on-error #(log/error "failed to unblock contact" % contact-id)}]}) + :on-error #(log/error "failed to unblock contact" % contact-id)}]}) diff --git a/src/status_im/data_store/contacts_test.cljs b/src/status_im/data_store/contacts_test.cljs index 8ad893cf97..d837d87788 100644 --- a/src/status_im/data_store/contacts_test.cljs +++ b/src/status_im/data_store/contacts_test.cljs @@ -3,12 +3,12 @@ [status-im.data-store.contacts :as c])) (deftest contact<-rpc - (let [contact {:id "pk" - :address "address" - :name "name" - :displayName "display-name" - :identicon "identicon" - :lastUpdated 1} + (let [contact {:id "pk" + :address "address" + :name "name" + :displayName "display-name" + :identicon "identicon" + :lastUpdated 1} expected-contact {:public-key "pk" :address "address" :display-name "display-name" diff --git a/src/status_im/data_store/invitations.cljs b/src/status_im/data_store/invitations.cljs index b963e47427..7966dbfe10 100644 --- a/src/status_im/data_store/invitations.cljs +++ b/src/status_im/data_store/invitations.cljs @@ -1,7 +1,8 @@ (ns status-im.data-store.invitations (:require clojure.set)) -(defn <-rpc [message] +(defn <-rpc + [message] (-> message (clojure.set/rename-keys {:chatId :chat-id :introductionMessage :introduction-message diff --git a/src/status_im/data_store/messages.cljs b/src/status_im/data_store/messages.cljs index 334f9f0172..b76c0cdb7c 100644 --- a/src/status_im/data_store/messages.cljs +++ b/src/status_im/data_store/messages.cljs @@ -4,100 +4,115 @@ [status-im.utils.fx :as fx] [taoensso.timbre :as log])) -(defn ->rpc [{:keys [content] :as message}] +(defn ->rpc + [{:keys [content] :as message}] (cond-> message content - (assoc :text (:text content) + (assoc :text (:text content) :sticker (:sticker content)) :always - (clojure.set/rename-keys {:chat-id :chat_id + (clojure.set/rename-keys {:chat-id :chat_id :whisper-timestamp :whisperTimestamp - :community-id :communityId - :clock-value :clock}))) + :community-id :communityId + :clock-value :clock}))) -(defn <-rpc [message] +(defn <-rpc + [message] (-> message - (clojure.set/rename-keys {:id :message-id - :whisperTimestamp :whisper-timestamp - :editedAt :edited-at + (clojure.set/rename-keys {:id :message-id + :whisperTimestamp :whisper-timestamp + :editedAt :edited-at :contactVerificationState :contact-verification-state - :contactRequestState :contact-request-state - :commandParameters :command-parameters - :gapParameters :gap-parameters - :messageType :message-type - :localChatId :chat-id - :communityId :community-id - :contentType :content-type - :clock :clock-value - :quotedMessage :quoted-message - :outgoingStatus :outgoing-status - :audioDurationMs :audio-duration-ms - :deleted :deleted? - :deletedForMe :deleted-for-me? - :new :new?}) + :contactRequestState :contact-request-state + :commandParameters :command-parameters + :gapParameters :gap-parameters + :messageType :message-type + :localChatId :chat-id + :communityId :community-id + :contentType :content-type + :clock :clock-value + :quotedMessage :quoted-message + :outgoingStatus :outgoing-status + :audioDurationMs :audio-duration-ms + :deleted :deleted? + :deletedForMe :deleted-for-me? + :new :new?}) - (update :quoted-message clojure.set/rename-keys {:parsedText :parsed-text :communityId :community-id}) + (update :quoted-message + clojure.set/rename-keys + {:parsedText :parsed-text :communityId :community-id}) (update :outgoing-status keyword) - (update :command-parameters clojure.set/rename-keys {:transactionHash :transaction-hash - :commandState :command-state}) - (assoc :content {:chat-id (:chatId message) - :text (:text message) - :image (:image message) - :sticker (:sticker message) - :ens-name (:ensName message) - :line-count (:lineCount message) - :parsed-text (:parsedText message) - :links (:links message) - :rtl? (:rtl message) - :response-to (:responseTo message)} + (update :command-parameters + clojure.set/rename-keys + {:transactionHash :transaction-hash + :commandState :command-state}) + (assoc :content {:chat-id (:chatId message) + :text (:text message) + :image (:image message) + :sticker (:sticker message) + :ens-name (:ensName message) + :line-count (:lineCount message) + :parsed-text (:parsedText message) + :links (:links message) + :rtl? (:rtl message) + :response-to (:responseTo message)} :outgoing (boolean (:outgoingStatus message))) (dissoc :ensName :chatId :text :rtl :responseTo :image :sticker :lineCount :parsedText :links))) -(defn messages-by-chat-id-rpc [chat-id - cursor - limit - on-success - on-error] +(defn messages-by-chat-id-rpc + [chat-id + cursor + limit + on-success + on-error] {::json-rpc/call [{:method "wakuext_chatMessages" :params [chat-id cursor limit] :on-success (fn [result] (on-success (update result :messages #(map <-rpc %)))) - :on-error on-error}]}) + :on-error on-error}]}) -(defn mark-seen-rpc [chat-id ids on-success] - {::json-rpc/call [{:method "wakuext_markMessagesSeen" - :params [chat-id ids] +(defn mark-seen-rpc + [chat-id ids on-success] + {::json-rpc/call [{:method "wakuext_markMessagesSeen" + :params [chat-id ids] :on-success #(do (log/debug "successfully marked as seen" %) (when on-success (on-success chat-id ids %))) - :on-error #(log/error "failed to get messages" %)}]}) + :on-error #(log/error "failed to get messages" %)}]}) -(defn delete-message-rpc [id] - {::json-rpc/call [{:method "wakuext_deleteMessage" - :params [id] +(defn delete-message-rpc + [id] + {::json-rpc/call [{:method "wakuext_deleteMessage" + :params [id] :on-success #(log/debug "successfully deleted message" id) - :on-error #(log/error "failed to delete message" % id)}]}) + :on-error #(log/error "failed to delete message" % id)}]}) -(defn delete-messages-from-rpc [author] - {::json-rpc/call [{:method "wakuext_deleteMessagesFrom" - :params [author] +(defn delete-messages-from-rpc + [author] + {::json-rpc/call [{:method "wakuext_deleteMessagesFrom" + :params [author] :on-success #(log/debug "successfully deleted messages from" author) - :on-error #(log/error "failed to delete messages from" % author)}]}) + :on-error #(log/error "failed to delete messages from" % author)}]}) -(defn delete-messages-by-chat-id-rpc [chat-id] - {::json-rpc/call [{:method "wakuext_deleteMessagesByChatID" - :params [chat-id] +(defn delete-messages-by-chat-id-rpc + [chat-id] + {::json-rpc/call [{:method "wakuext_deleteMessagesByChatID" + :params [chat-id] :on-success #(log/debug "successfully deleted messages by chat-id" chat-id) - :on-error #(log/error "failed to delete messages by chat-id" % chat-id)}]}) + :on-error #(log/error "failed to delete messages by chat-id" % chat-id)}]}) -(fx/defn delete-message [cofx id] +(fx/defn delete-message + [cofx id] (delete-message-rpc id)) -(fx/defn delete-messages-from [cofx author] +(fx/defn delete-messages-from + [cofx author] (delete-messages-from-rpc author)) -(fx/defn mark-messages-seen [cofx chat-id ids on-success] +(fx/defn mark-messages-seen + [cofx chat-id ids on-success] (mark-seen-rpc chat-id ids on-success)) -(fx/defn delete-messages-by-chat-id [cofx chat-id] +(fx/defn delete-messages-by-chat-id + [cofx chat-id] (delete-messages-by-chat-id-rpc chat-id)) diff --git a/src/status_im/data_store/messages_test.cljs b/src/status_im/data_store/messages_test.cljs index b5b42d0ca3..a3fc31735a 100644 --- a/src/status_im/data_store/messages_test.cljs +++ b/src/status_im/data_store/messages_test.cljs @@ -4,54 +4,55 @@ (def message-id "0xfe96d03da2159e632a6653d04028b0de8b55f78f03521b26ce10dc5f48a16aee") (def chat-id "chat-id") -(def from "0x0424a68f89ba5fcd5e0640c1e1f591d561fa4125ca4e2a43592bc4123eca10ce064e522c254bb83079ba404327f6eafc01ec90a1444331fe769d3f3a7f90b0dde1") +(def from + "0x0424a68f89ba5fcd5e0640c1e1f591d561fa4125ca4e2a43592bc4123eca10ce064e522c254bb83079ba404327f6eafc01ec90a1444331fe769d3f3a7f90b0dde1") (deftest message<-rpc (testing "message to rpc" - (let [expected {:message-id message-id - :content {:chat-id chat-id - :sticker {:hash "hash" :pack 1} - :text "hta" - :line-count 1 - :ens-name "ens-name" - :parsed-text "parsed-text" - :rtl? false - :image nil - :response-to "a" - :links nil} - :whisper-timestamp 1 - :contact-verification-state 1 - :contact-request-state 2 - :outgoing-status :sending - :command-parameters nil - :outgoing true - :message-type 0 - :clock-value 2 - :from from - :chat-id chat-id - :quoted-message {:from "from" - :text "reply"} - :content-type 1 - :timestamp 3} - message {:id message-id - :whisperTimestamp 1 - :parsedText "parsed-text" - :ensName "ens-name" - :contactVerificationState 1 - :contactRequestState 2 - :localChatId chat-id - :from from - :text "hta" - :rtl false - :chatId chat-id - :lineCount 1 - :sticker {:hash "hash" :pack 1} - :contentType 1 - :messageType 0 - :clock 2 - :responseTo "a" - :quotedMessage {:from "from" - :text "reply"} - :timestamp 3 - :outgoingStatus "sending"}] + (let [expected {:message-id message-id + :content {:chat-id chat-id + :sticker {:hash "hash" :pack 1} + :text "hta" + :line-count 1 + :ens-name "ens-name" + :parsed-text "parsed-text" + :rtl? false + :image nil + :response-to "a" + :links nil} + :whisper-timestamp 1 + :contact-verification-state 1 + :contact-request-state 2 + :outgoing-status :sending + :command-parameters nil + :outgoing true + :message-type 0 + :clock-value 2 + :from from + :chat-id chat-id + :quoted-message {:from "from" + :text "reply"} + :content-type 1 + :timestamp 3} + message {:id message-id + :whisperTimestamp 1 + :parsedText "parsed-text" + :ensName "ens-name" + :contactVerificationState 1 + :contactRequestState 2 + :localChatId chat-id + :from from + :text "hta" + :rtl false + :chatId chat-id + :lineCount 1 + :sticker {:hash "hash" :pack 1} + :contentType 1 + :messageType 0 + :clock 2 + :responseTo "a" + :quotedMessage {:from "from" + :text "reply"} + :timestamp 3 + :outgoingStatus "sending"}] (is (= expected (m/<-rpc message)))))) diff --git a/src/status_im/data_store/pin_messages.cljs b/src/status_im/data_store/pin_messages.cljs index 559bc98a0f..c0058f96d1 100644 --- a/src/status_im/data_store/pin_messages.cljs +++ b/src/status_im/data_store/pin_messages.cljs @@ -1,31 +1,36 @@ (ns status-im.data-store.pin-messages (:require [clojure.set :as clojure.set] + [status-im.data-store.messages :as messages] [status-im.ethereum.json-rpc :as json-rpc] [status-im.utils.fx :as fx] - [taoensso.timbre :as log] - [status-im.data-store.messages :as messages])) + [taoensso.timbre :as log])) -(defn <-rpc [message] +(defn <-rpc + [message] (-> message (merge (messages/<-rpc (message :message))) (clojure.set/rename-keys {:pinnedAt :pinned-at :pinnedBy :pinned-by}) (dissoc :message))) -(defn pinned-message-by-chat-id-rpc [chat-id - cursor - limit - on-success - on-error] +(defn pinned-message-by-chat-id-rpc + [chat-id + cursor + limit + on-success + on-error] {::json-rpc/call [{:method "wakuext_chatPinnedMessages" :params [chat-id cursor limit] :on-success (fn [result] - (let [result (clojure.set/rename-keys result {:pinnedMessages :pinned-messages})] + (let [result (clojure.set/rename-keys result + {:pinnedMessages + :pinned-messages})] (on-success (update result :pinned-messages #(map <-rpc %))))) - :on-error on-error}]}) + :on-error on-error}]}) -(fx/defn send-pin-message [cofx pin-message] - {::json-rpc/call [{:method "wakuext_sendPinMessage" - :params [(messages/->rpc pin-message)] +(fx/defn send-pin-message + [cofx pin-message] + {::json-rpc/call [{:method "wakuext_sendPinMessage" + :params [(messages/->rpc pin-message)] :on-success #(log/debug "successfully pinned message" pin-message) - :on-error #(log/error "failed to pin message" % pin-message)}]}) + :on-error #(log/error "failed to pin message" % pin-message)}]}) diff --git a/src/status_im/data_store/reactions.cljs b/src/status_im/data_store/reactions.cljs index 09939bfcd5..4265e77c76 100644 --- a/src/status_im/data_store/reactions.cljs +++ b/src/status_im/data_store/reactions.cljs @@ -2,7 +2,8 @@ (:require [clojure.set :as clojure.set] [status-im.ethereum.json-rpc :as json-rpc])) -(defn ->rpc [message] +(defn ->rpc + [message] (-> message (clojure.set/rename-keys {:message-id :messageId :emoji-id :emojiId @@ -10,7 +11,8 @@ :message-type :messageType :emoji-reaction-id :id}))) -(defn <-rpc [message] +(defn <-rpc + [message] (-> message (dissoc :chat_id) (clojure.set/rename-keys {:messageId :message-id @@ -19,13 +21,14 @@ :messageType :message-type :id :emoji-reaction-id}))) -(defn reactions-by-chat-id-rpc [chat-id - cursor - limit - on-success - on-error] +(defn reactions-by-chat-id-rpc + [chat-id + cursor + limit + on-success + on-error] {::json-rpc/call [{:method "wakuext_emojiReactionsByChatID" :params [chat-id cursor limit] :on-success (fn [result] (on-success (map <-rpc result))) - :on-error on-error}]}) + :on-error on-error}]}) diff --git a/src/status_im/data_store/settings.cljs b/src/status_im/data_store/settings.cljs index 1996ca030d..d73b2e7cf3 100644 --- a/src/status_im/data_store/settings.cljs +++ b/src/status_im/data_store/settings.cljs @@ -1,10 +1,10 @@ (ns status-im.data-store.settings - (:require - [status-im.utils.config :as config] - [status-im.ethereum.eip55 :as eip55] - [status-im.data-store.visibility-status-updates :as visibility-status-updates])) + (:require [status-im.data-store.visibility-status-updates :as visibility-status-updates] + [status-im.ethereum.eip55 :as eip55] + [status-im.utils.config :as config])) -(defn rpc->networks [networks] +(defn rpc->networks + [networks] (reduce (fn [acc {:keys [id] :as network}] (assoc acc id network)) {} @@ -12,38 +12,44 @@ networks config/default-networks))) -(defn rpc->visible-tokens [visible-tokens] +(defn rpc->visible-tokens + [visible-tokens] (reduce-kv (fn [acc chain visible-tokens] (assoc acc chain (into #{} (map keyword visible-tokens)))) {} visible-tokens)) -(defn rpc->pinned-mailservers [pinned-mailservers] +(defn rpc->pinned-mailservers + [pinned-mailservers] (reduce-kv (fn [acc chain pinned-mailserver] (assoc acc chain (keyword pinned-mailserver))) {} pinned-mailservers)) -(defn rpc->custom-bootnodes [custom-bootnodes] +(defn rpc->custom-bootnodes + [custom-bootnodes] (reduce-kv (fn [acc chain custom-bootnodes] (assoc acc (name chain) custom-bootnodes)) {} custom-bootnodes)) -(defn rpc->stickers-packs [stickers-packs] +(defn rpc->stickers-packs + [stickers-packs] (reduce-kv (fn [acc pack-id stickers-pack] (assoc acc (js/parseInt (name pack-id)) stickers-pack)) {} stickers-packs)) -(defn rpc->settings [settings] +(defn rpc->settings + [settings] (-> settings (update :dapps-address eip55/address->checksum) (update :address eip55/address->checksum) (update :networks/networks rpc->networks) - (update :networks/current-network #(if (seq %) - % - config/default-network)) + (update :networks/current-network + #(if (seq %) + % + config/default-network)) (update :wallet/visible-tokens rpc->visible-tokens) (update :pinned-mailservers rpc->pinned-mailservers) (update :link-previews-enabled-sites set) diff --git a/src/status_im/data_store/visibility_status_updates.cljs b/src/status_im/data_store/visibility_status_updates.cljs index 508df1cbd3..18b1c5ce73 100644 --- a/src/status_im/data_store/visibility_status_updates.cljs +++ b/src/status_im/data_store/visibility_status_updates.cljs @@ -5,20 +5,25 @@ [status-im.utils.fx :as fx] [taoensso.timbre :as log])) -(defn <-rpc [visibility-status-update] - (clojure.set/rename-keys visibility-status-update {:publicKey :public-key - :statusType :status-type})) -(defn <-rpc-settings [settings] +(defn <-rpc + [visibility-status-update] + (clojure.set/rename-keys visibility-status-update + {:publicKey :public-key + :statusType :status-type})) +(defn <-rpc-settings + [settings] (-> settings (clojure.set/rename-keys {:current-user-status :current-user-visibility-status}) (update :current-user-visibility-status <-rpc))) -(fx/defn fetch-visibility-status-updates-rpc [_] +(fx/defn fetch-visibility-status-updates-rpc + [_] {::json-rpc/call [{:method "wakuext_statusUpdates" :params [] :on-success #(re-frame/dispatch [:visibility-status-updates/visibility-status-updates-loaded (:statusUpdates ^js %)]) - :on-error #(log/error - "failed to fetch visibility-status-updates" %)}]}) + :on-error #(log/error + "failed to fetch visibility-status-updates" + %)}]}) diff --git a/src/status_im/ens/core.cljs b/src/status_im/ens/core.cljs index 5106c60812..8d129c32a8 100644 --- a/src/status_im/ens/core.cljs +++ b/src/status_im/ens/core.cljs @@ -2,18 +2,19 @@ (:refer-clojure :exclude [name]) (:require [clojure.string :as string] [re-frame.core :as re-frame] + [status-im.bottom-sheet.core :as bottom-sheet] [status-im.ethereum.core :as ethereum] [status-im.ethereum.eip55 :as eip55] [status-im.ethereum.ens :as ens] [status-im.ethereum.stateofus :as stateofus] [status-im.multiaccounts.update.core :as multiaccounts.update] - [status-im2.navigation.events :as navigation] [status-im.utils.datetime :as datetime] [status-im.utils.fx :as fx] [status-im.utils.random :as random] - [status-im.bottom-sheet.core :as bottom-sheet])) + [status-im2.navigation.events :as navigation])) -(defn fullname [custom-domain? username] +(defn fullname + [custom-domain? username] (if custom-domain? username (stateofus/subdomain username))) @@ -41,9 +42,11 @@ (fx/defn update-ens-tx-state {:events [:update-ens-tx-state]} [{:keys [db]} new-state username custom-domain? tx-hash] - {:db (assoc-in db [:ens/registrations tx-hash] {:state new-state - :username username - :custom-domain? custom-domain?})}) + {:db (assoc-in db + [:ens/registrations tx-hash] + {:state new-state + :username username + :custom-domain? custom-domain?})}) (fx/defn redirect-to-ens-summary {:events [::redirect-to-ens-summary]} @@ -79,28 +82,34 @@ (fx/defn save-username {:events [::save-username]} [{:keys [db] :as cofx} custom-domain? username redirectToSummary] - (let [name (fullname custom-domain? username) - names (get-in db [:multiaccount :usernames] []) + (let [name (fullname custom-domain? username) + names (get-in db [:multiaccount :usernames] []) new-names (conj names name)] (fx/merge cofx (multiaccounts.update/multiaccount-update - :usernames new-names + :usernames + new-names (when redirectToSummary {:on-success #(re-frame/dispatch [::redirect-to-ens-summary])})) (when (empty? names) (multiaccounts.update/multiaccount-update - :preferred-name name {}))))) + :preferred-name + name + {}))))) (fx/defn set-pub-key {:events [::set-pub-key]} [{:keys [db]}] (let [{:keys [username address custom-domain?]} (:ens/registration db) - address (or address (ethereum/default-address db)) - {:keys [public-key]} (:multiaccount db) - chain-id (ethereum/chain-id db) - username (fullname custom-domain? username)] + address (or address (ethereum/default-address db)) + {:keys [public-key]} (:multiaccount db) + chain-id (ethereum/chain-id db) + username (fullname custom-domain? username)] (ens/set-pub-key-prepare-tx - chain-id address username public-key + chain-id + address + username + public-key #(re-frame/dispatch [:signing.ui/sign {:tx-obj % :on-result [::save-username custom-domain? username true] @@ -135,14 +144,16 @@ (ens/pubkey chain-id (fullname custom-domain? username) - #(re-frame/dispatch [::name-resolved username - (cond - (not public-key) :owned - (and (string/ends-with? % "0000000000000000000000000000000000000000000000000000000000000000") (not custom-domain?)) - :invalid-ens - (= % public-key) :connected - :else :connected-with-different-key) - (eip55/address->checksum response)])) + #(re-frame/dispatch + [::name-resolved username + (cond + (not public-key) :owned + (and (string/ends-with? % "0000000000000000000000000000000000000000000000000000000000000000") + (not custom-domain?)) + :invalid-ens + (= % public-key) :connected + :else :connected-with-different-key) + (eip55/address->checksum response)])) :else (re-frame/dispatch [::name-resolved username :taken])))) @@ -159,27 +170,34 @@ (let [{:keys [username]} (:ens/registration db) {:keys [public-key]} (:multiaccount db) - chain-id (ethereum/chain-id db)] + chain-id (ethereum/chain-id db)] (ens/register-prepare-tx - chain-id address username public-key + chain-id + address + username + public-key #(re-frame/dispatch [:signing.ui/sign {:tx-obj % :on-result [:update-ens-tx-state-and-redirect :submitted username false] :on-error [::on-registration-failure]}])))) -(defn- valid-custom-domain? [username] +(defn- valid-custom-domain? + [username] (and (ens/is-valid-eth-name? username) (stateofus/lower-case? username))) -(defn- valid-username? [custom-domain? username] +(defn- valid-username? + [custom-domain? username] (if custom-domain? (valid-custom-domain? username) (stateofus/valid-username? username))) -(defn- state [custom-domain? username usernames] +(defn- state + [custom-domain? username usernames] (cond (or (string/blank? username) - (> 4 (count username))) :too-short + (> 4 (count username))) + :too-short (valid-username? custom-domain? username) (if (usernames (fullname custom-domain? username)) :already-added @@ -193,23 +211,30 @@ {:events [::set-username-candidate]} [{:keys [db]} username] (let [{:keys [custom-domain?]} (:ens/registration db) - usernames (into #{} (get-in db [:multiaccount :usernames])) - state (state custom-domain? username usernames)] + usernames (into #{} (get-in db [:multiaccount :usernames])) + state (state custom-domain? username usernames)] (reset! resolve-last-id (random/id)) (merge - {:db (update db :ens/registration assoc - :username username - :state state)} + {:db (update db + :ens/registration assoc + :username username + :state state)} (when (= state :searching) (let [{:keys [multiaccount]} db - {:keys [public-key]} multiaccount - addresses (ethereum/addresses-without-watch db) - chain-id (ethereum/chain-id db)] + {:keys [public-key]} multiaccount + addresses (ethereum/addresses-without-watch db) + chain-id (ethereum/chain-id db)] {::resolve-owner [chain-id (fullname custom-domain? username) #(on-resolve-owner - chain-id custom-domain? username addresses public-key % - resolve-last-id @resolve-last-id)]}))))) + chain-id + custom-domain? + username + addresses + public-key + % + resolve-last-id + @resolve-last-id)]}))))) (fx/defn return-to-ens-main-screen {:events [::got-it-pressed ::cancel-pressed]} @@ -219,8 +244,9 @@ {:db (dissoc db :ens/registration)} ;; we reset navigation so that navigate back doesn't return ;; into the registration flow - (navigation/set-stack-root :profile-stack [:my-profile - :ens-main]))) + (navigation/set-stack-root :profile-stack + [:my-profile + :ens-main]))) (fx/defn switch-domain-type {:events [::switch-domain-type]} @@ -241,7 +267,8 @@ {:events [::save-preferred-name]} [cofx name] (multiaccounts.update/multiaccount-update cofx - :preferred-name name + :preferred-name + name {})) (fx/defn on-registration-failure @@ -301,9 +328,12 @@ new-names (remove #(= name %) names)] (fx/merge cofx (multiaccounts.update/multiaccount-update - :usernames new-names + :usernames + new-names {}) (when (= name preferred-name) (multiaccounts.update/multiaccount-update - :preferred-name (first new-names) {})) + :preferred-name + (first new-names) + {})) (navigation/navigate-back)))) diff --git a/src/status_im/ethereum/core.cljs b/src/status_im/ethereum/core.cljs index 727d4f5600..ca4e5a356f 100644 --- a/src/status_im/ethereum/core.cljs +++ b/src/status_im/ethereum/core.cljs @@ -1,19 +1,22 @@ (ns status-im.ethereum.core (:require [clojure.string :as string] - [status-im.native-module.core :as status] - [status-im.ethereum.eip55 :as eip55])) + [status-im.ethereum.eip55 :as eip55] + [status-im.native-module.core :as status])) -(defn sha3 [s] +(defn sha3 + [s] (when s (status/sha3 (str s)))) -(defn utf8-to-hex [s] +(defn utf8-to-hex + [s] (let [hex (status/utf8-to-hex (str s))] (if (empty? hex) nil hex))) -(defn hex-to-utf8 [s] +(defn hex-to-utf8 + [s] (let [utf8 (status/hex-to-utf8 s)] (if (empty? utf8) nil @@ -32,90 +35,112 @@ :bsc-testnet {:id BSC-testnet-chain-id :name "BSC tetnet"}}) -(defn chain-id->chain-keyword [i] +(defn chain-id->chain-keyword + [i] (or (some #(when (= i (:id (val %))) (key %)) chains) :custom)) -(defn chain-id->chain-name [i] +(defn chain-id->chain-name + [i] (or (some #(when (= i (:id (val %))) (:name (val %))) chains) :custom)) -(defn chain-keyword->chain-id [k] +(defn chain-keyword->chain-id + [k] (get-in chains [k :id])) -(defn chain-keyword->snt-symbol [k] +(defn chain-keyword->snt-symbol + [k] (case k :mainnet :SNT :STT)) -(defn testnet? [id] +(defn testnet? + [id] (contains? #{(chain-keyword->chain-id :goerli) - (chain-keyword->chain-id :bsc-testnet)} id)) + (chain-keyword->chain-id :bsc-testnet)} + id)) -(defn sidechain? [id] +(defn sidechain? + [id] (contains? #{(chain-keyword->chain-id :xdai) - (chain-keyword->chain-id :bsc)} id)) + (chain-keyword->chain-id :bsc)} + id)) -(defn network-with-upstream-rpc? [network] +(defn network-with-upstream-rpc? + [network] (get-in network [:config :UpstreamConfig :Enabled])) (def hex-prefix "0x") -(defn normalized-hex [hex] +(defn normalized-hex + [hex] (when hex (if (string/starts-with? hex hex-prefix) hex (str hex-prefix hex)))) -(defn current-address [db] +(defn current-address + [db] (-> (get-in db [:multiaccount :address]) normalized-hex)) -(defn get-default-account [accounts] +(defn get-default-account + [accounts] (some #(when (:wallet %) %) accounts)) -(defn default-address [db] +(defn default-address + [db] (-> (get db :multiaccount/accounts) get-default-account :address)) -(defn addresses-without-watch [db] +(defn addresses-without-watch + [db] (into #{} (remove #(= (:type %) :watch) (map #(eip55/address->checksum (:address %)) (get db :multiaccount/accounts))))) -(defn naked-address [s] +(defn naked-address + [s] (when s (string/replace s hex-prefix ""))) (def public-key-length 128) -(defn coordinates [public-key] +(defn coordinates + [public-key] (when-let [hex (naked-address public-key)] (when (= public-key-length (count (subs hex 2))) {:x (normalized-hex (subs hex 2 66)) :y (normalized-hex (subs hex 66))}))) -(defn address? [s] +(defn address? + [s] (when s (status/address? s))) -(defn network->chain-id [network] +(defn network->chain-id + [network] (get-in network [:config :NetworkId])) -(defn network->chain-keyword [network] +(defn network->chain-keyword + [network] (chain-id->chain-keyword (network->chain-id network))) -(defn current-network [db] +(defn current-network + [db] (let [networks (get db :networks/networks) network-id (get db :networks/current-network)] (get networks network-id))) -(defn binance-chain-id? [chain-id] +(defn binance-chain-id? + [chain-id] (or (= BSC-mainnet-chain-id chain-id) (= BSC-testnet-chain-id chain-id))) -(defn binance-chain? [db] +(defn binance-chain? + [db] (-> db current-network network->chain-id @@ -123,13 +148,16 @@ (def custom-rpc-node-id-len 45) -(defn custom-rpc-node? [{:keys [id]}] +(defn custom-rpc-node? + [{:keys [id]}] (= custom-rpc-node-id-len (count id))) -(defn network->network-name [network] +(defn network->network-name + [network] (chain-id->chain-name (network->chain-id network))) -(defn network->chain-name [network] +(defn network->chain-name + [network] (-> network network->chain-keyword name)) @@ -146,16 +174,20 @@ [db] (network->chain-id (get-current-network db))) -(defn snt-symbol [db] +(defn snt-symbol + [db] (chain-keyword->snt-symbol (chain-keyword db))) -(defn address= [address1 address2] - (and address1 address2 +(defn address= + [address1 address2] + (and address1 + address2 (= (string/lower-case (normalized-hex address1)) (string/lower-case (normalized-hex address2))))) -(defn public-key->address [public-key] - (let [length (count public-key) +(defn public-key->address + [public-key] + (let [length (count public-key) normalized-key (case length 132 (str "0x" (subs public-key 4)) 130 public-key diff --git a/src/status_im/ethereum/core_test.cljs b/src/status_im/ethereum/core_test.cljs index 1f8a6b6868..bddf8d9b04 100644 --- a/src/status_im/ethereum/core_test.cljs +++ b/src/status_im/ethereum/core_test.cljs @@ -8,6 +8,9 @@ (is (= (ethereum/chain-id->chain-keyword 5777) :custom))) (deftest coordinates - (is (= {:x "0x46fa4851f3cccd01e3b8d96c130c00bf812502354939eacf06a68fa519ebcbd1" - :y "0xeb08bebe7403856c0d9686210b9b2e324aa0179747bbba56d53f304a002f31c3"} - (ethereum/coordinates "0x0446fa4851f3cccd01e3b8d96c130c00bf812502354939eacf06a68fa519ebcbd1eb08bebe7403856c0d9686210b9b2e324aa0179747bbba56d53f304a002f31c3")))) + (is + (= + {:x "0x46fa4851f3cccd01e3b8d96c130c00bf812502354939eacf06a68fa519ebcbd1" + :y "0xeb08bebe7403856c0d9686210b9b2e324aa0179747bbba56d53f304a002f31c3"} + (ethereum/coordinates + "0x0446fa4851f3cccd01e3b8d96c130c00bf812502354939eacf06a68fa519ebcbd1eb08bebe7403856c0d9686210b9b2e324aa0179747bbba56d53f304a002f31c3")))) diff --git a/src/status_im/ethereum/eip681.cljs b/src/status_im/ethereum/eip681.cljs index b92bf42c81..bdc6e83d3e 100644 --- a/src/status_im/ethereum/eip681.cljs +++ b/src/status_im/ethereum/eip681.cljs @@ -19,25 +19,39 @@ (def key-value-separator "=") ;;TODO(goranjovic) - rewrite all of these with something more readable than regex -(def uri-pattern (re-pattern (str scheme scheme-separator "([^" query-separator "]*)(?:\\" query-separator "(.*))?"))) -(def authority-path-pattern (re-pattern (str "^([^" chain-id-separator function-name-separator "]*)(?:" chain-id-separator "(\\d+))?(?:" function-name-separator "(\\w*))?"))) +(def uri-pattern + (re-pattern (str scheme scheme-separator "([^" query-separator "]*)(?:\\" query-separator "(.*))?"))) +(def authority-path-pattern + (re-pattern (str "^([^" + chain-id-separator + function-name-separator + "]*)(?:" + chain-id-separator + "(\\d+))?(?:" + function-name-separator + "(\\w*))?"))) (def key-value-format (str "([^" parameter-separator key-value-separator "]+)")) (def query-pattern (re-pattern (str key-value-format key-value-separator key-value-format))) (def valid-native-arguments #{:value :gas :gasPrice :gasLimit}) -(defn- parse-query [s] - (into {} (for [[_ k v] (re-seq query-pattern (or s ""))] - [(keyword k) v]))) +(defn- parse-query + [s] + (into {} + (for [[_ k v] (re-seq query-pattern (or s ""))] + [(keyword k) v]))) -(defn- parse-native-arguments [m] +(defn- parse-native-arguments + [m] (select-keys m valid-native-arguments)) -(defn- parse-arguments [function-name s] +(defn- parse-arguments + [function-name s] (let [m (parse-query s) arguments (parse-native-arguments m)] (if function-name - (merge arguments {:function-name function-name} + (merge arguments + {:function-name function-name} (when (seq m) {:function-arguments (apply dissoc m valid-native-arguments)})) arguments))) @@ -65,7 +79,9 @@ raw-address)] (when-let [arguments (parse-arguments function-name query)] (let [contract-address (get-in arguments [:function-arguments :address])] - (if-not (or (not contract-address) (or (ens/is-valid-eth-name? contract-address) (ethereum/address? contract-address))) + (if-not (or (not contract-address) + (or (ens/is-valid-eth-name? contract-address) + (ethereum/address? contract-address))) nil (merge {:address address :chain-id (if chain-id @@ -77,7 +93,7 @@ "Takes a map as returned by `parse-uri` and returns value as BigNumber" [s] (when (string? s) - (let [eth? (string/ends-with? s "ETH") + (let [eth? (string/ends-with? s "ETH") ^js n (money/bignumber (string/replace s "ETH" ""))] (if eth? (.times n 1e18) n)))) @@ -98,7 +114,8 @@ :address (:address function-arguments)} nil)))) -(defn- generate-query-string [m] +(defn- generate-query-string + [m] (string/join parameter-separator (for [[k v] m] (str (name k) key-value-separator v)))) @@ -109,13 +126,17 @@ [address {:keys [chain-id function-name function-arguments] :as m}] (when (ethereum/address? address) (let [parameters (dissoc (into {} (filter second m)) :chain-id)] ;; filter nil values - (str scheme scheme-separator address + (str scheme + scheme-separator + address (when (and chain-id (not= chain-id (ethereum/chain-keyword->chain-id :mainnet))) ;; Add chain-id if specified and is not main-net (str chain-id-separator chain-id)) (when-not (empty? parameters) (if function-name - (str function-name-separator function-name query-separator + (str function-name-separator + function-name + query-separator (let [native-parameters (dissoc parameters :function-name :function-arguments)] (generate-query-string (merge function-arguments native-parameters)))) (str query-separator (generate-query-string parameters)))))))) diff --git a/src/status_im/ethereum/eip681_test.cljs b/src/status_im/ethereum/eip681_test.cljs index 621817b764..b2d4887927 100644 --- a/src/status_im/ethereum/eip681_test.cljs +++ b/src/status_im/ethereum/eip681_test.cljs @@ -13,8 +13,12 @@ (is (= nil (eip681/parse-uri "ethereum:0x1234"))) (is (= nil (eip681/parse-uri "ethereum:gimme.ether?value=1e18"))) (is (= nil (eip681/parse-uri "ethereum:pay-gimme.ether?value=1e18"))) - (is (= nil (eip681/parse-uri "ethereum:pay-snt.thetoken.ether/transfer?address=gimme.eth&uint256=1&gas=100"))) - (is (= nil (eip681/parse-uri "ethereum:pay-snt.thetoken.eth/transfer?address=gimme.ether&uint256=1&gas=100"))) + (is (= nil + (eip681/parse-uri + "ethereum:pay-snt.thetoken.ether/transfer?address=gimme.eth&uint256=1&gas=100"))) + (is (= nil + (eip681/parse-uri + "ethereum:pay-snt.thetoken.eth/transfer?address=gimme.ether&uint256=1&gas=100"))) (is (= (eip681/parse-uri "0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7") {:address "0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7"})) @@ -32,10 +36,13 @@ {:address "0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7" :value "1" :chain-id 1})) (is (= (eip681/parse-uri "ethereum:0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7?unknown=1") - {:address "0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7", :chain-id 1})) + {:address "0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7" :chain-id 1})) - (is (= (eip681/parse-uri "ethereum:0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7?address=0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7") - {:address "0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7", :chain-id 1})) + (is + (= + (eip681/parse-uri + "ethereum:0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7?address=0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7") + {:address "0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7" :chain-id 1})) (is (= (eip681/parse-uri "ethereum:0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7?value=2.014e18") {:address "0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7" :value "2.014e18" :chain-id 1})) @@ -64,60 +71,111 @@ (is (= (eip681/parse-uri "ethereum:0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7/transfer") {:address "0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7" :chain-id 1 :function-name "transfer"})) - (is (= (eip681/parse-uri "ethereum:0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7/transfer?address=0x8e23ee67d1332ad560396262c48ffbb01f93d052&uint256=1") - {:address "0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7" :chain-id 1 - :function-name "transfer" :function-arguments {:address "0x8e23ee67d1332ad560396262c48ffbb01f93d052" :uint256 "1"}})) + (is + (= + (eip681/parse-uri + "ethereum:0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7/transfer?address=0x8e23ee67d1332ad560396262c48ffbb01f93d052&uint256=1") + {:address "0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7" + :chain-id 1 + :function-name "transfer" + :function-arguments {:address "0x8e23ee67d1332ad560396262c48ffbb01f93d052" :uint256 "1"}})) - (is (= (eip681/parse-uri "ethereum:0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7/transfer?address=0x8e23ee67d1332ad560396262c48ffbb01f93d052&uint256=1&gas=100") - {:address "0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7" :chain-id 1 :gas "100" - :function-name "transfer" :function-arguments {:address "0x8e23ee67d1332ad560396262c48ffbb01f93d052" :uint256 "1"}})) + (is + (= + (eip681/parse-uri + "ethereum:0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7/transfer?address=0x8e23ee67d1332ad560396262c48ffbb01f93d052&uint256=1&gas=100") + {:address "0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7" + :chain-id 1 + :gas "100" + :function-name "transfer" + :function-arguments {:address "0x8e23ee67d1332ad560396262c48ffbb01f93d052" :uint256 "1"}})) (is (= (eip681/parse-uri "ethereum:pay-snt.thetoken.eth/transfer?address=gimme.eth&uint256=1&gas=100") - {:address "snt.thetoken.eth" :chain-id 1 :gas "100" - :function-name "transfer" :function-arguments {:address "gimme.eth" :uint256 "1"}})) + {:address "snt.thetoken.eth" + :chain-id 1 + :gas "100" + :function-name "transfer" + :function-arguments {:address "gimme.eth" :uint256 "1"}})) (is (= (eip681/parse-uri "ethereum:snt.thetoken.eth/transfer?address=gimme.eth&uint256=1&gas=100") - {:address "snt.thetoken.eth" :chain-id 1 :gas "100" - :function-name "transfer" :function-arguments {:address "gimme.eth" :uint256 "1"}}))) + {:address "snt.thetoken.eth" + :chain-id 1 + :gas "100" + :function-name "transfer" + :function-arguments {:address "gimme.eth" :uint256 "1"}}))) (def all-tokens - {:mainnet {"0x744d70fdbe2ba4cf95131626614a1763df805b9e" {:address "0x744d70fdbe2ba4cf95131626614a1763df805b9e" + {:mainnet {"0x744d70fdbe2ba4cf95131626614a1763df805b9e" {:address + "0x744d70fdbe2ba4cf95131626614a1763df805b9e" :name "Status Network Token" :symbol :SNT :decimals 18}} - :goerli {"0x3d6afaa395c31fcd391fe3d562e75fe9e8ec7e6a" {:address "0x3d6afaa395c31fcd391fe3d562e75fe9e8ec7e6a" - :name "Status Test Token" - :symbol :STT - :decimals 18}}}) + :goerli {"0x3d6afaa395c31fcd391fe3d562e75fe9e8ec7e6a" + {:address + "0x3d6afaa395c31fcd391fe3d562e75fe9e8ec7e6a" + :name "Status Test Token" + :symbol :STT + :decimals 18}}}) (deftest generate-erc20-uri (is (= nil (eip681/generate-erc20-uri nil nil all-tokens))) - (is (= "ethereum:0x744d70fdbe2ba4cf95131626614a1763df805b9e/transfer?uint256=5&address=0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7" - (eip681/generate-erc20-uri "0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7" {:symbol :SNT :value 5} (:mainnet all-tokens)))) - (is (= "ethereum:0x744d70fdbe2ba4cf95131626614a1763df805b9e/transfer?uint256=5&address=0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7&gas=10000&gasPrice=10000" - (eip681/generate-erc20-uri "0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7" {:symbol :SNT :value 5 :gas 10000 :gasPrice 10000} (:mainnet all-tokens)))) - (is (= "ethereum:0x744d70fdbe2ba4cf95131626614a1763df805b9e/transfer?uint256=5&address=0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7" - (eip681/generate-erc20-uri "0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7" {:symbol :SNT :chain-id 1 :value 5} (:mainnet all-tokens))))) + (is + (= + "ethereum:0x744d70fdbe2ba4cf95131626614a1763df805b9e/transfer?uint256=5&address=0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7" + (eip681/generate-erc20-uri "0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7" + {:symbol :SNT :value 5} + (:mainnet all-tokens)))) + (is + (= + "ethereum:0x744d70fdbe2ba4cf95131626614a1763df805b9e/transfer?uint256=5&address=0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7&gas=10000&gasPrice=10000" + (eip681/generate-erc20-uri "0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7" + {:symbol :SNT :value 5 :gas 10000 :gasPrice 10000} + (:mainnet all-tokens)))) + (is + (= + "ethereum:0x744d70fdbe2ba4cf95131626614a1763df805b9e/transfer?uint256=5&address=0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7" + (eip681/generate-erc20-uri "0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7" + {:symbol :SNT :chain-id 1 :value 5} + (:mainnet all-tokens))))) (deftest generate-uri (is (= nil (eip681/generate-uri nil nil))) - (is (= "ethereum:0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7" (eip681/generate-uri "0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7" nil))) - (is (= "ethereum:0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7" (eip681/generate-uri "0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7" {}))) - (is (= "ethereum:0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7" (eip681/generate-uri "0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7" {:value nil}))) - (is (= "ethereum:0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7?value=1" (eip681/generate-uri "0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7" {:value (money/bignumber 1)}))) - (is (= "ethereum:0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7?value=1000000000000000000" (eip681/generate-uri "0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7" {:value (money/bignumber 1e18)}))) - (is (= "ethereum:0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7?value=1&gas=100" (eip681/generate-uri "0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7" {:value (money/bignumber 1) :gas (money/bignumber 100) :chain-id 1}))) - (is (= "ethereum:0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7@3?value=1&gas=100" (eip681/generate-uri "0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7" {:value (money/bignumber 1) :gas (money/bignumber 100) :chain-id 3}))) - (is (= "ethereum:0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7/transfer?address=0x8e23ee67d1332ad560396262c48ffbb01f93d052&uint256=1&gas=100" + (is (= "ethereum:0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7" + (eip681/generate-uri "0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7" nil))) + (is (= "ethereum:0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7" + (eip681/generate-uri "0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7" {}))) + (is (= "ethereum:0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7" + (eip681/generate-uri "0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7" {:value nil}))) + (is (= "ethereum:0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7?value=1" (eip681/generate-uri "0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7" - {:gas (money/bignumber 100) :chain-id 1 :function-name "transfer" :function-arguments {:address "0x8e23ee67d1332ad560396262c48ffbb01f93d052" :uint256 1}})))) + {:value (money/bignumber 1)}))) + (is (= "ethereum:0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7?value=1000000000000000000" + (eip681/generate-uri "0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7" + {:value (money/bignumber 1e18)}))) + (is (= "ethereum:0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7?value=1&gas=100" + (eip681/generate-uri "0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7" + {:value (money/bignumber 1) :gas (money/bignumber 100) :chain-id 1}))) + (is (= "ethereum:0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7@3?value=1&gas=100" + (eip681/generate-uri "0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7" + {:value (money/bignumber 1) :gas (money/bignumber 100) :chain-id 3}))) + (is + (= + "ethereum:0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7/transfer?address=0x8e23ee67d1332ad560396262c48ffbb01f93d052&uint256=1&gas=100" + (eip681/generate-uri "0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7" + {:gas (money/bignumber 100) + :chain-id 1 + :function-name "transfer" + :function-arguments {:address "0x8e23ee67d1332ad560396262c48ffbb01f93d052" + :uint256 1}})))) (deftest round-trip (let [uri "ethereum:0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7@3?value=1&gas=100" {:keys [address] :as params} (eip681/parse-uri uri)] (is (= uri (eip681/generate-uri address (dissoc params :address))))) - (let [uri "ethereum:0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7@3/transfer?uint256=5&address=0xc55cF4B03948D7EBc8b9E8BAD92643703811d162" - {:keys [address] :as params} (eip681/parse-uri uri)] + (let + [uri + "ethereum:0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7@3/transfer?uint256=5&address=0xc55cF4B03948D7EBc8b9E8BAD92643703811d162" + {:keys [address] :as params} (eip681/parse-uri uri)] (is (= uri (eip681/generate-uri address (dissoc params :address)))))) (deftest parse-eth-value @@ -132,17 +190,25 @@ (is (.equals (money/bignumber "111122223333441239") (eip681/parse-eth-value "111122223333441239")))) (deftest extract-request-details - (let [{:keys [value symbol address]} (eip681/extract-request-details {:address "0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7" :value "1ETH"} {})] + (let [{:keys [value symbol address]} (eip681/extract-request-details + {:address "0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7" + :value "1ETH"} + {})] (is (.equals (money/ether->wei (money/bignumber 1)) value)) (is (= :ETH symbol)) (is (= "0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7" address))) - (is (= (eip681/extract-request-details {:address "0x744d70fdbe2ba4cf95131626614a1763df805b9e" :chain-id 1 :function-name "unknown"} {}) + (is (= (eip681/extract-request-details + {:address "0x744d70fdbe2ba4cf95131626614a1763df805b9e" :chain-id 1 :function-name "unknown"} + {}) {:address "0x744d70fdbe2ba4cf95131626614a1763df805b9e" :chain-id 1 :function-name "unknown"})) (let [{:keys [value symbol address]} (eip681/extract-request-details - {:address "0x744d70fdbe2ba4cf95131626614a1763df805b9e" :chain-id 1 - :function-name "transfer" :function-arguments {:uint256 1000 :address "0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7"}} - {"0x744d70fdbe2ba4cf95131626614a1763df805b9e" {:address "0x744d70fdbe2ba4cf95131626614a1763df805b9e" + {:address "0x744d70fdbe2ba4cf95131626614a1763df805b9e" + :chain-id 1 + :function-name "transfer" + :function-arguments {:uint256 1000 :address "0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7"}} + {"0x744d70fdbe2ba4cf95131626614a1763df805b9e" {:address + "0x744d70fdbe2ba4cf95131626614a1763df805b9e" :name "Status Network Token" :symbol :SNT :decimals 18}})] diff --git a/src/status_im/ethereum/ens.cljs b/src/status_im/ethereum/ens.cljs index f828ebd498..7ddc9b4b7c 100644 --- a/src/status_im/ethereum/ens.cljs +++ b/src/status_im/ethereum/ens.cljs @@ -9,7 +9,8 @@ :goerli "0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e"}) (def default-address "0x0000000000000000000000000000000000000000") -(def default-key "0x0400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000") +(def default-key + "0x0400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000") (def default-hash "0x0000000000000000000000000000000000000000000000000000000000000000") (defn valid-eth-name-prefix? @@ -39,9 +40,10 @@ (json-rpc/call {:method "ens_publicKeyOf" :params [chain-id ens-name] :on-success cb - ;;at some point infura started to return execution reverted error instead of "0x" result + ;;at some point infura started to return execution reverted error instead of "0x" + ;;result ;;our code expects "0x" result - :on-error #(cb "0x")})) + :on-error #(cb "0x")})) (defn owner [chain-id ens-name cb] @@ -59,20 +61,20 @@ (defn expire-at [chain-id ens-name cb] - (json-rpc/call {:method "ens_expireAt" - :params [chain-id ens-name] + (json-rpc/call {:method "ens_expireAt" + :params [chain-id ens-name] :on-success ;;NOTE: returns a timestamp in s and we want ms #(cb (* (js/Number (status/hex-to-number %)) 1000))})) (defn register-prepare-tx [chain-id from ens-name pubkey cb] - (json-rpc/call {:method "ens_registerPrepareTx" - :params [chain-id {:from from} ens-name pubkey] + (json-rpc/call {:method "ens_registerPrepareTx" + :params [chain-id {:from from} ens-name pubkey] :on-success cb})) (defn set-pub-key-prepare-tx [chain-id from ens-name pubkey cb] - (json-rpc/call {:method "ens_setPubKeyPrepareTx" - :params [chain-id {:from from} ens-name pubkey] + (json-rpc/call {:method "ens_setPubKeyPrepareTx" + :params [chain-id {:from from} ens-name pubkey] :on-success cb})) diff --git a/src/status_im/ethereum/json_rpc.cljs b/src/status_im/ethereum/json_rpc.cljs index 724b7846fb..aaf9929fbb 100644 --- a/src/status_im/ethereum/json_rpc.cljs +++ b/src/status_im/ethereum/json_rpc.cljs @@ -16,7 +16,10 @@ (let [updated-delay (if delay (min 2000 (* 2 delay)) 50)] - (log/debug "[on-error-retry]" method "number-of-retries" number-of-retries "delay" delay "error" error) + (log/debug "[on-error-retry]" method + "number-of-retries" number-of-retries + "delay" delay + "error" error) (utils/set-timeout #(call-method (-> arg (update :number-of-retries dec) (assoc :delay updated-delay))) @@ -25,8 +28,10 @@ (defn call [{:keys [method params on-success on-error js-response] :as arg}] - (let [params (or params []) - on-error (or on-error (on-error-retry call arg) #(log/warn :json-rpc/error method :error % :params params))] + (let [params (or params []) + on-error (or on-error + (on-error-retry call arg) + #(log/warn :json-rpc/error method :error % :params params))] (status/call-private-rpc (types/clj->json {:jsonrpc "2.0" :id 1 diff --git a/src/status_im/ethereum/macros.clj b/src/status_im/ethereum/macros.clj index c3076bf8b8..3e3ed2bcdc 100644 --- a/src/status_im/ethereum/macros.clj +++ b/src/status_im/ethereum/macros.clj @@ -2,11 +2,12 @@ (:require [clojure.java.io :as io] [clojure.string :as string])) -(defn token-icon-path [path] +(defn token-icon-path + [path] (fn [el] - (let [el (string/replace el ".png" "") - s (str path el ".png") - s-js (str "." s)] + (let [el (string/replace el ".png" "") + s (str path el ".png") + s-js (str "." s)] (when (.exists (io/file s)) [el `(js/require ~s-js)])))) @@ -14,7 +15,7 @@ "In react-native arguments to require must be static strings. Resolve all icons at compilation time so no variable is used." [network] - (let [path (str "./resources/images/tokens/" (name network) "/") + (let [path (str "./resources/images/tokens/" (name network) "/") files (->> (io/file path) file-seq (filter #(string/ends-with? % "png")) @@ -22,9 +23,10 @@ distinct)] (into {} (map (token-icon-path path) files)))) -(defn network->icon [network] - (let [s (str "./resources/images/tokens/" (name network) "/0-native.png") - s-js (str "." s)] +(defn network->icon + [network] + (let [s (str "./resources/images/tokens/" (name network) "/0-native.png") + s-js (str "." s)] (if (.exists (io/file s)) `(js/require ~s-js) `(js/require "../resources/images/tokens/default-token.png")))) @@ -35,6 +37,8 @@ [all-native-currencies] (into {} (map (fn [[network native-currency]] - [network (assoc-in native-currency - [:icon :source] - (network->icon network))]) all-native-currencies))) + [network + (assoc-in native-currency + [:icon :source] + (network->icon network))]) + all-native-currencies))) diff --git a/src/status_im/ethereum/mnemonic.cljs b/src/status_im/ethereum/mnemonic.cljs index 48d0af439e..0551094940 100644 --- a/src/status_im/ethereum/mnemonic.cljs +++ b/src/status_im/ethereum/mnemonic.cljs @@ -2,44 +2,227 @@ (:require [clojure.string :as string])) (def dictionary - #{"abandon", "ability", "able", "about", "above", "absent", "absorb", "abstract", "absurd", "abuse", "access", "accident", "account", "accuse", "achieve", "acid", "acoustic", "acquire", "across", "act", "action", "actor", "actress", "actual", "adapt", "add", "addict", "address", "adjust", "admit", "adult", "advance", "advice", "aerobic", "affair", "afford", "afraid", "again", "age", "agent", "agree", "ahead", "aim", "air", "airport", "aisle", "alarm", "album", "alcohol", "alert", "alien", "all", "alley", "allow", "almost", "alone", "alpha", "already", "also", "alter", "always", "amateur", "amazing", "among", "amount", "amused", "analyst", "anchor", "ancient", "anger", "angle", "angry", "animal", "ankle", "announce", "annual", "another", "answer", "antenna", "antique", "anxiety", "any", "apart", "apology", "appear", "apple", "approve", "april", "arch", "arctic", "area", "arena", "argue", "arm", "armed", "armor", "army", "around", "arrange", "arrest", "arrive", "arrow", "art", "artefact", "artist", "artwork", "ask", "aspect", "assault", "asset", "assist", "assume", "asthma", "athlete", "atom", "attack", "attend", "attitude", "attract", "auction", "audit", "august", "aunt", "author", "auto", "autumn", "average", "avocado", "avoid", "awake", "aware", "away", "awesome", "awful", "awkward", "axis", "baby", "bachelor", "bacon", "badge", "bag", "balance", "balcony", "ball", "bamboo", "banana", "banner", "bar", "barely", "bargain", "barrel", "base", "basic", "basket", "battle", "beach", "bean", "beauty", "because", "become", "beef", "before", "begin", "behave", "behind", "believe", "below", "belt", "bench", "benefit", "best", "betray", "better", "between", "beyond", "bicycle", "bid", "bike", "bind", "biology", "bird", "birth", "bitter", "black", "blade", "blame", "blanket", "blast", "bleak", "bless", "blind", "blood", "blossom", "blouse", "blue", "blur", "blush", "board", "boat", "body", "boil", "bomb", "bone", "bonus", "book", "boost", "border", "boring", "borrow", "boss", "bottom", "bounce", "box", "boy", "bracket", "brain", "brand", "brass", "brave", "bread", "breeze", "brick", "bridge", "brief", "bright", "bring", "brisk", "broccoli", "broken", "bronze", "broom", "brother", "brown", "brush", "bubble", "buddy", "budget", "buffalo", "build", "bulb", "bulk", "bullet", "bundle", "bunker", "burden", "burger", "burst", "bus", "business", "busy", "butter", "buyer", "buzz", "cabbage", "cabin", "cable", "cactus", "cage", "cake", "call", "calm", "camera", "camp", "can", "canal", "cancel", "candy", "cannon", "canoe", "canvas", "canyon", "capable", "capital", "captain", "car", "carbon", "card", "cargo", "carpet", "carry", "cart", "case", "cash", "casino", "castle", "casual", "cat", "catalog", "catch", "category", "cattle", "caught", "cause", "caution", "cave", "ceiling", "celery", "cement", "census", "century", "cereal", "certain", "chair", "chalk", "champion", "change", "chaos", "chapter", "charge", "chase", "chat", "cheap", "check", "cheese", "chef", "cherry", "chest", "chicken", "chief", "child", "chimney", "choice", "choose", "chronic", "chuckle", "chunk", "churn", "cigar", "cinnamon", "circle", "citizen", "city", "civil", "claim", "clap", "clarify", "claw", "clay", "clean", "clerk", "clever", "click", "client", "cliff", "climb", "clinic", "clip", "clock", "clog", "close", "cloth", "cloud", "clown", "club", "clump", "cluster", "clutch", "coach", "coast", "coconut", "code", "coffee", "coil", "coin", "collect", "color", "column", "combine", "come", "comfort", "comic", "common", "company", "concert", "conduct", "confirm", "congress", "connect", "consider", "control", "convince", "cook", "cool", "copper", "copy", "coral", "core", "corn", "correct", "cost", "cotton", "couch", "country", "couple", "course", "cousin", "cover", "coyote", "crack", "cradle", "craft", "cram", "crane", "crash", "crater", "crawl", "crazy", "cream", "credit", "creek", "crew", "cricket", "crime", "crisp", "critic", "crop", "cross", "crouch", "crowd", "crucial", "cruel", "cruise", "crumble", "crunch", "crush", "cry", "crystal", "cube", "culture", "cup", "cupboard", "curious", "current", "curtain", "curve", "cushion", "custom", "cute", "cycle", "dad", "damage", "damp", "dance", "danger", "daring", "dash", "daughter", "dawn", "day", "deal", "debate", "debris", "decade", "december", "decide", "decline", "decorate", "decrease", "deer", "defense", "define", "defy", "degree", "delay", "deliver", "demand", "demise", "denial", "dentist", "deny", "depart", "depend", "deposit", "depth", "deputy", "derive", "describe", "desert", "design", "desk", "despair", "destroy", "detail", "detect", "develop", "device", "devote", "diagram", "dial", "diamond", "diary", "dice", "diesel", "diet", "differ", "digital", "dignity", "dilemma", "dinner", "dinosaur", "direct", "dirt", "disagree", "discover", "disease", "dish", "dismiss", "disorder", "display", "distance", "divert", "divide", "divorce", "dizzy", "doctor", "document", "dog", "doll", "dolphin", "domain", "donate", "donkey", "donor", "door", "dose", "double", "dove", "draft", "dragon", "drama", "drastic", "draw", "dream", "dress", "drift", "drill", "drink", "drip", "drive", "drop", "drum", "dry", "duck", "dumb", "dune", "during", "dust", "dutch", "duty", "dwarf", "dynamic", "eager", "eagle", "early", "earn", "earth", "easily", "east", "easy", "echo", "ecology", "economy", "edge", "edit", "educate", "effort", "egg", "eight", "either", "elbow", "elder", "electric", "elegant", "element", "elephant", "elevator", "elite", "else", "embark", "embody", "embrace", "emerge", "emotion", "employ", "empower", "empty", "enable", "enact", "end", "endless", "endorse", "enemy", "energy", "enforce", "engage", "engine", "enhance", "enjoy", "enlist", "enough", "enrich", "enroll", "ensure", "enter", "entire", "entry", "envelope", "episode", "equal", "equip", "era", "erase", "erode", "erosion", "error", "erupt", "escape", "essay", "essence", "estate", "eternal", "ethics", "evidence", "evil", "evoke", "evolve", "exact", "example", "excess", "exchange", "excite", "exclude", "excuse", "execute", "exercise", "exhaust", "exhibit", "exile", "exist", "exit", "exotic", "expand", "expect", "expire", "explain", "expose", "express", "extend", "extra", "eye", "eyebrow", "fabric", "face", "faculty", "fade", "faint", "faith", "fall", "false", "fame", "family", "famous", "fan", "fancy", "fantasy", "farm", "fashion", "fat", "fatal", "father", "fatigue", "fault", "favorite", "feature", "february", "federal", "fee", "feed", "feel", "female", "fence", "festival", "fetch", "fever", "few", "fiber", "fiction", "field", "figure", "file", "film", "filter", "final", "find", "fine", "finger", "finish", "fire", "firm", "first", "fiscal", "fish", "fit", "fitness", "fix", "flag", "flame", "flash", "flat", "flavor", "flee", "flight", "flip", "float", "flock", "floor", "flower", "fluid", "flush", "fly", "foam", "focus", "fog", "foil", "fold", "follow", "food", "foot", "force", "forest", "forget", "fork", "fortune", "forum", "forward", "fossil", "foster", "found", "fox", "fragile", "frame", "frequent", "fresh", "friend", "fringe", "frog", "front", "frost", "frown", "frozen", "fruit", "fuel", "fun", "funny", "furnace", "fury", "future", "gadget", "gain", "galaxy", "gallery", "game", "gap", "garage", "garbage", "garden", "garlic", "garment", "gas", "gasp", "gate", "gather", "gauge", "gaze", "general", "genius", "genre", "gentle", "genuine", "gesture", "ghost", "giant", "gift", "giggle", "ginger", "giraffe", "girl", "give", "glad", "glance", "glare", "glass", "glide", "glimpse", "globe", "gloom", "glory", "glove", "glow", "glue", "goat", "goddess", "gold", "good", "goose", "gorilla", "gospel", "gossip", "govern", "gown", "grab", "grace", "grain", "grant", "grape", "grass", "gravity", "great", "green", "grid", "grief", "grit", "grocery", "group", "grow", "grunt", "guard", "guess", "guide", "guilt", "guitar", "gun", "gym", "habit", "hair", "half", "hammer", "hamster", "hand", "happy", "harbor", "hard", "harsh", "harvest", "hat", "have", "hawk", "hazard", "head", "health", "heart", "heavy", "hedgehog", "height", "hello", "helmet", "help", "hen", "hero", "hidden", "high", "hill", "hint", "hip", "hire", "history", "hobby", "hockey", "hold", "hole", "holiday", "hollow", "home", "honey", "hood", "hope", "horn", "horror", "horse", "hospital", "host", "hotel", "hour", "hover", "hub", "huge", "human", "humble", "humor", "hundred", "hungry", "hunt", "hurdle", "hurry", "hurt", "husband", "hybrid", "ice", "icon", "idea", "identify", "idle", "ignore", "ill", "illegal", "illness", "image", "imitate", "immense", "immune", "impact", "impose", "improve", "impulse", "inch", "include", "income", "increase", "index", "indicate", "indoor", "industry", "infant", "inflict", "inform", "inhale", "inherit", "initial", "inject", "injury", "inmate", "inner", "innocent", "input", "inquiry", "insane", "insect", "inside", "inspire", "install", "intact", "interest", "into", "invest", "invite", "involve", "iron", "island", "isolate", "issue", "item", "ivory", "jacket", "jaguar", "jar", "jazz", "jealous", "jeans", "jelly", "jewel", "job", "join", "joke", "journey", "joy", "judge", "juice", "jump", "jungle", "junior", "junk", "just", "kangaroo", "keen", "keep", "ketchup", "key", "kick", "kid", "kidney", "kind", "kingdom", "kiss", "kit", "kitchen", "kite", "kitten", "kiwi", "knee", "knife", "knock", "know", "lab", "label", "labor", "ladder", "lady", "lake", "lamp", "language", "laptop", "large", "later", "latin", "laugh", "laundry", "lava", "law", "lawn", "lawsuit", "layer", "lazy", "leader", "leaf", "learn", "leave", "lecture", "left", "leg", "legal", "legend", "leisure", "lemon", "lend", "length", "lens", "leopard", "lesson", "letter", "level", "liar", "liberty", "library", "license", "life", "lift", "light", "like", "limb", "limit", "link", "lion", "liquid", "list", "little", "live", "lizard", "load", "loan", "lobster", "local", "lock", "logic", "lonely", "long", "loop", "lottery", "loud", "lounge", "love", "loyal", "lucky", "luggage", "lumber", "lunar", "lunch", "luxury", "lyrics", "machine", "mad", "magic", "magnet", "maid", "mail", "main", "major", "make", "mammal", "man", "manage", "mandate", "mango", "mansion", "manual", "maple", "marble", "march", "margin", "marine", "market", "marriage", "mask", "mass", "master", "match", "material", "math", "matrix", "matter", "maximum", "maze", "meadow", "mean", "measure", "meat", "mechanic", "medal", "media", "melody", "melt", "member", "memory", "mention", "menu", "mercy", "merge", "merit", "merry", "mesh", "message", "metal", "method", "middle", "midnight", "milk", "million", "mimic", "mind", "minimum", "minor", "minute", "miracle", "mirror", "misery", "miss", "mistake", "mix", "mixed", "mixture", "mobile", "model", "modify", "mom", "moment", "monitor", "monkey", "monster", "month", "moon", "moral", "more", "morning", "mosquito", "mother", "motion", "motor", "mountain", "mouse", "move", "movie", "much", "muffin", "mule", "multiply", "muscle", "museum", "mushroom", "music", "must", "mutual", "myself", "mystery", "myth", "naive", "name", "napkin", "narrow", "nasty", "nation", "nature", "near", "neck", "need", "negative", "neglect", "neither", "nephew", "nerve", "nest", "net", "network", "neutral", "never", "news", "next", "nice", "night", "noble", "noise", "nominee", "noodle", "normal", "north", "nose", "notable", "note", "nothing", "notice", "novel", "now", "nuclear", "number", "nurse", "nut", "oak", "obey", "object", "oblige", "obscure", "observe", "obtain", "obvious", "occur", "ocean", "october", "odor", "off", "offer", "office", "often", "oil", "okay", "old", "olive", "olympic", "omit", "once", "one", "onion", "online", "only", "open", "opera", "opinion", "oppose", "option", "orange", "orbit", "orchard", "order", "ordinary", "organ", "orient", "original", "orphan", "ostrich", "other", "outdoor", "outer", "output", "outside", "oval", "oven", "over", "own", "owner", "oxygen", "oyster", "ozone", "pact", "paddle", "page", "pair", "palace", "palm", "panda", "panel", "panic", "panther", "paper", "parade", "parent", "park", "parrot", "party", "pass", "patch", "path", "patient", "patrol", "pattern", "pause", "pave", "payment", "peace", "peanut", "pear", "peasant", "pelican", "pen", "penalty", "pencil", "people", "pepper", "perfect", "permit", "person", "pet", "phone", "photo", "phrase", "physical", "piano", "picnic", "picture", "piece", "pig", "pigeon", "pill", "pilot", "pink", "pioneer", "pipe", "pistol", "pitch", "pizza", "place", "planet", "plastic", "plate", "play", "please", "pledge", "pluck", "plug", "plunge", "poem", "poet", "point", "polar", "pole", "police", "pond", "pony", "pool", "popular", "portion", "position", "possible", "post", "potato", "pottery", "poverty", "powder", "power", "practice", "praise", "predict", "prefer", "prepare", "present", "pretty", "prevent", "price", "pride", "primary", "print", "priority", "prison", "private", "prize", "problem", "process", "produce", "profit", "program", "project", "promote", "proof", "property", "prosper", "protect", "proud", "provide", "public", "pudding", "pull", "pulp", "pulse", "pumpkin", "punch", "pupil", "puppy", "purchase", "purity", "purpose", "purse", "push", "put", "puzzle", "pyramid", "quality", "quantum", "quarter", "question", "quick", "quit", "quiz", "quote", "rabbit", "raccoon", "race", "rack", "radar", "radio", "rail", "rain", "raise", "rally", "ramp", "ranch", "random", "range", "rapid", "rare", "rate", "rather", "raven", "raw", "razor", "ready", "real", "reason", "rebel", "rebuild", "recall", "receive", "recipe", "record", "recycle", "reduce", "reflect", "reform", "refuse", "region", "regret", "regular", "reject", "relax", "release", "relief", "rely", "remain", "remember", "remind", "remove", "render", "renew", "rent", "reopen", "repair", "repeat", "replace", "report", "require", "rescue", "resemble", "resist", "resource", "response", "result", "retire", "retreat", "return", "reunion", "reveal", "review", "reward", "rhythm", "rib", "ribbon", "rice", "rich", "ride", "ridge", "rifle", "right", "rigid", "ring", "riot", "ripple", "risk", "ritual", "rival", "river", "road", "roast", "robot", "robust", "rocket", "romance", "roof", "rookie", "room", "rose", "rotate", "rough", "round", "route", "royal", "rubber", "rude", "rug", "rule", "run", "runway", "rural", "sad", "saddle", "sadness", "safe", "sail", "salad", "salmon", "salon", "salt", "salute", "same", "sample", "sand", "satisfy", "satoshi", "sauce", "sausage", "save", "say", "scale", "scan", "scare", "scatter", "scene", "scheme", "school", "science", "scissors", "scorpion", "scout", "scrap", "screen", "script", "scrub", "sea", "search", "season", "seat", "second", "secret", "section", "security", "seed", "seek", "segment", "select", "sell", "seminar", "senior", "sense", "sentence", "series", "service", "session", "settle", "setup", "seven", "shadow", "shaft", "shallow", "share", "shed", "shell", "sheriff", "shield", "shift", "shine", "ship", "shiver", "shock", "shoe", "shoot", "shop", "short", "shoulder", "shove", "shrimp", "shrug", "shuffle", "shy", "sibling", "sick", "side", "siege", "sight", "sign", "silent", "silk", "silly", "silver", "similar", "simple", "since", "sing", "siren", "sister", "situate", "six", "size", "skate", "sketch", "ski", "skill", "skin", "skirt", "skull", "slab", "slam", "sleep", "slender", "slice", "slide", "slight", "slim", "slogan", "slot", "slow", "slush", "small", "smart", "smile", "smoke", "smooth", "snack", "snake", "snap", "sniff", "snow", "soap", "soccer", "social", "sock", "soda", "soft", "solar", "soldier", "solid", "solution", "solve", "someone", "song", "soon", "sorry", "sort", "soul", "sound", "soup", "source", "south", "space", "spare", "spatial", "spawn", "speak", "special", "speed", "spell", "spend", "sphere", "spice", "spider", "spike", "spin", "spirit", "split", "spoil", "sponsor", "spoon", "sport", "spot", "spray", "spread", "spring", "spy", "square", "squeeze", "squirrel", "stable", "stadium", "staff", "stage", "stairs", "stamp", "stand", "start", "state", "stay", "steak", "steel", "stem", "step", "stereo", "stick", "still", "sting", "stock", "stomach", "stone", "stool", "story", "stove", "strategy", "street", "strike", "strong", "struggle", "student", "stuff", "stumble", "style", "subject", "submit", "subway", "success", "such", "sudden", "suffer", "sugar", "suggest", "suit", "summer", "sun", "sunny", "sunset", "super", "supply", "supreme", "sure", "surface", "surge", "surprise", "surround", "survey", "suspect", "sustain", "swallow", "swamp", "swap", "swarm", "swear", "sweet", "swift", "swim", "swing", "switch", "sword", "symbol", "symptom", "syrup", "system", "table", "tackle", "tag", "tail", "talent", "talk", "tank", "tape", "target", "task", "taste", "tattoo", "taxi", "teach", "team", "tell", "ten", "tenant", "tennis", "tent", "term", "test", "text", "thank", "that", "theme", "then", "theory", "there", "they", "thing", "this", "thought", "three", "thrive", "throw", "thumb", "thunder", "ticket", "tide", "tiger", "tilt", "timber", "time", "tiny", "tip", "tired", "tissue", "title", "toast", "tobacco", "today", "toddler", "toe", "together", "toilet", "token", "tomato", "tomorrow", "tone", "tongue", "tonight", "tool", "tooth", "top", "topic", "topple", "torch", "tornado", "tortoise", "toss", "total", "tourist", "toward", "tower", "town", "toy", "track", "trade", "traffic", "tragic", "train", "transfer", "trap", "trash", "travel", "tray", "treat", "tree", "trend", "trial", "tribe", "trick", "trigger", "trim", "trip", "trophy", "trouble", "truck", "true", "truly", "trumpet", "trust", "truth", "try", "tube", "tuition", "tumble", "tuna", "tunnel", "turkey", "turn", "turtle", "twelve", "twenty", "twice", "twin", "twist", "two", "type", "typical", "ugly", "umbrella", "unable", "unaware", "uncle", "uncover", "under", "undo", "unfair", "unfold", "unhappy", "uniform", "unique", "unit", "universe", "unknown", "unlock", "until", "unusual", "unveil", "update", "upgrade", "uphold", "upon", "upper", "upset", "urban", "urge", "usage", "use", "used", "useful", "useless", "usual", "utility", "vacant", "vacuum", "vague", "valid", "valley", "valve", "van", "vanish", "vapor", "various", "vast", "vault", "vehicle", "velvet", "vendor", "venture", "venue", "verb", "verify", "version", "very", "vessel", "veteran", "viable", "vibrant", "vicious", "victory", "video", "view", "village", "vintage", "violin", "virtual", "virus", "visa", "visit", "visual", "vital", "vivid", "vocal", "voice", "void", "volcano", "volume", "vote", "voyage", "wage", "wagon", "wait", "walk", "wall", "walnut", "want", "warfare", "warm", "warrior", "wash", "wasp", "waste", "water", "wave", "way", "wealth", "weapon", "wear", "weasel", "weather", "web", "wedding", "weekend", "weird", "welcome", "west", "wet", "whale", "what", "wheat", "wheel", "when", "where", "whip", "whisper", "wide", "width", "wife", "wild", "will", "win", "window", "wine", "wing", "wink", "winner", "winter", "wire", "wisdom", "wise", "wish", "witness", "wolf", "woman", "wonder", "wood", "wool", "word", "work", "world", "worry", "worth", "wrap", "wreck", "wrestle", "wrist", "write", "wrong", "yard", "year", "yellow", "you", "young", "youth", "zebra", "zero", "zone", "zoo"}) + #{"abandon" "ability" "able" "about" "above" "absent" "absorb" "abstract" "absurd" "abuse" "access" + "accident" "account" "accuse" "achieve" "acid" "acoustic" "acquire" "across" "act" "action" "actor" + "actress" "actual" "adapt" "add" "addict" "address" "adjust" "admit" "adult" "advance" "advice" + "aerobic" "affair" "afford" "afraid" "again" "age" "agent" "agree" "ahead" "aim" "air" "airport" + "aisle" "alarm" "album" "alcohol" "alert" "alien" "all" "alley" "allow" "almost" "alone" "alpha" + "already" "also" "alter" "always" "amateur" "amazing" "among" "amount" "amused" "analyst" "anchor" + "ancient" "anger" "angle" "angry" "animal" "ankle" "announce" "annual" "another" "answer" "antenna" + "antique" "anxiety" "any" "apart" "apology" "appear" "apple" "approve" "april" "arch" "arctic" "area" + "arena" "argue" "arm" "armed" "armor" "army" "around" "arrange" "arrest" "arrive" "arrow" "art" + "artefact" "artist" "artwork" "ask" "aspect" "assault" "asset" "assist" "assume" "asthma" "athlete" + "atom" "attack" "attend" "attitude" "attract" "auction" "audit" "august" "aunt" "author" "auto" + "autumn" "average" "avocado" "avoid" "awake" "aware" "away" "awesome" "awful" "awkward" "axis" "baby" + "bachelor" "bacon" "badge" "bag" "balance" "balcony" "ball" "bamboo" "banana" "banner" "bar" "barely" + "bargain" "barrel" "base" "basic" "basket" "battle" "beach" "bean" "beauty" "because" "become" "beef" + "before" "begin" "behave" "behind" "believe" "below" "belt" "bench" "benefit" "best" "betray" + "better" "between" "beyond" "bicycle" "bid" "bike" "bind" "biology" "bird" "birth" "bitter" "black" + "blade" "blame" "blanket" "blast" "bleak" "bless" "blind" "blood" "blossom" "blouse" "blue" "blur" + "blush" "board" "boat" "body" "boil" "bomb" "bone" "bonus" "book" "boost" "border" "boring" "borrow" + "boss" "bottom" "bounce" "box" "boy" "bracket" "brain" "brand" "brass" "brave" "bread" "breeze" + "brick" "bridge" "brief" "bright" "bring" "brisk" "broccoli" "broken" "bronze" "broom" "brother" + "brown" "brush" "bubble" "buddy" "budget" "buffalo" "build" "bulb" "bulk" "bullet" "bundle" "bunker" + "burden" "burger" "burst" "bus" "business" "busy" "butter" "buyer" "buzz" "cabbage" "cabin" "cable" + "cactus" "cage" "cake" "call" "calm" "camera" "camp" "can" "canal" "cancel" "candy" "cannon" "canoe" + "canvas" "canyon" "capable" "capital" "captain" "car" "carbon" "card" "cargo" "carpet" "carry" "cart" + "case" "cash" "casino" "castle" "casual" "cat" "catalog" "catch" "category" "cattle" "caught" "cause" + "caution" "cave" "ceiling" "celery" "cement" "census" "century" "cereal" "certain" "chair" "chalk" + "champion" "change" "chaos" "chapter" "charge" "chase" "chat" "cheap" "check" "cheese" "chef" + "cherry" "chest" "chicken" "chief" "child" "chimney" "choice" "choose" "chronic" "chuckle" "chunk" + "churn" "cigar" "cinnamon" "circle" "citizen" "city" "civil" "claim" "clap" "clarify" "claw" "clay" + "clean" "clerk" "clever" "click" "client" "cliff" "climb" "clinic" "clip" "clock" "clog" "close" + "cloth" "cloud" "clown" "club" "clump" "cluster" "clutch" "coach" "coast" "coconut" "code" "coffee" + "coil" "coin" "collect" "color" "column" "combine" "come" "comfort" "comic" "common" "company" + "concert" "conduct" "confirm" "congress" "connect" "consider" "control" "convince" "cook" "cool" + "copper" "copy" "coral" "core" "corn" "correct" "cost" "cotton" "couch" "country" "couple" "course" + "cousin" "cover" "coyote" "crack" "cradle" "craft" "cram" "crane" "crash" "crater" "crawl" "crazy" + "cream" "credit" "creek" "crew" "cricket" "crime" "crisp" "critic" "crop" "cross" "crouch" "crowd" + "crucial" "cruel" "cruise" "crumble" "crunch" "crush" "cry" "crystal" "cube" "culture" "cup" + "cupboard" "curious" "current" "curtain" "curve" "cushion" "custom" "cute" "cycle" "dad" "damage" + "damp" "dance" "danger" "daring" "dash" "daughter" "dawn" "day" "deal" "debate" "debris" "decade" + "december" "decide" "decline" "decorate" "decrease" "deer" "defense" "define" "defy" "degree" "delay" + "deliver" "demand" "demise" "denial" "dentist" "deny" "depart" "depend" "deposit" "depth" "deputy" + "derive" "describe" "desert" "design" "desk" "despair" "destroy" "detail" "detect" "develop" "device" + "devote" "diagram" "dial" "diamond" "diary" "dice" "diesel" "diet" "differ" "digital" "dignity" + "dilemma" "dinner" "dinosaur" "direct" "dirt" "disagree" "discover" "disease" "dish" "dismiss" + "disorder" "display" "distance" "divert" "divide" "divorce" "dizzy" "doctor" "document" "dog" "doll" + "dolphin" "domain" "donate" "donkey" "donor" "door" "dose" "double" "dove" "draft" "dragon" "drama" + "drastic" "draw" "dream" "dress" "drift" "drill" "drink" "drip" "drive" "drop" "drum" "dry" "duck" + "dumb" "dune" "during" "dust" "dutch" "duty" "dwarf" "dynamic" "eager" "eagle" "early" "earn" "earth" + "easily" "east" "easy" "echo" "ecology" "economy" "edge" "edit" "educate" "effort" "egg" "eight" + "either" "elbow" "elder" "electric" "elegant" "element" "elephant" "elevator" "elite" "else" "embark" + "embody" "embrace" "emerge" "emotion" "employ" "empower" "empty" "enable" "enact" "end" "endless" + "endorse" "enemy" "energy" "enforce" "engage" "engine" "enhance" "enjoy" "enlist" "enough" "enrich" + "enroll" "ensure" "enter" "entire" "entry" "envelope" "episode" "equal" "equip" "era" "erase" "erode" + "erosion" "error" "erupt" "escape" "essay" "essence" "estate" "eternal" "ethics" "evidence" "evil" + "evoke" "evolve" "exact" "example" "excess" "exchange" "excite" "exclude" "excuse" "execute" + "exercise" "exhaust" "exhibit" "exile" "exist" "exit" "exotic" "expand" "expect" "expire" "explain" + "expose" "express" "extend" "extra" "eye" "eyebrow" "fabric" "face" "faculty" "fade" "faint" "faith" + "fall" "false" "fame" "family" "famous" "fan" "fancy" "fantasy" "farm" "fashion" "fat" "fatal" + "father" "fatigue" "fault" "favorite" "feature" "february" "federal" "fee" "feed" "feel" "female" + "fence" "festival" "fetch" "fever" "few" "fiber" "fiction" "field" "figure" "file" "film" "filter" + "final" "find" "fine" "finger" "finish" "fire" "firm" "first" "fiscal" "fish" "fit" "fitness" "fix" + "flag" "flame" "flash" "flat" "flavor" "flee" "flight" "flip" "float" "flock" "floor" "flower" + "fluid" "flush" "fly" "foam" "focus" "fog" "foil" "fold" "follow" "food" "foot" "force" "forest" + "forget" "fork" "fortune" "forum" "forward" "fossil" "foster" "found" "fox" "fragile" "frame" + "frequent" "fresh" "friend" "fringe" "frog" "front" "frost" "frown" "frozen" "fruit" "fuel" "fun" + "funny" "furnace" "fury" "future" "gadget" "gain" "galaxy" "gallery" "game" "gap" "garage" "garbage" + "garden" "garlic" "garment" "gas" "gasp" "gate" "gather" "gauge" "gaze" "general" "genius" "genre" + "gentle" "genuine" "gesture" "ghost" "giant" "gift" "giggle" "ginger" "giraffe" "girl" "give" "glad" + "glance" "glare" "glass" "glide" "glimpse" "globe" "gloom" "glory" "glove" "glow" "glue" "goat" + "goddess" "gold" "good" "goose" "gorilla" "gospel" "gossip" "govern" "gown" "grab" "grace" "grain" + "grant" "grape" "grass" "gravity" "great" "green" "grid" "grief" "grit" "grocery" "group" "grow" + "grunt" "guard" "guess" "guide" "guilt" "guitar" "gun" "gym" "habit" "hair" "half" "hammer" "hamster" + "hand" "happy" "harbor" "hard" "harsh" "harvest" "hat" "have" "hawk" "hazard" "head" "health" "heart" + "heavy" "hedgehog" "height" "hello" "helmet" "help" "hen" "hero" "hidden" "high" "hill" "hint" "hip" + "hire" "history" "hobby" "hockey" "hold" "hole" "holiday" "hollow" "home" "honey" "hood" "hope" + "horn" "horror" "horse" "hospital" "host" "hotel" "hour" "hover" "hub" "huge" "human" "humble" + "humor" "hundred" "hungry" "hunt" "hurdle" "hurry" "hurt" "husband" "hybrid" "ice" "icon" "idea" + "identify" "idle" "ignore" "ill" "illegal" "illness" "image" "imitate" "immense" "immune" "impact" + "impose" "improve" "impulse" "inch" "include" "income" "increase" "index" "indicate" "indoor" + "industry" "infant" "inflict" "inform" "inhale" "inherit" "initial" "inject" "injury" "inmate" + "inner" "innocent" "input" "inquiry" "insane" "insect" "inside" "inspire" "install" "intact" + "interest" "into" "invest" "invite" "involve" "iron" "island" "isolate" "issue" "item" "ivory" + "jacket" "jaguar" "jar" "jazz" "jealous" "jeans" "jelly" "jewel" "job" "join" "joke" "journey" "joy" + "judge" "juice" "jump" "jungle" "junior" "junk" "just" "kangaroo" "keen" "keep" "ketchup" "key" + "kick" "kid" "kidney" "kind" "kingdom" "kiss" "kit" "kitchen" "kite" "kitten" "kiwi" "knee" "knife" + "knock" "know" "lab" "label" "labor" "ladder" "lady" "lake" "lamp" "language" "laptop" "large" + "later" "latin" "laugh" "laundry" "lava" "law" "lawn" "lawsuit" "layer" "lazy" "leader" "leaf" + "learn" "leave" "lecture" "left" "leg" "legal" "legend" "leisure" "lemon" "lend" "length" "lens" + "leopard" "lesson" "letter" "level" "liar" "liberty" "library" "license" "life" "lift" "light" "like" + "limb" "limit" "link" "lion" "liquid" "list" "little" "live" "lizard" "load" "loan" "lobster" "local" + "lock" "logic" "lonely" "long" "loop" "lottery" "loud" "lounge" "love" "loyal" "lucky" "luggage" + "lumber" "lunar" "lunch" "luxury" "lyrics" "machine" "mad" "magic" "magnet" "maid" "mail" "main" + "major" "make" "mammal" "man" "manage" "mandate" "mango" "mansion" "manual" "maple" "marble" "march" + "margin" "marine" "market" "marriage" "mask" "mass" "master" "match" "material" "math" "matrix" + "matter" "maximum" "maze" "meadow" "mean" "measure" "meat" "mechanic" "medal" "media" "melody" "melt" + "member" "memory" "mention" "menu" "mercy" "merge" "merit" "merry" "mesh" "message" "metal" "method" + "middle" "midnight" "milk" "million" "mimic" "mind" "minimum" "minor" "minute" "miracle" "mirror" + "misery" "miss" "mistake" "mix" "mixed" "mixture" "mobile" "model" "modify" "mom" "moment" "monitor" + "monkey" "monster" "month" "moon" "moral" "more" "morning" "mosquito" "mother" "motion" "motor" + "mountain" "mouse" "move" "movie" "much" "muffin" "mule" "multiply" "muscle" "museum" "mushroom" + "music" "must" "mutual" "myself" "mystery" "myth" "naive" "name" "napkin" "narrow" "nasty" "nation" + "nature" "near" "neck" "need" "negative" "neglect" "neither" "nephew" "nerve" "nest" "net" "network" + "neutral" "never" "news" "next" "nice" "night" "noble" "noise" "nominee" "noodle" "normal" "north" + "nose" "notable" "note" "nothing" "notice" "novel" "now" "nuclear" "number" "nurse" "nut" "oak" + "obey" "object" "oblige" "obscure" "observe" "obtain" "obvious" "occur" "ocean" "october" "odor" + "off" "offer" "office" "often" "oil" "okay" "old" "olive" "olympic" "omit" "once" "one" "onion" + "online" "only" "open" "opera" "opinion" "oppose" "option" "orange" "orbit" "orchard" "order" + "ordinary" "organ" "orient" "original" "orphan" "ostrich" "other" "outdoor" "outer" "output" + "outside" "oval" "oven" "over" "own" "owner" "oxygen" "oyster" "ozone" "pact" "paddle" "page" "pair" + "palace" "palm" "panda" "panel" "panic" "panther" "paper" "parade" "parent" "park" "parrot" "party" + "pass" "patch" "path" "patient" "patrol" "pattern" "pause" "pave" "payment" "peace" "peanut" "pear" + "peasant" "pelican" "pen" "penalty" "pencil" "people" "pepper" "perfect" "permit" "person" "pet" + "phone" "photo" "phrase" "physical" "piano" "picnic" "picture" "piece" "pig" "pigeon" "pill" "pilot" + "pink" "pioneer" "pipe" "pistol" "pitch" "pizza" "place" "planet" "plastic" "plate" "play" "please" + "pledge" "pluck" "plug" "plunge" "poem" "poet" "point" "polar" "pole" "police" "pond" "pony" "pool" + "popular" "portion" "position" "possible" "post" "potato" "pottery" "poverty" "powder" "power" + "practice" "praise" "predict" "prefer" "prepare" "present" "pretty" "prevent" "price" "pride" + "primary" "print" "priority" "prison" "private" "prize" "problem" "process" "produce" "profit" + "program" "project" "promote" "proof" "property" "prosper" "protect" "proud" "provide" "public" + "pudding" "pull" "pulp" "pulse" "pumpkin" "punch" "pupil" "puppy" "purchase" "purity" "purpose" + "purse" "push" "put" "puzzle" "pyramid" "quality" "quantum" "quarter" "question" "quick" "quit" + "quiz" "quote" "rabbit" "raccoon" "race" "rack" "radar" "radio" "rail" "rain" "raise" "rally" "ramp" + "ranch" "random" "range" "rapid" "rare" "rate" "rather" "raven" "raw" "razor" "ready" "real" "reason" + "rebel" "rebuild" "recall" "receive" "recipe" "record" "recycle" "reduce" "reflect" "reform" "refuse" + "region" "regret" "regular" "reject" "relax" "release" "relief" "rely" "remain" "remember" "remind" + "remove" "render" "renew" "rent" "reopen" "repair" "repeat" "replace" "report" "require" "rescue" + "resemble" "resist" "resource" "response" "result" "retire" "retreat" "return" "reunion" "reveal" + "review" "reward" "rhythm" "rib" "ribbon" "rice" "rich" "ride" "ridge" "rifle" "right" "rigid" "ring" + "riot" "ripple" "risk" "ritual" "rival" "river" "road" "roast" "robot" "robust" "rocket" "romance" + "roof" "rookie" "room" "rose" "rotate" "rough" "round" "route" "royal" "rubber" "rude" "rug" "rule" + "run" "runway" "rural" "sad" "saddle" "sadness" "safe" "sail" "salad" "salmon" "salon" "salt" + "salute" "same" "sample" "sand" "satisfy" "satoshi" "sauce" "sausage" "save" "say" "scale" "scan" + "scare" "scatter" "scene" "scheme" "school" "science" "scissors" "scorpion" "scout" "scrap" "screen" + "script" "scrub" "sea" "search" "season" "seat" "second" "secret" "section" "security" "seed" "seek" + "segment" "select" "sell" "seminar" "senior" "sense" "sentence" "series" "service" "session" "settle" + "setup" "seven" "shadow" "shaft" "shallow" "share" "shed" "shell" "sheriff" "shield" "shift" "shine" + "ship" "shiver" "shock" "shoe" "shoot" "shop" "short" "shoulder" "shove" "shrimp" "shrug" "shuffle" + "shy" "sibling" "sick" "side" "siege" "sight" "sign" "silent" "silk" "silly" "silver" "similar" + "simple" "since" "sing" "siren" "sister" "situate" "six" "size" "skate" "sketch" "ski" "skill" "skin" + "skirt" "skull" "slab" "slam" "sleep" "slender" "slice" "slide" "slight" "slim" "slogan" "slot" + "slow" "slush" "small" "smart" "smile" "smoke" "smooth" "snack" "snake" "snap" "sniff" "snow" "soap" + "soccer" "social" "sock" "soda" "soft" "solar" "soldier" "solid" "solution" "solve" "someone" "song" + "soon" "sorry" "sort" "soul" "sound" "soup" "source" "south" "space" "spare" "spatial" "spawn" + "speak" "special" "speed" "spell" "spend" "sphere" "spice" "spider" "spike" "spin" "spirit" "split" + "spoil" "sponsor" "spoon" "sport" "spot" "spray" "spread" "spring" "spy" "square" "squeeze" + "squirrel" "stable" "stadium" "staff" "stage" "stairs" "stamp" "stand" "start" "state" "stay" "steak" + "steel" "stem" "step" "stereo" "stick" "still" "sting" "stock" "stomach" "stone" "stool" "story" + "stove" "strategy" "street" "strike" "strong" "struggle" "student" "stuff" "stumble" "style" + "subject" "submit" "subway" "success" "such" "sudden" "suffer" "sugar" "suggest" "suit" "summer" + "sun" "sunny" "sunset" "super" "supply" "supreme" "sure" "surface" "surge" "surprise" "surround" + "survey" "suspect" "sustain" "swallow" "swamp" "swap" "swarm" "swear" "sweet" "swift" "swim" "swing" + "switch" "sword" "symbol" "symptom" "syrup" "system" "table" "tackle" "tag" "tail" "talent" "talk" + "tank" "tape" "target" "task" "taste" "tattoo" "taxi" "teach" "team" "tell" "ten" "tenant" "tennis" + "tent" "term" "test" "text" "thank" "that" "theme" "then" "theory" "there" "they" "thing" "this" + "thought" "three" "thrive" "throw" "thumb" "thunder" "ticket" "tide" "tiger" "tilt" "timber" "time" + "tiny" "tip" "tired" "tissue" "title" "toast" "tobacco" "today" "toddler" "toe" "together" "toilet" + "token" "tomato" "tomorrow" "tone" "tongue" "tonight" "tool" "tooth" "top" "topic" "topple" "torch" + "tornado" "tortoise" "toss" "total" "tourist" "toward" "tower" "town" "toy" "track" "trade" "traffic" + "tragic" "train" "transfer" "trap" "trash" "travel" "tray" "treat" "tree" "trend" "trial" "tribe" + "trick" "trigger" "trim" "trip" "trophy" "trouble" "truck" "true" "truly" "trumpet" "trust" "truth" + "try" "tube" "tuition" "tumble" "tuna" "tunnel" "turkey" "turn" "turtle" "twelve" "twenty" "twice" + "twin" "twist" "two" "type" "typical" "ugly" "umbrella" "unable" "unaware" "uncle" "uncover" "under" + "undo" "unfair" "unfold" "unhappy" "uniform" "unique" "unit" "universe" "unknown" "unlock" "until" + "unusual" "unveil" "update" "upgrade" "uphold" "upon" "upper" "upset" "urban" "urge" "usage" "use" + "used" "useful" "useless" "usual" "utility" "vacant" "vacuum" "vague" "valid" "valley" "valve" "van" + "vanish" "vapor" "various" "vast" "vault" "vehicle" "velvet" "vendor" "venture" "venue" "verb" + "verify" "version" "very" "vessel" "veteran" "viable" "vibrant" "vicious" "victory" "video" "view" + "village" "vintage" "violin" "virtual" "virus" "visa" "visit" "visual" "vital" "vivid" "vocal" + "voice" "void" "volcano" "volume" "vote" "voyage" "wage" "wagon" "wait" "walk" "wall" "walnut" "want" + "warfare" "warm" "warrior" "wash" "wasp" "waste" "water" "wave" "way" "wealth" "weapon" "wear" + "weasel" "weather" "web" "wedding" "weekend" "weird" "welcome" "west" "wet" "whale" "what" "wheat" + "wheel" "when" "where" "whip" "whisper" "wide" "width" "wife" "wild" "will" "win" "window" "wine" + "wing" "wink" "winner" "winter" "wire" "wisdom" "wise" "wish" "witness" "wolf" "woman" "wonder" + "wood" "wool" "word" "work" "world" "worry" "worth" "wrap" "wreck" "wrestle" "wrist" "write" "wrong" + "yard" "year" "yellow" "you" "young" "youth" "zebra" "zero" "zone" "zoo"}) -(defn sanitize-passphrase [s] +(defn sanitize-passphrase + [s] (-> (string/trim s) (string/replace #"[\s\n]+" " "))) -(defn passphrase->words [s] +(defn passphrase->words + [s] (when s (-> (sanitize-passphrase s) (string/split #" ")))) -(defn words->passphrase [v] +(defn words->passphrase + [v] (string/join " " v)) (def valid-word-counts #{12 15 18 21 24}) -(defn valid-word-counts? [v] +(defn valid-word-counts? + [v] (boolean (valid-word-counts (count v)))) -(defn words-count [s] +(defn words-count + [s] (if (empty? s) nil (-> s passphrase->words count))) -(defn- valid-word? [s] +(defn- valid-word? + [s] (re-matches #"^[A-z]+$" s)) -(defn valid-length? [s] +(defn valid-length? + [s] (-> s passphrase->words valid-word-counts?)) -(defn valid-words? [s] +(defn valid-words? + [s] (->> s passphrase->words (every? valid-word?))) -(defn status-generated-phrase? [s] +(defn status-generated-phrase? + [s] (every? dictionary (passphrase->words s))) diff --git a/src/status_im/ethereum/mnemonic_test.cljs b/src/status_im/ethereum/mnemonic_test.cljs index 431d8b5f74..3a8be00e97 100644 --- a/src/status_im/ethereum/mnemonic_test.cljs +++ b/src/status_im/ethereum/mnemonic_test.cljs @@ -22,8 +22,11 @@ (is (= ["one" "two" "three" "for" "five" "six" "seven" "height" "nine" "ten" "eleven" "twelve"] (mnemonic/passphrase->words "one two three for five six seven height nine ten eleven twelve")) (= ["one" "two" "three" "for" "five" "six" "seven" "height" "nine" "ten" "eleven" "twelve"] - (mnemonic/passphrase->words " one two three for five six seven height nine ten eleven twelve ")))) + (mnemonic/passphrase->words + " one two three for five six seven height nine ten eleven twelve ")))) (deftest status-generate-phrase? - (is (mnemonic/status-generated-phrase? "game buzz method pretty olympic fat quit display velvet unveil marine crater")) - (is (not (mnemonic/status-generated-phrase? "game buzz method pretty zeus fat quit display velvet unveil marine crater")))) + (is (mnemonic/status-generated-phrase? + "game buzz method pretty olympic fat quit display velvet unveil marine crater")) + (is (not (mnemonic/status-generated-phrase? + "game buzz method pretty zeus fat quit display velvet unveil marine crater")))) diff --git a/src/status_im/ethereum/stateofus.cljs b/src/status_im/ethereum/stateofus.cljs index 74bec380c7..f66a2326f7 100644 --- a/src/status_im/ethereum/stateofus.cljs +++ b/src/status_im/ethereum/stateofus.cljs @@ -1,8 +1,8 @@ (ns status-im.ethereum.stateofus (:require [clojure.string :as string] - [status-im.utils.config :as config] + [status-im.ethereum.core :as ethereum] [status-im.ethereum.ens :as ens] - [status-im.ethereum.core :as ethereum])) + [status-im.utils.config :as config])) (def domain "stateofus.eth") @@ -22,7 +22,8 @@ username (subdomain username)))) -(defn username [name] +(defn username + [name] (when (and name (string/ends-with? name domain)) (first (string/split name ".")))) @@ -34,7 +35,8 @@ (def registrars-cache (atom {})) -(defn get-registrar [chain callback] +(defn get-registrar + [chain callback] (if-let [contract (get @registrars-cache chain)] (callback contract) (ens/owner @@ -45,19 +47,23 @@ (swap! registrars-cache assoc chain addr) (callback addr)))))) -(defn get-cached-registrar [chain] +(defn get-cached-registrar + [chain] (get @registrars-cache chain (get old-registrars chain))) -(defn lower-case? [s] +(defn lower-case? + [s] (when s (= s (string/lower-case s)))) -(defn valid-username? [username] +(defn valid-username? + [username] (boolean (and (lower-case? username) (re-find #"^[a-z0-9]+$" username)))) -(defn ens-name-parse [contact-identity] +(defn ens-name-parse + [contact-identity] (when (string? contact-identity) (string/lower-case (if (ens/is-valid-eth-name? contact-identity) diff --git a/src/status_im/ethereum/subscriptions.cljs b/src/status_im/ethereum/subscriptions.cljs index 0c970e2868..d131b68f5b 100644 --- a/src/status_im/ethereum/subscriptions.cljs +++ b/src/status_im/ethereum/subscriptions.cljs @@ -1,22 +1,23 @@ (ns status-im.ethereum.subscriptions (:require [status-im.ethereum.eip55 :as eip55] - [status-im.wallet.db :as wallet] - [status-im.wallet.core :as wallet.core] [status-im.ethereum.transactions.core :as transactions] [status-im.utils.fx :as fx] + [status-im.wallet.core :as wallet.core] + [status-im.wallet.db :as wallet] [taoensso.timbre :as log])) (fx/defn new-transfers [cofx block-number accounts] (log/debug "[wallet-subs] new-transfers" "accounts" accounts - "block" block-number) + "block" block-number) (transactions/check-watched-transactions cofx)) (fx/defn recent-history-fetching-started [{:keys [db]} accounts] (log/debug "[wallet-subs] recent-history-fetching-started" - "accounts" accounts) + "accounts" + accounts) (let [event (get db :wallet/on-recent-history-fetching)] (cond-> {:db (-> db (transactions/update-fetching-status accounts :recent? true) @@ -29,16 +30,17 @@ [{:keys [db]} {:keys [accounts blockNumber]}] (log/debug "[wallet-subs] recent-history-fetching-ended" "accounts" accounts - "block" blockNumber) - {:db (-> db - (assoc :ethereum/current-block blockNumber) - (update-in [:wallet :accounts] - wallet/remove-transactions-since-block blockNumber) - (transactions/update-fetching-status accounts :recent? false) - (dissoc :wallet/waiting-for-recent-history? - :wallet/refreshing-history? - :wallet/fetching-error - :wallet/recent-history-fetching-started?)) + "block" blockNumber) + {:db (-> db + (assoc :ethereum/current-block blockNumber) + (update-in [:wallet :accounts] + wallet/remove-transactions-since-block + blockNumber) + (transactions/update-fetching-status accounts :recent? false) + (dissoc :wallet/waiting-for-recent-history? + :wallet/refreshing-history? + :wallet/fetching-error + :wallet/recent-history-fetching-started?)) :transactions/get-transfers {:chain-tokens (:wallet/all-tokens db) :addresses (reduce @@ -57,7 +59,7 @@ [{:keys [db] :as cofx} {:keys [message]}] (fx/merge cofx - {:db (assoc db :wallet/fetching-error message)} + {:db (assoc db :wallet/fetching-error message)} (wallet.core/after-checking-history))) (fx/defn non-archival-node-detected @@ -67,13 +69,13 @@ (fx/defn new-wallet-event [cofx {:keys [type blockNumber accounts] :as event}] (log/info "[wallet-subs] new-wallet-event" - "event-type" type + "event-type" type "blockNumber" blockNumber - "accounts" accounts) + "accounts" accounts) (case type - "new-transfers" (new-transfers cofx blockNumber accounts) - "recent-history-fetching" (recent-history-fetching-started cofx accounts) - "recent-history-ready" (recent-history-fetching-ended cofx event) - "fetching-history-error" (fetching-error cofx event) + "new-transfers" (new-transfers cofx blockNumber accounts) + "recent-history-fetching" (recent-history-fetching-started cofx accounts) + "recent-history-ready" (recent-history-fetching-ended cofx event) + "fetching-history-error" (fetching-error cofx event) "non-archival-node-detected" (non-archival-node-detected cofx event) (log/warn ::unknown-wallet-event :type type :event event))) diff --git a/src/status_im/ethereum/tokens.cljs b/src/status_im/ethereum/tokens.cljs index 1f65b36eb8..aec69d6faa 100644 --- a/src/status_im/ethereum/tokens.cljs +++ b/src/status_im/ethereum/tokens.cljs @@ -1,8 +1,7 @@ (ns status-im.ethereum.tokens (:require [clojure.string :as string] [status-im.ethereum.core :as ethereum]) - (:require-macros - [status-im.ethereum.macros :as ethereum.macros :refer [resolve-icons]])) + (:require-macros [status-im.ethereum.macros :as ethereum.macros :refer [resolve-icons]])) (def default-native-currency (memoize @@ -41,17 +40,19 @@ (def native-currency-symbols (set (map #(-> % val :symbol) all-native-currencies))) -(defn native-currency [{:keys [symbol] :as current-network}] +(defn native-currency + [{:keys [symbol] :as current-network}] (let [chain (ethereum/network->chain-keyword current-network)] (get all-native-currencies chain (default-native-currency symbol)))) -(defn ethereum? [symbol] +(defn ethereum? + [symbol] (native-currency-symbols symbol)) (def token-icons {:mainnet (resolve-icons :mainnet) - :xdai (resolve-icons :xdai) - :custom []}) + :xdai (resolve-icons :xdai) + :custom []}) (def default-token (js/require "../resources/images/tokens/default-token.png")) @@ -61,22 +62,27 @@ (assoc-in [:icon :source] (get-in token-icons [network (name (:symbol token))] default-token)) (update :address string/lower-case))) -(defn nfts-for [all-tokens] +(defn nfts-for + [all-tokens] (filter :nft? (vals all-tokens))) -(defn sorted-tokens-for [all-tokens] +(defn sorted-tokens-for + [all-tokens] (->> (vals all-tokens) (filter #(not (:hidden? %))) (sort #(compare (string/lower-case (:name %1)) (string/lower-case (:name %2)))))) -(defn symbol->token [all-tokens symbol] +(defn symbol->token + [all-tokens symbol] (some #(when (= symbol (:symbol %)) %) (vals all-tokens))) -(defn address->token [all-tokens address] +(defn address->token + [all-tokens address] (get all-tokens (string/lower-case address))) -(defn asset-for [all-tokens current-network symbol] +(defn asset-for + [all-tokens current-network symbol] (let [native-coin (native-currency current-network)] (if (or (= (:symbol-display native-coin) symbol) (= (:symbol native-coin) symbol)) diff --git a/src/status_im/ethereum/transactions/core.cljs b/src/status_im/ethereum/transactions/core.cljs index 726aa046ba..570e80c462 100644 --- a/src/status_im/ethereum/transactions/core.cljs +++ b/src/status_im/ethereum/transactions/core.cljs @@ -23,8 +23,9 @@ (def network->subdomain {5 "goerli"}) -(defn get-transaction-details-url [chain-id hash] - {:pre [(number? chain-id) (string? hash)] +(defn get-transaction-details-url + [chain-id hash] + {:pre [(number? chain-id) (string? hash)] :post [(or (nil? %) (string? %))]} (cond (etherscan-supported? chain-id) @@ -51,15 +52,16 @@ (defn- parse-token-transfer [chain-tokens contract] - (let [{:keys [symbol] :as token} (get chain-tokens contract + (let [{:keys [symbol] :as token} (get chain-tokens + contract default-erc20-token)] - {:symbol symbol - :token token + {:symbol symbol + :token token ;; NOTE(goranjovic) - just a flag we need when we merge this entry ;; with the existing entry in the app, e.g. transaction info with ;; gas details, or a previous transfer entry with old confirmations ;; count. - :transfer true})) + :transfer true})) (defn enrich-transfer [chain-tokens @@ -114,8 +116,9 @@ [{:keys [db]} transaction-id {:keys [trigger-fn on-trigger] :as watch-params}] (when (and (fn? trigger-fn) (fn? on-trigger)) - {:db (assoc-in db [:ethereum/watched-transactions transaction-id] - watch-params)})) + {:db (assoc-in db + [:ethereum/watched-transactions transaction-id] + watch-params)})) (fx/defn check-transaction "Check if the transaction has been triggered and applies the effects returned @@ -126,8 +129,10 @@ (let [{:keys [trigger-fn on-trigger]} watch-params] (when (trigger-fn db transaction) (fx/merge cofx - {:db (update db :ethereum/watched-transactions - dissoc hash)} + {:db (update db + :ethereum/watched-transactions + dissoc + hash)} (on-trigger transaction)))))) (fx/defn check-watched-transactions @@ -158,24 +163,29 @@ id hash))] (fx/merge cofx - {:db (assoc-in db [:wallet :accounts address :transactions unique-id] - (assoc transfer :hash unique-id))} + {:db (assoc-in db + [:wallet :accounts address :transactions unique-id] + (assoc transfer :hash unique-id))} (check-transaction transfer))))) -(defn get-min-known-block [db address] +(defn get-min-known-block + [db address] (get-in db [:wallet :accounts (eip55/address->checksum address) :min-block])) -(defn get-max-block-with-transfers [db address] +(defn get-max-block-with-transfers + [db address] (get-in db [:wallet :accounts (eip55/address->checksum address) :max-block])) -(defn min-block-transfers-count [db address] - (get-in db [:wallet :accounts - (eip55/address->checksum address) - :min-block-transfers-count])) +(defn min-block-transfers-count + [db address] + (get-in db + [:wallet :accounts + (eip55/address->checksum address) + :min-block-transfers-count])) (fx/defn set-lowest-fetched-block [{:keys [db]} address transfers] - (let [checksum (eip55/address->checksum address) + (let [checksum (eip55/address->checksum address) {:keys [min-block min-block-transfers-count]} (reduce (fn [{:keys [min-block] :as acc} @@ -198,23 +208,26 @@ (min-block-transfers-count db address)} transfers)] (log/debug "[transactions] set-lowest-fetched-block" - "address" address - "min-block" min-block + "address" address + "min-block" min-block "min-block-transfers-count" min-block-transfers-count) - {:db (update-in db [:wallet :accounts checksum] assoc - :min-block min-block + {:db (update-in db + [:wallet :accounts checksum] + assoc + :min-block min-block :min-block-transfers-count min-block-transfers-count)})) (defn update-fetching-status [db addresses fetching-type state] (update-in - db [:wallet :fetching] + db + [:wallet :fetching] (fn [accounts] (reduce (fn [accounts address] (assoc-in accounts - [(eip55/address->checksum address) fetching-type] - state)) + [(eip55/address->checksum address) fetching-type] + state)) accounts addresses)))) @@ -229,10 +242,11 @@ (fx/defn tx-history-end-reached [{:keys [db] :as cofx} address] (let [syncing-allowed? (utils.mobile-sync/syncing-allowed? cofx)] - {:db (assoc-in db [:wallet :fetching address :all-fetched?] - (if syncing-allowed? - :all - :all-preloaded))})) + {:db (assoc-in db + [:wallet :fetching address :all-fetched?] + (if syncing-allowed? + :all + :all-preloaded))})) (fx/defn delete-pending-transactions [{:keys [db]} address transactions] @@ -251,31 +265,31 @@ [{:keys [db] :as cofx} transfers {:keys [address limit]}] (log/debug "[transfers] new-transfers" "address" address - "count" (count transfers) - "limit" limit) - (let [checksum (eip55/address->checksum address) + "count" (count transfers) + "limit" limit) + (let [checksum (eip55/address->checksum address) max-known-block (get-max-block-with-transfers db address) - effects (cond-> [(when (seq transfers) - (set-lowest-fetched-block checksum transfers)) - (wallet/set-max-block-with-transfers checksum transfers)] + effects (cond-> [(when (seq transfers) + (set-lowest-fetched-block checksum transfers)) + (wallet/set-max-block-with-transfers checksum transfers)] - (seq transfers) - (concat - [(delete-pending-transactions address transfers)] - (mapv add-transfer transfers)) + (seq transfers) + (concat + [(delete-pending-transactions address transfers)] + (mapv add-transfer transfers)) - (and max-known-block - (some #(> (:block %) max-known-block) transfers)) - (conj (wallet/update-balances - [address] - (zero? max-known-block))) + (and max-known-block + (some #(> (:block %) max-known-block) transfers)) + (conj (wallet/update-balances + [address] + (zero? max-known-block))) - (and (zero? max-known-block) - (empty? transfers)) - (conj (wallet/set-zero-balances {:address address})) + (and (zero? max-known-block) + (empty? transfers)) + (conj (wallet/set-zero-balances {:address address})) - (< (count transfers) limit) - (conj (tx-history-end-reached checksum)))] + (< (count transfers) limit) + (conj (tx-history-end-reached checksum)))] (apply fx/merge cofx (tx-fetching-ended [checksum]) effects))) (fx/defn check-ens-transactions @@ -288,8 +302,11 @@ (contains? set-of-transactions-hash hash))) (get db :ens/registrations)) fxs (map (fn [[hash {:keys [username custom-domain?]}]] - (let [transfer (first (filter (fn [transfer] (let [transfer-hash (get transfer :hash)] (= transfer-hash hash))) transfers)) - type (get transfer :type) + (let [transfer (first (filter (fn [transfer] + (let [transfer-hash (get transfer :hash)] + (= transfer-hash hash))) + transfers)) + type (get transfer :type) transaction-success (get transfer :transfer)] (cond (= transaction-success true) @@ -316,54 +333,57 @@ [cofx error address] (log/debug "[transactions] tx-fetching-failed" "address" address - "error" error) + "error" error) (tx-fetching-ended cofx [address])) (re-frame/reg-fx :transactions/get-transfers - (fn [{:keys [chain-tokens addresses before-block limit - limit-per-address fetch-more?] - :as params - :or {limit default-transfers-limit + (fn + [{:keys [chain-tokens addresses before-block limit + limit-per-address fetch-more?] + :as params + :or {limit default-transfers-limit fetch-more? true}}] {:pre [(spec/valid? (spec/coll-of string?) addresses)]} (log/debug "[transactions] get-transfers" - "addresses" addresses - "block" before-block - "limit" limit + "addresses" addresses + "block" before-block + "limit" limit "limit-per-address" limit-per-address - "fetch-more?" fetch-more?) + "fetch-more?" fetch-more?) (doseq [address addresses] (let [limit (or (get limit-per-address address) limit)] (json-rpc/call - {:method "wallet_getTransfersByAddress" - :params [address (encode/uint before-block) (encode/uint limit) fetch-more?] + {:method "wallet_getTransfersByAddress" + :params [address (encode/uint before-block) (encode/uint limit) fetch-more?] :on-success #(re-frame/dispatch [::new-transfers (enrich-transfers chain-tokens %) - (assoc params :address address - :limit limit)]) - :on-error #(re-frame/dispatch [::tx-fetching-failed address])}))))) + (assoc params + :address address + :limit limit)]) + :on-error #(re-frame/dispatch [::tx-fetching-failed address])}))))) -(defn some-transactions-loaded? [db address] +(defn some-transactions-loaded? + [db address] (not-empty (get-in db [:wallet :accounts address :transactions]))) (fx/defn fetch-more-tx {:events [:transactions/fetch-more]} [{:keys [db] :as cofx} address] - (let [min-known-block (or (get-min-known-block db address) - (:ethereum/current-block db)) + (let [min-known-block (or (get-min-known-block db address) + (:ethereum/current-block db)) min-block-transfers-count (or (min-block-transfers-count db address) 0)] (fx/merge cofx {:transactions/get-transfers - {:chain-tokens (:wallet/all-tokens db) - :addresses [address] - :before-block min-known-block - :fetch-more? (utils.mobile-sync/syncing-allowed? cofx) + {:chain-tokens (:wallet/all-tokens db) + :addresses [address] + :before-block min-known-block + :fetch-more? (utils.mobile-sync/syncing-allowed? cofx) ;; Transfers are requested before and including `min-known-block` because ;; there is no guarantee that all transfers from that block are shown ;; already. To make sure that we fetch the whole `default-transfers-limit` diff --git a/src/status_im/events.cljs b/src/status_im/events.cljs index d99e630096..24ece34ce5 100644 --- a/src/status_im/events.cljs +++ b/src/status_im/events.cljs @@ -1,64 +1,65 @@ (ns status-im.events - (:require clojure.set - [re-frame.core :as re-frame] - status-im.add-new.core - [status-im.async-storage.core :as async-storage] - status-im.backup.core - status-im.bootnodes.core - status-im.browser.core - status-im.browser.permissions - [status-im.chat.models :as chat] - status-im.chat.models.images - status-im.chat.models.input - status-im.chat.models.loading - status-im.chat.models.transport - [status-im.constants :as constants] - status-im.contact.block - status-im.contact.chat - status-im.contact.core - status-im.currency.core - [status-im.ethereum.json-rpc :as json-rpc] - status-im.ethereum.subscriptions - status-im.fleet.core - status-im.http.core - [status-im.i18n.i18n :as i18n] - [status-im.keycard.core :as keycard] - status-im.log-level.core - status-im.mailserver.constants - [status-im.mailserver.core :as mailserver] - [status-im.multiaccounts.biometric.core :as biometric] - status-im.multiaccounts.login.core - status-im.multiaccounts.logout.core - status-im.multiaccounts.update.core - [status-im.native-module.core :as status] - [status-im2.navigation.events :as navigation] - status-im2.contexts.activity-center.events - status-im.pairing.core - status-im.profile.core - status-im.search.core - status-im.signals.core - status-im.stickers.core - status-im.transport.core - [status-im.ui.components.permissions :as permissions] - [status-im.ui.components.react :as react] - status-im.ui.screens.privacy-and-security-settings.events - [status-im.utils.dimensions :as dimensions] - [status-im.utils.fx :as fx] - status-im.utils.logging.core - [status-im.utils.universal-links.core :as universal-links] - [status-im.utils.utils :as utils] - status-im.visibility-status-updates.core - status-im.waku.core - status-im.wallet.accounts.core - status-im.wallet.choose-recipient.core - [status-im.wallet.core :as wallet] - status-im.wallet.custom-tokens.core - status-im.wallet-connect.core - status-im.wallet-connect-legacy.core - status-im.network.net-info - status-im.visibility-status-popover.core - status-im2.contexts.shell.events - [status-im.multiaccounts.model :as multiaccounts.model])) + (:require + clojure.set + [re-frame.core :as re-frame] + status-im.add-new.core + [status-im.async-storage.core :as async-storage] + status-im.backup.core + status-im.bootnodes.core + status-im.browser.core + status-im.browser.permissions + [status-im.chat.models :as chat] + status-im.chat.models.images + status-im.chat.models.input + status-im.chat.models.loading + status-im.chat.models.transport + [status-im.constants :as constants] + status-im.contact.block + status-im.contact.chat + status-im.contact.core + status-im.currency.core + [status-im.ethereum.json-rpc :as json-rpc] + status-im.ethereum.subscriptions + status-im.fleet.core + status-im.http.core + [status-im.i18n.i18n :as i18n] + [status-im.keycard.core :as keycard] + status-im.log-level.core + status-im.mailserver.constants + [status-im.mailserver.core :as mailserver] + [status-im.multiaccounts.biometric.core :as biometric] + status-im.multiaccounts.login.core + status-im.multiaccounts.logout.core + [status-im.multiaccounts.model :as multiaccounts.model] + status-im.multiaccounts.update.core + [status-im.native-module.core :as status] + status-im.network.net-info + status-im.pairing.core + status-im.profile.core + status-im.search.core + status-im.signals.core + status-im.stickers.core + status-im.transport.core + [status-im.ui.components.permissions :as permissions] + [status-im.ui.components.react :as react] + status-im.ui.screens.privacy-and-security-settings.events + [status-im.utils.dimensions :as dimensions] + [status-im.utils.fx :as fx] + status-im.utils.logging.core + [status-im.utils.universal-links.core :as universal-links] + [status-im.utils.utils :as utils] + status-im.visibility-status-popover.core + status-im.visibility-status-updates.core + status-im.waku.core + status-im.wallet-connect-legacy.core + status-im.wallet-connect.core + status-im.wallet.accounts.core + status-im.wallet.choose-recipient.core + [status-im.wallet.core :as wallet] + status-im.wallet.custom-tokens.core + status-im2.contexts.activity-center.events + status-im2.contexts.shell.events + [status-im2.navigation.events :as navigation])) (re-frame/reg-fx :dismiss-keyboard @@ -115,32 +116,37 @@ [cofx _] (when (multiaccounts.model/logged-in? cofx) {:multiaccounts.ui/switch-theme (get-in cofx [:db :multiaccount :appearance]) - :dispatch [:reload-new-ui]})) + :dispatch [:reload-new-ui]})) (def authentication-options {:reason (i18n/label :t/biometric-auth-reason-login)}) -(defn- on-biometric-auth-result [{:keys [bioauth-success bioauth-code bioauth-message]}] +(defn- on-biometric-auth-result + [{:keys [bioauth-success bioauth-code bioauth-message]}] (when-not bioauth-success (if (= bioauth-code "USER_FALLBACK") (re-frame/dispatch [:multiaccounts.logout.ui/logout-confirmed]) - (utils/show-confirmation {:title (i18n/label :t/biometric-auth-confirm-title) - :content (or bioauth-message (i18n/label :t/biometric-auth-confirm-message)) - :confirm-button-text (i18n/label :t/biometric-auth-confirm-try-again) - :cancel-button-text (i18n/label :t/biometric-auth-confirm-logout) - :on-accept #(biometric/authenticate nil on-biometric-auth-result authentication-options) - :on-cancel #(re-frame/dispatch [:multiaccounts.logout.ui/logout-confirmed])})))) + (utils/show-confirmation + {:title (i18n/label :t/biometric-auth-confirm-title) + :content (or bioauth-message (i18n/label :t/biometric-auth-confirm-message)) + :confirm-button-text (i18n/label :t/biometric-auth-confirm-try-again) + :cancel-button-text (i18n/label :t/biometric-auth-confirm-logout) + :on-accept #(biometric/authenticate nil + on-biometric-auth-result + authentication-options) + :on-cancel #(re-frame/dispatch [:multiaccounts.logout.ui/logout-confirmed])})))) -(fx/defn on-return-from-background [{:keys [db now] :as cofx}] +(fx/defn on-return-from-background + [{:keys [db now] :as cofx}] (let [app-in-background-since (get db :app-in-background-since) - signed-up? (get-in db [:multiaccount :signed-up?]) - biometric-auth? (= (:auth-method db) "biometric") - requires-bio-auth (and - signed-up? - biometric-auth? - (some? app-in-background-since) - (>= (- now app-in-background-since) - constants/ms-in-bg-for-require-bioauth))] + signed-up? (get-in db [:multiaccount :signed-up?]) + biometric-auth? (= (:auth-method db) "biometric") + requires-bio-auth (and + signed-up? + biometric-auth? + (some? app-in-background-since) + (>= (- now app-in-background-since) + constants/ms-in-bg-for-require-bioauth))] (fx/merge cofx {:db (dissoc db :app-in-background-since)} (mailserver/process-next-messages-request) @@ -160,7 +166,7 @@ {:events [:app-state-change]} [{:keys [db] :as cofx} state] (let [app-coming-from-background? (= state "active") - app-going-in-background? (= state "background")] + app-going-in-background? (= state "background")] (fx/merge cofx {::app-state-change-fx state :db (assoc db :app-state state)} @@ -193,7 +199,7 @@ (= :chat view-id) {::async-storage/set! {:chat-id (get-in cofx [:db :current-chat-id]) :key-uid (get-in cofx [:db :multiaccount :key-uid])} - :db (assoc db :screens/was-focused-once? true)} + :db (assoc db :screens/was-focused-once? true)} (= :login view-id) {} @@ -204,16 +210,16 @@ :else {::async-storage/set! {:chat-id nil :key-uid nil} - :db (assoc db :screens/was-focused-once? true)}) + :db (assoc db :screens/was-focused-once? true)}) #(case view-id - :keycard-settings (keycard/settings-screen-did-load %) - :reset-card (keycard/reset-card-screen-did-load %) - :enter-pin-settings (keycard/enter-pin-screen-did-load %) - :keycard-login-pin (keycard/login-pin-screen-did-load %) - :add-new-account-pin (keycard/enter-pin-screen-did-load %) + :keycard-settings (keycard/settings-screen-did-load %) + :reset-card (keycard/reset-card-screen-did-load %) + :enter-pin-settings (keycard/enter-pin-screen-did-load %) + :keycard-login-pin (keycard/login-pin-screen-did-load %) + :add-new-account-pin (keycard/enter-pin-screen-did-load %) :keycard-authentication-method (keycard/authentication-method-screen-did-load %) - :multiaccounts (keycard/multiaccounts-screen-did-load %) - :wallet (wallet/wallet-will-focus %) + :multiaccounts (keycard/multiaccounts-screen-did-load %) + :wallet (wallet/wallet-will-focus %) nil))) ;;TODO :replace by named events @@ -240,9 +246,11 @@ [{:keys [db]} path v] {:db (assoc-in db path v)}) -(defn on-ramp<-rpc [on-ramp] - (clojure.set/rename-keys on-ramp {:logoUrl :logo-url - :siteUrl :site-url})) +(defn on-ramp<-rpc + [on-ramp] + (clojure.set/rename-keys on-ramp + {:logoUrl :logo-url + :siteUrl :site-url})) (fx/defn crypto-loaded-event {:events [::crypto-loaded]} @@ -275,7 +283,8 @@ :global? true/false (close information box across all profiles)}]" []) -(defn information-box-id-hash [id public-key global?] +(defn information-box-id-hash + [id public-key global?] (if global? (hash id) (hash (str public-key id)))) @@ -286,15 +295,18 @@ (let [public-key (get-in db [:multiaccount :public-key]) hash (information-box-id-hash id public-key global?)] {::async-storage/set! {hash true} - :db (assoc-in db [:information-box-states id] true)})) + :db (assoc-in db [:information-box-states id] true)})) (fx/defn information-box-states-loaded {:events [:information-box-states-loaded]} [{:keys [db]} hashes states] - {:db (assoc db :information-box-states (reduce - (fn [acc [id hash]] - (assoc acc id (get states hash))) - {} hashes))}) + {:db (assoc db + :information-box-states + (reduce + (fn [acc [id hash]] + (assoc acc id (get states hash))) + {} + hashes))}) (fx/defn load-information-box-states {:events [:load-information-box-states]} @@ -302,11 +314,14 @@ (let [public-key (get-in db [:multiaccount :public-key]) {:keys [keys hashes]} (reduce (fn [acc {:keys [id global?]}] (let [hash (information-box-id-hash - id public-key global?)] + id + public-key + global?)] (-> acc (assoc-in [:hashes id] hash) (update :keys #(conj % hash))))) - {} closable-information-boxes)] + {} + closable-information-boxes)] {::async-storage/get {:keys keys :cb #(re-frame/dispatch [:information-box-states-loaded hashes %])}})) diff --git a/src/status_im/fleet/core.cljs b/src/status_im/fleet/core.cljs index 56f826c852..c7e1df590f 100644 --- a/src/status_im/fleet/core.cljs +++ b/src/status_im/fleet/core.cljs @@ -14,10 +14,10 @@ (defn format-mailserver [mailserver address] - {:id mailserver - :name (name mailserver) + {:id mailserver + :name (name mailserver) :password constants/mailserver-password - :address address}) + :address address}) (defn format-mailservers [mailservers] @@ -26,7 +26,8 @@ {} mailservers)) -(defn default-mailservers [db] +(defn default-mailservers + [db] (reduce (fn [acc [fleet node-by-type]] (assoc acc fleet (format-mailservers (:mail node-by-type)))) {} @@ -44,7 +45,8 @@ #(re-frame/dispatch [:fleet.ui/save-fleet-confirmed (keyword fleet)]) :on-cancel nil}}) -(defn nodes->fleet [nodes] +(defn nodes->fleet + [nodes] (letfn [(format-nodes [nodes] (reduce (fn [acc n] (assoc acc @@ -52,21 +54,23 @@ n)) {} nodes))] - {:boot (format-nodes nodes) - :mail (format-nodes nodes) + {:boot (format-nodes nodes) + :mail (format-nodes nodes) :whisper (format-nodes nodes)})) -(fx/defn set-nodes [{:keys [db]} fleet nodes] +(fx/defn set-nodes + [{:keys [db]} fleet nodes] {:db (-> db (assoc-in [:custom-fleets fleet] (nodes->fleet nodes)) - (assoc-in [:mailserver/mailservers fleet] (format-mailservers - (reduce - (fn [acc e] - (assoc acc - (keyword e) - e)) - {} - nodes))))}) + (assoc-in [:mailserver/mailservers fleet] + (format-mailservers + (reduce + (fn [acc e] + (assoc acc + (keyword e) + e)) + {} + nodes))))}) (fx/defn save {:events [:fleet.ui/save-fleet-confirmed]} diff --git a/src/status_im/fleet/core_test.cljs b/src/status_im/fleet/core_test.cljs index 362e48854e..8e8f351d57 100644 --- a/src/status_im/fleet/core_test.cljs +++ b/src/status_im/fleet/core_test.cljs @@ -14,18 +14,18 @@ (keys (node/fleets {}))))))) (testing "passing a custom fleet" (testing "it sets the custom fleet" - (is (= {:mail {"a" "a"} + (is (= {:mail {"a" "a"} :whisper {"w" "w"} - :boot {"b" "b"}} + :boot {"b" "b"}} (:custom-fleet (node/fleets {:custom-fleets {:custom-fleet - {:mail {"a" "a"} + {:mail {"a" "a"} :whisper {"w" "w"} - :boot {"b" "b"}}}}))))))) + :boot {"b" "b"}}}}))))))) (deftest set-nodes-test (testing "set-nodes" - (let [actual (fleet/set-nodes {:db {}} :test-fleet ["a" "b" "c"]) + (let [actual (fleet/set-nodes {:db {}} :test-fleet ["a" "b" "c"]) actual-custom-fleet (get-in actual [:db :custom-fleets]) actual-mailservers (get-in actual [:db :mailserver/mailservers :test-fleet])] (testing "it sets the custom fleet in the db" @@ -33,16 +33,16 @@ (testing "it sets the custom mailservers in the db" (is actual-mailservers)) (testing "it correctly formats mailservers" - (is (= {:a {:id :a - :name "a" + (is (= {:a {:id :a + :name "a" :password constants/mailserver-password - :address "a"} - :b {:id :b - :name "b" - :password constants/mailserver-password - :address "b"} - :c {:id :c - :name "c" + :address "a"} + :b {:id :b + :name "b" :password constants/mailserver-password - :address "c"}} + :address "b"} + :c {:id :c + :name "c" + :password constants/mailserver-password + :address "c"}} actual-mailservers)))))) diff --git a/src/status_im/fleet/default_fleet.cljs b/src/status_im/fleet/default_fleet.cljs index df2466495d..8a212cb3f3 100644 --- a/src/status_im/fleet/default_fleet.cljs +++ b/src/status_im/fleet/default_fleet.cljs @@ -1,5 +1,4 @@ -(ns status-im.fleet.default-fleet - (:require-macros [status-im.utils.slurp :refer [slurp]])) +(ns status-im.fleet.default-fleet (:require-macros [status-im.utils.slurp :refer [slurp]])) (def default-fleets (slurp "resources/config/fleets.json")) \ No newline at end of file diff --git a/src/status_im/goog/i18n.cljs b/src/status_im/goog/i18n.cljs index 7a7c557840..388498f4e0 100644 --- a/src/status_im/goog/i18n.cljs +++ b/src/status_im/goog/i18n.cljs @@ -1,243 +1,244 @@ (ns status-im.goog.i18n - (:require [clojure.string :as string] - goog.i18n.DateTimeSymbols - goog.i18n.DateTimeSymbolsType - goog.i18n.DateTimeSymbols_af - goog.i18n.DateTimeSymbols_am - goog.i18n.DateTimeSymbols_ar - goog.i18n.DateTimeSymbols_ar_DZ - goog.i18n.DateTimeSymbols_ar_EG - goog.i18n.DateTimeSymbols_az - goog.i18n.DateTimeSymbols_be - goog.i18n.DateTimeSymbols_bg - goog.i18n.DateTimeSymbols_bn - goog.i18n.DateTimeSymbols_br - goog.i18n.DateTimeSymbols_bs - goog.i18n.DateTimeSymbols_ca - goog.i18n.DateTimeSymbols_chr - goog.i18n.DateTimeSymbols_cs - goog.i18n.DateTimeSymbols_cy - goog.i18n.DateTimeSymbols_da - goog.i18n.DateTimeSymbols_de - goog.i18n.DateTimeSymbols_de_AT - goog.i18n.DateTimeSymbols_de_CH - goog.i18n.DateTimeSymbols_el - goog.i18n.DateTimeSymbols_en - goog.i18n.DateTimeSymbols_en_AU - goog.i18n.DateTimeSymbols_en_CA - goog.i18n.DateTimeSymbols_en_GB - goog.i18n.DateTimeSymbols_en_IE - goog.i18n.DateTimeSymbols_en_IN - goog.i18n.DateTimeSymbols_en_ISO - goog.i18n.DateTimeSymbols_en_SG - goog.i18n.DateTimeSymbols_en_US - goog.i18n.DateTimeSymbols_en_ZA - goog.i18n.DateTimeSymbols_es - goog.i18n.DateTimeSymbols_es_419 - goog.i18n.DateTimeSymbols_es_ES - goog.i18n.DateTimeSymbols_es_MX - goog.i18n.DateTimeSymbols_es_US - goog.i18n.DateTimeSymbols_et - goog.i18n.DateTimeSymbols_eu - goog.i18n.DateTimeSymbols_fa - goog.i18n.DateTimeSymbols_fi - goog.i18n.DateTimeSymbols_fil - goog.i18n.DateTimeSymbols_fr - goog.i18n.DateTimeSymbols_fr_CA - goog.i18n.DateTimeSymbols_ga - goog.i18n.DateTimeSymbols_gl - goog.i18n.DateTimeSymbols_gsw - goog.i18n.DateTimeSymbols_gu - goog.i18n.DateTimeSymbols_haw - goog.i18n.DateTimeSymbols_he - goog.i18n.DateTimeSymbols_hi - goog.i18n.DateTimeSymbols_hr - goog.i18n.DateTimeSymbols_hu - goog.i18n.DateTimeSymbols_hy - goog.i18n.DateTimeSymbols_id - goog.i18n.DateTimeSymbols_in - goog.i18n.DateTimeSymbols_is - goog.i18n.DateTimeSymbols_it - goog.i18n.DateTimeSymbols_iw - goog.i18n.DateTimeSymbols_ja - goog.i18n.DateTimeSymbols_ka - goog.i18n.DateTimeSymbols_kk - goog.i18n.DateTimeSymbols_km - goog.i18n.DateTimeSymbols_kn - goog.i18n.DateTimeSymbols_ko - goog.i18n.DateTimeSymbols_ky - goog.i18n.DateTimeSymbols_ln - goog.i18n.DateTimeSymbols_lo - goog.i18n.DateTimeSymbols_lt - goog.i18n.DateTimeSymbols_lv - goog.i18n.DateTimeSymbols_mk - goog.i18n.DateTimeSymbols_ml - goog.i18n.DateTimeSymbols_mn - goog.i18n.DateTimeSymbols_mo - goog.i18n.DateTimeSymbols_mr - goog.i18n.DateTimeSymbols_ms - goog.i18n.DateTimeSymbols_mt - goog.i18n.DateTimeSymbols_my - goog.i18n.DateTimeSymbols_nb - goog.i18n.DateTimeSymbols_ne - goog.i18n.DateTimeSymbols_nl - goog.i18n.DateTimeSymbols_no - goog.i18n.DateTimeSymbols_no_NO - goog.i18n.DateTimeSymbols_or - goog.i18n.DateTimeSymbols_pa - goog.i18n.DateTimeSymbols_pl - goog.i18n.DateTimeSymbols_pt - goog.i18n.DateTimeSymbols_pt_BR - goog.i18n.DateTimeSymbols_pt_PT - goog.i18n.DateTimeSymbols_ro - goog.i18n.DateTimeSymbols_ru - goog.i18n.DateTimeSymbols_sh - goog.i18n.DateTimeSymbols_si - goog.i18n.DateTimeSymbols_sk - goog.i18n.DateTimeSymbols_sl - goog.i18n.DateTimeSymbols_sq - goog.i18n.DateTimeSymbols_sr - goog.i18n.DateTimeSymbols_sr_Latn - goog.i18n.DateTimeSymbols_sv - goog.i18n.DateTimeSymbols_sw - goog.i18n.DateTimeSymbols_ta - goog.i18n.DateTimeSymbols_te - goog.i18n.DateTimeSymbols_th - goog.i18n.DateTimeSymbols_tl - goog.i18n.DateTimeSymbols_tr - goog.i18n.DateTimeSymbols_uk - goog.i18n.DateTimeSymbols_ur - goog.i18n.DateTimeSymbols_uz - goog.i18n.DateTimeSymbols_vi - goog.i18n.DateTimeSymbols_zh - goog.i18n.DateTimeSymbols_zh_CN - goog.i18n.DateTimeSymbols_zh_HK - goog.i18n.DateTimeSymbols_zh_TW - goog.i18n.DateTimeSymbols_zu - goog.i18n.CompactNumberFormatSymbols - goog.i18n.CompactNumberFormatSymbols_af - goog.i18n.CompactNumberFormatSymbols_am - goog.i18n.CompactNumberFormatSymbols_ar - goog.i18n.CompactNumberFormatSymbols_ar_DZ - goog.i18n.CompactNumberFormatSymbols_ar_EG - goog.i18n.CompactNumberFormatSymbols_az - goog.i18n.CompactNumberFormatSymbols_be - goog.i18n.CompactNumberFormatSymbols_bg - goog.i18n.CompactNumberFormatSymbols_bn - goog.i18n.CompactNumberFormatSymbols_br - goog.i18n.CompactNumberFormatSymbols_bs - goog.i18n.CompactNumberFormatSymbols_ca - goog.i18n.CompactNumberFormatSymbols_chr - goog.i18n.CompactNumberFormatSymbols_cs - goog.i18n.CompactNumberFormatSymbols_cy - goog.i18n.CompactNumberFormatSymbols_da - goog.i18n.CompactNumberFormatSymbols_de - goog.i18n.CompactNumberFormatSymbols_de_AT - goog.i18n.CompactNumberFormatSymbols_de_CH - goog.i18n.CompactNumberFormatSymbols_el - goog.i18n.CompactNumberFormatSymbols_en - goog.i18n.CompactNumberFormatSymbols_en_AU - goog.i18n.CompactNumberFormatSymbols_en_CA - goog.i18n.CompactNumberFormatSymbols_en_GB - goog.i18n.CompactNumberFormatSymbols_en_IE - goog.i18n.CompactNumberFormatSymbols_en_IN - goog.i18n.CompactNumberFormatSymbols_en_SG - goog.i18n.CompactNumberFormatSymbols_en_US - goog.i18n.CompactNumberFormatSymbols_en_ZA - goog.i18n.CompactNumberFormatSymbols_es - goog.i18n.CompactNumberFormatSymbols_es_419 - goog.i18n.CompactNumberFormatSymbols_es_ES - goog.i18n.CompactNumberFormatSymbols_es_MX - goog.i18n.CompactNumberFormatSymbols_es_US - goog.i18n.CompactNumberFormatSymbols_et - goog.i18n.CompactNumberFormatSymbols_eu - goog.i18n.CompactNumberFormatSymbols_fa - goog.i18n.CompactNumberFormatSymbols_fi - goog.i18n.CompactNumberFormatSymbols_fil - goog.i18n.CompactNumberFormatSymbols_fr - goog.i18n.CompactNumberFormatSymbols_fr_CA - goog.i18n.CompactNumberFormatSymbols_ga - goog.i18n.CompactNumberFormatSymbols_gl - goog.i18n.CompactNumberFormatSymbols_gsw - goog.i18n.CompactNumberFormatSymbols_gu - goog.i18n.CompactNumberFormatSymbols_haw - goog.i18n.CompactNumberFormatSymbols_he - goog.i18n.CompactNumberFormatSymbols_hi - goog.i18n.CompactNumberFormatSymbols_hr - goog.i18n.CompactNumberFormatSymbols_hu - goog.i18n.CompactNumberFormatSymbols_hy - goog.i18n.CompactNumberFormatSymbols_id - goog.i18n.CompactNumberFormatSymbols_in - goog.i18n.CompactNumberFormatSymbols_is - goog.i18n.CompactNumberFormatSymbols_it - goog.i18n.CompactNumberFormatSymbols_iw - goog.i18n.CompactNumberFormatSymbols_ja - goog.i18n.CompactNumberFormatSymbols_ka - goog.i18n.CompactNumberFormatSymbols_kk - goog.i18n.CompactNumberFormatSymbols_km - goog.i18n.CompactNumberFormatSymbols_kn - goog.i18n.CompactNumberFormatSymbols_ko - goog.i18n.CompactNumberFormatSymbols_ky - goog.i18n.CompactNumberFormatSymbols_ln - goog.i18n.CompactNumberFormatSymbols_lo - goog.i18n.CompactNumberFormatSymbols_lt - goog.i18n.CompactNumberFormatSymbols_lv - goog.i18n.CompactNumberFormatSymbols_mk - goog.i18n.CompactNumberFormatSymbols_ml - goog.i18n.CompactNumberFormatSymbols_mn - goog.i18n.CompactNumberFormatSymbols_mo - goog.i18n.CompactNumberFormatSymbols_mr - goog.i18n.CompactNumberFormatSymbols_ms - goog.i18n.CompactNumberFormatSymbols_mt - goog.i18n.CompactNumberFormatSymbols_my - goog.i18n.CompactNumberFormatSymbols_nb - goog.i18n.CompactNumberFormatSymbols_ne - goog.i18n.CompactNumberFormatSymbols_nl - goog.i18n.CompactNumberFormatSymbols_no - goog.i18n.CompactNumberFormatSymbols_no_NO - goog.i18n.CompactNumberFormatSymbols_or - goog.i18n.CompactNumberFormatSymbols_pa - goog.i18n.CompactNumberFormatSymbols_pl - goog.i18n.CompactNumberFormatSymbols_pt - goog.i18n.CompactNumberFormatSymbols_pt_BR - goog.i18n.CompactNumberFormatSymbols_pt_PT - goog.i18n.CompactNumberFormatSymbols_ro - goog.i18n.CompactNumberFormatSymbols_ru - goog.i18n.CompactNumberFormatSymbols_sh - goog.i18n.CompactNumberFormatSymbols_si - goog.i18n.CompactNumberFormatSymbols_sk - goog.i18n.CompactNumberFormatSymbols_sl - goog.i18n.CompactNumberFormatSymbols_sq - goog.i18n.CompactNumberFormatSymbols_sr - goog.i18n.CompactNumberFormatSymbols_sr_Latn - goog.i18n.CompactNumberFormatSymbols_sv - goog.i18n.CompactNumberFormatSymbols_sw - goog.i18n.CompactNumberFormatSymbols_ta - goog.i18n.CompactNumberFormatSymbols_te - goog.i18n.CompactNumberFormatSymbols_th - goog.i18n.CompactNumberFormatSymbols_tl - goog.i18n.CompactNumberFormatSymbols_tr - goog.i18n.CompactNumberFormatSymbols_uk - goog.i18n.CompactNumberFormatSymbols_ur - goog.i18n.CompactNumberFormatSymbols_uz - goog.i18n.CompactNumberFormatSymbols_vi - goog.i18n.CompactNumberFormatSymbols_zh - goog.i18n.CompactNumberFormatSymbols_zh_CN - goog.i18n.CompactNumberFormatSymbols_zh_HK - goog.i18n.CompactNumberFormatSymbols_zh_TW - goog.i18n.CompactNumberFormatSymbols_zu - goog.i18n.currency - goog.i18n.currency.CurrencyInfo - goog.i18n.currency.CurrencyInfoTier2 - goog.i18n.DateTimeFormat - goog.i18n.DateTimeFormat.Format - goog.i18n.MessageFormat - goog.i18n.NumberFormat - goog.i18n.NumberFormat.CurrencyStyle - goog.i18n.NumberFormat.Format - goog.i18n.ordinalRules - goog.i18n.pluralRules - goog.i18n.TimeZone)) + (:require + [clojure.string :as string] + goog.i18n.CompactNumberFormatSymbols + goog.i18n.CompactNumberFormatSymbols_af + goog.i18n.CompactNumberFormatSymbols_am + goog.i18n.CompactNumberFormatSymbols_ar + goog.i18n.CompactNumberFormatSymbols_ar_DZ + goog.i18n.CompactNumberFormatSymbols_ar_EG + goog.i18n.CompactNumberFormatSymbols_az + goog.i18n.CompactNumberFormatSymbols_be + goog.i18n.CompactNumberFormatSymbols_bg + goog.i18n.CompactNumberFormatSymbols_bn + goog.i18n.CompactNumberFormatSymbols_br + goog.i18n.CompactNumberFormatSymbols_bs + goog.i18n.CompactNumberFormatSymbols_ca + goog.i18n.CompactNumberFormatSymbols_chr + goog.i18n.CompactNumberFormatSymbols_cs + goog.i18n.CompactNumberFormatSymbols_cy + goog.i18n.CompactNumberFormatSymbols_da + goog.i18n.CompactNumberFormatSymbols_de + goog.i18n.CompactNumberFormatSymbols_de_AT + goog.i18n.CompactNumberFormatSymbols_de_CH + goog.i18n.CompactNumberFormatSymbols_el + goog.i18n.CompactNumberFormatSymbols_en + goog.i18n.CompactNumberFormatSymbols_en_AU + goog.i18n.CompactNumberFormatSymbols_en_CA + goog.i18n.CompactNumberFormatSymbols_en_GB + goog.i18n.CompactNumberFormatSymbols_en_IE + goog.i18n.CompactNumberFormatSymbols_en_IN + goog.i18n.CompactNumberFormatSymbols_en_SG + goog.i18n.CompactNumberFormatSymbols_en_US + goog.i18n.CompactNumberFormatSymbols_en_ZA + goog.i18n.CompactNumberFormatSymbols_es + goog.i18n.CompactNumberFormatSymbols_es_419 + goog.i18n.CompactNumberFormatSymbols_es_ES + goog.i18n.CompactNumberFormatSymbols_es_MX + goog.i18n.CompactNumberFormatSymbols_es_US + goog.i18n.CompactNumberFormatSymbols_et + goog.i18n.CompactNumberFormatSymbols_eu + goog.i18n.CompactNumberFormatSymbols_fa + goog.i18n.CompactNumberFormatSymbols_fi + goog.i18n.CompactNumberFormatSymbols_fil + goog.i18n.CompactNumberFormatSymbols_fr + goog.i18n.CompactNumberFormatSymbols_fr_CA + goog.i18n.CompactNumberFormatSymbols_ga + goog.i18n.CompactNumberFormatSymbols_gl + goog.i18n.CompactNumberFormatSymbols_gsw + goog.i18n.CompactNumberFormatSymbols_gu + goog.i18n.CompactNumberFormatSymbols_haw + goog.i18n.CompactNumberFormatSymbols_he + goog.i18n.CompactNumberFormatSymbols_hi + goog.i18n.CompactNumberFormatSymbols_hr + goog.i18n.CompactNumberFormatSymbols_hu + goog.i18n.CompactNumberFormatSymbols_hy + goog.i18n.CompactNumberFormatSymbols_id + goog.i18n.CompactNumberFormatSymbols_in + goog.i18n.CompactNumberFormatSymbols_is + goog.i18n.CompactNumberFormatSymbols_it + goog.i18n.CompactNumberFormatSymbols_iw + goog.i18n.CompactNumberFormatSymbols_ja + goog.i18n.CompactNumberFormatSymbols_ka + goog.i18n.CompactNumberFormatSymbols_kk + goog.i18n.CompactNumberFormatSymbols_km + goog.i18n.CompactNumberFormatSymbols_kn + goog.i18n.CompactNumberFormatSymbols_ko + goog.i18n.CompactNumberFormatSymbols_ky + goog.i18n.CompactNumberFormatSymbols_ln + goog.i18n.CompactNumberFormatSymbols_lo + goog.i18n.CompactNumberFormatSymbols_lt + goog.i18n.CompactNumberFormatSymbols_lv + goog.i18n.CompactNumberFormatSymbols_mk + goog.i18n.CompactNumberFormatSymbols_ml + goog.i18n.CompactNumberFormatSymbols_mn + goog.i18n.CompactNumberFormatSymbols_mo + goog.i18n.CompactNumberFormatSymbols_mr + goog.i18n.CompactNumberFormatSymbols_ms + goog.i18n.CompactNumberFormatSymbols_mt + goog.i18n.CompactNumberFormatSymbols_my + goog.i18n.CompactNumberFormatSymbols_nb + goog.i18n.CompactNumberFormatSymbols_ne + goog.i18n.CompactNumberFormatSymbols_nl + goog.i18n.CompactNumberFormatSymbols_no + goog.i18n.CompactNumberFormatSymbols_no_NO + goog.i18n.CompactNumberFormatSymbols_or + goog.i18n.CompactNumberFormatSymbols_pa + goog.i18n.CompactNumberFormatSymbols_pl + goog.i18n.CompactNumberFormatSymbols_pt + goog.i18n.CompactNumberFormatSymbols_pt_BR + goog.i18n.CompactNumberFormatSymbols_pt_PT + goog.i18n.CompactNumberFormatSymbols_ro + goog.i18n.CompactNumberFormatSymbols_ru + goog.i18n.CompactNumberFormatSymbols_sh + goog.i18n.CompactNumberFormatSymbols_si + goog.i18n.CompactNumberFormatSymbols_sk + goog.i18n.CompactNumberFormatSymbols_sl + goog.i18n.CompactNumberFormatSymbols_sq + goog.i18n.CompactNumberFormatSymbols_sr + goog.i18n.CompactNumberFormatSymbols_sr_Latn + goog.i18n.CompactNumberFormatSymbols_sv + goog.i18n.CompactNumberFormatSymbols_sw + goog.i18n.CompactNumberFormatSymbols_ta + goog.i18n.CompactNumberFormatSymbols_te + goog.i18n.CompactNumberFormatSymbols_th + goog.i18n.CompactNumberFormatSymbols_tl + goog.i18n.CompactNumberFormatSymbols_tr + goog.i18n.CompactNumberFormatSymbols_uk + goog.i18n.CompactNumberFormatSymbols_ur + goog.i18n.CompactNumberFormatSymbols_uz + goog.i18n.CompactNumberFormatSymbols_vi + goog.i18n.CompactNumberFormatSymbols_zh + goog.i18n.CompactNumberFormatSymbols_zh_CN + goog.i18n.CompactNumberFormatSymbols_zh_HK + goog.i18n.CompactNumberFormatSymbols_zh_TW + goog.i18n.CompactNumberFormatSymbols_zu + goog.i18n.currency + goog.i18n.currency.CurrencyInfo + goog.i18n.currency.CurrencyInfoTier2 + goog.i18n.DateTimeFormat + goog.i18n.DateTimeFormat.Format + goog.i18n.DateTimeSymbols + goog.i18n.DateTimeSymbols_af + goog.i18n.DateTimeSymbols_am + goog.i18n.DateTimeSymbols_ar + goog.i18n.DateTimeSymbols_ar_DZ + goog.i18n.DateTimeSymbols_ar_EG + goog.i18n.DateTimeSymbols_az + goog.i18n.DateTimeSymbols_be + goog.i18n.DateTimeSymbols_bg + goog.i18n.DateTimeSymbols_bn + goog.i18n.DateTimeSymbols_br + goog.i18n.DateTimeSymbols_bs + goog.i18n.DateTimeSymbols_ca + goog.i18n.DateTimeSymbols_chr + goog.i18n.DateTimeSymbols_cs + goog.i18n.DateTimeSymbols_cy + goog.i18n.DateTimeSymbols_da + goog.i18n.DateTimeSymbols_de + goog.i18n.DateTimeSymbols_de_AT + goog.i18n.DateTimeSymbols_de_CH + goog.i18n.DateTimeSymbols_el + goog.i18n.DateTimeSymbols_en + goog.i18n.DateTimeSymbols_en_AU + goog.i18n.DateTimeSymbols_en_CA + goog.i18n.DateTimeSymbols_en_GB + goog.i18n.DateTimeSymbols_en_IE + goog.i18n.DateTimeSymbols_en_IN + goog.i18n.DateTimeSymbols_en_ISO + goog.i18n.DateTimeSymbols_en_SG + goog.i18n.DateTimeSymbols_en_US + goog.i18n.DateTimeSymbols_en_ZA + goog.i18n.DateTimeSymbols_es + goog.i18n.DateTimeSymbols_es_419 + goog.i18n.DateTimeSymbols_es_ES + goog.i18n.DateTimeSymbols_es_MX + goog.i18n.DateTimeSymbols_es_US + goog.i18n.DateTimeSymbols_et + goog.i18n.DateTimeSymbols_eu + goog.i18n.DateTimeSymbols_fa + goog.i18n.DateTimeSymbols_fi + goog.i18n.DateTimeSymbols_fil + goog.i18n.DateTimeSymbols_fr + goog.i18n.DateTimeSymbols_fr_CA + goog.i18n.DateTimeSymbols_ga + goog.i18n.DateTimeSymbols_gl + goog.i18n.DateTimeSymbols_gsw + goog.i18n.DateTimeSymbols_gu + goog.i18n.DateTimeSymbols_haw + goog.i18n.DateTimeSymbols_he + goog.i18n.DateTimeSymbols_hi + goog.i18n.DateTimeSymbols_hr + goog.i18n.DateTimeSymbols_hu + goog.i18n.DateTimeSymbols_hy + goog.i18n.DateTimeSymbols_id + goog.i18n.DateTimeSymbols_in + goog.i18n.DateTimeSymbols_is + goog.i18n.DateTimeSymbols_it + goog.i18n.DateTimeSymbols_iw + goog.i18n.DateTimeSymbols_ja + goog.i18n.DateTimeSymbols_ka + goog.i18n.DateTimeSymbols_kk + goog.i18n.DateTimeSymbols_km + goog.i18n.DateTimeSymbols_kn + goog.i18n.DateTimeSymbols_ko + goog.i18n.DateTimeSymbols_ky + goog.i18n.DateTimeSymbols_ln + goog.i18n.DateTimeSymbols_lo + goog.i18n.DateTimeSymbols_lt + goog.i18n.DateTimeSymbols_lv + goog.i18n.DateTimeSymbols_mk + goog.i18n.DateTimeSymbols_ml + goog.i18n.DateTimeSymbols_mn + goog.i18n.DateTimeSymbols_mo + goog.i18n.DateTimeSymbols_mr + goog.i18n.DateTimeSymbols_ms + goog.i18n.DateTimeSymbols_mt + goog.i18n.DateTimeSymbols_my + goog.i18n.DateTimeSymbols_nb + goog.i18n.DateTimeSymbols_ne + goog.i18n.DateTimeSymbols_nl + goog.i18n.DateTimeSymbols_no + goog.i18n.DateTimeSymbols_no_NO + goog.i18n.DateTimeSymbols_or + goog.i18n.DateTimeSymbols_pa + goog.i18n.DateTimeSymbols_pl + goog.i18n.DateTimeSymbols_pt + goog.i18n.DateTimeSymbols_pt_BR + goog.i18n.DateTimeSymbols_pt_PT + goog.i18n.DateTimeSymbols_ro + goog.i18n.DateTimeSymbols_ru + goog.i18n.DateTimeSymbols_sh + goog.i18n.DateTimeSymbols_si + goog.i18n.DateTimeSymbols_sk + goog.i18n.DateTimeSymbols_sl + goog.i18n.DateTimeSymbols_sq + goog.i18n.DateTimeSymbols_sr + goog.i18n.DateTimeSymbols_sr_Latn + goog.i18n.DateTimeSymbols_sv + goog.i18n.DateTimeSymbols_sw + goog.i18n.DateTimeSymbols_ta + goog.i18n.DateTimeSymbols_te + goog.i18n.DateTimeSymbols_th + goog.i18n.DateTimeSymbols_tl + goog.i18n.DateTimeSymbols_tr + goog.i18n.DateTimeSymbols_uk + goog.i18n.DateTimeSymbols_ur + goog.i18n.DateTimeSymbols_uz + goog.i18n.DateTimeSymbols_vi + goog.i18n.DateTimeSymbols_zh + goog.i18n.DateTimeSymbols_zh_CN + goog.i18n.DateTimeSymbols_zh_HK + goog.i18n.DateTimeSymbols_zh_TW + goog.i18n.DateTimeSymbols_zu + goog.i18n.DateTimeSymbolsType + goog.i18n.MessageFormat + goog.i18n.NumberFormat + goog.i18n.NumberFormat.CurrencyStyle + goog.i18n.NumberFormat.Format + goog.i18n.ordinalRules + goog.i18n.pluralRules + goog.i18n.TimeZone)) (def locales {"af" ^js goog.i18n.DateTimeSymbols_af @@ -354,7 +355,8 @@ "zu" ^js goog.i18n.DateTimeSymbols_zu}) ;; xx-YY locale, xx locale or en fallback -(defn locale-symbols [locale-name] +(defn locale-symbols + [locale-name] (if-let [loc (get locales locale-name)] loc (let [name-first (string/replace (or locale-name "") #"-.*$" "") @@ -362,7 +364,8 @@ (or loc goog.i18n.DateTimeSymbols_en)))) ;; get formatter for current locale symbols and format function -(defn mk-fmt [locale format-fn] +(defn mk-fmt + [locale format-fn] (let [locsym (locale-symbols locale)] (goog.i18n.DateTimeFormat. (format-fn locsym) locsym))) @@ -436,7 +439,8 @@ nfs (or (get currency-code-to-nfs-map currency-code) ^js goog.i18n.NumberFormatSymbols_en)] (.format - ^js (new ^js goog.i18n.NumberFormat - (let [pattern (.-CURRENCY_PATTERN ^js nfs)] - (string/replace pattern #"\s*¤\s*" ""))) + ^js + (new ^js goog.i18n.NumberFormat + (let [pattern (.-CURRENCY_PATTERN ^js nfs)] + (string/replace pattern #"\s*¤\s*" ""))) value))) \ No newline at end of file diff --git a/src/status_im/group_chats/core.cljs b/src/status_im/group_chats/core.cljs index f5115865a7..035a654474 100644 --- a/src/status_im/group_chats/core.cljs +++ b/src/status_im/group_chats/core.cljs @@ -4,12 +4,12 @@ [clojure.string :as string] [re-frame.core :as re-frame] [status-im.chat.models :as models.chat] - [status-im.ethereum.json-rpc :as json-rpc] - [status-im2.navigation.events :as navigation] - [status-im.utils.fx :as fx] [status-im.constants :as constants] + [status-im.ethereum.json-rpc :as json-rpc] [status-im.i18n.i18n :as i18n] - [status-im2.contexts.activity-center.events :as activity-center])) + [status-im.utils.fx :as fx] + [status-im2.contexts.activity-center.events :as activity-center] + [status-im2.navigation.events :as navigation])) (fx/defn navigate-chat-updated {:events [:navigate-chat-updated]} @@ -30,7 +30,8 @@ {:events [:chat-updated]} [_ response do-not-navigate?] {:dispatch-n [[:sanitize-messages-and-process-response response] - (when-not do-not-navigate? [:navigate-chat-updated (.-id (aget (.-chats response) 0))])]}) + (when-not do-not-navigate? + [:navigate-chat-updated (.-id (aget (.-chats response) 0))])]}) (fx/defn remove-member "Format group update message and sign membership" @@ -115,7 +116,8 @@ (def not-blank? (complement string/blank?)) -(defn- valid-name? [name] +(defn- valid-name? + [name] (spec/valid? not-blank? name)) (fx/defn name-changed @@ -144,7 +146,7 @@ {:events [:send-group-chat-membership-request]} [{{:keys [current-chat-id chats] :as db} :db :as cofx}] (let [{:keys [invitation-admin]} (get chats current-chat-id) - message (get-in db [:chat/memberships current-chat-id :message])] + message (get-in db [:chat/memberships current-chat-id :message])] {:db (assoc-in db [:chat/memberships current-chat-id] nil) ::json-rpc/call [{:method "wakuext_sendGroupChatInvitationRequest" :params [nil current-chat-id invitation-admin message] @@ -162,12 +164,15 @@ (fx/defn handle-invitations [{db :db} invitations] - {:db (update db :group-chat/invitations #(reduce (fn [acc {:keys [id] :as inv}] - (assoc acc id inv)) - % - invitations))}) + {:db (update db + :group-chat/invitations + #(reduce (fn [acc {:keys [id] :as inv}] + (assoc acc id inv)) + % + invitations))}) -(defn member-removed? [{:keys [membership-update-events]} pk] +(defn member-removed? + [{:keys [membership-update-events]} pk] (->> membership-update-events (filter #(contains? (set (:members %)) pk)) (sort-by :clockValue >) diff --git a/src/status_im/group_chats/db.cljs b/src/status_im/group_chats/db.cljs index 8d7a607f17..936fb57e9c 100644 --- a/src/status_im/group_chats/db.cljs +++ b/src/status_im/group_chats/db.cljs @@ -21,5 +21,6 @@ from))) first)) -(defn group-chat? [chat] +(defn group-chat? + [chat] (and (:group-chat chat) (not (:public? chat)))) diff --git a/src/status_im/http/core.cljs b/src/status_im/http/core.cljs index 00ab4c1baf..c441d06e1f 100644 --- a/src/status_im/http/core.cljs +++ b/src/status_im/http/core.cljs @@ -1,6 +1,6 @@ (ns status-im.http.core - (:require [status-im.utils.http :as http] - [re-frame.core :as re-frame])) + (:require [re-frame.core :as re-frame] + [status-im.utils.http :as http])) (re-frame/reg-fx :http-get @@ -22,5 +22,5 @@ (fn [{:keys [url data response-validator on-success on-error timeout-ms opts]}] (let [all-opts (assoc opts :valid-response? response-validator - :timeout-ms timeout-ms)] + :timeout-ms timeout-ms)] (http/post url data on-success on-error all-opts)))) diff --git a/src/status_im/i18n/i18n.cljs b/src/status_im/i18n/i18n.cljs index e9ca9e80f5..96d7457c8a 100644 --- a/src/status_im/i18n/i18n.cljs +++ b/src/status_im/i18n/i18n.cljs @@ -1,10 +1,10 @@ (ns status-im.i18n.i18n - (:require - [clojure.string :as string] - ["i18n-js" :as i18n] - [status-im.i18n.i18n-resources :as i18n-resources] - [status-im.goog.i18n :as goog.i18n])) -;;TODO (14/11/22 flexsurfer) this namespace has been moved to the root level, we keep this only for old (status 1.0) code, + (:require ["i18n-js" :as i18n] + [clojure.string :as string] + [status-im.goog.i18n :as goog.i18n] + [status-im.i18n.i18n-resources :as i18n-resources])) +;;TODO (14/11/22 flexsurfer) this namespace has been moved to the root level, we keep this only for old +;;(status 1.0) code, ;; can be removed with old code later (set! (.-locale i18n) (name i18n-resources/default-device-language)) @@ -12,13 +12,15 @@ (set! (.-defaultSeparator i18n) "/") (set! (.-translations i18n) - (clj->js i18n-resources/translations-by-locale)) + (clj->js i18n-resources/translations-by-locale)) -(defn set-language [lang] +(defn set-language + [lang] (i18n-resources/load-language lang) (set! (.-locale i18n) lang)) -;;:zh, :zh-hans-xx, :zh-hant-xx have been added until this bug will be fixed https://github.com/fnando/i18n-js/issues/460 +;;:zh, :zh-hans-xx, :zh-hant-xx have been added until this bug will be fixed +;;https://github.com/fnando/i18n-js/issues/460 (def delimeters "This function is a hack: mobile Safari doesn't support toLocaleString(), so we need to pass @@ -31,7 +33,8 @@ {:delimiter "" :separator (subs n 4 5)}))) -(defn label-number [number] +(defn label-number + [number] (when number (let [{:keys [delimiter separator]} delimeters] (.toNumber i18n @@ -43,7 +46,8 @@ (def default-option-value "") -(defn label-options [options] +(defn label-options + [options] ;; i18n ignores nil value, leading to misleading messages (into {} (for [[k v] options] [k (or v default-option-value)]))) @@ -57,7 +61,8 @@ (def label (memoize label-fn)) -(defn label-pluralize [count path & options] +(defn label-pluralize + [count path & options] (if (exists? (.t i18n)) (.p i18n count (name path) (clj->js options)) (name path))) diff --git a/src/status_im/i18n/i18n_resources.cljs b/src/status_im/i18n/i18n_resources.cljs index a6155ca31a..a27e7659c7 100644 --- a/src/status_im/i18n/i18n_resources.cljs +++ b/src/status_im/i18n/i18n_resources.cljs @@ -1,8 +1,10 @@ (ns status-im.i18n.i18n-resources - (:require [clojure.string :as string] - ["i18n-js" :as i18n-js] - ["react-native-languages" :default react-native-languages])) -;;TODO (14/11/22 flexsurfer) this namespace has been moved to the status-im2 namespace, we keep this only for old (status 1.0) code, + (:require ["i18n-js" :as i18n-js] + ["react-native-languages" :default react-native-languages] + [clojure.string :as string])) +;;TODO (14/11/22 flexsurfer) this namespace has been moved to the status-im2 namespace, we keep this +;;only +;;for old (status 1.0) code, ;; can be removed with old code later (def get-keyword @@ -11,53 +13,57 @@ (def default-device-language (get-keyword (.-language react-native-languages))) -(def languages #{:ar :bn :de :el :en :es :es_419 :es_AR :fil :fr :hi :id :in :it :ja :ko :ms :nl :pl :pt :pt_BR :ru :tr :vi :zh :zh_Hant :zh_TW}) +(def languages + #{:ar :bn :de :el :en :es :es_419 :es_AR :fil :fr :hi :id :in :it :ja :ko :ms :nl :pl :pt :pt_BR :ru + :tr :vi :zh :zh_Hant :zh_TW}) (defonce loaded-languages - (atom - (conj #{:en} default-device-language))) + (atom + (conj #{:en} default-device-language))) -(defn valid-language [lang] +(defn valid-language + [lang] (if (contains? languages lang) (get-keyword lang) - (let [parts (string/split (name lang) #"[\-\_]") - short-lang (get-keyword (str (first parts) "_" (second parts))) + (let [parts (string/split (name lang) #"[\-\_]") + short-lang (get-keyword (str (first parts) "_" (second parts))) shortest-lang (get-keyword (first parts))] (if (and (> (count parts) 2) (contains? languages short-lang)) short-lang (when (contains? languages shortest-lang) shortest-lang))))) -(defn require-translation [lang-key] +(defn require-translation + [lang-key] (when-let [lang (valid-language (get-keyword lang-key))] (case lang - :ar (js/require "../translations/ar.json") - :bn (js/require "../translations/bn.json") - :de (js/require "../translations/de.json") - :el (js/require "../translations/el.json") - :en (js/require "../translations/en.json") - :es (js/require "../translations/es.json") - :es_419 (js/require "../translations/es_419.json") - :es_AR (js/require "../translations/es_AR.json") - :fil (js/require "../translations/fil.json") - :fr (js/require "../translations/fr.json") - :hi (js/require "../translations/hi.json") - :id (js/require "../translations/id.json") - :in (js/require "../translations/id.json") - :it (js/require "../translations/it.json") - :ja (js/require "../translations/ja.json") - :ko (js/require "../translations/ko.json") - :ms (js/require "../translations/ms.json") - :nl (js/require "../translations/nl.json") - :pl (js/require "../translations/pl.json") - :pt (js/require "../translations/pt.json") - :pt_BR (js/require "../translations/pt_BR.json") - :ru (js/require "../translations/ru.json") - :tr (js/require "../translations/tr.json") - :vi (js/require "../translations/vi.json") - :zh (js/require "../translations/zh.json") - :zh_Hant (js/require "../translations/zh_hant.json") - :zh_TW (js/require "../translations/zh_TW.json")))) + :ar (js/require "../translations/ar.json") + :bn (js/require "../translations/bn.json") + :de (js/require "../translations/de.json") + :el (js/require "../translations/el.json") + :en (js/require "../translations/en.json") + :es (js/require "../translations/es.json") + :es_419 (js/require "../translations/es_419.json") + :es_AR (js/require "../translations/es_AR.json") + :fil (js/require "../translations/fil.json") + :fr (js/require "../translations/fr.json") + :hi (js/require "../translations/hi.json") + :id (js/require "../translations/id.json") + :in (js/require "../translations/id.json") + :it (js/require "../translations/it.json") + :ja (js/require "../translations/ja.json") + :ko (js/require "../translations/ko.json") + :ms (js/require "../translations/ms.json") + :nl (js/require "../translations/nl.json") + :pl (js/require "../translations/pl.json") + :pt (js/require "../translations/pt.json") + :pt_BR (js/require "../translations/pt_BR.json") + :ru (js/require "../translations/ru.json") + :tr (js/require "../translations/tr.json") + :vi (js/require "../translations/vi.json") + :zh (js/require "../translations/zh.json") + :zh_Hant (js/require "../translations/zh_hant.json") + :zh_TW (js/require "../translations/zh_TW.json")))) ;; translations (def translations-by-locale @@ -68,7 +74,8 @@ (string/replace "-" "_") get-keyword))))) -(defn load-language [lang] +(defn load-language + [lang] (when-let [lang-key (valid-language (get-keyword lang))] (when-not (contains? @loaded-languages lang-key) (aset (.-translations i18n-js) diff --git a/src/status_im/i18n/i18n_test.cljs b/src/status_im/i18n/i18n_test.cljs index e967376e5b..5db8f5b350 100644 --- a/src/status_im/i18n/i18n_test.cljs +++ b/src/status_im/i18n/i18n_test.cljs @@ -1,21 +1,26 @@ (ns status-im.i18n.i18n-test - (:require [cljs.test :refer-macros [deftest is]] - [status-im.i18n.i18n :as i18n] - [status-im.i18n.i18n-resources :as i18n-resources] + (:require [cljs.spec.alpha :as spec] + [cljs.test :refer-macros [deftest is]] [clojure.set :as set] - [cljs.spec.alpha :as spec] - [clojure.string :as string])) -;;TODO (14/11/22 flexsurfer) this namespace has been moved to the status-im2 namespace, we keep this only for old (status 1.0) code, + [clojure.string :as string] + [status-im.i18n.i18n :as i18n] + [status-im.i18n.i18n-resources :as i18n-resources])) +;;TODO (14/11/22 flexsurfer) this namespace has been moved to the status-im2 namespace, we keep this +;;only +;;for old (status 1.0) code, ;; can be removed with old code later ;; english as source of truth -(def labels (set (keys (js->clj (:en i18n-resources/translations-by-locale) - :keywordize-keys true)))) +(def labels + (set (keys (js->clj (:en i18n-resources/translations-by-locale) + :keywordize-keys + true)))) (spec/def ::label labels) (spec/def ::labels (spec/coll-of ::label :kind set? :into #{})) -(defn labels-for-all-locales [] +(defn labels-for-all-locales + [] (->> i18n-resources/translations-by-locale (mapcat #(-> % val (js->clj :keywordize-keys true) keys)) set)) @@ -1001,21 +1006,25 @@ ;; NOTE: This defines the scope of each checkpoint. To support a checkpoint, ;; change the var `checkpoint-to-consider-locale-supported` a few lines ;; below. -(def checkpoints-def (spec/assert ::checkpoint-defs - {::checkpoint-1-0-0-rc1 checkpoint-1-0-0-rc1-labels})) +(def checkpoints-def + (spec/assert ::checkpoint-defs + {::checkpoint-1-0-0-rc1 checkpoint-1-0-0-rc1-labels})) (def checkpoints (set (keys checkpoints-def))) (spec/def ::checkpoint checkpoints) (def checkpoint-to-consider-locale-supported ::checkpoint-1-0-0-rc1) -(defn checkpoint->labels [checkpoint] +(defn checkpoint->labels + [checkpoint] (get checkpoints-def checkpoint)) -(defn checkpoint-val-to-compare [c] +(defn checkpoint-val-to-compare + [c] (-> c name (string/replace #"^.*\|" "") js/parseInt)) -(defn >checkpoints [& cs] +(defn >checkpoints + [& cs] (apply > (map checkpoint-val-to-compare cs))) ;; locales @@ -1025,10 +1034,12 @@ (spec/def ::locale locales) (spec/def ::locales (spec/coll-of ::locale :kind set? :into #{})) -(defn locale->labels [locale] - (-> i18n-resources/translations-by-locale (get locale) (js->clj :keywordize-keys true) keys set)) +(defn locale->labels + [locale] + (-> i18n-resources/translations-by-locale (get locale) (js->clj :keywordize-keys true) keys set)) -(defn locale->checkpoint [locale] +(defn locale->checkpoint + [locale] (let [locale-labels (locale->labels locale) checkpoint (->> checkpoints-def (filter (fn [[_ checkpoint-labels]] @@ -1036,38 +1047,43 @@ ffirst)] checkpoint)) -(defn locale-is-supported-based-on-translations? [locale] +(defn locale-is-supported-based-on-translations? + [locale] (let [c (locale->checkpoint locale)] - (and c (or (= c checkpoint-to-consider-locale-supported) - (>checkpoints checkpoint-to-consider-locale-supported c))))) + (and c + (or (= c checkpoint-to-consider-locale-supported) + (>checkpoints checkpoint-to-consider-locale-supported c))))) -(defn actual-supported-locales [] +(defn actual-supported-locales + [] (->> locales (filter locale-is-supported-based-on-translations?) set)) ;; NOTE: Add new locale keywords here to indicate support for them. -#_(def supported-locales (spec/assert ::locales #{:fr - :zh - :zh-hans - :zh-hans-cn - :zh-hans-mo - :zh-hant - :zh-hant-sg - :zh-hant-hk - :zh-hant-tw - :zh-hant-mo - :zh-hant-cn - :sr-RS_#Cyrl - :el - :en - :de - :lt - :sr-RS_#Latn - :sr - :sv - :ja - :uk})) +#_(def supported-locales + (spec/assert ::locales + #{:fr + :zh + :zh-hans + :zh-hans-cn + :zh-hans-mo + :zh-hant + :zh-hant-sg + :zh-hant-hk + :zh-hant-tw + :zh-hant-mo + :zh-hant-cn + :sr-RS_#Cyrl + :el + :en + :de + :lt + :sr-RS_#Latn + :sr + :sv + :ja + :uk})) (def supported-locales (spec/assert ::locales #{:en})) (spec/def ::supported-locale supported-locales) @@ -1081,7 +1097,9 @@ (->> locales (remove #(spec/valid? ::labels (locale->labels %))) (map (fn [l] - (str "Extra translations in locale " l "\n" + (str "Extra translations in locale " + l + "\n" (set/difference (locale->labels l) labels) "\n\n"))) (apply str)))) @@ -1091,7 +1109,9 @@ (->> supported-locales (remove locale-is-supported-based-on-translations?) (map (fn [l] - (str "Missing translations in supported locale " l "\n" + (str "Missing translations in supported locale " + l + "\n" (set/difference (checkpoint->labels checkpoint-to-consider-locale-supported) (locale->labels l)) "\n\n"))) diff --git a/src/status_im/integration_test.cljs b/src/status_im/integration_test.cljs index 78029e950d..c86c7e4d61 100644 --- a/src/status_im/integration_test.cljs +++ b/src/status_im/integration_test.cljs @@ -1,18 +1,18 @@ (ns status-im.integration-test (:require [cljs.test :refer [deftest is run-tests]] - [day8.re-frame.test :as rf-test] [clojure.string :as string] + [day8.re-frame.test :as rf-test] [re-frame.core :as rf] - status-im.events - status-im2.navigation.core [status-im.chat.models :as chat.models] - [utils.security.core :as security] + [status-im.ethereum.core :as ethereum] + status-im.events [status-im.multiaccounts.logout.core :as logout] [status-im.transport.core :as transport] - status-im2.subs.root ;;so integration tests can run independently - [status-im.ethereum.core :as ethereum] [status-im.utils.test :as utils.test] - [taoensso.timbre :as log])) + status-im2.navigation.core + status-im2.subs.root ;;so integration tests can run independently + [taoensso.timbre :as log] + [utils.security.core :as security])) (def password "testabc") @@ -22,51 +22,64 @@ (utils.test/init!) -(defn initialize-app! [] +(defn initialize-app! + [] (rf/dispatch [:setup/app-started])) -(defn generate-and-derive-addresses! [] +(defn generate-and-derive-addresses! + [] (rf/dispatch [:generate-and-derive-addresses])) -(defn create-multiaccount! [] +(defn create-multiaccount! + [] (rf/dispatch [:create-multiaccount password])) -(defn assert-app-initialized [] - (let [app-state @(rf/subscribe [:app-state]) +(defn assert-app-initialized + [] + (let [app-state @(rf/subscribe [:app-state]) multiaccounts-loading? @(rf/subscribe [:multiaccounts/loading])] (is (= "active" app-state)) (is (false? multiaccounts-loading?)))) -(defn assert-logout [] +(defn assert-logout + [] (let [multiaccounts-loading? @(rf/subscribe [:multiaccounts/loading])] (is multiaccounts-loading?))) -(defn assert-multiaccounts-generated [] +(defn assert-multiaccounts-generated + [] (let [wizard-state @(rf/subscribe [:intro-wizard/choose-key])] (is (= 5 (count (:multiaccounts wizard-state)))))) -(defn messenger-started [] +(defn messenger-started + [] @(rf/subscribe [:messenger/started?])) -(defn assert-messenger-started [] +(defn assert-messenger-started + [] (is (messenger-started))) -(defn assert-multiaccount-loaded [] +(defn assert-multiaccount-loaded + [] (is (false? @(rf/subscribe [:multiaccounts/loading])))) -(defn assert-community-created [] +(defn assert-community-created + [] (is (= @(rf/subscribe [:communities/create]) community))) -(defn create-new-account! [] +(defn create-new-account! + [] (rf/dispatch-sync [:wallet.accounts/start-adding-new-account {:type :generate}]) (rf/dispatch-sync [:set-in [:add-account :account :name] account-name]) (rf/dispatch [:wallet.accounts/add-new-account (ethereum/sha3 password)])) -(defn assert-new-account-created [] +(defn assert-new-account-created + [] (is (true? (some #(= (:name %) account-name) @(rf/subscribe [:multiaccount/accounts]))))) -(defn logout! [] +(defn logout! + [] (rf/dispatch [:logout])) (deftest initialize-app-test @@ -94,7 +107,8 @@ [::transport/messenger-started] (assert-messenger-started) (logout!) - (rf-test/wait-for [::logout/logout-method] ; we need to logout to make sure the node is not in an inconsistent state between tests + (rf-test/wait-for [::logout/logout-method] ; we need to logout to make sure the node is not in an + ; inconsistent state between tests (assert-logout))))))) (deftest create-community-test @@ -141,7 +155,8 @@ [:wallet.accounts/account-stored] (assert-new-account-created) ; assert account was created (logout!) - (rf-test/wait-for [::logout/logout-method] ; we need to logout to make sure the node is not in an inconsistent state between tests + (rf-test/wait-for [::logout/logout-method] ; we need to logout to make sure the node is not in an + ; inconsistent state between tests (assert-logout)))))))) (deftest back-up-seed-phrase-test @@ -160,8 +175,8 @@ (assert-messenger-started) (rf/dispatch-sync [:set-in [:my-profile/seed :step] :12-words]) ; display seed phrase to user (rf/dispatch-sync [:my-profile/enter-two-random-words]) ; begin prompting user for seed words - (let [ma @(rf/subscribe [:multiaccount]) - seed @(rf/subscribe [:my-profile/seed]) + (let [ma @(rf/subscribe [:multiaccount]) + seed @(rf/subscribe [:my-profile/seed]) word1 (second (:first-word seed)) word2 (second (:second-word seed))] (is (= 12 (count (string/split (:mnemonic ma) #" ")))) ; assert 12-word seed phrase @@ -174,11 +189,13 @@ [:my-profile/finish-success] (is (nil? @(rf/subscribe [:mnemonic]))) ; assert seed phrase has been removed (logout!) - (rf-test/wait-for [::logout/logout-method] ; we need to logout to make sure the node is not in an inconsistent state between tests + (rf-test/wait-for [::logout/logout-method] ; we need to logout to make sure the node is not in + ; an inconsistent state between tests (assert-logout))))))))) (def multiaccount-name "Narrow Frail Lemming") -(def multiaccount-mnemonic "tattoo ramp health green tongue universe style vapor become tape lava reason") +(def multiaccount-mnemonic + "tattoo ramp health green tongue universe style vapor become tape lava reason") (def multiaccount-key-uid "0x694b8229524820a3a00a6e211141561d61b251ad99d6b65daf82a73c9a57697b") (deftest recover-multiaccount-test @@ -206,10 +223,12 @@ [::transport/messenger-started] (assert-messenger-started) (logout!) - (rf-test/wait-for [::logout/logout-method] ; we need to logout to make sure the node is not in an inconsistent state between tests + (rf-test/wait-for [::logout/logout-method] ; we need to logout to make sure the node is not in an + ; inconsistent state between tests (assert-logout)))))))) -(def chat-id "0x0402905bed83f0bbf993cee8239012ccb1a8bc86907ead834c1e38476a0eda71414eed0e25f525f270592a2eebb01c9119a4ed6429ba114e51f5cb0a28dae1adfd") +(def chat-id + "0x0402905bed83f0bbf993cee8239012ccb1a8bc86907ead834c1e38476a0eda71414eed0e25f525f270592a2eebb01c9119a4ed6429ba114e51f5cb0a28dae1adfd") (deftest one-to-one-chat-test (log/info "========= one-to-one-chat-test ==================") @@ -230,8 +249,10 @@ [:status-im.chat.models/one-to-one-chat-created] (rf/dispatch-sync [:chat.ui/navigate-to-chat chat-id]) (is (= chat-id @(rf/subscribe [:chats/current-chat-id]))) - (logout!) (rf-test/wait-for [::logout/logout-method] ; we need to logout to make sure the node is not in an inconsistent state between tests - (assert-logout)))))))) + (logout!) + (rf-test/wait-for [::logout/logout-method] ; we need to logout to make sure the node is not in an + ; inconsistent state between tests + (assert-logout)))))))) (deftest delete-chat-test (log/info "========= delete-chat-test ==================") @@ -258,8 +279,10 @@ (rf-test/wait-for [::chat.models/chat-deactivated] (is (not @(rf/subscribe [:chats/chat chat-id]))) - (logout!) (rf-test/wait-for [::logout/logout-method] ; we need to logout to make sure the node is not in an inconsistent state between tests - (assert-logout))))))))) + (logout!) + (rf-test/wait-for [::logout/logout-method] ; we need to logout to make sure the node is not in an + ; inconsistent state between tests + (assert-logout))))))))) (deftest mute-chat-test (log/info "========= mute-chat-test ==================") @@ -290,8 +313,10 @@ [::chat.models/mute-chat-toggled-successfully] (is (not @(rf/subscribe [:chats/muted chat-id]))) - (logout!) (rf-test/wait-for [::logout/logout-method] ; we need to logout to make sure the node is not in an inconsistent state between tests - (assert-logout)))))))))) + (logout!) + (rf-test/wait-for [::logout/logout-method] ; we need to logout to make sure the node is not in + ; an inconsistent state between tests + (assert-logout)))))))))) (comment (run-tests)) diff --git a/src/status_im/keycard/backup_key.cljs b/src/status_im/keycard/backup_key.cljs index e2a9eb63c0..80f10b9695 100644 --- a/src/status_im/keycard/backup_key.cljs +++ b/src/status_im/keycard/backup_key.cljs @@ -1,13 +1,13 @@ (ns status-im.keycard.backup-key - (:require [status-im.utils.fx :as fx] - [re-frame.core :as re-frame] - [status-im.utils.utils :as utils] - [status-im.i18n.i18n :as i18n] + (:require [re-frame.core :as re-frame] [status-im.ethereum.mnemonic :as mnemonic] + [status-im.i18n.i18n :as i18n] [status-im.keycard.common :as common] [status-im.multiaccounts.recover.core :as multiaccounts.recover] - [status-im2.navigation.events :as navigation] [status-im.signing.core :as signing.core] + [status-im.utils.fx :as fx] + [status-im.utils.utils :as utils] + [status-im2.navigation.events :as navigation] [taoensso.timbre :as log])) (fx/defn backup-card-pressed @@ -25,45 +25,48 @@ {:events [:keycard-settings.ui/recovery-card-pressed]} [{:keys [db] :as cofx} show-warning] (fx/merge cofx - {:db (-> db - ;setting pin-retry-counter is a workaround for the way the PIN view decides if it should accept PUK or PIN - (assoc-in [:keycard :application-info :pin-retry-counter] 3) - (assoc-in [:keycard :factory-reset-card?] true) - (dissoc :popover/popover)) + {:db (-> db + ;setting pin-retry-counter is a workaround for the way the PIN view + ;decides if it should accept PUK or PIN + (assoc-in [:keycard :application-info :pin-retry-counter] 3) + (assoc-in [:keycard :factory-reset-card?] true) + (dissoc :popover/popover)) :hide-popover nil} (signing.core/discard) (if show-warning - (utils/show-confirmation {:title (i18n/label :t/keycard-recover-title) - :content (i18n/label :t/keycard-recover-text) - :confirm-button-text (i18n/label :t/yes) - :cancel-button-text (i18n/label :t/no) - :on-accept #(re-frame/dispatch [:keycard-settings.ui/backup-card-pressed :recovery-card]) - :on-cancel #()}) + (utils/show-confirmation + {:title (i18n/label :t/keycard-recover-title) + :content (i18n/label :t/keycard-recover-text) + :confirm-button-text (i18n/label :t/yes) + :cancel-button-text (i18n/label :t/no) + :on-accept #(re-frame/dispatch [:keycard-settings.ui/backup-card-pressed + :recovery-card]) + :on-cancel #()}) (backup-card-pressed :recovery-card)))) (fx/defn start-keycard-backup {:events [::start-keycard-backup]} [{:keys [db] :as cofx}] - {::multiaccounts.recover/import-multiaccount {:passphrase (-> db - :multiaccounts/key-storage - :seed-phrase - mnemonic/sanitize-passphrase) - :password nil + {::multiaccounts.recover/import-multiaccount {:passphrase (-> db + :multiaccounts/key-storage + :seed-phrase + mnemonic/sanitize-passphrase) + :password nil :success-event ::create-backup-card}}) (fx/defn create-backup-card {:events [::create-backup-card]} [{:keys [db] :as cofx} root-data derived-data] (fx/merge cofx - {:db (-> db - (update :intro-wizard - assoc - :root-key root-data - :derived derived-data - :recovering? true - :selected-storage-type :advanced) - (assoc-in [:keycard :flow] :recovery) - (update :multiaccounts/key-storage dissoc :seed-phrase)) + {:db (-> db + (update :intro-wizard + assoc + :root-key root-data + :derived derived-data + :recovering? true + :selected-storage-type :advanced) + (assoc-in [:keycard :flow] :recovery) + (update :multiaccounts/key-storage dissoc :seed-phrase)) :dismiss-keyboard nil} (common/listen-to-hardware-back-button) (navigation/navigate-to-cofx :keycard-onboarding-intro nil))) diff --git a/src/status_im/keycard/card.cljs b/src/status_im/keycard/card.cljs index 40257eeb3c..1e489d1e8c 100644 --- a/src/status_im/keycard/card.cljs +++ b/src/status_im/keycard/card.cljs @@ -6,11 +6,13 @@ [status-im.utils.config :as config] [taoensso.timbre :as log])) -(defonce card (if config/keycard-test-menu-enabled? - (simulated-keycard/SimulatedKeycard.) - (real-keycard/RealKeycard.))) +(defonce card + (if config/keycard-test-menu-enabled? + (simulated-keycard/SimulatedKeycard.) + (real-keycard/RealKeycard.))) -(defn check-nfc-support [] +(defn check-nfc-support + [] (log/debug "[keycard] check-nfc-support") (keycard/check-nfc-support card @@ -20,7 +22,8 @@ (re-frame/dispatch [:keycard.callback/check-nfc-support-success response]))})) -(defn check-nfc-enabled [] +(defn check-nfc-enabled + [] (log/debug "[keycard] check-nfc-enabled") (keycard/check-nfc-enabled card @@ -30,27 +33,33 @@ (re-frame/dispatch [:keycard.callback/check-nfc-enabled-success response]))})) -(defn open-nfc-settings [] +(defn open-nfc-settings + [] (log/debug "[keycard] open-nfc-settings") (keycard/open-nfc-settings card)) -(defn remove-event-listener [event] +(defn remove-event-listener + [event] (log/debug "[keycard] remove-event-listener") (keycard/remove-event-listener card event)) -(defn on-card-disconnected [callback] +(defn on-card-disconnected + [callback] (log/debug "[keycard] on-card-disconnected") (keycard/on-card-disconnected card callback)) -(defn on-card-connected [callback] +(defn on-card-connected + [callback] (log/debug "[keycard] on-card-connected") (keycard/on-card-connected card callback)) -(defn remove-event-listeners [] +(defn remove-event-listeners + [] (log/debug "[keycard] remove-event-listeners") (keycard/remove-event-listeners card)) -(defn register-card-events [] +(defn register-card-events + [] (log/debug "[keycard] register-card-events") (keycard/register-card-events card @@ -72,15 +81,18 @@ :on-nfc-disabled #(re-frame/dispatch [:keycard.callback/check-nfc-enabled-success false])})) -(defn- error-object->map [^js object] +(defn- error-object->map + [^js object] {:code (.-code object) :error (.-message object)}) -(defn set-pairings [pairings] +(defn set-pairings + [pairings] (log/debug "[keycard] open-nfc-settings") (keycard/set-pairings card {:pairings pairings})) -(defn get-application-info [{:keys [on-success] :as args}] +(defn get-application-info + [{:keys [on-success] :as args}] (log/debug "[keycard] get-application-info") (keycard/get-application-info card @@ -99,7 +111,8 @@ [:keycard.callback/on-get-application-info-error (error-object->map response)]))}))) -(defn factory-reset [{:keys [on-success] :as args}] +(defn factory-reset + [{:keys [on-success] :as args}] (log/debug "[keycard] factory-reset") (keycard/factory-reset card @@ -118,7 +131,8 @@ [:keycard.callback/on-get-application-info-error (error-object->map response)]))}))) -(defn install-applet [] +(defn install-applet + [] (log/debug "[keycard] install-applet") (keycard/install-applet card @@ -134,11 +148,12 @@ [:keycard.callback/on-install-applet-error (error-object->map response)]))})) -(defn init-card [pin] +(defn init-card + [pin] (log/debug "[keycard] init-card") (keycard/init-card card - {:pin pin + {:pin pin :on-success (fn [response] (log/debug "[keycard response succ] init-card") @@ -151,11 +166,12 @@ [:keycard.callback/on-init-card-error (error-object->map response)]))})) -(defn install-applet-and-init-card [pin] +(defn install-applet-and-init-card + [pin] (log/debug "[keycard] install-applet-and-init-card") (keycard/install-applet-and-init-card card - {:pin pin + {:pin pin :on-success (fn [response] (log/debug "[keycard response succ] install-applet-and-init-card") @@ -169,7 +185,8 @@ [:keycard.callback/on-install-applet-and-init-card-error (error-object->map response)]))})) -(defn pair [args] +(defn pair + [args] (log/debug "[keycard] pair") (keycard/pair card @@ -186,7 +203,8 @@ (re-frame/dispatch [:keycard.callback/on-pair-error (error-object->map response)]))}))) -(defn generate-and-load-key [args] +(defn generate-and-load-key + [args] (log/debug "[keycard] generate-and-load-key") (keycard/generate-and-load-key card @@ -204,7 +222,8 @@ [:keycard.callback/on-generate-and-load-key-error (error-object->map response)]))}))) -(defn unblock-pin [args] +(defn unblock-pin + [args] (log/debug "[keycard] unblock-pin") (keycard/unblock-pin card @@ -221,7 +240,8 @@ (re-frame/dispatch [:keycard.callback/on-unblock-pin-error (error-object->map response)]))}))) -(defn verify-pin [args] +(defn verify-pin + [args] (log/debug "[keycard] verify-pin") (keycard/verify-pin card @@ -239,7 +259,8 @@ [:keycard.callback/on-verify-pin-error (error-object->map response)]))}))) -(defn change-pin [args] +(defn change-pin + [args] (log/debug "[keycard] change-pin") (keycard/change-pin card @@ -257,7 +278,8 @@ [:keycard.callback/on-change-pin-error (error-object->map response)]))}))) -(defn change-puk [args] +(defn change-puk + [args] (log/debug "[keycard] change-puk") (keycard/change-puk card @@ -275,7 +297,8 @@ [:keycard.callback/on-change-pin-error (error-object->map response)]))}))) -(defn change-pairing [args] +(defn change-pairing + [args] (log/debug "[keycard] change-pairing") (keycard/change-pairing card @@ -293,7 +316,8 @@ [:keycard.callback/on-change-pin-error (error-object->map response)]))}))) -(defn unpair [args] +(defn unpair + [args] (log/debug "[keycard] unpair") (keycard/unpair card @@ -311,7 +335,8 @@ [:keycard.callback/on-unpair-error (error-object->map response)]))}))) -(defn delete [] +(defn delete + [] (log/debug "[keycard] delete") (keycard/delete card @@ -327,7 +352,8 @@ [:keycard.callback/on-delete-error (error-object->map response)]))})) -(defn remove-key [args] +(defn remove-key + [args] (log/debug "[keycard] remove-key") (keycard/remove-key card @@ -344,7 +370,8 @@ (re-frame/dispatch [:keycard.callback/on-remove-key-error (error-object->map response)]))}))) -(defn remove-key-with-unpair [args] +(defn remove-key-with-unpair + [args] (log/debug "[keycard] remove-key-with-unpair") (keycard/remove-key-with-unpair card @@ -361,7 +388,8 @@ (re-frame/dispatch [:keycard.callback/on-remove-key-error (error-object->map response)]))}))) -(defn export-key [args] +(defn export-key + [args] (log/debug "[keycard] export-key") (keycard/export-key card @@ -378,7 +406,8 @@ (re-frame/dispatch [:keycard.callback/on-export-key-error (error-object->map response)]))}))) -(defn unpair-and-delete [args] +(defn unpair-and-delete + [args] (log/debug "[keycard] unpair-and-delete") (keycard/unpair card @@ -395,7 +424,8 @@ (re-frame/dispatch [:keycard.callback/on-unpair-and-delete-error (error-object->map response)]))}))) -(defn import-keys [{:keys [on-success] :as args}] +(defn import-keys + [{:keys [on-success] :as args}] (log/debug "[keycard] import-keys") (keycard/import-keys card @@ -414,7 +444,8 @@ (re-frame/dispatch [:keycard.callback/on-get-keys-error (error-object->map response)]))))) -(defn get-keys [{:keys [on-success] :as args}] +(defn get-keys + [{:keys [on-success] :as args}] (log/debug "[keycard] get-keys") (keycard/get-keys card @@ -433,7 +464,8 @@ (re-frame/dispatch [:keycard.callback/on-get-keys-error (error-object->map response)]))))) -(defn sign [{:keys [on-success on-failure] :as args}] +(defn sign + [{:keys [on-success on-failure] :as args}] (log/debug "[keycard] sign") (keycard/sign card @@ -454,7 +486,8 @@ [:keycard.callback/on-sign-error (error-object->map response)])))}))) -(defn install-cash-applet [] +(defn install-cash-applet + [] (log/debug "[keycard] install-cash-applet") (keycard/install-cash-applet card @@ -475,7 +508,7 @@ (log/debug "[keycard] sign-typed-data") (keycard/sign-typed-data card - {:hash hash + {:hash hash :on-success (fn [response] (log/debug "[keycard response succ] sign-typed-data") @@ -488,23 +521,30 @@ [:keycard.callback/on-sign-error (error-object->map response)]))})) -(defn save-multiaccount-and-login [args] +(defn save-multiaccount-and-login + [args] (keycard/save-multiaccount-and-login card args)) -(defn login [args] +(defn login + [args] (keycard/login card args)) -(defn send-transaction-with-signature [args] +(defn send-transaction-with-signature + [args] (keycard/send-transaction-with-signature card args)) -(defn start-nfc [args] +(defn start-nfc + [args] (keycard/start-nfc card args)) -(defn stop-nfc [args] +(defn stop-nfc + [args] (keycard/stop-nfc card args)) -(defn set-nfc-message [args] +(defn set-nfc-message + [args] (keycard/set-nfc-message card args)) -(defn delete-multiaccount-before-migration [args] +(defn delete-multiaccount-before-migration + [args] (keycard/delete-multiaccount-before-migration card args)) diff --git a/src/status_im/keycard/change_pin.cljs b/src/status_im/keycard/change_pin.cljs index 53f653973b..9c941b1263 100644 --- a/src/status_im/keycard/change_pin.cljs +++ b/src/status_im/keycard/change_pin.cljs @@ -1,10 +1,10 @@ (ns status-im.keycard.change-pin (:require [status-im.i18n.i18n :as i18n] - [status-im2.navigation.events :as navigation] + [status-im.keycard.common :as common] [status-im.keycard.onboarding :as onboarding] [status-im.utils.fx :as fx] + [status-im2.navigation.events :as navigation] [taoensso.timbre :as log] - [status-im.keycard.common :as common] [utils.security.core :as security])) (fx/defn change-credentials-pressed @@ -12,19 +12,21 @@ [{:keys [db] :as cofx} changing] (fx/merge cofx {:db - (assoc-in db [:keycard :pin] {:enter-step :current - :current [] - :puk [] - :original [] - :confirmation [] - :puk-original [] - :puk-confirmation [] - :status nil - :error-label nil - :on-verified (case changing - :pin :keycard/proceed-to-change-pin - :puk :keycard/proceed-to-change-puk - :pairing :keycard/proceed-to-change-pairing)})} + (assoc-in db + [:keycard :pin] + {:enter-step :current + :current [] + :puk [] + :original [] + :confirmation [] + :puk-original [] + :puk-confirmation [] + :status nil + :error-label nil + :on-verified (case changing + :pin :keycard/proceed-to-change-pin + :puk :keycard/proceed-to-change-puk + :pairing :keycard/proceed-to-change-pairing)})} (common/navigate-to-enter-pin-screen))) (fx/defn proceed-to-change-pin @@ -65,7 +67,8 @@ [{:keys [db] :as cofx}] (let [setup-step (get-in db [:keycard :setup-step])] (log/debug "[keycard] change-pin" - "setup-step" setup-step) + "setup-step" + setup-step) (if (= setup-step :pin) (onboarding/load-preparing-screen cofx) (common/show-connection-sheet @@ -80,7 +83,7 @@ (get-in db [:keycard :pin :current]))] (fx/merge cofx - {:db (assoc-in db [:keycard :pin :status] :verifying) + {:db (assoc-in db [:keycard :pin :status] :verifying) :keycard/change-pin {:new-pin new-pin @@ -137,10 +140,12 @@ (let [pin (get-in db [:keycard :pin :original]) puk-restore? (get-in db [:keycard :pin :puk-restore?])] (fx/merge cofx - {:db (assoc-in db [:keycard :pin] {:status nil - :login pin - :confirmation [] - :error-label nil}) + {:db (assoc-in db + [:keycard :pin] + {:status nil + :login pin + :confirmation [] + :error-label nil}) :utils/show-popup {:title "" :content (i18n/label :t/pin-changed)}} (common/hide-connection-sheet) @@ -154,10 +159,12 @@ {:events [:keycard.callback/on-change-puk-success]} [{:keys [db] :as cofx}] (fx/merge cofx - {:db (assoc-in db [:keycard :pin] {:status nil - :puk-original [] - :puk-confirmation [] - :error-label nil}) + {:db (assoc-in db + [:keycard :pin] + {:status nil + :puk-original [] + :puk-confirmation [] + :error-label nil}) :utils/show-popup {:title "" :content (i18n/label :t/puk-changed)}} (common/hide-connection-sheet) @@ -167,9 +174,11 @@ {:events [:keycard.callback/on-change-pairing-success]} [{:keys [db] :as cofx}] (fx/merge cofx - {:db (assoc-in db [:keycard :pin] {:status nil - :pairing-code nil - :error-label nil}) + {:db (assoc-in db + [:keycard :pin] + {:status nil + :pairing-code nil + :error-label nil}) :utils/show-popup {:title "" :content (i18n/label :t/pairing-changed)}} (common/hide-connection-sheet) @@ -180,25 +189,27 @@ [{:keys [db] :as cofx} error] (log/debug "[keycard] change pin error" error) (let [tag-was-lost? (common/tag-lost? (:error error)) - pin-retries (common/pin-retries (:error error))] - (fx/merge cofx - (if tag-was-lost? - (fx/merge cofx - {:db (assoc-in db [:keycard :pin :status] nil)} - (common/set-on-card-connected :keycard/change-pin)) - (if-not (nil? pin-retries) - (fx/merge cofx - {:db (-> db - (assoc-in [:keycard :application-info :pin-retry-counter] pin-retries) - (update-in [:keycard :pin] assoc - :status :error - :enter-step :current - :puk [] - :current [] - :original [] - :confirmation [] - :sign [] - :error-label :t/pin-mismatch))} - (when (zero? pin-retries) (common/frozen-keycard-popup)) - (navigation/navigate-to-cofx :enter-pin-settings nil)) - (common/show-wrong-keycard-alert)))))) + pin-retries (common/pin-retries (:error error))] + (fx/merge + cofx + (if tag-was-lost? + (fx/merge cofx + {:db (assoc-in db [:keycard :pin :status] nil)} + (common/set-on-card-connected :keycard/change-pin)) + (if-not (nil? pin-retries) + (fx/merge cofx + {:db (-> db + (assoc-in [:keycard :application-info :pin-retry-counter] pin-retries) + (update-in [:keycard :pin] + assoc + :status :error + :enter-step :current + :puk [] + :current [] + :original [] + :confirmation [] + :sign [] + :error-label :t/pin-mismatch))} + (when (zero? pin-retries) (common/frozen-keycard-popup)) + (navigation/navigate-to-cofx :enter-pin-settings nil)) + (common/show-wrong-keycard-alert)))))) diff --git a/src/status_im/keycard/common.cljs b/src/status_im/keycard/common.cljs index f4a1aa4452..4b34967235 100644 --- a/src/status_im/keycard/common.cljs +++ b/src/status_im/keycard/common.cljs @@ -1,26 +1,28 @@ (ns status-im.keycard.common (:require [clojure.string :as string] [re-frame.core :as re-frame] + [status-im.bottom-sheet.core :as bottom-sheet] [status-im.ethereum.core :as ethereum] - [status-im.keycard.nfc :as nfc] [status-im.i18n.i18n :as i18n] - [status-im2.navigation.events :as navigation] + [status-im.keycard.nfc :as nfc] + [status-im.popover.core :as popover] [status-im.ui.screens.keycard.keycard-interaction :as keycard-sheet] + [status-im.utils.datetime :as utils.datetime] [status-im.utils.fx :as fx] [status-im.utils.keychain.core :as keychain] - [status-im.utils.types :as types] - [taoensso.timbre :as log] - [status-im.utils.datetime :as utils.datetime] - [status-im.bottom-sheet.core :as bottom-sheet] [status-im.utils.platform :as platform] - [status-im.popover.core :as popover])) + [status-im.utils.types :as types] + [status-im2.navigation.events :as navigation] + [taoensso.timbre :as log])) (def default-pin "000000") (def pin-mismatch-error #"Unexpected error SW, 0x63C(\d+)|wrongPIN\(retryCounter: (\d+)\)") -(defn pin-retries [error] - (when-let [matched-error (re-matches pin-mismatch-error error)] (js/parseInt (second (filter some? matched-error))))) +(defn pin-retries + [error] + (when-let [matched-error (re-matches pin-mismatch-error error)] + (js/parseInt (second (filter some? matched-error))))) (fx/defn dispatch-event [_ event] @@ -56,7 +58,8 @@ (zero? free-pairing-slots)) :no-pairing-slots)) -(defn tag-lost? [error] +(defn tag-lost? + [error] (or (= error "Tag was lost.") (= error "NFCError:100") @@ -168,7 +171,8 @@ (assoc-in [:keycard :on-card-read] nil) (assoc-in [:keycard :last-on-card-read] nil))}) -(defn keycard-sheet-content [on-cancel connected? params] +(defn keycard-sheet-content + [on-cancel connected? params] (fn [] [keycard-sheet/connect-keycard {:on-cancel #(re-frame/dispatch on-cancel) @@ -178,15 +182,17 @@ :on-disconnect ::on-card-disconnected}])) (fx/defn show-connection-sheet-component - [{:keys [db] :as cofx} {:keys [on-card-connected on-card-read handler] - {:keys [on-cancel] - :or {on-cancel [::cancel-sheet-confirm]}} - :sheet-options}] + [{:keys [db] :as cofx} + {:keys [on-card-connected on-card-read handler] + {:keys [on-cancel] + :or {on-cancel [::cancel-sheet-confirm]}} + :sheet-options}] (assert (keyword? on-card-connected)) (assert (fn? handler)) (let [connected? (get-in db [:keycard :card-connected?])] (log/debug "[keycard] show-sheet-with-connection-check" - "card-connected?" connected?) + "card-connected?" + connected?) (fx/merge cofx {:dismiss-keyboard true} @@ -243,18 +249,18 @@ (fx/merge cofx {:db (assoc-in db - [:keycard :pin] - {:status nil - :login (get-in db [:keycard :pin :original]) - :export-key [] - :sign [] - :puk [] - :current [] - :original [] - :confirmation [] - :error-label nil - :on-verified (get-in db [:keycard :pin :on-verified]) - :on-verified-failure (get-in db [:keycard :pin :on-verified])})})) + [:keycard :pin] + {:status nil + :login (get-in db [:keycard :pin :original]) + :export-key [] + :sign [] + :puk [] + :current [] + :original [] + :confirmation [] + :error-label nil + :on-verified (get-in db [:keycard :pin :on-verified]) + :on-verified-failure (get-in db [:keycard :pin :on-verified])})})) (fx/defn cancel-sheet-confirm {:events [::cancel-sheet-confirm @@ -305,13 +311,15 @@ {:db (assoc-in db [:keycard :pin :current] [])} (navigation/navigate-to-cofx :enter-pin-settings nil))) -(defn- tag-lost-exception? [code error] +(defn- tag-lost-exception? + [code error] (or (= code "android.nfc.TagLostException") (= error "Tag was lost.") (= error "NFCError:100"))) -(fx/defn process-error [{:keys [db]} code error] +(fx/defn process-error + [{:keys [db]} code error] (when-not (tag-lost-exception? code error) {:db (assoc-in db [:keycard :setup-step] :error)})) @@ -320,7 +328,8 @@ (let [key-uid (get-in db [:multiaccounts/login :key-uid]) pin (string/join (get-in db [:keycard :pin :login]))] (log/debug "[keycard] get-keys-from-keycard" - ", not empty pin:" (boolean (seq pin))) + ", not empty pin:" + (boolean (seq pin))) (when (seq pin) {:db (assoc-in db [:keycard :pin :status] :verifying) :keycard/get-keys {:pin pin}}))) @@ -329,34 +338,36 @@ {:events [:keycard.callback/on-get-keys-success]} [{:keys [db] :as cofx} data] (let [{:keys [key-uid encryption-public-key whisper-private-key] - :as account-data} (js->clj data :keywordize-keys true) + :as account-data} + (js->clj data :keywordize-keys true) {:keys [identicon name]} (get-in db [:multiaccounts/multiaccounts key-uid]) - key-uid (get-in db [:keycard :application-info :key-uid]) - multiaccount-data (types/clj->json {:name name - :key-uid key-uid - :identicon identicon}) - save-keys? (get-in db [:multiaccounts/login :save-password?])] + key-uid (get-in db [:keycard :application-info :key-uid]) + multiaccount-data (types/clj->json {:name name + :key-uid key-uid + :identicon identicon}) + save-keys? (get-in db [:multiaccounts/login :save-password?])] (fx/merge cofx {:db (-> db (assoc-in [:keycard :pin :status] nil) (assoc-in [:keycard :pin :login] []) - (update-in [:keycard :application-info] assoc + (update-in [:keycard :application-info] + assoc :puk-retry-counter 5 :pin-retry-counter 3) (assoc-in [:keycard :multiaccount] (update account-data :whisper-public-key ethereum/normalized-hex)) (assoc-in [:keycard :flow] nil) (update :multiaccounts/login assoc - :password encryption-public-key - :key-uid key-uid - :identicon identicon - :name name)) + :password encryption-public-key + :key-uid key-uid + :identicon identicon + :name name)) - :keycard/login-with-keycard {:multiaccount-data multiaccount-data - :password encryption-public-key - :chat-key whisper-private-key - :key-uid key-uid}} + :keycard/login-with-keycard {:multiaccount-data multiaccount-data + :password encryption-public-key + :chat-key whisper-private-key + :key-uid key-uid}} (when save-keys? (keychain/save-keycard-keys key-uid encryption-public-key whisper-private-key)) (clear-on-card-connected) @@ -369,8 +380,8 @@ cofx {:db (assoc-in db [:keycard :pin :status] card-status)} (hide-connection-sheet) - ; do not try to display the popover if it is already open or - ; we are in the login interface (which has a different handling) + ; do not try to display the popover if it is already open or + ; we are in the login interface (which has a different handling) (when-not (or (:multiaccounts/login db) (:popover/popover db)) (popover/show-popover {:view card-status})))) @@ -386,10 +397,10 @@ {:events [:keycard.callback/on-get-keys-error]} [{:keys [db] :as cofx} error] (log/debug "[keycard] get keys error: " error) - (let [tag-was-lost? (tag-lost? (:error error)) - key-uid (get-in db [:keycard :application-info :key-uid]) - flow (get-in db [:keycard :flow]) - pin-retries-count (pin-retries (:error error))] + (let [tag-was-lost? (tag-lost? (:error error)) + key-uid (get-in db [:keycard :application-info :key-uid]) + flow (get-in db [:keycard :flow]) + pin-retries-count (pin-retries (:error error))] (if tag-was-lost? {:db (assoc-in db [:keycard :pin :status] nil)} (if-not (nil? pin-retries-count) @@ -397,7 +408,8 @@ cofx {:db (-> db (assoc-in [:keycard :application-info :pin-retry-counter] pin-retries-count) - (update-in [:keycard :pin] assoc + (update-in [:keycard :pin] + assoc :status :error :login [] :import-multiaccount [] @@ -410,18 +422,18 @@ {:events [:keycard/factory-reset]} [{:keys [db]} on-card-read] (log/debug "[keycard] factory-reset") - {:db (update db :keycard dissoc :factory-reset-card?) + {:db (update db :keycard dissoc :factory-reset-card?) :keycard/factory-reset {:on-success on-card-read}}) ;; Get application info (fx/defn update-pairings [{:keys [db]} instance-uid pairing] - (let [paired-on (utils.datetime/timestamp) - pairings (-> (get-in db [:keycard :pairings]) - (assoc instance-uid {:pairing pairing :paired-on paired-on}))] + (let [paired-on (utils.datetime/timestamp) + pairings (-> (get-in db [:keycard :pairings]) + (assoc instance-uid {:pairing pairing :paired-on paired-on}))] {:keycard/persist-pairings pairings - :db (assoc-in db [:keycard :pairings] pairings)})) + :db (assoc-in db [:keycard :pairings] pairings)})) (fx/defn get-application-info {:events [:keycard/get-application-info]} @@ -433,12 +445,15 @@ {:events [:keycard.callback/on-get-application-info-success]} [{:keys [db] :as cofx} info on-success] (let [{:keys [pin-retry-counter puk-retry-counter instance-uid new-pairing]} info - view-id (:view-id db) - {:keys [on-card-read]} (:keycard db) - on-success' (or on-success on-card-read) - enter-step (get-in db [:keycard :pin :enter-step])] + view-id (:view-id db) + {:keys [on-card-read]} (:keycard db) + on-success' (or on-success + on-card-read) + enter-step (get-in db + [:keycard :pin + :enter-step])] (log/debug "[keycard] on-get-application-info-success" - "on-success" on-success' + "on-success" on-success' "pin-retry-counter" pin-retry-counter "puk-retry-counter" puk-retry-counter) (fx/merge @@ -500,7 +515,7 @@ should-read-instance-uid? :keycard/get-application-info :else (get-in db [:keycard :on-card-read]))] (log/debug "[keycard] on-card-connected" on-card-connected - "on-card-read" on-card-read) + "on-card-read" on-card-read) (when on-card-connected (fx/merge cofx {:db (-> db @@ -522,7 +537,8 @@ (restore-on-card-connected) (restore-on-card-read))) -(defn keycard-multiaccount? [db] +(defn keycard-multiaccount? + [db] (boolean (get-in db [:multiaccount :keycard-pairing]))) (fx/defn verify-pin @@ -536,18 +552,20 @@ (get-in db [:keycard :pin :step]))] (fx/merge cofx - {:db (update-in db [:keycard :pin] assoc - :step pin-step - :on-verified on-success + {:db (update-in db + [:keycard :pin] + assoc + :step pin-step + :on-verified on-success :on-verified-failure on-failure)} (show-connection-sheet {:on-card-connected (or on-card-connected :keycard/verify-pin) :handler (fn [{:keys [db] :as cofx}] - (let [pin (vector->string (get-in db [:keycard :pin pin-step]))] + (let [pin (vector->string (get-in db [:keycard :pin pin-step]))] (fx/merge cofx - {:db (assoc-in db [:keycard :pin :status] :verifying) + {:db (assoc-in db [:keycard :pin :status] :verifying) :keycard/verify-pin {:pin pin}})))})))) (fx/defn navigete-to-keycard-settings diff --git a/src/status_im/keycard/core.cljs b/src/status_im/keycard/core.cljs index 3d5adb3c7d..7125ce5032 100644 --- a/src/status_im/keycard/core.cljs +++ b/src/status_im/keycard/core.cljs @@ -1,24 +1,24 @@ (ns status-im.keycard.core (:require [re-frame.db] + [status-im.i18n.i18n :as i18n] + status-im.keycard.backup-key + [status-im.keycard.card :as card] [status-im.keycard.change-pin :as change-pin] [status-im.keycard.common :as common] status-im.keycard.delete-key status-im.keycard.export-key - status-im.keycard.unpair - status-im.keycard.backup-key [status-im.keycard.login :as login] [status-im.keycard.mnemonic :as mnemonic] [status-im.keycard.onboarding :as onboarding] [status-im.keycard.recovery :as recovery] [status-im.keycard.sign :as sign] + status-im.keycard.unpair [status-im.keycard.wallet :as wallet] - [status-im.keycard.card :as card] - [status-im.i18n.i18n :as i18n] [status-im.multiaccounts.recover.core :as multiaccounts.recover] [status-im.multiaccounts.update.core :as multiaccounts.update] - [status-im2.navigation.events :as navigation] [status-im.utils.datetime :as utils.datetime] [status-im.utils.fx :as fx] + [status-im2.navigation.events :as navigation] [taoensso.timbre :as log])) (fx/defn show-keycard-has-multiaccount-alert @@ -26,7 +26,8 @@ (fx/merge cofx {:db (assoc-in db [:keycard :setup-step] nil) :utils/show-confirmation {:title nil - :content (i18n/label :t/keycard-has-multiaccount-on-it) + :content (i18n/label + :t/keycard-has-multiaccount-on-it) :cancel-button-text "" :confirm-button-text :t/okay}})) @@ -35,18 +36,20 @@ (fx/merge cofx {:db (-> db (assoc-in [:keycard :setup-step] :pin) - (assoc-in [:keycard :pin] {:enter-step :original - :original [] - :confirmation []}))} + (assoc-in [:keycard :pin] + {:enter-step :original + :original [] + :confirmation []}))} (navigation/navigate-to-cofx :keycard-onboarding-pin nil))) (fx/defn load-recovery-pin-screen [{:keys [db] :as cofx}] (fx/merge cofx {:db (-> db - (assoc-in [:keycard :pin] {:enter-step :import-multiaccount - :import-multiaccount [] - :current []}))} + (assoc-in [:keycard :pin] + {:enter-step :import-multiaccount + :import-multiaccount [] + :current []}))} (common/listen-to-hardware-back-button) (navigation/navigate-replace :keycard-recovery-pin nil))) @@ -60,7 +63,8 @@ (fx/defn proceed-setup-with-initialized-card [{:keys [db] :as cofx} flow instance-uid paired?] (log/debug "[keycard] proceed-setup-with-initialized-card" - "instance-uid" instance-uid) + "instance-uid" + instance-uid) (if (= flow :import) (navigation/navigate-to-cofx cofx :keycard-recovery-no-key nil) (if paired? @@ -111,7 +115,7 @@ [{:keys [db]}] (let [enter-step (get-in db [:keycard :pin :enter-step])] {:db (-> db - (assoc-in [:keycard :pin enter-step] []) + (assoc-in [:keycard :pin enter-step] []) (dissoc :intro-wizard :recovered-account?))})) (defn multiaccounts-screen-did-load @@ -132,21 +136,26 @@ {:events [:keycard.callback/check-nfc-enabled-success]} [{:keys [db]} nfc-enabled?] (log/debug "[keycard] check-nfc-enabled-success" - "nfc-enabled?" nfc-enabled?) + "nfc-enabled?" + nfc-enabled?) {:db (assoc-in db [:keycard :nfc-enabled?] nfc-enabled?)}) -(defn- proceed-to-pin-confirmation [fx] +(defn- proceed-to-pin-confirmation + [fx] (assoc-in fx [:db :keycard :pin :enter-step] :confirmation)) -(defn- proceed-to-change-puk-confirmation [fx] +(defn- proceed-to-change-puk-confirmation + [fx] (assoc-in fx [:db :keycard :pin :enter-step] :puk-confirmation)) -(defn- proceed-to-pin-reset-confirmation [fx] +(defn- proceed-to-pin-reset-confirmation + [fx] (-> fx (update-in [:db :keycard :pin] dissoc :reset-confirmation) (assoc-in [:db :keycard :pin :enter-step] :reset-confirmation))) -(defn- proceed-to-puk-confirmation [fx] +(defn- proceed-to-puk-confirmation + [fx] (assoc-in fx [:db :keycard :pin :enter-step] :puk)) (fx/defn on-unblock-pin-success @@ -156,10 +165,12 @@ (fx/merge cofx {:db (-> db - (update-in [:keycard :application-info] assoc + (update-in [:keycard :application-info] + assoc :puk-retry-counter 5 :pin-retry-counter 3) - (update-in [:keycard :pin] assoc + (update-in [:keycard :pin] + assoc :status :after-unblocking :enter-step :login :login reset-pin @@ -175,14 +186,15 @@ {:events [:keycard.callback/on-unblock-pin-error]} [{:keys [db] :as cofx} error] (let [tag-was-lost? (common/tag-lost? (:error error)) - puk-retries (common/pin-retries (:error error))] + puk-retries (common/pin-retries (:error error))] (log/debug "[keycard] unblock pin error" error) (when-not tag-was-lost? (fx/merge cofx {:db (-> db (assoc-in [:keycard :application-info :puk-retry-counter] puk-retries) - (update-in [:keycard :pin] merge + (update-in [:keycard :pin] + merge {:status (if (zero? puk-retries) :blocked-card :error) :error-label :t/puk-mismatch :enter-step :puk @@ -192,8 +204,11 @@ (fx/defn clear-on-verify-handlers [{:keys [db]}] - {:db (update-in db [:keycard :pin] - dissoc :on-verified-failure :on-verified)}) + {:db (update-in db + [:keycard :pin] + dissoc + :on-verified-failure + :on-verified)}) (fx/defn on-verify-pin-success {:events [:keycard.callback/on-verify-pin-success]} @@ -201,8 +216,11 @@ (let [on-verified (get-in db [:keycard :pin :on-verified])] (log/debug "[hardwaller] success pin verification. on-verified" on-verified) (fx/merge cofx - {:db (update-in db [:keycard :pin] merge {:status nil - :error-label nil})} + {:db (update-in db + [:keycard :pin] + merge + {:status nil + :error-label nil})} (common/clear-on-card-connected) (common/clear-on-card-read) ;; TODO(Ferossgp): Each pin input should handle this event on it's own, @@ -211,13 +229,15 @@ (when-not (contains? #{:keycard/generate-and-load-key :wallet.accounts/generate-new-keycard-account :keycard/remove-key-with-unpair - :keycard/unpair-and-delete} on-verified) + :keycard/unpair-and-delete} + on-verified) (common/hide-connection-sheet)) (when-not (contains? #{:keycard/unpair :keycard/generate-and-load-key :keycard/remove-key-with-unpair :keycard/unpair-and-delete - :wallet.accounts/generate-new-keycard-account} on-verified) + :wallet.accounts/generate-new-keycard-account} + on-verified) (common/get-application-info nil)) (when on-verified (common/dispatch-event on-verified)) @@ -230,14 +250,15 @@ setup? (boolean (get-in db [:keycard :setup-step])) on-verified-failure (get-in db [:keycard :pin :on-verified-failure]) exporting? (get-in db [:keycard :on-export-success]) - pin-retries (common/pin-retries (:error error))] + pin-retries (common/pin-retries (:error error))] (log/debug "[keycard] verify pin error" error) (when-not tag-was-lost? (if-not (nil? pin-retries) (fx/merge cofx {:db (-> db (assoc-in [:keycard :application-info :pin-retry-counter] pin-retries) - (update-in [:keycard :pin] assoc + (update-in [:keycard :pin] + assoc :status :error :enter-step :current :puk [] @@ -251,12 +272,13 @@ (not on-verified-failure)) (when exporting? (navigation/navigate-back))) - ;(navigation/navigate-to-cofx :enter-pin-settings nil))) + ;(navigation/navigate-to-cofx :enter-pin-settings nil))) (when (zero? pin-retries) (common/frozen-keycard-popup)) (when on-verified-failure - (fn [_] {:utils/dispatch-later - [{:dispatch [on-verified-failure] - :ms 200}]})) + (fn [_] + {:utils/dispatch-later + [{:dispatch [on-verified-failure] + :ms 200}]})) #_(clear-on-verify-handlers)) (fx/merge cofx @@ -275,7 +297,7 @@ (let [puk (common/vector->string (get-in db [:keycard :pin :puk])) pin (common/vector->string (get-in db [:keycard :pin :reset])) key-uid (get-in db [:keycard :application-info :key-uid])] - {:db (assoc-in db [:keycard :pin :status] :verifying) + {:db (assoc-in db [:keycard :pin :status] :verifying) :keycard/unblock-pin {:puk puk :new-pin pin}}))})) @@ -295,9 +317,11 @@ [{:keys [db] :as cofx} number enter-step] (log/debug "update-pin" enter-step) (let [numbers-entered (count (get-in db [:keycard :pin enter-step])) - need-update? (if (or (= enter-step :puk) (= enter-step :puk-original) (= enter-step :puk-confirmation)) - (< numbers-entered puk-code-length) - (< numbers-entered pin-code-length))] + need-update? (if (or (= enter-step :puk) + (= enter-step :puk-original) + (= enter-step :puk-confirmation)) + (< numbers-entered puk-code-length) + (< numbers-entered pin-code-length))] (fx/merge cofx {:db (cond-> (-> db (assoc-in [:keycard :pin :enter-step] enter-step) @@ -306,26 +330,38 @@ (when need-update? (handle-pin-input enter-step))))) -(defn- pin-enter-error [fx error-label] - (update-in fx [:db :keycard :pin] merge {:status :error - :error-label error-label - :enter-step :original - :original [] - :confirmation []})) +(defn- pin-enter-error + [fx error-label] + (update-in fx + [:db :keycard :pin] + merge + {:status :error + :error-label error-label + :enter-step :original + :original [] + :confirmation []})) -(defn- puk-enter-error [fx error-label] - (update-in fx [:db :keycard :pin] merge {:status :error - :error-label error-label - :enter-step :puk-original - :puk-original [] - :puk-confirmation []})) +(defn- puk-enter-error + [fx error-label] + (update-in fx + [:db :keycard :pin] + merge + {:status :error + :error-label error-label + :enter-step :puk-original + :puk-original [] + :puk-confirmation []})) -(defn- pin-reset-error [fx error-label] - (update-in fx [:db :keycard :pin] merge {:status :error - :error-label error-label - :enter-step :reset - :reset [] - :reset-confirmation []})) +(defn- pin-reset-error + [fx error-label] + (update-in fx + [:db :keycard :pin] + merge + {:status :error + :error-label error-label + :enter-step :reset + :reset [] + :reset-confirmation []})) ; PIN enter steps: ; login - PIN is used to login @@ -336,11 +372,11 @@ (fx/defn process-pin-input {:events [:keycard/process-pin-input]} [{:keys [db]}] - (let [enter-step (get-in db [:keycard :pin :enter-step]) - pin (get-in db [:keycard :pin enter-step]) + (let [enter-step (get-in db [:keycard :pin :enter-step]) + pin (get-in db [:keycard :pin enter-step]) numbers-entered (count pin)] (log/debug "[keycard] process-pin-input" - "enter-step" enter-step + "enter-step" enter-step "numbers-entered" numbers-entered) (cond-> {:db (assoc-in db [:keycard :pin :status] nil)} @@ -421,9 +457,13 @@ [cofx _ pairing paired-on] (fx/merge cofx (multiaccounts.update/multiaccount-update - :keycard-pairing pairing {}) + :keycard-pairing + pairing + {}) (multiaccounts.update/multiaccount-update - :keycard-paired-on paired-on {}))) + :keycard-paired-on + paired-on + {}))) (fx/defn on-retrieve-pairings-success {:events [:keycard.callback/on-retrieve-pairings-success]} @@ -448,19 +488,20 @@ paired-on (utils.datetime/timestamp) pairings (-> (get-in db [:keycard :pairings]) (dissoc (keyword instance-uid)) - (assoc instance-uid {:pairing pairing :paired-on paired-on})) + (assoc instance-uid {:pairing pairing :paired-on paired-on})) next-step (if (= setup-step :pair) :begin :card-ready)] (fx/merge cofx {:keycard/persist-pairings pairings - :db (-> db - (assoc-in [:keycard :pairings] pairings) - (assoc-in [:keycard :application-info :paired?] true) - (assoc-in [:keycard :setup-step] next-step) - (assoc-in [:keycard :secrets :pairing] pairing) - (assoc-in [:keycard :secrets :paired-on] paired-on))} - (when-not (and (= flow :recovery) (= next-step :card-ready)) (common/hide-connection-sheet)) + :db (-> db + (assoc-in [:keycard :pairings] pairings) + (assoc-in [:keycard :application-info :paired?] true) + (assoc-in [:keycard :setup-step] next-step) + (assoc-in [:keycard :secrets :pairing] pairing) + (assoc-in [:keycard :secrets :paired-on] paired-on))} + (when-not (and (= flow :recovery) (= next-step :card-ready)) + (common/hide-connection-sheet)) (when multiaccount (set-multiaccount-pairing multiaccount pairing paired-on)) (when (= flow :login) @@ -496,15 +537,17 @@ (fx/defn set-setup-step [{:keys [db]} card-state] - {:db (assoc-in db [:keycard :setup-step] - (case card-state - :not-paired :pair - :no-pairing-slots :no-slots - :init :card-ready - :multiaccount :import-multiaccount - :begin))}) + {:db (assoc-in db + [:keycard :setup-step] + (case card-state + :not-paired :pair + :no-pairing-slots :no-slots + :init :card-ready + :multiaccount :import-multiaccount + :begin))}) -(fx/defn show-no-keycard-applet-alert [_] +(fx/defn show-no-keycard-applet-alert + [_] {:utils/show-confirmation {:title (i18n/label :t/no-keycard-applet-on-card) :content (i18n/label :t/keycard-applet-install-instructions) :cancel-button-text "" @@ -515,13 +558,13 @@ (fx/defn check-card-state {:events [:keycard/check-card-state]} [{:keys [db] :as cofx}] - (let [app-info (get-in db [:keycard :application-info]) - flow (get-in db [:keycard :flow]) + (let [app-info (get-in db [:keycard :application-info]) + flow (get-in db [:keycard :flow]) {:keys [instance-uid key-uid paired?]} app-info - card-state (common/get-card-state app-info)] + card-state (common/get-card-state app-info)] (log/debug "[keycard] check-card-state" "card-state" card-state - "flow" flow) + "flow" flow) (fx/merge cofx {:db (assoc-in db [:keycard :card-state] card-state)} (set-setup-step card-state) @@ -582,9 +625,9 @@ {:events [:keycard.callback/on-nfc-timeout]} [{:keys [db]} _] (log/debug "[keycard] nfc timeout") - {:db (-> db - (assoc-in [:keycard :nfc-running?] false) - (assoc-in [:keycard :card-connected?] false)) + {:db (-> db + (assoc-in [:keycard :nfc-running?] false) + (assoc-in [:keycard :card-connected?] false)) :dispatch-later [{:ms 500 :dispatch [:keycard.ui/start-nfc]}]}) (fx/defn on-register-card-events @@ -611,7 +654,7 @@ (fx/defn stop-nfc {:events [:keycard.ui/stop-nfc]} [cofx] - {:keycard/stop-nfc nil + {:keycard/stop-nfc nil :keycard.callback/on-card-disconnected nil}) (fx/defn start-nfc-success diff --git a/src/status_im/keycard/core_test.cljs b/src/status_im/keycard/core_test.cljs index 55182c5fca..fbb3fe2940 100644 --- a/src/status_im/keycard/core_test.cljs +++ b/src/status_im/keycard/core_test.cljs @@ -45,26 +45,33 @@ :enter-step :confirmation}}}}))))) #_(deftest on-generate-and-load-key-success - (is (= (select-keys - (get-in - (keycard/on-generate-and-load-key-success - {:random-guid-generator (constantly "") - :signing-phrase "" - :status "" - :db {}} - #js {"whisper-private-key" "f342f3ef17ce86abfa92b912b3a108a4546cfc687fd8e0f02fedf87a60ee4c0f" - "whisper-public-key" "04de2e21f1642ebee03b9aa4bf1936066124cc89967eaf269544c9b90c539fd5c980166a897d06dd4d3732b38116239f63c89395a8d73eac72881fab802010cb56" - "encryption-public-key" "04f2a432677a1b7c4f1bb22078135821d1d10fce23422297b5c808a545f2b61cdba38ee7394762172fc6ff5e9e28db7535e555efe2812905ffd4e0c25e82a98ae8" - "whisper-address" "87df2285f90b71221fab6267b7cb37532fedbb1f" - "wallet-address" "7e92236392a850980d00d0cd2a4b92886bd7fe7b"}) - [:db :keycard :multiaccount]) - [:whisper-private-key - :whisper-public-key - :encryption-public-key - :wallet-address - :whisper-address])) - {:whisper-private-key "f342f3ef17ce86abfa92b912b3a108a4546cfc687fd8e0f02fedf87a60ee4c0f" - :whisper-public-key "0x04de2e21f1642ebee03b9aa4bf1936066124cc89967eaf269544c9b90c539fd5c980166a897d06dd4d3732b38116239f63c89395a8d73eac72881fab802010cb56" - :encryption-public-key "04f2a432677a1b7c4f1bb22078135821d1d10fce23422297b5c808a545f2b61cdba38ee7394762172fc6ff5e9e28db7535e555efe2812905ffd4e0c25e82a98ae8" - :whisper-address "87df2285f90b71221fab6267b7cb37532fedbb1f" - :wallet-address "7e92236392a850980d00d0cd2a4b92886bd7fe7b"})) + (is + (= + (select-keys + (get-in + (keycard/on-generate-and-load-key-success + {:random-guid-generator (constantly "") + :signing-phrase "" + :status "" + :db {}} + #js + {"whisper-private-key" "f342f3ef17ce86abfa92b912b3a108a4546cfc687fd8e0f02fedf87a60ee4c0f" + "whisper-public-key" + "04de2e21f1642ebee03b9aa4bf1936066124cc89967eaf269544c9b90c539fd5c980166a897d06dd4d3732b38116239f63c89395a8d73eac72881fab802010cb56" + "encryption-public-key" + "04f2a432677a1b7c4f1bb22078135821d1d10fce23422297b5c808a545f2b61cdba38ee7394762172fc6ff5e9e28db7535e555efe2812905ffd4e0c25e82a98ae8" + "whisper-address" "87df2285f90b71221fab6267b7cb37532fedbb1f" + "wallet-address" "7e92236392a850980d00d0cd2a4b92886bd7fe7b"}) + [:db :keycard :multiaccount]) + [:whisper-private-key + :whisper-public-key + :encryption-public-key + :wallet-address + :whisper-address])) + {:whisper-private-key "f342f3ef17ce86abfa92b912b3a108a4546cfc687fd8e0f02fedf87a60ee4c0f" + :whisper-public-key + "0x04de2e21f1642ebee03b9aa4bf1936066124cc89967eaf269544c9b90c539fd5c980166a897d06dd4d3732b38116239f63c89395a8d73eac72881fab802010cb56" + :encryption-public-key + "04f2a432677a1b7c4f1bb22078135821d1d10fce23422297b5c808a545f2b61cdba38ee7394762172fc6ff5e9e28db7535e555efe2812905ffd4e0c25e82a98ae8" + :whisper-address "87df2285f90b71221fab6267b7cb37532fedbb1f" + :wallet-address "7e92236392a850980d00d0cd2a4b92886bd7fe7b"})) diff --git a/src/status_im/keycard/delete_key.cljs b/src/status_im/keycard/delete_key.cljs index 39b204c9cd..1b88d9647f 100644 --- a/src/status_im/keycard/delete_key.cljs +++ b/src/status_im/keycard/delete_key.cljs @@ -1,7 +1,7 @@ (ns status-im.keycard.delete-key - (:require [status-im2.navigation.events :as navigation] + (:require [status-im.keycard.common :as common] [status-im.utils.fx :as fx] - [status-im.keycard.common :as common])) + [status-im2.navigation.events :as navigation])) (fx/defn reset-card-pressed {:events [:keycard-settings.ui/reset-card-pressed]} @@ -10,7 +10,7 @@ (fx/defn delete-card [{:keys [db] :as cofx}] - (let [key-uid (get-in db [:keycard :application-info :key-uid]) + (let [key-uid (get-in db [:keycard :application-info :key-uid]) multiaccount-key-uid (get-in db [:multiaccount :key-uid])] (if (and key-uid (= key-uid multiaccount-key-uid)) @@ -32,13 +32,15 @@ {:events [:keycard/proceed-to-reset-card]} [{:keys [db] :as cofx} keep-keys-on-keycard?] (fx/merge cofx - {:db (assoc-in db [:keycard :pin] {:enter-step :current - :current [] - :puk [] - :status nil - :error-label nil - :on-verified (if keep-keys-on-keycard? - :keycard/unpair-and-delete - :keycard/remove-key-with-unpair)})} + {:db (assoc-in db + [:keycard :pin] + {:enter-step :current + :current [] + :puk [] + :status nil + :error-label nil + :on-verified (if keep-keys-on-keycard? + :keycard/unpair-and-delete + :keycard/remove-key-with-unpair)})} (common/set-on-card-connected :keycard/navigate-to-enter-pin-screen) (common/navigate-to-enter-pin-screen))) diff --git a/src/status_im/keycard/export_key.cljs b/src/status_im/keycard/export_key.cljs index 7b06cf7b09..0bb1a5cc83 100644 --- a/src/status_im/keycard/export_key.cljs +++ b/src/status_im/keycard/export_key.cljs @@ -1,41 +1,43 @@ (ns status-im.keycard.export-key - (:require [status-im.utils.fx :as fx] - [taoensso.timbre :as log] + (:require [status-im.keycard.common :as common] [status-im.keycard.wallet :as wallet] - [status-im.keycard.common :as common])) + [status-im.utils.fx :as fx] + [taoensso.timbre :as log])) (fx/defn on-export-key-error {:events [:keycard.callback/on-export-key-error]} [{:keys [db] :as cofx} error] (log/debug "[keycard] export key error" error) (let [tag-was-lost? (common/tag-lost? (:error error)) - pin-retries (common/pin-retries (:error error))] - (cond tag-was-lost? - (fx/merge cofx - {:db (assoc-in db [:keycard :pin :status] nil)} - (common/set-on-card-connected :wallet.accounts/generate-new-keycard-account)) + pin-retries (common/pin-retries (:error error))] + (cond + tag-was-lost? + (fx/merge cofx + {:db (assoc-in db [:keycard :pin :status] nil)} + (common/set-on-card-connected :wallet.accounts/generate-new-keycard-account)) - (not (nil? pin-retries)) - (fx/merge cofx - {:db (-> db - (assoc-in [:keycard :application-info :pin-retry-counter] pin-retries) - (update-in [:keycard :pin] assoc - :status :error - :enter-step :export-key - :puk [] - :current [] - :original [] - :confirmation [] - :sign [] - :export-key [] - :error-label :t/pin-mismatch))} - (common/hide-connection-sheet) - (when (zero? pin-retries) (common/frozen-keycard-popup))) - :else - (fx/merge cofx - (common/show-wrong-keycard-alert) - (common/clear-pin) - (common/hide-connection-sheet))))) + (not (nil? pin-retries)) + (fx/merge cofx + {:db (-> db + (assoc-in [:keycard :application-info :pin-retry-counter] pin-retries) + (update-in [:keycard :pin] + assoc + :status :error + :enter-step :export-key + :puk [] + :current [] + :original [] + :confirmation [] + :sign [] + :export-key [] + :error-label :t/pin-mismatch))} + (common/hide-connection-sheet) + (when (zero? pin-retries) (common/frozen-keycard-popup))) + :else + (fx/merge cofx + (common/show-wrong-keycard-alert) + (common/clear-pin) + (common/hide-connection-sheet))))) (fx/defn on-export-key-success {:events [:keycard.callback/on-export-key-success]} diff --git a/src/status_im/keycard/fx.cljs b/src/status_im/keycard/fx.cljs index aa185e3b43..1270d2d1b1 100644 --- a/src/status_im/keycard/fx.cljs +++ b/src/status_im/keycard/fx.cljs @@ -1,11 +1,11 @@ (ns status-im.keycard.fx - (:require [re-frame.core :as re-frame] - [status-im.utils.types :as types] + (:require ["@react-native-async-storage/async-storage" :default AsyncStorage] + ["react-native" :refer (BackHandler)] + [re-frame.core :as re-frame] [status-im.keycard.card :as card] [status-im.native-module.core :as status] - ["react-native" :refer (BackHandler)] - [taoensso.timbre :as log] - ["@react-native-async-storage/async-storage" :default AsyncStorage])) + [status-im.utils.types :as types] + [taoensso.timbre :as log])) (re-frame/reg-fx :keycard/start-nfc @@ -188,7 +188,8 @@ ;;particular screen to be loaded. An fx is easier to re-use and test. (fn [] (re-frame/dispatch [:keycard/add-listener-to-hardware-back-button - (.addEventListener BackHandler "hardwareBackPress" + (.addEventListener BackHandler + "hardwareBackPress" (fn [] (re-frame/dispatch [:keycard/back-button-pressed]) true))]))) diff --git a/src/status_im/keycard/login.cljs b/src/status_im/keycard/login.cljs index 34d4d3dfed..5919ccb10f 100644 --- a/src/status_im/keycard/login.cljs +++ b/src/status_im/keycard/login.cljs @@ -1,15 +1,15 @@ (ns status-im.keycard.login - (:require [status-im.ethereum.core :as ethereum] - [status-im2.navigation.events :as navigation] + (:require [status-im.bottom-sheet.core :as bottom-sheet] + [status-im.ethereum.core :as ethereum] + [status-im.keycard.common :as common] + status-im.keycard.fx + [status-im.keycard.onboarding :as onboarding] + [status-im.keycard.recovery :as recovery] + [status-im.signing.core :as signing.core] [status-im.utils.fx :as fx] [status-im.utils.types :as types] - [taoensso.timbre :as log] - [status-im.keycard.common :as common] - [status-im.keycard.recovery :as recovery] - [status-im.keycard.onboarding :as onboarding] - status-im.keycard.fx - [status-im.bottom-sheet.core :as bottom-sheet] - [status-im.signing.core :as signing.core])) + [status-im2.navigation.events :as navigation] + [taoensso.timbre :as log])) (fx/defn login-got-it-pressed {:events [:keycard.login.pin.ui/got-it-pressed @@ -57,14 +57,17 @@ cofx (signing.core/discard) (fn [{:keys [db]}] - {:db (-> db - (dissoc :popover/popover) - (update-in [:keycard :pin] dissoc - :reset :puk) - (update-in [:keycard :pin] assoc - :enter-step :reset - :error nil - :status nil)) + {:db (-> db + (dissoc :popover/popover) + (update-in [:keycard :pin] + dissoc + :reset + :puk) + (update-in [:keycard :pin] + assoc + :enter-step :reset + :error nil + :status nil)) :hide-popover nil}) (when (:multiaccount db) (navigation/change-tab :profile)) @@ -76,29 +79,30 @@ (fx/defn dismiss-frozen-keycard-popover {:events [::frozen-keycard-popover-dismissed]} [{:keys [db]}] - {:db (-> db - (dissoc :popover/popover) - (update :keycard dissoc :setup-step)) + {:db (-> db + (dissoc :popover/popover) + (update :keycard dissoc :setup-step)) :hide-popover nil}) (fx/defn login-with-keycard {:events [:keycard/login-with-keycard]} [{:keys [db] :as cofx}] (let [{:keys [:pin-retry-counter :puk-retry-counter] - :as application-info} + :as application-info} (get-in db [:keycard :application-info]) - key-uid (get-in db [:keycard :application-info :key-uid]) - paired? (get-in db [:keycard :application-info :paired?]) - multiaccount (get-in db [:multiaccounts/multiaccounts (get-in db [:multiaccounts/login :key-uid])]) - multiaccount-key-uid (get multiaccount :key-uid) + key-uid (get-in db [:keycard :application-info :key-uid]) + paired? (get-in db [:keycard :application-info :paired?]) + multiaccount (get-in db + [:multiaccounts/multiaccounts (get-in db [:multiaccounts/login :key-uid])]) + multiaccount-key-uid (get multiaccount :key-uid) multiaccount-mismatch? (or (nil? multiaccount) (not= multiaccount-key-uid key-uid))] (log/debug "[keycard] login-with-keycard" "empty application info" (empty? application-info) - "no key-uid" (empty? key-uid) + "no key-uid" (empty? key-uid) "multiaccount-mismatch?" multiaccount-mismatch? - "no pairing" paired?) + "no pairing" paired?) (cond (empty? application-info) (fx/merge cofx @@ -143,8 +147,10 @@ {:events [:multiaccounts.login.callback/get-keycard-keys-success]} [{:keys [db] :as cofx} key-uid [encryption-public-key whisper-private-key :as creds]] (if (nil? creds) - (navigation/set-stack-root cofx :multiaccounts-stack [:multiaccounts - :keycard-login-pin]) + (navigation/set-stack-root cofx + :multiaccounts-stack + [:multiaccounts + :keycard-login-pin]) (let [{:keys [identicon name]} (get-in db [:multiaccounts/multiaccounts key-uid]) multiaccount-data (types/clj->json {:name name :key-uid key-uid @@ -160,11 +166,11 @@ (update account-data :whisper-public-key ethereum/normalized-hex)) (assoc-in [:keycard :flow] nil) (update :multiaccounts/login assoc - :password encryption-public-key - :key-uid key-uid - :identicon identicon - :name name - :save-password? true)) + :password encryption-public-key + :key-uid key-uid + :identicon identicon + :name name + :save-password? true)) :keycard/login-with-keycard {:multiaccount-data multiaccount-data :key-uid key-uid diff --git a/src/status_im/keycard/mnemonic.cljs b/src/status_im/keycard/mnemonic.cljs index 0ddb291beb..91ca453241 100644 --- a/src/status_im/keycard/mnemonic.cljs +++ b/src/status_im/keycard/mnemonic.cljs @@ -1,20 +1,20 @@ (ns status-im.keycard.mnemonic - (:require [status-im2.navigation.events :as navigation] + (:require [status-im.keycard.common :as common] + status-im.keycard.fx [status-im.utils.fx :as fx] - [taoensso.timbre :as log] - [status-im.keycard.common :as common] - status-im.keycard.fx)) + [status-im2.navigation.events :as navigation] + [taoensso.timbre :as log])) (fx/defn set-mnemonic [{:keys [db] :as cofx}] (log/debug "[keycard] set-mnemonic") (let [selected-id (get-in db [:intro-wizard :selected-id]) - mnemonic (reduce - (fn [_ {:keys [id mnemonic]}] - (when (= selected-id id) - (reduced mnemonic))) - nil - (get-in db [:intro-wizard :multiaccounts]))] + mnemonic (reduce + (fn [_ {:keys [id mnemonic]}] + (when (= selected-id id) + (reduced mnemonic))) + nil + (get-in db [:intro-wizard :multiaccounts]))] (fx/merge cofx {:db (-> db diff --git a/src/status_im/keycard/nfc.cljs b/src/status_im/keycard/nfc.cljs index cc7837bfc9..46db93c6c7 100644 --- a/src/status_im/keycard/nfc.cljs +++ b/src/status_im/keycard/nfc.cljs @@ -2,8 +2,10 @@ (def is-nfc-supported? (atom nil)) -(defn set-nfc-supported? [supported?] +(defn set-nfc-supported? + [supported?] (reset! is-nfc-supported? supported?)) -(defn nfc-supported? [] +(defn nfc-supported? + [] @is-nfc-supported?) diff --git a/src/status_im/keycard/onboarding.cljs b/src/status_im/keycard/onboarding.cljs index 64cb7d50fd..41f6681702 100644 --- a/src/status_im/keycard/onboarding.cljs +++ b/src/status_im/keycard/onboarding.cljs @@ -1,16 +1,16 @@ (ns status-im.keycard.onboarding (:require [clojure.string :as string] [re-frame.core :as re-frame] + [status-im.constants :as constants] [status-im.i18n.i18n :as i18n] - [status-im2.navigation.events :as navigation] + [status-im.keycard.common :as common] + status-im.keycard.fx + [status-im.keycard.mnemonic :as mnemonic] + [status-im.ui.components.react :as react] [status-im.utils.fx :as fx] [status-im.utils.utils :as utils] - [status-im.keycard.common :as common] - [status-im.keycard.mnemonic :as mnemonic] - [taoensso.timbre :as log] - status-im.keycard.fx - [status-im.ui.components.react :as react] - [status-im.constants :as constants])) + [status-im2.navigation.events :as navigation] + [taoensso.timbre :as log])) (fx/defn begin-setup-button-pressed {:keys [:keycard.ui/begin-setup-button-pressed]} @@ -66,7 +66,8 @@ :content (i18n/label :t/secret-keys-confirmation-text) :confirm-button-text (i18n/label :t/yes) :cancel-button-text (i18n/label :t/cancel) - :on-accept #(re-frame/dispatch [:keycard.onboarding.puk-code.ui/confirm-pressed]) + :on-accept #(re-frame/dispatch + [:keycard.onboarding.puk-code.ui/confirm-pressed]) :on-cancel #()}}) (fx/defn load-finishing-screen @@ -90,12 +91,13 @@ {:events [:keycard.onboarding.recovery-phrase.ui/next-pressed :keycard.ui/recovery-phrase-next-button-pressed]} [_] - {:ui/show-confirmation {:title (i18n/label :t/keycard-recovery-phrase-confirmation-title) - :content (i18n/label :t/keycard-recovery-phrase-confirmation-text) + {:ui/show-confirmation {:title (i18n/label :t/keycard-recovery-phrase-confirmation-title) + :content (i18n/label :t/keycard-recovery-phrase-confirmation-text) :confirm-button-text (i18n/label :t/yes) - :cancel-button-text (i18n/label :t/cancel) - :on-accept #(re-frame/dispatch [:keycard.onboarding.recovery-phrase.ui/confirm-pressed]) - :on-cancel #()}}) + :cancel-button-text (i18n/label :t/cancel) + :on-accept #(re-frame/dispatch + [:keycard.onboarding.recovery-phrase.ui/confirm-pressed]) + :on-cancel #()}}) (fx/defn recovery-phrase-start-confirmation [{:keys [db] :as cofx}] @@ -130,14 +132,16 @@ (fx/defn proceed-with-generating-key [{:keys [db] :as cofx}] - (let [pin (get-in db [:keycard :secrets :pin] + (let [pin (get-in db + [:keycard :secrets :pin] (common/vector->string (get-in db [:keycard :pin :current])))] (if (empty? pin) (fx/merge cofx {:db (-> db - (assoc-in [:keycard :pin] {:enter-step :current - :on-verified :keycard/generate-and-load-key - :current []}) + (assoc-in [:keycard :pin] + {:enter-step :current + :on-verified :keycard/generate-and-load-key + :current []}) (assoc-in [:keycard :setup-step] :loading-keys))} (navigation/navigate-to-cofx :keycard-onboarding-pin nil)) (load-finishing-screen cofx)))) @@ -146,8 +150,8 @@ {:events [:keycard.onboarding.recovery-phrase-confirm-word.ui/next-pressed :keycard.onboarding.recovery-phrase-confirm-word.ui/input-submitted]} [{:keys [db] :as cofx}] - (let [step (get-in db [:keycard :recovery-phrase :step]) - input-word (get-in db [:keycard :recovery-phrase :input-word]) + (let [step (get-in db [:keycard :recovery-phrase :step]) + input-word (get-in db [:keycard :recovery-phrase :input-word]) {:keys [word]} (get-in db [:keycard :recovery-phrase step])] (if (= word input-word) (if (= (:view-id db) :keycard-onboarding-recovery-phrase-confirm-word1) @@ -183,7 +187,7 @@ {:events [:keycard/start-onboarding-flow]} [{:keys [db] :as cofx}] (fx/merge cofx - {:db (assoc-in db [:keycard :flow] :create) + {:db (assoc-in db [:keycard :flow] :create) :keycard/check-nfc-enabled nil} (common/listen-to-hardware-back-button) (navigation/navigate-to-cofx :keycard-onboarding-intro nil))) @@ -193,19 +197,22 @@ [_] {:keycard/open-nfc-settings nil}) -(defn- show-recover-confirmation [] +(defn- show-recover-confirmation + [] {:ui/show-confirmation {:title (i18n/label :t/are-you-sure?) :content (i18n/label :t/are-you-sure-description) :confirm-button-text (clojure.string/upper-case (i18n/label :t/yes)) :cancel-button-text (i18n/label :t/see-it-again) - :on-accept #(re-frame/dispatch [:keycard.ui/recovery-phrase-confirm-pressed]) - :on-cancel #(re-frame/dispatch [:keycard.ui/recovery-phrase-cancel-pressed])}}) + :on-accept #(re-frame/dispatch + [:keycard.ui/recovery-phrase-confirm-pressed]) + :on-cancel #(re-frame/dispatch + [:keycard.ui/recovery-phrase-cancel-pressed])}}) (fx/defn recovery-phrase-confirm-word {:events [:keycard.ui/recovery-phrase-confirm-word-next-button-pressed]} [{:keys [db]}] - (let [step (get-in db [:keycard :recovery-phrase :step]) - input-word (get-in db [:keycard :recovery-phrase :input-word]) + (let [step (get-in db [:keycard :recovery-phrase :step]) + input-word (get-in db [:keycard :recovery-phrase :input-word]) {:keys [word]} (get-in db [:keycard :recovery-phrase step])] (if (= word input-word) (if (= step :word1) @@ -256,16 +263,19 @@ {:keys [selected-id multiaccounts]} (:intro-wizard db) - multiaccount (or (->> multiaccounts - (filter #(= (:id %) selected-id)) - first) - (assoc (get-in db [:intro-wizard :root-key]) - :derived - (get-in db [:intro-wizard :derived]))) - recovery-mnemonic (get-in db [:intro-wizard :passphrase]) - mnemonic (or (:mnemonic multiaccount) - recovery-mnemonic) - pin' (or pin (common/vector->string (get-in db [:keycard :pin :current])))] + multiaccount (or (->> multiaccounts + (filter #(= (:id %) selected-id)) + first) + (assoc (get-in db [:intro-wizard :root-key]) + :derived + (get-in db [:intro-wizard :derived]))) + recovery-mnemonic (get-in db [:intro-wizard :passphrase]) + mnemonic (or (:mnemonic multiaccount) + recovery-mnemonic) + pin' (or pin + (common/vector->string (get-in db + [:keycard :pin + :current])))] (fx/merge cofx {:keycard/generate-and-load-key {:mnemonic mnemonic @@ -285,7 +295,9 @@ cofx {:db (-> db (update :keycard - dissoc :secrets :card-state :multiaccount-wallet-address + dissoc + :secrets + :card-state :multiaccount-wallet-address :multiaccount-whisper-public-key :application-info) (assoc-in [:keycard :setup-step] :begin) (assoc-in [:keycard :pin :on-verified] nil))} diff --git a/src/status_im/keycard/real_keycard.cljs b/src/status_im/keycard/real_keycard.cljs index fc867a87b9..f52d25f3c0 100644 --- a/src/status_im/keycard/real_keycard.cljs +++ b/src/status_im/keycard/real_keycard.cljs @@ -1,56 +1,65 @@ (ns status-im.keycard.real-keycard (:require ["react-native" :as rn] ["react-native-status-keycard" :default status-keycard] + [clojure.string :as string] [status-im.ethereum.core :as ethereum] [status-im.keycard.keycard :as keycard] [status-im.native-module.core :as status] [status-im.utils.platform :as platform] [status-im.utils.types :as types] - [clojure.string :as string] [taoensso.timbre :as log])) -(defonce event-emitter (if platform/ios? - (new (.-NativeEventEmitter rn) status-keycard) - (.-DeviceEventEmitter rn))) +(defonce event-emitter + (if platform/ios? + (new (.-NativeEventEmitter rn) status-keycard) + (.-DeviceEventEmitter rn))) (defonce active-listeners (atom [])) -(defn start-nfc [{:keys [on-success on-failure prompt-message]}] +(defn start-nfc + [{:keys [on-success on-failure prompt-message]}] (log/debug "start-nfc") (.. status-keycard (startNFC (str prompt-message)) (then on-success) (catch on-failure))) -(defn stop-nfc [{:keys [on-success on-failure error-message]}] +(defn stop-nfc + [{:keys [on-success on-failure error-message]}] (log/debug "stop-nfc") (.. status-keycard (stopNFC (str error-message)) (then on-success) (catch on-failure))) -(defn set-nfc-message [{:keys [on-success on-failure status-message]}] +(defn set-nfc-message + [{:keys [on-success on-failure status-message]}] (log/debug "set-nfc-message") (.. status-keycard (setNFCMessage (str status-message)) (then on-success) (catch on-failure))) -(defn check-nfc-support [{:keys [on-success]}] +(defn check-nfc-support + [{:keys [on-success]}] (.. status-keycard nfcIsSupported (then on-success))) -(defn check-nfc-enabled [{:keys [on-success]}] +(defn check-nfc-enabled + [{:keys [on-success]}] (.. status-keycard nfcIsEnabled (then on-success))) -(defn open-nfc-settings [] +(defn open-nfc-settings + [] (.openNfcSettings status-keycard)) -(defn remove-event-listeners [] - (doseq [event ["keyCardOnConnected" "keyCardOnDisconnected", "keyCardOnNFCUserCancelled", "keyCardOnNFCTimeout"]] +(defn remove-event-listeners + [] + (doseq [event ["keyCardOnConnected" "keyCardOnDisconnected" "keyCardOnNFCUserCancelled" + "keyCardOnNFCTimeout"]] (.removeAllListeners ^js event-emitter event))) (defn remove-event-listener @@ -81,7 +90,8 @@ [callback] (.addListener ^js event-emitter "keyCardOnNFCDisabled" callback)) -(defn set-pairings [{:keys [pairings]}] +(defn set-pairings + [{:keys [pairings]}] (.. status-keycard (setPairings (clj->js (or pairings {}))))) (defn register-card-events @@ -89,12 +99,12 @@ (doseq [listener @active-listeners] (remove-event-listener listener)) (reset! active-listeners - [(on-card-connected (:on-card-connected args)) - (on-card-disconnected (:on-card-disconnected args)) - (on-nfc-user-cancelled (:on-nfc-user-cancelled args)) - (on-nfc-timeout (:on-nfc-timeout args)) - (on-nfc-enabled (:on-nfc-enabled args)) - (on-nfc-disabled (:on-nfc-disabled args))])) + [(on-card-connected (:on-card-connected args)) + (on-card-disconnected (:on-card-disconnected args)) + (on-nfc-user-cancelled (:on-nfc-user-cancelled args)) + (on-nfc-timeout (:on-nfc-timeout args)) + (on-nfc-enabled (:on-nfc-enabled args)) + (on-nfc-disabled (:on-nfc-disabled args))])) (defn get-application-info [{:keys [on-success on-failure]}] @@ -119,25 +129,29 @@ (on-success info)))) (catch on-failure))) -(defn install-applet [{:keys [on-success on-failure]}] +(defn install-applet + [{:keys [on-success on-failure]}] (.. status-keycard installApplet (then on-success) (catch on-failure))) -(defn install-cash-applet [{:keys [on-success on-failure]}] +(defn install-cash-applet + [{:keys [on-success on-failure]}] (.. status-keycard installCashApplet (then on-success) (catch on-failure))) -(defn init-card [{:keys [pin on-success on-failure]}] +(defn init-card + [{:keys [pin on-success on-failure]}] (.. status-keycard (init pin) (then on-success) (catch on-failure))) -(defn install-applet-and-init-card [{:keys [pin on-success on-failure]}] +(defn install-applet-and-init-card + [{:keys [pin on-success on-failure]}] (.. status-keycard (installAppletAndInitCard pin) (then on-success) @@ -206,7 +220,8 @@ (then on-success) (catch on-failure)))) -(defn delete [{:keys [on-success on-failure]}] +(defn delete + [{:keys [on-success on-failure]}] (.. status-keycard (delete) (then on-success) @@ -290,7 +305,8 @@ (types/clj->json accounts-data) chat-key)) -(defn login [args] +(defn login + [args] (status/login-with-keycard args)) (defn send-transaction-with-signature @@ -309,81 +325,81 @@ (defrecord RealKeycard [] keycard/Keycard - (keycard/start-nfc [_this args] - (start-nfc args)) - (keycard/stop-nfc [_this args] - (stop-nfc args)) - (keycard/set-nfc-message [_this args] - (set-nfc-message args)) - (keycard/check-nfc-support [_this args] - (check-nfc-support args)) - (keycard/check-nfc-enabled [_this args] - (check-nfc-enabled args)) - (keycard/open-nfc-settings [_this] - (open-nfc-settings)) - (keycard/register-card-events [_this args] - (register-card-events args)) - (keycard/on-card-connected [_this callback] - (on-card-connected callback)) - (keycard/on-card-disconnected [_this callback] - (on-card-disconnected callback)) - (keycard/remove-event-listener [_this event] - (remove-event-listener event)) - (keycard/remove-event-listeners [_this] - (remove-event-listeners)) - (keycard/set-pairings [_this args] - (set-pairings args)) - (keycard/get-application-info [_this args] - (get-application-info args)) - (keycard/factory-reset [_this args] - (factory-reset args)) - (keycard/install-applet [_this args] - (install-applet args)) - (keycard/install-cash-applet [_this args] - (install-cash-applet args)) - (keycard/init-card [_this args] - (init-card args)) - (keycard/install-applet-and-init-card [_this args] - (install-applet-and-init-card args)) - (keycard/pair [_this args] - (pair args)) - (keycard/generate-and-load-key [_this args] - (generate-and-load-key args)) - (keycard/unblock-pin [_this args] - (unblock-pin args)) - (keycard/verify-pin [_this args] - (verify-pin args)) - (keycard/change-pin [_this args] - (change-pin args)) - (keycard/change-puk [_this args] - (change-puk args)) - (keycard/change-pairing [_this args] - (change-pairing args)) - (keycard/unpair [_this args] - (unpair args)) - (keycard/delete [_this args] - (delete args)) - (keycard/remove-key [_this args] - (remove-key args)) - (keycard/remove-key-with-unpair [_this args] - (remove-key-with-unpair args)) - (keycard/export-key [_this args] - (export-key args)) - (keycard/unpair-and-delete [_this args] - (unpair-and-delete args)) - (keycard/import-keys [_this args] - (import-keys args)) - (keycard/get-keys [_this args] - (get-keys args)) - (keycard/sign [_this args] - (sign args)) - (keycard/sign-typed-data [_this args] - (sign-typed-data args)) - (keycard/save-multiaccount-and-login [_this args] - (save-multiaccount-and-login args)) - (keycard/login [_this args] - (login args)) - (keycard/send-transaction-with-signature [_this args] - (send-transaction-with-signature args)) - (keycard/delete-multiaccount-before-migration [_ args] - (delete-multiaccount-before-migration args))) + (keycard/start-nfc [_this args] + (start-nfc args)) + (keycard/stop-nfc [_this args] + (stop-nfc args)) + (keycard/set-nfc-message [_this args] + (set-nfc-message args)) + (keycard/check-nfc-support [_this args] + (check-nfc-support args)) + (keycard/check-nfc-enabled [_this args] + (check-nfc-enabled args)) + (keycard/open-nfc-settings [_this] + (open-nfc-settings)) + (keycard/register-card-events [_this args] + (register-card-events args)) + (keycard/on-card-connected [_this callback] + (on-card-connected callback)) + (keycard/on-card-disconnected [_this callback] + (on-card-disconnected callback)) + (keycard/remove-event-listener [_this event] + (remove-event-listener event)) + (keycard/remove-event-listeners [_this] + (remove-event-listeners)) + (keycard/set-pairings [_this args] + (set-pairings args)) + (keycard/get-application-info [_this args] + (get-application-info args)) + (keycard/factory-reset [_this args] + (factory-reset args)) + (keycard/install-applet [_this args] + (install-applet args)) + (keycard/install-cash-applet [_this args] + (install-cash-applet args)) + (keycard/init-card [_this args] + (init-card args)) + (keycard/install-applet-and-init-card [_this args] + (install-applet-and-init-card args)) + (keycard/pair [_this args] + (pair args)) + (keycard/generate-and-load-key [_this args] + (generate-and-load-key args)) + (keycard/unblock-pin [_this args] + (unblock-pin args)) + (keycard/verify-pin [_this args] + (verify-pin args)) + (keycard/change-pin [_this args] + (change-pin args)) + (keycard/change-puk [_this args] + (change-puk args)) + (keycard/change-pairing [_this args] + (change-pairing args)) + (keycard/unpair [_this args] + (unpair args)) + (keycard/delete [_this args] + (delete args)) + (keycard/remove-key [_this args] + (remove-key args)) + (keycard/remove-key-with-unpair [_this args] + (remove-key-with-unpair args)) + (keycard/export-key [_this args] + (export-key args)) + (keycard/unpair-and-delete [_this args] + (unpair-and-delete args)) + (keycard/import-keys [_this args] + (import-keys args)) + (keycard/get-keys [_this args] + (get-keys args)) + (keycard/sign [_this args] + (sign args)) + (keycard/sign-typed-data [_this args] + (sign-typed-data args)) + (keycard/save-multiaccount-and-login [_this args] + (save-multiaccount-and-login args)) + (keycard/login [_this args] + (login args)) + (keycard/send-transaction-with-signature [_this args] + (send-transaction-with-signature args)) + (keycard/delete-multiaccount-before-migration [_ args] + (delete-multiaccount-before-migration args))) diff --git a/src/status_im/keycard/recovery.cljs b/src/status_im/keycard/recovery.cljs index 12c51e3544..dd9d3019a5 100644 --- a/src/status_im/keycard/recovery.cljs +++ b/src/status_im/keycard/recovery.cljs @@ -1,27 +1,28 @@ (ns status-im.keycard.recovery - (:require [status-im2.navigation.events :as navigation] - [status-im.utils.datetime :as utils.datetime] - [status-im.multiaccounts.create.core :as multiaccounts.create] - [status-im.multiaccounts.model :as multiaccounts.model] - [status-im.utils.fx :as fx] + (:require [clojure.string :as string] [re-frame.core :as re-frame] - [clojure.string :as string] + [status-im.bottom-sheet.core :as bottom-sheet] + [status-im.constants :as constants] + [status-im.ethereum.core :as ethereum] + [status-im.ethereum.eip55 :as eip55] [status-im.i18n.i18n :as i18n] - [taoensso.timbre :as log] [status-im.keycard.common :as common] status-im.keycard.fx - [status-im.constants :as constants] - [status-im.ethereum.eip55 :as eip55] - [status-im.ethereum.core :as ethereum] - [status-im.bottom-sheet.core :as bottom-sheet] + [status-im.multiaccounts.create.core :as multiaccounts.create] + [status-im.multiaccounts.model :as multiaccounts.model] [status-im.native-module.core :as status] [status-im.popover.core :as popover] - [status-im.utils.types :as types] - [utils.security.core :as security] + [status-im.utils.datetime :as utils.datetime] + [status-im.utils.fx :as fx] [status-im.utils.keychain.core :as keychain] - [status-im.utils.platform :as platform])) + [status-im.utils.platform :as platform] + [status-im.utils.types :as types] + [status-im2.navigation.events :as navigation] + [taoensso.timbre :as log] + [utils.security.core :as security])) -(fx/defn pair* [_ password] +(fx/defn pair* + [_ password] {:keycard/pair {:password password}}) (fx/defn pair @@ -38,7 +39,7 @@ :keycard.ui/pair-code-next-button-pressed :keycard.onboarding.pair.ui/next-pressed]} [{:keys [db] :as cofx}] - (let [pairing (get-in db [:keycard :secrets :pairing]) + (let [pairing (get-in db [:keycard :secrets :pairing]) paired-on (get-in db [:keycard :secrets :paired-on] (utils.datetime/timestamp))] (fx/merge cofx (if pairing @@ -51,8 +52,8 @@ [{:keys [db] :as cofx}] (log/debug "[keycard] load-pair-screen") (fx/merge cofx - {:db (-> db - (assoc-in [:keycard :setup-step] :pair)) + {:db (-> db + (assoc-in [:keycard :setup-step] :pair)) :dispatch [:bottom-sheet/hide]} (common/listen-to-hardware-back-button) (navigation/navigate-to-cofx :keycard-recovery-pair nil))) @@ -85,7 +86,7 @@ {:events [:recovery.ui/keycard-option-pressed]} [{:keys [db] :as cofx}] (fx/merge cofx - {:db (assoc-in db [:keycard :flow] :recovery) + {:db (assoc-in db [:keycard :flow] :recovery) :keycard/check-nfc-enabled nil} (common/listen-to-hardware-back-button) (navigation/navigate-to-cofx :keycard-onboarding-intro nil))) @@ -104,7 +105,9 @@ cofx {:db (-> db (update :keycard - dissoc :secrets :card-state :multiaccount-wallet-address + dissoc + :secrets + :card-state :multiaccount-wallet-address :multiaccount-whisper-public-key :application-info) (assoc-in [:keycard :setup-step] :begin) @@ -119,11 +122,14 @@ {:events [:keycard.recovery.success/finish-pressed]} [{:keys [db] :as cofx}] (fx/merge cofx - {:db (update db :keycard dissoc + {:db (update db + :keycard dissoc :multiaccount-wallet-address :multiaccount-whisper-public-key)} (navigation/navigate-to-cofx (if platform/android? - :notifications-settings :welcome) nil))) + :notifications-settings + :welcome) + nil))) (fx/defn intro-wizard {:events [:multiaccounts.create.ui/intro-wizard]} @@ -142,12 +148,12 @@ {:events [:keycard.recovery.no-key.ui/generate-key-pressed]} [{:keys [db] :as cofx}] (fx/merge cofx - {:db (assoc-in db [:keycard :flow] :create) + {:db (assoc-in db [:keycard :flow] :create) :keycard/check-nfc-enabled nil} (intro-wizard))) (fx/defn create-keycard-multiaccount - {:events [::create-keycard-multiaccount] + {:events [::create-keycard-multiaccount] :interceptors [(re-frame/inject-cofx :random-guid-generator) (re-frame/inject-cofx ::multiaccounts.create/get-signing-phrase)]} [{:keys [db] :as cofx}] @@ -166,13 +172,14 @@ encryption-public-key instance-uid key-uid - recovered]} multiaccount - {:keys [pairing paired-on]} secrets + recovered]} + multiaccount + {:keys [pairing paired-on]} secrets {:keys [name identicon]} (if (nil? name) ;; name might have been generated during recovery via passphrase (get-in db [:intro-wizard :derived constants/path-whisper-keyword]) - {:name name + {:name name :identicon identicon})] ;; if a name is still `nil` we have to generate it before multiaccount's ;; creation otherwise spec validation will fail @@ -193,7 +200,7 @@ {:public-key whisper-public-key :address (eip55/address->checksum whisper-address) :name name - :identicon identicon} + :identicon identicon} constants/path-default-wallet-keyword {:public-key wallet-public-key :address (eip55/address->checksum wallet-address)}} @@ -211,20 +218,25 @@ [{:keys [db] :as cofx}] (fx/merge cofx {:db (-> db - (update-in [:keycard :pin] assoc :enter-step :login - :status nil - :login []) + (update-in [:keycard :pin] + assoc + :enter-step :login + :status nil + :login []) (update :keycard dissoc :application-info))} - (navigation/set-stack-root :multiaccounts-stack [:multiaccounts - :keycard-login-pin]))) + (navigation/set-stack-root :multiaccounts-stack + [:multiaccounts + :keycard-login-pin]))) (fx/defn on-backup-success [{:keys [db] :as cofx} backup-type] (fx/merge cofx - {:utils/show-popup {:title (i18n/label (if (= backup-type :recovery-card) - :t/keycard-access-reset :t/keycard-backup-success-title)) - :content (i18n/label (if (= backup-type :recovery-card) - :t/keycard-can-use-with-new-passcode :t/keycard-backup-success-body))}} + {:utils/show-popup {:title (i18n/label (if (= backup-type :recovery-card) + :t/keycard-access-reset + :t/keycard-backup-success-title)) + :content (i18n/label (if (= backup-type :recovery-card) + :t/keycard-can-use-with-new-passcode + :t/keycard-backup-success-body))}} (cond (multiaccounts.model/logged-in? cofx) (navigation/set-stack-root :profile-stack [:my-profile :keycard-settings]) @@ -246,33 +258,37 @@ #(let [{:keys [error]} (types/json->clj %)] (if (string/blank? error) (status/login-with-keycard login-params) - (throw (js/Error. "Please shake the phone to report this error and restart the app. Migration failed unexpectedly."))))))) + (throw + (js/Error. + "Please shake the phone to report this error and restart the app. Migration failed unexpectedly."))))))) (fx/defn migrate-account [{:keys [db] :as cofx}] - (let [pairing (get-in db [:keycard :secrets :pairing]) - paired-on (get-in db [:keycard :secrets :paired-on]) - instance-uid (get-in db [:keycard :multiaccount :instance-uid]) - account (-> db - :multiaccounts/login - (assoc :keycard-pairing pairing) - (assoc :save-password? false)) - key-uid (-> account :key-uid) - settings {:keycard-instance-uid instance-uid - :keycard-paired-on paired-on - :keycard-pairing pairing} - password (ethereum/sha3 (security/safe-unmask-data (get-in db [:keycard :migration-password]))) + (let [pairing (get-in db [:keycard :secrets :pairing]) + paired-on (get-in db [:keycard :secrets :paired-on]) + instance-uid (get-in db [:keycard :multiaccount :instance-uid]) + account (-> db + :multiaccounts/login + (assoc :keycard-pairing pairing) + (assoc :save-password? false)) + key-uid (-> account :key-uid) + settings {:keycard-instance-uid instance-uid + :keycard-paired-on paired-on + :keycard-pairing pairing} + password (ethereum/sha3 (security/safe-unmask-data (get-in db + [:keycard + :migration-password]))) encryption-pass (get-in db [:keycard :multiaccount :encryption-public-key]) - login-params {:key-uid key-uid - :multiaccount-data (types/clj->json account) - :password encryption-pass - :chat-key (get-in db [:keycard :multiaccount :whisper-private-key])}] - {:db (-> db - (assoc-in [:multiaccounts/multiaccounts key-uid :keycard-pairing] pairing) - (assoc :multiaccounts/login account) - (assoc :auth-method keychain/auth-method-none) - (update :keycard dissoc :flow :migration-password) - (dissoc :recovered-account?)) + login-params {:key-uid key-uid + :multiaccount-data (types/clj->json account) + :password encryption-pass + :chat-key (get-in db [:keycard :multiaccount :whisper-private-key])}] + {:db (-> db + (assoc-in [:multiaccounts/multiaccounts key-uid :keycard-pairing] pairing) + (assoc :multiaccounts/login account) + (assoc :auth-method keychain/auth-method-none) + (update :keycard dissoc :flow :migration-password) + (dissoc :recovered-account?)) ::finish-migration [account settings password encryption-pass login-params]})) (fx/defn delete-multiaccount @@ -294,38 +310,40 @@ (re-frame/inject-cofx ::multiaccounts.create/get-signing-phrase)]} [{:keys [db random-guid-generator] :as cofx} data] (let [account-data (js->clj data :keywordize-keys true) - backup? (get-in db [:keycard :creating-backup?]) - migration? (get-in db [:keycard :converting-account?])] - (fx/merge cofx - {:db (-> db - (assoc-in [:keycard :multiaccount] - (-> account-data - (update :address ethereum/normalized-hex) - (update :whisper-address ethereum/normalized-hex) - (update :wallet-address ethereum/normalized-hex) - (update :wallet-root-address ethereum/normalized-hex) - (update :public-key ethereum/normalized-hex) - (update :whisper-public-key ethereum/normalized-hex) - (update :wallet-public-key ethereum/normalized-hex) - (update :wallet-root-public-key ethereum/normalized-hex) - (update :instance-uid #(get-in db [:keycard :multiaccount :instance-uid] %)))) - (assoc-in [:keycard :multiaccount-wallet-address] (:wallet-address account-data)) - (assoc-in [:keycard :multiaccount-whisper-public-key] (:whisper-public-key account-data)) - (assoc-in [:keycard :pin :status] nil) - (assoc-in [:keycard :application-info :key-uid] - (ethereum/normalized-hex (:key-uid account-data))) - (update :keycard dissoc :recovery-phrase :creating-backup? :converting-account?) - (update-in [:keycard :secrets] dissoc :pin :puk :password :mnemonic) - (assoc :multiaccounts/new-installation-id (random-guid-generator)))} - (common/remove-listener-to-hardware-back-button) - (common/hide-connection-sheet) - (cond backup? (on-backup-success backup?) - migration? (migrate-account) + backup? (get-in db [:keycard :creating-backup?]) + migration? (get-in db [:keycard :converting-account?])] + (fx/merge + cofx + {:db (-> db + (assoc-in + [:keycard :multiaccount] + (-> account-data + (update :address ethereum/normalized-hex) + (update :whisper-address ethereum/normalized-hex) + (update :wallet-address ethereum/normalized-hex) + (update :wallet-root-address ethereum/normalized-hex) + (update :public-key ethereum/normalized-hex) + (update :whisper-public-key ethereum/normalized-hex) + (update :wallet-public-key ethereum/normalized-hex) + (update :wallet-root-public-key ethereum/normalized-hex) + (update :instance-uid #(get-in db [:keycard :multiaccount :instance-uid] %)))) + (assoc-in [:keycard :multiaccount-wallet-address] (:wallet-address account-data)) + (assoc-in [:keycard :multiaccount-whisper-public-key] (:whisper-public-key account-data)) + (assoc-in [:keycard :pin :status] nil) + (assoc-in [:keycard :application-info :key-uid] + (ethereum/normalized-hex (:key-uid account-data))) + (update :keycard dissoc :recovery-phrase :creating-backup? :converting-account?) + (update-in [:keycard :secrets] dissoc :pin :puk :password :mnemonic) + (assoc :multiaccounts/new-installation-id (random-guid-generator)))} + (common/remove-listener-to-hardware-back-button) + (common/hide-connection-sheet) + (cond backup? (on-backup-success backup?) + migration? (migrate-account) - (get-in db [:keycard :delete-account?]) - (delete-multiaccount) + (get-in db [:keycard :delete-account?]) + (delete-multiaccount) - :else (create-keycard-multiaccount))))) + :else (create-keycard-multiaccount))))) (fx/defn on-generate-and-load-key-error {:events [:keycard.callback/on-generate-and-load-key-error]} @@ -349,11 +367,12 @@ {:db (-> db (assoc-in [:keycard :multiaccount :instance-uid] instance-uid) (assoc-in [:keycard :pin :status] :verifying) - (assoc-in [:keycard :secrets] {:pairing pairing' - :paired-on (utils.datetime/timestamp)})) + (assoc-in [:keycard :secrets] + {:pairing pairing' + :paired-on (utils.datetime/timestamp)})) :keycard/import-keys - {:pin pin - :on-success :keycard.callback/on-generate-and-load-key-success}}))) + {:pin pin + :on-success :keycard.callback/on-generate-and-load-key-success}}))) (fx/defn load-recovering-key-screen {:events [:keycard/load-recovering-key-screen]} @@ -364,16 +383,17 @@ :handler (common/dispatch-event :keycard/import-multiaccount)})) (fx/defn on-name-and-photo-generated - {:events [::on-name-and-photo-generated] + {:events [::on-name-and-photo-generated] :interceptors [(re-frame/inject-cofx :random-guid-generator) (re-frame/inject-cofx ::multiaccounts.create/get-signing-phrase)]} [{:keys [db] :as cofx} whisper-name identicon] (fx/merge cofx - {:db (update-in db [:keycard :multiaccount] + {:db (update-in db + [:keycard :multiaccount] (fn [multiacc] (assoc multiacc :recovered (get db :recovered-account?) - :name whisper-name + :name whisper-name :identicon identicon)))} (create-keycard-multiaccount))) diff --git a/src/status_im/keycard/sign.cljs b/src/status_im/keycard/sign.cljs index 414e86e687..29535d1730 100644 --- a/src/status_im/keycard/sign.cljs +++ b/src/status_im/keycard/sign.cljs @@ -1,33 +1,35 @@ (ns status-im.keycard.sign - (:require [re-frame.core :as re-frame] + (:require [clojure.string :as string] + [re-frame.core :as re-frame] [status-im.ethereum.core :as ethereum] [status-im.ethereum.json-rpc :as json-rpc] + [status-im.keycard.common :as common] [status-im.utils.fx :as fx] [status-im.utils.money :as money] [status-im.utils.types :as types] - [taoensso.timbre :as log] - [status-im.keycard.common :as common] - [clojure.string :as string])) + [taoensso.timbre :as log])) (fx/defn sign {:events [:keycard/sign]} [{:keys [db] :as cofx} hash on-success] - (let [card-connected? (get-in db [:keycard :card-connected?]) - key-uid (get-in db [:multiaccount :key-uid]) - keycard-key-uid (get-in db [:keycard :application-info :key-uid]) - keycard-pin-retries (get-in db [:keycard :application-info :pin-retry-counter]) - keycard-match? (= key-uid keycard-key-uid) - hash (or hash (get-in db [:keycard :hash])) - data (get-in db [:keycard :data]) - typed? (get-in db [:keycard :typed?]) - pin (common/vector->string (get-in db [:keycard :pin :sign])) - from (or (get-in db [:signing/tx :from :address]) (get-in db [:signing/tx :message :from]) (ethereum/default-address db)) - path (reduce - (fn [_ {:keys [address path]}] - (when (ethereum/address= from address) - (reduced path))) - nil - (:multiaccount/accounts db))] + (let [card-connected? (get-in db [:keycard :card-connected?]) + key-uid (get-in db [:multiaccount :key-uid]) + keycard-key-uid (get-in db [:keycard :application-info :key-uid]) + keycard-pin-retries (get-in db [:keycard :application-info :pin-retry-counter]) + keycard-match? (= key-uid keycard-key-uid) + hash (or hash (get-in db [:keycard :hash])) + data (get-in db [:keycard :data]) + typed? (get-in db [:keycard :typed?]) + pin (common/vector->string (get-in db [:keycard :pin :sign])) + from (or (get-in db [:signing/tx :from :address]) + (get-in db [:signing/tx :message :from]) + (ethereum/default-address db)) + path (reduce + (fn [_ {:keys [address path]}] + (when (ethereum/address= from address) + (reduced path))) + nil + (:multiaccount/accounts db))] (cond (not keycard-match?) (common/show-wrong-keycard-alert cofx) @@ -37,10 +39,11 @@ {:db (assoc-in db [:signing/sign :keycard-step] :signing)} (common/set-on-card-connected :keycard/sign)) - (pos? keycard-pin-retries) ; if 0, get-application-info will have already closed the connection sheet and opened the frozen card popup - {:db (-> db - (assoc-in [:keycard :card-read-in-progress?] true) - (assoc-in [:keycard :pin :status] :verifying)) + (pos? keycard-pin-retries) ; if 0, get-application-info will have already closed the connection + ; sheet and opened the frozen card popup + {:db (-> db + (assoc-in [:keycard :card-read-in-progress?] true) + (assoc-in [:keycard :pin :status] :verifying)) :keycard/sign {:hash (ethereum/naked-address hash) :data data :typed? typed? ; this parameter is for e2e @@ -48,19 +51,20 @@ :pin pin :path path}}))) -(defn normalize-signature [signature] +(defn normalize-signature + [signature] (-> signature - (string/replace-first #"00$", "1b") - (string/replace-first #"01$", "1c") + (string/replace-first #"00$" "1b") + (string/replace-first #"01$" "1c") ethereum/normalized-hex)) (fx/defn sign-message {:events [:keycard/sign-message]} [cofx params result] (let [{:keys [result error]} (types/json->clj result) - on-success #(re-frame/dispatch [:keycard/on-sign-message-success params - (normalize-signature %)]) - hash (ethereum/naked-address result)] + on-success #(re-frame/dispatch [:keycard/on-sign-message-success params + (normalize-signature %)]) + hash (ethereum/naked-address result)] (sign cofx hash on-success))) (fx/defn on-sign-message-success @@ -73,9 +77,9 @@ [:sign/send-accept-transaction-message message-id tx-hash signature] [:sign/send-transaction-message chat-id value contract tx-hash signature]) :signing/show-transaction-result nil - :db (-> db - (assoc-in [:keycard :pin :sign] []) - (assoc-in [:keycard :pin :status] nil))} + :db (-> db + (assoc-in [:keycard :pin :sign] []) + (assoc-in [:keycard :pin :status] nil))} (common/clear-on-card-connected) (common/get-application-info nil) (common/hide-connection-sheet))) @@ -84,7 +88,7 @@ {:events [:keycard/sign-typed-data]} [{:keys [db] :as cofx}] (let [card-connected? (get-in db [:keycard :card-connected?]) - hash (get-in db [:keycard :hash])] + hash (get-in db [:keycard :hash])] (if card-connected? {:db (-> db (assoc-in [:keycard :card-read-in-progress?] true) @@ -100,19 +104,22 @@ {:db (-> db (assoc-in [:signing/sign :formatted-data :message :formatted-currency] symbol) (update-in [:signing/sign :formatted-data :message] - #(assoc % :formatted-amount (.dividedBy ^js (money/bignumber (:amount %)) - (money/bignumber (money/from-decimal decimals))))))}) + #(assoc % + :formatted-amount + (.dividedBy ^js (money/bignumber (:amount %)) + (money/bignumber (money/from-decimal decimals))))))}) (fx/defn store-hash-and-sign-typed {:events [:keycard/store-hash-and-sign-typed]} [{:keys [db] :as cofx} result] - (let [{:keys [result]} (types/json->clj result) - message (get-in db [:signing/sign :formatted-data :message]) + (let [{:keys [result]} (types/json->clj result) + message (get-in db [:signing/sign :formatted-data :message]) currency-contract (:currency message)] (when currency-contract - {::json-rpc/call [{:method "wallet_discoverToken" - :params [(ethereum/chain-id db) currency-contract] - :on-success #(re-frame/dispatch [:keycard/fetch-currency-token-on-success %])}]}) + {::json-rpc/call [{:method "wallet_discoverToken" + :params [(ethereum/chain-id db) currency-contract] + :on-success #(re-frame/dispatch [:keycard/fetch-currency-token-on-success + %])}]}) (fx/merge cofx {:db (assoc-in db [:keycard :hash] result)} sign-typed-data))) @@ -140,7 +147,8 @@ (log/debug "[keycard] sign success: " signature) (let [signature-json (types/clj->json {:result (normalize-signature signature)}) transaction (get-in db [:keycard :transaction]) - tx-obj (select-keys transaction [:from :to :value :gas :gasPrice :command? :chat-id :message-id]) + tx-obj (select-keys transaction + [:from :to :value :gas :gasPrice :command? :chat-id :message-id]) command? (:command? transaction)] (fx/merge cofx {:db (-> db @@ -168,13 +176,14 @@ [{:keys [db] :as cofx} error] (log/debug "[keycard] sign error: " error) (let [tag-was-lost? (common/tag-lost? (:error error)) - pin-retries (common/pin-retries (:error error))] + pin-retries (common/pin-retries (:error error))] (when-not tag-was-lost? (if (not (nil? pin-retries)) (fx/merge cofx {:db (-> db (assoc-in [:keycard :application-info :pin-retry-counter] pin-retries) - (update-in [:keycard :pin] assoc + (update-in [:keycard :pin] + assoc :status :error :sign [] :error-label :t/pin-mismatch) diff --git a/src/status_im/keycard/simulated_keycard.cljs b/src/status_im/keycard/simulated_keycard.cljs index 71ef3b776a..f89cf2554d 100644 --- a/src/status_im/keycard/simulated_keycard.cljs +++ b/src/status_im/keycard/simulated_keycard.cljs @@ -24,136 +24,165 @@ (defonce state (atom initial-state)) -(defn connect-card [] +(defn connect-card + [] (swap! state assoc :card-connected? true) (doseq [callback (vals (get @state :on-card-connected))] (callback))) -(defn connect-selected-card [] - (swap! state assoc :application-info - {:free-pairing-slots 5 - :app-version "3.0" - :secure-channel-pub-key "04c3071768912a515c00aeab7ceb8a5bfda91d036f4a4e60b7944cee3ca7fb67b6d118e8df1e2480b87fd636c6615253245bbbc93a6a407f155f2c58f76c96ef0e", - :instance-uid "1b360b10a9a68b7d494e8f059059f118" - :paired? true - :has-master-key? true - :initialized? true - :pin-retry-counter 3 - :puk-retry-counter 5 - :key-uid (get-in @re-frame.db/app-db [:multiaccounts/login :key-uid])}) +(defn connect-selected-card + [] + (swap! state assoc + :application-info + {:free-pairing-slots 5 + :app-version "3.0" + :secure-channel-pub-key + "04c3071768912a515c00aeab7ceb8a5bfda91d036f4a4e60b7944cee3ca7fb67b6d118e8df1e2480b87fd636c6615253245bbbc93a6a407f155f2c58f76c96ef0e" + :instance-uid "1b360b10a9a68b7d494e8f059059f118" + :paired? true + :has-master-key? true + :initialized? true + :pin-retry-counter 3 + :puk-retry-counter 5 + :key-uid (get-in @re-frame.db/app-db [:multiaccounts/login :key-uid])}) (swap! state assoc :pin default-pin :puk default-puk :password kk1-password) (connect-card)) (def initialization (atom false)) -(defn connect-pairing-card [] +(defn connect-pairing-card + [] (reset! initialization false) - (swap! state assoc :application-info - {:free-pairing-slots 5 - :app-version "3.0" - :secure-channel-pub-key "04c3071768912a515c00aeab7ceb8a5bfda91d036f4a4e60b7944cee3ca7fb67b6d118e8df1e2480b87fd636c6615253245bbbc93a6a407f155f2c58f76c96ef0e", - :instance-uid "1b360b10a9a68b7d494e8f059059f118" - :paired? false - :has-master-key? true - :initialized? true - :pin-retry-counter 3 - :puk-retry-counter 5 - :key-uid "0x16839e8b1b8a395acb18100c7e2b161701c225adf31eefa02114099b4d81a30b"}) + (swap! state assoc + :application-info + {:free-pairing-slots 5 + :app-version "3.0" + :secure-channel-pub-key + "04c3071768912a515c00aeab7ceb8a5bfda91d036f4a4e60b7944cee3ca7fb67b6d118e8df1e2480b87fd636c6615253245bbbc93a6a407f155f2c58f76c96ef0e" + :instance-uid "1b360b10a9a68b7d494e8f059059f118" + :paired? false + :has-master-key? true + :initialized? true + :pin-retry-counter 3 + :puk-retry-counter 5 + :key-uid "0x16839e8b1b8a395acb18100c7e2b161701c225adf31eefa02114099b4d81a30b"}) (swap! state assoc :pin default-pin :puk default-puk :password kk1-password) (connect-card)) -(defn disconnect-card [] +(defn disconnect-card + [] (swap! state assoc :card-connected? false) (doseq [callback (vals (get @state :on-card-disconnected))] (callback))) -(defn reset-state [] +(defn reset-state + [] (swap! state assoc :application-info {:initialized? false})) -(defn- later [f] +(defn- later + [f] (when f (utils/set-timeout f 500))) -(defn pin-error [] - #js {:code "EUNSPECIFIED" - :message (str "Unexpected error SW, 0x63C" (get-in @state [:application-info :pin-retry-counter]))}) +(defn pin-error + [] + #js + {:code "EUNSPECIFIED" + :message (str "Unexpected error SW, 0x63C" (get-in @state [:application-info :pin-retry-counter]))}) -(defn puk-error [] - #js {:code "EUNSPECIFIED" - :message (str "Unexpected error SW, 0x63C" (get-in @state [:application-info :puk-retry-counter]))}) +(defn puk-error + [] + #js + {:code "EUNSPECIFIED" + :message (str "Unexpected error SW, 0x63C" (get-in @state [:application-info :puk-retry-counter]))}) -(defn with-pin [pin on-failure on-valid] +(defn with-pin + [pin on-failure on-valid] (cond (= (get-in @state [:application-info :pin-retry-counter]) 0) (later #(on-failure (pin-error))) (= pin (get @state :pin)) (do - (swap! state update :application-info assoc - :pin-retry-counter 3 - :puk-retry-counter 5) + (swap! state update + :application-info assoc + :pin-retry-counter 3 + :puk-retry-counter 5) (later on-valid)) :else (do (swap! state update-in - [:application-info :pin-retry-counter] - (fnil dec 3)) + [:application-info :pin-retry-counter] + (fnil dec 3)) (later #(on-failure (pin-error)))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(defn start-nfc [{:keys [on-success]}] +(defn start-nfc + [{:keys [on-success]}] (when (get @state :card-connected?) (connect-card)) (later #(on-success true))) -(defn stop-nfc [{:keys [on-success]}] +(defn stop-nfc + [{:keys [on-success]}] (later #(on-success true))) -(defn set-nfc-message [{:keys [on-success]}] +(defn set-nfc-message + [{:keys [on-success]}] (later #(on-success true))) -(defn check-nfc-support [{:keys [on-success]}] +(defn check-nfc-support + [{:keys [on-success]}] (later #(on-success true))) -(defn check-nfc-enabled [{:keys [on-success]}] +(defn check-nfc-enabled + [{:keys [on-success]}] (later #(on-success true))) (defn open-nfc-settings []) -(defn on-card-connected [callback] +(defn on-card-connected + [callback] (let [id (random-uuid)] (swap! state update :on-card-connected assoc id callback) id)) -(defn on-card-disconnected [callback] +(defn on-card-disconnected + [callback] (let [id (random-uuid)] (swap! state update :on-card-disconnected assoc id callback) id)) -(defn register-card-events [args] +(defn register-card-events + [args] (log/debug "register-card-events") (on-card-connected (:on-card-connected args)) (on-card-disconnected (:on-card-disconnected args))) -(defn set-pairings [args] +(defn set-pairings + [args] (log/warn "set-pairings not implemented" args)) -(defn remove-event-listener [id] +(defn remove-event-listener + [id] (log/debug "remove-event-listener") (swap! state update :on-card-connected dissoc id) (swap! state update :on-card-disconnected dissoc id)) -(defn remove-event-listeners [] +(defn remove-event-listeners + [] (log/debug "remove-event-listeners") (swap! state dissoc :on-card-connected) (swap! state dissoc :on-card-disconnected)) -(defn get-application-info [{:keys [on-success]}] +(defn get-application-info + [{:keys [on-success]}] (log/debug "get-application-info") (later #(on-success (get @state :application-info)))) -(defn factory-reset [args] +(defn factory-reset + [args] (log/debug "factory-reset") (reset-state) (get-application-info args)) @@ -161,19 +190,22 @@ (defn install-applet [_]) (defn install-cash-applet [_]) -(defn init-card [{:keys [pin on-success]}] +(defn init-card + [{:keys [pin on-success]}] (reset! initialization true) - (swap! state assoc :application-info - {:free-pairing-slots 5 - :app-version "3.0" - :secure-channel-pub-key "04c3071768912a515c00aeab7ceb8a5bfda91d036f4a4e60b7944cee3ca7fb67b6d118e8df1e2480b87fd636c6615253245bbbc93a6a407f155f2c58f76c96ef0e", - :key-uid "", - :instance-uid "1b360b10a9a68b7d494e8f059059f118" - :paired? false - :has-master-key? false - :pin-retry-counter 3 - :puk-retry-counter 5 - :initialized? true}) + (swap! state assoc + :application-info + {:free-pairing-slots 5 + :app-version "3.0" + :secure-channel-pub-key + "04c3071768912a515c00aeab7ceb8a5bfda91d036f4a4e60b7944cee3ca7fb67b6d118e8df1e2480b87fd636c6615253245bbbc93a6a407f155f2c58f76c96ef0e" + :key-uid "" + :instance-uid "1b360b10a9a68b7d494e8f059059f118" + :paired? false + :has-master-key? false + :pin-retry-counter 3 + :puk-retry-counter 5 + :initialized? true}) (swap! state assoc :pin pin :puk default-puk :password kk1-password) (later #(on-success {:password kk1-password @@ -184,7 +216,8 @@ (def derived-acc (atom nil)) -(defn multiaccount->keys [data] +(defn multiaccount->keys + [data] (let [{:keys [selected-id multiaccounts root-key derived]} data @@ -194,25 +227,26 @@ first) (assoc root-key :derived derived)) - whisper (get derived constants/path-whisper-keyword) - wallet (get derived constants/path-default-wallet-keyword) + whisper (get derived constants/path-whisper-keyword) + wallet (get derived constants/path-default-wallet-keyword) wallet-root (get derived constants/path-wallet-root-keyword) - password account-password - response {:key-uid key-uid - :encryption-public-key password - :address address - :whisper-public-key (:public-key whisper) - :instance-uid "1b360b10a9a68b7d494e8f059059f118" - :wallet-root-public-key (:public-key wallet-root) - :wallet-root-address (:address wallet-root) - :whisper-address (:address whisper) - :public-key public-key - :whisper-private-key "34bc7d0c258c4f2ac1dac4fd6c55c9478bac1f4a9d8b9f1152c8551ab7187b43" - :wallet-address (:address wallet) - :wallet-public-key (:public-key wallet)}] + password account-password + response {:key-uid key-uid + :encryption-public-key password + :address address + :whisper-public-key (:public-key whisper) + :instance-uid "1b360b10a9a68b7d494e8f059059f118" + :wallet-root-public-key (:public-key wallet-root) + :wallet-root-address (:address wallet-root) + :whisper-address (:address whisper) + :public-key public-key + :whisper-private-key "34bc7d0c258c4f2ac1dac4fd6c55c9478bac1f4a9d8b9f1152c8551ab7187b43" + :wallet-address (:address wallet) + :wallet-public-key (:public-key wallet)}] [id response])) -(defn pair [{:keys [password on-success]}] +(defn pair + [{:keys [password on-success]}] (when (and (not @initialization) (not @derived-acc)) (status/multiaccount-import-mnemonic @@ -231,17 +265,18 @@ (fn [result] (let [derived-data (multiaccounts.create/normalize-derived-data-keys (types/json->clj result)) - public-key (get-in derived-data [constants/path-whisper-keyword :public-key])] + public-key (get-in derived-data [constants/path-whisper-keyword :public-key])] (status/gfycat-identicon-async public-key (fn [name photo-path] (let [derived-data-extended (update derived-data constants/path-whisper-keyword - merge {:name name :identicon photo-path})] + merge + {:name name :identicon photo-path})] (reset! derived-acc - {:root-key root-data - :derived derived-data-extended}))))))))))) + {:root-key root-data + :derived derived-data-extended}))))))))))) (when (= password (get @state :password)) (swap! state assoc-in [:application-info :paired?] true) (later #(on-success (str (rand-int 10000000)))))) @@ -253,7 +288,8 @@ (:intro-wizard @re-frame.db/app-db))] (log/debug "[simulated kk] generate-and-load-key response" response) (swap! state assoc-in - [:application-info :key-uid] (:key-uid response)) + [:application-info :key-uid] + (:key-uid response)) (let [deletion-wrapper (if delete-multiaccount? (fn [on-deletion-success] @@ -286,153 +322,178 @@ (= puk (get @state :puk)) (do - (swap! state update :application-info assoc - :pin-retry-counter 3 - :puk-retry-counter 5) + (swap! state update + :application-info assoc + :pin-retry-counter 3 + :puk-retry-counter 5) (swap! state assoc :pin new-pin) (later #(on-success true))) :else (do (swap! state update-in - [:application-info :puk-retry-counter] - (fnil dec 5)) + [:application-info :puk-retry-counter] + (fnil dec 5)) (later #(on-failure (puk-error)))))) -(defn verify-pin [{:keys [pin on-success on-failure]}] +(defn verify-pin + [{:keys [pin on-success on-failure]}] (with-pin pin on-failure #(on-success 3))) -(defn change-pin [{:keys [current-pin new-pin on-success on-failure]}] - (with-pin current-pin on-failure - (fn [] - (swap! state assoc :pin new-pin) - (on-success)))) +(defn change-pin + [{:keys [current-pin new-pin on-success on-failure]}] + (with-pin current-pin + on-failure + (fn [] + (swap! state assoc :pin new-pin) + (on-success)))) -(defn change-puk [{:keys [pin puk on-success on-failure]}] - (with-pin pin on-failure - (fn [] - (swap! state assoc :puk puk) - (on-success)))) +(defn change-puk + [{:keys [pin puk on-success on-failure]}] + (with-pin pin + on-failure + (fn [] + (swap! state assoc :puk puk) + (on-success)))) -(defn change-pairing [{:keys [pin pairing on-success on-failure]}] - (with-pin pin on-failure - (fn [] - (swap! state assoc :password pairing) - (on-success)))) +(defn change-pairing + [{:keys [pin pairing on-success on-failure]}] + (with-pin pin + on-failure + (fn [] + (swap! state assoc :password pairing) + (on-success)))) -(defn unpair [args] +(defn unpair + [args] (log/warn "unpair not implemented" args)) -(defn delete [args] +(defn delete + [args] (log/warn "delete not implemented" args)) -(defn remove-key [args] +(defn remove-key + [args] (log/warn "remove-key not implemented" args)) -(defn remove-key-with-unpair [args] +(defn remove-key-with-unpair + [args] (log/warn "remove-key-with-unpair not implemented" args)) -(defn normalize-path [path] +(defn normalize-path + [path] (if (string/starts-with? path "m/") (str constants/path-wallet-root - "/" (last (string/split path "/"))) + "/" + (last (string/split path "/"))) path)) -(defn export-key [{:keys [pin on-success on-failure]}] - (with-pin pin on-failure - #(let [{:keys [key-uid wallet-root-address]} - (get @re-frame.db/app-db :multiaccount) - accounts (get @re-frame.db/app-db :multiaccount/accounts) - hashed-password account-password - path-num (inc (get-in @re-frame.db/app-db [:multiaccount :latest-derived-path])) - path (str "m/" path-num)] - (status/multiaccount-load-account - wallet-root-address - hashed-password - (fn [value] - (let [{:keys [id error]} (types/json->clj value)] - (if error - (re-frame/dispatch [::new-account-error :password-error error]) - (status/multiaccount-derive-addresses - id - [path] - (fn [derived] - (let [derived-address (get-in (types/json->clj derived) [(keyword path) :address])] - (if (some (fn [a] (= derived-address (get a :address))) accounts) - (re-frame/dispatch [::new-account-error :account-error (i18n/label :t/account-exists-title)]) - (status/multiaccount-store-derived - id - key-uid - [path] - hashed-password - (fn [result] - (let [{:keys [error] :as result} (types/json->clj result) - {:keys [publicKey]} (get result (keyword path))] - (if error - (on-failure error) - (on-success publicKey)))))))))))))))) +(defn export-key + [{:keys [pin on-success on-failure]}] + (with-pin + pin + on-failure + #(let [{:keys [key-uid wallet-root-address]} + (get @re-frame.db/app-db :multiaccount) + accounts (get @re-frame.db/app-db :multiaccount/accounts) + hashed-password account-password + path-num (inc (get-in @re-frame.db/app-db + [:multiaccount :latest-derived-path])) + path (str "m/" path-num)] + (status/multiaccount-load-account + wallet-root-address + hashed-password + (fn [value] + (let [{:keys [id error]} (types/json->clj value)] + (if error + (re-frame/dispatch [::new-account-error :password-error error]) + (status/multiaccount-derive-addresses + id + [path] + (fn [derived] + (let [derived-address (get-in (types/json->clj derived) [(keyword path) :address])] + (if (some (fn [a] (= derived-address (get a :address))) accounts) + (re-frame/dispatch [::new-account-error :account-error + (i18n/label :t/account-exists-title)]) + (status/multiaccount-store-derived + id + key-uid + [path] + hashed-password + (fn [result] + (let [{:keys [error] :as result} (types/json->clj result) + {:keys [publicKey]} (get result (keyword path))] + (if error + (on-failure error) + (on-success publicKey)))))))))))))))) (defn unpair-and-delete [_]) -(defn get-keys [{:keys [on-success on-failure pin]}] - (with-pin pin on-failure - (if @derived-acc - (let [[id keys] (multiaccount->keys @derived-acc)] - (swap! state assoc-in - [:application-info :key-uid] (:key-uid keys)) - (status/multiaccount-store-derived - id - (:key-uid keys) - [constants/path-wallet-root - constants/path-eip1581 - constants/path-whisper - constants/path-default-wallet] - account-password - #(on-success keys))) - #(on-success - {:key-uid (get-in @state [:application-info :key-uid]) - :instance-uid (get-in @state [:application-info :instance-uid]) - :encryption-public-key account-password})))) +(defn get-keys + [{:keys [on-success on-failure pin]}] + (with-pin pin + on-failure + (if @derived-acc + (let [[id keys] (multiaccount->keys @derived-acc)] + (swap! state assoc-in + [:application-info :key-uid] + (:key-uid keys)) + (status/multiaccount-store-derived + id + (:key-uid keys) + [constants/path-wallet-root + constants/path-eip1581 + constants/path-whisper + constants/path-default-wallet] + account-password + #(on-success keys))) + #(on-success + {:key-uid (get-in @state [:application-info :key-uid]) + :instance-uid (get-in @state [:application-info :instance-uid]) + :encryption-public-key account-password})))) (def import-keys get-keys) -(defn sign [{:keys [pin hash data path typed? on-success on-failure]}] - (with-pin pin on-failure - #(let [address - (if path - (reduce - (fn [_ {:keys [address] :as acc}] - (when (= path (:path acc)) - (reduced address))) - nil - (:multiaccount/accounts @re-frame.db/app-db)) - (-> (:multiaccount/accounts @re-frame.db/app-db) - first - :address)) - password account-password] - (if-not typed? - (let [params (types/clj->json - {:account address - :password password - :data (or data (str "0x" hash))})] - (status/sign-message - params - (fn [res] - (let [signature (-> res - types/json->clj - :result - ethereum/normalized-hex)] - (on-success signature))))) - (status/sign-typed-data - data - address - password - (fn [res] - (let [signature (-> res - types/json->clj - :result - ethereum/normalized-hex)] - (on-success signature)))))))) +(defn sign + [{:keys [pin hash data path typed? on-success on-failure]}] + (with-pin pin + on-failure + #(let [address + (if path + (reduce + (fn [_ {:keys [address] :as acc}] + (when (= path (:path acc)) + (reduced address))) + nil + (:multiaccount/accounts @re-frame.db/app-db)) + (-> (:multiaccount/accounts @re-frame.db/app-db) + first + :address)) + password account-password] + (if-not typed? + (let [params (types/clj->json + {:account address + :password password + :data (or data (str "0x" hash))})] + (status/sign-message + params + (fn [res] + (let [signature (-> res + types/json->clj + :result + ethereum/normalized-hex)] + (on-success signature))))) + (status/sign-typed-data + data + address + password + (fn [res] + (let [signature (-> res + types/json->clj + :result + ethereum/normalized-hex)] + (on-success signature)))))))) -(defn sign-typed-data [args] +(defn sign-typed-data + [args] (log/warn "sign-typed-data not implemented" args)) (defn save-multiaccount-and-login @@ -445,7 +506,8 @@ node-config (types/clj->json accounts-data))) -(defn login [{:keys [key-uid multiaccount-data password]}] +(defn login + [{:keys [key-uid multiaccount-data password]}] (status/login-with-config key-uid multiaccount-data password node/login-node-config)) (defn send-transaction-with-signature @@ -458,114 +520,114 @@ (defrecord SimulatedKeycard [] keycard/Keycard - (keycard/start-nfc [_this args] - (log/debug "simulated card start-nfc") - (start-nfc args)) - (keycard/stop-nfc [_this args] - (log/debug "simulated card stop-nfc") - (stop-nfc args)) - (keycard/set-nfc-message [_this args] - (log/debug "simulated card set-nfc-message") - (set-nfc-message args)) - (keycard/check-nfc-support [_this args] - (log/debug "simulated card check-nfc-support") - (check-nfc-support args)) - (keycard/check-nfc-enabled [_this args] - (log/debug "simulated card check-nfc-enabled") - (check-nfc-enabled args)) - (keycard/open-nfc-settings [_this] - (log/debug "simulated card open-nfc-setting") - (open-nfc-settings)) - (keycard/register-card-events [_this args] - (log/debug "simulated card register-card-event") - (register-card-events args)) - (keycard/set-pairings [_this args] - (log/debug "simulated card set-pairings") - (set-pairings args)) - (keycard/on-card-connected [_this callback] - (log/debug "simulated card on-card-connected") - (on-card-connected callback)) - (keycard/on-card-disconnected [_this callback] - (log/debug "simulated card on-card-disconnected") - (on-card-disconnected callback)) - (keycard/remove-event-listener [_this event] - (log/debug "simulated card remove-event-listener") - (remove-event-listener event)) - (keycard/remove-event-listeners [_this] - (log/debug "simulated card remove-event-listener") - (remove-event-listeners)) - (keycard/get-application-info [_this args] - (log/debug "simulated card get-application-info") - (get-application-info args)) - (keycard/factory-reset [_this args] - (log/debug "simulated card factory-reset") - (factory-reset args)) - (keycard/install-applet [_this args] - (log/debug "simulated card install-applet") - (install-applet args)) - (keycard/init-card [_this args] - (log/debug "simulated card init-card") - (init-card args)) - (keycard/install-applet-and-init-card [_this args] - (log/debug "simulated card install-applet-and-init-card") - (install-applet-and-init-card args)) - (keycard/pair [_this args] - (log/debug "simulated card pair") - (pair args)) - (keycard/generate-and-load-key [_this args] - (log/debug "simulated card generate-and-load-key") - (generate-and-load-key args)) - (keycard/unblock-pin [_this args] - (log/debug "simulated card unblock-pin") - (unblock-pin args)) - (keycard/verify-pin [_this args] - (log/debug "simulated card verify-pin") - (verify-pin args)) - (keycard/change-pin [_this args] - (log/debug "simulated card change-pin") - (change-pin args)) - (keycard/change-puk [_this args] - (log/debug "simulated card change-puk") - (change-puk args)) - (keycard/change-pairing [_this args] - (log/debug "simulated card change-pairing") - (change-pairing args)) - (keycard/unpair [_this args] - (log/debug "simulated card unpair") - (unpair args)) - (keycard/delete [_this args] - (log/debug "simulated card delete") - (delete args)) - (keycard/remove-key [_this args] - (log/debug "simulated card remove-key") - (remove-key args)) - (keycard/remove-key-with-unpair [_this args] - (log/debug "simulated card remove-key-with-unpair") - (remove-key-with-unpair args)) - (keycard/export-key [_this args] - (log/debug "simulated card export-key") - (export-key args)) - (keycard/unpair-and-delete [_this args] - (log/debug "simulated card unpair-and-delete") - (unpair-and-delete args)) - (keycard/import-keys [_this args] - (log/debug "simulated card import-keys") - (import-keys args)) - (keycard/get-keys [_this args] - (log/debug "simulated card get-keys") - (get-keys args)) - (keycard/sign [_this args] - (log/debug "simulated card sign") - (sign args)) - (keycard/save-multiaccount-and-login [_this args] - (log/debug "simulated card save-multiaccount-and-login") - (save-multiaccount-and-login args)) - (keycard/login [_this args] - (log/debug "simulated card login") - (login args)) - (keycard/send-transaction-with-signature [_this args] - (log/debug "simulated card send-transaction-with-signature") - (send-transaction-with-signature args)) - (keycard/delete-multiaccount-before-migration [_ args] - (log/debug "simulated card delete-multiaccount-before-migration") - (delete-multiaccount-before-migration args))) + (keycard/start-nfc [_this args] + (log/debug "simulated card start-nfc") + (start-nfc args)) + (keycard/stop-nfc [_this args] + (log/debug "simulated card stop-nfc") + (stop-nfc args)) + (keycard/set-nfc-message [_this args] + (log/debug "simulated card set-nfc-message") + (set-nfc-message args)) + (keycard/check-nfc-support [_this args] + (log/debug "simulated card check-nfc-support") + (check-nfc-support args)) + (keycard/check-nfc-enabled [_this args] + (log/debug "simulated card check-nfc-enabled") + (check-nfc-enabled args)) + (keycard/open-nfc-settings [_this] + (log/debug "simulated card open-nfc-setting") + (open-nfc-settings)) + (keycard/register-card-events [_this args] + (log/debug "simulated card register-card-event") + (register-card-events args)) + (keycard/set-pairings [_this args] + (log/debug "simulated card set-pairings") + (set-pairings args)) + (keycard/on-card-connected [_this callback] + (log/debug "simulated card on-card-connected") + (on-card-connected callback)) + (keycard/on-card-disconnected [_this callback] + (log/debug "simulated card on-card-disconnected") + (on-card-disconnected callback)) + (keycard/remove-event-listener [_this event] + (log/debug "simulated card remove-event-listener") + (remove-event-listener event)) + (keycard/remove-event-listeners [_this] + (log/debug "simulated card remove-event-listener") + (remove-event-listeners)) + (keycard/get-application-info [_this args] + (log/debug "simulated card get-application-info") + (get-application-info args)) + (keycard/factory-reset [_this args] + (log/debug "simulated card factory-reset") + (factory-reset args)) + (keycard/install-applet [_this args] + (log/debug "simulated card install-applet") + (install-applet args)) + (keycard/init-card [_this args] + (log/debug "simulated card init-card") + (init-card args)) + (keycard/install-applet-and-init-card [_this args] + (log/debug "simulated card install-applet-and-init-card") + (install-applet-and-init-card args)) + (keycard/pair [_this args] + (log/debug "simulated card pair") + (pair args)) + (keycard/generate-and-load-key [_this args] + (log/debug "simulated card generate-and-load-key") + (generate-and-load-key args)) + (keycard/unblock-pin [_this args] + (log/debug "simulated card unblock-pin") + (unblock-pin args)) + (keycard/verify-pin [_this args] + (log/debug "simulated card verify-pin") + (verify-pin args)) + (keycard/change-pin [_this args] + (log/debug "simulated card change-pin") + (change-pin args)) + (keycard/change-puk [_this args] + (log/debug "simulated card change-puk") + (change-puk args)) + (keycard/change-pairing [_this args] + (log/debug "simulated card change-pairing") + (change-pairing args)) + (keycard/unpair [_this args] + (log/debug "simulated card unpair") + (unpair args)) + (keycard/delete [_this args] + (log/debug "simulated card delete") + (delete args)) + (keycard/remove-key [_this args] + (log/debug "simulated card remove-key") + (remove-key args)) + (keycard/remove-key-with-unpair [_this args] + (log/debug "simulated card remove-key-with-unpair") + (remove-key-with-unpair args)) + (keycard/export-key [_this args] + (log/debug "simulated card export-key") + (export-key args)) + (keycard/unpair-and-delete [_this args] + (log/debug "simulated card unpair-and-delete") + (unpair-and-delete args)) + (keycard/import-keys [_this args] + (log/debug "simulated card import-keys") + (import-keys args)) + (keycard/get-keys [_this args] + (log/debug "simulated card get-keys") + (get-keys args)) + (keycard/sign [_this args] + (log/debug "simulated card sign") + (sign args)) + (keycard/save-multiaccount-and-login [_this args] + (log/debug "simulated card save-multiaccount-and-login") + (save-multiaccount-and-login args)) + (keycard/login [_this args] + (log/debug "simulated card login") + (login args)) + (keycard/send-transaction-with-signature [_this args] + (log/debug "simulated card send-transaction-with-signature") + (send-transaction-with-signature args)) + (keycard/delete-multiaccount-before-migration [_ args] + (log/debug "simulated card delete-multiaccount-before-migration") + (delete-multiaccount-before-migration args))) diff --git a/src/status_im/keycard/test_menu.cljs b/src/status_im/keycard/test_menu.cljs index 8b83011656..295ffa6115 100644 --- a/src/status_im/keycard/test_menu.cljs +++ b/src/status_im/keycard/test_menu.cljs @@ -1,8 +1,9 @@ (ns status-im.keycard.test-menu - (:require [status-im.ui.components.react :as react] - [status-im.keycard.simulated-keycard :as simulated-keycard])) + (:require [status-im.keycard.simulated-keycard :as simulated-keycard] + [status-im.ui.components.react :as react])) -(defn button [label accessibility-label handler] +(defn button + [label accessibility-label handler] [react/view {:style {:width 20 :height 30 @@ -14,7 +15,8 @@ :accessibility-label accessibility-label} label]]) -(defn test-menu [] +(defn test-menu + [] [react/view {:style {:position :absolute :top 70 @@ -26,4 +28,4 @@ [button "conn sell" :connect-selected-card simulated-keycard/connect-selected-card] [button "pair" :connect-pairing-card simulated-keycard/connect-pairing-card] [button "disc" :disconnect-card simulated-keycard/disconnect-card] - [button "res" :keycard-reset-state simulated-keycard/reset-state]]) \ No newline at end of file + [button "res" :keycard-reset-state simulated-keycard/reset-state]]) \ No newline at end of file diff --git a/src/status_im/keycard/unpair.cljs b/src/status_im/keycard/unpair.cljs index 7e9cdb51a5..c66506114e 100644 --- a/src/status_im/keycard/unpair.cljs +++ b/src/status_im/keycard/unpair.cljs @@ -1,12 +1,12 @@ (ns status-im.keycard.unpair (:require [re-frame.core :as re-frame] - [status-im.multiaccounts.update.core :as multiaccounts.update] [status-im.i18n.i18n :as i18n] - [status-im2.navigation.events :as navigation] - [status-im.utils.fx :as fx] - [taoensso.timbre :as log] [status-im.keycard.common :as common] - [status-im.multiaccounts.key-storage.core :as key-storage])) + [status-im.multiaccounts.key-storage.core :as key-storage] + [status-im.multiaccounts.update.core :as multiaccounts.update] + [status-im.utils.fx :as fx] + [status-im2.navigation.events :as navigation] + [taoensso.timbre :as log])) (fx/defn unpair-card-pressed {:events [:keycard-settings.ui/unpair-card-pressed]} @@ -15,25 +15,28 @@ :content (i18n/label :t/unpair-card-confirmation) :confirm-button-text (i18n/label :t/yes) :cancel-button-text (i18n/label :t/no) - :on-accept #(re-frame/dispatch [:keycard-settings.ui/unpair-card-confirmed]) + :on-accept #(re-frame/dispatch + [:keycard-settings.ui/unpair-card-confirmed]) :on-cancel #()}}) (fx/defn unpair-card-confirmed {:events [:keycard-settings.ui/unpair-card-confirmed]} [{:keys [db] :as cofx}] (fx/merge cofx - {:db (assoc-in db [:keycard :pin] {:enter-step :current - :current [] - :puk [] - :status nil - :error-label nil - :on-verified :keycard/unpair})} + {:db (assoc-in db + [:keycard :pin] + {:enter-step :current + :current [] + :puk [] + :status nil + :error-label nil + :on-verified :keycard/unpair})} (common/navigate-to-enter-pin-screen))) (fx/defn unpair {:events [:keycard/unpair]} [{:keys [db]}] - (let [pin (common/vector->string (get-in db [:keycard :pin :current]))] + (let [pin (common/vector->string (get-in db [:keycard :pin :current]))] {:keycard/unpair {:pin pin}})) (fx/defn unpair-and-delete @@ -44,7 +47,7 @@ {:on-card-connected :keycard/unpair-and-delete :handler (fn [{:keys [db]}] - (let [pin (common/vector->string (get-in db [:keycard :pin :current]))] + (let [pin (common/vector->string (get-in db [:keycard :pin :current]))] {:keycard/unpair-and-delete {:pin pin}}))})) @@ -52,43 +55,53 @@ [cofx {:keys [remove-instance-uid?]}] (fx/merge cofx (multiaccounts.update/multiaccount-update - :keycard-pairing nil {}) + :keycard-pairing + nil + {}) (multiaccounts.update/multiaccount-update - :keycard-paired-on nil {}) + :keycard-paired-on + nil + {}) (when remove-instance-uid? (multiaccounts.update/multiaccount-update - :keycard-instance-uid nil {})))) + :keycard-instance-uid + nil + {})))) (fx/defn on-unpair-success {:events [:keycard.callback/on-unpair-success]} [{:keys [db] :as cofx}] (let [instance-uid (get-in db [:keycard :application-info :instance-uid]) pairings (get-in db [:keycard :pairings])] - (fx/merge cofx - {:db (-> db - (assoc-in [:keycard :secrets] nil) - (update-in [:keycard :pairings] dissoc (keyword instance-uid)) - (assoc-in [:keycard :pin] {:status nil - :error-label nil - :on-verified nil})) - :keycard/persist-pairings (dissoc pairings (keyword instance-uid)) - :utils/show-popup {:title "" - :content (i18n/label :t/card-unpaired)}} - (common/clear-on-card-connected) - (remove-pairing-from-multiaccount nil) - (navigation/navigate-to-cofx :keycard-settings nil)))) + (fx/merge + cofx + {:db (-> db + (assoc-in [:keycard :secrets] nil) + (update-in [:keycard :pairings] dissoc (keyword instance-uid)) + (assoc-in [:keycard :pin] + {:status nil + :error-label nil + :on-verified nil})) + :keycard/persist-pairings (dissoc pairings (keyword instance-uid)) + :utils/show-popup {:title "" + :content (i18n/label :t/card-unpaired)}} + (common/clear-on-card-connected) + (remove-pairing-from-multiaccount nil) + (navigation/navigate-to-cofx :keycard-settings nil)))) (fx/defn on-unpair-error {:events [:keycard.callback/on-unpair-error]} [{:keys [db] :as cofx} error] (log/debug "[keycard] unpair error" error) (fx/merge cofx - {:db (assoc-in db [:keycard :pin] {:status nil - :error-label nil - :on-verified nil}) + {:db (assoc-in db + [:keycard :pin] + {:status nil + :error-label nil + :on-verified nil}) :keycard/get-application-info nil - :utils/show-popup {:title "" - :content (i18n/label :t/something-went-wrong)}} + :utils/show-popup {:title "" + :content (i18n/label :t/something-went-wrong)}} (common/clear-on-card-connected) (navigation/navigate-to-cofx :keycard-settings nil))) @@ -100,35 +113,44 @@ {:on-card-connected :keycard/remove-key-with-unpair :handler (fn [{:keys [db]}] - (let [pin (common/vector->string (get-in db [:keycard :pin :current]))] + (let [pin (common/vector->string (get-in db [:keycard :pin :current]))] {:keycard/remove-key-with-unpair {:pin pin}}))})) -(defn handle-account-removal [{:keys [db] :as cofx} keys-removed-from-card?] - (let [key-uid (get-in db [:multiaccount :key-uid]) +(defn handle-account-removal + [{:keys [db] :as cofx} keys-removed-from-card?] + (let [key-uid (get-in db [:multiaccount :key-uid]) instance-uid (get-in db [:keycard :application-info :instance-uid]) - pairings (get-in db [:keycard :pairings])] - (fx/merge cofx - {:db (-> db - (update :multiaccounts/multiaccounts dissoc key-uid) - (assoc-in [:keycard :secrets] nil) - (update-in [:keycard :pairings] dissoc (keyword instance-uid)) - (update-in [:keycard :pairings] dissoc instance-uid) - (assoc-in [:keycard :whisper-public-key] nil) - (assoc-in [:keycard :wallet-address] nil) - (assoc-in [:keycard :application-info] nil) - (assoc-in [:keycard :pin] {:status nil - :error-label nil - :on-verified nil})) - :keycard/persist-pairings (dissoc pairings (keyword instance-uid)) - :utils/show-popup {:title (i18n/label (if keys-removed-from-card? :t/profile-deleted-title :t/database-reset-title)) - :content (i18n/label (if keys-removed-from-card? :t/profile-deleted-keycard :t/database-reset-content)) - :on-dismiss #(re-frame/dispatch [:logout])} - ::key-storage/delete-multiaccount {:key-uid key-uid - :on-success #(log/debug "[keycard] remove account ok") - :on-error #(log/warn "[keycard] remove account: " %)}} - (common/clear-on-card-connected) - (common/hide-connection-sheet)))) + pairings (get-in db [:keycard :pairings])] + (fx/merge + cofx + {:db (-> db + (update :multiaccounts/multiaccounts dissoc key-uid) + (assoc-in [:keycard :secrets] nil) + (update-in [:keycard :pairings] + dissoc + (keyword instance-uid)) + (update-in [:keycard :pairings] dissoc instance-uid) + (assoc-in [:keycard :whisper-public-key] nil) + (assoc-in [:keycard :wallet-address] nil) + (assoc-in [:keycard :application-info] nil) + (assoc-in [:keycard :pin] + {:status nil + :error-label nil + :on-verified nil})) + :keycard/persist-pairings (dissoc pairings (keyword instance-uid)) + :utils/show-popup {:title (i18n/label (if keys-removed-from-card? + :t/profile-deleted-title + :t/database-reset-title)) + :content (i18n/label (if keys-removed-from-card? + :t/profile-deleted-keycard + :t/database-reset-content)) + :on-dismiss #(re-frame/dispatch [:logout])} + ::key-storage/delete-multiaccount {:key-uid key-uid + :on-success #(log/debug "[keycard] remove account ok") + :on-error #(log/warn "[keycard] remove account: " %)}} + (common/clear-on-card-connected) + (common/hide-connection-sheet)))) (fx/defn on-remove-key-success {:events [:keycard.callback/on-remove-key-success]} diff --git a/src/status_im/keycard/wallet.cljs b/src/status_im/keycard/wallet.cljs index 98bfdf6d7a..d1fe791e25 100644 --- a/src/status_im/keycard/wallet.cljs +++ b/src/status_im/keycard/wallet.cljs @@ -1,21 +1,21 @@ (ns status-im.keycard.wallet - (:require [status-im.ethereum.core :as ethereum] - [status-im.utils.fx :as fx] - [status-im.keycard.common :as common] + (:require [status-im.bottom-sheet.core :as bottom-sheet] [status-im.constants :as constants] + [status-im.ethereum.core :as ethereum] [status-im.ethereum.eip55 :as eip55] - [status-im.utils.hex :as utils.hex] + [status-im.keycard.common :as common] [status-im.ui.screens.wallet.add-new.views :as add-new.views] - [status-im.bottom-sheet.core :as bottom-sheet])) + [status-im.utils.fx :as fx] + [status-im.utils.hex :as utils.hex])) (fx/defn show-pin-sheet {:events [:keycard/new-account-pin-sheet]} [{:keys [db] :as cofx}] (fx/merge cofx - {:db (-> db - (assoc-in [:keycard :pin :enter-step] :export-key) - (update-in [:keycard :pin] dissoc :export-key)) + {:db (-> db + (assoc-in [:keycard :pin :enter-step] :export-key) + (update-in [:keycard :pin] dissoc :export-key)) :dismiss-keyboard nil} (bottom-sheet/show-bottom-sheet {:view {:content add-new.views/pin}}))) @@ -23,7 +23,7 @@ [cofx] {:utils/dispatch-later ;; We need to give previous sheet some time to be fully hidden - [{:ms 200 + [{:ms 200 :dispatch [:wallet.accounts/verify-pin]}]}) (fx/defn hide-pin-sheet @@ -39,7 +39,8 @@ pin (common/vector->string (get-in db [:keycard :pin :export-key]))] {:db (assoc-in - db [:keycard :on-export-success] + db + [:keycard :on-export-success] #(vector :wallet.accounts/account-stored (let [public-key (utils.hex/normalize-hex %)] {;; Strip leading 04 prefix denoting uncompressed key format diff --git a/src/status_im/log_level/core.cljs b/src/status_im/log_level/core.cljs index 0731609f01..629dba0dec 100644 --- a/src/status_im/log_level/core.cljs +++ b/src/status_im/log_level/core.cljs @@ -1,8 +1,8 @@ (ns status-im.log-level.core (:require [re-frame.core :as re-frame] + [status-im.i18n.i18n :as i18n] [status-im.multiaccounts.update.core :as multiaccounts.update] [status-im.node.core :as node] - [status-im.i18n.i18n :as i18n] [status-im.utils.fx :as fx])) (fx/defn save-log-level @@ -12,7 +12,8 @@ (when (not= old-log-level log-level) (fx/merge cofx (multiaccounts.update/multiaccount-update - :log-level log-level + :log-level + log-level {}) (node/prepare-new-config {:on-success #(re-frame/dispatch [:logout])}))))) diff --git a/src/status_im/mailserver/constants.cljs b/src/status_im/mailserver/constants.cljs index 13f66016af..7cb4c86db9 100644 --- a/src/status_im/mailserver/constants.cljs +++ b/src/status_im/mailserver/constants.cljs @@ -1,5 +1,4 @@ -(ns ^{:doc "Mailserver events and API"} - status-im.mailserver.constants) +(ns ^{:doc "Mailserver events and API"} status-im.mailserver.constants) (def one-day (* 24 3600)) (def seven-days (* 7 one-day)) diff --git a/src/status_im/mailserver/core.cljs b/src/status_im/mailserver/core.cljs index 8673f965fe..61e85048a8 100644 --- a/src/status_im/mailserver/core.cljs +++ b/src/status_im/mailserver/core.cljs @@ -1,14 +1,13 @@ -(ns ^{:doc "Mailserver events and API"} - status-im.mailserver.core +(ns ^{:doc "Mailserver events and API"} status-im.mailserver.core (:require [clojure.string :as string] [re-frame.core :as re-frame] [status-im.ethereum.json-rpc :as json-rpc] [status-im.i18n.i18n :as i18n] [status-im.multiaccounts.update.core :as multiaccounts.update] [status-im.node.core :as node] + [status-im.utils.fx :as fx] [status-im.utils.mobile-sync :as mobile-network-utils] [status-im2.navigation.events :as navigation] - [status-im.utils.fx :as fx] [taoensso.timbre :as log])) ;; How do mailserver work ? @@ -24,22 +23,28 @@ ;; as soon as the mailserver becomes available -(defn connected? [db id] +(defn connected? + [db id] (= (:mailserver/current-id db) id)) -(defn fetch [db id] +(defn fetch + [db id] (get-in db [:mailserver/mailservers (node/current-fleet-key db) id])) -(defn fetch-current [db] +(defn fetch-current + [db] (fetch db (:mailserver/current-id db))) -(defn preferred-mailserver-id [db] +(defn preferred-mailserver-id + [db] (get-in db [:multiaccount :pinned-mailservers (node/current-fleet-key db)])) -(defn connection-error-dismissed [db] +(defn connection-error-dismissed + [db] (get-in db [:mailserver/connection-error-dismissed])) -(defn mailserver-address->id [db address] +(defn mailserver-address->id + [db address] (let [current-fleet (node/current-fleet-key db)] (:id (some #(when (= address (:address %)) %) @@ -55,12 +60,14 @@ (assoc :mailserver/state nil) (dissoc :mailserver/current-request :mailserver/fetching-gaps-in-progress))}) -(defn fetch-use-mailservers? [{:keys [db]}] +(defn fetch-use-mailservers? + [{:keys [db]}] (get-in db [:multiaccount :use-mailservers?])) (defonce showing-connection-error-popup? (atom false)) -(defn cancel-connection-popup! [] +(defn cancel-connection-popup! + [] (reset! showing-connection-error-popup? false) (re-frame/dispatch [:mailserver.ui/dismiss-connection-error true])) @@ -68,7 +75,8 @@ ::cancel-connection-popup cancel-connection-popup!) -(fx/defn show-connection-error! [cofx current-fleet preferred-mailserver] +(fx/defn show-connection-error! + [cofx current-fleet preferred-mailserver] (reset! showing-connection-error-popup? true) {:ui/show-confirmation {:title (i18n/label :t/mailserver-error-title) @@ -94,10 +102,11 @@ {:db (dissoc db :mailserver/current-request) :dispatch [:sanitize-messages-and-process-response response-js]}) -(fx/defn handle-mailserver-not-working [{:keys [db] :as cofx}] - (let [current-fleet (node/current-fleet-key db) - error-dismissed? (connection-error-dismissed db) - pinned-mailserver (get-in db [:multiaccount :pinned-mailservers current-fleet])] +(fx/defn handle-mailserver-not-working + [{:keys [db] :as cofx}] + (let [current-fleet (node/current-fleet-key db) + error-dismissed? (connection-error-dismissed db) + pinned-mailserver (get-in db [:multiaccount :pinned-mailservers current-fleet])] (when (and pinned-mailserver (not error-dismissed?) (not @showing-connection-error-popup?)) @@ -116,33 +125,35 @@ (mobile-network-utils/syncing-allowed? cofx) (fetch-use-mailservers? cofx) (not (:mailserver/current-request db))) - {:db (assoc db :mailserver/current-request true) - ::json-rpc/call [{:method "wakuext_requestAllHistoricMessagesWithRetries" - :params [] + {:db (assoc db :mailserver/current-request true) + ::json-rpc/call [{:method "wakuext_requestAllHistoricMessagesWithRetries" + :params [] :js-response true - :on-success #(do - (log/info "fetched historical messages") - (re-frame/dispatch [::request-success %])) - :on-error #(do - (log/error "failed retrieve historical messages" %) - (re-frame/dispatch [::request-error]))}]})) + :on-success #(do + (log/info "fetched historical messages") + (re-frame/dispatch [::request-success %])) + :on-error #(do + (log/error "failed retrieve historical messages" %) + (re-frame/dispatch [::request-error]))}]})) (fx/defn handle-mailserver-changed [{:keys [db] :as cofx} ms] (if (seq ms) - {:db (assoc db :mailserver/state :connecting + {:db (assoc db + :mailserver/state :connecting :mailserver/current-id (keyword ms))} {:db (assoc db :mailserver/state nil)})) (fx/defn handle-mailserver-available [{:keys [db] :as cofx} ms] {::cancel-connection-popup [] - :db (assoc db :mailserver/state :connected - :mailserver/current-id (keyword ms))}) + :db (assoc db + :mailserver/state :connected + :mailserver/current-id (keyword ms))}) (fx/defn connected-to-mailserver [{:keys [db] :as cofx}] - (let [{:keys [address]} (fetch-current db)] + (let [{:keys [address]} (fetch-current db)] (fx/merge cofx {:mailserver/update-mailservers [[address] #(re-frame/dispatch [::request-messages])]}))) @@ -151,14 +162,15 @@ {:events [:mailserver.callback/request-success]} [{{:keys [chats] :as db} :db} {:keys [request-id topics]}] (when (:mailserver/current-request db) - {:db (assoc-in db [:mailserver/current-request :request-id] - request-id)})) + {:db (assoc-in db + [:mailserver/current-request :request-id] + request-id)})) (fx/defn toggle-use-mailservers [cofx value] {::json-rpc/call - [{:method "wakuext_toggleUseMailservers" - :params [value] + [{:method "wakuext_toggleUseMailservers" + :params [value] :on-success #(log/info "successfully toggled use-mailservers" value) :on-failure #(log/error "failed to toggle use-mailserver" value %)}]}) @@ -183,22 +195,25 @@ [{:keys [db]}] (let [mailserver-error (:mailserver/request-error db)] {:utils/show-confirmation - {:title (i18n/label :t/mailserver-request-error-title) - :content (i18n/label :t/mailserver-request-error-content - {:error mailserver-error}) - :on-accept #(re-frame/dispatch [:mailserver.ui/retry-request-pressed]) + {:title (i18n/label :t/mailserver-request-error-title) + :content (i18n/label :t/mailserver-request-error-content + {:error mailserver-error}) + :on-accept #(re-frame/dispatch [:mailserver.ui/retry-request-pressed]) :confirm-button-text (i18n/label :t/mailserver-request-retry)}})) (def enode-url-regex #"enode://[a-zA-Z0-9]+\@\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b:(\d{1,5})") -(defn- extract-url-components [address] +(defn- extract-url-components + [address] (rest (re-matches #"enode://(.*)@(.*)" address))) -(defn valid-enode-url? [address] +(defn valid-enode-url? + [address] (re-matches enode-url-regex address)) -(defn build-url [address] +(defn build-url + [address] (let [[initial host] (extract-url-components address)] (str "enode://" initial "@" host))) @@ -216,16 +231,18 @@ :name (string/blank? value) :url (not (valid-enode-url? value)))})}) -(defn- address->mailserver [address] +(defn- address->mailserver + [address] (let [[enode url :as response] (extract-url-components address)] {:address (if (seq response) (str "enode://" enode "@" url) address) - :custom true})) + :custom true})) -(defn- build [id mailserver-name address] +(defn- build + [id mailserver-name address] (assoc (address->mailserver address) - :id id + :id id :name mailserver-name)) (def default? (comp not :custom fetch)) @@ -234,7 +251,7 @@ {:events [:mailserver.ui/custom-mailserver-selected]} [{:keys [db] :as cofx} id] (let [{:keys [id address name]} (fetch db id) - url (when address (build-url address))] + url (when address (build-url address))] (fx/merge cofx (set-input :id id) (set-input :url (str url)) @@ -248,13 +265,14 @@ (update :id name))) (fx/defn upsert - {:events [:mailserver.ui/save-pressed] + {:events [:mailserver.ui/save-pressed] :interceptors [(re-frame/inject-cofx :random-id-generator)]} [{{:mailserver.edit/keys [mailserver] :keys [multiaccount] :as db} :db - random-id-generator :random-id-generator :as cofx}] + random-id-generator :random-id-generator + :as cofx}] (let [{:keys [name url id]} mailserver - current-fleet (node/current-fleet-key db)] + current-fleet (node/current-fleet-key db)] (when (and (not (string/blank? (:value name))) (valid-enode-url? (:value url))) @@ -263,14 +281,14 @@ (keyword (string/replace (random-id-generator) "-" ""))) (:value name) (:value url)) - current (connected? db (:id mailserver))] - {:db (-> db - (dissoc :mailserver.edit/mailserver) - (assoc-in [:mailserver/mailservers current-fleet (:id mailserver)] - mailserver)) + current (connected? db (:id mailserver))] + {:db (-> db + (dissoc :mailserver.edit/mailserver) + (assoc-in [:mailserver/mailservers current-fleet (:id mailserver)] + mailserver)) ::json-rpc/call - [{:method "mailservers_addMailserver" - :params [(mailserver->rpc mailserver current-fleet)] + [{:method "mailservers_addMailserver" + :params [(mailserver->rpc mailserver current-fleet)] :on-success (fn [] ;; we naively logout if the user is connected to ;; the edited mailserver @@ -279,7 +297,7 @@ [:multiaccounts.logout.ui/logout-confirmed])) (log/debug "saved mailserver" id "successfuly")) :on-failure #(log/error "failed to save mailserver" id %)}] - :dispatch [:navigate-back]})))) + :dispatch [:navigate-back]})))) (defn can-delete? [db id] @@ -290,17 +308,18 @@ {:events [:mailserver.ui/delete-confirmed]} [{:keys [db] :as cofx} id] (if (can-delete? db id) - {:db (-> db - (update-in - [:mailserver/mailservers (node/current-fleet-key db)] - dissoc id) - (dissoc :mailserver.edit/mailserver)) + {:db (-> db + (update-in + [:mailserver/mailservers (node/current-fleet-key db)] + dissoc + id) + (dissoc :mailserver.edit/mailserver)) ::json-rpc/call - [{:method "mailservers_deleteMailserver" - :params [(name id)] + [{:method "mailservers_deleteMailserver" + :params [(name id)] :on-success #(log/debug "deleted mailserver" id) :on-failure #(log/error "failed to delete mailserver" id %)}] - :dispatch [:navigate-back]} + :dispatch [:navigate-back]} {:dispatch [:navigate-back]})) (fx/defn show-connection-confirmation @@ -308,16 +327,17 @@ [{:keys [db]} mailserver-id] (let [current-fleet (node/current-fleet-key db)] {:ui/show-confirmation - {:title (i18n/label :t/close-app-title) + {:title (i18n/label :t/close-app-title) :content (i18n/label :t/connect-mailserver-content - {:name (get-in db [:mailserver/mailservers - current-fleet mailserver-id :name])}) + {:name (get-in db + [:mailserver/mailservers + current-fleet mailserver-id :name])}) :confirm-button-text (i18n/label :t/close-app-button) :on-accept #(re-frame/dispatch [:mailserver.ui/connect-confirmed current-fleet mailserver-id]) - :on-cancel nil}})) + :on-cancel nil}})) (fx/defn show-delete-confirmation {:events [:mailserver.ui/delete-pressed]} @@ -333,7 +353,8 @@ {:events [:mailserver.callback/qr-code-scanned]} [cofx url] (assoc (set-input cofx :url url) - :dispatch [:navigate-back])) + :dispatch + [:navigate-back])) (fx/defn dismiss-connection-error {:events [:mailserver.ui/dismiss-connection-error]} @@ -347,40 +368,42 @@ (get-in [:multiaccount :pinned-mailservers]) (assoc current-fleet mailserver-id))] (fx/merge cofx - {:db (assoc db :mailserver/current-id mailserver-id) - ::json-rpc/call [{:method "wakuext_setPinnedMailservers" - :params [pinned-mailservers] + {:db (assoc db :mailserver/current-id mailserver-id) + ::json-rpc/call [{:method "wakuext_setPinnedMailservers" + :params [pinned-mailservers] :on-success #(log/info "successfully pinned mailserver") - :on-error #(log/error "failed to pin mailserver" %)}]} + :on-error #(log/error "failed to pin mailserver" %)}]} (multiaccounts.update/optimistic :pinned-mailservers pinned-mailservers)))) (fx/defn unpin {:events [:mailserver.ui/unpin-pressed]} [{:keys [db] :as cofx}] - (let [current-fleet (node/current-fleet-key db) - pinned-mailservers (-> db - (get-in [:multiaccount :pinned-mailservers]) - (dissoc current-fleet))] + (let [current-fleet (node/current-fleet-key db) + pinned-mailservers (-> db + (get-in [:multiaccount :pinned-mailservers]) + (dissoc current-fleet))] (fx/merge cofx - {::json-rpc/call [{:method "wakuext_setPinnedMailservers" - :params [pinned-mailservers] + {::json-rpc/call [{:method "wakuext_setPinnedMailservers" + :params [pinned-mailservers] :on-success #(log/info "successfully unpinned mailserver") - :on-error #(log/error "failed to unpin mailserver" %)}]} + :on-error #(log/error "failed to unpin mailserver" %)}]} (multiaccounts.update/optimistic - :pinned-mailservers pinned-mailservers) + :pinned-mailservers + pinned-mailservers) (dismiss-connection-error false)))) (fx/defn pin {:events [:mailserver.ui/pin-pressed]} [{:keys [db] :as cofx}] - (let [current-fleet (node/current-fleet-key db) - mailserver-id (:mailserver/current-id db) + (let [current-fleet (node/current-fleet-key db) + mailserver-id (:mailserver/current-id db) pinned-mailservers (get-in db [:multiaccount :pinned-mailservers])] (fx/merge cofx (multiaccounts.update/multiaccount-update - :pinned-mailservers (assoc pinned-mailservers - current-fleet - mailserver-id) + :pinned-mailservers + (assoc pinned-mailservers + current-fleet + mailserver-id) {}) (dismiss-connection-error false)))) diff --git a/src/status_im/mobile_sync_settings/core.cljs b/src/status_im/mobile_sync_settings/core.cljs index ef2daffb2c..1e14bb6541 100644 --- a/src/status_im/mobile_sync_settings/core.cljs +++ b/src/status_im/mobile_sync_settings/core.cljs @@ -1,25 +1,26 @@ (ns status-im.mobile-sync-settings.core - (:require - [status-im.multiaccounts.update.core :as multiaccounts.update] - [status-im.utils.fx :as fx] - [status-im.wallet.core :as wallet] - [status-im.bottom-sheet.core :as bottom-sheet] - [status-im.multiaccounts.model :as multiaccounts.model] - [status-im2.navigation.events :as navigation] - [status-im.mailserver.core :as mailserver] - [status-im.utils.mobile-sync :as utils] - [taoensso.timbre :as log])) + (:require [status-im.bottom-sheet.core :as bottom-sheet] + [status-im.mailserver.core :as mailserver] + [status-im.multiaccounts.model :as multiaccounts.model] + [status-im.multiaccounts.update.core :as multiaccounts.update] + [status-im.utils.fx :as fx] + [status-im.utils.mobile-sync :as utils] + [status-im.wallet.core :as wallet] + [status-im2.navigation.events :as navigation] + [taoensso.timbre :as log])) (fx/defn sheet-defaults [{:keys [db]}] (let [remember-choice? (get-in db [:multiaccount :remember-syncing-choice?])] - {:db (assoc db :mobile-network/remember-choice? (or (nil? remember-choice?) - remember-choice?))})) + {:db (assoc db + :mobile-network/remember-choice? + (or (nil? remember-choice?) + remember-choice?))})) (fx/defn on-network-status-change [{:keys [db] :as cofx}] - (let [initialized? (get db :network-status/initialized?) - logged-in? (multiaccounts.model/logged-in? cofx) + (let [initialized? (get db :network-status/initialized?) + logged-in? (multiaccounts.model/logged-in? cofx) {:keys [remember-syncing-choice?]} (:multiaccount db)] (apply fx/merge @@ -51,22 +52,26 @@ ([sync?] (apply-settings sync? :default)) ([sync? remember?] (fn [{:keys [db] :as cofx}] - (let [network (:network/type db) + (let [network (:network/type db) remember-choice? (if (not= :default remember?) remember? (:mobile-network/remember-choice? db)) - cellular? (utils/cellular? network)] + cellular? (utils/cellular? network)] (log/info "apply mobile network settings" - "sunc?" sync? + "sunc?" sync? "remember?" remember? "cellular?" cellular?) (fx/merge cofx (multiaccounts.update/multiaccount-update - :syncing-on-mobile-network? (boolean sync?) {}) + :syncing-on-mobile-network? + (boolean sync?) + {}) (multiaccounts.update/multiaccount-update - :remember-syncing-choice? (boolean remember-choice?) {}) + :remember-syncing-choice? + (boolean remember-choice?) + {}) (when (and cellular? sync?) (mailserver/process-next-messages-request)) (wallet/restart-wallet-service nil)))))) diff --git a/src/status_im/multiaccounts/biometric/core.cljs b/src/status_im/multiaccounts/biometric/core.cljs index 547bb0ba48..87f96293cc 100644 --- a/src/status_im/multiaccounts/biometric/core.cljs +++ b/src/status_im/multiaccounts/biometric/core.cljs @@ -1,14 +1,14 @@ (ns status-im.multiaccounts.biometric.core - (:require [re-frame.core :as re-frame] + (:require ["react-native-touch-id" :default touchid] + [quo.design-system.colors :as colors] + [re-frame.core :as re-frame] [status-im.i18n.i18n :as i18n] [status-im.native-module.core :as status] [status-im.popover.core :as popover] - [quo.design-system.colors :as colors] [status-im.utils.fx :as fx] [status-im.utils.keychain.core :as keychain] [status-im.utils.platform :as platform] - [taoensso.timbre :as log] - ["react-native-touch-id" :default touchid])) + [taoensso.timbre :as log])) ;; currently, for android, react-native-touch-id ;; is not returning supported biometric type @@ -28,11 +28,12 @@ (def android-device-blacklisted? (cond (= (:brand deviceinfo) "bannedbrand") true - :else false)) + :else false)) ;; biometric auth config ;; https://github.com/naoufal/react-native-touch-id#authenticatereason-config -(defn- authenticate-options [ios-fallback-label] +(defn- authenticate-options + [ios-fallback-label] (clj->js (merge {:unifiedErrors true} (when platform/ios? @@ -46,7 +47,8 @@ :sensorErrorDescription (i18n/label :t/biometric-auth-android-sensor-error-desc) :cancelText (i18n/label :cancel)})))) -(defn get-label [supported-biometric-auth] +(defn get-label + [supported-biometric-auth] (case supported-biometric-auth :fingerprint (i18n/label :t/biometric-fingerprint) :FaceID (i18n/label :t/biometric-faceid) @@ -61,29 +63,33 @@ (= touchid-error-code "USER_FALLBACK") nil ;; add here more specific errors if needed ;; https://github.com/naoufal/react-native-touch-id#unified-errors - :else (i18n/label :t/biometric-auth-error {:code touchid-error-code}))) + :else (i18n/label :t/biometric-auth-error + {:code touchid-error-code}))) (def success-result {:bioauth-success true}) -(defn- generate-error-result [touchid-error-obj] +(defn- generate-error-result + [touchid-error-obj] (let [code (aget touchid-error-obj "code")] {:bioauth-success false :bioauth-code code :bioauth-message (get-error-message code)})) -(defn- do-get-supported [callback] +(defn- do-get-supported + [callback] (-> (.isSupported touchid) (.then #(callback (or (keyword %) android-default-support))) (.catch #(callback nil)))) -(defn get-supported [callback] +(defn get-supported + [callback] (log/debug "[biometric] get-supported") - (cond platform/ios? (do-get-supported callback) + (cond platform/ios? (do-get-supported callback) platform/android? (if android-device-blacklisted? (callback nil) (do-get-supported callback)) - :else (callback nil))) + :else (callback nil))) (defn authenticate-fx ([cb] @@ -119,7 +125,8 @@ (fn [[cb options]] (authenticate-fx #(cb %) options))) -(fx/defn update-biometric [{db :db :as cofx} biometric-auth?] +(fx/defn update-biometric + [{db :db :as cofx} biometric-auth?] (let [key-uid (or (get-in db [:multiaccount :key-uid]) (get-in db [:multiaccounts/login :key-uid]))] (fx/merge cofx @@ -148,7 +155,7 @@ bioauth-message)] (when content {:utils/show-popup - {:title (i18n/label :t/biometric-auth-login-error-title) + {:title (i18n/label :t/biometric-auth-login-error-title) :content content}}))) (fx/defn biometric-init-done @@ -194,7 +201,7 @@ (log/debug "[biometric] setup-done" "bioauth-success" bioauth-success "bioauth-message" bioauth-message - "bioauth-code" bioauth-code) + "bioauth-code" bioauth-code) (if bioauth-success {:db (assoc db :auth-method keychain/auth-method-biometric-prepare)} (show-message cofx bioauth-message bioauth-code))) diff --git a/src/status_im/multiaccounts/core.cljs b/src/status_im/multiaccounts/core.cljs index e3c8560b90..3e99d4a061 100644 --- a/src/status_im/multiaccounts/core.cljs +++ b/src/status_im/multiaccounts/core.cljs @@ -1,19 +1,19 @@ (ns status-im.multiaccounts.core - (:require [re-frame.core :as re-frame] + (:require [clojure.string :as string] + [quo.platform :as platform] + [re-frame.core :as re-frame] + [status-im.bottom-sheet.core :as bottom-sheet] + [status-im.ethereum.json-rpc :as json-rpc] [status-im.ethereum.stateofus :as stateofus] [status-im.multiaccounts.update.core :as multiaccounts.update] - [status-im.bottom-sheet.core :as bottom-sheet] [status-im.native-module.core :as native-module] - [status-im.ethereum.json-rpc :as json-rpc] + [status-im.theme.core :as theme] [status-im.utils.fx :as fx] [status-im.utils.gfycat.core :as gfycat] [status-im.utils.identicon :as identicon] - [status-im.theme.core :as theme] [status-im.utils.utils :as utils] - [quo.platform :as platform] - [taoensso.timbre :as log] - [clojure.string :as string] - [status-im2.common.theme.core :as utils.theme])) + [status-im2.common.theme.core :as utils.theme] + [taoensso.timbre :as log])) ;; validate that the given mnemonic was generated from Status Dictionary (re-frame/reg-fx @@ -35,7 +35,7 @@ (cond-> {:nickname nickname :display-name display-name :three-words-name (or alias (gfycat/generate-gfy public-key))} - ;; Preferred name is our own otherwise we make sure it's verified + ;; Preferred name is our own otherwise we make sure it's verified (or preferred-name (and ens-verified name)) (assoc :ens-name (str "@" (or (stateofus/username ens-name) ens-name)))))) @@ -48,7 +48,8 @@ (let [{:keys [nickname ens-name display-name - three-words-name]} (or names (contact-names contact)) + three-words-name]} + (or names (contact-names contact)) short-public-key (when public-key? (utils/get-shortened-address public-key))] (->> [nickname ens-name display-name three-words-name short-public-key] (remove string/blank?) @@ -70,11 +71,13 @@ (str "@" (or username ens-name))) (or alias (gfycat/generate-gfy public-key))))) -(defn contact-by-identity [contacts identity] +(defn contact-by-identity + [contacts identity] (or (get contacts identity) (contact-with-names {:public-key identity}))) -(defn contact-two-names-by-identity [contact current-multiaccount identity] +(defn contact-two-names-by-identity + [contact current-multiaccount identity] (let [me? (= (:public-key current-multiaccount) identity)] (if me? [(or (:preferred-name current-multiaccount) @@ -114,13 +117,17 @@ {:events [:multiaccounts.ui/wallet-set-up-confirmed]} [cofx] (multiaccounts.update/multiaccount-update cofx - :wallet-set-up-passed? true {})) + :wallet-set-up-passed? + true + {})) (fx/defn confirm-home-tooltip {:events [:multiaccounts.ui/hide-home-tooltip]} [cofx] (multiaccounts.update/multiaccount-update cofx - :hide-home-tooltip? true {})) + :hide-home-tooltip? + true + {})) (fx/defn switch-webview-debug {:events [:multiaccounts.ui/switch-webview-debug]} @@ -128,7 +135,8 @@ (fx/merge cofx {::webview-debug-changed value} (multiaccounts.update/multiaccount-update - :webview-debug (boolean value) + :webview-debug + (boolean value) {}))) (fx/defn switch-preview-privacy-mode @@ -137,7 +145,8 @@ (fx/merge cofx {::blank-preview-flag-changed private?} (multiaccounts.update/multiaccount-update - :preview-privacy? (boolean private?) + :preview-privacy? + (boolean private?) {}))) (fx/defn switch-webview-permission-requests? @@ -145,7 +154,8 @@ [cofx enabled?] (multiaccounts.update/multiaccount-update cofx - :webview-allow-permission-requests? (boolean enabled?) + :webview-allow-permission-requests? + (boolean enabled?) {})) (fx/defn switch-default-sync-period @@ -153,7 +163,8 @@ [cofx value] (multiaccounts.update/multiaccount-update cofx - :default-sync-period value + :default-sync-period + value {})) (fx/defn switch-preview-privacy-mode-flag @@ -180,8 +191,8 @@ {:events [:multiaccounts.ui/profile-picture-show-to-switched]} [cofx id] (fx/merge cofx - {::json-rpc/call [{:method "wakuext_changeIdentityImageShowTo" - :params [id] + {::json-rpc/call [{:method "wakuext_changeIdentityImageShowTo" + :params [id] :on-success #(log/debug "picture settings changed successfully")}]} (multiaccounts.update/optimistic :profile-pictures-show-to id))) @@ -190,7 +201,8 @@ [cofx id] (multiaccounts.update/multiaccount-update cofx :profile-pictures-visibility id {})) -(defn clean-path [path] +(defn clean-path + [path] (if path (string/replace-first path #"file://" "") (log/warn "[native-module] Empty path was provided"))) @@ -218,7 +230,9 @@ (bottom-sheet/hide-bottom-sheet)))) (comment - (re-frame/dispatch [::save-profile-picture-from-url "https://lh3.googleusercontent.com/XuKjNm3HydsaxbPkbpGs9YyCKhn5QQk5oDC8XF2jzmPyYXeZofxFtfUDZuQ3EVmacS_BlBKzbX2ypm37YNX3n1fDJA3WndeFcPsp7Z0=w600"])) + (re-frame/dispatch + [::save-profile-picture-from-url + "https://lh3.googleusercontent.com/XuKjNm3HydsaxbPkbpGs9YyCKhn5QQk5oDC8XF2jzmPyYXeZofxFtfUDZuQ3EVmacS_BlBKzbX2ypm37YNX3n1fDJA3WndeFcPsp7Z0=w600"])) (fx/defn delete-profile-picture {:events [::delete-profile-picture]} @@ -227,7 +241,8 @@ (fx/merge cofx {::json-rpc/call [{:method "multiaccounts_deleteIdentityImage" :params [key-uid] - ;; NOTE: In case of an error we could fallback to previous image in UI with a toast error + ;; NOTE: In case of an error we could fallback to previous image in UI + ;; with a toast error :on-success #(log/info "[multiaccount] Delete profile image" %)}]} (multiaccounts.update/optimistic :images nil) (bottom-sheet/hide-bottom-sheet)))) @@ -246,4 +261,6 @@ (comment ;; Test seed for Dim Venerated Yaffle, it's not here by mistake, this is just a test account - (native-module/validate-mnemonic "rocket mixed rebel affair umbrella legal resemble scene virus park deposit cargo" prn)) + (native-module/validate-mnemonic + "rocket mixed rebel affair umbrella legal resemble scene virus park deposit cargo" + prn)) diff --git a/src/status_im/multiaccounts/core_test.cljs b/src/status_im/multiaccounts/core_test.cljs index 85d9fa7e1b..8a65c1a1f6 100644 --- a/src/status_im/multiaccounts/core_test.cljs +++ b/src/status_im/multiaccounts/core_test.cljs @@ -2,7 +2,8 @@ (:require [cljs.test :refer-macros [deftest is testing]] [status-im.multiaccounts.core :as ma])) -(def public-key "0x0461f576da67dc0bca9888cdb4cb28c80285b756b324109da94a081585ed6f007cf00afede6b3ee5638593674fee100b590318fc7bdb0054b8dd9445acea216ad2") +(def public-key + "0x0461f576da67dc0bca9888cdb4cb28c80285b756b324109da94a081585ed6f007cf00afede6b3ee5638593674fee100b590318fc7bdb0054b8dd9445acea216ad2") (def short-public-key "0x0461…ad2") (def random-name "Studious Gold Mustang") (def override-random-name (str "override" random-name)) diff --git a/src/status_im/multiaccounts/create/core.cljs b/src/status_im/multiaccounts/create/core.cljs index 0a4cd121ed..b873c16a59 100644 --- a/src/status_im/multiaccounts/create/core.cljs +++ b/src/status_im/multiaccounts/create/core.cljs @@ -1,5 +1,6 @@ (ns status-im.multiaccounts.create.core - (:require [re-frame.core :as re-frame] + (:require [quo.design-system.colors :as colors] + [re-frame.core :as re-frame] [status-im.constants :as constants] [status-im.data-store.settings :as data-store.settings] [status-im.ethereum.core :as ethereum] @@ -7,26 +8,27 @@ [status-im.i18n.i18n :as i18n] [status-im.native-module.core :as status] [status-im.node.core :as node] - [quo.design-system.colors :as colors] [status-im.utils.config :as config] [status-im.utils.fx :as fx] - [utils.security.core :as security] [status-im.utils.signing-phrase.core :as signing-phrase] - [status-im.utils.types :as types])) + [status-im.utils.types :as types] + [utils.security.core :as security])) -(defn normalize-derived-data-keys [derived-data] +(defn normalize-derived-data-keys + [derived-data] (->> derived-data (map (fn [[path {:keys [publicKey] :as data}]] - [path (cond-> (-> data - (dissoc :publicKey) - (assoc :public-key publicKey)))])) + [path + (cond-> (-> data + (dissoc :publicKey) + (assoc :public-key publicKey)))])) (into {}))) (defn normalize-multiaccount-data-keys [{:keys [publicKey keyUid derived] :as data}] (cond-> (-> data (dissoc :keyUid :publicKey) - (assoc :key-uid keyUid + (assoc :key-uid keyUid :public-key publicKey)) derived (update :derived normalize-derived-data-keys))) @@ -63,14 +65,14 @@ (ethereum/sha3 (security/safe-unmask-data key-code)) (fn [result] (let [derived-data (normalize-derived-data-keys (types/json->clj result)) - public-key (get-in derived-data [constants/path-whisper-keyword :public-key])] + public-key (get-in derived-data [constants/path-whisper-keyword :public-key])] (status/gfycat-identicon-async public-key (fn [name identicon] - (let [derived-whisper (derived-data constants/path-whisper-keyword) + (let [derived-whisper (derived-data constants/path-whisper-keyword) derived-data-extended (assoc-in derived-data - [constants/path-whisper-keyword] - (merge derived-whisper {:name name :identicon identicon}))] + [constants/path-whisper-keyword] + (merge derived-whisper {:name name :identicon identicon}))] (re-frame/dispatch [::store-multiaccount-success key-code derived-data-extended]))))))]})) (re-frame/reg-fx @@ -89,22 +91,26 @@ (fx/defn multiaccount-generate-and-derive-addresses-success {:events [:multiaccount-generate-and-derive-addresses-success]} [{:keys [db]} result] - {:db (update db :intro-wizard - (fn [data] - (-> data - (dissoc :processing?) - (assoc :multiaccounts result - :selected-storage-type :default - :selected-id (-> result first :id) - :step :choose-key)))) + {:db (update db + :intro-wizard + (fn [data] + (-> data + (dissoc :processing?) + (assoc :multiaccounts result + :selected-storage-type :default + :selected-id (-> result first :id) + :step :choose-key)))) :navigate-to-fx :choose-name}) (fx/defn generate-and-derive-addresses {:events [:generate-and-derive-addresses]} [{:keys [db]}] - {:db (-> db - (update :intro-wizard #(-> % (assoc :processing? true) (dissoc :recovering?))) - (dissoc :recovered-account?)) + {:db (-> db + (update :intro-wizard + #(-> % + (assoc :processing? true) + (dissoc :recovering?))) + (dissoc :recovered-account?)) :multiaccount-generate-and-derive-addresses nil}) (fx/defn prepare-intro-wizard @@ -150,7 +156,7 @@ {:public-key public-key :address (eip55/address->checksum address) :name name - :identicon identicon + :identicon identicon :path constants/path-whisper :chat true})]) @@ -158,45 +164,49 @@ [{:keys [signing-phrase random-guid-generator db] :as cofx} {:keys [address chat-key keycard-instance-uid key-uid keycard-pairing keycard-paired-on mnemonic recovered] - :as multiaccount} + :as multiaccount} password {:keys [save-mnemonic? login?] :or {login? true save-mnemonic? false}}] - (let [[wallet-account {:keys [public-key identicon name]} :as accounts-data] (prepare-accounts-data multiaccount) - multiaccount-data {:name name - :address address - :identicon identicon - :key-uid key-uid + (let [[wallet-account {:keys [public-key identicon name]} :as accounts-data] (prepare-accounts-data + multiaccount) + multiaccount-data {:name name + :address address + :identicon identicon + :key-uid key-uid :keycard-pairing keycard-pairing} keycard-multiaccount? (boolean keycard-pairing) - eip1581-address (get-in multiaccount [:derived - constants/path-eip1581-keyword - :address]) + eip1581-address (get-in multiaccount + [:derived + constants/path-eip1581-keyword + :address]) new-multiaccount - (cond-> (merge - {;; address of the master key - :address address - ;; sha256 of master public key - :key-uid key-uid - ;; The address from which we derive any wallet - :wallet-root-address - (get-in multiaccount [:derived - constants/path-wallet-root-keyword - :address]) - :name name - :identicon identicon - ;; public key of the chat account - :public-key public-key - ;; default address for Dapps - :dapps-address (:address wallet-account) - :latest-derived-path 0 - :signing-phrase signing-phrase - :send-push-notifications? true - :backup-enabled? true - :installation-id (random-guid-generator) - ;; default mailserver (history node) setting - :use-mailservers? true - :recovered recovered} - config/default-multiaccount) + (cond-> + (merge + {;; address of the master key + :address address + ;; sha256 of master public key + :key-uid key-uid + ;; The address from which we derive any wallet + :wallet-root-address + (get-in multiaccount + [:derived + constants/path-wallet-root-keyword + :address]) + :name name + :identicon identicon + ;; public key of the chat account + :public-key public-key + ;; default address for Dapps + :dapps-address (:address wallet-account) + :latest-derived-path 0 + :signing-phrase signing-phrase + :send-push-notifications? true + :backup-enabled? true + :installation-id (random-guid-generator) + ;; default mailserver (history node) setting + :use-mailservers? true + :recovered recovered} + config/default-multiaccount) ;; The address from which we derive any chat ;; account/encryption keys eip1581-address @@ -205,22 +215,22 @@ (assoc :mnemonic mnemonic) keycard-multiaccount? (assoc :keycard-instance-uid keycard-instance-uid - :keycard-pairing keycard-pairing - :keycard-paired-on keycard-paired-on)) + :keycard-pairing keycard-pairing + :keycard-paired-on keycard-paired-on)) db (assoc db - :multiaccounts/login {:key-uid key-uid - :name name - :identicon identicon - :password password - :creating? true - :processing true} - :multiaccount new-multiaccount - :multiaccount/accounts [wallet-account] + :multiaccounts/login {:key-uid key-uid + :name name + :identicon identicon + :password password + :creating? true + :processing true} + :multiaccount new-multiaccount + :multiaccount/accounts [wallet-account] :networks/current-network config/default-network - :networks/networks (data-store.settings/rpc->networks config/default-networks)) + :networks/networks (data-store.settings/rpc->networks config/default-networks)) settings (assoc new-multiaccount :networks/current-network config/default-network - :networks/networks config/default-networks)] + :networks/networks config/default-networks)] (fx/merge cofx {:db db} (if keycard-multiaccount? @@ -241,7 +251,7 @@ accounts-data))))) (fx/defn store-multiaccount-success - {:events [::store-multiaccount-success] + {:events [::store-multiaccount-success] :interceptors [(re-frame/inject-cofx :random-guid-generator) (re-frame/inject-cofx ::get-signing-phrase)]} [{:keys [db] :as cofx} password derived] @@ -249,7 +259,7 @@ {:db (dissoc db :intro-wizard)} (on-multiaccount-created (assoc (let [{:keys [selected-id multiaccounts]} (:intro-wizard db)] (some #(when (= selected-id (:id %)) %) multiaccounts)) - :derived derived + :derived derived :recovered (get-in db [:intro-wizard :recovering?])) password {:save-mnemonic? true}))) diff --git a/src/status_im/multiaccounts/db.cljs b/src/status_im/multiaccounts/db.cljs index 7af08de910..70f55bf7a6 100644 --- a/src/status_im/multiaccounts/db.cljs +++ b/src/status_im/multiaccounts/db.cljs @@ -2,7 +2,8 @@ (:require [cljs.spec.alpha :as spec] [status-im.constants :as const])) -(defn valid-length? [password] +(defn valid-length? + [password] (>= (count password) const/min-password-length)) -(spec/def ::password (spec/and string? not-empty valid-length?)) +(spec/def ::password (spec/and string? not-empty valid-length?)) diff --git a/src/status_im/multiaccounts/key_storage/core.cljs b/src/status_im/multiaccounts/key_storage/core.cljs index b787edfa3d..fa7e8181d6 100644 --- a/src/status_im/multiaccounts/key_storage/core.cljs +++ b/src/status_im/multiaccounts/key_storage/core.cljs @@ -1,22 +1,22 @@ (ns status-im.multiaccounts.key-storage.core (:require [clojure.string :as string] [re-frame.core :as re-frame] + [status-im.bottom-sheet.core :as bottom-sheet] + [status-im.ethereum.core :as ethereum] [status-im.ethereum.mnemonic :as mnemonic] + [status-im.i18n.i18n :as i18n] + [status-im.keycard.backup-key :as keycard.backup] [status-im.keycard.common :as common] [status-im.multiaccounts.core :as multiaccounts] - [status-im.multiaccounts.recover.core :as multiaccounts.recover] - [status-im.multiaccounts.model :as multiaccounts.model] [status-im.multiaccounts.logout.core :as multiaccounts.logout] + [status-im.multiaccounts.model :as multiaccounts.model] + [status-im.multiaccounts.recover.core :as multiaccounts.recover] [status-im.native-module.core :as native-module] - [status-im2.navigation.events :as navigation] [status-im.popover.core :as popover] [status-im.utils.fx :as fx] - [utils.security.core :as security] - [status-im.ethereum.core :as ethereum] - [status-im.i18n.i18n :as i18n] [status-im.utils.types :as types] - [status-im.keycard.backup-key :as keycard.backup] - [status-im.bottom-sheet.core :as bottom-sheet])) + [status-im2.navigation.events :as navigation] + [utils.security.core :as security])) (fx/defn key-and-storage-management-pressed "This event can be dispatched before login and from profile and needs to redirect accordingly" @@ -46,7 +46,11 @@ cofx {:db (-> db (dissoc :recovered-account?) - (update :keycard dissoc :from-key-storage-and-migration? :creating-backup? :factory-reset-card?))} + (update :keycard + dissoc + :from-key-storage-and-migration? + :creating-backup? + :factory-reset-card?))} (navigation/navigate-back))) (fx/defn enter-seed-pressed @@ -62,12 +66,13 @@ {:events [::seed-phrase-input-changed]} [{:keys [db] :as cofx} masked-seed-phrase] (let [seed-phrase (security/safe-unmask-data masked-seed-phrase)] - {:db (update db :multiaccounts/key-storage assoc - :seed-phrase (when seed-phrase - (string/lower-case seed-phrase)) - :seed-shape-invalid? (or (empty? seed-phrase) - (not (mnemonic/valid-length? seed-phrase))) - :seed-word-count (mnemonic/words-count seed-phrase))})) + {:db (update db + :multiaccounts/key-storage assoc + :seed-phrase (when seed-phrase + (string/lower-case seed-phrase)) + :seed-shape-invalid? (or (empty? seed-phrase) + (not (mnemonic/valid-length? seed-phrase))) + :seed-word-count (mnemonic/words-count seed-phrase))})) (fx/defn key-uid-seed-mismatch {:events [::show-seed-key-uid-mismatch-error-popup]} @@ -100,25 +105,30 @@ ::validate-seed-against-key-uid (partial validate-seed-against-key-uid {:import-mnemonic-fn native-module/multiaccount-import-mnemonic - :on-success #(re-frame/dispatch [::key-uid-matches]) - :on-error #(re-frame/dispatch [::show-seed-key-uid-mismatch-error-popup])})) + :on-success #(re-frame/dispatch [::key-uid-matches]) + :on-error #(re-frame/dispatch [::show-seed-key-uid-mismatch-error-popup])})) (fx/defn seed-phrase-validated {:events [::seed-phrase-validated]} [{:keys [db] :as cofx} validation-error] - (let [error? (-> validation-error - types/json->clj - :error - string/blank? - not) + (let [error? (-> validation-error + types/json->clj + :error + string/blank? + not) onboarding? (not (or (:multiaccounts/login db) (:multiaccount db)))] (if error? (popover/show-popover cofx {:view :custom-seed-phrase}) {::validate-seed-against-key-uid {:seed-phrase (-> db :multiaccounts/key-storage :seed-phrase) - ;; Unique key-uid of the account for which we are going to move keys - :key-uid (or (-> db :multiaccounts/login :key-uid) - (-> db :multiaccount :key-uid) - (and onboarding? (-> db :keycard :application-info :key-uid)))}}))) + ;; Unique key-uid of the account for which we are going to move + ;; keys + :key-uid (or (-> db :multiaccounts/login :key-uid) + (-> db :multiaccount :key-uid) + (and onboarding? + (-> db + :keycard + :application-info + :key-uid)))}}))) (fx/defn choose-storage-pressed {:events [::choose-storage-pressed]} @@ -163,7 +173,7 @@ We don't need to take the exact steps, just set the required state and redirect " (fx/defn import-multiaccount [{:keys [db] :as cofx}] - {:dispatch [:bottom-sheet/hide] + {:dispatch [:bottom-sheet/hide] ::multiaccounts.recover/import-multiaccount {:passphrase (get-in db [:multiaccounts/key-storage :seed-phrase]) :password nil @@ -193,9 +203,10 @@ We don't need to take the exact steps, just set the required state and redirect {:events [::password-changed]} [{db :db} password] (let [unmasked-pass (security/safe-unmask-data password)] - {:db (update db :keycard assoc - :migration-password password - :migration-password-error nil + {:db (update db + :keycard assoc + :migration-password password + :migration-password-error nil :migration-password-valid? (and unmasked-pass (> (count unmasked-pass) 5)))})) (fx/defn verify-password-result @@ -221,19 +232,20 @@ We don't need to take the exact steps, just set the required state and redirect {:events [::import-multiaccount-success]} [{:keys [db] :as cofx} root-data derived-data] (fx/merge cofx - {:db (-> db - (update :intro-wizard - assoc - :root-key root-data - :derived derived-data - :recovering? true - :selected-storage-type :advanced) - (assoc-in [:keycard :flow] :recovery) - (assoc-in [:keycard :from-key-storage-and-migration?] true) - (assoc-in [:keycard :converting-account?] (not (get-in db [:multiaccounts/key-storage :reset-db-checked?]))) - (assoc-in [:keycard :delete-account?] - (true? (get-in db [:multiaccounts/key-storage :reset-db-checked?]))) - (dissoc :multiaccounts/key-storage))} + {:db (-> db + (update :intro-wizard + assoc + :root-key root-data + :derived derived-data + :recovering? true + :selected-storage-type :advanced) + (assoc-in [:keycard :flow] :recovery) + (assoc-in [:keycard :from-key-storage-and-migration?] true) + (assoc-in [:keycard :converting-account?] + (not (get-in db [:multiaccounts/key-storage :reset-db-checked?]))) + (assoc-in [:keycard :delete-account?] + (true? (get-in db [:multiaccounts/key-storage :reset-db-checked?]))) + (dissoc :multiaccounts/key-storage))} (popover/hide-popover) (common/listen-to-hardware-back-button) (navigation/navigate-to-cofx :keycard-onboarding-intro nil))) @@ -264,9 +276,11 @@ We don't need to take the exact steps, just set the required state and redirect (comment ;; check import mnemonic output - (native-module/multiaccount-import-mnemonic "rocket mixed rebel affair umbrella legal resemble scene virus park deposit cargo" nil - (fn [result] - (prn (types/json->clj result)))) + (native-module/multiaccount-import-mnemonic + "rocket mixed rebel affair umbrella legal resemble scene virus park deposit cargo" + nil + (fn [result] + (prn (types/json->clj result)))) ;; check delete account output (native-module/delete-multiaccount "0x3831d0f22996a65970a214f0a94bfa9a63a21dac235d8dadb91be8e32e7d3ab7" (fn [result] diff --git a/src/status_im/multiaccounts/key_storage/core_test.cljs b/src/status_im/multiaccounts/key_storage/core_test.cljs index ea1e59022d..5ad48f93dd 100644 --- a/src/status_im/multiaccounts/key_storage/core_test.cljs +++ b/src/status_im/multiaccounts/key_storage/core_test.cljs @@ -15,7 +15,7 @@ (is (get-in res [:db :multiaccounts/key-storage :seed-shape-invalid?])))) (let [sample-phrase "h h h h h h h h h h h H" ;; 12 characters - res (models/seed-phrase-input-changed {:db {}} (security/mask-data sample-phrase))] + res (models/seed-phrase-input-changed {:db {}} (security/mask-data sample-phrase))] (testing "Seed shape for 12 letter seed phrase is valid" (is (false? (get-in res [:db :multiaccounts/key-storage :seed-shape-invalid?])))) @@ -28,9 +28,10 @@ (def seed-key-uid-pair {:seed-phrase "rocket mixed rebel affair umbrella legal resemble scene virus park deposit cargo" - :key-uid "0x3831d0f22996a65970a214f0a94bfa9a63a21dac235d8dadb91be8e32e7d3ab7"}) + :key-uid "0x3831d0f22996a65970a214f0a94bfa9a63a21dac235d8dadb91be8e32e7d3ab7"}) -(defn mock-import-mnemonic-fn [_ _ _] +(defn mock-import-mnemonic-fn + [_ _ _] ;; return json with keyUid, the real world will have more info in the response (str "{\"keyUid\": \"" (:key-uid seed-key-uid-pair) "\"}")) @@ -38,25 +39,26 @@ (testing "Success event is triggered if correct seed is entered for selected multiaccount (key-uid)" (models/validate-seed-against-key-uid {:import-mnemonic-fn mock-import-mnemonic-fn - :on-success #(is true) ; this callback should be called - :on-error #(is false)} + :on-success #(is true) ; this callback should be called + :on-error #(is false)} {:seed-phrase (:seed-phrase seed-key-uid-pair) - :key-uid (:key-uid seed-key-uid-pair)})) + :key-uid (:key-uid seed-key-uid-pair)})) (testing "Error event is triggered if incorrect seed is entered for selected multiaccount" (models/validate-seed-against-key-uid {:import-mnemonic-fn mock-import-mnemonic-fn - :on-success #(is false) - :on-error #(is true)} + :on-success #(is false) + :on-error #(is true)} {:seed-phrase (:seed-phrase seed-key-uid-pair) - :key-uid "0xInvalid-Will-make-the-function-fail"}))) + :key-uid "0xInvalid-Will-make-the-function-fail"}))) (deftest handle-multiaccount-import (testing "Sets correct state for Keycard onboarding after multiaccounts seeds are derived" (let [res (models/handle-multiaccount-import {:db {}} :passed-root-data :passed-derived-data)] (is (= :passed-root-data (get-in res [:db :intro-wizard :root-key]))) (is (= :passed-derived-data (get-in res [:db :intro-wizard :derived]))) - (is (= :advanced (get-in res [:db :intro-wizard :selected-storage-type]))) ; :advanced storage type means Keycard + (is (= :advanced (get-in res [:db :intro-wizard :selected-storage-type]))) ; :advanced storage type + ; means Keycard (is (= :recovery (get-in res [:db :keycard :flow]))) (is (get-in res [:db :keycard :from-key-storage-and-migration?]))))) diff --git a/src/status_im/multiaccounts/login/core.cljs b/src/status_im/multiaccounts/login/core.cljs index c5d31c8dc0..0b608865da 100644 --- a/src/status_im/multiaccounts/login/core.cljs +++ b/src/status_im/multiaccounts/login/core.cljs @@ -1,48 +1,48 @@ (ns status-im.multiaccounts.login.core - (:require [re-frame.core :as re-frame] + (:require [clojure.string :as string] + [re-frame.core :as re-frame] + [status-im.async-storage.core :as async-storage] + [status-im.chat.models.link-preview :as link-preview] + [status-im.communities.core :as communities] [status-im.contact.core :as contact] - [status-im.utils.config :as config] + [status-im.data-store.chats :as data-store.chats] + [status-im.data-store.invitations :as data-store.invitations] [status-im.data-store.settings :as data-store.settings] + [status-im.data-store.visibility-status-updates :as visibility-status-updates-store] [status-im.ethereum.core :as ethereum] - [status-im.ethereum.transactions.core :as transactions] [status-im.ethereum.eip55 :as eip55] [status-im.ethereum.json-rpc :as json-rpc] - [status-im.keycard.common :as keycard.common] + [status-im.ethereum.tokens :as tokens] + [status-im.ethereum.transactions.core :as transactions] [status-im.fleet.core :as fleet] [status-im.i18n.i18n :as i18n] + [status-im.keycard.common :as keycard.common] + [status-im.mobile-sync-settings.core :as mobile-network] [status-im.multiaccounts.biometric.core :as biometric] [status-im.multiaccounts.core :as multiaccounts] [status-im.native-module.core :as status] + [status-im.node.core :as node] [status-im.notifications.core :as notifications] [status-im.popover.core :as popover] - [status-im.communities.core :as communities] + [status-im.signing.eip1559 :as eip1559] [status-im.transport.core :as transport] - [status-im.mobile-sync-settings.core :as mobile-network] + [status-im.ui.components.react :as react] + [status-im.utils.config :as config] [status-im.utils.fx :as fx] [status-im.utils.keychain.core :as keychain] - [status-im2.setup.log :as logging] - [utils.security.core :as security] + [status-im.utils.mobile-sync :as utils.mobile-sync] + [status-im.utils.platform :as platform] [status-im.utils.types :as types] [status-im.utils.utils :as utils] - [status-im.wallet.core :as wallet] + [status-im.utils.wallet-connect :as wallet-connect] [status-im.wallet-connect-legacy.core :as wallet-connect-legacy] + [status-im.wallet.core :as wallet] [status-im.wallet.prices :as prices] - [taoensso.timbre :as log] - [status-im.data-store.invitations :as data-store.invitations] - [status-im.chat.models.link-preview :as link-preview] - [status-im.utils.mobile-sync :as utils.mobile-sync] - [status-im.async-storage.core :as async-storage] [status-im2.contexts.activity-center.events :as activity-center] [status-im2.navigation.events :as navigation] - [status-im.signing.eip1559 :as eip1559] - [status-im.data-store.chats :as data-store.chats] - [status-im.data-store.visibility-status-updates :as visibility-status-updates-store] - [status-im.ui.components.react :as react] - [status-im.utils.platform :as platform] - [status-im.ethereum.tokens :as tokens] - [clojure.string :as string] - [status-im.utils.wallet-connect :as wallet-connect] - [status-im.node.core :as node])) + [status-im2.setup.log :as logging] + [taoensso.timbre :as log] + [utils.security.core :as security])) (re-frame/reg-fx ::initialize-communities-enabled @@ -89,11 +89,13 @@ #(re-frame/dispatch [:wallet-connect/client-init %]) #(log/error "[wallet-connect]" %)))) -(defn rpc->accounts [accounts] +(defn rpc->accounts + [accounts] (reduce (fn [acc {:keys [chat type wallet] :as account}] (if chat acc - (let [account (cond-> (update account :address + (let [account (cond-> (update account + :address eip55/address->checksum) type (update :type keyword))] @@ -119,12 +121,14 @@ "0x86a12d91c813f69a53349ff640e32af97d5f5c1f8d42d54cf4c8aa8dea231955" "0x0011a30f5b2023bc228f6dd5608b3e7149646fa83f33350926ceb1925af78f08"}) -(fx/defn check-invalid-ens [{:keys [db]}] +(fx/defn check-invalid-ens + [{:keys [db]}] (async-storage/get-item :invalid-ens-name-seen (fn [already-seen] (when (and (not already-seen) - (boolean (get invalid-addrr (ethereum/sha3 (string/lower-case (ethereum/default-address db)))))) + (boolean (get invalid-addrr + (ethereum/sha3 (string/lower-case (ethereum/default-address db)))))) (utils/show-popup (i18n/label :t/warning) (i18n/label :t/ens-username-invalid-name-warning) @@ -137,7 +141,8 @@ favourites scan-all-tokens? new-account?] (fx/merge cofx - {:db (assoc db :multiaccount/accounts + {:db (assoc db + :multiaccount/accounts (rpc->accounts accounts)) ;; NOTE: Local notifications should be enabled only after wallet was started ::enable-local-notifications nil} @@ -146,16 +151,18 @@ (wallet/initialize-favourites favourites) (wallet/get-pending-transactions) (wallet/fetch-collectibles-collection) - (cond (and new-account? - (not scan-all-tokens?)) - (wallet/set-zero-balances (first accounts)) + (cond + (and new-account? + (not scan-all-tokens?)) + (wallet/set-zero-balances (first accounts)) - (and new-account? scan-all-tokens? - (not (utils.mobile-sync/cellular? (:network/type db)))) - (wallet/set-max-block (get (first accounts) :address) 0) + (and new-account? + scan-all-tokens? + (not (utils.mobile-sync/cellular? (:network/type db)))) + (wallet/set-max-block (get (first accounts) :address) 0) - :else - (wallet/get-cached-balances scan-all-tokens?)) + :else + (wallet/get-cached-balances scan-all-tokens?)) (when-not (get db :wallet/new-account) (wallet/restart-wallet-service nil)) (when (or (not (utils.mobile-sync/syncing-allowed? cofx)) @@ -170,14 +177,14 @@ {:events [:multiaccounts.login.ui/password-input-submitted]} [{:keys [db]}] (let [{:keys [key-uid password name identicon]} (:multiaccounts/login db)] - {:db (-> db - (assoc-in [:multiaccounts/login :processing] true) - (dissoc :intro-wizard :recovered-account?) - (update :keycard dissoc :flow)) + {:db (-> db + (assoc-in [:multiaccounts/login :processing] true) + (dissoc :intro-wizard :recovered-account?) + (update :keycard dissoc :flow)) ::login [key-uid - (types/clj->json {:name name - :key-uid key-uid - :identicon identicon}) + (types/clj->json {:name name + :key-uid key-uid + :identicon identicon}) (ethereum/sha3 (security/safe-unmask-data password))]})) (fx/defn export-db-submitted @@ -192,24 +199,25 @@ (fn [path] (when platform/ios? (let [uri (str "file://" path)] - (.share ^js react/sharing (clj->js {:title "Unencrypted database" - :url uri})))))]})) + (.share ^js react/sharing + (clj->js {:title "Unencrypted database" + :url uri})))))]})) (fx/defn import-db-submitted {:events [:multiaccounts.login.ui/import-db-submitted]} [{:keys [db]}] (let [{:keys [key-uid password name identicon]} (:multiaccounts/login db)] {::import-db [key-uid - (types/clj->json {:name name - :key-uid key-uid - :identicon identicon}) + (types/clj->json {:name name + :key-uid key-uid + :identicon identicon}) (ethereum/sha3 (security/safe-unmask-data password))]})) (fx/defn finish-keycard-setup [{:keys [db] :as cofx}] {:db (update db :keycard dissoc :flow)}) -(fx/defn initialize-dapp-permissions +(fx/defn initialize-dapp-permissions {:events [::initialize-dapp-permissions]} [{:keys [db]} all-dapp-permissions] (let [dapp-permissions (reduce (fn [acc {:keys [dapp] :as dapp-permissions}] @@ -239,10 +247,12 @@ (fx/defn initialize-invitations {:events [::initialize-invitations]} [{:keys [db]} invitations] - {:db (assoc db :group-chat/invitations (reduce (fn [acc {:keys [id] :as inv}] - (assoc acc id (data-store.invitations/<-rpc inv))) - {} - invitations))}) + {:db (assoc db + :group-chat/invitations + (reduce (fn [acc {:keys [id] :as inv}] + (assoc acc id (data-store.invitations/<-rpc inv))) + {} + invitations))}) (fx/defn initialize-web3-client-version {:events [::initialize-web3-client-version]} @@ -257,7 +267,7 @@ (fx/defn check-network-version [_ network-id] {::json-rpc/call - [{:method "net_version" + [{:method "net_version" :on-success (fn [fetched-network-id] (when (not= network-id (str (int fetched-network-id))) @@ -270,7 +280,8 @@ :fetched-network-id fetched-network-id}) #(re-frame/dispatch [::close-app-confirmed]))))}]}) -(defn normalize-tokens [network-id tokens] +(defn normalize-tokens + [network-id tokens] (mapv #(-> % (update :symbol keyword) ((partial tokens/update-icon (ethereum/chain-id->chain-keyword (int network-id))))) @@ -281,8 +292,8 @@ (fn [[network-id accounts recovered-account?]] (utils/set-timeout (fn [] - (json-rpc/call {:method "wallet_getTokens" - :params [(int network-id)] + (json-rpc/call {:method "wallet_getTokens" + :params [(int network-id)] :on-success #(re-frame/dispatch [::initialize-wallet accounts (normalize-tokens network-id %) @@ -299,25 +310,25 @@ (clj->js [(js/Promise. (fn [resolve reject] - (json-rpc/call {:method "accounts_getAccounts" + (json-rpc/call {:method "accounts_getAccounts" :on-success resolve - :on-error reject}))) + :on-error reject}))) (js/Promise. (fn [resolve _] - (json-rpc/call {:method "wallet_getTokens" - :params [(int network-id)] + (json-rpc/call {:method "wallet_getTokens" + :params [(int network-id)] :on-success resolve - :on-error #(resolve nil)}))) + :on-error #(resolve nil)}))) (js/Promise. (fn [resolve reject] - (json-rpc/call {:method "wallet_getCustomTokens" + (json-rpc/call {:method "wallet_getCustomTokens" :on-success resolve - :on-error reject}))) + :on-error reject}))) (js/Promise. (fn [resolve reject] - (json-rpc/call {:method "wallet_getSavedAddresses" + (json-rpc/call {:method "wallet_getSavedAddresses" :on-success resolve - :on-error reject})))])) + :on-error reject})))])) (.then (fn [[accounts tokens custom-tokens favourites]] (callback accounts (normalize-tokens network-id tokens) @@ -326,7 +337,8 @@ (.catch (fn [_] (log/error "Failed to initialize wallet")))))) -(fx/defn initialize-browser [_] +(fx/defn initialize-browser + [_] {::json-rpc/call [{:method "wakuext_getBrowsers" :on-success #(re-frame/dispatch [::initialize-browsers %])} @@ -335,10 +347,12 @@ {:method "permissions_getDappPermissions" :on-success #(re-frame/dispatch [::initialize-dapp-permissions %])}]}) -(fx/defn initialize-appearance [cofx] +(fx/defn initialize-appearance + [cofx] {:multiaccounts.ui/switch-theme (get-in cofx [:db :multiaccount :appearance])}) -(fx/defn get-group-chat-invitations [_] +(fx/defn get-group-chat-invitations + [_] {::json-rpc/call [{:method "wakuext_getGroupChatInvitations" :on-success #(re-frame/dispatch [::initialize-invitations %])}]}) @@ -359,8 +373,9 @@ {:events [::get-node-config-callback]} [{:keys [db] :as cofx} node-config-json] (let [node-config (types/json->clj node-config-json)] - {:db (assoc-in db [:multiaccount :wakuv2-config] - (get node-config :WakuV2Config))})) + {:db (assoc-in db + [:multiaccount :wakuv2-config] + (get node-config :WakuV2Config))})) (fx/defn get-node-config [_] @@ -373,15 +388,16 @@ :as settings} (data-store.settings/rpc->settings settings) multiaccount (dissoc settings :networks/current-network :networks/networks) - ;;for some reason we save default networks in db, in case when we want to modify default-networks for + ;;for some reason we save default networks in db, in case when we want to modify default-networks + ;;for ;; existing accounts we have to merge them again into networks merged-networks (merge networks config/default-networks-by-id)] (fx/merge cofx {:db (-> db (dissoc :multiaccounts/login) (assoc :networks/current-network current-network - :networks/networks merged-networks - :multiaccount multiaccount))} + :networks/networks merged-networks + :multiaccount multiaccount))} (data-store.chats/fetch-chats-rpc {:on-success #(do (re-frame/dispatch [:chats-list/load-success %]) @@ -416,20 +432,21 @@ (fx/defn update-wallet-accounts [{:keys [db]} accounts] (let [existing-accounts (into {} (map #(vector (:address %1) %1) (:multiaccount/accounts db))) - reduce-fn (fn [existing-accs new-acc] - (let [address (:address new-acc)] - (if (:removed new-acc) - (dissoc existing-accs address) - (assoc existing-accs address new-acc)))) - new-accounts (reduce reduce-fn existing-accounts (rpc->accounts accounts))] + reduce-fn (fn [existing-accs new-acc] + (let [address (:address new-acc)] + (if (:removed new-acc) + (dissoc existing-accs address) + (assoc existing-accs address new-acc)))) + new-accounts (reduce reduce-fn existing-accounts (rpc->accounts accounts))] {:db (assoc db :multiaccount/accounts (vals new-accounts))})) (fx/defn get-chats-callback {:events [::get-chats-callback]} [{:keys [db] :as cofx}] (let [{:networks/keys [current-network networks]} db - notifications-enabled? (get-in db [:multiaccount :notifications-enabled?]) - network-id (str (get-in networks [current-network :config :NetworkId])) + notifications-enabled? (get-in db [:multiaccount :notifications-enabled?]) + network-id (str (get-in networks + [current-network :config :NetworkId])) remote-push-notifications-enabled? (get-in db [:multiaccount :remote-push-notifications-enabled?])] (fx/merge cofx @@ -442,7 +459,7 @@ (fn [accounts tokens custom-tokens favourites] (re-frame/dispatch [::initialize-wallet accounts tokens custom-tokens favourites]))] - ::open-last-chat (get-in db [:multiaccount :key-uid])} + ::open-last-chat (get-in db [:multiaccount :key-uid])} (or notifications-enabled? remote-push-notifications-enabled?) (assoc ::notifications/enable remote-push-notifications-enabled?)) (transport/start-messenger) @@ -457,7 +474,8 @@ (link-preview/request-link-preview-whitelist) (visibility-status-updates-store/fetch-visibility-status-updates-rpc)))) -(defn get-new-auth-method [auth-method save-password?] +(defn get-new-auth-method + [auth-method save-password?] (when save-password? (when-not (or (= keychain/auth-method-biometric auth-method) (= keychain/auth-method-password auth-method)) @@ -477,10 +495,10 @@ (let [auth-method (:auth-method db) new-auth-method (get-new-auth-method auth-method save-password?)] (log/debug "[login] login-only-events" - "auth-method" auth-method + "auth-method" auth-method "new-auth-method" new-auth-method) (fx/merge cofx - {:db (assoc db :chats/loading? true) + {:db (assoc db :chats/loading? true) ::json-rpc/call [{:method "settings_getSettings" :on-success #(do (re-frame/dispatch [::get-settings-callback %]) @@ -488,24 +506,26 @@ (notifications/load-notification-preferences) (when save-password? (keychain/save-user-password key-uid password)) - (keychain/save-auth-method key-uid (or new-auth-method auth-method keychain/auth-method-none))))) + (keychain/save-auth-method key-uid + (or new-auth-method auth-method keychain/auth-method-none))))) (fx/defn create-only-events [{:keys [db] :as cofx} recovered-account?] (let [{:keys [multiaccount :multiaccounts/multiaccounts - :multiaccount/accounts]} db - {:keys [creating?]} (:multiaccounts/login db) - first-account? (and creating? - (empty? multiaccounts)) - tos-accepted? (get db :tos/accepted?) + :multiaccount/accounts]} + db + {:keys [creating?]} (:multiaccounts/login db) + first-account? (and creating? + (empty? multiaccounts)) + tos-accepted? (get db :tos/accepted?) {:networks/keys [current-network networks]} db network-id (str (get-in networks [current-network :config :NetworkId]))] (fx/merge cofx - {:db (-> db - (dissoc :multiaccounts/login) - (assoc :tos/next-root :onboarding-notification :chats/loading? false) - (assoc-in [:multiaccount :multiaccounts/first-account] first-account?)) + {:db (-> db + (dissoc :multiaccounts/login) + (assoc :tos/next-root :onboarding-notification :chats/loading? false) + (assoc-in [:multiaccount :multiaccounts/first-account] first-account?)) ::get-tokens [network-id accounts recovered-account?]} (finish-keycard-setup) (transport/start-messenger) @@ -525,14 +545,16 @@ (navigation/init-root :onboarding-notification) (navigation/init-root :tos)))))) -(defn- keycard-setup? [cofx] +(defn- keycard-setup? + [cofx] (boolean (get-in cofx [:db :keycard :flow]))) -(defn on-login-update-db [db login-only? now] +(defn on-login-update-db + [db login-only? now] (-> db (dissoc :connectivity/ui-status-properties) (update :keycard dissoc :from-key-storage-and-migration?) - (update :keycard dissoc + (update :keycard dissoc :on-card-read :card-read-in-progress? :pin @@ -549,18 +571,20 @@ (let [{:keys [key-uid password save-password? creating?]} (:multiaccounts/login db) - multiaccounts (:multiaccounts/multiaccounts db) - recovered-account? (get db :recovered-account?) - login-only? (not (or creating? - recovered-account? - (keycard-setup? cofx))) - from-migration? (get-in db [:keycard :from-key-storage-and-migration?]) - nodes nil] + multiaccounts (:multiaccounts/multiaccounts db) + recovered-account? (get db :recovered-account?) + login-only? (not (or creating? + recovered-account? + (keycard-setup? cofx))) + from-migration? (get-in db + [:keycard + :from-key-storage-and-migration?]) + nodes nil] (log/debug "[multiaccount] multiaccount-login-success" - "login-only?" login-only? + "login-only?" login-only? "recovered-account?" recovered-account?) (fx/merge cofx - {:db (on-login-update-db db login-only? now) + {:db (on-login-update-db db login-only? now) ::json-rpc/call [{:method "web3_clientVersion" :on-success #(re-frame/dispatch [::initialize-web3-client-version %])}]} @@ -571,7 +595,8 @@ (not recovered-account?)) (wallet/set-initial-blocks-range)) (when from-migration? - (utils/show-popup (i18n/label :t/migration-successful) (i18n/label :t/migration-successful-text))) + (utils/show-popup (i18n/label :t/migration-successful) + (i18n/label :t/migration-successful-text))) (if login-only? (login-only-events key-uid password save-password?) (create-only-events recovered-account?))))) @@ -596,17 +621,20 @@ (fx/defn open-login-callback {:events [:multiaccounts.login.callback/get-user-password-success]} [{:keys [db] :as cofx} password] - (let [key-uid (get-in db [:multiaccounts/login :key-uid]) - keycard-account? (boolean (get-in db [:multiaccounts/multiaccounts - key-uid - :keycard-pairing])) + (let [key-uid (get-in db [:multiaccounts/login :key-uid]) + keycard-account? (boolean (get-in db + [:multiaccounts/multiaccounts + key-uid + :keycard-pairing])) goto-key-storage? (:goto-key-storage? db)] (if password (fx/merge cofx - {:db (update-in db [:multiaccounts/login] assoc - :password password - :save-password? true) + {:db (update-in db + [:multiaccounts/login] + assoc + :password password + :save-password? true) :init-root-fx :progress} login) (fx/merge @@ -624,9 +652,11 @@ (fx/defn get-credentials [{:keys [db] :as cofx} key-uid] - (let [keycard-multiaccount? (boolean (get-in db [:multiaccounts/multiaccounts key-uid :keycard-pairing]))] + (let [keycard-multiaccount? (boolean (get-in db + [:multiaccounts/multiaccounts key-uid :keycard-pairing]))] (log/debug "[login] get-credentials" - "keycard-multiacc?" keycard-multiaccount?) + "keycard-multiacc?" + keycard-multiaccount?) (if keycard-multiaccount? (keychain/get-keycard-keys cofx key-uid) (keychain/get-user-password cofx key-uid)))) @@ -636,9 +666,10 @@ {:events [:multiaccounts.login/get-auth-method-success]} [{:keys [db] :as cofx} auth-method] (let [key-uid (get-in db [:multiaccounts/login :key-uid]) - keycard-multiaccount? (boolean (get-in db [:multiaccounts/multiaccounts key-uid :keycard-pairing]))] + keycard-multiaccount? (boolean (get-in db + [:multiaccounts/multiaccounts key-uid :keycard-pairing]))] (log/debug "[login] get-auth-method-success" - "auth-method" auth-method + "auth-method" auth-method "keycard-multiacc?" keycard-multiaccount?) (fx/merge cofx @@ -661,13 +692,13 @@ (log/debug "[biometric] biometric-auth-done" "bioauth-success" bioauth-success "bioauth-message" bioauth-message - "bioauth-code" bioauth-code) + "bioauth-code" bioauth-code) (if bioauth-success (get-credentials cofx key-uid) (fx/merge cofx {:db (assoc-in db - [:multiaccounts/login :save-password?] - (= auth-method keychain/auth-method-biometric))} + [:multiaccounts/login :save-password?] + (= auth-method keychain/auth-method-biometric))} (when-not (= auth-method keychain/auth-method-biometric) (keychain/save-auth-method key-uid keychain/auth-method-none)) (biometric/show-message bioauth-message bioauth-code) @@ -679,8 +710,8 @@ (let [bioauth-supported? (boolean (get db :supported-biometric-auth)) previous-auth-method (get db :auth-method)] (log/debug "[login] save-password" - "save-password?" save-password? - "bioauth-supported?" bioauth-supported? + "save-password?" save-password? + "bioauth-supported?" bioauth-supported? "previous-auth-method" previous-auth-method) (fx/merge cofx @@ -710,11 +741,11 @@ [{:keys [db] :as cofx} key-uid] ;; We specifically pass a bunch of fields instead of the whole multiaccount ;; as we want store some fields in multiaccount that are not here - (let [multiaccount (get-in db [:multiaccounts/multiaccounts key-uid]) + (let [multiaccount (get-in db [:multiaccounts/multiaccounts key-uid]) keycard-multiaccount? (boolean (:keycard-pairing multiaccount))] (fx/merge cofx - {:db (update db :keycard dissoc :application-info) + {:db (update db :keycard dissoc :application-info) :navigate-to-fx (if keycard-multiaccount? :keycard-login-pin :login)} (open-login (select-keys multiaccount [:key-uid :name :public-key :identicon :images]))))) diff --git a/src/status_im/multiaccounts/login/data_test.cljs b/src/status_im/multiaccounts/login/data_test.cljs index 9ee1bbb68b..aa723fe766 100644 --- a/src/status_im/multiaccounts/login/data_test.cljs +++ b/src/status_im/multiaccounts/login/data_test.cljs @@ -1,21 +1,25 @@ (ns status-im.multiaccounts.login.data-test) (def all-contacts - [{:last-updated 1547185503000 - :tags #{} - :address "2f88d65f3cb52605a54a833ae118fb1363acccd2" - :name "Darkviolet Lightgreen Halcyon" - :identicon "data:image/png;base64iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAMAAAC7IEhfAAADAFBMVEX///+M2KwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADdPOdBAAABAHRSTlP//wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKmfXxgAABnNJREFUeNoBaAaX+QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAQEBAQEBAQEBAQEBAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQEBAQEBAQEBAQEBAQEBAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQEBAQEBAQEBAQEBAQEBAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAQEBAQEBAQEBAQEBAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAQEBAQEBAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQEBAQEBAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQEBAQEBAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAQEBAQEBAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAQEBAQEBAQEBAQEBAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQEBAQEBAQEBAQEBAQEBAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQEBAQEBAQEBAQEBAQEBAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAQEBAQEBAQEBAQEBAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAAAAAAEBAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQEAAAAAAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQAAAAABAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAAAAAAEBAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAY5UBARL8TK8AAAAASUVORK5CYII=" - :added true - :last-online 0 - :public-key "0x04d6e56a475cd35f512d6ce0bf76c2c2af435c85ff48c2b9bdefd129f620e051a436f50961eae5717b2a750e59c3f5b60647d927da46d0b8b11621640b5678fc24"} - {:last-updated 1547271764000 - :address "b267ff8336ac10b3a1986c04a70ff91fb03d0b78" - :name "rv" - :identicon "data:image/png;base64iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAMAAAC7IEhfAAADAFBMVEX////VjNgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwYzy6AAABAHRSTlP//wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKmfXxgAABnNJREFUeNoBaAaX+QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQEBAQEBAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAQEBAQEBAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQEBAQEBAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQEBAQEBAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQEBAQEBAQEBAQEBAQEBAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQEBAQEBAQEBAQEBAQEBAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAQEBAQEBAQEBAQEBAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQEBAQEBAQEBAQEBAQEBAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQEBAQEBAAAAAAEBAQEBAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQEBAQEAAAAAAQEBAQEBAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAQEBAQAAAAABAQEBAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQEBAQEBAAAAAAEBAQEBAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA6IYA4bRtf+EAAAAASUVORK5CYII=" - :added true - :last-online 0 - :public-key "0x043ae31038ff45a31b096a91d3f8290e079366fbbae76a00fbbd349cd0e5b8d7598965d206772ec4504f68908649a08383cdc51a52cdae5e9ccc744ace4d37020f"}]) + [{:last-updated 1547185503000 + :tags #{} + :address "2f88d65f3cb52605a54a833ae118fb1363acccd2" + :name "Darkviolet Lightgreen Halcyon" + :identicon + "data:image/png;base64iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAMAAAC7IEhfAAADAFBMVEX///+M2KwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADdPOdBAAABAHRSTlP//wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKmfXxgAABnNJREFUeNoBaAaX+QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAQEBAQEBAQEBAQEBAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQEBAQEBAQEBAQEBAQEBAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQEBAQEBAQEBAQEBAQEBAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAQEBAQEBAQEBAQEBAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAQEBAQEBAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQEBAQEBAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQEBAQEBAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAQEBAQEBAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAQEBAQEBAQEBAQEBAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQEBAQEBAQEBAQEBAQEBAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQEBAQEBAQEBAQEBAQEBAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAQEBAQEBAQEBAQEBAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAAAAAAEBAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQEAAAAAAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQAAAAABAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAAAAAAEBAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAY5UBARL8TK8AAAAASUVORK5CYII=" + :added true + :last-online 0 + :public-key + "0x04d6e56a475cd35f512d6ce0bf76c2c2af435c85ff48c2b9bdefd129f620e051a436f50961eae5717b2a750e59c3f5b60647d927da46d0b8b11621640b5678fc24"} + {:last-updated 1547271764000 + :address "b267ff8336ac10b3a1986c04a70ff91fb03d0b78" + :name "rv" + :identicon + "data:image/png;base64iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAMAAAC7IEhfAAADAFBMVEX////VjNgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwYzy6AAABAHRSTlP//wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKmfXxgAABnNJREFUeNoBaAaX+QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQEBAQEBAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAQEBAQEBAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQEBAQEBAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQEBAQEBAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQEBAQEBAQEBAQEBAQEBAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQEBAQEBAQEBAQEBAQEBAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAQEBAQEBAQEBAQEBAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQEBAQEBAQEBAQEBAQEBAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQEBAQEBAAAAAAEBAQEBAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQEBAQEAAAAAAQEBAQEBAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAQEBAQAAAAABAQEBAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQEBAQEBAAAAAAEBAQEBAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA6IYA4bRtf+EAAAAASUVORK5CYII=" + :added true + :last-online 0 + :public-key + "0x043ae31038ff45a31b096a91d3f8290e079366fbbae76a00fbbd349cd0e5b8d7598965d206772ec4504f68908649a08383cdc51a52cdae5e9ccc744ace4d37020f"}]) (def chats [{:updated-at nil @@ -39,7 +43,8 @@ {:updated-at nil :tags #{} :color "#d37ef4" - :contacts #{"0x043ae31038ff45a31b096a91d3f8290e079366fbbae76a00fbbd349cd0e5b8d7598965d206772ec4504f68908649a08383cdc51a52cdae5e9ccc744ace4d37020f"} + :contacts + #{"0x043ae31038ff45a31b096a91d3f8290e079366fbbae76a00fbbd349cd0e5b8d7598965d206772ec4504f68908649a08383cdc51a52cdae5e9ccc744ace4d37020f"} :last-clock-value 154727176928001 :admins #{} :members-joined #{} @@ -47,18 +52,22 @@ :membership-updates () :unviewed-messages-count 0 :last-message-content-type "text/plain" - :last-message-content {:chat-id "0x04173f7cdea0076a7998abb674cc79fe61337c42db77043c01d5b0f3e3ac1e5a45bca0c93bb9f3c3d38b7cc9a7337cd64f9f9b2114fe4bbdfe1ae2633ba14d8c9c" - :text "Hey"} + :last-message-content + {:chat-id + "0x04173f7cdea0076a7998abb674cc79fe61337c42db77043c01d5b0f3e3ac1e5a45bca0c93bb9f3c3d38b7cc9a7337cd64f9f9b2114fe4bbdfe1ae2633ba14d8c9c" + :text "Hey"} :debug? false :group-chat false :public? false - :chat-id "0x043ae31038ff45a31b096a91d3f8290e079366fbbae76a00fbbd349cd0e5b8d7598965d206772ec4504f68908649a08383cdc51a52cdae5e9ccc744ace4d37020f" + :chat-id + "0x043ae31038ff45a31b096a91d3f8290e079366fbbae76a00fbbd349cd0e5b8d7598965d206772ec4504f68908649a08383cdc51a52cdae5e9ccc744ace4d37020f" :timestamp 1547271770816 :deleted-at-clock-value nil} {:updated-at nil :tags #{} :color "#7cda00" - :contacts #{"0x04d6e56a475cd35f512d6ce0bf76c2c2af435c85ff48c2b9bdefd129f620e051a436f50961eae5717b2a750e59c3f5b60647d927da46d0b8b11621640b5678fc24"} + :contacts + #{"0x04d6e56a475cd35f512d6ce0bf76c2c2af435c85ff48c2b9bdefd129f620e051a436f50961eae5717b2a750e59c3f5b60647d927da46d0b8b11621640b5678fc24"} :last-clock-value 154718689430301 :admins #{} :members-joined #{} @@ -66,34 +75,39 @@ :membership-updates () :unviewed-messages-count 0 :last-message-content-type "text/plain" - :last-message-content {:chat-id "0x04173f7cdea0076a7998abb674cc79fe61337c42db77043c01d5b0f3e3ac1e5a45bca0c93bb9f3c3d38b7cc9a7337cd64f9f9b2114fe4bbdfe1ae2633ba14d8c9c" - :text "Djndjd"} + :last-message-content + {:chat-id + "0x04173f7cdea0076a7998abb674cc79fe61337c42db77043c01d5b0f3e3ac1e5a45bca0c93bb9f3c3d38b7cc9a7337cd64f9f9b2114fe4bbdfe1ae2633ba14d8c9c" + :text "Djndjd"} :debug? false :group-chat false :public? false - :chat-id "0x04d6e56a475cd35f512d6ce0bf76c2c2af435c85ff48c2b9bdefd129f620e051a436f50961eae5717b2a750e59c3f5b60647d927da46d0b8b11621640b5678fc24" + :chat-id + "0x04d6e56a475cd35f512d6ce0bf76c2c2af435c85ff48c2b9bdefd129f620e051a436f50961eae5717b2a750e59c3f5b60647d927da46d0b8b11621640b5678fc24" :timestamp 1547186895328 :deleted-at-clock-value nil}]) (def multiaccount - {:last-updated 0 - :address "7540c34d6c4082391f12468580a9a4e0724c6755" - :mnemonic "tumble gorilla neglect dumb budget involve tennis ocean diary eagle lady ring" - :custom-bootnodes {} - :signing-phrase "bull exam weed" - :signed-up? true - :name "name" - :last-request nil - :wallet/visible-tokens {:mainnet #{:SNT} - :goerli #{:STT} - :xdai #{}} - :preview-privacy? true - :fleet :eth.prod - :identicon "data:image/png;base64iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAMAAAC7IEhfAAADAFBMVEX////YsYwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABPGFwxAAABAHRSTlP//wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKmfXxgAABnNJREFUeNoBaAaX+QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQEAAAAAAQEBAQAAAAABAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQAAAAABAQEBAAAAAAEBAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAAAAAAEBAQEAAAAAAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQEAAAAAAQEBAQAAAAABAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQAAAAABAQEBAAAAAAEBAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAAAAAAEBAQEAAAAAAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQEAAAAAAQEBAQAAAAABAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQAAAAABAQEBAAAAAAEBAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAAAAAAEBAQEAAAAAAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQEAAAAAAQEBAQAAAAABAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQAAAAABAQEBAAAAAAEBAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAAAAAAEBAQEAAAAAAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQEBAQEBAAAAAAEBAQEBAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQEBAQEAAAAAAQEBAQEBAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAQEBAQAAAAABAQEBAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQEBAQEBAAAAAAEBAQEBAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAloYA4a9rBHIAAAAASUVORK5CYII=" - :wallet-set-up-passed? false - :public-key "0x04173f7cdea0076a7998abb674cc79fe61337c42db77043c01d5b0f3e3ac1e5a45bca0c93bb9f3c3d38b7cc9a7337cd64f9f9b2114fe4bbdfe1ae2633ba14d8c9c" - :keycard-key-uid nil - :installation-id "618ec020-13c8-5505-8aa6-9c5444317e7f"}) + {:last-updated 0 + :address "7540c34d6c4082391f12468580a9a4e0724c6755" + :mnemonic "tumble gorilla neglect dumb budget involve tennis ocean diary eagle lady ring" + :custom-bootnodes {} + :signing-phrase "bull exam weed" + :signed-up? true + :name "name" + :last-request nil + :wallet/visible-tokens {:mainnet #{:SNT} + :goerli #{:STT} + :xdai #{}} + :preview-privacy? true + :fleet :eth.prod + :identicon + "data:image/png;base64iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAMAAAC7IEhfAAADAFBMVEX////YsYwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABPGFwxAAABAHRSTlP//wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKmfXxgAABnNJREFUeNoBaAaX+QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQEAAAAAAQEBAQAAAAABAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQAAAAABAQEBAAAAAAEBAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAAAAAAEBAQEAAAAAAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQEAAAAAAQEBAQAAAAABAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQAAAAABAQEBAAAAAAEBAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAAAAAAEBAQEAAAAAAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQEAAAAAAQEBAQAAAAABAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQAAAAABAQEBAAAAAAEBAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAAAAAAEBAQEAAAAAAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQEAAAAAAQEBAQAAAAABAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQAAAAABAQEBAAAAAAEBAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAAAAAAEBAQEAAAAAAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQEBAQEBAAAAAAEBAQEBAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQEBAQEAAAAAAQEBAQEBAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAQEBAQAAAAABAQEBAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQEBAQEBAAAAAAEBAQEBAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAloYA4a9rBHIAAAAASUVORK5CYII=" + :wallet-set-up-passed? false + :public-key + "0x04173f7cdea0076a7998abb674cc79fe61337c42db77043c01d5b0f3e3ac1e5a45bca0c93bb9f3c3d38b7cc9a7337cd64f9f9b2114fe4bbdfe1ae2633ba14d8c9c" + :keycard-key-uid nil + :installation-id "618ec020-13c8-5505-8aa6-9c5444317e7f"}) (def multiaccounts {"address" multiaccount}) @@ -102,11 +116,12 @@ (def transport {"0x04d6e56a475cd35f512d6ce0bf76c2c2af435c85ff48c2b9bdefd129f620e051a436f50961eae5717b2a750e59c3f5b60647d927da46d0b8b11621640b5678fc24" - {:resend? nil} + {:resend? nil} "0x043ae31038ff45a31b096a91d3f8290e079366fbbae76a00fbbd349cd0e5b8d7598965d206772ec4504f68908649a08383cdc51a52cdae5e9ccc744ace4d37020f" - {:resend? nil} + {:resend? nil} "status" - {:resend? nil}}) + {:resend? nil}}) -(def topics {"0xf8946aac" {:chat-ids #{:discovery-topic} - :last-request 1547319670}}) +(def topics + {"0xf8946aac" {:chat-ids #{:discovery-topic} + :last-request 1547319670}}) diff --git a/src/status_im/multiaccounts/login/flow_test.cljs b/src/status_im/multiaccounts/login/flow_test.cljs index 97e08abcf4..cc40697063 100644 --- a/src/status_im/multiaccounts/login/flow_test.cljs +++ b/src/status_im/multiaccounts/login/flow_test.cljs @@ -5,31 +5,32 @@ (:require [cljs.test :refer-macros [deftest is testing]] [status-im.ethereum.core :as ethereum] [status-im.ethereum.json-rpc :as json-rpc] - [status-im.multiaccounts.login.data-test :as data] - [status-im.multiaccounts.login.core :as login.core])) + [status-im.multiaccounts.login.core :as login.core] + [status-im.multiaccounts.login.data-test :as data])) (deftest on-password-input-submitted (testing - "handling :multiaccounts.login.ui/password-input-submitted event" - (let [cofx {:db {:multiaccounts/login {:key-uid "key-uid" - :password "password" - :name "user" + "handling :multiaccounts.login.ui/password-input-submitted event" + (let [cofx {:db {:multiaccounts/login {:key-uid "key-uid" + :password "password" + :name "user" :identicon "photo"}}} - efx (login.core/login cofx)] + efx (login.core/login cofx)] (testing "Change multiaccount." (is (= (::login.core/login efx) - ["key-uid" "{\"name\":\"user\",\"key-uid\":\"key-uid\",\"identicon\":\"photo\"}" (ethereum/sha3 "password")]))) + ["key-uid" "{\"name\":\"user\",\"key-uid\":\"key-uid\",\"identicon\":\"photo\"}" + (ethereum/sha3 "password")]))) (testing "start activity indicator" (is (= (get-in efx [:db :multiaccounts/login :processing]) true)))))) (deftest login-success (testing ":accounts.login.callback/login-success event received." - (let [db {:multiaccounts/login {:address "address" - :password "password"} - :multiaccount data/multiaccount} - cofx {:db db} - efx (login.core/multiaccount-login-success cofx) - json-rpc (into #{} (map :method (::json-rpc/call efx)))] + (let [db {:multiaccounts/login {:address "address" + :password "password"} + :multiaccount data/multiaccount} + cofx {:db db} + efx (login.core/multiaccount-login-success cofx) + json-rpc (into #{} (map :method (::json-rpc/call efx)))] ;; TODO: Account is now cleared only after all sign in fx are executed. ;; (testing ":accounts/login cleared." ;; (is (not (contains? new-db :multiaccounts/login)))) @@ -39,14 +40,20 @@ ;;TODO re-enable when keycard is fixed #_(deftest login (testing "login with keycard" - (let [wpk "c56c7ac797c27b3790ce02c2459e9957c5d20d7a2c55320535526ce9e4dcbbef" - epk "04f43da85ff1c333f3e7277b9ac4df92c9120fbb251f1dede7d41286e8c055acfeb845f6d2654821afca25da119daff9043530b296ee0e28e202ba92ec5842d617" - db {:keycard {:multiaccount {:encryption-public-key epk - :whisper-private-key wpk - :wallet-address "83278851e290d2488b6add2a257259f5741a3b7d" - :whisper-public-key "0x04491c1272149d7fa668afa45968c9914c0661641ace7dbcbc585c15070257840a0b4b1f71ce66c2147e281e1a44d6231b4731a26f6cc0a49e9616bbc7fc2f1a93" - :whisper-address "b8bec30855ff20c2ddab32282e2b2c8c8baca70d"}}} - result (login.core/login {:db db})] + (let + [wpk "c56c7ac797c27b3790ce02c2459e9957c5d20d7a2c55320535526ce9e4dcbbef" + epk + "04f43da85ff1c333f3e7277b9ac4df92c9120fbb251f1dede7d41286e8c055acfeb845f6d2654821afca25da119daff9043530b296ee0e28e202ba92ec5842d617" + db + {:keycard + {:multiaccount + {:encryption-public-key epk + :whisper-private-key wpk + :wallet-address "83278851e290d2488b6add2a257259f5741a3b7d" + :whisper-public-key + "0x04491c1272149d7fa668afa45968c9914c0661641ace7dbcbc585c15070257840a0b4b1f71ce66c2147e281e1a44d6231b4731a26f6cc0a49e9616bbc7fc2f1a93" + :whisper-address "b8bec30855ff20c2ddab32282e2b2c8c8baca70d"}}} + result (login.core/login {:db db})] (is (= (-> result (get :keycard/login-with-keycard) keys count) 3)) (is (= (get-in result [:keycard/login-with-keycard :whisper-private-key wpk]))) diff --git a/src/status_im/multiaccounts/logout/core.cljs b/src/status_im/multiaccounts/logout/core.cljs index 882dd1b2e5..92f1198d30 100644 --- a/src/status_im/multiaccounts/logout/core.cljs +++ b/src/status_im/multiaccounts/logout/core.cljs @@ -1,18 +1,18 @@ (ns status-im.multiaccounts.logout.core (:require [re-frame.core :as re-frame] [status-im.i18n.i18n :as i18n] - [status-im.native-module.core :as status] - [status-im.utils.fx :as fx] [status-im.multiaccounts.core :as multiaccounts] - [status-im.utils.keychain.core :as keychain] + [status-im.native-module.core :as status] [status-im.notifications.core :as notifications] + [status-im.utils.fx :as fx] + [status-im.utils.keychain.core :as keychain] [status-im.wallet.core :as wallet] [status-im2.setup.events :as init])) (fx/defn logout-method {:events [::logout-method]} [{:keys [db] :as cofx} {:keys [auth-method logout?]}] - (let [key-uid (get-in db [:multiaccount :key-uid])] + (let [key-uid (get-in db [:multiaccount :key-uid])] (fx/merge cofx {:init-root-fx :progress :chat.ui/clear-inputs nil @@ -22,17 +22,19 @@ ::logout nil ::multiaccounts/webview-debug-changed false :keychain/clear-user-password key-uid - :setup/open-multiaccounts #(re-frame/dispatch [:setup/initialize-multiaccounts % {:logout? logout?}])} + :setup/open-multiaccounts #(re-frame/dispatch [:setup/initialize-multiaccounts + % {:logout? logout?}])} (keychain/save-auth-method key-uid auth-method) (wallet/clear-timeouts) (init/initialize-app-db)))) (fx/defn logout - {:events [:logout :multiaccounts.logout.ui/logout-confirmed :multiaccounts.update.callback/save-settings-success]} + {:events [:logout :multiaccounts.logout.ui/logout-confirmed + :multiaccounts.update.callback/save-settings-success]} [cofx] ;; we need to disable notifications before starting the logout process (fx/merge cofx - {:dispatch [:wallet-connect-legacy/clean-up-sessions] + {:dispatch [:wallet-connect-legacy/clean-up-sessions] :dispatch-later [{:ms 100 :dispatch [::logout-method {:auth-method keychain/auth-method-none diff --git a/src/status_im/multiaccounts/model.cljs b/src/status_im/multiaccounts/model.cljs index c91b1c01e3..8ab31064f2 100644 --- a/src/status_im/multiaccounts/model.cljs +++ b/src/status_im/multiaccounts/model.cljs @@ -1,10 +1,12 @@ (ns status-im.multiaccounts.model) -(defn logged-in? [cofx] +(defn logged-in? + [cofx] (boolean (get-in cofx [:db :multiaccount]))) -(defn credentials [cofx] +(defn credentials + [cofx] (select-keys (get-in cofx [:db :multiaccounts/login]) [:key-uid :password :save-password?])) (defn current-public-key diff --git a/src/status_im/multiaccounts/recover/core.cljs b/src/status_im/multiaccounts/recover/core.cljs index d20ead6322..917cebb1dd 100644 --- a/src/status_im/multiaccounts/recover/core.cljs +++ b/src/status_im/multiaccounts/recover/core.cljs @@ -1,29 +1,30 @@ (ns status-im.multiaccounts.recover.core (:require [clojure.string :as string] [re-frame.core :as re-frame] + [status-im.bottom-sheet.core :as bottom-sheet] [status-im.constants :as constants] [status-im.ethereum.core :as ethereum] [status-im.ethereum.mnemonic :as mnemonic] - [status-im.keycard.nfc :as nfc] [status-im.i18n.i18n :as i18n] + [status-im.keycard.nfc :as nfc] [status-im.multiaccounts.core :as multiaccounts] [status-im.multiaccounts.create.core :as multiaccounts.create] [status-im.native-module.core :as status] [status-im.popover.core :as popover] - [status-im2.navigation.events :as navigation] [status-im.utils.fx :as fx] - [utils.security.core :as security] [status-im.utils.types :as types] [status-im.utils.utils :as utils] - [status-im.bottom-sheet.core :as bottom-sheet] - [taoensso.timbre :as log])) + [status-im2.navigation.events :as navigation] + [taoensso.timbre :as log] + [utils.security.core :as security])) (defn existing-account? [multiaccounts key-uid] {:pre [(not (nil? key-uid))]} (contains? multiaccounts key-uid)) -(defn check-phrase-warnings [recovery-phrase] +(defn check-phrase-warnings + [recovery-phrase] (cond (string/blank? recovery-phrase) :t/required-field)) (fx/defn set-phrase @@ -31,16 +32,18 @@ [{:keys [db]} masked-recovery-phrase] (let [recovery-phrase (security/safe-unmask-data masked-recovery-phrase)] (fx/merge - {:db (update db :intro-wizard assoc - :passphrase (string/lower-case recovery-phrase) - :passphrase-error nil + {:db (update db + :intro-wizard assoc + :passphrase (string/lower-case recovery-phrase) + :passphrase-error nil :next-button-disabled? (or (empty? recovery-phrase) (not (mnemonic/valid-length? recovery-phrase))))}))) (fx/defn validate-phrase-for-warnings [{:keys [db]}] (let [recovery-phrase (get-in db [:intro-wizard :passphrase])] - {:db (update db :intro-wizard assoc + {:db (update db + :intro-wizard assoc :passphrase-error (check-phrase-warnings recovery-phrase))})) (fx/defn on-store-multiaccount-success @@ -54,15 +57,18 @@ :content (i18n/label :t/multiaccount-exists-title) :on-dismiss #(re-frame/dispatch [:pop-to-root-tab :multiaccounts])}} (let [{:keys [key-uid] :as multiaccount} (get-in db [:intro-wizard :root-key]) - keycard-multiaccount? (boolean (get-in db [:multiaccounts/multiaccounts key-uid :keycard-pairing]))] + keycard-multiaccount? (boolean (get-in db + [:multiaccounts/multiaccounts key-uid + :keycard-pairing]))] (if keycard-multiaccount? ;; trying to recover multiaccount created with keycard - {:db (-> db - (update :intro-wizard assoc - :processing? false - :passphrase-error :recover-keycard-multiaccount-not-supported) - (update :intro-wizard dissoc - :passphrase-valid?))} + {:db (-> db + (update :intro-wizard assoc + :processing? false + :passphrase-error :recover-keycard-multiaccount-not-supported) + (update :intro-wizard + dissoc + :passphrase-valid?))} (let [multiaccount (assoc multiaccount :derived (get-in db [:intro-wizard :derived]))] (multiaccounts.create/on-multiaccount-created cofx multiaccount @@ -72,11 +78,11 @@ (fx/defn store-multiaccount {:events [::recover-multiaccount-confirmed]} [{:keys [db]} password] - (let [{:keys [root-key]} (:intro-wizard db) + (let [{:keys [root-key]} (:intro-wizard db) {:keys [id key-uid]} root-key - callback #(re-frame/dispatch [::store-multiaccount-success % password]) - hashed-password (ethereum/sha3 (security/safe-unmask-data password))] - {:db (assoc-in db [:intro-wizard :processing?] true) + callback #(re-frame/dispatch [::store-multiaccount-success % password]) + hashed-password (ethereum/sha3 (security/safe-unmask-data password))] + {:db (assoc-in db [:intro-wizard :processing?] true) ::multiaccounts.create/store-multiaccount [id key-uid hashed-password callback]})) (re-frame/reg-fx @@ -99,14 +105,15 @@ (fn [result] (let [derived-data (multiaccounts.create/normalize-derived-data-keys (types/json->clj result)) - public-key (get-in derived-data [constants/path-whisper-keyword :public-key])] + public-key (get-in derived-data [constants/path-whisper-keyword :public-key])] (status/gfycat-identicon-async public-key (fn [name identicon] (let [derived-data-extended (update derived-data constants/path-whisper-keyword - merge {:name name :identicon identicon})] + merge + {:name name :identicon identicon})] (re-frame/dispatch [success-event root-data derived-data-extended])))))))))))) (fx/defn show-existing-multiaccount-alert @@ -127,10 +134,12 @@ (let [multiaccounts (:multiaccounts/multiaccounts db)] (fx/merge cofx - {:db (update db :intro-wizard - assoc :root-key root-data - :derived derived-data - :step :recovery-success)} + {:db (update db + :intro-wizard + assoc + :root-key root-data + :derived derived-data + :step :recovery-success)} (when (existing-account? multiaccounts key-uid) (show-existing-multiaccount-alert key-uid)) (navigation/navigate-to-cofx :recover-multiaccount-success nil)))) @@ -169,7 +178,9 @@ {:events [:multiaccounts.recover/enter-phrase-next-pressed]} [{:keys [db] :as cofx}] (let [{:keys [passphrase]} (:intro-wizard db)] - {::multiaccounts/validate-mnemonic [passphrase #(re-frame/dispatch [:multiaccounts.recover/phrase-validated %])]})) + {::multiaccounts/validate-mnemonic [passphrase + #(re-frame/dispatch [:multiaccounts.recover/phrase-validated + %])]})) (fx/defn continue-to-import-mnemonic {:events [::continue-pressed]} @@ -187,13 +198,15 @@ (let [step (get-in db [:intro-wizard :step])] (if (= step :enter-phrase) {:db (dissoc db :intro-wizard)} - {:db (update db :intro-wizard assoc :step + {:db (update db + :intro-wizard assoc + :step (case step - :recovery-success :enter-phrase + :recovery-success :enter-phrase :select-key-storage :recovery-success - :create-code :select-key-storage) + :create-code :select-key-storage) :confirm-failure? false - :key-code nil)}))) + :key-code nil)}))) (fx/defn cancel-pressed {:events [:multiaccounts.recover/cancel-pressed]} @@ -227,15 +240,17 @@ {:events [:multiaccounts.recover/re-encrypt-pressed]} [{:keys [db] :as cofx}] (fx/merge cofx - {:db (update db :intro-wizard - assoc :step :select-key-storage + {:db (update db + :intro-wizard + assoc + :step :select-key-storage :selected-storage-type :default)} (if (nfc/nfc-supported?) (navigation/navigate-to-cofx :select-key-storage nil) (select-storage-next-pressed)))) (fx/defn confirm-password-next-button-pressed - {:events [:multiaccounts.recover/enter-password-next-pressed] + {:events [:multiaccounts.recover/enter-password-next-pressed] :interceptors [(re-frame/inject-cofx :random-guid-generator)]} [cofx key-code] (store-multiaccount cofx key-code)) @@ -243,8 +258,9 @@ (fx/defn count-words [{:keys [db]}] (let [passphrase (get-in db [:intro-wizard :passphrase])] - {:db (assoc-in db [:intro-wizard :passphrase-word-count] - (mnemonic/words-count passphrase))})) + {:db (assoc-in db + [:intro-wizard :passphrase-word-count] + (mnemonic/words-count passphrase))})) (fx/defn run-validation [{:keys [db] :as cofx}] @@ -264,6 +280,7 @@ (fx/defn enter-passphrase-input-changed {:events [:multiaccounts.recover/enter-passphrase-input-changed]} [{:keys [db]} masked-passphrase] - {:db (update db :intro-wizard assoc - :password masked-passphrase + {:db (update db + :intro-wizard assoc + :password masked-passphrase :password-error nil)}) diff --git a/src/status_im/multiaccounts/recover/core_test.cljs b/src/status_im/multiaccounts/recover/core_test.cljs index e4d6977858..6573bd631b 100644 --- a/src/status_im/multiaccounts/recover/core_test.cljs +++ b/src/status_im/multiaccounts/recover/core_test.cljs @@ -1,7 +1,7 @@ (ns status-im.multiaccounts.recover.core-test (:require [cljs.test :refer-macros [deftest is testing]] - [status-im.multiaccounts.recover.core :as models] [status-im.multiaccounts.create.core :as multiaccounts.create] + [status-im.multiaccounts.recover.core :as models] [utils.security.core :as security])) ;;;; helpers @@ -13,31 +13,61 @@ ;;;; handlers (deftest set-phrase - (is (= {:db {:intro-wizard {:passphrase "game buzz method pretty olympic fat quit display velvet unveil marine crater" - :passphrase-error nil - :next-button-disabled? false}}} - (models/set-phrase {:db {}} (security/mask-data "game buzz method pretty olympic fat quit display velvet unveil marine crater")))) - (is (= {:db {:intro-wizard {:passphrase "game buzz method pretty olympic fat quit display velvet unveil marine crater" - :passphrase-error nil - :next-button-disabled? false}}} - (models/set-phrase {:db {}} (security/mask-data "Game buzz method pretty Olympic fat quit DISPLAY velvet unveil marine crater")))) - (is (= {:db {:intro-wizard {:passphrase "game buzz method pretty zeus fat quit display velvet unveil marine crater" - :passphrase-error nil - :next-button-disabled? false}}} - (models/set-phrase {:db {}} (security/mask-data "game buzz method pretty zeus fat quit display velvet unveil marine crater")))) - (is (= {:db {:intro-wizard {:passphrase " game\t buzz method pretty olympic fat quit\t display velvet unveil marine crater " - :passphrase-error nil - :next-button-disabled? false}}} - (models/set-phrase {:db {}} (security/mask-data " game\t buzz method pretty olympic fat quit\t display velvet unveil marine crater ")))) - (is (= {:db {:intro-wizard {:passphrase "game buzz method pretty 1234 fat quit display velvet unveil marine crater" - :passphrase-error nil - :next-button-disabled? false}}} - (models/set-phrase {:db {}} (security/mask-data "game buzz method pretty 1234 fat quit display velvet unveil marine crater"))))) + (is + (= {:db {:intro-wizard + {:passphrase + "game buzz method pretty olympic fat quit display velvet unveil marine crater" + :passphrase-error nil + :next-button-disabled? false}}} + (models/set-phrase + {:db {}} + (security/mask-data + "game buzz method pretty olympic fat quit display velvet unveil marine crater")))) + (is + (= {:db {:intro-wizard + {:passphrase + "game buzz method pretty olympic fat quit display velvet unveil marine crater" + :passphrase-error nil + :next-button-disabled? false}}} + (models/set-phrase + {:db {}} + (security/mask-data + "Game buzz method pretty Olympic fat quit DISPLAY velvet unveil marine crater")))) + (is + (= {:db {:intro-wizard {:passphrase + "game buzz method pretty zeus fat quit display velvet unveil marine crater" + :passphrase-error nil + :next-button-disabled? false}}} + (models/set-phrase {:db {}} + (security/mask-data + "game buzz method pretty zeus fat quit display velvet unveil marine crater")))) + (is + (= + {:db {:intro-wizard + {:passphrase + " game\t buzz method pretty olympic fat quit\t display velvet unveil marine crater " + :passphrase-error nil + :next-button-disabled? false}}} + (models/set-phrase + {:db {}} + (security/mask-data + " game\t buzz method pretty olympic fat quit\t display velvet unveil marine crater ")))) + (is + (= {:db {:intro-wizard {:passphrase + "game buzz method pretty 1234 fat quit display velvet unveil marine crater" + :passphrase-error nil + :next-button-disabled? false}}} + (models/set-phrase + {:db {}} + (security/mask-data + "game buzz method pretty 1234 fat quit display velvet unveil marine crater"))))) (deftest store-multiaccount - (let [new-cofx (models/store-multiaccount {:db {:intro-wizard - {:passphrase "game buzz method pretty zeus fat quit display velvet unveil marine crater"}}} - (security/mask-data "thisisapaswoord"))] + (let [new-cofx (models/store-multiaccount + {:db {:intro-wizard + {:passphrase + "game buzz method pretty zeus fat quit display velvet unveil marine crater"}}} + (security/mask-data "thisisapaswoord"))] (is (::multiaccounts.create/store-multiaccount new-cofx)))) (deftest on-import-multiaccount-success diff --git a/src/status_im/multiaccounts/reset_password/core.cljs b/src/status_im/multiaccounts/reset_password/core.cljs index 70bd25d87a..2e0713b5ec 100644 --- a/src/status_im/multiaccounts/reset_password/core.cljs +++ b/src/status_im/multiaccounts/reset_password/core.cljs @@ -1,23 +1,23 @@ (ns status-im.multiaccounts.reset-password.core - (:require [re-frame.core :as re-frame] - [status-im.utils.fx :as fx] - [status-im.utils.types :as types] - [clojure.string :as string] - [utils.security.core :as security] - [status-im.utils.keychain.core :as keychain] - [status-im.popover.core :as popover] + (:require [clojure.string :as string] + [re-frame.core :as re-frame] + [status-im.ethereum.core :as ethereum] [status-im.native-module.core :as status] - [status-im.ethereum.core :as ethereum])) + [status-im.popover.core :as popover] + [status-im.utils.fx :as fx] + [status-im.utils.keychain.core :as keychain] + [status-im.utils.types :as types] + [utils.security.core :as security])) (fx/defn on-input-change {:events [::handle-input-change]} [{:keys [db]} input-id value] (let [new-password (get-in db [:multiaccount/reset-password-form-vals :new-password]) - error (when (and (= input-id :confirm-new-password) - (pos? (count new-password)) - (pos? (count value)) - (not= value new-password)) - :t/password-mismatch)] + error (when (and (= input-id :confirm-new-password) + (pos? (count new-password)) + (pos? (count value)) + (not= value new-password)) + :t/password-mismatch)] {:db (-> db (assoc-in [:multiaccount/reset-password-form-vals input-id] value) (assoc-in [:multiaccount/reset-password-errors input-id] error))})) @@ -49,7 +49,8 @@ (when (= auth-method keychain/auth-method-biometric) (keychain/save-user-password key-uid new-password))))) -(defn change-db-password-cb [res] +(defn change-db-password-cb + [res] (let [{:keys [error]} (types/json->clj res)] (if (not (string/blank? error)) (re-frame/dispatch [::password-reset-error error]) @@ -70,12 +71,14 @@ (let [{:keys [key-uid name]} (:multiaccount db)] (fx/merge cofx {::change-db-password [key-uid form-vals] - :db (assoc db - :multiaccount/resetting-password? true)} + :db (assoc db + :multiaccount/resetting-password? + true)} (popover/show-popover {:view :password-reset-popover :prevent-closing? true})))) -(defn handle-verification [form-vals result] +(defn handle-verification + [form-vals result] (let [{:keys [error]} (types/json->clj result)] (if (not (string/blank? error)) (re-frame/dispatch [::handle-verification-error :t/wrong-password]) @@ -83,9 +86,10 @@ (re-frame/reg-fx ::validate-current-password-and-reset - (fn [{:keys [address current-password] :as form-vals}] + (fn [{:keys [address current-password] :as form-vals}] (let [hashed-pass (ethereum/sha3 (security/safe-unmask-data current-password))] - (status/verify address hashed-pass + (status/verify address + hashed-pass (partial handle-verification form-vals))))) (fx/defn reset diff --git a/src/status_im/multiaccounts/update/core.cljs b/src/status_im/multiaccounts/update/core.cljs index 3154662880..af5ece88cd 100644 --- a/src/status_im/multiaccounts/update/core.cljs +++ b/src/status_im/multiaccounts/update/core.cljs @@ -5,11 +5,12 @@ [status-im.utils.types :as types] [taoensso.timbre :as log])) -(fx/defn send-multiaccount-update [{:keys [db] :as cofx}] - (let [multiaccount (:multiaccount db) +(fx/defn send-multiaccount-update + [{:keys [db] :as cofx}] + (let [multiaccount (:multiaccount db) {:keys [name preferred-name address]} multiaccount] - {::json-rpc/call [{:method "wakuext_sendContactUpdates" - :params [(or preferred-name name) ""] + {::json-rpc/call [{:method "wakuext_sendContactUpdates" + :params [(or preferred-name name) ""] :on-success #(log/debug "sent contact update")}]})) (fx/defn multiaccount-update @@ -22,14 +23,16 @@ (if (empty? current-multiaccount) ;; NOTE: this should never happen, but if it does this is a critical error ;; and it is better to crash than risk having an unstable state - (throw (js/Error. "Please shake the phone to report this error and restart the app. multiaccount is currently empty, which means something went wrong when trying to update it with")) + (throw + (js/Error. + "Please shake the phone to report this error and restart the app. multiaccount is currently empty, which means something went wrong when trying to update it with")) (fx/merge cofx - {:db (if setting-value - (assoc-in db [:multiaccount setting] setting-value) - (update db :multiaccount dissoc setting)) + {:db (if setting-value + (assoc-in db [:multiaccount setting] setting-value) + (update db :multiaccount dissoc setting)) ::json-rpc/call - [{:method "settings_saveSetting" - :params [setting setting-value] + [{:method "settings_saveSetting" + :params [setting setting-value] :on-success on-success}]} (when (and (not dont-sync?) (#{:name :prefered-name} setting)) @@ -44,31 +47,40 @@ "Add 'url' parameter to stickers that are synchronized from other devices. It is not sent from aanother devices but we have it in our db." [synced-stickers stickers-from-db] - (mapv #(assoc % :url (->> (get stickers-from-db (or (:packID %) (:pack %))) - (:stickers) - (filter (fn [sticker-db] (= (:hash sticker-db) (:hash %)))) - (first) - (:url))) + (mapv #(assoc % + :url + (->> (get stickers-from-db (or (:packID %) (:pack %))) + (:stickers) + (filter (fn [sticker-db] (= (:hash sticker-db) (:hash %)))) + (first) + (:url))) synced-stickers)) (fx/defn optimistic [{:keys [db] :as cofx} setting setting-value] (let [current-multiaccount (:multiaccount db) - setting-value (if (= :currency setting) - (keyword setting-value) - setting-value) - db (case setting - :stickers/packs-pending - (let [packs-pending (keys (js->clj setting-value))] - (update db :stickers/packs-pending conj packs-pending)) - :stickers/packs-installed - (let [packs-installed-keys (keys (js->clj setting-value))] - (reduce #(assoc-in %1 [:stickers/packs %2 :status] constants/sticker-pack-status-installed) db packs-installed-keys)) - :stickers/recent-stickers - (let [recent-stickers-from-remote (augment-synchronized-recent-stickers (types/js->clj setting-value) (:stickers/packs db)) - merged (into recent-stickers-from-remote (:stickers/recent-stickers db))] - (assoc db :stickers/recent-stickers recent-stickers-from-remote)) - db)] + setting-value (if (= :currency setting) + (keyword setting-value) + setting-value) + db (case setting + :stickers/packs-pending + (let [packs-pending (keys (js->clj setting-value))] + (update db :stickers/packs-pending conj packs-pending)) + :stickers/packs-installed + (let [packs-installed-keys (keys (js->clj setting-value))] + (reduce #(assoc-in %1 + [:stickers/packs %2 :status] + constants/sticker-pack-status-installed) + db + packs-installed-keys)) + :stickers/recent-stickers + (let [recent-stickers-from-remote (augment-synchronized-recent-stickers + (types/js->clj setting-value) + (:stickers/packs db)) + merged (into recent-stickers-from-remote + (:stickers/recent-stickers db))] + (assoc db :stickers/recent-stickers recent-stickers-from-remote)) + db)] {:db (if setting-value (assoc-in db [:multiaccount setting] setting-value) (update db :multiaccount dissoc setting))})) diff --git a/src/status_im/multiaccounts/update/core_test.cljs b/src/status_im/multiaccounts/update/core_test.cljs index 3d989156b4..aa2e3b2728 100644 --- a/src/status_im/multiaccounts/update/core_test.cljs +++ b/src/status_im/multiaccounts/update/core_test.cljs @@ -6,16 +6,19 @@ (deftest test-multiaccount-update ;;TODO this test case actually shows that we are doing a needless rpc call when ;;there is no changes, but it is an edge case that shouldn't really happen - (let [efx (multiaccounts.update/multiaccount-update - {:db {:multiaccount {:not-empty "would throw an error if was empty"}}} - nil nil {}) + (let [efx (multiaccounts.update/multiaccount-update + {:db {:multiaccount {:not-empty "would throw an error if was empty"}}} + nil + nil + {}) json-rpc (into #{} (map :method (::json-rpc/call efx)))] (is (json-rpc "settings_saveSetting")) (is (= (get-in efx [:db :multiaccount]) {:not-empty "would throw an error if was empty"})))) (deftest test-clean-seed-phrase - (let [efx (multiaccounts.update/clean-seed-phrase - {:db {:multiaccount {:mnemonic "lalalala"}}} {}) + (let [efx (multiaccounts.update/clean-seed-phrase + {:db {:multiaccount {:mnemonic "lalalala"}}} + {}) json-rpc (into #{} (map :method (::json-rpc/call efx)))] (is (json-rpc "settings_saveSetting")) (is (nil? (get-in efx [:db :multiaccount :mnemonic]))))) diff --git a/src/status_im/native_module/core.cljs b/src/status_im/native_module/core.cljs index 0a2db99c23..a7bd2cc49a 100644 --- a/src/status_im/native_module/core.cljs +++ b/src/status_im/native_module/core.cljs @@ -1,29 +1,33 @@ (ns status-im.native-module.core - (:require [re-frame.core :as re-frame] + (:require ["react-native" :as react-native] + [re-frame.core :as re-frame] [status-im.utils.db :as utils.db] - [status-im.utils.react-native :as react-native-utils] [status-im.utils.platform :as platform] + [status-im.utils.react-native :as react-native-utils] [status-im.utils.types :as types] - [taoensso.timbre :as log] - ["react-native" :as react-native])) + [taoensso.timbre :as log])) -(defn status [] +(defn status + [] (when (exists? (.-NativeModules react-native)) (.-Status ^js (.-NativeModules react-native)))) (def adjust-resize 16) -(defn clear-web-data [] +(defn clear-web-data + [] (log/debug "[native-module] clear-web-data") (when (status) (.clearCookies ^js (status)) (.clearStorageAPIs ^js (status)))) -(defn init-keystore [key-uid callback] +(defn init-keystore + [key-uid callback] (log/debug "[native-module] init-keystore" key-uid) (.initKeystore ^js (status) key-uid callback)) -(defn open-accounts [callback] +(defn open-accounts + [callback] (log/debug "[native-module] open-accounts") (.openAccounts ^js (status) #(callback (types/json->clj %)))) @@ -39,12 +43,18 @@ "NOTE: beware, the password has to be sha3 hashed" [key-uid multiaccount-data hashed-password settings config accounts-data] (log/debug "[native-module] save-account-and-login" - "multiaccount-data" multiaccount-data) + "multiaccount-data" + multiaccount-data) (clear-web-data) (init-keystore key-uid #(.saveAccountAndLogin - ^js (status) multiaccount-data hashed-password settings config accounts-data))) + ^js (status) + multiaccount-data + hashed-password + settings + config + accounts-data))) (defn save-multiaccount-and-login-with-keycard "NOTE: chat-key is a whisper private key sent from keycard" @@ -53,7 +63,13 @@ (init-keystore key-uid #(.saveAccountAndLoginWithKeycard - ^js (status) multiaccount-data password settings config accounts-data chat-key))) + ^js (status) + multiaccount-data + password + settings + config + accounts-data + chat-key))) (defn login "NOTE: beware, the password has to be sha3 hashed" @@ -92,14 +108,16 @@ key-uid #(.importUnencryptedDatabase ^js (status) account-data hashed-password))) -(defn logout [] +(defn logout + [] (log/debug "[native-module] logout") (clear-web-data) (.logout ^js (status))) (defonce listener - (.addListener ^js react-native-utils/device-event-emitter "gethEvent" - #(re-frame/dispatch [:signals/signal-received (.-jsonEvent ^js %)]))) + (.addListener ^js react-native-utils/device-event-emitter + "gethEvent" + #(re-frame/dispatch [:signals/signal-received (.-jsonEvent ^js %)]))) (defn multiaccount-load-account "NOTE: beware, the password has to be sha3 hashed @@ -110,7 +128,7 @@ [address hashed-password callback] (log/debug "[native-module] multiaccount-load-account") (.multiAccountLoadAccount ^js (status) - (types/clj->json {:address address + (types/clj->json {:address address :password hashed-password}) callback)) @@ -132,7 +150,7 @@ (when (status) (.multiAccountDeriveAddresses ^js (status) (types/clj->json {:accountID account-id - :paths paths}) + :paths paths}) callback))) (defn multiaccount-store-account @@ -148,23 +166,24 @@ (when (status) (init-keystore key-uid - #(.multiAccountStoreAccount ^js (status) - (types/clj->json {:accountID account-id - :password hashed-password}) - callback)))) + #(.multiAccountStoreAccount ^js (status) + (types/clj->json {:accountID account-id + :password hashed-password}) + callback)))) (defn multiaccount-store-derived "NOTE: beware, the password has to be sha3 hashed" [account-id key-uid paths hashed-password callback] (log/debug "[native-module] multiaccount-store-derived" - "account-id" account-id) + "account-id" + account-id) (init-keystore key-uid - #(.multiAccountStoreDerived ^js (status) - (types/clj->json {:accountID account-id - :paths paths - :password hashed-password}) - callback))) + #(.multiAccountStoreDerived ^js (status) + (types/clj->json {:accountID account-id + :paths paths + :password hashed-password}) + callback))) (defn multiaccount-generate-and-derive-addresses "used to generate multiple multiaccounts for onboarding @@ -173,27 +192,27 @@ to store the key" [n mnemonic-length paths callback] (log/debug "[native-module] multiaccount-generate-and-derive-addresses") - (.multiAccountGenerateAndDeriveAddresses ^js (status) - (types/clj->json {:n n - :mnemonicPhraseLength mnemonic-length - :bip39Passphrase "" - :paths paths}) - callback)) + (.multiAccountGenerateAndDeriveAddresses ^js (status) + (types/clj->json {:n n + :mnemonicPhraseLength mnemonic-length + :bip39Passphrase "" + :paths paths}) + callback)) (defn multiaccount-import-mnemonic [mnemonic password callback] (log/debug "[native-module] multiaccount-import-mnemonic") - (.multiAccountImportMnemonic ^js (status) - (types/clj->json {:mnemonicPhrase mnemonic - ;;NOTE this is not the multiaccount password - :Bip39Passphrase password}) - callback)) + (.multiAccountImportMnemonic ^js (status) + (types/clj->json {:mnemonicPhrase mnemonic + ;;NOTE this is not the multiaccount password + :Bip39Passphrase password}) + callback)) (defn multiaccount-import-private-key [private-key callback] (log/debug "[native-module] multiaccount-import-private-key") (.multiAccountImportPrivateKey ^js (status) - (types/clj->json {:privateKey private-key}) + (types/clj->json {:privateKey private-key}) callback)) (defn verify @@ -216,15 +235,18 @@ key-uid #(.loginWithKeycard ^js (status) multiaccount-data password chat-key))) -(defn set-soft-input-mode [mode] +(defn set-soft-input-mode + [mode] (log/debug "[native-module] set-soft-input-mode") (.setSoftInputMode ^js (status) mode)) -(defn call-rpc [payload callback] +(defn call-rpc + [payload callback] (log/debug "[native-module] call-rpc") (.callRPC ^js (status) payload callback)) -(defn call-private-rpc [payload callback] +(defn call-private-rpc + [payload callback] (.callPrivateRPC ^js (status) payload callback)) (defn hash-transaction @@ -243,7 +265,7 @@ "Generates connection string form status-go for the purpose of local pairing on the sender end" [config-json callback] (log/info "[native-module] Fetching Connection String" - {:fn :get-connection-string-for-bootstrapping-another-device + {:fn :get-connection-string-for-bootstrapping-another-device :config-json config-json}) (.getConnectionStringForBootstrappingAnotherDevice ^js (status) config-json callback)) @@ -251,8 +273,8 @@ "Provides connection string to status-go for the purpose of local pairing on the receiver end" [connection-string config-json callback] (log/info "[native-module] Sending Connection String" - {:fn :input-connection-string-for-bootstrapping - :config-json config-json + {:fn :input-connection-string-for-bootstrapping + :config-json config-json :connection-string connection-string}) (.inputConnectionStringForBootstrapping ^js (status) connection-string config-json callback)) @@ -303,45 +325,55 @@ (log/debug "[native-module] sign-typed-data-v4") (.signTypedDataV4 ^js (status) data account hashed-password callback)) -(defn send-logs [dbJson js-logs callback] +(defn send-logs + [dbJson js-logs callback] (log/debug "[native-module] send-logs") (.sendLogs ^js (status) dbJson js-logs callback)) -(defn add-peer [enode on-result] +(defn add-peer + [enode on-result] (log/debug "[native-module] add-peer") (.addPeer ^js (status) enode on-result)) -(defn close-application [] +(defn close-application + [] (log/debug "[native-module] close-application") (.closeApplication ^js (status))) -(defn connection-change [type expensive?] +(defn connection-change + [type expensive?] (log/debug "[native-module] connection-change") (.connectionChange ^js (status) type (boolean expensive?))) -(defn app-state-change [state] +(defn app-state-change + [state] (log/debug "[native-module] app-state-change") (.appStateChange ^js (status) state)) -(defn stop-local-notifications [] +(defn stop-local-notifications + [] (log/debug "[native-module] stop-local-notifications") (.stopLocalNotifications ^js (status))) -(defn start-local-notifications [] +(defn start-local-notifications + [] (log/debug "[native-module] start-local-notifications") (.startLocalNotifications ^js (status))) -(defn set-blank-preview-flag [flag] +(defn set-blank-preview-flag + [flag] (log/debug "[native-module] set-blank-preview-flag") (.setBlankPreviewFlag ^js (status) flag)) -(defn is24Hour [] +(defn is24Hour + [] (log/debug "[native-module] is24Hour") ;;NOTE: we have to check for status module because of tests (when (status) (.-is24Hour ^js (status)))) -(defn get-device-model-info [] +(defn get-device-model-info + [] (log/debug "[native-module] get-device-model-info") ;;NOTE: we have to check for status module because of tests (when-let [^js status (status)] @@ -355,11 +387,13 @@ (log/debug "[native-module] extract-group-membership-signatures") (.extractGroupMembershipSignatures ^js (status) signature-pairs callback)) -(defn sign-group-membership [content callback] +(defn sign-group-membership + [content callback] (log/debug "[native-module] sign-group-membership") (.signGroupMembership ^js (status) content callback)) -(defn get-node-config [callback] +(defn get-node-config + [callback] (log/debug "[native-module] get-node-config") (.getNodeConfig ^js (status) callback)) @@ -368,11 +402,13 @@ (log/debug "[native-module] update-mailservers") (.updateMailservers ^js (status) enodes on-result)) -(defn toggle-webview-debug [on] +(defn toggle-webview-debug + [on] (log/debug "[native-module] toggle-webview-debug" on) (.toggleWebviewDebug ^js (status) on)) -(defn rooted-device? [callback] +(defn rooted-device? + [callback] (log/debug "[native-module] rooted-device?") (cond ;; we assume that iOS is safe by default @@ -387,7 +423,7 @@ (callback true)) ;; in unknown scenarios we also consider the device rooted to avoid degrading security - :else (callback true))) + :else (callback true))) (defn generate-gfycat "Generate a 3 words random name based on the user public-key, synchronously" @@ -421,7 +457,8 @@ (defn decode-parameters [bytes-string types] (log/debug "[native-module] decode-parameters") - (let [json-str (.decodeParameters ^js (status) (types/clj->json {:bytesString bytes-string :types types}))] + (let [json-str (.decodeParameters ^js (status) + (types/clj->json {:bytesString bytes-string :types types}))] (types/json->clj json-str))) (defn hex-to-number @@ -497,15 +534,18 @@ (log/debug "[native-module] delete-imported-key") (.deleteImportedKey ^js (status) key-uid address hashed-password callback)) -(defn activate-keep-awake [] +(defn activate-keep-awake + [] (log/debug "[native-module] activateKeepAwake") (.activateKeepAwake ^js (status))) -(defn deactivate-keep-awake [] +(defn deactivate-keep-awake + [] (log/debug "[native-module] deactivateKeepAwake") (.deactivateKeepAwake ^js (status))) -(defn reset-keyboard-input [input selection] +(defn reset-keyboard-input + [input selection] (log/debug "[native-module] resetKeyboardInput") (when platform/android? (.resetKeyboardInputCursor ^js (status) input selection))) diff --git a/src/status_im/native_module/core_test.cljs b/src/status_im/native_module/core_test.cljs index 3b4968418d..9d1f37ca56 100644 --- a/src/status_im/native_module/core_test.cljs +++ b/src/status_im/native_module/core_test.cljs @@ -4,4 +4,7 @@ (deftest identicon-test (testing "check if identicon test works" - (is (= "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAAjklEQVR4nOzXsQmAMBQGYRV3cUAdQwd0Gm2sJIWSBI6f+0oR8XjwSKYhhCE0htAYQmMITUzI/PXF49yv0vN12cYWP1L7/ZiJGEJjCE31xvm7bXptv5iJGEJjCE31WasVz1oPQ2gMoWlyuyvpfaN8i5mIITSG0BhCYwiNIeokZiKG0BhCYwiNITR3AAAA//+A3RtWaKqXgQAAAABJRU5ErkJggg==" (status/identicon "a"))))) + (is + (= + "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAAjklEQVR4nOzXsQmAMBQGYRV3cUAdQwd0Gm2sJIWSBI6f+0oR8XjwSKYhhCE0htAYQmMITUzI/PXF49yv0vN12cYWP1L7/ZiJGEJjCE31xvm7bXptv5iJGEJjCE31WasVz1oPQ2gMoWlyuyvpfaN8i5mIITSG0BhCYwiNIeokZiKG0BhCYwiNITR3AAAA//+A3RtWaKqXgQAAAABJRU5ErkJggg==" + (status/identicon "a"))))) diff --git a/src/status_im/navigation/core.cljs b/src/status_im/navigation/core.cljs index 1146d52048..d10d7179fe 100644 --- a/src/status_im/navigation/core.cljs +++ b/src/status_im/navigation/core.cljs @@ -1,31 +1,31 @@ (ns status-im.navigation.core - (:require - ["react-native" :as rn] - ["react-native-gesture-handler" :refer (gestureHandlerRootHOC)] - ["react-native-navigation" :refer (Navigation)] - [clojure.set :as clojure.set] - [quo.components.text-input :as quo.text-input] - [quo.design-system.colors :as quo.colors] - [re-frame.core :as re-frame] - [status-im.multiaccounts.login.core :as login-core] - [status-im.navigation.roots :as roots] - [status-im.navigation.state :as state] - [status-im.ui.components.icons.icons :as icons] - [status-im.ui.components.react :as react] - [status-im.ui.screens.views :as views] - [status-im.utils.fx :as fx] - [status-im.utils.platform :as platform] - [taoensso.timbre :as log])) + (:require ["react-native" :as rn] + ["react-native-gesture-handler" :refer (gestureHandlerRootHOC)] + ["react-native-navigation" :refer (Navigation)] + [clojure.set :as clojure.set] + [quo.components.text-input :as quo.text-input] + [quo.design-system.colors :as quo.colors] + [re-frame.core :as re-frame] + [status-im.multiaccounts.login.core :as login-core] + [status-im.navigation.roots :as roots] + [status-im.navigation.state :as state] + [status-im.ui.components.icons.icons :as icons] + [status-im.ui.components.react :as react] + [status-im.ui.screens.views :as views] + [status-im.utils.fx :as fx] + [status-im.utils.platform :as platform] + [taoensso.timbre :as log])) (def debug? ^boolean js/goog.DEBUG) (def splash-screen (-> rn .-NativeModules .-SplashScreen)) (defonce set-navigation-default-options - (.setDefaultOptions Navigation (clj->js {:layout {:orientation "portrait"}}))) + (.setDefaultOptions Navigation (clj->js {:layout {:orientation "portrait"}}))) ;; REGISTER COMPONENT (LAZY) -(defn reg-comp [key] +(defn reg-comp + [key] (log/debug "reg-comp" key) (if-let [comp (get views/components (keyword key))] (.registerComponent Navigation key (fn [] (views/component comp))) @@ -33,9 +33,10 @@ (.registerComponent Navigation key (fn [] (gestureHandlerRootHOC screen)) (fn [] screen))))) (defonce rset-lazy-reg - (.setLazyComponentRegistrator Navigation reg-comp)) + (.setLazyComponentRegistrator Navigation reg-comp)) -(defn dismiss-all-modals [] +(defn dismiss-all-modals + [] (log/debug "dissmiss-all-modals") (when @state/curr-modal (reset! state/curr-modal false) @@ -45,21 +46,24 @@ (reset! state/modals []))) ;; PUSH SCREEN -(defn navigate [comp] +(defn navigate + [comp] (log/debug "NAVIGATE" comp) (let [{:keys [options]} (get views/screens comp)] (.push Navigation (name @state/root-comp-id) - (clj->js {:component {:id comp - :name comp - :options (merge options - (roots/status-bar-options) - (roots/merge-top-bar (roots/topbar-options) options))}})) + (clj->js + {:component {:id comp + :name comp + :options (merge options + (roots/status-bar-options) + (roots/merge-top-bar (roots/topbar-options) options))}})) ;;if we push the screen from modal, we want to dismiss all modals (dismiss-all-modals))) ;; OPEN MODAL -(defn update-modal-topbar-options [options] +(defn update-modal-topbar-options + [options] (log/debug "update-modal-topbar-options" options) (merge options (roots/merge-top-bar {:elevation 0 @@ -71,7 +75,8 @@ :icon (icons/icon-source :main-icons/close)}} options))) -(defn open-modal [comp] +(defn open-modal + [comp] (log/debug "open-modal" comp) (let [{:keys [options]} (get views/screens comp)] (if @state/dissmissing @@ -92,25 +97,27 @@ (re-frame/reg-fx :open-modal-fx open-modal) ;; DISSMISS MODAL -(defn dissmissModal [] +(defn dissmissModal + [] (log/debug "dissmissModal") (reset! state/dissmissing true) (.dismissModal Navigation (name (last @state/modals)))) (defonce register-nav-button-reg - (.registerNavigationButtonPressedListener - (.events Navigation) - (fn [^js evn] - (let [id (.-buttonId evn)] - (if (= "dismiss-modal" id) - (do - (when-let [event (get-in views/screens [(last @state/modals) :on-dissmiss])] - (re-frame/dispatch event)) - (dissmissModal)) - (when-let [handler (get-in views/screens [(keyword id) :right-handler])] - (handler))))))) + (.registerNavigationButtonPressedListener + (.events Navigation) + (fn [^js evn] + (let [id (.-buttonId evn)] + (if (= "dismiss-modal" id) + (do + (when-let [event (get-in views/screens [(last @state/modals) :on-dissmiss])] + (re-frame/dispatch event)) + (dissmissModal)) + (when-let [handler (get-in views/screens [(keyword id) :right-handler])] + (handler))))))) -(defn set-view-id [view-id] +(defn set-view-id + [view-id] (log/debug "set-view-id" view-id) (when-let [{:keys [on-focus]} (get views/screens view-id)] (re-frame/dispatch [:set-view-id view-id]) @@ -119,51 +126,53 @@ (re-frame/dispatch on-focus)))) (defonce register-modal-reg - (.registerModalDismissedListener - (.events Navigation) - (fn [_] - (if (> (count @state/modals) 1) - (let [new-modals (butlast @state/modals)] - (reset! state/modals (vec new-modals)) - (set-view-id (last new-modals))) - (do - (reset! state/modals []) - (reset! state/curr-modal false) - (set-view-id @state/pushed-screen-id))) + (.registerModalDismissedListener + (.events Navigation) + (fn [_] + (if (> (count @state/modals) 1) + (let [new-modals (butlast @state/modals)] + (reset! state/modals (vec new-modals)) + (set-view-id (last new-modals))) + (do + (reset! state/modals []) + (reset! state/curr-modal false) + (set-view-id @state/pushed-screen-id))) - (let [comp @state/dissmissing] - (reset! state/dissmissing false) - (when (keyword? comp) - (open-modal comp)))))) + (let [comp @state/dissmissing] + (reset! state/dissmissing false) + (when (keyword? comp) + (open-modal comp)))))) ;; SCREEN DID APPEAR (defonce screen-appear-reg - (.registerComponentDidAppearListener - (.events Navigation) - (fn [^js evn] - (let [view-id (keyword (.-componentName evn))] - (log/debug "screen-appear-reg" view-id) - (when (get views/screens view-id) - (when (and (not= view-id :bottom-sheet) - (not= view-id :popover) - (not= view-id :visibility-status-popover)) - (set-view-id view-id) - (when-not @state/curr-modal - (reset! state/pushed-screen-id view-id)))))))) + (.registerComponentDidAppearListener + (.events Navigation) + (fn [^js evn] + (let [view-id (keyword (.-componentName evn))] + (log/debug "screen-appear-reg" view-id) + (when (get views/screens view-id) + (when (and (not= view-id :bottom-sheet) + (not= view-id :popover) + (not= view-id :visibility-status-popover)) + (set-view-id view-id) + (when-not @state/curr-modal + (reset! state/pushed-screen-id view-id)))))))) ;; SCREEN DID DISAPPEAR (defonce screen-disappear-reg - (.registerComponentDidDisappearListener - (.events Navigation) - (fn [^js evn] - (let [view-id (keyword (.-componentName evn))] - (when-not (#{"popover" "bottom-sheet" "signing-sheet" "visibility-status-popover" "wallet-connect-sheet" "wallet-connect-success-sheet" "wallet-connect-app-management-sheet"} - (.-componentName evn)) - (re-frame/dispatch [::view-disappeared view-id]) - (doseq [[_ {:keys [ref value]}] @quo.text-input/text-input-refs] - (.setNativeProps ^js ref (clj->js {:text value}))) - (doseq [[^js text-input default-value] @react/text-input-refs] - (.setNativeProps text-input (clj->js {:text default-value})))))))) + (.registerComponentDidDisappearListener + (.events Navigation) + (fn [^js evn] + (let [view-id (keyword (.-componentName evn))] + (when-not (#{"popover" "bottom-sheet" "signing-sheet" "visibility-status-popover" + "wallet-connect-sheet" "wallet-connect-success-sheet" + "wallet-connect-app-management-sheet"} + (.-componentName evn)) + (re-frame/dispatch [::view-disappeared view-id]) + (doseq [[_ {:keys [ref value]}] @quo.text-input/text-input-refs] + (.setNativeProps ^js ref (clj->js {:text value}))) + (doseq [[^js text-input default-value] @react/text-input-refs] + (.setNativeProps text-input (clj->js {:text default-value})))))))) ;; SET ROOT (re-frame/reg-fx @@ -187,26 +196,29 @@ [{:keys [db]}] (log/debug :set-multiaccounts-root) (let [key-uid (get-in db [:multiaccounts/login :key-uid]) - keycard-account? (boolean (get-in db [:multiaccounts/multiaccounts - key-uid - :keycard-pairing]))] + keycard-account? (boolean (get-in db + [:multiaccounts/multiaccounts + key-uid + :keycard-pairing]))] {:init-root-fx (if keycard-account? :multiaccounts-keycard :multiaccounts)})) -(defonce rset-app-launched - (.registerAppLaunchedListener (.events Navigation) - (fn [] - (reset! state/curr-modal false) - (reset! state/dissmissing false) - (if (or (= @state/root-id :multiaccounts) - (= @state/root-id :multiaccounts-keycard)) - (re-frame/dispatch-sync [::set-multiaccount-root]) - (when @state/root-id - (reset! state/root-comp-id @state/root-id) - (.setRoot Navigation (clj->js (get (roots/roots) @state/root-id))) - (re-frame/dispatch [::login-core/check-last-chat]))) - (.hide ^js splash-screen)))) +(defonce + rset-app-launched + (.registerAppLaunchedListener (.events Navigation) + (fn [] + (reset! state/curr-modal false) + (reset! state/dissmissing false) + (if (or (= @state/root-id :multiaccounts) + (= @state/root-id :multiaccounts-keycard)) + (re-frame/dispatch-sync [::set-multiaccount-root]) + (when @state/root-id + (reset! state/root-comp-id @state/root-id) + (.setRoot Navigation (clj->js (get (roots/roots) @state/root-id))) + (re-frame/dispatch [::login-core/check-last-chat]))) + (.hide ^js splash-screen)))) -(defn get-screen-component [comp] +(defn get-screen-component + [comp] (log/debug :get-screen-component comp) (let [{:keys [options]} (get views/screens comp)] {:component {:id comp @@ -227,24 +239,28 @@ (get-screen-component comp)))))) ;; BOTTOM TABS -(def tab-root-ids {0 :chat-stack - 1 :browser-stack - 2 :wallet-stack - 3 :status-stack - 4 :profile-stack}) +(def tab-root-ids + {0 :chat-stack + 1 :browser-stack + 2 :wallet-stack + 3 :status-stack + 4 :profile-stack}) -(def tab-key-idx {:chat 0 - :browser 1 - :wallet 2 - :status 3 - :profile 4}) +(def tab-key-idx + {:chat 0 + :browser 1 + :wallet 2 + :status 3 + :profile 4}) (re-frame/reg-fx :change-tab-fx (fn [tab] (log/debug :change-tab-fx) (reset! state/root-comp-id (get tab-root-ids (get tab-key-idx tab))) - (.mergeOptions Navigation "tabs-stack" (clj->js {:bottomTabs {:currentTabIndex (get tab-key-idx tab)}})) + (.mergeOptions Navigation + "tabs-stack" + (clj->js {:bottomTabs {:currentTabIndex (get tab-key-idx tab)}})) ;;when we change tab we want to dismiss all modals (dismiss-all-modals))) @@ -253,21 +269,22 @@ :change-tab-count-fx (fn [[tab cnt]] (log/debug :change-tab-count-fx tab cnt) - (.mergeOptions Navigation - (name (get tab-root-ids (get tab-key-idx tab))) - (clj->js {:bottomTab (cond - (or (pos? cnt) (pos? (:other cnt))) - (if (and (= :chat tab) platform/ios?) - {:dotIndicator {:visible true}} - {:badge (str (or (:other cnt) cnt)) :dotIndicator {:visible false}}) - (pos? (:public cnt)) - (if platform/ios? - {:dotIndicator {:visible true}} - {:badge nil :dotIndicator {:visible true}}) - :else - (if (and (= :chat tab) platform/ios?) - {:dotIndicator {:visible false}} - {:dotIndicator {:visible false} :badge ""}))})))) + (.mergeOptions + Navigation + (name (get tab-root-ids (get tab-key-idx tab))) + (clj->js {:bottomTab (cond + (or (pos? cnt) (pos? (:other cnt))) + (if (and (= :chat tab) platform/ios?) + {:dotIndicator {:visible true}} + {:badge (str (or (:other cnt) cnt)) :dotIndicator {:visible false}}) + (pos? (:public cnt)) + (if platform/ios? + {:dotIndicator {:visible true}} + {:badge nil :dotIndicator {:visible true}}) + :else + (if (and (= :chat tab) platform/ios?) + {:dotIndicator {:visible false}} + {:dotIndicator {:visible false} :badge ""}))})))) (re-frame/reg-fx :pop-to-root-tab-fx @@ -277,54 +294,57 @@ (.popToRoot Navigation (name comp)))) (defonce register-bottom-tab-reg - (.registerBottomTabSelectedListener - (.events Navigation) - (fn [^js evn] - (let [selected-tab-index (.-selectedTabIndex evn) - comp (get tab-root-ids selected-tab-index) - tab-key (get (clojure.set/map-invert tab-key-idx) selected-tab-index)] - (re-frame/dispatch [:set :current-tab tab-key]) - (when (= @state/root-comp-id comp) - (when (= :chat tab-key) - (re-frame/dispatch [:close-chat])) - (when platform/android? - (.popToRoot Navigation (name comp)))) - (reset! state/root-comp-id comp))))) + (.registerBottomTabSelectedListener + (.events Navigation) + (fn [^js evn] + (let [selected-tab-index (.-selectedTabIndex evn) + comp (get tab-root-ids selected-tab-index) + tab-key (get (clojure.set/map-invert tab-key-idx) selected-tab-index)] + (re-frame/dispatch [:set :current-tab tab-key]) + (when (= @state/root-comp-id comp) + (when (= :chat tab-key) + (re-frame/dispatch [:close-chat])) + (when platform/android? + (.popToRoot Navigation (name comp)))) + (reset! state/root-comp-id comp))))) ;; OVERLAY (Popover and bottom sheets) -(defn dissmiss-overlay [comp] +(defn dissmiss-overlay + [comp] (.catch (.dismissOverlay Navigation comp) #())) -(defn show-overlay [comp] +(defn show-overlay + [comp] (dissmiss-overlay comp) - (.showOverlay Navigation - (clj->js - {:component {:name comp - :id comp - :options (merge (cond-> (roots/status-bar-options) - (and platform/android? (not (quo.colors/dark?))) - (assoc-in [:statusBar :backgroundColor] "#99999A")) - {:layout {:componentBackgroundColor (if platform/android? - (:backdrop @quo.colors/theme) - "transparent")} - :overlay {:interceptTouchOutside true}})}}))) + (.showOverlay + Navigation + (clj->js + {:component {:name comp + :id comp + :options (merge (cond-> (roots/status-bar-options) + (and platform/android? (not (quo.colors/dark?))) + (assoc-in [:statusBar :backgroundColor] "#99999A")) + {:layout {:componentBackgroundColor (if platform/android? + (:backdrop @quo.colors/theme) + "transparent")} + :overlay {:interceptTouchOutside true}})}}))) ;; POPOVER (defonce popover-reg - (.registerComponent Navigation - "popover" - (fn [] (gestureHandlerRootHOC views/popover-comp)) - (fn [] views/popover-comp))) + (.registerComponent Navigation + "popover" + (fn [] (gestureHandlerRootHOC views/popover-comp)) + (fn [] views/popover-comp))) (re-frame/reg-fx :show-popover (fn [] (show-overlay "popover"))) (re-frame/reg-fx :hide-popover (fn [] (dissmiss-overlay "popover"))) ;; VISIBILITY STATUS POPOVER (defonce visibility-status-popover-reg - (.registerComponent Navigation - "visibility-status-popover" - (fn [] (gestureHandlerRootHOC views/visibility-status-popover-comp)) - (fn [] views/visibility-status-popover-comp))) + (.registerComponent Navigation + "visibility-status-popover" + (fn [] (gestureHandlerRootHOC views/visibility-status-popover-comp)) + (fn [] views/visibility-status-popover-comp))) (re-frame/reg-fx :show-visibility-status-popover (fn [] (show-overlay "visibility-status-popover"))) @@ -333,10 +353,10 @@ ;; BOTTOM SHEETS (defonce bottom-sheet-reg - (.registerComponent Navigation - "bottom-sheet" - (fn [] (gestureHandlerRootHOC views/sheet-comp)) - (fn [] views/sheet-comp))) + (.registerComponent Navigation + "bottom-sheet" + (fn [] (gestureHandlerRootHOC views/sheet-comp)) + (fn [] views/sheet-comp))) (re-frame/reg-fx :show-bottom-sheet (fn [] (show-overlay "bottom-sheet"))) (re-frame/reg-fx :hide-bottom-sheet (fn [] (dissmiss-overlay "bottom-sheet"))) @@ -344,37 +364,41 @@ ;; WALLET CONNECT (defonce wallet-connect-sheet-reg - (.registerComponent Navigation - "wallet-connect-sheet" - (fn [] (gestureHandlerRootHOC views/wallet-connect-comp)) - (fn [] views/wallet-connect-comp))) + (.registerComponent Navigation + "wallet-connect-sheet" + (fn [] (gestureHandlerRootHOC views/wallet-connect-comp)) + (fn [] views/wallet-connect-comp))) (defonce wallet-connect-success-sheet-reg - (.registerComponent Navigation - "wallet-connect-success-sheet" - (fn [] (gestureHandlerRootHOC views/wallet-connect-success-comp)) - (fn [] views/wallet-connect-success-comp))) + (.registerComponent Navigation + "wallet-connect-success-sheet" + (fn [] (gestureHandlerRootHOC views/wallet-connect-success-comp)) + (fn [] views/wallet-connect-success-comp))) (defonce wallet-connect-app-management-sheet-reg - (.registerComponent Navigation - "wallet-connect-app-management-sheet" - (fn [] (gestureHandlerRootHOC views/wallet-connect-app-management-comp)) - (fn [] views/wallet-connect-app-management-comp))) + (.registerComponent Navigation + "wallet-connect-app-management-sheet" + (fn [] (gestureHandlerRootHOC views/wallet-connect-app-management-comp)) + (fn [] views/wallet-connect-app-management-comp))) (re-frame/reg-fx :show-wallet-connect-sheet (fn [] (show-overlay "wallet-connect-sheet"))) (re-frame/reg-fx :hide-wallet-connect-sheet (fn [] (dissmiss-overlay "wallet-connect-sheet"))) -(re-frame/reg-fx :show-wallet-connect-success-sheet (fn [] (show-overlay "wallet-connect-success-sheet"))) -(re-frame/reg-fx :hide-wallet-connect-success-sheet (fn [] (dissmiss-overlay "wallet-connect-success-sheet"))) -(re-frame/reg-fx :show-wallet-connect-app-management-sheet (fn [] (show-overlay "wallet-connect-app-management-sheet"))) -(re-frame/reg-fx :hide-wallet-connect-app-management-sheet (fn [] (dissmiss-overlay "wallet-connect-app-management-sheet"))) +(re-frame/reg-fx :show-wallet-connect-success-sheet + (fn [] (show-overlay "wallet-connect-success-sheet"))) +(re-frame/reg-fx :hide-wallet-connect-success-sheet + (fn [] (dissmiss-overlay "wallet-connect-success-sheet"))) +(re-frame/reg-fx :show-wallet-connect-app-management-sheet + (fn [] (show-overlay "wallet-connect-app-management-sheet"))) +(re-frame/reg-fx :hide-wallet-connect-app-management-sheet + (fn [] (dissmiss-overlay "wallet-connect-app-management-sheet"))) ;; SIGNING (defonce signing-sheet-reg - (.registerComponent Navigation - "signing-sheet" - (fn [] (gestureHandlerRootHOC views/signing-comp)) - (fn [] views/signing-comp))) + (.registerComponent Navigation + "signing-sheet" + (fn [] (gestureHandlerRootHOC views/signing-comp)) + (fn [] views/signing-comp))) (re-frame/reg-fx :show-signing-sheet (fn [] (show-overlay "signing-sheet"))) (re-frame/reg-fx :hide-signing-sheet (fn [] (dissmiss-overlay "signing-sheet"))) @@ -383,10 +407,10 @@ ;; TODO why is this not a regular bottom sheet ? (defonce select-acc-sheet-reg - (.registerComponent Navigation - "select-acc-sheet" - (fn [] (gestureHandlerRootHOC views/select-acc-comp)) - (fn [] views/select-acc-comp))) + (.registerComponent Navigation + "select-acc-sheet" + (fn [] (gestureHandlerRootHOC views/select-acc-comp)) + (fn [] views/select-acc-comp))) (re-frame/reg-fx :show-select-acc-sheet (fn [] (show-overlay "select-acc-sheet"))) (re-frame/reg-fx :hide-select-acc-sheet (fn [] (dissmiss-overlay "select-acc-sheet"))) @@ -414,15 +438,16 @@ (.pop Navigation (name @state/root-comp-id)) (navigate view-id))) -(def community-screens '(:community-management - :community-members - :community-requests-to-join - :create-community-channel - :community-emoji-thumbnail-picker - :create-community-category - :community-edit-chats - :community-edit - :community-reorder-categories)) +(def community-screens + '(:community-management + :community-members + :community-requests-to-join + :create-community-channel + :community-emoji-thumbnail-picker + :create-community-category + :community-edit-chats + :community-edit + :community-reorder-categories)) ;; change view-id if it is still same after component is disappeared ;; https://github.com/wix/react-native-navigation/issues/5744#issuecomment-563226820 @@ -430,12 +455,14 @@ {:events [::view-disappeared]} [{:keys [db]} view-id] (when (= view-id (:view-id db)) - {:db (assoc db :view-id (cond - (= view-id :community-emoji-thumbnail-picker) - :create-community-channel + {:db (assoc db + :view-id + (cond + (= view-id :community-emoji-thumbnail-picker) + :create-community-channel - (some #(= view-id %) community-screens) - :community + (some #(= view-id %) community-screens) + :community - :else - :home))})) + :else + :home))})) diff --git a/src/status_im/network/core.cljs b/src/status_im/network/core.cljs index 5d121d8b0d..b557ef8759 100644 --- a/src/status_im/network/core.cljs +++ b/src/status_im/network/core.cljs @@ -5,37 +5,42 @@ [status-im.ethereum.json-rpc :as json-rpc] [status-im.i18n.i18n :as i18n] [status-im.node.core :as node] - [status-im2.navigation.events :as navigation] [status-im.utils.fx :as fx] [status-im.utils.http :as http] - [status-im.utils.types :as types])) + [status-im.utils.types :as types] + [status-im2.navigation.events :as navigation])) (def url-regex #"https?://(www\.)?[-a-zA-Z0-9@:%._\+~#=]{2,256}(\.[a-z]{2,6})?\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)") -(defn valid-rpc-url? [url] +(defn valid-rpc-url? + [url] (boolean (re-matches url-regex (str url)))) (def default-manage - {:name {:value ""} - :url {:value ""} + {:name {:value ""} + :url {:value ""} :symbol {:value ""} - :chain {:value :mainnet}}) + :chain {:value :mainnet}}) -(defn validate-string [{:keys [value]}] +(defn validate-string + [{:keys [value]}] {:value value :error (string/blank? value)}) -(defn validate-network-id [{:keys [value]}] +(defn validate-network-id + [{:keys [value]}] {:value value :error (and (not (string/blank? value)) (= (int value) 0))}) -(defn validate-url [{:keys [value]}] +(defn validate-url + [{:keys [value]}] {:value value :error (not (valid-rpc-url? value))}) -(defn validate-manage [manage] +(defn validate-manage + [manage] (-> manage (update :url validate-url) (update :name validate-string) @@ -43,17 +48,20 @@ (update :chain validate-string) (update :network-id validate-network-id))) -(defn valid-manage? [manage] +(defn valid-manage? + [manage] (->> (validate-manage manage) vals (map :error) (not-any? identity))) -(defn chain-id-available? [current-networks network] +(defn chain-id-available? + [current-networks network] (let [chain-id (get-in network [:config :NetworkId])] (every? #(not= chain-id (get-in % [1 :config :NetworkId])) current-networks))) -(defn get-network [{:keys [db]} network-id] +(defn get-network + [{:keys [db]} network-id] (get-in db [:networks/networks network-id])) (fx/defn set-input @@ -104,12 +112,14 @@ (js/parseInt res))] (if (and network-id (= expected-network-id rpc-network-id)) (re-frame/dispatch [::connect-success network-id]) - (re-frame/dispatch [::connect-failure (if (not= expected-network-id rpc-network-id) - (i18n/label :t/network-invalid-network-id) - (i18n/label :t/network-invalid-url))])))) + (re-frame/dispatch [::connect-failure + (if (not= expected-network-id rpc-network-id) + (i18n/label :t/network-invalid-network-id) + (i18n/label :t/network-invalid-url))])))) :on-error (fn [{:keys [response-body status-code]}] (let [reason (if status-code - (i18n/label :t/network-invalid-status-code {:code status-code}) + (i18n/label :t/network-invalid-status-code + {:code status-code}) (str response-body))] (re-frame/dispatch [::connect-failure reason])))}} (connect-success cofx network-id)) @@ -125,16 +135,17 @@ {:ui/show-confirmation {:title (i18n/label :t/delete-network-title) :content (i18n/label :t/delete-network-confirmation) :confirm-button-text (i18n/label :t/delete) - :on-accept #(re-frame/dispatch [::remove-network-confirmed network]) + :on-accept #(re-frame/dispatch [::remove-network-confirmed + network]) :on-cancel nil}}))) (fx/defn save-network-settings {:events [::save-network-settings-pressed]} [{:keys [db] :as cofx} network] (fx/merge cofx - {:db (assoc db :networks/current-network network) - ::json-rpc/call [{:method "settings_saveSetting" - :params [:networks/current-network network] + {:db (assoc db :networks/current-network network) + ::json-rpc/call [{:method "settings_saveSetting" + :params [:networks/current-network network] :on-success #()}]} (node/prepare-new-config {:on-success #(re-frame/dispatch [:logout])}))) @@ -142,9 +153,9 @@ {:events [::remove-network-confirmed]} [{:keys [db] :as cofx} network] (let [networks (dissoc (:networks/networks db) network)] - {:db (assoc db :networks/networks networks) - ::json-rpc/call [{:method "settings_saveSetting" - :params [:networks/networks (vals networks)] + {:db (assoc db :networks/networks networks) + ::json-rpc/call [{:method "settings_saveSetting" + :params [:networks/networks (vals networks)] :on-success #(re-frame/dispatch [:navigate-back])}]})) (defn new-network @@ -155,35 +166,36 @@ :DataDir data-dir :UpstreamConfig {:Enabled true :URL upstream-url}}] - {:id random-id - :name network-name - :symbol symbol - :config config})) + {:id random-id + :name network-name + :symbol symbol + :config config})) (fx/defn save - {:events [::save-network-pressed] + {:events [::save-network-pressed] :interceptors [(re-frame/inject-cofx :random-id-generator)]} [{{:networks/keys [manage networks] :as db} :db - random-id-generator :random-id-generator :as cofx}] + random-id-generator :random-id-generator + :as cofx}] (if (valid-manage? manage) ;; rename network-id from UI to chain-id (let [{:keys [name url chain network-id symbol]} manage - random-id (string/replace (random-id-generator) "-" "") - network (new-network random-id - (:value name) - (:value symbol) - (:value url) - (:value chain) - (:value network-id)) - custom-chain-type? (= :custom (:value chain)) - new-networks (assoc networks random-id network)] + random-id (string/replace (random-id-generator) "-" "") + network (new-network random-id + (:value name) + (:value symbol) + (:value url) + (:value chain) + (:value network-id)) + custom-chain-type? (= :custom (:value chain)) + new-networks (assoc networks random-id network)] (if (or (not custom-chain-type?) (chain-id-available? networks network)) - {:db (-> db - (dissoc :networks/manage) - (assoc :networks/networks new-networks)) - ::json-rpc/call [{:method "settings_saveSetting" - :params [:networks/networks (vals new-networks)] + {:db (-> db + (dissoc :networks/manage) + (assoc :networks/networks new-networks)) + ::json-rpc/call [{:method "settings_saveSetting" + :params [:networks/networks (vals new-networks)] :on-success #(re-frame/dispatch [:navigate-back])}]} {:ui/show-error "chain-id already defined"})) {:ui/show-error "invalid network parameters"})) diff --git a/src/status_im/network/core_test.cljs b/src/status_im/network/core_test.cljs index 1c7f166ad8..54c949ce25 100644 --- a/src/status_im/network/core_test.cljs +++ b/src/status_im/network/core_test.cljs @@ -65,33 +65,33 @@ (deftest valid-manage-test (testing "a valid manage" - (is (network.core/valid-manage? {:url {:value "http://valid.com"} - :name {:value "valid"} + (is (network.core/valid-manage? {:url {:value "http://valid.com"} + :name {:value "valid"} :symbol {:value "valid"} - :chain {:value "valid"}}))) + :chain {:value "valid"}}))) (testing "invalid url" - (is (not (network.core/valid-manage? {:url {:value "invalid"} - :name {:value "valid"} + (is (not (network.core/valid-manage? {:url {:value "invalid"} + :name {:value "valid"} :symbol {:value "valid"} - :chain {:value "valid"}})))) + :chain {:value "valid"}})))) (testing "invalid name" - (is (not (network.core/valid-manage? {:url {:value "http://valid.com"} - :name {:value ""} + (is (not (network.core/valid-manage? {:url {:value "http://valid.com"} + :name {:value ""} :symbol {:value "valid"} - :chain {:value "valid"}})))) + :chain {:value "valid"}})))) (testing "invalid chain" - (is (not (network.core/valid-manage? {:url {:value "http://valid.com"} - :name {:value "valid"} + (is (not (network.core/valid-manage? {:url {:value "http://valid.com"} + :name {:value "valid"} :symbol {:value "valid"} - :chain {:value ""}})))) + :chain {:value ""}})))) (testing "invalid symbol" - (is (not (network.core/valid-manage? {:url {:value "http://valid.com"} - :name {:value "valid"} + (is (not (network.core/valid-manage? {:url {:value "http://valid.com"} + :name {:value "valid"} :symbol {:value ""} - :chain {:value "valid"}}))))) + :chain {:value "valid"}}))))) (deftest set-input-test (testing "it updates and validate a field" @@ -105,40 +105,44 @@ :error false} :network-id {:value nil :error false}}}} - (network.core/set-input {:db {:networks/manage {:url {:value "something" - :error true} - :name {:value "" - :error false} + (network.core/set-input {:db {:networks/manage {:url {:value "something" + :error true} + :name {:value "" + :error false} :symbol {:value "symbol" :error false} - :chain {:value "mainnet" - :error false}}}} - :url "http://valid.com"))))) + :chain {:value "mainnet" + :error false}}}} + :url + "http://valid.com"))))) (deftest not-save-invalid-url (testing "it does not save a network with an invalid url" - (is (:ui/show-error (network.core/save {:random-id-generator (constantly "random") - :db {:networks/manage {:url {:value "wrong"} - :chain {:value "1"} - :name {:value "empty"}} - :multiaccount {}}}))))) + (is + (:ui/show-error (network.core/save {:random-id-generator (constantly "random") + :db {:networks/manage {:url {:value "wrong"} + :chain {:value "1"} + :name {:value "empty"}} + :multiaccount {}}}))))) (deftest save-valid-network (testing "save a valid network" - (let [fx (network.core/save {:random-id-generator (constantly "random-id") - :db {:networks/manage {:url {:value "http://valid.com"} - :chain {:value :mainnet} - :symbol {:value "symbol"} - :name {:value "valid"}} - :multiaccount {} - :networks/networks {"random2" - {:id "random2" - :name "network-name" - :symbol "symbol" - :config {:NetworkId 1 - :DataDir "/ethereum/mainnet_rpc" - :UpstreamConfig {:Enabled true - :URL "upstream-url"}}}}}})] + (let [fx (network.core/save + {:random-id-generator (constantly "random-id") + :db {:networks/manage {:url {:value "http://valid.com"} + :chain {:value :mainnet} + :symbol {:value "symbol"} + :name {:value "valid"}} + :multiaccount {} + :networks/networks {"random2" + {:id "random2" + :name "network-name" + :symbol "symbol" + :config {:NetworkId 1 + :DataDir "/ethereum/mainnet_rpc" + :UpstreamConfig + {:Enabled true + :URL "upstream-url"}}}}}})] (is (= "settings_saveSetting" (:method (first (::json-rpc/call fx))))) (is (nil? (:networks/manage (:db fx)))) (testing "and check that it has an id with `-` and the correct mainnet NetworkId" @@ -146,38 +150,42 @@ (deftest not-save-custom-chain-with-non-unique-id (testing "it does not save a custom chain with network-id already defined" - (let [result (network.core/save {:random-id-generator (constantly "already-defined") - :db {:networks/manage {:url {:value "http://valid.com"} - :chain {:value :custom} - :name {:value "valid"} - :network-id {:value 1}} - :multiaccount {} - :networks/networks {"random" - {:id "random" - :name "network-name" - :config {:NetworkId 1 - :DataDir "/ethereum/mainnet_rpc" - :UpstreamConfig {:Enabled true - :URL "upstream-url"}}}}}})] + (let [result (network.core/save + {:random-id-generator (constantly "already-defined") + :db {:networks/manage {:url {:value "http://valid.com"} + :chain {:value :custom} + :name {:value "valid"} + :network-id {:value 1}} + :multiaccount {} + :networks/networks {"random" + {:id "random" + :name "network-name" + :config {:NetworkId 1 + :DataDir "/ethereum/mainnet_rpc" + :UpstreamConfig + {:Enabled true + :URL "upstream-url"}}}}}})] (is (:ui/show-error result))))) (deftest save-valid-network-with-unique-chain-id-check (testing "save a valid network with chain-id not already defined" - (let [fx (network.core/save {:random-id-generator (constantly "random") - :db {:networks/manage {:url {:value "http://valid.com"} - :chain {:value :mainnet} - :name {:value "valid"} - :symbol {:value "symbol"} - :network-id {:value 5}} - :multiaccount {} - :networks/networks {"randomid" - {:id "randomid" - :name "network-name" - :symbol "symbol" - :config {:NetworkId 3 - :DataDir "/ethereum/mainnet_rpc" - :UpstreamConfig {:Enabled true - :URL "upstream-url"}}}}}})] + (let [fx (network.core/save + {:random-id-generator (constantly "random") + :db {:networks/manage {:url {:value "http://valid.com"} + :chain {:value :mainnet} + :name {:value "valid"} + :symbol {:value "symbol"} + :network-id {:value 5}} + :multiaccount {} + :networks/networks {"randomid" + {:id "randomid" + :name "network-name" + :symbol "symbol" + :config {:NetworkId 3 + :DataDir "/ethereum/mainnet_rpc" + :UpstreamConfig + {:Enabled true + :URL "upstream-url"}}}}}})] (is (= "settings_saveSetting" (:method (first (::json-rpc/call fx))))) (is (nil? (:networks/manage (:db fx)))) (is (get-in fx [:db :networks/networks "random"]))))) diff --git a/src/status_im/network/net_info.cljs b/src/status_im/network/net_info.cljs index aed78daa7e..014993b328 100644 --- a/src/status_im/network/net_info.cljs +++ b/src/status_im/network/net_info.cljs @@ -1,10 +1,10 @@ (ns status-im.network.net-info - (:require [re-frame.core :as re-frame] - [status-im.native-module.core :as status] + (:require ["@react-native-community/netinfo" :default net-info] + [re-frame.core :as re-frame] [status-im.mobile-sync-settings.core :as mobile-network] + [status-im.native-module.core :as status] [status-im.utils.fx :as fx] [status-im.wallet.core :as wallet] - ["@react-native-community/netinfo" :default net-info] [taoensso.timbre :as log])) (fx/defn change-network-status @@ -20,7 +20,7 @@ (fx/defn change-network-type [{:keys [db] :as cofx} old-network-type network-type expensive?] (fx/merge cofx - {:db (assoc db :network/type network-type) + {:db (assoc db :network/type network-type) :network/notify-status-go [network-type expensive?]} (mobile-network/on-network-status-change))) @@ -33,18 +33,19 @@ status-changed? (= connectivity-status old-network-status) type-changed? (= type old-network-type)] (log/debug "[net-info]" - "old-network-status" old-network-status - "old-network-type" old-network-type + "old-network-status" old-network-status + "old-network-type" old-network-type "connectivity-status" connectivity-status - "type" type - "details" details) + "type" type + "details" details) (fx/merge cofx (when-not status-changed? (change-network-status isConnected)) (when-not type-changed? (change-network-type old-network-type type (:is-connection-expensive details)))))) -(defn add-net-info-listener [] +(defn add-net-info-listener + [] (when net-info (.addEventListener ^js net-info #(re-frame/dispatch [::network-info-changed diff --git a/src/status_im/node/core.cljs b/src/status_im/node/core.cljs index 7f737fed78..5b55aed17e 100644 --- a/src/status_im/node/core.cljs +++ b/src/status_im/node/core.cljs @@ -7,7 +7,8 @@ [status-im.utils.platform :as utils.platform] [status-im.utils.types :as types])) -(defn- add-custom-bootnodes [config network all-bootnodes] +(defn- add-custom-bootnodes + [config network all-bootnodes] (let [bootnodes (as-> all-bootnodes $ (get $ network) (vals $) @@ -16,18 +17,19 @@ (assoc-in config [:ClusterConfig :BootNodes] bootnodes) config))) -(defn- add-log-level [config log-level] +(defn- add-log-level + [config log-level] (if (empty? log-level) (assoc config - :MaxPeers 20 + :MaxPeers 20 :MaxPendingPeers 20 - :LogLevel "ERROR" - :LogEnabled false) + :LogLevel "ERROR" + :LogEnabled false) (assoc config - :MaxPeers 20 + :MaxPeers 20 :MaxPendingPeers 20 - :LogLevel log-level - :LogEnabled true))) + :LogLevel log-level + :LogEnabled true))) (defn get-network-genesis-hash-prefix "returns the hex representation of the first 8 bytes of @@ -50,22 +52,25 @@ (defn get-topics [network] (let [les-topic (get-les-topic network)] - (cond-> {"whisper" {:Min 2, :Max 2}} - les-topic (assoc les-topic {:Min 2, :Max 2})))) + (cond-> {"whisper" {:Min 2 :Max 2}} + les-topic (assoc les-topic {:Min 2 :Max 2})))) -(defn- get-base-node-config [config] +(defn- get-base-node-config + [config] (cond-> (assoc config - :Name "StatusIM") + :Name + "StatusIM") config/dev-build? (assoc :ListenAddr ":30304" - :DataDir (str (:DataDir config) "_dev")))) + :DataDir (str (:DataDir config) "_dev")))) (def login-node-config {:WalletConfig (cond-> {:Enabled true} (not= config/opensea-api-key "") (assoc :OpenseaAPIKey config/opensea-api-key))}) -(defn- get-login-node-config [config] +(defn- get-login-node-config + [config] (merge config login-node-config)) (defn- pick-nodes @@ -75,14 +80,17 @@ [limit nodes] (take limit (shuffle nodes))) -(defn fleets [{:keys [custom-fleets]}] +(defn fleets + [{:keys [custom-fleets]}] (as-> [(js/require "./fleets.js")] $ (mapv #(:fleets (types/json->clj %)) $) (conj $ custom-fleets) (reduce merge $))) -(defn current-fleet-key [db] - (keyword (get-in db [:multiaccount :fleet] +(defn current-fleet-key + [db] + (keyword (get-in db + [:multiaccount :fleet] config/fleet))) (defn get-current-fleet @@ -93,15 +101,16 @@ (defn- get-multiaccount-node-config [{:keys [multiaccount :networks/networks :networks/current-network] :as db}] - (let [wakuv2-config (get multiaccount :wakuv2-config {}) - current-fleet-key (current-fleet-key db) - current-fleet (get-current-fleet db) - wakuv2-enabled (boolean (:waku current-fleet)) - waku-nodes (:waku-nodes current-fleet) - rendezvous-nodes (pick-nodes 3 (vals (:rendezvous current-fleet))) + (let [wakuv2-config (get multiaccount :wakuv2-config {}) + current-fleet-key (current-fleet-key db) + current-fleet (get-current-fleet db) + wakuv2-enabled (boolean (:waku current-fleet)) + waku-nodes (:waku-nodes current-fleet) + rendezvous-nodes (pick-nodes 3 (vals (:rendezvous current-fleet))) {:keys [installation-id log-level waku-bloom-filter-mode - custom-bootnodes custom-bootnodes-enabled?]} multiaccount + custom-bootnodes custom-bootnodes-enabled?]} + multiaccount use-custom-bootnodes (get custom-bootnodes-enabled? current-network)] (cond-> (get-in networks [current-network :config]) :always @@ -111,10 +120,10 @@ (get-login-node-config) current-fleet - (assoc :NoDiscovery wakuv2-enabled - :Rendezvous (if wakuv2-enabled false (boolean (seq rendezvous-nodes))) - :ClusterConfig {:Enabled true - :Fleet (name current-fleet-key) + (assoc :NoDiscovery wakuv2-enabled + :Rendezvous (if wakuv2-enabled false (boolean (seq rendezvous-nodes))) + :ClusterConfig {:Enabled true + :Fleet (name current-fleet-key) :DiscV5BootstrapNodes (if wakuv2-enabled waku-nodes @@ -124,29 +133,30 @@ :TrustedMailServers (if wakuv2-enabled [] (pick-nodes 6 (vals (:mail current-fleet)))) :StaticNodes - (if wakuv2-enabled [] - (into (pick-nodes 2 - (vals (:whisper current-fleet))) - (vals (:static current-fleet)))) - :RendezvousNodes (if wakuv2-enabled [] rendezvous-nodes) - :WakuNodes (or waku-nodes [])}) + (if wakuv2-enabled + [] + (into (pick-nodes 2 + (vals (:whisper current-fleet))) + (vals (:static current-fleet)))) + :RendezvousNodes (if wakuv2-enabled [] rendezvous-nodes) + :WakuNodes (or waku-nodes [])}) :always (assoc :LocalNotificationsConfig {:Enabled true} - :BrowsersConfig {:Enabled true} - :PermissionsConfig {:Enabled true} - :MailserversConfig {:Enabled true} - :EnableNTPSync true + :BrowsersConfig {:Enabled true} + :PermissionsConfig {:Enabled true} + :MailserversConfig {:Enabled true} + :EnableNTPSync true :WakuConfig {:Enabled true :BloomFilterMode waku-bloom-filter-mode :LightClient true :MinimumPoW 0.000001} - :WakuV2Config (assoc wakuv2-config - :DiscoveryLimit 20 - :EnableDiscV5 true - :Enabled wakuv2-enabled - :Host "0.0.0.0") + :WakuV2Config (assoc wakuv2-config + :DiscoveryLimit 20 + :EnableDiscV5 true + :Enabled wakuv2-enabled + :Host "0.0.0.0") :ShhextConfig {:BackupDisabledDataDir (utils.platform/no-backup-directory) :InstallationID installation-id @@ -158,8 +168,8 @@ :VerifyTransactionChainID config/verify-transaction-chain-id :DataSyncEnabled true :PFSEnabled true} - :RequireTopics (get-topics current-network) - :StatusAccountsConfig {:Enabled true}) + :RequireTopics (get-topics current-network) + :StatusAccountsConfig {:Enabled true}) (and config/bootnodes-settings-enabled? diff --git a/src/status_im/notifications/android.cljs b/src/status_im/notifications/android.cljs index e1a3ea1ff5..6e892465af 100644 --- a/src/status_im/notifications/android.cljs +++ b/src/status_im/notifications/android.cljs @@ -3,27 +3,35 @@ [quo.platform :as platform] [taoensso.timbre :as log])) -(defn pn-android [] +(defn pn-android + [] (when platform/android? (.-PushNotification ^js (.-NativeModules react-native)))) -(defn present-local-notification [opts] +(defn present-local-notification + [opts] (.presentLocalNotification ^js (pn-android) (clj->js opts))) -(defn clear-message-notifications [chat-id] +(defn clear-message-notifications + [chat-id] (.clearMessageNotifications ^js (pn-android) chat-id)) -(defn clear-all-message-notifications [] +(defn clear-all-message-notifications + [] (.clearAllMessageNotifications ^js (pn-android))) -(defn create-channel [{:keys [channel-id channel-name]}] +(defn create-channel + [{:keys [channel-id channel-name]}] (.createChannel ^js (pn-android) - #js {:channelId channel-id - :channelName channel-name} + #js + {:channelId channel-id + :channelName channel-name} #(log/info "Notifications create channel:" %))) -(defn enable-notifications [] +(defn enable-notifications + [] (.enableNotifications ^js (pn-android))) -(defn disable-notifications [] +(defn disable-notifications + [] (.disableNotifications ^js (pn-android))) diff --git a/src/status_im/notifications/core.cljs b/src/status_im/notifications/core.cljs index 3baff0fa56..1d1e992480 100644 --- a/src/status_im/notifications/core.cljs +++ b/src/status_im/notifications/core.cljs @@ -1,14 +1,14 @@ (ns status-im.notifications.core - (:require [re-frame.core :as re-frame] - [taoensso.timbre :as log] - [status-im.utils.fx :as fx] + (:require ["@react-native-community/push-notification-ios" :default pn-ios] + [quo.platform :as platform] + [re-frame.core :as re-frame] + [status-im.ethereum.json-rpc :as json-rpc] [status-im.multiaccounts.update.core :as multiaccounts.update] - ["@react-native-community/push-notification-ios" :default pn-ios] [status-im.notifications.android :as pn-android] [status-im.notifications.local :as local] - [quo.platform :as platform] [status-im.utils.config :as config] - [status-im.ethereum.json-rpc :as json-rpc])) + [status-im.utils.fx :as fx] + [taoensso.timbre :as log])) (def server-type-default 1) (def server-type-custom 2) @@ -16,12 +16,14 @@ (def apn-token-type 1) (def firebase-token-type 2) (def listeners-added? (atom nil)) -(defn server<-rpc [{:keys [type publicKey registered]}] +(defn server<-rpc + [{:keys [type publicKey registered]}] {:public-key publicKey - :type type + :type type :registered registered}) -(defn add-event-listeners [] +(defn add-event-listeners + [] (when-not @listeners-added? (reset! listeners-added? true) (.addEventListener @@ -35,23 +37,27 @@ (fn [error] (re-frame/dispatch [:notifications/switch-error true error]))))) -(defn enable-ios-notifications [] +(defn enable-ios-notifications + [] (add-event-listeners) (-> (.requestPermissions ^js pn-ios) (.then #()) (.catch #()))) -(defn disable-ios-notifications [] +(defn disable-ios-notifications + [] (.abandonPermissions ^js pn-ios) (re-frame/dispatch [:notifications/unregistered-from-push-notifications])) -(defn enable-android-notifications [] +(defn enable-android-notifications + [] (pn-android/create-channel {:channel-id "status-im-notifications" :channel-name "Status push notifications"}) (pn-android/enable-notifications)) -(defn disable-android-notifications [] +(defn disable-android-notifications + [] (pn-android/disable-notifications)) ;; FIXME: Repalce with request permission from audio messages PR lib @@ -107,7 +113,8 @@ {:events [:notifications/registered-for-push-notifications]} [cofx token] {::json-rpc/call [{:method "wakuext_registerForPushNotifications" - :params [token (if platform/ios? config/apn-topic) (if platform/ios? apn-token-type firebase-token-type)] + :params [token (if platform/ios? config/apn-topic) + (if platform/ios? apn-token-type firebase-token-type)] :on-success #(log/info "[push-notifications] register-success" %) :on-error #(re-frame/dispatch [:notifications/switch-error true %])}]}) @@ -131,24 +138,33 @@ {:events [:notifications/switch-error]} [cofx enabled?] (multiaccounts.update/multiaccount-update - cofx :remote-push-notifications-enabled? (not enabled?) {})) + cofx + :remote-push-notifications-enabled? + (not enabled?) + {})) (fx/defn notification-switch {:events [::switch]} [{:keys [db] :as cofx} enabled? remote-push-notifications?] (fx/merge cofx (if enabled? - {::enable remote-push-notifications?} + {::enable remote-push-notifications?} {::disable nil}) (multiaccounts.update/multiaccount-update - :remote-push-notifications-enabled? (and remote-push-notifications? enabled?) {}) + :remote-push-notifications-enabled? + (and remote-push-notifications? enabled?) + {}) (multiaccounts.update/multiaccount-update - :notifications-enabled? (and (not remote-push-notifications?) enabled?) {}))) + :notifications-enabled? + (and (not remote-push-notifications?) enabled?) + {}))) (fx/defn notification-non-contacts-error {:events [::non-contacts-update-error]} [cofx enabled?] - (multiaccounts.update/optimistic cofx :push-notifications-from-contacts-only? (not (boolean enabled?)))) + (multiaccounts.update/optimistic cofx + :push-notifications-from-contacts-only? + (not (boolean enabled?)))) (fx/defn notification-block-mentions-error {:events [::block-mentions-update-error]} @@ -161,13 +177,14 @@ (let [method (if enabled? "wakuext_enablePushNotificationsFromContactsOnly" "wakuext_disablePushNotificationsFromContactsOnly")] - (fx/merge cofx - {::json-rpc/call [{:method method - :params [] - :on-success #(log/info "[push-notifications] contacts-notification-success" %) - :on-error #(re-frame/dispatch [::non-contacts-update-error enabled? %])}]} + (fx/merge + cofx + {::json-rpc/call [{:method method + :params [] + :on-success #(log/info "[push-notifications] contacts-notification-success" %) + :on-error #(re-frame/dispatch [::non-contacts-update-error enabled? %])}]} - (multiaccounts.update/optimistic :push-notifications-from-contacts-only? (boolean enabled?))))) + (multiaccounts.update/optimistic :push-notifications-from-contacts-only? (boolean enabled?))))) (fx/defn notification-block-mentions {:events [::switch-block-mentions]} @@ -180,7 +197,8 @@ {::json-rpc/call [{:method method :params [] :on-success #(log/info "[push-notifications] block-mentions-success" %) - :on-error #(re-frame/dispatch [::block-mentions-update-error enabled? %])}]} + :on-error #(re-frame/dispatch [::block-mentions-update-error enabled? + %])}]} (multiaccounts.update/optimistic :push-notifications-block-mentions? (boolean enabled?))))) @@ -190,13 +208,15 @@ (let [method (if enabled? "wakuext_startPushNotificationsServer" "wakuext_stopPushNotificationsServer")] - (fx/merge cofx - {::json-rpc/call [{:method method - :params [] - :on-success #(log/info "[push-notifications] switch-server-enabled successful" %) - :on-error #(re-frame/dispatch [::push-notifications-server-update-error enabled? %])}]} + (fx/merge + cofx + {::json-rpc/call [{:method method + :params [] + :on-success #(log/info "[push-notifications] switch-server-enabled successful" %) + :on-error #(re-frame/dispatch [::push-notifications-server-update-error + enabled? %])}]} - (multiaccounts.update/optimistic :push-notifications-server-enabled? (boolean enabled?))))) + (multiaccounts.update/optimistic :push-notifications-server-enabled? (boolean enabled?))))) (fx/defn switch-send-notifications {:events [::switch-send-push-notifications]} @@ -207,15 +227,18 @@ (fx/merge cofx {::json-rpc/call [{:method method :params [] - :on-success #(log/info "[push-notifications] switch-send-notifications successful" %) - :on-error #(re-frame/dispatch [::push-notifications-send-update-error enabled? %])}]} + :on-success + #(log/info "[push-notifications] switch-send-notifications successful" + %) + :on-error #(re-frame/dispatch [::push-notifications-send-update-error + enabled? %])}]} (multiaccounts.update/optimistic :send-push-notifications? (boolean enabled?))))) (fx/defn handle-add-server-error {:events [::push-notifications-add-server-error]} [_ public-key error] - (log/error "failed to add server", public-key, error)) + (log/error "failed to add server" public-key error)) (fx/defn add-server {:events [::add-server]} @@ -223,10 +246,13 @@ (fx/merge cofx {::json-rpc/call [{:method "wakuext_addPushNotificationsServer" :params [public-key] - :on-success #(do - (log/info "[push-notifications] switch-send-notifications successful" %) - (re-frame/dispatch [::fetch-servers])) - :on-error #(re-frame/dispatch [::push-notifications-add-server-error public-key %])}]})) + :on-success + #(do + (log/info "[push-notifications] switch-send-notifications successful" + %) + (re-frame/dispatch [::fetch-servers])) + :on-error #(re-frame/dispatch [::push-notifications-add-server-error + public-key %])}]})) (fx/defn handle-servers-fetched {:events [::servers-fetched]} @@ -256,22 +282,31 @@ :params [] :on-success #(re-frame/dispatch [::preferences-loaded %])}]}) -(defn preference= [x y] +(defn preference= + [x y] (and (= (:service x) (:service y)) (= (:event x) (:event y)) (= (:identifier x) (:identifier y)))) -(defn- update-preference [all new] +(defn- update-preference + [all new] (conj (filter (comp not (partial preference= new)) all) new)) (fx/defn switch-transaction-notifications {:events [::switch-transaction-notifications]} [{:keys [db] :as cofx} enabled?] - {:db (update db :push-notifications/preferences update-preference {:enabled (not enabled?) - :service "wallet" - :event "transaction" - :identifier "all"}) + {:db (update db + :push-notifications/preferences + update-preference + {:enabled (not enabled?) + :service "wallet" + :event "transaction" + :identifier "all"}) ::json-rpc/call [{:method "localnotifications_switchWalletNotifications" :params [(not enabled?)] - :on-success #(log/info "[push-notifications] switch-transaction-notifications successful" %) - :on-error #(log/error "[push-notifications] switch-transaction-notifications error" %)}]}) + :on-success #(log/info + "[push-notifications] switch-transaction-notifications successful" + %) + :on-error #(log/error + "[push-notifications] switch-transaction-notifications error" + %)}]}) diff --git a/src/status_im/notifications/local.cljs b/src/status_im/notifications/local.cljs index 632751a925..3549dfbd6b 100644 --- a/src/status_im/notifications/local.cljs +++ b/src/status_im/notifications/local.cljs @@ -1,20 +1,20 @@ (ns status-im.notifications.local - (:require [status-im.async-storage.core :as async-storage] - [status-im.utils.fx :as fx] - [status-im.ethereum.decode :as decode] - ["@react-native-community/push-notification-ios" :default pn-ios] - [status-im.notifications.android :as pn-android] - [status-im.ethereum.tokens :as tokens] - [status-im.utils.utils :as utils] - [status-im.utils.types :as types] - [status-im.utils.money :as money] - [status-im.i18n.i18n :as i18n] - [quo.platform :as platform] - [re-frame.core :as re-frame] + (:require ["@react-native-community/push-notification-ios" :default pn-ios] [cljs-bean.core :as bean] [clojure.string :as string] + [quo.platform :as platform] + [re-frame.core :as re-frame] + [status-im.async-storage.core :as async-storage] [status-im.chat.models :as chat.models] - [status-im.utils.react-native :as react-native-utils])) + [status-im.ethereum.decode :as decode] + [status-im.ethereum.tokens :as tokens] + [status-im.i18n.i18n :as i18n] + [status-im.notifications.android :as pn-android] + [status-im.utils.fx :as fx] + [status-im.utils.money :as money] + [status-im.utils.react-native :as react-native-utils] + [status-im.utils.types :as types] + [status-im.utils.utils :as utils])) (def default-erc20-token {:symbol :ERC20 @@ -24,34 +24,39 @@ (def notification-event-ios "localNotification") (def notification-event-android "remoteNotificationReceived") -(defn local-push-ios [{:keys [title message user-info body-type]}] +(defn local-push-ios + [{:keys [title message user-info body-type]}] (when (not= body-type "message") (.presentLocalNotification pn-ios - #js {:alertBody message - :alertTitle title - ;; NOTE: Use a special type to hide in Obj-C code other notifications - :userInfo (bean/->js (merge user-info - {:notificationType "local-notification"}))}))) + #js + {:alertBody message + :alertTitle title + ;; NOTE: Use a special type to hide in Obj-C code other notifications + :userInfo (bean/->js (merge user-info + {:notificationType "local-notification"}))}))) (defn local-push-android [notification] (pn-android/present-local-notification notification)) -(defn handle-notification-press [{{deep-link :deepLink} :userInfo - interaction :userInteraction}] +(defn handle-notification-press + [{{deep-link :deepLink} :userInfo + interaction :userInteraction}] (async-storage/set-item! (str :chat-id) nil) (when (and deep-link (or platform/ios? (and platform/android? interaction))) (re-frame/dispatch [:universal-links/handle-url deep-link]))) -(defn listen-notifications [] +(defn listen-notifications + [] (if platform/ios? (.addEventListener ^js pn-ios notification-event-ios (fn [notification] - (handle-notification-press {:userInfo (bean/bean (.getData ^js notification))}))) + (handle-notification-press {:userInfo (bean/bean (.getData ^js + notification))}))) (.addListener ^js react-native-utils/device-event-emitter notification-event-android (fn [^js data] @@ -72,21 +77,27 @@ to (or (:name toAccount) (utils/get-shortened-address to)) from (or (:name fromAccount) (utils/get-shortened-address from)) title (case state - "inbound" (i18n/label :t/push-inbound-transaction {:value amount - :currency (:symbol token)}) - "outbound" (i18n/label :t/push-outbound-transaction {:value amount - :currency (:symbol token)}) - "failed" (i18n/label :t/push-failed-transaction {:value amount - :currency (:symbol token)}) + "inbound" (i18n/label :t/push-inbound-transaction + {:value amount + :currency (:symbol token)}) + "outbound" (i18n/label :t/push-outbound-transaction + {:value amount + :currency (:symbol token)}) + "failed" (i18n/label :t/push-failed-transaction + {:value amount + :currency (:symbol token)}) nil) description (case state - "inbound" (i18n/label :t/push-inbound-transaction-body {:from from - :to to}) - "outbound" (i18n/label :t/push-outbound-transaction-body {:from from - :to to}) - "failed" (i18n/label :t/push-failed-transaction-body {:value amount - :currency (:symbol token) - :to to}) + "inbound" (i18n/label :t/push-inbound-transaction-body + {:from from + :to to}) + "outbound" (i18n/label :t/push-outbound-transaction-body + {:from from + :to to}) + "failed" (i18n/label :t/push-failed-transaction-body + {:value amount + :currency (:symbol token) + :to to}) nil)] {:title title :icon (get-in token [:icon :source]) @@ -113,7 +124,8 @@ "message" (when (show-message-pn? cofx notification) notification) "transaction" (create-transfer-notification cofx notification) nil) - :body-type bodyType))) + :body-type + bodyType))) (re-frame/reg-fx ::local-push-ios diff --git a/src/status_im/pairing/core.cljs b/src/status_im/pairing/core.cljs index c52299e4d4..bcd3ad1a28 100644 --- a/src/status_im/pairing/core.cljs +++ b/src/status_im/pairing/core.cljs @@ -2,36 +2,40 @@ (:require [re-frame.core :as re-frame] [status-im.ethereum.json-rpc :as json-rpc] [status-im.i18n.i18n :as i18n] - [status-im2.navigation.events :as navigation] + [status-im.multiaccounts.update.core :as multiaccounts.update] [status-im.utils.config :as config] [status-im.utils.fx :as fx] [status-im.utils.platform :as utils.platform] - [taoensso.timbre :as log] - [status-im.multiaccounts.update.core :as multiaccounts.update])) + [status-im2.navigation.events :as navigation] + [taoensso.timbre :as log])) -(defn enable-installation-rpc [installation-id on-success on-error] +(defn enable-installation-rpc + [installation-id on-success on-error] (json-rpc/call {:method "wakuext_enableInstallation" :params [installation-id] :on-success on-success - :on-error on-error})) + :on-error on-error})) -(defn disable-installation-rpc [installation-id on-success on-error] +(defn disable-installation-rpc + [installation-id on-success on-error] (json-rpc/call {:method "wakuext_disableInstallation" :params [installation-id] :on-success on-success - :on-error on-error})) + :on-error on-error})) -(defn set-installation-metadata-rpc [installation-id metadata on-success on-error] +(defn set-installation-metadata-rpc + [installation-id metadata on-success on-error] (json-rpc/call {:method "wakuext_setInstallationMetadata" :params [installation-id metadata] :on-success on-success - :on-error on-error})) + :on-error on-error})) -(defn get-our-installations-rpc [on-success on-error] +(defn get-our-installations-rpc + [on-success on-error] (json-rpc/call {:method "wakuext_getOurInstallations" :params [] :on-success on-success - :on-error on-error})) + :on-error on-error})) (defn compare-installation "Sort installations, first by our installation-id, then on whether is @@ -71,11 +75,13 @@ {:db (assoc-in db [:pairing/prompt-user-pop-up] false)} (navigation/navigate-to-cofx :installations nil))) -(fx/defn prompt-user-on-new-installation [{:keys [db]}] +(fx/defn prompt-user-on-new-installation + [{:keys [db]}] (when-not config/pairing-popup-disabled? {:db (assoc-in db [:pairing/prompt-user-pop-up] true) :ui/show-confirmation {:title (i18n/label :t/pairing-new-installation-detected-title) - :content (i18n/label :t/pairing-new-installation-detected-content) + :content (i18n/label + :t/pairing-new-installation-detected-content) :confirm-button-text (i18n/label :t/pairing-go-to-installation) :cancel-button-text (i18n/label :t/cancel) :on-cancel #(re-frame/dispatch [:pairing.ui/prompt-dismissed]) @@ -87,21 +93,24 @@ [{:keys [db]} installation-name] (let [our-installation-id (get-in db [:multiaccount :installation-id])] {:pairing/set-installation-metadata [our-installation-id - {:name installation-name + {:name installation-name :deviceType utils.platform/os}]})) -(fx/defn init [cofx] +(fx/defn init + [cofx] {:pairing/get-our-installations nil}) -(fx/defn enable [{:keys [db]} installation-id] +(fx/defn enable + [{:keys [db]} installation-id] {:db (assoc-in db - [:pairing/installations installation-id :enabled?] - true)}) + [:pairing/installations installation-id :enabled?] + true)}) -(fx/defn disable [{:keys [db]} installation-id] +(fx/defn disable + [{:keys [db]} installation-id] {:db (assoc-in db - [:pairing/installations installation-id :enabled?] - false)}) + [:pairing/installations installation-id :enabled?] + false)}) (defn handle-enable-installation-response-success "Callback to dispatch on enable signature response" @@ -123,34 +132,39 @@ [result] (re-frame/dispatch [:pairing.callback/get-our-installations-success result])) -(defn enable-installation! [installation-id] +(defn enable-installation! + [installation-id] (enable-installation-rpc installation-id (partial handle-enable-installation-response-success installation-id) nil)) -(defn disable-installation! [installation-id] +(defn disable-installation! + [installation-id] (disable-installation-rpc installation-id (partial handle-disable-installation-response-success installation-id) nil)) -(defn set-installation-metadata! [installation-id metadata] +(defn set-installation-metadata! + [installation-id metadata] (set-installation-metadata-rpc installation-id metadata (partial handle-set-installation-metadata-response-success installation-id metadata) nil)) -(defn get-our-installations [] +(defn get-our-installations + [] (get-our-installations-rpc handle-get-our-installations-response-success nil)) (fx/defn enable-fx {:events [:pairing.ui/enable-installation-pressed]} [cofx installation-id] - (if (< (count (filter :enabled? (vals (get-in cofx [:db :pairing/installations])))) (inc config/max-installations)) + (if (< (count (filter :enabled? (vals (get-in cofx [:db :pairing/installations])))) + (inc config/max-installations)) {:pairing/enable-installation [installation-id]} - {:utils/show-popup {:title (i18n/label :t/pairing-maximum-number-reached-title) + {:utils/show-popup {:title (i18n/label :t/pairing-maximum-number-reached-title) :content (i18n/label :t/pairing-maximum-number-reached-content)}})) @@ -181,44 +195,52 @@ (fx/defn send-installation-messages {:events [:pairing.ui/synchronize-installation-pressed]} [{:keys [db]}] - (let [multiaccount (:multiaccount db) + (let [multiaccount (:multiaccount db) {:keys [name preferred-name identicon]} multiaccount] {::json-rpc/call [{:method "wakuext_syncDevices" :params [(or preferred-name name) identicon] :on-success #(log/debug "successfully synced devices")}]})) -(defn installation<-rpc [{:keys [metadata id enabled timestamp]}] +(defn installation<-rpc + [{:keys [metadata id enabled timestamp]}] {:installation-id id - :name (:name metadata) - :timestamp timestamp - :device-type (:deviceType metadata) - :enabled? enabled}) + :name (:name metadata) + :timestamp timestamp + :device-type (:deviceType metadata) + :enabled? enabled}) (fx/defn update-installation {:events [:pairing.callback/set-installation-metadata-success]} [{:keys [db]} installation-id metadata] - {:db (update-in db [:pairing/installations installation-id] + {:db (update-in db + [:pairing/installations installation-id] assoc :installation-id installation-id - :name (:name metadata) - :device-type (:deviceType metadata))}) + :name (:name metadata) + :device-type (:deviceType metadata))}) -(fx/defn handle-installations [{:keys [db]} installations] - {:db (update db :pairing/installations #(reduce - (fn [acc {:keys [id] :as i}] - (update acc id merge (installation<-rpc i))) - % - installations))}) +(fx/defn handle-installations + [{:keys [db]} installations] + {:db (update db + :pairing/installations + #(reduce + (fn [acc {:keys [id] :as i}] + (update acc id merge (installation<-rpc i))) + % + installations))}) (fx/defn load-installations {:events [:pairing.callback/get-our-installations-success]} [{:keys [db]} installations] - {:db (assoc db :pairing/installations (reduce - (fn [acc {:keys [id] :as i}] - (assoc acc id - (installation<-rpc i))) - {} - installations))}) + {:db (assoc db + :pairing/installations + (reduce + (fn [acc {:keys [id] :as i}] + (assoc acc + id + (installation<-rpc i))) + {} + installations))}) (fx/defn enable-installation-success {:events [:pairing.callback/enable-installation-success]} diff --git a/src/status_im/popover/core.cljs b/src/status_im/popover/core.cljs index a61cd0dcde..78be99375b 100644 --- a/src/status_im/popover/core.cljs +++ b/src/status_im/popover/core.cljs @@ -4,7 +4,7 @@ (fx/defn show-popover {:events [:show-popover]} [_ value] - {:show-popover nil + {:show-popover nil ;;TODO refactor popover just start animation on mount :dispatch-later [{:ms 250 :dispatch [:show-popover-db value]}] :dismiss-keyboard nil}) @@ -17,5 +17,5 @@ (fx/defn hide-popover {:events [:hide-popover]} [{:keys [db]}] - {:db (dissoc db :popover/popover) + {:db (dissoc db :popover/popover) :hide-popover nil}) diff --git a/src/status_im/profile/core.cljs b/src/status_im/profile/core.cljs index 8689ecb65b..78a0b7a4d5 100644 --- a/src/status_im/profile/core.cljs +++ b/src/status_im/profile/core.cljs @@ -1,11 +1,11 @@ (ns status-im.profile.core - (:require [re-frame.core :as re-frame] - [status-im.utils.fx :as fx] - [status-im.ui.components.list-selection :as list-selection] - [status-im.utils.universal-links.utils :as universal-links] + (:require [clojure.string :as string] + [re-frame.core :as re-frame] [status-im.multiaccounts.update.core :as multiaccounts.update] + [status-im.ui.components.list-selection :as list-selection] [status-im.ui.components.react :as react] - [clojure.string :as string])) + [status-im.utils.fx :as fx] + [status-im.utils.universal-links.utils :as universal-links])) (re-frame/reg-fx :copy-to-clipboard @@ -30,7 +30,9 @@ (swap! tooltips dissoc interval-id)) (do (re-frame/dispatch [:set-in [:tooltips tooltip-id] opacity]) (when (< 10 cnt) - (swap! tooltips assoc-in [tooltip-id :opacity] (- opacity 0.05))))))) + (swap! tooltips assoc-in + [tooltip-id :opacity] + (- opacity 0.05))))))) 100)] (swap! tooltips assoc tooltip-id {:opacity 1.0 :interval-id interval-id :cnt 0})))))) @@ -56,10 +58,12 @@ {:events [:my-profile/enter-two-random-words]} [{:keys [db]}] (let [{:keys [mnemonic]} (:multiaccount db) - shuffled-mnemonic (shuffle (map-indexed vector (string/split mnemonic #" ")))] - {:db (assoc db :my-profile/seed {:step :first-word - :first-word (first shuffled-mnemonic) - :second-word (second shuffled-mnemonic)})})) + shuffled-mnemonic (shuffle (map-indexed vector (string/split mnemonic #" ")))] + {:db (assoc db + :my-profile/seed + {:step :first-word + :first-word (first shuffled-mnemonic) + :second-word (second shuffled-mnemonic)})})) (fx/defn set-step {:events [:my-profile/set-step]} diff --git a/src/status_im/profile/db.cljs b/src/status_im/profile/db.cljs index 79d85e2d71..423d8c6fc4 100644 --- a/src/status_im/profile/db.cljs +++ b/src/status_im/profile/db.cljs @@ -1,18 +1,22 @@ (ns status-im.profile.db - (:require [clojure.string :as string] - [status-im.chat.constants :as chat.constants] - [cljs.spec.alpha :as spec])) + (:require [cljs.spec.alpha :as spec] + [clojure.string :as string] + [status-im.chat.constants :as chat.constants])) -(defn correct-name? [username] - (when-let [username (some-> username (string/trim))] +(defn correct-name? + [username] + (when-let [username (some-> username + (string/trim))] (every? false? [(string/blank? username) (string/includes? username chat.constants/command-char)]))) -(defn base64-png? [photo-path] +(defn base64-png? + [photo-path] (string/starts-with? photo-path "data:image/png;base64,")) -(defn base64-jpeg? [photo-path] +(defn base64-jpeg? + [photo-path] (string/starts-with? photo-path "data:image/jpeg;base64,")) (spec/def :profile/name correct-name?) \ No newline at end of file diff --git a/src/status_im/qr_scanner/core.cljs b/src/status_im/qr_scanner/core.cljs index 80cf90329c..4a2a1ec350 100644 --- a/src/status_im/qr_scanner/core.cljs +++ b/src/status_im/qr_scanner/core.cljs @@ -1,15 +1,15 @@ (ns status-im.qr-scanner.core - (:require [re-frame.core :as re-frame] - [status-im.i18n.i18n :as i18n] - [status-im.chat.models :as chat] - [status-im.router.core :as router] - [status-im2.navigation.events :as navigation] - [status-im.utils.utils :as utils] - [status-im.ethereum.core :as ethereum] + (:require [clojure.string :as string] + [re-frame.core :as re-frame] [status-im.add-new.db :as new-chat.db] - [status-im.utils.fx :as fx] + [status-im.chat.models :as chat] + [status-im.ethereum.core :as ethereum] [status-im.group-chats.core :as group-chats] - [clojure.string :as string] + [status-im.i18n.i18n :as i18n] + [status-im.router.core :as router] + [status-im.utils.fx :as fx] + [status-im.utils.utils :as utils] + [status-im2.navigation.events :as navigation] [taoensso.timbre :as log])) (fx/defn scan-qr-code @@ -38,22 +38,26 @@ (when-let [handler (:cancel-handler opts)] (fn [] {:dispatch [handler opts]})))) -(fx/defn handle-browse [cofx {:keys [url]}] +(fx/defn handle-browse + [cofx {:keys [url]}] (fx/merge cofx {:browser/show-browser-selection url} (navigation/navigate-back))) -(fx/defn handle-private-chat [{:keys [db] :as cofx} {:keys [chat-id]}] +(fx/defn handle-private-chat + [{:keys [db] :as cofx} {:keys [chat-id]}] (if-not (new-chat.db/own-public-key? db chat-id) (chat/start-chat cofx chat-id nil) {:utils/show-popup {:title (i18n/label :t/unable-to-read-this-code) :content (i18n/label :t/can-not-add-yourself)}})) -(fx/defn handle-public-chat [cofx {:keys [topic]}] +(fx/defn handle-public-chat + [cofx {:keys [topic]}] (when (seq topic) (chat/start-public-chat cofx topic))) -(fx/defn handle-group-chat [cofx params] +(fx/defn handle-group-chat + [cofx params] (group-chats/create-from-link cofx params)) (fx/defn handle-view-profile @@ -61,7 +65,7 @@ (let [own (new-chat.db/own-public-key? db public-key)] (cond (and public-key own) - {:change-tab-fx :profile + {:change-tab-fx :profile :pop-to-root-tab-fx :profile-stack} (and public-key (not own)) @@ -74,7 +78,8 @@ :content (i18n/label :t/ens-name-not-found) :on-dismiss #(re-frame/dispatch [:pop-to-root-tab :chat-stack])}}))) -(fx/defn handle-eip681 [cofx data] +(fx/defn handle-eip681 + [cofx data] (fx/merge cofx {:dispatch [:wallet/parse-eip681-uri-and-resolve-ens data]} (navigation/change-tab :wallet) @@ -109,7 +114,7 @@ (log/info "Unable to find matcher for scanned value" {:type type :event ::match-scanned-value}) - {:dispatch [:navigate-back] + {:dispatch [:navigate-back] :utils/show-popup {:title (i18n/label :t/unable-to-read-this-code) :on-dismiss #(re-frame/dispatch [:pop-to-root-tab :chat-stack])}}))) diff --git a/src/status_im/react_native/resources.cljs b/src/status_im/react_native/resources.cljs index 47a79c0188..f3394a516b 100644 --- a/src/status_im/react_native/resources.cljs +++ b/src/status_im/react_native/resources.cljs @@ -65,22 +65,29 @@ :user-picture-male4 (js/require "../resources/images/mock/user_picture_male4.png") :user-picture-male5 (js/require "../resources/images/mock/user_picture_male5.png")}) -(defn get-theme-image [k] +(defn get-theme-image + [k] (get ui (when (colors/dark?) (keyword (str (name k) "-dark"))) (get ui k))) (def loaded-images (atom {})) -(defn get-image [k] +(defn get-image + [k] (if (contains? @loaded-images k) (get @loaded-images k) - (get (swap! loaded-images assoc k - (get ui k)) k))) + (get (swap! loaded-images assoc + k + (get ui k)) + k))) -(defn get-mock-image [k] +(defn get-mock-image + [k] (if (contains? @loaded-images k) (get @loaded-images k) - (get (swap! loaded-images assoc k - (get mock-images k)) k))) + (get (swap! loaded-images assoc + k + (get mock-images k)) + k))) (def reactions-old {:love (js/require "../resources/images/reactions/love.png") diff --git a/src/status_im/router/core.cljs b/src/status_im/router/core.cljs index 486f63fb78..18965404ce 100644 --- a/src/status_im/router/core.cljs +++ b/src/status_im/router/core.cljs @@ -4,16 +4,16 @@ [re-frame.core :as re-frame] [status-im.add-new.db :as public-chat.db] [status-im.chat.models :as chat.models] + [status-im.constants :as constants] [status-im.ethereum.core :as ethereum] [status-im.ethereum.eip681 :as eip681] [status-im.ethereum.ens :as ens] [status-im.ethereum.stateofus :as stateofus] [status-im.utils.db :as utils.db] [status-im.utils.http :as http] - [utils.security.core :as security] [status-im.utils.wallet-connect :as wallet-connect] - [status-im.constants :as constants] - [taoensso.timbre :as log])) + [taoensso.timbre :as log] + [utils.security.core :as security])) (def ethereum-scheme "ethereum:") @@ -29,38 +29,44 @@ (def handled-schemes (set (into uri-schemes web-urls))) -(def browser-extractor {[#"(.*)" :domain] {"" :browser - "/" :browser}}) +(def browser-extractor + {[#"(.*)" :domain] {"" :browser + "/" :browser}}) -(def group-chat-extractor {[#"(.*)" :params] {"" :group-chat - "/" :group-chat}}) +(def group-chat-extractor + {[#"(.*)" :params] {"" :group-chat + "/" :group-chat}}) -(def eip-extractor {#{[:prefix "-" :address] - [:address]} - {#{["@" :chain-id] ""} - {#{["/" :function] ""} - :ethereum}}}) +(def eip-extractor + {#{[:prefix "-" :address] + [:address]} + {#{["@" :chain-id] ""} + {#{["/" :function] ""} + :ethereum}}}) -(def routes ["" {handled-schemes {["" :chat-id] :public-chat - "chat" {["/public/" :chat-id] :public-chat} - "b/" browser-extractor - "browser/" browser-extractor - ["p/" :chat-id] :private-chat - ["cr/" :community-id] :community-requests - ["c/" :community-id] :community - ["cc/" :chat-id] :community-chat - "g/" group-chat-extractor - ["wallet/" :account] :wallet-account - ["u/" :user-id] :user - ["user/" :user-id] :user} - ethereum-scheme eip-extractor}]) +(def routes + ["" + {handled-schemes {["" :chat-id] :public-chat + "chat" {["/public/" :chat-id] :public-chat} + "b/" browser-extractor + "browser/" browser-extractor + ["p/" :chat-id] :private-chat + ["cr/" :community-id] :community-requests + ["c/" :community-id] :community + ["cc/" :chat-id] :community-chat + "g/" group-chat-extractor + ["wallet/" :account] :wallet-account + ["u/" :user-id] :user + ["user/" :user-id] :user} + ethereum-scheme eip-extractor}]) (defn parse-query-params [url] (let [url (goog.Uri. url)] (http/query->map (.getQuery url)))) -(defn match-uri [uri] +(defn match-uri + [uri] (assoc (bidi/match-route routes uri) :uri uri :query-params (parse-query-params uri))) (defn match-contact-async @@ -73,7 +79,9 @@ :public-key user-id :ens-name ens-name}) - (and (not valid-key) (string? user-id) (not (string/blank? user-id)) + (and (not valid-key) + (string? user-id) + (not (string/blank? user-id)) (not= user-id "0x")) (let [chain-id (ethereum/chain-keyword->chain-id chain) ens-name (stateofus/ens-name-parse user-id) @@ -84,39 +92,47 @@ (callback {:type :contact :error :not-found})))) -(defn match-public-chat [{:keys [chat-id]}] +(defn match-public-chat + [{:keys [chat-id]}] (if (public-chat.db/valid-topic? chat-id) {:type :public-chat :topic chat-id} {:type :public-chat :error :invalid-topic})) -(defn match-group-chat [chats {:strs [a a1 a2]}] +(defn match-group-chat + [chats {:strs [a a1 a2]}] (let [[admin-pk encoded-chat-name chat-id] [a a1 a2] - chat-id-parts (when (not (string/blank? chat-id)) (string/split chat-id #"-")) - chat-name (when (not (string/blank? encoded-chat-name)) (js/decodeURI encoded-chat-name))] - (cond (and (not (string/blank? chat-id)) (not (string/blank? admin-pk)) (not (string/blank? chat-name)) - (> (count chat-id-parts) 1) - (not (string/blank? (first chat-id-parts))) - (utils.db/valid-public-key? admin-pk) - (utils.db/valid-public-key? (last chat-id-parts))) - {:type :group-chat - :chat-id chat-id - :invitation-admin admin-pk - :chat-name chat-name} + chat-id-parts (when (not (string/blank? chat-id)) + (string/split chat-id #"-")) + chat-name (when (not (string/blank? encoded-chat-name)) + (js/decodeURI encoded-chat-name))] + (cond + (and (not (string/blank? chat-id)) + (not (string/blank? admin-pk)) + (not (string/blank? chat-name)) + (> (count chat-id-parts) 1) + (not (string/blank? (first chat-id-parts))) + (utils.db/valid-public-key? admin-pk) + (utils.db/valid-public-key? (last chat-id-parts))) + {:type :group-chat + :chat-id chat-id + :invitation-admin admin-pk + :chat-name chat-name} - (and (not (string/blank? chat-id)) - (chat.models/group-chat? (get chats chat-id))) - (let [{:keys [chat-name invitation-admin]} (get chats chat-id)] - {:type :group-chat - :chat-id chat-id - :invitation-admin invitation-admin - :chat-name chat-name}) + (and (not (string/blank? chat-id)) + (chat.models/group-chat? (get chats chat-id))) + (let [{:keys [chat-name invitation-admin]} (get chats chat-id)] + {:type :group-chat + :chat-id chat-id + :invitation-admin invitation-admin + :chat-name chat-name}) - :else - {:error :invalid-group-chat-data}))) + :else + {:error :invalid-group-chat-data}))) -(defn match-private-chat-async [chain {:keys [chat-id]} cb] +(defn match-private-chat-async + [chain {:keys [chat-id]} cb] (match-contact-async chain {:user-id chat-id} (fn [{:keys [public-key]}] @@ -126,7 +142,8 @@ (cb {:type :private-chat :error :invalid-chat-id}))))) -(defn match-browser [uri {:keys [domain]}] +(defn match-browser + [uri {:keys [domain]}] ;; NOTE: We rebuild domain from original URI and matched domain (let [domain (->> (string/split uri domain) second @@ -137,7 +154,8 @@ {:type :browser :error :unsafe-link}))) -(defn match-browser-string [domain] +(defn match-browser-string + [domain] (if (security/safe-link? domain) {:type :browser :url domain} @@ -145,7 +163,8 @@ :error :unsafe-link})) ;; NOTE(Ferossgp): Better to handle eip681 also with router instead of regexp. -(defn match-eip681 [uri] +(defn match-eip681 + [uri] (if-let [message (eip681/parse-uri uri)] (let [{:keys [paths ens-names]} (reduce (fn [acc path] @@ -171,14 +190,17 @@ :uri uri :error :cannot-parse})) -(defn address->eip681 [address] +(defn address->eip681 + [address] (match-eip681 (str ethereum-scheme address))) -(defn match-wallet-account [{:keys [account]}] +(defn match-wallet-account + [{:keys [account]}] {:type :wallet-account :account (when account (string/lower-case account))}) -(defn handle-uri [chain chats uri cb] +(defn handle-uri + [chain chats uri cb] (let [{:keys [handler route-params query-params]} (match-uri uri)] (log/info "[router] uri " uri " matched " handler " with " route-params) (cond diff --git a/src/status_im/router/core_test.cljs b/src/status_im/router/core_test.cljs index cd8fee8ad2..314925b475 100644 --- a/src/status_im/router/core_test.cljs +++ b/src/status_im/router/core_test.cljs @@ -1,9 +1,11 @@ (ns status-im.router.core-test - (:require [status-im.router.core :as router] - [cljs.test :refer [deftest are]])) + (:require [cljs.test :refer [are deftest]] + [status-im.router.core :as router])) -(def public-key "0x04fbce10971e1cd7253b98c7b7e54de3729ca57ce41a2bfb0d1c4e0a26f72c4b6913c3487fa1b4bb86125770f1743fb4459da05c1cbe31d938814cfaf36e252073") -(def chat-id "59eb36e6-9d4d-4724-9d3a-8a3cdc5e8a8e-0x04f383daedc92a66add4c90d8884004ef826cba113183a0052703c8c77fed1522f88f44550498d20679af98907627059a295e43212a1cd3c1f21a157704d608c13") +(def public-key + "0x04fbce10971e1cd7253b98c7b7e54de3729ca57ce41a2bfb0d1c4e0a26f72c4b6913c3487fa1b4bb86125770f1743fb4459da05c1cbe31d938814cfaf36e252073") +(def chat-id + "59eb36e6-9d4d-4724-9d3a-8a3cdc5e8a8e-0x04f383daedc92a66add4c90d8884004ef826cba113183a0052703c8c77fed1522f88f44550498d20679af98907627059a295e43212a1cd3c1f21a157704d608c13") (def chat-name-url "Test%20group%20chat") (def chat-name "Test group chat") @@ -11,71 +13,94 @@ (are [uri expected] (= (cond-> (router/match-uri uri) (< (count expected) 3) (assoc :query-params nil)) - {:handler (first expected) + {:handler (first expected) :route-params (second expected) :query-params (when (= 3 (count expected)) (last expected)) - :uri uri}) + :uri uri}) - "status-im://status" [:public-chat {:chat-id "status"}] + "status-im://status" + [:public-chat {:chat-id "status"}] - "status-im://u/statuse2e" [:user {:user-id "statuse2e"}] + "status-im://u/statuse2e" + [:user {:user-id "statuse2e"}] - (str "status-im://user/" public-key) [:user {:user-id public-key}] + (str "status-im://user/" public-key) + [:user {:user-id public-key}] - "status-im://b/www.cryptokitties.co" [:browser {:domain "www.cryptokitties.c"}] + "status-im://b/www.cryptokitties.co" + [:browser {:domain "www.cryptokitties.c"}] - (str "status-im://g/args?a=" public-key "&a1=" chat-name-url "&a2=" chat-id) + (str "status-im://g/args?a=" public-key "&a1=" chat-name-url "&a2=" chat-id) [:group-chat {:params "arg"} {"a" public-key "a1" chat-name "a2" chat-id}] - (str "https://join.status.im/g/args?a=" public-key "&a1=" chat-name-url "&a2=" chat-id) + (str "https://join.status.im/g/args?a=" public-key "&a1=" chat-name-url "&a2=" chat-id) [:group-chat {:params "arg"} {"a" public-key "a1" chat-name "a2" chat-id}] - "https://join.status.im/status" [:public-chat {:chat-id "status"}] + "https://join.status.im/status" + [:public-chat {:chat-id "status"}] - "https://join.status.im/u/statuse2e" [:user {:user-id "statuse2e"}] + "https://join.status.im/u/statuse2e" + [:user {:user-id "statuse2e"}] - (str "https://join.status.im/user/" public-key) [:user {:user-id public-key}] + (str "https://join.status.im/user/" public-key) + [:user {:user-id public-key}] - ;; Last char removed by: https://github.com/juxt/bidi/issues/104 - "https://join.status.im/b/www.cryptokitties.co" [:browser {:domain "www.cryptokitties.c"}] + ;; Last char removed by: https://github.com/juxt/bidi/issues/104 + "https://join.status.im/b/www.cryptokitties.co" + [:browser {:domain "www.cryptokitties.c"}] - "https://join.status.im/b/https://www.google.com/" [:browser {:domain "https://www.google.co"}] + "https://join.status.im/b/https://www.google.com/" + [:browser {:domain "https://www.google.co"}] - "ethereum:0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7" [:ethereum {:address "0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7"}] + "ethereum:0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7" + [:ethereum {:address "0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7"}] - "ethereum:pay-0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7" [:ethereum {:prefix "pay" - :address "0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7"}] - "ethereum:foo-0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7" [:ethereum {:prefix "foo" - :address "0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7"}] + "ethereum:pay-0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7" + [:ethereum + {:prefix "pay" + :address "0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7"}] + "ethereum:foo-0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7" + [:ethereum + {:prefix "foo" + :address "0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7"}] - ;; FIXME: Should handle only first line - "ethereum:foo-state-of-us.eth" [:ethereum {:prefix "foo-state-of" - :address "us.eth"}] + ;; FIXME: Should handle only first line + "ethereum:foo-state-of-us.eth" + [:ethereum + {:prefix "foo-state-of" + :address "us.eth"}] - "ethereum:0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7@42" [:ethereum {:address "0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7" - :chain-id "42"}] + "ethereum:0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7@42" + [:ethereum + {:address "0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7" + :chain-id "42"}] - "ethereum:0x0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7/transfer?address=0x12345&uint256=1" [:ethereum {:address "0x0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7" - :function "transfer"}] + "ethereum:0x0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7/transfer?address=0x12345&uint256=1" + [:ethereum + {:address "0x0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7" + :function "transfer"}] - "ethereum:0x0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7?value=2.014e18&gas=10&gasLimit=21000&gasPrice=50" [:ethereum {:address "0x0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7"}] + "ethereum:0x0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7?value=2.014e18&gas=10&gasLimit=21000&gasPrice=50" + [:ethereum {:address "0x0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7"}] - "ethereum:0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7@1/transfer?uint256=1" [:ethereum {:address "0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7" - :chain-id "1" - :function "transfer"}])) + "ethereum:0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7@1/transfer?uint256=1" + [:ethereum + {:address "0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7" + :chain-id "1" + :function "transfer"}])) (def error {:error :invalid-group-chat-data}) (deftest match-group-chat-query (are [query-params expected] (= (router/match-group-chat {} query-params) expected) - nil error - {} error - {"b" public-key} error - {"a" public-key "a1" chat-name} error - {"a" "0x00ceded" "a1" chat-name "a2" chat-id} error - {"a" public-key "a1" chat-name "a2" public-key} error - {"a" public-key "a1" chat-name "a2" chat-id} {:type :group-chat - :chat-id chat-id - :invitation-admin public-key - :chat-name chat-name})) + nil error + {} error + {"b" public-key} error + {"a" public-key "a1" chat-name} error + {"a" "0x00ceded" "a1" chat-name "a2" chat-id} error + {"a" public-key "a1" chat-name "a2" public-key} error + {"a" public-key "a1" chat-name "a2" chat-id} {:type :group-chat + :chat-id chat-id + :invitation-admin public-key + :chat-name chat-name})) diff --git a/src/status_im/search/core_test.cljs b/src/status_im/search/core_test.cljs index cf8cf4137c..f0ef9ce155 100644 --- a/src/status_im/search/core_test.cljs +++ b/src/status_im/search/core_test.cljs @@ -2,23 +2,24 @@ (:require [cljs.test :refer-macros [deftest testing is]] [status-im2.subs.search :as search.subs])) -(defn extract-chat-attributes [chat] +(defn extract-chat-attributes + [chat] (let [{:keys [name alias tags]} (val chat)] (into [name alias] tags))) (deftest filter-chats - (let [chats {:chat-1 {:name "name1" + (let [chats {:chat-1 {:name "name1" :alias "alias1" - :tags #{"tag1"}} - :chat-2 {:name "name2" + :tags #{"tag1"}} + :chat-2 {:name "name2" :alias "alias2" - :tags #{"tag2" "tag3"}} - :chat-3 {:name "name3" + :tags #{"tag2" "tag3"}} + :chat-3 {:name "name3" :alias "alias3" - :tags #{}} - :chat-4 {:name "name4" + :tags #{}} + :chat-4 {:name "name4" :alias "alias4" - :tags #{"tag4"}}}] + :tags #{"tag4"}}}] (testing "no search filter" (is (= (count chats) (count (search.subs/apply-filter "" diff --git a/src/status_im/signals/core.cljs b/src/status_im/signals/core.cljs index 0b773dd56d..244e5148f3 100644 --- a/src/status_im/signals/core.cljs +++ b/src/status_im/signals/core.cljs @@ -1,35 +1,37 @@ (ns status-im.signals.core - (:require [status-im.ethereum.subscriptions :as ethereum.subscriptions] + (:require [status-im.chat.models.link-preview :as link.preview] + [status-im.chat.models.message :as models.message] + [status-im.ethereum.subscriptions :as ethereum.subscriptions] [status-im.i18n.i18n :as i18n] [status-im.mailserver.core :as mailserver] [status-im.multiaccounts.login.core :as login] - [status-im.transport.message.core :as transport.message] [status-im.notifications.local :as local-notifications] - [status-im.chat.models.message :as models.message] - [status-im.chat.models.link-preview :as link.preview] - [status-im.visibility-status-updates.core :as visibility-status-updates] + [status-im.transport.message.core :as transport.message] [status-im.utils.fx :as fx] + [status-im.visibility-status-updates.core :as visibility-status-updates] [taoensso.timbre :as log])) (fx/defn status-node-started [{db :db :as cofx} {:keys [error]}] (log/debug "[signals] status-node-started" - "error" error) + "error" + error) (if error - (cond-> {:db (-> db - (update :multiaccounts/login dissoc :processing) - (assoc-in [:multiaccounts/login :error] - ;; NOTE: the only currently known error is - ;; "file is not a database" which occurs - ;; when the user inputs the wrong password - ;; if that is the error that is found - ;; we show the i18n label for wrong password - ;; to the user - ;; in case of an unknown error we show the - ;; error - (if (= error "file is not a database") - (i18n/label :t/wrong-password) - error)))} + (cond-> + {:db (-> db + (update :multiaccounts/login dissoc :processing) + (assoc-in [:multiaccounts/login :error] + ;; NOTE: the only currently known error is + ;; "file is not a database" which occurs + ;; when the user inputs the wrong password + ;; if that is the error that is found + ;; we show the i18n label for wrong password + ;; to the user + ;; in case of an unknown error we show the + ;; error + (if (= error "file is not a database") + (i18n/label :t/wrong-password) + error)))} (= (:view-id db) :progress) (assoc :dispatch [:navigate-to :login])) (login/multiaccount-login-success cofx))) @@ -41,16 +43,18 @@ (fx/merge cofx {:db (assoc db :peers-summary peers-summary - :peers-count peers-count)} + :peers-count peers-count)} (visibility-status-updates/peers-summary-change peers-count)))) (fx/defn wakuv2-peer-stats [{:keys [db]} peer-stats] (let [previous-stats (:peer-stats db)] - {:db (assoc db :peer-stats peer-stats + {:db (assoc db + :peer-stats peer-stats :peers-count (count (:peers peer-stats)))})) -(defn handle-local-pairing-signals [signal-type] +(defn handle-local-pairing-signals + [signal-type] (log/info "local pairing signal received" {:signal-type signal-type})) @@ -60,26 +64,51 @@ ;; We only convert to clojure when strictly necessary or we know it ;; won't impact performance, as it is a fairly costly operation on large-ish ;; data structures - (let [^js data (.parse js/JSON event-str) + (let [^js data (.parse js/JSON event-str) ^js event-js (.-event data) - type (.-type data)] + type (.-type data)] (case type - "node.login" (status-node-started cofx (js->clj event-js :keywordize-keys true)) - "backup.performed" {:db (assoc-in db [:multiaccount :last-backup] (.-lastBackup event-js))} - "envelope.sent" (transport.message/update-envelopes-status cofx (:ids (js->clj event-js :keywordize-keys true)) :sent) - "envelope.expired" (transport.message/update-envelopes-status cofx (:ids (js->clj event-js :keywordize-keys true)) :not-sent) - "message.delivered" (let [{:keys [chatID messageID]} (js->clj event-js :keywordize-keys true)] - (models.message/update-db-message-status cofx chatID messageID :delivered)) - "mailserver.changed" (mailserver/handle-mailserver-changed cofx (.-id event-js)) - "mailserver.available" (mailserver/handle-mailserver-available cofx (.-id event-js)) - "mailserver.not.working" (mailserver/handle-mailserver-not-working cofx) - "discovery.summary" (summary cofx (js->clj event-js :keywordize-keys true)) - "mediaserver.started" {:db (assoc db :mediaserver/port (.-port event-js))} - "wakuv2.peerstats" (wakuv2-peer-stats cofx (js->clj event-js :keywordize-keys true)) - "messages.new" (transport.message/sanitize-messages-and-process-response cofx event-js true) - "wallet" (ethereum.subscriptions/new-wallet-event cofx (js->clj event-js :keywordize-keys true)) - "local-notifications" (local-notifications/process cofx (js->clj event-js :keywordize-keys true)) - "community.found" (link.preview/cache-community-preview-data (js->clj event-js :keywordize-keys true)) - "status.updates.timedout" (visibility-status-updates/handle-visibility-status-updates cofx (js->clj event-js :keywordize-keys true)) - "localPairing" (handle-local-pairing-signals event-str) + "node.login" (status-node-started cofx (js->clj event-js :keywordize-keys true)) + "backup.performed" {:db (assoc-in db [:multiaccount :last-backup] (.-lastBackup event-js))} + "envelope.sent" (transport.message/update-envelopes-status cofx + (:ids + (js->clj event-js + :keywordize-keys + true)) + :sent) + "envelope.expired" (transport.message/update-envelopes-status cofx + (:ids + (js->clj event-js + :keywordize-keys + true)) + :not-sent) + "message.delivered" (let [{:keys [chatID messageID]} (js->clj event-js + :keywordize-keys + true)] + (models.message/update-db-message-status cofx + chatID + messageID + :delivered)) + "mailserver.changed" (mailserver/handle-mailserver-changed cofx (.-id event-js)) + "mailserver.available" (mailserver/handle-mailserver-available cofx (.-id event-js)) + "mailserver.not.working" (mailserver/handle-mailserver-not-working cofx) + "discovery.summary" (summary cofx (js->clj event-js :keywordize-keys true)) + "mediaserver.started" {:db (assoc db :mediaserver/port (.-port event-js))} + "wakuv2.peerstats" (wakuv2-peer-stats cofx (js->clj event-js :keywordize-keys true)) + "messages.new" (transport.message/sanitize-messages-and-process-response cofx + event-js + true) + "wallet" (ethereum.subscriptions/new-wallet-event cofx + (js->clj event-js + :keywordize-keys + true)) + "local-notifications" (local-notifications/process cofx + (js->clj event-js :keywordize-keys true)) + "community.found" (link.preview/cache-community-preview-data (js->clj event-js + :keywordize-keys + true)) + "status.updates.timedout" (visibility-status-updates/handle-visibility-status-updates + cofx + (js->clj event-js :keywordize-keys true)) + "localPairing" (handle-local-pairing-signals event-str) (log/debug "Event " type " not handled")))) diff --git a/src/status_im/signing/core.cljs b/src/status_im/signing/core.cljs index 3089fd630a..e334ba8396 100644 --- a/src/status_im/signing/core.cljs +++ b/src/status_im/signing/core.cljs @@ -1,27 +1,27 @@ (ns status-im.signing.core - (:require [clojure.string :as string] + (:require [clojure.set :as clojure.set] + [clojure.string :as string] [re-frame.core :as re-frame] [status-im.constants :as constants] [status-im.ethereum.core :as ethereum] [status-im.ethereum.eip55 :as eip55] [status-im.ethereum.json-rpc :as json-rpc] [status-im.ethereum.tokens :as tokens] - [status-im.keycard.common :as keycard.common] [status-im.i18n.i18n :as i18n] [status-im.keycard.card :as keycard.card] + [status-im.keycard.common :as keycard.common] [status-im.native-module.core :as status] + [status-im.signing.eip1559 :as eip1559] [status-im.signing.keycard :as signing.keycard] [status-im.utils.fx :as fx] [status-im.utils.hex :as utils.hex] [status-im.utils.money :as money] - [utils.security.core :as security] [status-im.utils.types :as types] [status-im.utils.utils :as utils] - [status-im.wallet.prices :as prices] [status-im.wallet.core :as wallet] + [status-im.wallet.prices :as prices] [taoensso.timbre :as log] - [clojure.set :as clojure.set] - [status-im.signing.eip1559 :as eip1559])) + [utils.security.core :as security])) (re-frame/reg-fx :signing/send-transaction-fx @@ -59,7 +59,8 @@ (status/sign-typed-data-v4 data account hashed-password on-completed) (status/sign-typed-data data account hashed-password on-completed)))) -(defn get-contact [db to] +(defn get-contact + [db to] (let [to (utils.hex/normalize-hex to)] (or (get-in db [:contacts/contacts to]) @@ -69,30 +70,33 @@ {:events [:signing.ui/password-is-changed]} [{db :db} password] (let [unmasked-pass (security/safe-unmask-data password)] - {:db (update db :signing/sign assoc - :password password - :error nil - :enabled? (and unmasked-pass (> (count unmasked-pass) 5)))})) + {:db (update db + :signing/sign assoc + :password password + :error nil + :enabled? (and unmasked-pass (> (count unmasked-pass) 5)))})) (fx/defn sign-message [{{:signing/keys [sign tx] :as db} :db}] (let [{{:keys [data typed? from v4]} :message} tx - {:keys [in-progress? password]} sign - from (or from (ethereum/default-address db)) - hashed-password (ethereum/sha3 (security/safe-unmask-data password))] + {:keys [in-progress? password]} sign + from (or from (ethereum/default-address db)) + hashed-password (ethereum/sha3 (security/safe-unmask-data password))] (when-not in-progress? (merge {:db (update db :signing/sign assoc :error nil :in-progress? true)} (if typed? - {:signing.fx/sign-typed-data {:v4 v4 - :data data - :account from + {:signing.fx/sign-typed-data {:v4 v4 + :data data + :account from :hashed-password hashed-password - :on-completed #(re-frame/dispatch [:signing/sign-message-completed %])}} + :on-completed #(re-frame/dispatch + [:signing/sign-message-completed %])}} {:signing.fx/sign-message {:params {:data data :password hashed-password :account from} - :on-completed #(re-frame/dispatch [:signing/sign-message-completed %])}}))))) + :on-completed #(re-frame/dispatch [:signing/sign-message-completed + %])}}))))) (fx/defn send-transaction {:events [:signing.ui/sign-is-pressed]} @@ -110,57 +114,62 @@ (when nonce {:nonce (str "0x" (status/number-to-hex nonce))}) (when maxPriorityFeePerGas - {:maxPriorityFeePerGas (str "0x" (status/number-to-hex - (js/parseInt maxPriorityFeePerGas)))}) + {:maxPriorityFeePerGas (str "0x" + (status/number-to-hex + (js/parseInt maxPriorityFeePerGas)))}) (when maxFeePerGas - {:maxFeePerGas (str "0x" (status/number-to-hex - (js/parseInt maxFeePerGas)))}))] + {:maxFeePerGas (str "0x" + (status/number-to-hex + (js/parseInt maxFeePerGas)))}))] (when-not in-progress? {:db (update db :signing/sign assoc :error nil :in-progress? true) - :signing/send-transaction-fx {:tx-obj tx-obj-to-send + :signing/send-transaction-fx {:tx-obj tx-obj-to-send :hashed-password hashed-password - :cb #(re-frame/dispatch [:signing/transaction-completed % tx-obj-to-send hashed-password])}}))))) + :cb #(re-frame/dispatch + [:signing/transaction-completed % + tx-obj-to-send hashed-password])}}))))) (fx/defn prepare-unconfirmed-transaction [{:keys [db now]} new-tx-hash {:keys [value gasPrice maxFeePerGas maxPriorityFeePerGas gas data to from hash]} symbol amount] - (let [token (tokens/symbol->token (:wallet/all-tokens db) symbol) - from (eip55/address->checksum from) + (let [token (tokens/symbol->token (:wallet/all-tokens db) symbol) + from (eip55/address->checksum from) ;;if there is a hash in the tx object that means we resending transaction old-tx-hash hash - gas-price (money/to-fixed (money/bignumber gasPrice)) - gas-limit (money/to-fixed (money/bignumber gas)) - tx {:timestamp now - :to to - :from from - :type :pending - :hash new-tx-hash - :data data - :token token - :symbol symbol - :value (if token - (money/to-fixed (money/unit->token amount (:decimals token))) - (money/to-fixed (money/bignumber value))) - :gas-price gas-price - :fee-cap maxFeePerGas - :tip-cap maxPriorityFeePerGas - :gas-limit gas-limit}] + gas-price (money/to-fixed (money/bignumber gasPrice)) + gas-limit (money/to-fixed (money/bignumber gas)) + tx {:timestamp now + :to to + :from from + :type :pending + :hash new-tx-hash + :data data + :token token + :symbol symbol + :value (if token + (money/to-fixed (money/unit->token amount (:decimals token))) + (money/to-fixed (money/bignumber value))) + :gas-price gas-price + :fee-cap maxFeePerGas + :tip-cap maxPriorityFeePerGas + :gas-limit gas-limit}] (log/info "[signing] prepare-unconfirmed-transaction" tx) - {:db (-> db - ;;remove old transaction, because we replace it with the new one - (update-in [:wallet :accounts from :transactions] dissoc old-tx-hash) - (assoc-in [:wallet :accounts from :transactions new-tx-hash] tx)) - ::json-rpc/call [{:method "wallet_storePendingTransaction" - :params [(-> tx - (dissoc :gas-price :gas-limit) - (assoc :gasPrice - (money/to-fixed (money/bignumber gasPrice)) - :gasLimit (money/to-fixed (money/bignumber gas))) - clj->js)] + {:db (-> db + ;;remove old transaction, because we replace it with the new one + (update-in [:wallet :accounts from :transactions] dissoc old-tx-hash) + (assoc-in [:wallet :accounts from :transactions new-tx-hash] tx)) + ::json-rpc/call [{:method "wallet_storePendingTransaction" + :params [(-> tx + (dissoc :gas-price :gas-limit) + (assoc :gasPrice + (money/to-fixed (money/bignumber gasPrice)) + :gasLimit (money/to-fixed (money/bignumber gas))) + clj->js)] :on-success #(log/info "pending transfer is saved") - :on-error #(log/info "pending transfer was not saved" %)}]})) + :on-error #(log/info "pending transfer was not saved" %)}]})) -(defn get-method-type [data] +(defn get-method-type + [data] (cond (string/starts-with? data constants/method-id-transfer) :transfer @@ -169,13 +178,16 @@ (string/starts-with? data constants/method-id-approve-and-call) :approve-and-call)) -(defn get-transfer-token [db to data] +(defn get-transfer-token + [db to data] (let [{:keys [symbol decimals] :as token} (tokens/address->token (:wallet/all-tokens db) to)] (when (and token data (string? data)) (when-let [type (get-method-type data)] (let [[address value _] (status/decode-parameters (str "0x" (subs data 10)) - (if (= type :approve-and-call) ["address" "uint256" "bytes"] ["address" "uint256"]))] + (if (= type :approve-and-call) + ["address" "uint256" "bytes"] + ["address" "uint256"]))] (when (and address value) {:to address :contact (get-contact db address) @@ -186,7 +198,8 @@ :token token :symbol symbol})))))) -(defn parse-tx-obj [db {:keys [from to value data cancel? hash]}] +(defn parse-tx-obj + [db {:keys [from to value data cancel? hash]}] (merge {:from {:address from} :cancel? cancel? :hash hash} @@ -196,20 +209,23 @@ eth-amount (when eth-value (money/to-fixed (money/wei->ether eth-value))) token (get-transfer-token db to data)] (cond - (and eth-amount (or (not (.equals ^js (money/bignumber 0) ^js eth-amount)) (nil? data))) + (and eth-amount (or (not (.equals ^js (money/bignumber 0) ^js eth-amount)) (nil? data))) {:to to :contact (get-contact db to) :symbol :ETH :value value :amount (str eth-amount) - :token (tokens/asset-for (:wallet/all-tokens db) (ethereum/get-current-network db) :ETH)} + :token (tokens/asset-for (:wallet/all-tokens db) + (ethereum/get-current-network db) + :ETH)} (not (nil? token)) token :else {:to to :contact {:address (ethereum/normalized-hex to)}}))))) -(defn prepare-tx [db {{:keys [data gas gasPrice maxFeePerGas maxPriorityFeePerGas] :as tx-obj} :tx-obj :as tx}] +(defn prepare-tx + [db {{:keys [data gas gasPrice maxFeePerGas maxPriorityFeePerGas] :as tx-obj} :tx-obj :as tx}] (merge tx (parse-tx-obj db tx-obj) @@ -221,45 +237,52 @@ :maxPriorityFeePerGas (when maxPriorityFeePerGas (money/bignumber maxPriorityFeePerGas))})) -(fx/defn show-sign [{:keys [db] :as cofx}] +(fx/defn show-sign + [{:keys [db] :as cofx}] (let [{:signing/keys [queue]} db - {{:keys [gas gasPrice maxFeePerGas] :as tx-obj} :tx-obj {:keys [data typed? pinless?] :as message} :message :as tx} (last queue) + {{:keys [gas gasPrice maxFeePerGas] :as tx-obj} :tx-obj + {:keys [data typed? pinless?] :as message} :message + :as tx} + (last queue) keycard-multiaccount? (boolean (get-in db [:multiaccount :keycard-pairing])) wallet-set-up-passed? (get-in db [:multiaccount :wallet-set-up-passed?])] (if message (fx/merge cofx - {:db (assoc db - :signing/queue (drop-last queue) - :signing/tx tx - :signing/sign {:type (cond pinless? :pinless - keycard-multiaccount? :keycard - :else :password) - :formatted-data (if typed? - (types/js->pretty-json (types/json->js data)) - (ethereum/hex->text data)) - :keycard-step (when pinless? :connect)}) + {:db (assoc db + :signing/queue (drop-last queue) + :signing/tx tx + :signing/sign {:type (cond pinless? :pinless + keycard-multiaccount? :keycard + :else :password) + :formatted-data (if typed? + (types/js->pretty-json + (types/json->js data)) + (ethereum/hex->text data)) + :keycard-step (when pinless? :connect)}) :show-signing-sheet nil} #(when-not wallet-set-up-passed? {:dispatch-later [{:dispatch [:show-popover {:view :signing-phrase}] :ms 200}]}) (when pinless? (keycard.card/start-nfc {:on-success #(re-frame/dispatch [:keycard.callback/start-nfc-success]) - :on-failure #(re-frame/dispatch [:keycard.callback/start-nfc-failure])}) - (signing.keycard/hash-message {:data data - :typed? true - :on-completed #(re-frame/dispatch [:keycard/store-hash-and-sign-typed %])}))) + :on-failure #(re-frame/dispatch + [:keycard.callback/start-nfc-failure])}) + (signing.keycard/hash-message + {:data data + :typed? true + :on-completed #(re-frame/dispatch [:keycard/store-hash-and-sign-typed %])}))) (fx/merge cofx - {:db (assoc db - :signing/queue (drop-last queue) - :signing/tx (prepare-tx db tx)) + {:db (assoc db + :signing/queue (drop-last queue) + :signing/tx (prepare-tx db tx)) :show-signing-sheet nil - :dismiss-keyboard nil} + :dismiss-keyboard nil} #(when-not wallet-set-up-passed? {:dispatch-later [{:dispatch [:show-popover {:view :signing-phrase}] :ms 200}]}) (prices/update-prices) #(when-not gas - {:db (assoc-in (:db %) [:signing/edit-fee :gas-loading?] true) + {:db (assoc-in (:db %) [:signing/edit-fee :gas-loading?] true) :signing/update-estimated-gas {:obj (-> tx-obj (dissoc :gasPrice) (update :maxFeePerGas @@ -269,9 +292,9 @@ (money/mul 2) money/to-hex)))) :success-event :signing/update-estimated-gas-success - :error-event :signing/update-estimated-gas-error}}) + :error-event :signing/update-estimated-gas-error}}) (fn [cofx] - {:db (assoc-in (:db cofx) [:signing/edit-fee :gas-price-loading?] true) + {:db (assoc-in (:db cofx) [:signing/edit-fee :gas-price-loading?] true) :signing/update-gas-price {:success-callback #(re-frame/dispatch [:wallet.send/update-gas-price-success :signing/tx % tx-obj]) @@ -279,7 +302,8 @@ :network-id (get-in (ethereum/current-network db) [:config :NetworkId])}}))))) -(fx/defn check-queue [{:keys [db] :as cofx}] +(fx/defn check-queue + [{:keys [db] :as cofx}] (let [{:signing/keys [tx queue]} db] (when (and (not tx) (seq queue)) (show-sign cofx)))) @@ -287,12 +311,12 @@ (fx/defn send-transaction-message {:events [:sign/send-transaction-message]} [cofx chat-id value contract transaction-hash signature] - {::json-rpc/call [{:method "wakuext_sendTransaction" + {::json-rpc/call [{:method "wakuext_sendTransaction" ;; We make sure `value` is serialized as string, and not ;; as an integer or big-int - :params [chat-id (str value) contract transaction-hash - (or (:result (types/json->clj signature)) - (ethereum/normalized-hex signature))] + :params [chat-id (str value) contract transaction-hash + (or (:result (types/json->clj signature)) + (ethereum/normalized-hex signature))] :js-response true :on-success #(re-frame/dispatch [:transport/message-sent %])}]}) @@ -300,10 +324,10 @@ (fx/defn send-accept-request-transaction-message {:events [:sign/send-accept-transaction-message]} [cofx message-id transaction-hash signature] - {::json-rpc/call [{:method "wakuext_acceptRequestTransaction" - :params [transaction-hash message-id - (or (:result (types/json->clj signature)) - (ethereum/normalized-hex signature))] + {::json-rpc/call [{:method "wakuext_acceptRequestTransaction" + :params [transaction-hash message-id + (or (:result (types/json->clj signature)) + (ethereum/normalized-hex signature))] :js-response true :on-success #(re-frame/dispatch [:transport/message-sent %])}]}) @@ -312,7 +336,7 @@ [{:keys [db] :as cofx} result tx-obj] (let [{:keys [on-result symbol amount from]} (get db :signing/tx)] (fx/merge cofx - {:db (dissoc db :signing/tx :signing/sign) + {:db (dissoc db :signing/tx :signing/sign) :signing/show-transaction-result nil} (prepare-unconfirmed-transaction result tx-obj symbol amount) (check-queue) @@ -324,31 +348,31 @@ [{:keys [db] :as cofx} transaction-hash hashed-password {:keys [message-id chat-id from] :as tx-obj}] (let [{:keys [on-result symbol amount contract value]} (get db :signing/tx) - data (str (get-in db [:multiaccount :public-key]) - (subs transaction-hash 2))] + data (str (get-in db [:multiaccount :public-key]) + (subs transaction-hash 2))] (fx/merge cofx {:db (dissoc db :signing/tx :signing/sign)} (wallet/watch-tx (get from :address) transaction-hash) (if (keycard.common/keycard-multiaccount? db) (signing.keycard/hash-message - {:data data + {:data data :on-completed (fn [hash] (re-frame/dispatch [:keycard/sign-message - {:tx-hash transaction-hash + {:tx-hash transaction-hash :message-id message-id - :chat-id chat-id - :value value - :contract contract - :data data} + :chat-id chat-id + :value value + :contract contract + :data data} hash]))}) (fn [_] {:signing.fx/sign-message - {:params {:data data - :password hashed-password - :account from} + {:params {:data data + :password hashed-password + :account from} :on-completed (fn [res] (re-frame/dispatch @@ -384,10 +408,12 @@ {:events [:signing/sign-message-completed]} [{:keys [db] :as cofx} result] (let [{:keys [result error]} (types/json->clj result) - on-result (get-in db [:signing/tx :on-result])] + on-result (get-in db [:signing/tx :on-result])] (if error - {:db (update db :signing/sign - assoc :error (if (= 5 (:code error)) + {:db (update db + :signing/sign + assoc + :error (if (= 5 (:code error)) (i18n/label :t/wrong-password) (:message error)) :in-progress? false)} @@ -395,7 +421,7 @@ (when-not (= (-> db :signing/sign :type) :pinless) (dissoc-signing-db-entries-and-check-queue)) #(when (= (-> db :signing/sign :type) :pinless) - {:dispatch-later [{:ms 3000 + {:dispatch-later [{:ms 3000 :dispatch [:signing/dissoc-entries-and-check-queue]}]}) #(when on-result {:dispatch (conj on-result result)}))))) @@ -418,9 +444,9 @@ [{:keys [db] :as cofx}] (let [{:keys [on-error]} (get-in db [:signing/tx])] (fx/merge cofx - {:db (-> db - (assoc-in [:keycard :pin :status] nil) - (dissoc :signing/tx :signing/sign)) + {:db (-> db + (assoc-in [:keycard :pin :status] nil) + (dissoc :signing/tx :signing/sign)) :hide-signing-sheet nil} (check-queue) (keycard.common/hide-connection-sheet) @@ -428,7 +454,8 @@ #(when on-error {:dispatch (conj on-error "transaction was cancelled by user")})))) -(defn normalize-tx-obj [db tx] +(defn normalize-tx-obj + [db tx] (update-in tx [:tx-obj :from] #(eip55/address->checksum (or % (ethereum/default-address db))))) (fx/defn sign @@ -445,74 +472,74 @@ (check-queue))) (fx/defn sign-transaction-button-clicked-from-chat - {:events [:wallet.ui/sign-transaction-button-clicked-from-chat]} + {:events [:wallet.ui/sign-transaction-button-clicked-from-chat]} [{:keys [db] :as cofx} {:keys [to amount from token]}] (let [{:keys [symbol address]} token - amount-hex (str "0x" (status/number-to-hex amount)) - to-norm (ethereum/normalized-hex (if (string? to) to (:address to))) - from-address (:address from) - identity (:current-chat-id db) - db (dissoc db :wallet/prepare-transaction :signing/edit-fee)] + amount-hex (str "0x" (status/number-to-hex amount)) + to-norm (ethereum/normalized-hex (if (string? to) to (:address to))) + from-address (:address from) + identity (:current-chat-id db) + db (dissoc db :wallet/prepare-transaction :signing/edit-fee)] (if to-norm (fx/merge cofx {:db db} (sign {:tx-obj (if (= symbol :ETH) - {:to to-norm - :from from-address + {:to to-norm + :from from-address :chat-id identity :command? true - :value amount-hex} + :value amount-hex} {:to (ethereum/normalized-hex address) :from from-address :chat-id identity :command? true :data (status/encode-transfer to-norm amount-hex)})})) - {:db db + {:db db ::json-rpc/call - [{:method "wakuext_requestAddressForTransaction" - :params [(:current-chat-id db) - from-address - amount - (when-not (= symbol :ETH) - address)] + [{:method "wakuext_requestAddressForTransaction" + :params [(:current-chat-id db) + from-address + amount + (when-not (= symbol :ETH) + address)] :js-response true - :on-success #(re-frame/dispatch [:transport/message-sent %])}]}))) + :on-success #(re-frame/dispatch [:transport/message-sent %])}]}))) (fx/defn sign-transaction-button-clicked-from-request - {:events [:wallet.ui/sign-transaction-button-clicked-from-request]} + {:events [:wallet.ui/sign-transaction-button-clicked-from-request]} [{:keys [db] :as cofx} {:keys [amount from token]}] (let [{:keys [request-parameters chat-id]} (:wallet/prepare-transaction db) - {:keys [symbol address]} token - amount-hex (str "0x" (status/number-to-hex amount)) - to-norm (:address request-parameters) - from-address (:address from)] + {:keys [symbol address]} token + amount-hex (str "0x" (status/number-to-hex amount)) + to-norm (:address request-parameters) + from-address (:address from)] (fx/merge cofx {:db (dissoc db :wallet/prepare-transaction :signing/edit-fee)} (fn [cofx] (sign cofx {:tx-obj (if (= symbol :ETH) - {:to to-norm - :from from-address + {:to to-norm + :from from-address :message-id (:id request-parameters) - :chat-id chat-id - :command? true - :value amount-hex} - {:to (ethereum/normalized-hex address) - :from from-address - :command? true + :chat-id chat-id + :command? true + :value amount-hex} + {:to (ethereum/normalized-hex address) + :from from-address + :command? true :message-id (:id request-parameters) - :chat-id chat-id - :data (status/encode-transfer to-norm amount-hex)})}))))) + :chat-id chat-id + :data (status/encode-transfer to-norm amount-hex)})}))))) (fx/defn sign-transaction-button-clicked {:events [:wallet.ui/sign-transaction-button-clicked]} [{:keys [db] :as cofx} {:keys [to amount from token gas gasPrice maxFeePerGas maxPriorityFeePerGas]}] (let [{:keys [symbol address]} token - amount-hex (str "0x" (status/number-to-hex amount)) - to-norm (ethereum/normalized-hex (if (string? to) to (:address to))) - from-address (:address from)] + amount-hex (str "0x" (status/number-to-hex amount)) + to-norm (ethereum/normalized-hex (if (string? to) to (:address to))) + from-address (:address from)] (fx/merge cofx {:db (dissoc db :wallet/prepare-transaction :signing/edit-fee)} (sign @@ -554,17 +581,19 @@ {:events [:signing/cancel-transaction]} [cofx {:keys [from nonce hash]}] (when (and from nonce hash) - (sign cofx {:tx-obj {:from from - :to from - :nonce nonce - :value "0x0" - :cancel? true - :hash hash}}))) + (sign cofx + {:tx-obj {:from from + :to from + :nonce nonce + :value "0x0" + :cancel? true + :hash hash}}))) (fx/defn increase-gas {:events [:signing/increase-gas]} [cofx {:keys [from nonce] :as tx}] (when (and from nonce) - (sign cofx {:tx-obj (-> tx - (select-keys [:from :to :value :input :gas :nonce :hash]) - (clojure.set/rename-keys {:input :data}))}))) + (sign cofx + {:tx-obj (-> tx + (select-keys [:from :to :value :input :gas :nonce :hash]) + (clojure.set/rename-keys {:input :data}))}))) diff --git a/src/status_im/signing/core_test.cljs b/src/status_im/signing/core_test.cljs index a9fbb52f32..a56b49d3ee 100644 --- a/src/status_im/signing/core_test.cljs +++ b/src/status_im/signing/core_test.cljs @@ -1,22 +1,22 @@ (ns status-im.signing.core-test (:require [cljs.test :refer-macros [deftest is testing]] - [status-im.signing.core :as signing] - [status-im.native-module.core :as status])) + [status-im.native-module.core :as status] + [status-im.signing.core :as signing])) (deftest signing-test (testing "showing sheet" - (let [to "0x2f88d65f3cb52605a54a833ae118fb1363acccd2" - contract "0xc55cf4b03948d7ebc8b9e8bad92643703811d162" + (let [to "0x2f88d65f3cb52605a54a833ae118fb1363acccd2" + contract "0xc55cf4b03948d7ebc8b9e8bad92643703811d162" amount1-hex (str "0x" (status/number-to-hex "10000000000000000000")) amount2-hex (str "0x" (status/number-to-hex "100000000000000000000")) - data (status/encode-transfer to amount2-hex) - first-tx {:tx-obj {:to to - :from nil - :value amount1-hex}} - second-tx {:tx-obj {:to contract - :from nil - :data data}} - sign-first (signing/sign {:db {}} first-tx) + data (status/encode-transfer to amount2-hex) + first-tx {:tx-obj {:to to + :from nil + :value amount1-hex}} + second-tx {:tx-obj {:to contract + :from nil + :data data}} + sign-first (signing/sign {:db {}} first-tx) sign-second (signing/sign sign-first second-tx)] (testing "after fist transaction" (testing "signing in progress" @@ -26,17 +26,17 @@ (testing "first tx object is parsed" (is (= (dissoc (get-in sign-first [:db :signing/tx]) :token :hash :cancel?) (merge first-tx - {:from {:address nil} - :gas nil - :gasPrice nil - :maxFeePerGas nil + {:from {:address nil} + :gas nil + :gasPrice nil + :maxFeePerGas nil :maxPriorityFeePerGas nil - :data nil - :to to - :contact {:address to} - :symbol :ETH - :value "0x8ac7230489e80000" - :amount "10"}))))) + :data nil + :to to + :contact {:address to} + :symbol :ETH + :value "0x8ac7230489e80000" + :amount "10"}))))) (testing "after second transaction" (testing "signing still in progress" (is (get-in sign-second [:db :signing/tx]))) @@ -53,11 +53,11 @@ (testing "second tx object is parsed" (is (= (dissoc (get-in first-discarded [:db :signing/tx]) :token :hash :cancel?) (merge second-tx - {:from {:address nil} - :gas nil - :gasPrice nil - :maxFeePerGas nil + {:from {:address nil} + :gas nil + :gasPrice nil + :maxFeePerGas nil :maxPriorityFeePerGas nil - :data data - :to contract - :contact {:address contract}})))))))))) + :data data + :to contract + :contact {:address contract}})))))))))) diff --git a/src/status_im/signing/eip1559.cljs b/src/status_im/signing/eip1559.cljs index 593d54863c..f5f8c304f4 100644 --- a/src/status_im/signing/eip1559.cljs +++ b/src/status_im/signing/eip1559.cljs @@ -6,15 +6,18 @@ (defonce activated-on-current-network? (atom nil)) -(defn london-is-definitely-activated [network-id] +(defn london-is-definitely-activated + [network-id] (contains? #{"1" "3"} network-id)) -(defn on-block [callback header] +(defn on-block + [callback header] (let [activated? (contains? header :baseFeePerGas)] (reset! london-activated? activated?) (callback activated?))) -(defn check-activation [callback] +(defn check-activation + [callback] (json-rpc/call {:method "eth_getBlockByNumber" :params ["latest" false] diff --git a/src/status_im/signing/gas.cljs b/src/status_im/signing/gas.cljs index 4292925bf6..df9e99a440 100644 --- a/src/status_im/signing/gas.cljs +++ b/src/status_im/signing/gas.cljs @@ -1,15 +1,15 @@ (ns status-im.signing.gas - (:require [re-frame.core :as re-frame] + (:require [clojure.string :as string] + [re-frame.core :as re-frame] + [status-im.bottom-sheet.core :as bottom-sheet] + [status-im.ethereum.core :as ethereum] [status-im.ethereum.json-rpc :as json-rpc] [status-im.i18n.i18n :as i18n] - [status-im.bottom-sheet.core :as bottom-sheet] + [status-im.popover.core :as popover] + [status-im.signing.eip1559 :as eip1559] [status-im.utils.fx :as fx] [status-im.utils.money :as money] - [status-im.signing.eip1559 :as eip1559] - [taoensso.timbre :as log] - [status-im.popover.core :as popover] - [status-im.ethereum.core :as ethereum] - [clojure.string :as string])) + [taoensso.timbre :as log])) (def min-gas-price-wei ^js (money/bignumber 1)) @@ -17,19 +17,22 @@ (defmulti get-error-label-key (fn [type _] type)) -(defmethod get-error-label-key :gasPrice [_ value] +(defmethod get-error-label-key :gasPrice + [_ value] (cond - (not value) :t/invalid-number + (not value) :t/invalid-number (.lt ^js (money/->wei :gwei value) min-gas-price-wei) :t/wallet-send-min-wei - (-> (money/->wei :gwei value) .decimalPlaces pos?) :t/invalid-number)) + (-> (money/->wei :gwei value) .decimalPlaces pos?) :t/invalid-number)) -(defmethod get-error-label-key :gas [_ ^js value] +(defmethod get-error-label-key :gas + [_ ^js value] (cond - (not value) :t/invalid-number - (.lt value min-gas-units) :t/wallet-send-min-units + (not value) :t/invalid-number + (.lt value min-gas-units) :t/wallet-send-min-units (-> value .decimalPlaces pos?) :t/invalid-number)) -(defmethod get-error-label-key :default [_ value] +(defmethod get-error-label-key :default + [_ value] (when (or (not value) (<= value 0)) :t/invalid-number)) @@ -40,7 +43,8 @@ (money/to-fixed (money/wei->ether (.times gas gasPrice))) "0")) -(defn edit-max-fee [edit] +(defn edit-max-fee + [edit] (let [gasPrice (get-in edit [:gasPrice :value-number]) gas (get-in edit [:gas :value-number])] (assoc edit :max-fee (calculate-max-fee gas gasPrice)))) @@ -50,7 +54,7 @@ Wei for gas, and gwei for gas price. Validates them and sets max fee" [edit-value key value] - (let [^js bn-value (money/bignumber value) + (let [^js bn-value (money/bignumber value) error-label-key (get-error-label-key key bn-value) data (if error-label-key {:value value @@ -69,17 +73,20 @@ (def minimum-priority-fee-gwei (money/bignumber 0.3)) -(defn get-suggested-tip [latest-priority-fee] +(defn get-suggested-tip + [latest-priority-fee] (money/bignumber latest-priority-fee)) -(defn get-minimum-priority-fee [latest-priority-fee] +(defn get-minimum-priority-fee + [latest-priority-fee] (let [latest-priority-fee-bn (money/bignumber latest-priority-fee) - suggested-tip-gwei (money/wei->gwei (get-suggested-tip latest-priority-fee-bn))] + suggested-tip-gwei (money/wei->gwei (get-suggested-tip latest-priority-fee-bn))] (if (money/greater-than minimum-priority-fee-gwei suggested-tip-gwei) (money/div suggested-tip-gwei 2) minimum-priority-fee-gwei))) -(defn get-suggestions-range [latest-priority-fee] +(defn get-suggestions-range + [latest-priority-fee] (let [current-minimum-fee (get-minimum-priority-fee latest-priority-fee)] [(if (money/greater-than minimum-priority-fee-gwei current-minimum-fee) current-minimum-fee @@ -89,47 +96,52 @@ (def average-priority-fee (money/wei->gwei (money/->wei :gwei 1.5))) -(defn validate-max-fee [db] +(defn validate-max-fee + [db] (let [{:keys [maxFeePerGas maxPriorityFeePerGas]} (get db :signing/edit-fee) - latest-base-fee (money/wei->gwei - (money/bignumber - (get db :wallet/current-base-fee))) - fee-error (cond - (or (:error maxFeePerGas) - (:error maxPriorityFeePerGas)) - nil + latest-base-fee (money/wei->gwei + (money/bignumber + (get db :wallet/current-base-fee))) + fee-error (cond + (or (:error maxFeePerGas) + (:error maxPriorityFeePerGas)) + nil - (money/greater-than latest-base-fee - (:value-number maxFeePerGas)) - {:label (i18n/label :t/below-base-fee) - :severity :error})] + (money/greater-than latest-base-fee + (:value-number maxFeePerGas)) + {:label (i18n/label :t/below-base-fee) + :severity :error})] (if fee-error (assoc-in db [:signing/edit-fee :maxFeePerGas :fee-error] fee-error) (update-in db [:signing/edit-fee :maxFeePerGas] dissoc :fee-error)))) -(defn validate-max-priority-fee [db] +(defn validate-max-priority-fee + [db] (let [{:keys [maxPriorityFeePerGas]} (get db :signing/edit-fee) - latest-priority-fee (get db :wallet/current-priority-fee) - fee-error (cond - (:error maxPriorityFeePerGas) - nil + latest-priority-fee (get db :wallet/current-priority-fee) + fee-error (cond + (:error maxPriorityFeePerGas) + nil - (money/greater-than (get-minimum-priority-fee - (money/div - (money/wei->gwei (money/bignumber latest-priority-fee)) 2)) - (:value-number maxPriorityFeePerGas)) - {:label (i18n/label :t/low-tip) - :severity :error} + (money/greater-than (get-minimum-priority-fee + (money/div + (money/wei->gwei (money/bignumber + latest-priority-fee)) + 2)) + (:value-number maxPriorityFeePerGas)) + {:label (i18n/label :t/low-tip) + :severity :error} - #_(money/greater-than average-priority-fee - (:value-number maxPriorityFeePerGas)) - #_{:label (i18n/label :t/lower-than-average-tip) - :severity :error})] + #_(money/greater-than average-priority-fee + (:value-number maxPriorityFeePerGas)) + #_{:label (i18n/label :t/lower-than-average-tip) + :severity :error})] (if fee-error (assoc-in db [:signing/edit-fee :maxPriorityFeePerGas :fee-error] fee-error) (update-in db [:signing/edit-fee :maxPriorityFeePerGas] dissoc :fee-error)))) -(defn validate-eip1559-fees [db] +(defn validate-eip1559-fees + [db] (if (eip1559/sync-enabled?) (reduce (fn [acc f] @@ -147,7 +159,8 @@ (update :signing/edit-fee build-edit key value) validate-eip1559-fees)}) -(defn get-fee-options [tip slow normal fast] +(defn get-fee-options + [tip slow normal fast] (let [tip-bn (money/bignumber tip) normal-bn (money/bignumber normal) slow-bn (money/bignumber slow) @@ -176,21 +189,23 @@ {:db (-> db (assoc-in [:signing/edit-fee :selected-fee-option] option) (update :signing/edit-fee - build-edit :maxFeePerGas (money/wei->gwei fee)) + build-edit + :maxFeePerGas (money/wei->gwei fee)) (update :signing/edit-fee - build-edit :maxPriorityFeePerGas (money/wei->gwei tip)))})) + build-edit + :maxPriorityFeePerGas (money/wei->gwei tip)))})) (fx/defn set-priority-fee {:events [:signing.edit-fee.ui/set-priority-fee]} [{:keys [db]} value] (let [{:keys [maxFeePerGas maxPriorityFeePerGas]} (get db :signing/edit-fee) - latest-base-fee (get db :wallet/current-base-fee) - max-fee-value (:value-number maxFeePerGas) - max-priority-fee-value (:value-number maxPriorityFeePerGas) - new-value (money/bignumber value) - fee-without-tip (money/sub max-fee-value max-priority-fee-value) - base-fee (money/wei->gwei (money/bignumber latest-base-fee)) + latest-base-fee (get db :wallet/current-base-fee) + max-fee-value (:value-number maxFeePerGas) + max-priority-fee-value (:value-number maxPriorityFeePerGas) + new-value (money/bignumber value) + fee-without-tip (money/sub max-fee-value max-priority-fee-value) + base-fee (money/wei->gwei (money/bignumber latest-base-fee)) new-max-fee-value (money/to-fixed (if (money/greater-than base-fee fee-without-tip) @@ -213,7 +228,9 @@ [{db :db} price] (if (eip1559/sync-enabled?) (let [{:keys [normal-base-fee max-priority-fee]} price - max-priority-fee-bn (money/with-precision (get-suggested-tip max-priority-fee) 0)] + max-priority-fee-bn (money/with-precision (get-suggested-tip + max-priority-fee) + 0)] {:db (-> db (assoc-in [:signing/tx :maxFeePerGas] (money/to-hex (money/add max-priority-fee-bn @@ -242,14 +259,22 @@ {:events [:signing.ui/open-fee-sheet]} [{{:signing/keys [tx] :as db} :db :as cofx} sheet-opts] (let [{:keys [gas gasPrice maxFeePerGas maxPriorityFeePerGas]} tx - max-fee (money/to-fixed (money/wei->gwei maxFeePerGas)) - max-priority-fee (money/to-fixed (money/wei->gwei maxPriorityFeePerGas)) - edit-fee (reduce (partial apply build-edit) - {:selected-fee-option (get-in db [:signing/edit-fee :selected-fee-option])} - {:gas (money/to-fixed gas) - :gasPrice (money/to-fixed (money/wei->gwei gasPrice)) - :maxFeePerGas max-fee - :maxPriorityFeePerGas max-priority-fee})] + max-fee (money/to-fixed (money/wei->gwei + maxFeePerGas)) + max-priority-fee (money/to-fixed (money/wei->gwei + maxPriorityFeePerGas)) + edit-fee (reduce + (partial apply build-edit) + {:selected-fee-option + (get-in db + [:signing/edit-fee + :selected-fee-option])} + {:gas (money/to-fixed gas) + :gasPrice (money/to-fixed + (money/wei->gwei gasPrice)) + :maxFeePerGas max-fee + :maxPriorityFeePerGas + max-priority-fee})] (fx/merge cofx {:db (assoc db :signing/edit-fee edit-fee)} (bottom-sheet/show-bottom-sheet {:view sheet-opts})))) @@ -268,11 +293,13 @@ (not force?)) (popover/show-popover cofx {:view :fees-warning}) (fx/merge cofx - {:db (update db :signing/tx assoc - :gas (:value-number gas) - :gasPrice (:value-number gasPrice) - :maxFeePerGas (money/->wei :gwei (:value-number maxFeePerGas)) - :maxPriorityFeePerGas (money/->wei :gwei (:value-number maxPriorityFeePerGas)))} + {:db (update db + :signing/tx assoc + :gas (:value-number gas) + :gasPrice (:value-number gasPrice) + :maxFeePerGas (money/->wei :gwei (:value-number maxFeePerGas)) + :maxPriorityFeePerGas (money/->wei :gwei + (:value-number maxPriorityFeePerGas)))} (bottom-sheet/hide-bottom-sheet))))) (fx/defn submit-nonce @@ -289,10 +316,10 @@ network-id (fn [] (json-rpc/call - {:method "eth_feeHistory" + {:method "eth_feeHistory" ;; NOTE(rasom): We don't need `reward` atm but if the last parameter is ;; `nil` request fails on some chains (particularly on xDai). - :params [101 "latest" [100]] + :params [101 "latest" [100]] :on-success #(re-frame/dispatch [::header-fetched (assoc params :fee-history %)])})) (fn [] @@ -303,7 +330,8 @@ (def london-block-gas-limit (money/bignumber 30000000)) -(defn calc-percentiles [v ps] +(defn calc-percentiles + [v ps] (let [sorted-v (sort-by identity (fn [a b] @@ -319,7 +347,8 @@ ;; fee might be very small and using this value might slow transaction (def minimum-base-fee (money/->wei :gwei (money/bignumber 1))) -(defn recommended-base-fee [current perc20] +(defn recommended-base-fee + [current perc20] (let [fee (cond (money/greater-than-or-equals current perc20) current @@ -332,13 +361,15 @@ (defn slow-base-fee [_ perc10] perc10) -(defn fast-base-fee [current] +(defn fast-base-fee + [current] (let [fee (money/mul current 2)] (if (money/greater-than minimum-base-fee fee) (money/mul minimum-base-fee 2) fee))) -(defn check-base-fee [{:keys [baseFeePerGas testnet?]}] +(defn check-base-fee + [{:keys [baseFeePerGas testnet?]}] (let [all-base-fees (mapv money/bignumber baseFeePerGas) next-base-fee (peek all-base-fees) previous-fees (subvec all-base-fees 0 101) @@ -357,7 +388,8 @@ :fast-base-fee (money/to-hex (fast-base-fee next-base-fee)) :current-base-fee (money/to-hex current-base-fee)})) -(defn max-priority-fee-hex [gas-price base-fee] +(defn max-priority-fee-hex + [gas-price base-fee] (money/to-hex (money/sub (money/bignumber gas-price) (money/bignumber base-fee)))) @@ -375,8 +407,9 @@ (let [{:keys [current-base-fee] :as base-fees} (check-base-fee (assoc fee-history - :testnet? (ethereum/testnet? - (get-in networks [current-network :config :NetworkId]))))] + :testnet? + (ethereum/testnet? + (get-in networks [current-network :config :NetworkId]))))] (merge {:max-priority-fee (max-priority-fee-hex (money/bignumber %) current-base-fee)} base-fees))) @@ -390,5 +423,6 @@ (json-rpc/call {:method "eth_estimateGas" :params [obj] - :on-success #(re-frame/dispatch [success-event (money/bignumber (if (= (int %) 21000) % (int (* % 1.2))))]) + :on-success #(re-frame/dispatch [success-event + (money/bignumber (if (= (int %) 21000) % (int (* % 1.2))))]) :on-error #(re-frame/dispatch [error-event %])}))) diff --git a/src/status_im/signing/keycard.cljs b/src/status_im/signing/keycard.cljs index e9b0a56a25..74a3793f96 100644 --- a/src/status_im/signing/keycard.cljs +++ b/src/status_im/signing/keycard.cljs @@ -1,8 +1,8 @@ (ns status-im.signing.keycard (:require [re-frame.core :as re-frame] + [status-im.i18n.i18n :as i18n] [status-im.native-module.core :as status] [status-im.utils.fx :as fx] - [status-im.i18n.i18n :as i18n] [status-im.utils.types :as types] [taoensso.timbre :as log])) @@ -27,7 +27,7 @@ [{:keys [gas gasPrice data nonce tx-obj] :as params}] (let [{:keys [from to value chat-id message-id command? maxPriorityFeePerGas maxFeePerGas]} tx-obj maxPriorityFeePerGas (or maxPriorityFeePerGas (get params :maxPriorityFeePerGas)) - maxFeePerGas (or maxFeePerGas (get params :maxFeePerGas))] + maxFeePerGas (or maxFeePerGas (get params :maxFeePerGas))] (cond-> {:from from :to to :value value @@ -35,11 +35,15 @@ :message-id message-id :command? command?} maxPriorityFeePerGas - (assoc :maxPriorityFeePerGas (str "0x" (status/number-to-hex - (js/parseInt maxPriorityFeePerGas)))) + (assoc :maxPriorityFeePerGas + (str "0x" + (status/number-to-hex + (js/parseInt maxPriorityFeePerGas)))) maxFeePerGas - (assoc :maxFeePerGas (str "0x" (status/number-to-hex - (js/parseInt maxFeePerGas)))) + (assoc :maxFeePerGas + (str "0x" + (status/number-to-hex + (js/parseInt maxFeePerGas)))) gas (assoc :gas (str "0x" (status/number-to-hex gas))) gasPrice @@ -76,10 +80,11 @@ {:dispatch [:signing.ui/cancel-is-pressed] :utils/show-popup {:title (i18n/label :t/sign-request-failed) :content (:message error)}} - {:db (update db :keycard assoc - :hash result - :typed? typed? - :data data)}))) + {:db (update db + :keycard assoc + :hash result + :typed? typed? + :data data)}))) (fx/defn hash-transaction [{:keys [db]}] diff --git a/src/status_im/stickers/core.cljs b/src/status_im/stickers/core.cljs index c291bc4f74..e1fccd3fcc 100644 --- a/src/status_im/stickers/core.cljs +++ b/src/status_im/stickers/core.cljs @@ -1,13 +1,13 @@ (ns status-im.stickers.core (:require [clojure.string :as string] [re-frame.core :as re-frame] + [status-im.constants :as constants] [status-im.ethereum.core :as ethereum] [status-im.ethereum.json-rpc :as json-rpc] - [status-im2.navigation.events :as navigation] + [status-im.utils.config :as config] [status-im.utils.fx :as fx] [status-im.utils.utils :as utils] - [status-im.utils.config :as config] - [status-im.constants :as constants])) + [status-im2.navigation.events :as navigation])) (re-frame/reg-fx :stickers/set-pending-timeout-fx @@ -54,7 +54,8 @@ {:events [:stickers/pending-pack]} [{db :db} id] {:db (-> db - (assoc-in [:stickers/packs id :status] constants/sticker-pack-status-pending) + (assoc-in [:stickers/packs id :status] + constants/sticker-pack-status-pending) (update :stickers/packs-pending conj id)) :stickers/set-pending-timeout-fx nil ::json-rpc/call [{:method "stickers_addPending" @@ -67,17 +68,18 @@ (when (seq packs-pending) {::json-rpc/call [{:method "stickers_processPending" :params [(ethereum/chain-id db)] - :on-success #(re-frame/dispatch [:stickers/stickers-process-pending-success %])}]})) + :on-success #(re-frame/dispatch [:stickers/stickers-process-pending-success + %])}]})) (fx/defn stickers-process-pending-success {:events [:stickers/stickers-process-pending-success]} [{{:stickers/keys [packs-pending packs] :as db} :db} purchased] (let [purchased-ids (map :id (vals purchased)) packs-pending (apply disj packs-pending purchased-ids) - packs (reduce (fn [packs id] - (assoc-in packs [id :status] constants/sticker-pack-status-owned)) - packs - purchased-ids)] + packs (reduce (fn [packs id] + (assoc-in packs [id :status] constants/sticker-pack-status-owned)) + packs + purchased-ids)] (merge {:db (-> db (assoc :stickers/packs packs) diff --git a/src/status_im/test_helpers.clj b/src/status_im/test_helpers.clj index 7e2943e820..a1de97e0dd 100644 --- a/src/status_im/test_helpers.clj +++ b/src/status_im/test_helpers.clj @@ -19,8 +19,8 @@ (s/fdef deftest-sub :args (s/cat :sub-name keyword? - :args (s/coll-of symbol? :count 1) - :body (s/* any?))) + :args (s/coll-of symbol? :count 1) + :body (s/* any?))) (defmacro deftest-sub "Defines a test based on `sub-name`, executes `body` and restores the app db. diff --git a/src/status_im/test_helpers.cljs b/src/status_im/test_helpers.cljs index 006b16d802..0fc10e4b70 100644 --- a/src/status_im/test_helpers.cljs +++ b/src/status_im/test_helpers.cljs @@ -97,10 +97,10 @@ [f] (let [logs (atom [])] (binding [log/*config* (assoc-in log/*config* - [:appenders :test] - {:enabled? true - :fn (fn [{:keys [vargs level]}] - (swap! logs conj {:args vargs :level level}))})] + [:appenders :test] + {:enabled? true + :fn (fn [{:keys [vargs level]}] + (swap! logs conj {:args vargs :level level}))})] (f logs)))) (defn restore-app-db @@ -113,4 +113,4 @@ (try (f) (finally - (reset! rf-db/app-db original-db))))) + (reset! rf-db/app-db original-db))))) diff --git a/src/status_im/test_runner.cljs b/src/status_im/test_runner.cljs index 6ad97a1b7a..698d018754 100644 --- a/src/status_im/test_runner.cljs +++ b/src/status_im/test_runner.cljs @@ -1,25 +1,28 @@ (ns status-im.test-runner {:dev/always true} - (:require - [cljs.test :as ct] - [clojure.string :as string] - [shadow.test :as st] - [shadow.test.env :as env])) + (:require [cljs.test :as ct] + [clojure.string :as string] + [shadow.test :as st] + [shadow.test.env :as env])) (defonce repl? (atom false)) -(defmethod ct/report [::ct/default :end-run-tests] [m] +(defmethod ct/report [::ct/default :end-run-tests] + [m] (when-not @repl? (if (ct/successful? m) (js/process.exit 0) (js/process.exit 1)))) -;; get-test-data is a macro so this namespace REQUIRES :dev/always hint ns so that it is always recompiled -(defn ^:dev/after-load reset-test-data! [] +;; get-test-data is a macro so this namespace REQUIRES :dev/always hint ns so that it is always +;; recompiled +(defn ^:dev/after-load reset-test-data! + [] (-> (env/get-test-data) (env/reset-test-data!))) -(defn parse-args [args] +(defn parse-args + [args] (reduce (fn [opts arg] (cond @@ -33,7 +36,7 @@ (assoc opts :repl true) (string/starts-with? arg "--test=") - (let [test-arg (subs arg 7) + (let [test-arg (subs arg 7) test-syms (->> (string/split test-arg ",") (map symbol))] @@ -45,12 +48,17 @@ {:test-syms []} args)) -(defn find-matching-test-vars [test-syms] +(defn find-matching-test-vars + [test-syms] ;; FIXME: should have some kind of wildcard support (let [test-namespaces - (->> test-syms (filter simple-symbol?) (set)) + (->> test-syms + (filter simple-symbol?) + (set)) test-var-syms - (->> test-syms (filter qualified-symbol?) (set))] + (->> test-syms + (filter qualified-symbol?) + (set))] (->> (env/get-test-vars) (filter (fn [the-var] @@ -58,7 +66,8 @@ (or (contains? test-namespaces ns) (contains? test-var-syms (symbol ns name))))))))) -(defn execute-cli [{:keys [test-syms help list repl] :as _opts}] +(defn execute-cli + [{:keys [test-syms help list repl] :as _opts}] (let [test-env (-> (ct/empty-env) ;; can't think of a proper way to let CLI specify custom reporter? @@ -70,17 +79,19 @@ (cond help - (do (println "Usage:") - (println " --list (list known test names)") - (println " --test=, (run test for namespace or single var, separated by comma)") - (println " --repl (start node without automatically running tests)")) + (do + (println "Usage:") + (println " --list (list known test names)") + (println + " --test=, (run test for namespace or single var, separated by comma)") + (println " --repl (start node without automatically running tests)")) list (doseq [[ns ns-info] (->> (env/get-tests) (sort-by first))] (println "Namespace:" ns) - (doseq [var (:vars ns-info) + (doseq [var (:vars ns-info) :let [m (meta var)]] (println (str " " (:ns m) "/" (:name m)))) (println "---------------------------------")) @@ -97,7 +108,8 @@ :else (st/run-all-tests test-env nil)))) -(defn ^:export main [& args] +(defn ^:export main + [& args] (reset-test-data!) (let [opts (parse-args args)] diff --git a/src/status_im/theme/core.cljs b/src/status_im/theme/core.cljs index 86fd2115f1..0968e92194 100644 --- a/src/status_im/theme/core.cljs +++ b/src/status_im/theme/core.cljs @@ -2,6 +2,7 @@ (:require [quo.theme :as quo.theme] [quo2.theme :as quo2.theme])) -(defn change-theme [theme] +(defn change-theme + [theme] (quo.theme/set-theme theme) (quo2.theme/set-theme theme)) diff --git a/src/status_im/transport/core.cljs b/src/status_im/transport/core.cljs index ea38784653..dd206a59e0 100644 --- a/src/status_im/transport/core.cljs +++ b/src/status_im/transport/core.cljs @@ -1,23 +1,23 @@ -(ns ^{:doc "API to init and stop whisper messaging"} - status-im.transport.core +(ns ^{:doc "API to init and stop whisper messaging"} status-im.transport.core (:require [re-frame.core :as re-frame] [status-im.ethereum.json-rpc :as json-rpc] [status-im.pairing.core :as pairing] - [status-im.utils.fx :as fx] + [status-im.stickers.core :as stickers] status-im.transport.shh - [taoensso.timbre :as log] + [status-im.utils.fx :as fx] [status-im.utils.universal-links.core :as universal-links] - [status-im.stickers.core :as stickers])) + [taoensso.timbre :as log])) (fx/defn set-node-info {:events [:transport.callback/node-info-fetched]} [{:keys [db]} node-info] {:db (assoc db :node-info node-info)}) -(fx/defn fetch-node-info-fx [_] - {::json-rpc/call [{:method "admin_nodeInfo" +(fx/defn fetch-node-info-fx + [_] + {::json-rpc/call [{:method "admin_nodeInfo" :on-success #(re-frame/dispatch [:transport.callback/node-info-fetched %]) - :on-error #(log/error "node-info: failed error" %)}]}) + :on-error #(log/error "node-info: failed error" %)}]}) (defn add-mailservers [db mailservers] @@ -28,8 +28,8 @@ (assoc :name (if (seq name) name id)) (dissoc :fleet))] (assoc-in db - [:mailserver/mailservers (keyword fleet) (keyword id)] - updated-mailserver))) + [:mailserver/mailservers (keyword fleet) (keyword id)] + updated-mailserver))) db mailservers)) @@ -38,9 +38,9 @@ initializiation is completed, otherwise we might receive messages/topics when the state has not been properly initialized." [_] - {::json-rpc/call [{:method "wakuext_startMessenger" + {::json-rpc/call [{:method "wakuext_startMessenger" :on-success #(re-frame/dispatch [::messenger-started %]) - :on-error #(log/error "failed to start messenger")}]}) + :on-error #(log/error "failed to start messenger")}]}) (fx/defn messenger-started {:events [::messenger-started]} diff --git a/src/status_im/transport/message/core.cljs b/src/status_im/transport/message/core.cljs index 2513405ce9..5c06e54c24 100644 --- a/src/status_im/transport/message/core.cljs +++ b/src/status_im/transport/message/core.cljs @@ -1,29 +1,28 @@ -(ns ^{:doc "Definition of the StatusMessage protocol"} - status-im.transport.message.core - (:require [status-im2.contexts.activity-center.events :as activity-center] - [status-im.chat.models.message :as models.message] - [status-im2.contexts.chat.messages.pin.events :as messages.pin] +(ns ^{:doc "Definition of the StatusMessage protocol"} status-im.transport.message.core + (:require [clojure.string :as string] + [status-im.browser.core :as browser] [status-im.chat.models :as models.chat] + [status-im.chat.models.message :as models.message] [status-im.chat.models.reactions :as models.reactions] - [status-im.contact.core :as models.contact] [status-im.communities.core :as models.communities] - [status-im.pairing.core :as models.pairing] - [status-im.data-store.reactions :as data-store.reactions] - [status-im.data-store.contacts :as data-store.contacts] - [status-im.data-store.chats :as data-store.chats] - [status-im.data-store.invitations :as data-store.invitations] + [status-im.constants :as constants] + [status-im.contact.core :as models.contact] [status-im.data-store.activities :as data-store.activities] + [status-im.data-store.chats :as data-store.chats] + [status-im.data-store.contacts :as data-store.contacts] + [status-im.data-store.invitations :as data-store.invitations] [status-im.data-store.messages :as data-store.messages] + [status-im.data-store.reactions :as data-store.reactions] [status-im.group-chats.core :as models.group] + [status-im.multiaccounts.login.core :as multiaccounts.login] + [status-im.multiaccounts.model :as multiaccounts.model] [status-im.multiaccounts.update.core :as update.core] + [status-im.pairing.core :as models.pairing] [status-im.utils.fx :as fx] [status-im.utils.types :as types] - [status-im.constants :as constants] - [status-im.multiaccounts.model :as multiaccounts.model] - [status-im.multiaccounts.login.core :as multiaccounts.login] [status-im.visibility-status-updates.core :as models.visibility-status-updates] - [status-im.browser.core :as browser] - [clojure.string :as string])) + [status-im2.contexts.activity-center.events :as activity-center] + [status-im2.contexts.chat.messages.pin.events :as messages.pin])) (fx/defn process-next [cofx ^js response-js sync-handler] @@ -85,13 +84,14 @@ (seq contacts) (let [contacts-clj (types/js->clj contacts) - ^js chats (.-chatsForContacts response-js)] + ^js chats (.-chatsForContacts response-js)] (js-delete response-js "contacts") (js-delete response-js "chatsForContacts") (fx/merge cofx (process-next response-js sync-handler) (models.contact/ensure-contacts - (map data-store.contacts/<-rpc contacts-clj) chats))) + (map data-store.contacts/<-rpc contacts-clj) + chats))) (seq communities) (let [communities-clj (types/js->clj communities)] @@ -195,19 +195,21 @@ (defn group-by-and-update-unviewed-counts "group messages by current chat, profile updates, transactions and update unviewed counters in db for not curent chats" [{:keys [current-chat-id db] :as acc} ^js message-js] - (let [chat-id (.-localChatId message-js) - message-type (.-messageType message-js) - from (.-from message-js) - mentioned (.-mentioned message-js) - profile (models.chat/profile-chat? {:db db} chat-id) - new (.-new message-js) - current (= current-chat-id chat-id) + (let [chat-id (.-localChatId message-js) + message-type (.-messageType message-js) + from (.-from message-js) + mentioned (.-mentioned message-js) + profile (models.chat/profile-chat? {:db db} chat-id) + new (.-new message-js) + current (= current-chat-id chat-id) should-update-unviewed? (and (not current) new (not profile) - (not (= message-type constants/message-type-private-group-system-message)) + (not (= message-type + constants/message-type-private-group-system-message)) (not (= from (multiaccounts.model/current-public-key {:db db})))) - tx-hash (and (.-commandParameters message-js) (.-commandParameters.transactionHash message-js))] + tx-hash (and (.-commandParameters message-js) + (.-commandParameters.transactionHash message-js))] (cond-> acc current (update :messages conj message-js) @@ -235,9 +237,9 @@ [response-js messages] (if (seq messages) (set! (.-messages response-js) - (.sort (to-array messages) - (fn [a b] - (- (.-clock b) (.-clock a))))) + (.sort (to-array messages) + (fn [a b] + (- (.-clock b) (.-clock a))))) (js-delete response-js "messages"))) (fx/defn sanitize-messages-and-process-response @@ -245,21 +247,26 @@ {:events [:sanitize-messages-and-process-response]} [{:keys [db] :as cofx} ^js response-js process-async] (when response-js - (let [current-chat-id (:current-chat-id db) + (let [current-chat-id (:current-chat-id db) {:keys [db messages transactions chats statuses]} (reduce group-by-and-update-unviewed-counts - {:db db :chats #{} :transactions #{} :statuses [] :messages [] + {:db db + :chats #{} + :transactions #{} + :statuses [] + :messages [] :current-chat-id current-chat-id} (.-messages response-js))] (sort-js-messages! response-js messages) (fx/merge cofx - {:db db + {:db db :utils/dispatch-later (concat [] (when (seq statuses) [{:ms 100 :dispatch [:process-statuses statuses]}]) (when (seq transactions) (for [transaction-hash transactions] - {:ms 100 :dispatch [:watch-tx nil transaction-hash]})))} + {:ms 100 + :dispatch [:watch-tx nil transaction-hash]})))} (process-response response-js process-async))))) (fx/defn remove-hash @@ -274,7 +281,8 @@ (fx/merge cofx {:db (update db :transport/message-ids->confirmations - dissoc message-id)} + dissoc + message-id)} (models.message/update-message-status chat-id message-id (if not-sent @@ -282,11 +290,11 @@ status)) (remove-hash message-id)) (let [confirmations {:pending-confirmations (dec pending-confirmations) - :not-sent (or not-sent - (= :not-sent status))}] + :not-sent (or not-sent + (= :not-sent status))}] {:db (assoc-in db - [:transport/message-ids->confirmations message-id] - confirmations)})))) + [:transport/message-ids->confirmations message-id] + confirmations)})))) (fx/defn update-envelope-status [{:keys [db] :as cofx} message-id status] @@ -308,19 +316,19 @@ "message-type is used for tracking" [{:keys [db] :as cofx} chat-id message-id message-type] ;; Check first if the confirmation has already arrived - (let [statuses (get-in db [:transport/message-confirmations message-id]) + (let [statuses (get-in db [:transport/message-confirmations message-id]) check-confirmations-fx (map #(check-confirmations % chat-id message-id) statuses) - add-envelope-data (fn [{:keys [db]}] - {:db (-> db - (update :transport/message-confirmations dissoc message-id) - (assoc-in [:transport/message-envelopes message-id] - {:chat-id chat-id - :message-type message-type}) - (update-in [:transport/message-ids->confirmations message-id] - #(or % {:pending-confirmations 1})))})] + add-envelope-data (fn [{:keys [db]}] + {:db (-> db + (update :transport/message-confirmations dissoc message-id) + (assoc-in [:transport/message-envelopes message-id] + {:chat-id chat-id + :message-type message-type}) + (update-in [:transport/message-ids->confirmations message-id] + #(or % {:pending-confirmations 1})))})] (apply fx/merge cofx (conj check-confirmations-fx add-envelope-data)))) (fx/defn transport-message-sent @@ -329,6 +337,7 @@ (let [set-hash-fxs (map (fn [{:keys [localChatId id messageType]}] (set-message-envelope-hash localChatId id messageType)) (types/js->clj (.-messages response-js)))] - (apply fx/merge cofx + (apply fx/merge + cofx (conj set-hash-fxs #(sanitize-messages-and-process-response % response-js false))))) diff --git a/src/status_im/transport/message/protocol.cljs b/src/status_im/transport/message/protocol.cljs index 6cd9fe0c0d..d87a2182a0 100644 --- a/src/status_im/transport/message/protocol.cljs +++ b/src/status_im/transport/message/protocol.cljs @@ -1,20 +1,20 @@ -(ns ^{:doc "Protocol API and protocol utils"} - status-im.transport.message.protocol +(ns ^{:doc "Protocol API and protocol utils"} status-im.transport.message.protocol (:require [re-frame.core :as re-frame] [status-im.ethereum.json-rpc :as json-rpc] [status-im.utils.fx :as fx] [taoensso.timbre :as log])) -(defn build-message [{:keys [chat-id - text - response-to - ens-name - community-id - image-path - audio-path - audio-duration-ms - sticker - content-type]}] +(defn build-message + [{:keys [chat-id + text + response-to + ens-name + community-id + image-path + audio-path + audio-duration-ms + sticker + content-type]}] {:chatId chat-id :text text :responseTo response-to @@ -26,25 +26,28 @@ :sticker sticker :contentType content-type}) -(fx/defn send-chat-messages [_ messages] - {::json-rpc/call [{:method "wakuext_sendChatMessages" - :params [(mapv build-message messages)] +(fx/defn send-chat-messages + [_ messages] + {::json-rpc/call [{:method "wakuext_sendChatMessages" + :params [(mapv build-message messages)] :js-response true - :on-success #(re-frame/dispatch [:transport/message-sent %]) - :on-error #(do - (log/warn "failed to send a message" %) - (js/alert (str "failed to send a message: " %)))}]}) + :on-success #(re-frame/dispatch [:transport/message-sent %]) + :on-error #(do + (log/warn "failed to send a message" %) + (js/alert (str "failed to send a message: " %)))}]}) -(fx/defn send-reaction [_ {:keys [message-id chat-id emoji-id]}] - {::json-rpc/call [{:method "wakuext_sendEmojiReaction" - :params [chat-id message-id emoji-id] +(fx/defn send-reaction + [_ {:keys [message-id chat-id emoji-id]}] + {::json-rpc/call [{:method "wakuext_sendEmojiReaction" + :params [chat-id message-id emoji-id] :js-response true - :on-success #(re-frame/dispatch [:sanitize-messages-and-process-response %]) - :on-error #(log/error "failed to send a reaction" %)}]}) + :on-success #(re-frame/dispatch [:sanitize-messages-and-process-response %]) + :on-error #(log/error "failed to send a reaction" %)}]}) -(fx/defn send-retract-reaction [_ {:keys [emoji-reaction-id]}] - {::json-rpc/call [{:method "wakuext_sendEmojiReactionRetraction" - :params [emoji-reaction-id] +(fx/defn send-retract-reaction + [_ {:keys [emoji-reaction-id]}] + {::json-rpc/call [{:method "wakuext_sendEmojiReactionRetraction" + :params [emoji-reaction-id] :js-response true - :on-success #(re-frame/dispatch [:sanitize-messages-and-process-response %]) - :on-error #(log/error "failed to send a reaction retraction" %)}]}) + :on-success #(re-frame/dispatch [:sanitize-messages-and-process-response %]) + :on-error #(log/error "failed to send a reaction retraction" %)}]}) diff --git a/src/status_im/transport/shh.cljs b/src/status_im/transport/shh.cljs index bdf6898e1a..d23a3f31e2 100644 --- a/src/status_im/transport/shh.cljs +++ b/src/status_im/transport/shh.cljs @@ -1,5 +1,4 @@ -(ns ^{:doc "Whisper API and events for managing keys and posting messages"} - status-im.transport.shh +(ns ^{:doc "Whisper API and events for managing keys and posting messages"} status-im.transport.shh (:require [re-frame.core :as re-frame] [status-im.ethereum.json-rpc :as json-rpc] [taoensso.timbre :as log])) @@ -18,7 +17,8 @@ :on-success on-success :on-error on-error})) -(defn log-error [error] +(defn log-error + [error] (log/error :shh/get-new-sym-key-error error)) (re-frame/reg-fx diff --git a/src/status_im/transport/utils.cljs b/src/status_im/transport/utils.cljs index 9a70c49c16..68cdceb2a3 100644 --- a/src/status_im/transport/utils.cljs +++ b/src/status_im/transport/utils.cljs @@ -1,8 +1,8 @@ -(ns ^{:doc "Utils for transport layer"} - status-im.transport.utils +(ns ^{:doc "Utils for transport layer"} status-im.transport.utils (:require [clojure.string :as string])) -(defn extract-enode-id [enode] +(defn extract-enode-id + [enode] (-> enode (string/split #"/") (get 2 "") @@ -11,6 +11,7 @@ (string/split "@") (get 0))) -(defn extract-url-components [address] +(defn extract-url-components + [address] (when address (rest (re-matches #"enode://(.*?)@(.*):(.*)" address)))) diff --git a/src/status_im/ui/components/accordion.cljs b/src/status_im/ui/components/accordion.cljs index f5dc2dc58b..f60d6141ae 100644 --- a/src/status_im/ui/components/accordion.cljs +++ b/src/status_im/ui/components/accordion.cljs @@ -1,11 +1,12 @@ (ns status-im.ui.components.accordion - (:require [reagent.core :as reagent] - [quo.core :as quo] + (:require [quo.core :as quo] [quo.design-system.colors :as colors] - [status-im.ui.components.react :as react] - [status-im.ui.components.icons.icons :as icons])) + [reagent.core :as reagent] + [status-im.ui.components.icons.icons :as icons] + [status-im.ui.components.react :as react])) -(defn drop-down-icon [{:keys [opened? dropdown-margin-left]}] +(defn drop-down-icon + [{:keys [opened? dropdown-margin-left]}] [react/view {:flex-direction :row :align-items :center} [icons/icon (if opened? :main-icons/dropdown-up :main-icons/dropdown) {:container-style {:align-items :center @@ -18,20 +19,22 @@ "Render collapsible section" [_props] (let [opened? (reagent/atom (get _props :default))] - (fn [{:keys [title content icon opened disabled - padding-vertical dropdown-margin-left - open-container-style - on-open on-close] - :or {padding-vertical 8 - dropdown-margin-left 8 - open-container-style {} - on-open #() - on-close #()}}] + (fn + [{:keys [title content icon opened disabled + padding-vertical dropdown-margin-left + open-container-style + on-open on-close] + :or {padding-vertical 8 + dropdown-margin-left 8 + open-container-style {} + on-open #() + on-close #()}}] (let [on-press #(do (apply (if @opened? on-close on-open) []) (swap! opened? not))] - [react/view (merge {:padding-vertical padding-vertical} - (when @opened? open-container-style)) + [react/view + (merge {:padding-vertical padding-vertical} + (when @opened? open-container-style)) (if (string? title) [quo/list-item {:title title @@ -39,12 +42,14 @@ :on-press on-press :accessory [drop-down-icon (or @opened? opened)]}] [react/touchable-opacity {:on-press on-press :disabled disabled} - [react/view {:flex-direction :row - :margin-right 14 - :justify-content :space-between} + [react/view + {:flex-direction :row + :margin-right 14 + :justify-content :space-between} title - [drop-down-icon {:opened? (or @opened? opened) - :dropdown-margin-left dropdown-margin-left}]]]) + [drop-down-icon + {:opened? (or @opened? opened) + :dropdown-margin-left dropdown-margin-left}]]]) (when (or @opened? opened) content)])))) diff --git a/src/status_im/ui/components/action_sheet.cljs b/src/status_im/ui/components/action_sheet.cljs index 0546c53578..63d3ff9ce0 100644 --- a/src/status_im/ui/components/action_sheet.cljs +++ b/src/status_im/ui/components/action_sheet.cljs @@ -1,9 +1,10 @@ (ns status-im.ui.components.action-sheet - (:require [status-im.i18n.i18n :as i18n] - [status-im.utils.core :as utils] - ["react-native" :refer (ActionSheetIOS)])) + (:require ["react-native" :refer (ActionSheetIOS)] + [status-im.i18n.i18n :as i18n] + [status-im.utils.core :as utils])) -(defn- callback [options on-cancel] +(defn- callback + [options on-cancel] (fn [index] (if (< index (count options)) (when-let [handler (:action (nth options index))] @@ -11,8 +12,10 @@ (when on-cancel (on-cancel))))) -(defn- prepare-options [title message options] - (let [destructive-opt-index (utils/first-index :destructive? options)] ;; TODO Can only be a single destructive? +(defn- prepare-options + [title message options] + (let [destructive-opt-index (utils/first-index :destructive? options)] ;; TODO Can only be a single + ;; destructive? (clj->js (merge {:options (conj (mapv :label options) (i18n/label :t/cancel)) :cancelButtonIndex (count options)} (when destructive-opt-index @@ -20,7 +23,8 @@ (when title {:title title}) (when message {:message message}))))) -(defn show [{:keys [title message options on-cancel]}] +(defn show + [{:keys [title message options on-cancel]}] (.showActionSheetWithOptions ActionSheetIOS (prepare-options title message options) (callback options on-cancel))) diff --git a/src/status_im/ui/components/animation.cljs b/src/status_im/ui/components/animation.cljs index 1e11dac45e..8411c819c8 100644 --- a/src/status_im/ui/components/animation.cljs +++ b/src/status_im/ui/components/animation.cljs @@ -1,61 +1,75 @@ (ns status-im.ui.components.animation - (:require [status-im.ui.components.react :as react] - ["react-native" :as rn])) + (:require ["react-native" :as rn] + [status-im.ui.components.react :as react])) (defn start ([^js anim] (.start anim)) ([^js anim callback] (.start anim callback))) -(defn anim-loop [animation] +(defn anim-loop + [animation] (.loop ^js react/animated animation)) -(defn interpolate [^js anim-value config] +(defn interpolate + [^js anim-value config] (.interpolate anim-value (clj->js config))) -(defn add-native-driver [{:keys [useNativeDriver] :as config}] +(defn add-native-driver + [{:keys [useNativeDriver] :as config}] (assoc config :useNativeDriver (if (nil? useNativeDriver) true useNativeDriver))) -(defn timing [anim-value config] +(defn timing + [anim-value config] (.timing ^js react/animated anim-value (clj->js (add-native-driver config)))) -(defn spring [anim-value config] +(defn spring + [anim-value config] (.spring ^js react/animated anim-value (clj->js (add-native-driver config)))) -(defn decay [anim-value config] +(defn decay + [anim-value config] (.decay ^js react/animated anim-value (clj->js (add-native-driver config)))) -(defn anim-sequence [animations] +(defn anim-sequence + [animations] (.sequence ^js react/animated (clj->js animations))) -(defn parallel [animations] +(defn parallel + [animations] (.parallel ^js react/animated (clj->js animations))) -(defn anim-delay [duration] +(defn anim-delay + [duration] (.delay ^js react/animated duration)) -(defn event [mapping config] +(defn event + [mapping config] (.event ^js react/animated (clj->js mapping) (clj->js config))) -(defn add-listener [^js anim-value listener] +(defn add-listener + [^js anim-value listener] (.addListener anim-value listener)) -(defn remove-all-listeners [^js anim-value] +(defn remove-all-listeners + [^js anim-value] (.removeAllListeners anim-value)) -(defn stop-animation [^js anim-value] +(defn stop-animation + [^js anim-value] (.stopAnimation anim-value)) -(defn set-value [^js anim-value value] +(defn set-value + [^js anim-value value] (.setValue anim-value value)) (def animated (.-Animated ^js rn)) @@ -63,25 +77,32 @@ (def animated-value-xy (-> ^js rn .-Animated .-ValueXY)) (def easing (-> ^js rn .-Easing)) -(defn create-value [value] +(defn create-value + [value] (new animated-value value)) -(defn create-value-xy [value] +(defn create-value-xy + [value] (new animated-value-xy value)) -(defn add [anim-x anim-y] +(defn add + [anim-x anim-y] ((-> ^js rn .-Animated .add) anim-x anim-y)) -(defn subtract [anim-x anim-y] +(defn subtract + [anim-x anim-y] ((-> ^js rn .-Animated .-subtract) anim-x anim-y)) -(defn x [^js value-xy] +(defn x + [^js value-xy] (.-x value-xy)) -(defn y [^js value-xy] +(defn y + [^js value-xy] (.-y value-xy)) -(defn get-layout [^js value-xy] +(defn get-layout + [^js value-xy] (js->clj (.getLayout value-xy))) (defn easing-in [] (.-in ^js easing)) diff --git a/src/status_im/ui/components/badge.cljs b/src/status_im/ui/components/badge.cljs index 2df981ac5b..652cb8146b 100644 --- a/src/status_im/ui/components/badge.cljs +++ b/src/status_im/ui/components/badge.cljs @@ -1,22 +1,26 @@ (ns status-im.ui.components.badge - (:require [status-im.ui.components.react :as react] - [quo.design-system.colors :as colors] - [status-im.i18n.i18n :as i18n])) + (:require [quo.design-system.colors :as colors] + [status-im.i18n.i18n :as i18n] + [status-im.ui.components.react :as react])) -(defn badge [label & [small?]] - [react/view (merge - (if small? - {:height 18 :border-radius 9 :min-width 18 :padding-horizontal 6} - {:height 22 :border-radius 11 :min-width 22 :padding-horizontal 8}) - {:background-color colors/blue - :justify-content :center - :align-items :center}) - [react/text {:style {:typography :caption - :font-weight "500" - :color colors/white-persist}} +(defn badge + [label & [small?]] + [react/view + (merge + (if small? + {:height 18 :border-radius 9 :min-width 18 :padding-horizontal 6} + {:height 22 :border-radius 11 :min-width 22 :padding-horizontal 8}) + {:background-color colors/blue + :justify-content :center + :align-items :center}) + [react/text + {:style {:typography :caption + :font-weight "500" + :color colors/white-persist}} label]]) -(defn message-counter [value & [small?]] +(defn message-counter + [value & [small?]] [badge (if (> value 99) (i18n/label :t/counter-99-plus) diff --git a/src/status_im/ui/components/bottom_panel/views.cljs b/src/status_im/ui/components/bottom_panel/views.cljs index ab8b4442cb..0c149315a6 100644 --- a/src/status_im/ui/components/bottom_panel/views.cljs +++ b/src/status_im/ui/components/bottom_panel/views.cljs @@ -1,20 +1,22 @@ (ns status-im.ui.components.bottom-panel.views (:require ["react-native" :refer (BackHandler)] + [quo.design-system.colors :as colors] [reagent.core :as reagent] [status-im.ui.components.animation :as anim] - [quo.design-system.colors :as colors] [status-im.ui.components.react :as react] [status-im.utils.platform :as platform]) (:require-macros [status-im.utils.views :as views])) (def back-listener (atom nil)) -(defn remove-back-listener [] +(defn remove-back-listener + [] (when @back-listener (.remove ^js @back-listener) (reset! back-listener nil))) -(defn add-back-listener [] +(defn add-back-listener + [] (remove-back-listener) (reset! back-listener (.addEventListener BackHandler "hardwareBackPress" @@ -26,24 +28,29 @@ (react/dismiss-keyboard!) (anim/start (anim/parallel - [(anim/spring bottom-anim-value {:toValue (- window-height) - :useNativeDriver true}) - (anim/timing alpha-value {:toValue 0 - :duration 500 - :useNativeDriver true})]))) + [(anim/spring bottom-anim-value + {:toValue (- window-height) + :useNativeDriver true}) + (anim/timing alpha-value + {:toValue 0 + :duration 500 + :useNativeDriver true})]))) (defn show-panel-anim [bottom-anim-value alpha-value] (add-back-listener) (anim/start (anim/parallel - [(anim/spring bottom-anim-value {:toValue 40 - :useNativeDriver true}) - (anim/timing alpha-value {:toValue 0.4 - :duration 500 - :useNativeDriver true})]))) + [(anim/spring bottom-anim-value + {:toValue 40 + :useNativeDriver true}) + (anim/timing alpha-value + {:toValue 0.4 + :duration 500 + :useNativeDriver true})]))) -(defn bottom-panel [_ render window-height on-close on-touch-outside show-overlay?] +(defn bottom-panel + [_ render window-height on-close on-touch-outside show-overlay?] (let [bottom-anim-value (anim/create-value window-height) alpha-value (anim/create-value 0) clear-timeout (atom nil) @@ -62,15 +69,21 @@ (and @current-obj obj) (do (reset! update? true) (js/setTimeout #(reset! current-obj obj) 600) - (hide-panel-anim bottom-anim-value alpha-value (- window-height))) + (hide-panel-anim bottom-anim-value + alpha-value + (- window-height))) obj (do (reset! current-obj obj) (show-panel-anim bottom-anim-value alpha-value)) :else - (do (reset! clear-timeout (js/setTimeout #(reset! current-obj nil) 600)) - (hide-panel-anim bottom-anim-value alpha-value (- window-height))))))) + (do (reset! clear-timeout (js/setTimeout #(reset! current-obj + nil) + 600)) + (hide-panel-anim bottom-anim-value + alpha-value + (- window-height))))))) :UNSAFE_componentWillUpdate (fn [_ [_ obj _ _]] (when @clear-timeout (js/clearTimeout @clear-timeout)) (when (or (not= obj @current-obj) @update?) @@ -82,36 +95,57 @@ (and @current-obj obj) (do (reset! update? true) (js/setTimeout #(reset! current-obj obj) 600) - (hide-panel-anim bottom-anim-value alpha-value (- window-height))) + (hide-panel-anim bottom-anim-value + alpha-value + (- window-height))) obj (do (reset! current-obj obj) (show-panel-anim bottom-anim-value alpha-value)) :else - (do (reset! clear-timeout (js/setTimeout #(reset! current-obj nil) 600)) - (hide-panel-anim bottom-anim-value alpha-value (- window-height)))))) + (do (reset! clear-timeout (js/setTimeout #(reset! current-obj + nil) + 600)) + (hide-panel-anim bottom-anim-value + alpha-value + (- window-height)))))) :reagent-render (fn [] (if @current-obj - [react/keyboard-avoiding-view {:style {:position :absolute :top 0 :bottom 0 :left 0 :right 0} - :ignore-offset true} + [react/keyboard-avoiding-view + {:style {:position :absolute :top 0 :bottom 0 :left 0 :right 0} + :ignore-offset true} [react/view {:flex 1} (when (and platform/ios? show-overlay?) - [react/animated-view {:flex 1 :background-color colors/black-persist :opacity alpha-value}]) + [react/animated-view + {:flex 1 + :background-color colors/black-persist + :opacity alpha-value}]) (when on-touch-outside - [react/touchable-opacity {:active-opacity 0 - :on-press on-touch-outside - :style {:flex 1}}]) - [react/animated-view {:style {:position :absolute - :transform [{:translateY bottom-anim-value}] - :bottom 0 :left 0 :right 0}} + [react/touchable-opacity + {:active-opacity 0 + :on-press on-touch-outside + :style {:flex 1}}]) + [react/animated-view + {:style {:position :absolute + :transform [{:translateY bottom-anim-value}] + :bottom 0 + :left 0 + :right 0}} [react/view {:flex 1} [render @current-obj]]]]] ;;TODO this is not great, improve! #(do (on-close) nil)))}))) -(views/defview animated-bottom-panel [val view on-close on-touch-outside show-overlay?] +(views/defview animated-bottom-panel + [val view on-close on-touch-outside show-overlay?] (views/letsubs [{window-height :height} [:dimensions/window]] - [bottom-panel (when val (select-keys val [:from :contact :amount :token :approve? :message :cancel? :hash :name :url :icons :wc-version :params :connector :description :topic :relay :self :peer :permissions :state])) view window-height on-close on-touch-outside (if-not (nil? show-overlay?) show-overlay? true)])) + [bottom-panel + (when val + (select-keys val + [:from :contact :amount :token :approve? :message :cancel? :hash :name :url :icons + :wc-version :params :connector :description :topic :relay :self :peer :permissions + :state])) view window-height on-close on-touch-outside + (if-not (nil? show-overlay?) show-overlay? true)])) diff --git a/src/status_im/ui/components/chat_icon/screen.cljs b/src/status_im/ui/components/chat_icon/screen.cljs index 7532d4a50d..fc050ce110 100644 --- a/src/status_im/ui/components/chat_icon/screen.cljs +++ b/src/status_im/ui/components/chat_icon/screen.cljs @@ -1,31 +1,32 @@ (ns status-im.ui.components.chat-icon.screen (:require [clojure.string :as string] + [quo.design-system.colors :as colors] + [quo.react-native :as rn] [re-frame.core :as re-frame.core] [status-im.multiaccounts.core :as multiaccounts] - [status-im.ui.components.icons.icons :as icons] - [status-im.ui.components.chat-icon.styles :as styles] - [quo.design-system.colors :as colors] - [status-im.ui.screens.chat.photos :as photos] [status-im.profile.db :as profile.db] - [status-im.ui.screens.profile.visibility-status.utils :as visibility-status-utils] - [quo.react-native :as rn])) + [status-im.ui.components.chat-icon.styles :as styles] + [status-im.ui.components.icons.icons :as icons] + [status-im.ui.screens.chat.photos :as photos] + [status-im.ui.screens.profile.visibility-status.utils :as visibility-status-utils])) ;;TODO REWORK THIS NAMESPACE (def get-name-first-char (memoize (fn [name] - ;; TODO: for now we check if the first letter is a # - ;; which means it is most likely a public chat and - ;; use the second letter if that is the case - ;; a broader refactoring should clean up upstream params - ;; for default-chat-icon + ;; TODO: for now we check if the first letter is a # + ;; which means it is most likely a public chat and + ;; use the second letter if that is the case + ;; a broader refactoring should clean up upstream params + ;; for default-chat-icon (string/capitalize (if (and (= "#" (first name)) (< 1 (count name))) (second name) (first name)))))) -(defn default-chat-icon [name styles] +(defn default-chat-icon + [name styles] (when-not (string/blank? name) [rn/view (:default-chat-icon styles) [rn/text {:style (:default-chat-icon-text styles)} @@ -39,29 +40,35 @@ (let [photo-path @(re-frame.core/subscribe [:chats/photo-path chat-id])] [photos/photo photo-path styles]))]) -(defn emoji-chat-icon [emoji styles] +(defn emoji-chat-icon + [emoji styles] (when-not (string/blank? emoji) [rn/view (:default-chat-icon styles) [rn/text {:style (:default-chat-icon-text styles)} emoji]])) (defn profile-photo-plus-dot-view [{:keys [public-key photo-container photo-path community?]}] - (let [photo-path (if (nil? photo-path) - @(re-frame.core/subscribe [:chats/photo-path public-key]) - photo-path) - photo-container (if (nil? photo-container) - styles/container-chat-list photo-container) + (let [photo-path (if (nil? photo-path) + @(re-frame.core/subscribe [:chats/photo-path public-key]) + photo-path) + photo-container (if (nil? photo-container) + styles/container-chat-list + photo-container) size (:width photo-container) identicon? (when photo-path (profile.db/base64-png? photo-path)) dot-styles (visibility-status-utils/icon-visibility-status-dot - public-key size identicon?) + public-key + size + identicon?) dot-accessibility-label (:accessibility-label dot-styles)] - [rn/view {:style photo-container - :accessibility-label :profile-photo} + [rn/view + {:style photo-container + :accessibility-label :profile-photo} [photos/photo photo-path {:size size}] (when-not community? - [rn/view {:style dot-styles - :accessibility-label dot-accessibility-label}])])) + [rn/view + {:style dot-styles + :accessibility-label dot-accessibility-label}])])) (defn emoji-chat-icon-view [chat-id group-chat name emoji styles] @@ -70,8 +77,9 @@ (if (string/blank? emoji) [default-chat-icon name styles] [emoji-chat-icon emoji styles]) - [profile-photo-plus-dot-view {:public-key chat-id - :photo-container (:default-chat-icon styles)}])]) + [profile-photo-plus-dot-view + {:public-key chat-id + :photo-container (:default-chat-icon styles)}])]) (defn chat-icon-view-toolbar [chat-id group-chat name color emoji size] @@ -129,19 +137,22 @@ (defn custom-icon-view-list [name color & [size]] [rn/view (styles/container-list-size (or size 40)) - [default-chat-icon name {:default-chat-icon (styles/default-chat-icon-profile color (or size 40)) - :default-chat-icon-text (styles/default-chat-icon-text (or size 40))}]]) + [default-chat-icon name + {:default-chat-icon (styles/default-chat-icon-profile color (or size 40)) + :default-chat-icon-text (styles/default-chat-icon-text (or size 40))}]]) (defn contact-icon-view [contact {:keys [container] :as styles}] [rn/view container [photos/photo (multiaccounts/displayed-photo contact) styles]]) -(defn contact-icon-contacts-tab [photo-path] +(defn contact-icon-contacts-tab + [photo-path] [rn/view styles/container-chat-list [photos/photo photo-path {:size 40}]]) -(defn dapp-icon-permission [contact size] +(defn dapp-icon-permission + [contact size] [contact-icon-view contact {:container {:width size :height size} :size size @@ -149,14 +160,16 @@ :default-chat-icon (styles/default-chat-icon-profile colors/default-chat-color size) :default-chat-icon-text (styles/default-chat-icon-text size)}]) -(defn chat-intro-icon-view [icon-text chat-id group-chat styles] +(defn chat-intro-icon-view + [icon-text chat-id group-chat styles] (if group-chat [default-chat-icon icon-text styles] (let [photo-path @(re-frame.core/subscribe [:chats/photo-path chat-id])] (when-not (string/blank? photo-path) [photos/photo photo-path styles])))) -(defn emoji-chat-intro-icon-view [icon-text chat-id group-chat emoji styles] +(defn emoji-chat-intro-icon-view + [icon-text chat-id group-chat emoji styles] (if group-chat (if (string/blank? emoji) [default-chat-icon icon-text styles] @@ -173,13 +186,15 @@ :default-chat-icon (styles/default-chat-icon-profile color size) :default-chat-icon-text (if (string/blank? emoji) (styles/default-chat-icon-text size) - (styles/emoji-chat-icon-text size))} override-styles)] + (styles/emoji-chat-icon-text size))} + override-styles)] [rn/view (:container styles) (if (and photo-path (seq photo-path)) - [profile-photo-plus-dot-view {:photo-path photo-path - :public-key public-key - :photo-container (:container styles) - :community? community?}] + [profile-photo-plus-dot-view + {:photo-path photo-path + :public-key public-key + :photo-container (:container styles) + :community? community?}] (if (string/blank? emoji) [default-chat-icon name styles] [emoji-chat-icon emoji styles])) diff --git a/src/status_im/ui/components/chat_icon/styles.cljs b/src/status_im/ui/components/chat_icon/styles.cljs index 0c2746fba7..6ef0b4bef8 100644 --- a/src/status_im/ui/components/chat_icon/styles.cljs +++ b/src/status_im/ui/components/chat_icon/styles.cljs @@ -2,7 +2,8 @@ (:require [quo.design-system.colors :as colors] [status-im.ui.components.emoji-thumbnail.utils :as emoji-utils])) -(defn default-chat-icon [color] +(defn default-chat-icon + [color] {:margin 0 :width 40 :height 40 @@ -11,7 +12,8 @@ :border-radius 20 :background-color color}) -(defn default-chat-icon-redesign [color size] +(defn default-chat-icon-redesign + [color size] {:margin 0 :width size :height size @@ -20,49 +22,57 @@ :border-radius (/ size 2) :background-color color}) -(defn default-chat-icon-chat-list [color] +(defn default-chat-icon-chat-list + [color] (merge (default-chat-icon color) {:width 40 :height 40 :border-radius 20})) -(defn default-list-chat-icon-redesign [color size] +(defn default-list-chat-icon-redesign + [color size] (merge (default-chat-icon-redesign color size) {:width size :height size :border-radius (/ size 2)})) -(defn default-community-icon-chat-list [color] +(defn default-community-icon-chat-list + [color] (merge (default-chat-icon color) {:width 48 :height 48 :border-radius 48})) -(defn default-token-icon-chat-list [color] +(defn default-token-icon-chat-list + [color] (merge (default-chat-icon color) {:width 20 :height 20 :border-radius 20})) -(defn default-chat-icon-chat-toolbar [color size] +(defn default-chat-icon-chat-toolbar + [color size] (merge (default-chat-icon color) {:width size :height size :border-radius size})) -(defn default-chat-icon-profile [color size] +(defn default-chat-icon-profile + [color size] (merge (default-chat-icon color) {:width size :height size :border-radius (/ size 2)})) -(defn default-chat-icon-text [size] +(defn default-chat-icon-text + [size] {:color colors/white-transparent-70-persist :font-weight "700" :font-size (/ size 2) :line-height size}) -(defn emoji-chat-icon-text [size] +(defn emoji-chat-icon-text + [size] {:font-size (emoji-utils/emoji-font-size size) :line-height size :margin-top (emoji-utils/emoji-top-margin-for-vertical-alignment size)}) ;; Required for vertical alignment bug - Check function defination for more info @@ -73,7 +83,8 @@ :width 40 :height 40}) -(defn chat-icon-redesign [size] +(defn chat-icon-redesign + [size] {:margin 4 :border-radius (/ size 2) :width size @@ -85,7 +96,8 @@ :height 40 :margin 0})) -(defn community-status-icon [size] +(defn community-status-icon + [size] {:margin 4 :border-radius 10 :width size @@ -97,13 +109,15 @@ :height 48 :margin 0})) -(defn community-icon-chat-list-redesign [size] +(defn community-icon-chat-list-redesign + [size] (merge (chat-icon size) {:width size :height size :margin 0})) -(defn community-status-chat-list-icon [size] +(defn community-status-chat-list-icon + [size] (merge (community-status-icon size) {:width size :height size @@ -115,13 +129,15 @@ :height 20 :margin 0})) -(defn chat-icon-chat-toolbar [size] +(defn chat-icon-chat-toolbar + [size] (merge chat-icon {:width size :height size :margin 0})) -(defn custom-size-icon [size] +(defn custom-size-icon + [size] (merge chat-icon {:width size :height size @@ -145,15 +161,18 @@ {:width 48 :height 48}) -(defn container-list-size [size] +(defn container-list-size + [size] {:width size :height size}) -(defn container-chat-toolbar [size] +(defn container-chat-toolbar + [size] {:width size :height size}) -(defn chat-icon-profile-edit [] +(defn chat-icon-profile-edit + [] {:width 24 :height 24 :border-radius 12 diff --git a/src/status_im/ui/components/checkbox/styles.cljs b/src/status_im/ui/components/checkbox/styles.cljs index 702e3564f0..91783d652b 100644 --- a/src/status_im/ui/components/checkbox/styles.cljs +++ b/src/status_im/ui/components/checkbox/styles.cljs @@ -4,10 +4,11 @@ (def wrapper {:width 24 :height 24 :align-items :center :justify-content :center}) -(defn icon-check-container [checked?] +(defn icon-check-container + [checked?] {:background-color (if checked? colors/blue colors/gray-lighter) - :align-items :center - :justify-content :center - :border-radius 2 - :width 18 - :height 18}) \ No newline at end of file + :align-items :center + :justify-content :center + :border-radius 2 + :width 18 + :height 18}) \ No newline at end of file diff --git a/src/status_im/ui/components/checkbox/view.cljs b/src/status_im/ui/components/checkbox/view.cljs index 7b9b7fac28..9a58627069 100644 --- a/src/status_im/ui/components/checkbox/view.cljs +++ b/src/status_im/ui/components/checkbox/view.cljs @@ -1,6 +1,6 @@ (ns status-im.ui.components.checkbox.view - (:require [status-im.ui.components.checkbox.styles :as styles] - [quo.design-system.colors :as colors] + (:require [quo.design-system.colors :as colors] + [status-im.ui.components.checkbox.styles :as styles] [status-im.ui.components.icons.icons :as icons] [status-im.ui.components.react :as react])) @@ -20,6 +20,7 @@ {:on-press #(on-value-change (not checked?))})) (if checked? [icons/tiny-icon - :tiny-icons/tiny-check {:container-style (styles/icon-check-container true) - :color colors/white-persist}] - [react/view {:style (styles/icon-check-container false)}])]) + :tiny-icons/tiny-check + {:container-style (styles/icon-check-container true) + :color colors/white-persist}] + [react/view {:style (styles/icon-check-container false)}])]) diff --git a/src/status_im/ui/components/common/common.cljs b/src/status_im/ui/components/common/common.cljs index 14e637da32..69588dacd9 100644 --- a/src/status_im/ui/components/common/common.cljs +++ b/src/status_im/ui/components/common/common.cljs @@ -16,31 +16,36 @@ ([{:keys [size accessibility-label] :or {size 18}} value] (let [more-than-9 (> value 9)] [react/view {:style (styles/counter-container size)} - [react/text (cond-> {:style (styles/counter-label size)} - accessibility-label - (assoc :accessibility-label accessibility-label)) + [react/text + (cond-> {:style (styles/counter-label size)} + accessibility-label + (assoc :accessibility-label accessibility-label)) (if more-than-9 (i18n/label :t/counter-9-plus) value)]]))) (def small-screen-image-k 0.8) (def small-screen-height 600) -(defview image-contain [{:keys [container-style style]} {:keys [image width height]}] - (letsubs [content-width (reagent/atom 0) +(defview image-contain + [{:keys [container-style style]} {:keys [image width height]}] + (letsubs [content-width (reagent/atom 0) {window-width :width window-height :height} [:dimensions/window]] - [react/view {:style (merge styles/image-contain container-style) - :on-layout #(reset! content-width (-> ^js % .-nativeEvent .-layout .-width))} - [react/image {:source image - :resize-mode :contain - :style (merge style - (if (> window-height window-width) - {:width (* @content-width - (if (< window-height small-screen-height) - small-screen-image-k - 1)) - :height (/ (* @content-width height - (if (< window-height small-screen-height) - small-screen-image-k - 1)) - width)} - {:width @content-width - :height (* window-height small-screen-image-k)}))}]])) + [react/view + {:style (merge styles/image-contain container-style) + :on-layout #(reset! content-width (-> ^js % .-nativeEvent .-layout .-width))} + [react/image + {:source image + :resize-mode :contain + :style (merge style + (if (> window-height window-width) + {:width (* @content-width + (if (< window-height small-screen-height) + small-screen-image-k + 1)) + :height (/ (* @content-width + height + (if (< window-height small-screen-height) + small-screen-image-k + 1)) + width)} + {:width @content-width + :height (* window-height small-screen-image-k)}))}]])) diff --git a/src/status_im/ui/components/common/styles.cljs b/src/status_im/ui/components/common/styles.cljs index e4d84c8b5e..50717a1704 100644 --- a/src/status_im/ui/components/common/styles.cljs +++ b/src/status_im/ui/components/common/styles.cljs @@ -1,7 +1,8 @@ (ns status-im.ui.components.common.styles (:require [quo.design-system.colors :as colors])) -(defn logo-container [size] +(defn logo-container + [size] {:width size :height size :border-radius size @@ -9,13 +10,15 @@ :align-items :center :justify-content :center}) -(defn logo [icon-size] - {:width icon-size - :height icon-size - :color :none +(defn logo + [icon-size] + {:width icon-size + :height icon-size + :color :none :container-style {}}) -(defn counter-container [size] +(defn counter-container + [size] {:width size :height size :border-radius (/ size 2) @@ -23,7 +26,8 @@ :align-items :center :justify-content :center}) -(defn counter-label [size] +(defn counter-label + [size] {:font-size (inc (/ size 2)) :typography :main-medium :color colors/white-persist diff --git a/src/status_im/ui/components/connectivity/view.cljs b/src/status_im/ui/components/connectivity/view.cljs index d2abc0a0e4..e91dca1e94 100644 --- a/src/status_im/ui/components/connectivity/view.cljs +++ b/src/status_im/ui/components/connectivity/view.cljs @@ -1,15 +1,15 @@ (ns status-im.ui.components.connectivity.view - (:require [re-frame.core :as re-frame] + (:require [clojure.string :as string] + [quo.core :as quo] + [quo.design-system.colors :as colors] + [re-frame.core :as re-frame] [status-im.i18n.i18n :as i18n] [status-im.ui.components.animation :as animation] - [quo.design-system.colors :as colors] - [status-im.ui.components.react :as react] - [quo.core :as quo] - [clojure.string :as string]) - (:require-macros - [status-im.utils.views :as views :refer [defview letsubs]])) + [status-im.ui.components.react :as react]) + (:require-macros [status-im.utils.views :as views :refer [defview letsubs]])) -(defn easing [direction n] +(defn easing + [direction n] {:toValue n :easing ((if (= :in direction) (animation/easing-in) @@ -18,7 +18,8 @@ :duration 400 :useNativeDriver true}) -(defn animated-bar-style [margin-value width color] +(defn animated-bar-style + [margin-value width color] {:position :absolute :width width :transform [{:translateX @@ -29,8 +30,9 @@ :height 3 :background-color color}) -(views/defview loading-indicator-anim [parent-width] - (views/letsubs [blue-bar-left-margin (animation/create-value 0) +(views/defview loading-indicator-anim + [parent-width] + (views/letsubs [blue-bar-left-margin (animation/create-value 0) white-bar-left-margin (animation/create-value 0)] {:component-did-mount (fn [_] @@ -50,111 +52,126 @@ [(animation/timing blue-bar-left-margin (easing :out 0)) (animation/timing white-bar-left-margin (easing :out 0))])]))))} [react/view - [react/view {:style {:width parent-width - :position :absolute - :top -3 - :z-index 3 - :height 3 - :background-color colors/white} - :accessibility-label :loading-indicator} - [react/animated-view {:style (animated-bar-style blue-bar-left-margin - parent-width - colors/blue)}] - [react/animated-view {:style (assoc (animated-bar-style white-bar-left-margin - parent-width - colors/white) - :left (* 0.15 parent-width))}]]])) + [react/view + {:style {:width parent-width + :position :absolute + :top -3 + :z-index 3 + :height 3 + :background-color colors/white} + :accessibility-label :loading-indicator} + [react/animated-view + {:style (animated-bar-style blue-bar-left-margin + parent-width + colors/blue)}] + [react/animated-view + {:style (assoc (animated-bar-style white-bar-left-margin + parent-width + colors/white) + :left + (* 0.15 parent-width))}]]])) -(defview loading-indicator [] - (letsubs [fetching? [:mailserver/fetching?] +(defview loading-indicator + [] + (letsubs [fetching? [:mailserver/fetching?] window-width [:dimensions/window-width]] (when fetching? [loading-indicator-anim window-width]))) -(defn hide-sheet-and-dispatch [event] +(defn hide-sheet-and-dispatch + [event] (re-frame/dispatch [:bottom-sheet/hide]) (re-frame/dispatch event)) -(defview connectivity-sheet [] - (letsubs [{:keys [peers node mobile sync]} [:connectivity/state] - current-mailserver-name [:mailserver/current-name] - peers-count [:peers-count] +(defview connectivity-sheet + [] + (letsubs [{:keys [peers node mobile sync]} [:connectivity/state] + current-mailserver-name [:mailserver/current-name] + peers-count [:peers-count] {:keys [syncing-on-mobile-network?]} [:multiaccount]] [:<> [quo/header {:title (i18n/label :t/connection-status) :border-bottom false}] [quo/list-header (i18n/label :t/peer-to-peer)] (if (= peers :offline) [quo/list-item - {:title (i18n/label :t/not-connected-to-peers) + {:title (i18n/label :t/not-connected-to-peers) :accessibility-label "not-connected-to-peers" - :subtitle (i18n/label :t/unable-to-send-messages) - :subtitle-max-lines 2 - :theme :negative - :icon :main-icons/network}] + :subtitle (i18n/label :t/unable-to-send-messages) + :subtitle-max-lines 2 + :theme :negative + :icon :main-icons/network}] [quo/list-item - {:title (str (i18n/label :t/connected-to) " " peers-count " " (string/lower-case (i18n/label :t/peers))) + {:title (str (i18n/label :t/connected-to) + " " peers-count + " " (string/lower-case (i18n/label :t/peers))) :accessibility-label "connected-to-n-peers" - :subtitle (i18n/label :t/can-send-messages) - :subtitle-max-lines 2 - :theme :positive - :icon :main-icons/network}]) + :subtitle (i18n/label :t/can-send-messages) + :subtitle-max-lines 2 + :theme :positive + :icon :main-icons/network}]) [quo/list-header (i18n/label :t/history-nodes)] (cond (#{:error :offline} node) [quo/list-item - {:title (i18n/label :t/not-connected-nodes) + {:title (i18n/label :t/not-connected-nodes) :accessibility-label "not-connected-nodes" - :subtitle (i18n/label :t/unable-to-fetch) - :theme :negative - :icon :main-icons/mailserver}] + :subtitle (i18n/label :t/unable-to-fetch) + :theme :negative + :icon :main-icons/mailserver}] (= node :disabled) [quo/list-item - {:title (i18n/label :t/nodes-disabled) + {:title (i18n/label :t/nodes-disabled) :accessibility-label "nodes-disabled" - :subtitle (i18n/label :t/unable-to-fetch) - :disabled true - :icon :main-icons/mailserver}] + :subtitle (i18n/label :t/unable-to-fetch) + :disabled true + :icon :main-icons/mailserver}] (and mobile (not sync)) [quo/list-item - {:title (i18n/label :t/waiting-wi-fi) + {:title (i18n/label :t/waiting-wi-fi) :accessibility-label "waiting-wi-fi" - :subtitle (i18n/label :t/unable-to-fetch) - :disabled true - :icon :main-icons/mailserver}] + :subtitle (i18n/label :t/unable-to-fetch) + :disabled true + :icon :main-icons/mailserver}] (= node :connecting) [quo/list-item - {:title (i18n/label :t/connecting) + {:title (i18n/label :t/connecting) :accessibility-label "connecting" - :subtitle (i18n/label :t/unable-to-fetch) - :icon :main-icons/mailserver}] + :subtitle (i18n/label :t/unable-to-fetch) + :icon :main-icons/mailserver}] (= node :online) [quo/list-item - {:title (str (i18n/label :t/connected-to) " " current-mailserver-name) + {:title (str (i18n/label :t/connected-to) " " current-mailserver-name) :accessibility-label "connected-to-mailserver" - :subtitle (i18n/label :t/you-can-fetch) - :theme :positive - :icon :main-icons/mailserver}]) + :subtitle (i18n/label :t/you-can-fetch) + :theme :positive + :icon :main-icons/mailserver}]) [quo/list-item - {:title (i18n/label :t/settings) + {:title (i18n/label :t/settings) :accessibility-label "settings" - :theme :accent - :on-press #(hide-sheet-and-dispatch [:navigate-to :sync-settings]) - :icon :main-icons/settings}] + :theme :accent + :on-press #(hide-sheet-and-dispatch [:navigate-to :sync-settings]) + :icon :main-icons/settings}] (when mobile [:<> - [react/view {:margin-vertical 8 :background-color colors/gray-lighter :height 36 - :align-items :center :justify-content :center} + [react/view + {:margin-vertical 8 + :background-color colors/gray-lighter + :height 36 + :align-items :center + :justify-content :center} [react/text {:style {:color colors/gray}} (i18n/label :t/youre-on-mobile-network)]] [quo/list-item - {:title (i18n/label :t/mobile-network-use-mobile) + {:title (i18n/label :t/mobile-network-use-mobile) :accessibility-label "mobile-network-use-mobile" - :accessory :switch - :on-press #(re-frame/dispatch [:mobile-network/set-syncing (not syncing-on-mobile-network?)]) - :active syncing-on-mobile-network?}] + :accessory :switch + :on-press #(re-frame/dispatch [:mobile-network/set-syncing + (not syncing-on-mobile-network?)]) + :active syncing-on-mobile-network?}] [react/text {:style {:margin-horizontal 16 :margin-bottom 12 :color colors/gray}} (i18n/label :t/status-mobile-descr)]])])) -(defn get-icon [{:keys [peers node mobile sync]}] +(defn get-icon + [{:keys [peers node mobile sync]}] (if (or (= peers :offline) (= node :offline) (= node :connecting)) @@ -163,11 +180,13 @@ (if sync :main-icons/mobile-sync :main-icons/mobile-sync-off) (when (#{:error :disabled} node) :main-icons/node-offline)))) -(defview connectivity-button [] +(defview connectivity-button + [] (letsubs [state [:connectivity/state]] (when-let [icon (get-icon state)] - [quo/button {:type :icon - :accessibility-label (str "conn-button-" (name icon)) - :on-press #(re-frame/dispatch [:bottom-sheet/show-sheet + [quo/button + {:type :icon + :accessibility-label (str "conn-button-" (name icon)) + :on-press #(re-frame/dispatch [:bottom-sheet/show-sheet {:content connectivity-sheet}]) - :theme (if (= (:peers state) :offline) :negative :secondary)} icon]))) + :theme (if (= (:peers state) :offline) :negative :secondary)} icon]))) diff --git a/src/status_im/ui/components/copyable_text.cljs b/src/status_im/ui/components/copyable_text.cljs index e7781ea01f..8bb5ded708 100644 --- a/src/status_im/ui/components/copyable_text.cljs +++ b/src/status_im/ui/components/copyable_text.cljs @@ -1,11 +1,12 @@ (ns status-im.ui.components.copyable-text - (:require [reagent.core :as reagent] + (:require [quo.design-system.colors :as colors] + [reagent.core :as reagent] [status-im.i18n.i18n :as i18n] [status-im.ui.components.animation :as animation] - [quo.design-system.colors :as colors] [status-im.ui.components.react :as react])) -(defn hide-cue-atom [anim-opacity anim-y cue-atom] +(defn hide-cue-atom + [anim-opacity anim-y cue-atom] (animation/start (animation/parallel [(animation/timing @@ -24,7 +25,8 @@ :useNativeDriver true})]) #(reset! cue-atom false))) -(defn show-cue-atom [anim-opacity anim-y cue-atom y] +(defn show-cue-atom + [anim-opacity anim-y cue-atom y] (when @cue-atom (animation/start (animation/parallel @@ -42,7 +44,8 @@ :useNativeDriver true})]) #(hide-cue-atom anim-opacity anim-y cue-atom)))) -(defn copy-action-visual-cue [anim-opacity anim-y width cue-atom] +(defn copy-action-visual-cue + [anim-opacity anim-y width cue-atom] [react/animated-view {:style {:opacity anim-opacity @@ -80,12 +83,12 @@ (defn copyable-text-view [{:keys [label container-style]} content] - (let [cue-atom (reagent/atom false) + (let [cue-atom (reagent/atom false) background-color (or (get container-style :background-color) colors/white) - width (reagent/atom 0) - height (reagent/atom 0) - anim-y (animation/create-value 0) - anim-opacity (animation/create-value 0)] + width (reagent/atom 0) + height (reagent/atom 0) + anim-y (animation/create-value 0) + anim-opacity (animation/create-value 0)] (reagent/create-class {:reagent-render (fn [{:keys [copied-text]} _] @@ -100,7 +103,7 @@ (- (+ 17 @height)))) (react/copy-to-clipboard copied-text))] [react/view - {:style (if container-style container-style {}) + {:style (if container-style container-style {}) :on-layout #(do (reset! width (-> ^js % .-nativeEvent .-layout .-width)) diff --git a/src/status_im/ui/components/dialog.cljs b/src/status_im/ui/components/dialog.cljs index a8e68b359a..964a92c2bd 100644 --- a/src/status_im/ui/components/dialog.cljs +++ b/src/status_im/ui/components/dialog.cljs @@ -1,12 +1,15 @@ (ns status-im.ui.components.dialog (:require ["react-native-dialogs" :default dialogs])) -(defn show [{:keys [title options cancel-text on-cancel]}] +(defn show + [{:keys [title options cancel-text on-cancel]}] (.. dialogs - (showPicker title nil (clj->js {:items (mapv #(select-keys % [:label]) - options) - :negativeText cancel-text - :positiveText nil})) + (showPicker title + nil + (clj->js {:items (mapv #(select-keys % [:label]) + options) + :negativeText cancel-text + :positiveText nil})) (then (fn [selected] (let [result (js->clj selected :keywordize-keys true)] (if (not= (get result :action) "actionSelect") diff --git a/src/status_im/ui/components/emoji_thumbnail/color_picker.cljs b/src/status_im/ui/components/emoji_thumbnail/color_picker.cljs index aa36508667..dc226e20ce 100644 --- a/src/status_im/ui/components/emoji_thumbnail/color_picker.cljs +++ b/src/status_im/ui/components/emoji_thumbnail/color_picker.cljs @@ -3,37 +3,39 @@ [status-im.ui.components.emoji-thumbnail.styles :as styles])) (def emoji-picker-colors-row1 - [{:name "red" :color "#F5A3A3" :key "1"} - {:name "pink" :color "#F5A3BF" :key "2"} + [{:name "red" :color "#F5A3A3" :key "1"} + {:name "pink" :color "#F5A3BF" :key "2"} {:name "magenta" :color "#E9A3F5" :key "3"} - {:name "purple" :color "#C0A3F5" :key "4"} - {:name "indigo" :color "#A3B0F5" :key "5"} - {:name "blue" :color "#A3C2F5" :key "6"} - {:name "cyan" :color "#A3DCF5" :key "7"}]) + {:name "purple" :color "#C0A3F5" :key "4"} + {:name "indigo" :color "#A3B0F5" :key "5"} + {:name "blue" :color "#A3C2F5" :key "6"} + {:name "cyan" :color "#A3DCF5" :key "7"}]) (def emoji-picker-colors-row2 - [{:name "teal" :color "#A3ECF5" :key "8"} - {:name "mint" :color "#A3F5E2" :key "9"} - {:name "green" :color "#A3F5BA" :key "10"} - {:name "moss" :color "#CFF5A3" :key "11"} - {:name "lemon" :color "#EEF5A3" :key "12"} - {:name "yellow" :color "#F5F5A3" :key "13"}]) + [{:name "teal" :color "#A3ECF5" :key "8"} + {:name "mint" :color "#A3F5E2" :key "9"} + {:name "green" :color "#A3F5BA" :key "10"} + {:name "moss" :color "#CFF5A3" :key "11"} + {:name "lemon" :color "#EEF5A3" :key "12"} + {:name "yellow" :color "#F5F5A3" :key "13"}]) (def emoji-picker-colors-row3 - [{:name "honey" :color "#F5E4A3" :key "14"} - {:name "orange" :color "#F5D7A3" :key "15"} - {:name "peach" :color "#F5B6A3" :key "16"} - {:name "brown" :color "#E0C2B8" :key "17"} - {:name "grey" :color "#CCCCCC" :key "18"} - {:name "dove" :color "#DAE2E7" :key "19"} - {:name "white" :color "#FFFFFF" :key "20"}]) + [{:name "honey" :color "#F5E4A3" :key "14"} + {:name "orange" :color "#F5D7A3" :key "15"} + {:name "peach" :color "#F5B6A3" :key "16"} + {:name "brown" :color "#E0C2B8" :key "17"} + {:name "grey" :color "#CCCCCC" :key "18"} + {:name "dove" :color "#DAE2E7" :key "19"} + {:name "white" :color "#FFFFFF" :key "20"}]) -(defn colors-row [color-circle container-style colors] +(defn colors-row + [color-circle container-style colors] [rn/view {:style container-style :accessibility-label :colors-row} (for [x colors] [color-circle x])]) -(defn color-picker-section [color-circle] +(defn color-picker-section + [color-circle] [:<> [colors-row ;; Row - 1st color-circle diff --git a/src/status_im/ui/components/emoji_thumbnail/preview.cljs b/src/status_im/ui/components/emoji_thumbnail/preview.cljs index 5f02c38b49..56ee2dfbb6 100644 --- a/src/status_im/ui/components/emoji_thumbnail/preview.cljs +++ b/src/status_im/ui/components/emoji_thumbnail/preview.cljs @@ -1,15 +1,18 @@ (ns status-im.ui.components.emoji-thumbnail.preview - (:require [status-im.ui.components.react :as react] + (:require [clojure.string :as string] [status-im.ui.components.emoji-thumbnail.styles :as styles] - [clojure.string :as string])) + [status-im.ui.components.react :as react])) -(defn emoji-thumbnail [emoji color size] +(defn emoji-thumbnail + [emoji color size] (when-not (string/blank? emoji) [react/view (styles/emoji-thumbnail-icon color size) - [react/text {:style (styles/emoji-thumbnail-icon-text size) - :accessibility-label :thumbnail-emoji} emoji]])) + [react/text + {:style (styles/emoji-thumbnail-icon-text size) + :accessibility-label :thumbnail-emoji} emoji]])) -(defn emoji-thumbnail-touchable [emoji color size func] +(defn emoji-thumbnail-touchable + [emoji color size func] (when-not (string/blank? emoji) [react/touchable-opacity {:on-press func} [emoji-thumbnail emoji color size]])) diff --git a/src/status_im/ui/components/emoji_thumbnail/styles.cljs b/src/status_im/ui/components/emoji_thumbnail/styles.cljs index 752f0f3d5b..fe54d38c76 100644 --- a/src/status_im/ui/components/emoji_thumbnail/styles.cljs +++ b/src/status_im/ui/components/emoji_thumbnail/styles.cljs @@ -1,23 +1,25 @@ (ns status-im.ui.components.emoji-thumbnail.styles (:require [quo.design-system.colors :as colors] - [status-im.utils.platform :as platform] - [status-im.ui.components.emoji-thumbnail.utils :as emoji-utils])) + [status-im.ui.components.emoji-thumbnail.utils :as emoji-utils] + [status-im.utils.platform :as platform])) -(defn emoji-thumbnail-icon [color size] - {:width size - :height size - :align-items :center - :justify-content :center - :border-radius (/ size 2) - :background-color color - :border-width 0.5 +(defn emoji-thumbnail-icon + [color size] + {:width size + :height size + :align-items :center + :justify-content :center + :border-radius (/ size 2) + :background-color color + :border-width 0.5 :border-color "rgba(0,0,0,0.1)" :accessibility-label :thumbnail-container-circle}) -(defn emoji-thumbnail-icon-text [size] - {:font-size (emoji-utils/emoji-font-size size) - :line-height size - :margin-top (emoji-utils/emoji-top-margin-for-vertical-alignment size)}) ;; Required for vertical alignment bug - Check function defination for more info +(defn emoji-thumbnail-icon-text + [size] + {:font-size (emoji-utils/emoji-font-size size) + :line-height size + :margin-top (emoji-utils/emoji-top-margin-for-vertical-alignment size)}) ;; Required for vertical alignment bug - Check function defination for more info ;; Styles Related to Emoji Thumbnail Picker @@ -27,16 +29,20 @@ (def emoji-picker-upper-components-size (if platform/android? 350 405)) -(defn emoji-picker-gray-color [] +(defn emoji-picker-gray-color + [] (if (colors/dark?) "#c3c3bc99" "#3C3C4399")) -(defn emoji-picker-category-container [] +(defn emoji-picker-category-container + [] (if (colors/dark?) "#110d0a" "#EEF2F5")) -(defn emoji-picker-active-category-container [] +(defn emoji-picker-active-category-container + [] (if (colors/dark?) "#87877f33" "#78788033")) -(defn emoji-picker-active-category-color [] +(defn emoji-picker-active-category-color + [] (if (colors/dark?) "#bbbbbb" "#000000")) (def emoji-thumbnail-preview @@ -46,26 +52,31 @@ :justify-content :center :accessibility-label :emoji-preview}) -(defn emoji-picker-keyboard-container [] +(defn emoji-picker-keyboard-container + [] {:border-radius 0 :background-color (colors/get-color :ui-background)}) -(defn emoji-picker-search-bar [] +(defn emoji-picker-search-bar + [] {:border-radius 10 :height 36 :background-color (:ui-01 @colors/theme)}) -(defn emoji-picker-search-bar-text [] +(defn emoji-picker-search-bar-text + [] {:color (emoji-picker-gray-color)}) -(defn emoji-picker-header [] +(defn emoji-picker-header + [] {:font-size 13 :color (emoji-picker-gray-color) :font-weight "600" :margin-top 7 :margin-bottom 0}) -(defn emoji-keyboard [func] +(defn emoji-keyboard + [func] {:onEmojiSelected func :emojiSize 23 :containerStyles (emoji-picker-keyboard-container) @@ -88,18 +99,19 @@ :flex-grow 1}) (def emoji-picker-row1-style - {:margin-top 10}) + {:margin-top 10}) (def emoji-picker-row2-style - {:margin-top 10 - :margin-bottom 10 - :margin-left 25 - :margin-right 25}) + {:margin-top 10 + :margin-bottom 10 + :margin-left 25 + :margin-right 25}) (def emoji-picker-row3-style - {:margin-bottom 10}) + {:margin-bottom 10}) -(defn emoji-picker-color-border [item_color color-selected?] +(defn emoji-picker-color-border + [item_color color-selected?] {:height 44 :width 44 :border-radius 22 @@ -108,13 +120,14 @@ :align-items :center :justify-content :center}) -(defn emoji-picker-color [item_color] - {:height 36 - :width 36 - :border-radius 18 - :border-width 0.5 +(defn emoji-picker-color + [item_color] + {:height 36 + :width 36 + :border-radius 18 + :border-width 0.5 :background-color item_color - :border-color "rgba(0,0,0,0.1)"}) + :border-color "rgba(0,0,0,0.1)"}) (def emoji-picker-default-thumbnails [{:emoji "🐺" :color "#CCCCCC"} diff --git a/src/status_im/ui/components/emoji_thumbnail/utils.cljs b/src/status_im/ui/components/emoji_thumbnail/utils.cljs index bf8b97c581..5c9d1fedbc 100644 --- a/src/status_im/ui/components/emoji_thumbnail/utils.cljs +++ b/src/status_im/ui/components/emoji_thumbnail/utils.cljs @@ -1,13 +1,15 @@ (ns status-im.ui.components.emoji-thumbnail.utils - (:require [status-im.utils.platform :as platform])) + (:require [status-im.utils.platform :as platform])) -(defn emoji-font-size [container_size] +(defn emoji-font-size + [container_size] (int (* (/ container_size 10) 6))) ;; React Native Bug: Till version 0.65 React Native has a bug. It doesn't center text/emoji ;; inside the container(In Android & Web Only). Even with the textAlign: "center" and other properties. ;; So this top margin is required so that emoji will center in the emoji circle. ;; More Info: https://github.com/facebook/react-native/issues/32198 -;; TODO: Remove this top margin, if future updates of react-native fix this issue. -(defn emoji-top-margin-for-vertical-alignment [container_size] +;; TODO: Remove this top margin, if future updates of react-native fix this issue. +(defn emoji-top-margin-for-vertical-alignment + [container_size] (if platform/android? (- (int (/ container_size 20))) 0)) diff --git a/src/status_im/ui/components/fast_image.cljs b/src/status_im/ui/components/fast_image.cljs index 2b879f83b4..af4afc09c9 100644 --- a/src/status_im/ui/components/fast_image.cljs +++ b/src/status_im/ui/components/fast_image.cljs @@ -3,25 +3,28 @@ [status-im.ui.components.icons.icons :as icons] [status-im.ui.components.react :as react])) -(defn placeholder [style child] +(defn placeholder + [style child] [react/view {:style (merge style {:flex 1 :justify-content :center :align-items :center})} child]) -(defn fast-image [_] +(defn fast-image + [_] (let [loaded? (reagent/atom false) - error? (reagent/atom false)] + error? (reagent/atom false)] (fn [props] - [react/fast-image-class (merge - props - {:on-error (fn [e] - (when-let [on-error (:on-error props)] - (on-error e)) - (reset! error? true)) - :on-load (fn [e] - (when-let [on-load (:on-load props)] - (on-load e)) - (reset! loaded? true) - (reset! error? false))}) + [react/fast-image-class + (merge + props + {:on-error (fn [e] + (when-let [on-error (:on-error props)] + (on-error e)) + (reset! error? true)) + :on-load (fn [e] + (when-let [on-load (:on-load props)] + (on-load e)) + (reset! loaded? true) + (reset! error? false))}) (when (or @error? (not @loaded?)) [placeholder (:style props) (if @error? diff --git a/src/status_im/ui/components/icons/icons.clj b/src/status_im/ui/components/icons/icons.clj index 1744fa2a6f..a160a2bcfa 100644 --- a/src/status_im/ui/components/icons/icons.clj +++ b/src/status_im/ui/components/icons/icons.clj @@ -4,12 +4,14 @@ (def icon-path "./resources/images/icons/") -(defn require-icon [el] +(defn require-icon + [el] (let [s (str "../resources/images/icons/" el ".png") k (string/replace el "_" "-")] [k `(js/require ~s)])) -(defmacro resolve-icons [] +(defmacro resolve-icons + [] (let [files (->> (io/file icon-path) file-seq (filter #(string/ends-with? % "png")) diff --git a/src/status_im/ui/components/icons/icons.cljs b/src/status_im/ui/components/icons/icons.cljs index cf89b5e8df..67ec599fc7 100644 --- a/src/status_im/ui/components/icons/icons.cljs +++ b/src/status_im/ui/components/icons/icons.cljs @@ -1,25 +1,27 @@ (ns status-im.ui.components.icons.icons - (:require [status-im.ui.components.react :as react] - [quo.design-system.colors :as colors]) + (:require [quo.design-system.colors :as colors] + [status-im.ui.components.react :as react]) (:refer-clojure :exclude [use]) (:require-macros [status-im.ui.components.icons.icons :as icons])) (def icons (icons/resolve-icons)) -(defn icon-source [icon] +(defn icon-source + [icon] (get icons (name icon))) -(defn- match-color [color] +(defn- match-color + [color] (cond (keyword? color) (case color - :dark colors/black - :gray colors/gray - :blue colors/blue + :dark colors/black + :gray colors/gray + :blue colors/blue :active colors/blue - :white colors/white - :red colors/red - :none nil + :white colors/white + :red colors/red + :none nil colors/black) (string? color) color @@ -28,26 +30,29 @@ (defn memo-icon-fn ([name] (memo-icon-fn name nil)) - ([name {:keys [color resize-mode container-style - accessibility-label width height no-color] - :or {accessibility-label :icon}}] + ([name + {:keys [color resize-mode container-style + accessibility-label width height no-color] + :or {accessibility-label :icon}}] ^{:key name} - [react/image {:style (merge (cond-> {:width (or width 24) - :height (or height 24)} + [react/image + {:style (merge (cond-> {:width (or width 24) + :height (or height 24)} - resize-mode - (assoc :resize-mode resize-mode) + resize-mode + (assoc :resize-mode resize-mode) - (not no-color) - (assoc :tint-color (match-color color))) - container-style) - :accessibility-label accessibility-label - :source (icon-source name)}])) + (not no-color) + (assoc :tint-color (match-color color))) + container-style) + :accessibility-label accessibility-label + :source (icon-source name)}])) (def icon (memoize memo-icon-fn)) (defn tiny-icon ([name] (tiny-icon name {})) ([name options] - (icon name (merge {:width 16 :height 16} - options)))) + (icon name + (merge {:width 16 :height 16} + options)))) diff --git a/src/status_im/ui/components/invite/events.cljs b/src/status_im/ui/components/invite/events.cljs index 48d093ffd8..4a7d4a55ad 100644 --- a/src/status_im/ui/components/invite/events.cljs +++ b/src/status_im/ui/components/invite/events.cljs @@ -1,8 +1,8 @@ (ns status-im.ui.components.invite.events (:require [re-frame.core :as re-frame] - [status-im.utils.fx :as fx] [status-im.i18n.i18n :as i18n] [status-im.ui.components.react :as react] + [status-im.utils.fx :as fx] [status-im.utils.universal-links.utils :as universal-links])) (re-frame/reg-fx @@ -14,7 +14,9 @@ {:events [::share-link]} [{:keys [db]}] (let [{:keys [public-key preferred-name]} (get db :multiaccount) - profile-link (universal-links/generate-link :user :external - (or preferred-name public-key)) + profile-link (universal-links/generate-link :user + :external + (or preferred-name + public-key)) message (i18n/label :t/join-me {:url profile-link})] {::share {:message message}})) diff --git a/src/status_im/ui/components/invite/views.cljs b/src/status_im/ui/components/invite/views.cljs index e1bacffabc..89d3d88fbd 100644 --- a/src/status_im/ui/components/invite/views.cljs +++ b/src/status_im/ui/components/invite/views.cljs @@ -1,15 +1,18 @@ (ns status-im.ui.components.invite.views (:require [quo.core :as quo] [re-frame.core :as re-frame] - [status-im.ui.components.invite.events :as invite.events] - [status-im.i18n.i18n :as i18n])) + [status-im.i18n.i18n :as i18n] + [status-im.ui.components.invite.events :as invite.events])) -(defn button [] - [quo/button {:on-press #(re-frame/dispatch [::invite.events/share-link nil]) - :accessibility-label :invite-friends-button} +(defn button + [] + [quo/button + {:on-press #(re-frame/dispatch [::invite.events/share-link nil]) + :accessibility-label :invite-friends-button} (i18n/label :t/invite-friends)]) -(defn list-item [{:keys [accessibility-label]}] +(defn list-item + [{:keys [accessibility-label]}] [quo/list-item {:theme :accent :title (i18n/label :t/invite-friends) @@ -18,7 +21,8 @@ :on-press (fn [] (re-frame/dispatch [:bottom-sheet/hide]) (js/setTimeout - #(re-frame/dispatch [::invite.events/share-link nil]) 250))}]) + #(re-frame/dispatch [::invite.events/share-link nil]) + 250))}]) diff --git a/src/status_im/ui/components/keyboard_avoid_presentation.cljs b/src/status_im/ui/components/keyboard_avoid_presentation.cljs index 9b261737d7..e44ba3e79f 100644 --- a/src/status_im/ui/components/keyboard_avoid_presentation.cljs +++ b/src/status_im/ui/components/keyboard_avoid_presentation.cljs @@ -1,18 +1,23 @@ (ns status-im.ui.components.keyboard-avoid-presentation - (:require [status-im.ui.components.react :as react] - [oops.core :refer [oget]] - [reagent.core :as reagent])) + (:require [oops.core :refer [oget]] + [reagent.core :as reagent] + [status-im.ui.components.react :as react])) -(defn keyboard-avoiding-view [] +(defn keyboard-avoiding-view + [] (let [this (reagent/current-component) props (reagent/props this) children (reagent/children this)] [react/safe-area-consumer (fn [insets] (let [vertical-offset (+ (oget insets "top") - ;; 20 is the margin-top for presentation modal + ;; 20 is the margin-top for presentation modal 20)] (reagent/as-element - (into [react/keyboard-avoiding-view (update props :keyboardVerticalOffset - + vertical-offset (if (:ignore-offset props) 44 0))] + (into [react/keyboard-avoiding-view + (update props + :keyboardVerticalOffset + + + vertical-offset + (if (:ignore-offset props) 44 0))] children))))])) diff --git a/src/status_im/ui/components/list/styles.cljs b/src/status_im/ui/components/list/styles.cljs index ddc8d389e8..efabe35907 100644 --- a/src/status_im/ui/components/list/styles.cljs +++ b/src/status_im/ui/components/list/styles.cljs @@ -53,11 +53,11 @@ {:margin-left 64})) (styles/def section-header - {:font-size 14 - :color colors/gray - :margin-left 16 - :margin-top 16 - :android {:margin-bottom 3} - :ios {:margin-bottom 10}}) + {:font-size 14 + :color colors/gray + :margin-left 16 + :margin-top 16 + :android {:margin-bottom 3} + :ios {:margin-bottom 10}}) (def section-header-container {}) \ No newline at end of file diff --git a/src/status_im/ui/components/list/views.cljs b/src/status_im/ui/components/list/views.cljs index 3467214084..67cee5539d 100644 --- a/src/status_im/ui/components/list/views.cljs +++ b/src/status_im/ui/components/list/views.cljs @@ -1,9 +1,9 @@ (ns status-im.ui.components.list.views - (:require [reagent.core :as reagent] + (:require ["react-native" :as react-native] + [reagent.core :as reagent] [status-im.ui.components.list.styles :as styles] [status-im.ui.components.react :as react] - [status-im.utils.platform :as platform] - ["react-native" :as react-native])) + [status-im.utils.platform :as platform])) (def flat-list-class (reagent/adapt-react-class (.-FlatList react-native))) (def section-list-class (reagent/adapt-react-class (.-SectionList react-native))) @@ -17,7 +17,9 @@ (def memo-separator-fn (memoize (fn [separator default-separator?] - (when-let [separator (or separator (when (and platform/ios? default-separator?) [react/view styles/separator]))] + (when-let [separator (or separator + (when (and platform/ios? default-separator?) + [react/view styles/separator]))] (fn [] (reagent/as-element separator)))))) @@ -35,12 +37,12 @@ (defn- base-list-props [{:keys [key-fn render-fn empty-component header footer separator default-separator? render-data]}] - (merge (when key-fn {:keyExtractor (memo-wrap-key-fn key-fn)}) - (when render-fn {:renderItem (memo-wrap-render-fn render-fn render-data)}) - (when separator {:ItemSeparatorComponent (memo-separator-fn separator default-separator?)}) - (when empty-component {:ListEmptyComponent (memo-as-element empty-component)}) - (when header {:ListHeaderComponent (memo-as-element header)}) - (when footer {:ListFooterComponent (memo-as-element footer)}))) + (merge (when key-fn {:keyExtractor (memo-wrap-key-fn key-fn)}) + (when render-fn {:renderItem (memo-wrap-render-fn render-fn render-data)}) + (when separator {:ItemSeparatorComponent (memo-separator-fn separator default-separator?)}) + (when empty-component {:ListEmptyComponent (memo-as-element empty-component)}) + (when header {:ListHeaderComponent (memo-as-element header)}) + (when footer {:ListFooterComponent (memo-as-element footer)}))) (defn flat-list "A wrapper for FlatList. @@ -55,33 +57,41 @@ props {:data (to-array data)})]))) -(defn- wrap-render-section-header-fn [f] +(defn- wrap-render-section-header-fn + [f] (fn [^js data] (let [^js section (.-section data)] - (reagent/as-element [f {:title (.-title section) - :data (.-data section)}])))) + (reagent/as-element [f + {:title (.-title section) + :data (.-data section)}])))) -(defn- default-render-section-header [{:keys [title data]}] +(defn- default-render-section-header + [{:keys [title data]}] (when (seq data) [react/view styles/section-header-container [react/text {:style styles/section-header} title]])) -(defn- wrap-per-section-render-fn [props] +(defn- wrap-per-section-render-fn + [props] (update (if-let [f (:render-fn props)] - (assoc (dissoc props :render-fn :render-data) :renderItem (memo-wrap-render-fn f (:render-data props))) + (assoc (dissoc props :render-fn :render-data) + :renderItem + (memo-wrap-render-fn f (:render-data props))) props) - :data to-array)) + :data + to-array)) (defn section-list "A wrapper for SectionList. To render something on empty sections, use renderSectionFooter and conditionaly render on empty data See https://facebook.github.io/react-native/docs/sectionlist.html" - [{:keys [sections render-section-header-fn render-section-footer-fn style] :as props - :or {render-section-header-fn default-render-section-header - style {}}}] + [{:keys [sections render-section-header-fn render-section-footer-fn style] + :as props + :or {render-section-header-fn default-render-section-header + style {}}}] [section-list-class (merge (base-list-props props) props diff --git a/src/status_im/ui/components/list_selection.cljs b/src/status_im/ui/components/list_selection.cljs index 65657f7667..356eb91c8a 100644 --- a/src/status_im/ui/components/list_selection.cljs +++ b/src/status_im/ui/components/list_selection.cljs @@ -4,23 +4,27 @@ [status-im.ui.components.action-sheet :as action-sheet] [status-im.ui.components.dialog :as dialog] [status-im.ui.components.react :as react] - [status-im.utils.platform :as platform] - [status-im.utils.http :as http])) + [status-im.utils.http :as http] + [status-im.utils.platform :as platform])) -(defn open-share [content] +(defn open-share + [content] (when (or (:message content) (:url content)) (.share ^js react/sharing (clj->js content)))) -(defn show [options] +(defn show + [options] (cond platform/ios? (action-sheet/show options) platform/android? (dialog/show options))) -(defn- platform-web-browser [] +(defn- platform-web-browser + [] (if platform/ios? :t/browsing-open-in-ios-web-browser :t/browsing-open-in-android-web-browser)) -(defn browse [link] +(defn browse + [link] (show {:title (i18n/label :t/browsing-title) :options [{:label (i18n/label :t/browsing-open-in-status) :action #(re-frame/dispatch [:browser.ui/open-url link])} @@ -28,7 +32,8 @@ :action #(.openURL ^js react/linking (http/normalize-url link))}] :cancel-text (i18n/label :t/browsing-cancel)})) -(defn browse-in-web-browser [link] +(defn browse-in-web-browser + [link] (show {:title (i18n/label :t/browsing-title) :options [{:label (i18n/label (platform-web-browser)) :action #(.openURL ^js react/linking (http/normalize-url link))}] diff --git a/src/status_im/ui/components/permissions.cljs b/src/status_im/ui/components/permissions.cljs index 26a3612b1a..ca3ce31bb8 100644 --- a/src/status_im/ui/components/permissions.cljs +++ b/src/status_im/ui/components/permissions.cljs @@ -1,6 +1,6 @@ (ns status-im.ui.components.permissions - (:require [status-im.utils.platform :as platform] - ["react-native-permissions" :refer (requestMultiple PERMISSIONS RESULTS)])) + (:require ["react-native-permissions" :refer (requestMultiple PERMISSIONS RESULTS)] + [status-im.utils.platform :as platform])) (def permissions-map {:read-external-storage (cond @@ -14,7 +14,8 @@ platform/android? (.-RECORD_AUDIO (.-ANDROID PERMISSIONS)) platform/ios? (.-MICROPHONE (.-IOS PERMISSIONS)))}) -(defn all-granted? [permissions] +(defn all-granted? + [permissions] (let [permission-vals (distinct (vals permissions))] (and (= (count permission-vals) 1) (not (#{(.-BLOCKED RESULTS) (.-DENIED RESULTS)} (first permission-vals)))))) diff --git a/src/status_im/ui/components/plus_button.cljs b/src/status_im/ui/components/plus_button.cljs index 3072916c8c..772d616023 100644 --- a/src/status_im/ui/components/plus_button.cljs +++ b/src/status_im/ui/components/plus_button.cljs @@ -1,8 +1,8 @@ (ns status-im.ui.components.plus-button - (:require [quo.design-system.colors :as colors] - [quo.core :as quo] - [status-im.ui.components.react :as react] - [status-im.ui.components.icons.icons :as icons])) + (:require [quo.core :as quo] + [quo.design-system.colors :as colors] + [status-im.ui.components.icons.icons :as icons] + [status-im.ui.components.react :as react])) (def action-button-container-old {:position :absolute @@ -14,7 +14,8 @@ :bottom 16 :height 40}) -(defn action-button-old [] +(defn action-button-old + [] {:width 40 :height 40 :background-color colors/blue @@ -29,13 +30,16 @@ "rgba(0, 12, 63, 0.2)") :elevation 2}) -(defn plus-button-old [{:keys [on-press loading accessibility-label]}] +(defn plus-button-old + [{:keys [on-press loading accessibility-label]}] [react/view action-button-container-old - [quo/button {:type :scale - :accessibility-label (or accessibility-label :plus-button) - :on-press on-press} + [quo/button + {:type :scale + :accessibility-label (or accessibility-label :plus-button) + :on-press on-press} [react/view (action-button-old) (if loading - [react/activity-indicator {:color colors/white-persist - :animating true}] + [react/activity-indicator + {:color colors/white-persist + :animating true}] [icons/icon :main-icons/add {:color colors/white-persist}])]]]) \ No newline at end of file diff --git a/src/status_im/ui/components/profile_header/view.cljs b/src/status_im/ui/components/profile_header/view.cljs index 506c7b6f58..5f112a2857 100644 --- a/src/status_im/ui/components/profile_header/view.cljs +++ b/src/status_im/ui/components/profile_header/view.cljs @@ -1,17 +1,18 @@ (ns status-im.ui.components.profile-header.view - (:require [quo.core :as quo] - [quo.animated :as animated] - [quo.design-system.spacing :as spacing] + (:require [quo.animated :as animated] + [quo.core :as quo] [quo.design-system.colors :as colors] + [quo.design-system.spacing :as spacing] [quo.react-native :as rn] - [status-im.ui.components.icons.icons :as icons] - [status-im.ui.components.chat-icon.screen :as chat-icon.screen])) + [status-im.ui.components.chat-icon.screen :as chat-icon.screen] + [status-im.ui.components.icons.icons :as icons])) (def avatar-extended-size 64) (def avatar-minimized-size 40) (def subtitle-margin 4) -(defn container-style [{:keys [animation minimized]}] +(defn container-style + [{:keys [animation minimized]}] (merge {:flex-direction :row :padding-vertical 4 :align-items :center} @@ -19,23 +20,26 @@ (:base spacing/padding-horizontal) {:opacity animation}))) -(defn header-bottom-separator [] +(defn header-bottom-separator + [] {:margin-bottom (:tiny spacing/spacing) :height (:small spacing/spacing) :border-bottom-width 1 :border-bottom-color (:ui-01 @colors/theme)}) -(defn header-text [] - {:padding-left (:base spacing/spacing) - :flex 1 - :justify-content :center}) +(defn header-text + [] + {:padding-left (:base spacing/spacing) + :flex 1 + :justify-content :center}) -(defn header-subtitle [{:keys [minimized]}] +(defn header-subtitle + [{:keys [minimized]}] (merge {:padding-right (:large spacing/spacing) :flex-direction :row :align-items :center} (when-not minimized - {:padding-top subtitle-margin}))) + {:padding-top subtitle-margin}))) (defn extended-header [{:keys [title photo color membership subtitle subtitle-icon on-edit on-press monospace @@ -51,46 +55,55 @@ (into wrapper [[animated/view {:pointer-events :box-none} - [animated/view {:style (container-style {:animation animation - :minimized minimized}) - :pointer-events :box-none} + [animated/view + {:style (container-style {:animation animation + :minimized minimized}) + :pointer-events :box-none} (into editable [[animated/view {:pointer-events :box-none} [chat-icon.screen/profile-icon-view photo title color emoji (and (not minimized) on-edit) (if minimized avatar-minimized-size avatar-extended-size) nil public-key community?]]]) - [animated/view {:style (header-text) - :pointer-events :box-none} - [quo/text {:animated? true - :number-of-lines (if minimized 1 2) - :size (if minimized :base :x-large) - :weight :bold - :elipsize-mode :tail - :accessibility-role :text - :accessibility-label :default-username} + [animated/view + {:style (header-text) + :pointer-events :box-none} + [quo/text + {:animated? true + :number-of-lines (if minimized 1 2) + :size (if minimized :base :x-large) + :weight :bold + :elipsize-mode :tail + :accessibility-role :text + :accessibility-label :default-username} title] - (when membership [quo/text {:number-of-lines 1 - :ellipsize-mode :middle - :monospace monospace - :size (if minimized :small :base) - :color :secondary} - membership]) + (when membership + [quo/text + {:number-of-lines 1 + :ellipsize-mode :middle + :monospace monospace + :size (if minimized :small :base) + :color :secondary} + membership]) (when subtitle - [animated/view {:style (header-subtitle {:minimized minimized}) - :pointer-events :box-none} + [animated/view + {:style (header-subtitle {:minimized minimized}) + :pointer-events :box-none} (when subtitle-icon - [icons/icon subtitle-icon {:color (:icon-02 @colors/theme) - :width 16 - :height 16 - :container-style {:margin-right 4}}]) - [quo/text {:number-of-lines 1 - :ellipsize-mode :middle - :monospace monospace - :size (if minimized :small :base) - :color :secondary} + [icons/icon subtitle-icon + {:color (:icon-02 @colors/theme) + :width 16 + :height 16 + :container-style {:margin-right 4}}]) + [quo/text + {:number-of-lines 1 + :ellipsize-mode :middle + :monospace monospace + :size (if minimized :small :base) + :color :secondary} subtitle]])]] (when-not minimized - [animated/view {:pointer-events :none - :style (when bottom-separator (header-bottom-separator))}])]])))) + [animated/view + {:pointer-events :none + :style (when bottom-separator (header-bottom-separator))}])]])))) diff --git a/src/status_im/ui/components/qr_code_viewer/styles.cljs b/src/status_im/ui/components/qr_code_viewer/styles.cljs index 2d43afe168..af02a05b28 100644 --- a/src/status_im/ui/components/qr_code_viewer/styles.cljs +++ b/src/status_im/ui/components/qr_code_viewer/styles.cljs @@ -3,14 +3,15 @@ (def qr-code-padding 16) -(defn qr-code-container [width] - {:align-self :center - :width width - :height width +(defn qr-code-container + [width] + {:align-self :center + :width width + :height width :padding-horizontal 16 - :background-color colors/white-persist - :border-color colors/black-transparent - :align-items :center - :justify-content :center - :border-width 1 - :border-radius 8}) \ No newline at end of file + :background-color colors/white-persist + :border-color colors/black-transparent + :align-items :center + :justify-content :center + :border-width 1 + :border-radius 8}) \ No newline at end of file diff --git a/src/status_im/ui/components/qr_code_viewer/views.cljs b/src/status_im/ui/components/qr_code_viewer/views.cljs index 16b8cd74ca..0425b703eb 100644 --- a/src/status_im/ui/components/qr_code_viewer/views.cljs +++ b/src/status_im/ui/components/qr_code_viewer/views.cljs @@ -1,14 +1,15 @@ (ns status-im.ui.components.qr-code-viewer.views - (:require [cljs-bean.core :as bean] + (:require ["qrcode" :as qr-code-js] + ["react-native-svg" :refer (SvgXml)] + [cljs-bean.core :as bean] [reagent.core :as reagent] [status-im.ui.components.qr-code-viewer.styles :as styles] - [status-im.ui.components.react :as react] - ["qrcode" :as qr-code-js] - ["react-native-svg" :refer (SvgXml)])) + [status-im.ui.components.react :as react])) (def svgxml (reagent/adapt-react-class SvgXml)) -(defn qr-code [{:keys [size value]}] +(defn qr-code + [{:keys [size value]}] (let [uri (reagent/atom nil)] (.toString qr-code-js @@ -24,7 +25,9 @@ Note: `size` includes frame with `styles/qr-code-padding.`" [size value] (when (and size value) - [react/view {:style (styles/qr-code-container size) - :accessibility-label :qr-code-image} - [qr-code {:value value - :size (- size (* styles/qr-code-padding 2))}]])) + [react/view + {:style (styles/qr-code-container size) + :accessibility-label :qr-code-image} + [qr-code + {:value value + :size (- size (* styles/qr-code-padding 2))}]])) diff --git a/src/status_im/ui/components/react.cljs b/src/status_im/ui/components/react.cljs index dc64fbc224..393e032170 100644 --- a/src/status_im/ui/components/react.cljs +++ b/src/status_im/ui/components/react.cljs @@ -1,21 +1,21 @@ (ns status-im.ui.components.react - (:require [reagent.core :as reagent] - [status-im.i18n.i18n :as i18n] - [quo.design-system.colors :as colors] - [status-im.ui.components.typography :as typography] - [status-im.utils.platform :as platform] - [status-im.utils.utils :as utils] + (:require ["@react-native-community/blur" :as blur] + ["@react-native-community/clipboard" :default Clipboard] + ["@react-native-community/masked-view" :default MaskedView] ["react" :as reactjs] ["react-native" :as react-native :refer (Keyboard BackHandler)] - ["react-native-image-crop-picker" :default image-picker] - ["react-native-safe-area-context" :as safe-area-context - :refer (SafeAreaProvider SafeAreaInsetsContext)] - ["@react-native-community/clipboard" :default Clipboard] - ["react-native-linear-gradient" :default LinearGradient] - ["@react-native-community/masked-view" :default MaskedView] - ["react-native-navigation" :refer (Navigation)] ["react-native-fast-image" :as FastImage] - ["@react-native-community/blur" :as blur]) + ["react-native-image-crop-picker" :default image-picker] + ["react-native-linear-gradient" :default LinearGradient] + ["react-native-navigation" :refer (Navigation)] + ["react-native-safe-area-context" :as safe-area-context :refer + (SafeAreaProvider SafeAreaInsetsContext)] + [quo.design-system.colors :as colors] + [reagent.core :as reagent] + [status-im.i18n.i18n :as i18n] + [status-im.ui.components.typography :as typography] + [status-im.utils.platform :as platform] + [status-im.utils.utils :as utils]) (:require-macros [status-im.utils.views :as views])) (def native-modules (.-NativeModules react-native)) @@ -36,7 +36,9 @@ (def fast-image-class (reagent/adapt-react-class FastImage)) (defn image-get-size [uri callback] (.getSize (.-Image react-native) uri callback)) -(defn resolve-asset-source [uri] (js->clj (.resolveAssetSource (.-Image react-native) uri) :keywordize-keys true)) +(defn resolve-asset-source + [uri] + (js->clj (.resolveAssetSource (.-Image react-native) uri) :keywordize-keys true)) (def linear-gradient (reagent/adapt-react-class LinearGradient)) @@ -44,34 +46,41 @@ (def blur-view (reagent/adapt-react-class (.-BlurView blur))) -(defn valid-source? [source] +(defn valid-source? + [source] (or (not (map? source)) (not (contains? source :uri)) (and (contains? source :uri) (:uri source)))) -(defn image [{:keys [source] :as props}] +(defn image + [{:keys [source] :as props}] (when (valid-source? source) [image-class props])) (def switch-class (reagent/adapt-react-class (.-Switch react-native))) -(defn switch [props] +(defn switch + [props] [switch-class props]) (def touchable-highlight-class (reagent/adapt-react-class (.-TouchableHighlight react-native))) (def pressable-class (reagent/adapt-react-class (.-Pressable react-native))) -(def touchable-without-feedback-class (reagent/adapt-react-class (.-TouchableWithoutFeedback react-native))) +(def touchable-without-feedback-class + (reagent/adapt-react-class (.-TouchableWithoutFeedback react-native))) (def touchable-opacity-class (reagent/adapt-react-class (.-TouchableOpacity react-native))) (def activity-indicator-class (reagent/adapt-react-class (.-ActivityIndicator react-native))) -(defn activity-indicator [props] +(defn activity-indicator + [props] [activity-indicator-class (update props :color #(or % colors/gray))]) -(defn small-loading-indicator [color] - [activity-indicator {:color color - :ios {:size :small} - :android {:size :16}}]) +(defn small-loading-indicator + [color] + [activity-indicator + {:color color + :ios {:size :small} + :android {:size :16}}]) (def animated (.-Animated react-native)) @@ -84,10 +93,12 @@ (def animated-scroll-view-class (reagent/adapt-react-class (.-ScrollView ^js animated))) -(defn animated-view [props & content] +(defn animated-view + [props & content] (vec (conj content props animated-view-class))) -(defn animated-scroll-view [props & children] +(defn animated-scroll-view + [props & children] (vec (conj children props animated-scroll-view-class))) (def dimensions (.-Dimensions react-native)) @@ -97,12 +108,14 @@ (def max-font-size-multiplier 1.25) -(defn prepare-text-props [props] +(defn prepare-text-props + [props] (-> props (update :style typography/get-style) (assoc :max-font-size-multiplier max-font-size-multiplier))) -(defn prepare-nested-text-props [props] +(defn prepare-nested-text-props + [props] (-> props (update :style typography/get-nested-style) (assoc :parseBasicMarkdown true) @@ -128,7 +141,8 @@ (if (string? text-element) text-element (let [[options & nested-text-elements] text-element] - (apply nested-text (prepare-nested-text-props options) + (apply nested-text + (prepare-nested-text-props options) nested-text-elements))))) [text-class (dissoc options-with-style :nested?)] nested-text-elements))) @@ -161,7 +175,8 @@ (swap! text-input-refs dissoc @input-ref)) :reagent-render (fn [options text] - (render-fn (assoc options :ref + (render-fn (assoc options + :ref (fn [r] ;; Store input and its defaultValue ;; one we receive a non-nil ref @@ -169,33 +184,40 @@ (swap! text-input-refs assoc r (:default-value options))) (reset! input-ref r) (when (:ref options) - ((:ref options) r)))) text))}))))) + ((:ref options) r)))) + text))}))))) (defn i18n-text [{:keys [style key]}] - [text {:style style} (i18n/label key)]) + [text {:style style} (i18n/label key)]) -(defn touchable-opacity [props content] +(defn touchable-opacity + [props content] [touchable-opacity-class props content]) -(defn touchable-highlight [props content] +(defn touchable-highlight + [props content] [touchable-highlight-class (merge {:underlay-color :transparent} props) content]) -(defn pressable [props content] +(defn pressable + [props content] [pressable-class props content]) -(defn touchable-without-feedback [props content] +(defn touchable-without-feedback + [props content] [touchable-without-feedback-class props content]) -(defn get-dimensions [name] +(defn get-dimensions + [name] (js->clj (.get ^js dimensions name) :keywordize-keys true)) ;; Image picker -(defn show-access-error [o] +(defn show-access-error + [o] (when (= "E_PERMISSION_MISSING" (.-code ^js o)) (utils/show-popup (i18n/label :t/error) (i18n/label :t/photos-access-error)))) @@ -203,34 +225,37 @@ (defn show-image-picker ([images-fn] (show-image-picker images-fn nil)) - ([images-fn {:keys [media-type] - :or {media-type "any"} - :as props}] - (-> ^js image-picker - (.openPicker (clj->js (merge {:mediaType media-type} - props))) - (.then images-fn) - (.catch show-access-error)))) + ([images-fn + {:keys [media-type] + :or {media-type "any"} + :as props}] + (-> ^js image-picker + (.openPicker (clj->js (merge {:mediaType media-type} + props))) + (.then images-fn) + (.catch show-access-error)))) (defn show-image-picker-camera ([images-fn] (show-image-picker-camera images-fn nil)) ([images-fn props] - (-> ^js image-picker - (.openCamera (clj->js props)) - (.then images-fn) - (.catch show-access-error)))) + (-> ^js image-picker + (.openCamera (clj->js props)) + (.then images-fn) + (.catch show-access-error)))) ;; Clipboard (def sharing (.-Share react-native)) -(defn copy-to-clipboard [text] +(defn copy-to-clipboard + [text] (.setString ^js Clipboard text)) -(defn get-from-clipboard [clbk] - (let [clipboard-contents (.getString ^js Clipboard)] +(defn get-from-clipboard + [clbk] + (let [clipboard-contents (.getString ^js Clipboard)] (.then clipboard-contents #(clbk %)))) ;; KeyboardAvoidingView @@ -238,11 +263,12 @@ (.then (.constants Navigation) (fn [^js consts] - (reset! navigation-const {:top-bar-height (.-topBarHeight consts) + (reset! navigation-const {:top-bar-height (.-topBarHeight consts) :bottom-tabs-height (.-bottomTabsHeight consts) - :status-bar-height (.-statusBarHeight consts)}))) + :status-bar-height (.-statusBarHeight consts)}))) -(defn keyboard-avoiding-view [props & children] +(defn keyboard-avoiding-view + [props & children] (into [keyboard-avoiding-view-class (merge (when platform/ios? {:behavior :padding}) (if (:ignore-offset props) @@ -250,7 +276,8 @@ (update props :keyboardVerticalOffset + 44 (:status-bar-height @navigation-const))))] children)) -(defn keyboard-avoiding-view-new [props & children] +(defn keyboard-avoiding-view-new + [props & children] (into [keyboard-avoiding-view-class (merge (when platform/ios? {:behavior :padding}) (if (:ignore-offset props) @@ -258,7 +285,8 @@ (update props :keyboardVerticalOffset + 44)))] children)) -(defn scroll-view [props & children] +(defn scroll-view + [props & children] (vec (conj children props scroll-view-class))) (views/defview with-activity-indicator @@ -273,16 +301,20 @@ timeout)))} (if (and (not enabled?) @loading) (or preview - [view {:style (or style {:justify-content :center - :align-items :center})} + [view + {:style (or style + {:justify-content :center + :align-items :center})} [activity-indicator {:animating true}]]) comp))) (def safe-area-provider (reagent/adapt-react-class SafeAreaProvider)) (def safe-area-consumer (reagent/adapt-react-class (.-Consumer ^js SafeAreaInsetsContext))) -(defn hw-back-add-listener [callback] +(defn hw-back-add-listener + [callback] (.addEventListener BackHandler "hardwareBackPress" callback)) -(defn hw-back-remove-listener [callback] +(defn hw-back-remove-listener + [callback] (.removeEventListener BackHandler "hardwareBackPress" callback)) diff --git a/src/status_im/ui/components/search_input/view.cljs b/src/status_im/ui/components/search_input/view.cljs index 578b478e7d..9b0e5bccef 100644 --- a/src/status_im/ui/components/search_input/view.cljs +++ b/src/status_im/ui/components/search_input/view.cljs @@ -1,85 +1,96 @@ (ns status-im.ui.components.search-input.view - (:require [reagent.core :as reagent] - [status-im.i18n.i18n :as i18n] - [quo.core :as quo] + (:require [quo.core :as quo] [quo.design-system.colors :as colors] - [quo2.foundations.colors :as quo2.colors])) + [quo2.foundations.colors :as quo2.colors] + [reagent.core :as reagent] + [status-im.i18n.i18n :as i18n])) -(defn search-input [{:keys [search-active?]}] +(defn search-input + [{:keys [search-active?]}] (let [input-ref (atom nil) search-active? (or search-active? (reagent/atom nil))] (fn [{:keys [on-focus on-change on-blur on-cancel search-filter auto-focus]}] - [quo/text-input {:placeholder (i18n/label :t/search) - :accessibility-label :search-input - :blur-on-submit true - :multiline false - :get-ref #(reset! input-ref %) - :default-value search-filter - :auto-focus auto-focus - :on-cancel on-cancel - :show-cancel true - :auto-correct false - :auto-capitalize :none - :container-style {:border-radius 10 - :border-width 1 - :border-color (:ui-01 @colors/theme) - :background-color (quo2.colors/theme-colors quo2.colors/white quo2.colors/neutral-90) - :overflow :hidden} - :input-style {:height 32 - :padding-top 2 - :padding-bottom 2 - :background-color (quo2.colors/theme-colors quo2.colors/white quo2.colors/neutral-90)} - :before {:icon :main-icons/search2 - :style {:padding-horizontal 8 - :background-color (quo2.colors/theme-colors quo2.colors/white quo2.colors/neutral-90)} - :on-press #(some-> ^js @input-ref (.focus)) - :icon-opts {:color (quo2.colors/theme-colors quo2.colors/neutral-50 quo2.colors/white)}} - :on-focus #(do - (when on-focus - (on-focus search-filter)) - (reset! search-active? true)) - :on-blur #(do - (when on-blur - (on-blur)) - (reset! search-active? false)) - :on-change (fn [e] - (let [^js native-event (.-nativeEvent ^js e) - text (.-text native-event)] - (when on-change - (on-change text))))}]))) + [quo/text-input + {:placeholder (i18n/label :t/search) + :accessibility-label :search-input + :blur-on-submit true + :multiline false + :get-ref #(reset! input-ref %) + :default-value search-filter + :auto-focus auto-focus + :on-cancel on-cancel + :show-cancel true + :auto-correct false + :auto-capitalize :none + :container-style {:border-radius 10 + :border-width 1 + :border-color (:ui-01 @colors/theme) + :background-color (quo2.colors/theme-colors quo2.colors/white + quo2.colors/neutral-90) + :overflow :hidden} + :input-style {:height 32 + :padding-top 2 + :padding-bottom 2 + :background-color (quo2.colors/theme-colors quo2.colors/white + quo2.colors/neutral-90)} + :before {:icon :main-icons/search2 + :style {:padding-horizontal 8 + :background-color (quo2.colors/theme-colors + quo2.colors/white + quo2.colors/neutral-90)} + :on-press #(some-> ^js @input-ref + (.focus)) + :icon-opts {:color (quo2.colors/theme-colors quo2.colors/neutral-50 + quo2.colors/white)}} + :on-focus #(do + (when on-focus + (on-focus search-filter)) + (reset! search-active? true)) + :on-blur #(do + (when on-blur + (on-blur)) + (reset! search-active? false)) + :on-change (fn [e] + (let [^js native-event (.-nativeEvent ^js e) + text (.-text native-event)] + (when on-change + (on-change text))))}]))) -(defn search-input-old [{:keys [search-active?]}] +(defn search-input-old + [{:keys [search-active?]}] (let [input-ref (atom nil) search-active? (or search-active? (reagent/atom nil))] (fn [{:keys [on-focus on-change on-blur on-cancel search-filter auto-focus]}] - [quo/text-input {:placeholder (i18n/label :t/search) - :accessibility-label :search-input - :blur-on-submit true - :multiline false - :get-ref #(reset! input-ref %) - :default-value search-filter - :auto-focus auto-focus - :on-cancel on-cancel - :show-cancel true - :auto-correct false - :auto-capitalize :none - :input-style {:height 36 - :padding-top 2 - :padding-bottom 2} - :before {:icon :main-icons/search - :style {:padding-horizontal 8} - :on-press #(some-> ^js @input-ref (.focus)) - :icon-opts {:color (:icon-02 @colors/theme)}} - :on-focus #(do - (when on-focus - (on-focus search-filter)) - (reset! search-active? true)) - :on-blur #(do - (when on-blur - (on-blur)) - (reset! search-active? false)) - :on-change (fn [e] - (let [^js native-event (.-nativeEvent ^js e) - text (.-text native-event)] - (when on-change - (on-change text))))}]))) \ No newline at end of file + [quo/text-input + {:placeholder (i18n/label :t/search) + :accessibility-label :search-input + :blur-on-submit true + :multiline false + :get-ref #(reset! input-ref %) + :default-value search-filter + :auto-focus auto-focus + :on-cancel on-cancel + :show-cancel true + :auto-correct false + :auto-capitalize :none + :input-style {:height 36 + :padding-top 2 + :padding-bottom 2} + :before {:icon :main-icons/search + :style {:padding-horizontal 8} + :on-press #(some-> ^js @input-ref + (.focus)) + :icon-opts {:color (:icon-02 @colors/theme)}} + :on-focus #(do + (when on-focus + (on-focus search-filter)) + (reset! search-active? true)) + :on-blur #(do + (when on-blur + (on-blur)) + (reset! search-active? false)) + :on-change (fn [e] + (let [^js native-event (.-nativeEvent ^js e) + text (.-text native-event)] + (when on-change + (on-change text))))}]))) \ No newline at end of file diff --git a/src/status_im/ui/components/slider.cljs b/src/status_im/ui/components/slider.cljs index 90ee45a35c..2284c83325 100644 --- a/src/status_im/ui/components/slider.cljs +++ b/src/status_im/ui/components/slider.cljs @@ -1,7 +1,7 @@ (ns status-im.ui.components.slider - (:require [reagent.core :as reagent] + (:require ["@react-native-community/slider" :default Slider] ["react-native" :refer (Animated)] - ["@react-native-community/slider" :default Slider])) + [reagent.core :as reagent])) (def slider (reagent/adapt-react-class Slider)) diff --git a/src/status_im/ui/components/tabbar/core.cljs b/src/status_im/ui/components/tabbar/core.cljs index 80691c84a7..fa80262f72 100644 --- a/src/status_im/ui/components/tabbar/core.cljs +++ b/src/status_im/ui/components/tabbar/core.cljs @@ -1,7 +1,8 @@ (ns status-im.ui.components.tabbar.core (:require [status-im.utils.platform :as platform])) -(defn get-height [] +(defn get-height + [] (if platform/android? 56 (if platform/iphone-x? diff --git a/src/status_im/ui/components/tabs.cljs b/src/status_im/ui/components/tabs.cljs index e8a54421c0..9f5d1394af 100644 --- a/src/status_im/ui/components/tabs.cljs +++ b/src/status_im/ui/components/tabs.cljs @@ -1,26 +1,35 @@ (ns status-im.ui.components.tabs - (:require [status-im.ui.components.react :as react] - [quo.design-system.colors :as colors])) + (:require [quo.design-system.colors :as colors] + [status-im.ui.components.react :as react])) -(defn tab-title [state key label active?] +(defn tab-title + [state key label active?] [react/view {:align-items :center} - [react/touchable-highlight {:on-press #(swap! state assoc :tab key) - :underlay-color colors/gray-lighter - :accessibility-label (str label "-item-button") - :style {:border-radius 8}} + [react/touchable-highlight + {:on-press #(swap! state assoc :tab key) + :underlay-color colors/gray-lighter + :accessibility-label (str label "-item-button") + :style {:border-radius 8}} [react/view {:padding-horizontal 12 :padding-vertical 8} - [react/text {:style {:font-weight "500" :color (if active? colors/blue colors/gray) :line-height 22}} + [react/text + {:style {:font-weight "500" :color (if active? colors/blue colors/gray) :line-height 22}} label]]] (when active? [react/view {:width 24 :height 3 :border-radius 4 :background-color colors/blue}])]) -(defn tab-button [state key label active?] - [react/view {:flex 1 :align-items :center :border-radius 8 - :background-color (if active? colors/blue colors/blue-light)} - [react/touchable-highlight {:on-press #(swap! state assoc :tab key) - :accessibility-label (str label "-item-button") - :style {:border-radius 8} - :flex 1} +(defn tab-button + [state key label active?] + [react/view + {:flex 1 + :align-items :center + :border-radius 8 + :background-color (if active? colors/blue colors/blue-light)} + [react/touchable-highlight + {:on-press #(swap! state assoc :tab key) + :accessibility-label (str label "-item-button") + :style {:border-radius 8} + :flex 1} [react/view {:padding-horizontal 12 :padding-vertical 8} - [react/text {:style {:font-weight "500" :color (if active? colors/white colors/blue) :line-height 22}} + [react/text + {:style {:font-weight "500" :color (if active? colors/white colors/blue) :line-height 22}} label]]]]) \ No newline at end of file diff --git a/src/status_im/ui/components/toastable_highlight.cljs b/src/status_im/ui/components/toastable_highlight.cljs index a164e35e71..df10875ea8 100644 --- a/src/status_im/ui/components/toastable_highlight.cljs +++ b/src/status_im/ui/components/toastable_highlight.cljs @@ -1,11 +1,12 @@ (ns status-im.ui.components.toastable-highlight "A wrapped touchable highlight that presents a toast when clicked" - (:require [reagent.core :as reagent] + (:require [quo.design-system.colors :as colors] + [reagent.core :as reagent] [status-im.ui.components.animation :as animation] - [quo.design-system.colors :as colors] [status-im.ui.components.react :as react])) -(defn hide-cue-atom [anim-opacity anim-y cue-atom] +(defn hide-cue-atom + [anim-opacity anim-y cue-atom] (animation/start (animation/parallel [(animation/timing @@ -24,7 +25,8 @@ :useNativeDriver true})]) #(reset! cue-atom false))) -(defn show-cue-atom [anim-opacity anim-y cue-atom y] +(defn show-cue-atom + [anim-opacity anim-y cue-atom y] (when @cue-atom (animation/start (animation/parallel @@ -42,7 +44,8 @@ :useNativeDriver true})]) #(hide-cue-atom anim-opacity anim-y cue-atom)))) -(defn toast [anim-opacity anim-y width cue-atom label] +(defn toast + [anim-opacity anim-y width cue-atom label] [react/animated-view {:style {:opacity anim-opacity @@ -102,7 +105,7 @@ (when on-press (on-press)))] [react/view - {:style (if container-style container-style {}) + {:style (if container-style container-style {}) :on-layout #(do (reset! width (-> ^js % .-nativeEvent .-layout .-width)) diff --git a/src/status_im/ui/components/toolbar.cljs b/src/status_im/ui/components/toolbar.cljs index 8b27f93406..69a011a1c9 100644 --- a/src/status_im/ui/components/toolbar.cljs +++ b/src/status_im/ui/components/toolbar.cljs @@ -1,9 +1,10 @@ (ns status-im.ui.components.toolbar - (:require [status-im.ui.components.react :as react] - [quo.design-system.colors :as colors])) + (:require [quo.design-system.colors :as colors] + [status-im.ui.components.react :as react])) -(defn toolbar-container [{:keys [show-border? size center?] - :or {size :default}}] +(defn toolbar-container + [{:keys [show-border? size center?] + :or {size :default}}] (merge {:align-items :center :padding-horizontal 8 :width "100%" @@ -19,15 +20,18 @@ {:height 52}))) ;; TODO(Ferossgp): Allow components when moving to Quo -(defn toolbar [{:keys [center left right show-border? size]}] +(defn toolbar + [{:keys [center left right show-border? size]}] (if center - [react/view {:style (toolbar-container {:show-border? show-border? - :center? true - :size size})} + [react/view + {:style (toolbar-container {:show-border? show-border? + :center? true + :size size})} center] - [react/view {:style (toolbar-container {:show-border? show-border? - :center? false - :size size})} + [react/view + {:style (toolbar-container {:show-border? show-border? + :center? false + :size size})} [react/view {:flex-shrink 1} (when left left)] [react/view diff --git a/src/status_im/ui/components/tooltip/animations.cljs b/src/status_im/ui/components/tooltip/animations.cljs index 78daa6b992..38e965b0b5 100644 --- a/src/status_im/ui/components/tooltip/animations.cljs +++ b/src/status_im/ui/components/tooltip/animations.cljs @@ -1,14 +1,17 @@ (ns status-im.ui.components.tooltip.animations (:require [status-im.ui.components.animation :as animation])) -(defn animate-tooltip [bottom-value bottom-anim-value opacity-value delta] +(defn animate-tooltip + [bottom-value bottom-anim-value opacity-value delta] (fn [] (animation/start (animation/parallel - [(animation/timing opacity-value {:toValue 1 - :duration 500 - :useNativeDriver true}) - (animation/timing bottom-anim-value {:toValue (- bottom-value delta) - :easing (.bezier ^js animation/easing 0.685, 0.000, 0.025, 1.185) - :duration 500 - :useNativeDriver true})])))) + [(animation/timing opacity-value + {:toValue 1 + :duration 500 + :useNativeDriver true}) + (animation/timing bottom-anim-value + {:toValue (- bottom-value delta) + :easing (.bezier ^js animation/easing 0.685 0.000 0.025 1.185) + :duration 500 + :useNativeDriver true})])))) diff --git a/src/status_im/ui/components/tooltip/styles.cljs b/src/status_im/ui/components/tooltip/styles.cljs index 839694e9a8..8ce1a5f278 100644 --- a/src/status_im/ui/components/tooltip/styles.cljs +++ b/src/status_im/ui/components/tooltip/styles.cljs @@ -1,15 +1,15 @@ (ns status-im.ui.components.tooltip.styles (:require [quo.design-system.colors :as colors] - [status-im.utils.styles :as styles] - [status-im.utils.config :as config])) + [status-im.utils.config :as config] + [status-im.utils.styles :as styles])) (def tooltip-container (merge - {:position :absolute - :align-items :center - :left 0 - :right 0 - :top 0} + {:position :absolute + :align-items :center + :left 0 + :right 0 + :top 0} ;;we need this for e2e tests (when-not config/tooltip-events? {:pointer-events :none}))) @@ -22,7 +22,8 @@ :ios {:top 0} :android {:top 30}}) -(defn tooltip-animated [bottom-value opacity-value] +(defn tooltip-animated + [bottom-value opacity-value] (cond-> {:position :absolute :align-items :center :left 0 @@ -34,9 +35,10 @@ ;;we need this for e2e tests config/tooltip-events? (assoc :margin-top -20 - :position :relative))) + :position :relative))) -(defn tooltip-text-container [color] +(defn tooltip-text-container + [color] {:padding-horizontal 16 :padding-vertical 6 :elevation 3 @@ -58,10 +60,11 @@ :background-color colors/gray :border-radius 8}) -(defn tooltip-text [font-size] - {:color colors/red - :line-height 15 - :font-size font-size}) +(defn tooltip-text + [font-size] + {:color colors/red + :line-height 15 + :font-size font-size}) (def bottom-tooltip-text {:color colors/white}) diff --git a/src/status_im/ui/components/tooltip/views.cljs b/src/status_im/ui/components/tooltip/views.cljs index 830e5aeca4..96ab0d6732 100644 --- a/src/status_im/ui/components/tooltip/views.cljs +++ b/src/status_im/ui/components/tooltip/views.cljs @@ -1,15 +1,17 @@ (ns status-im.ui.components.tooltip.views - (:require [status-im.ui.components.animation :as animation] - [quo.design-system.colors :as colors] + (:require [quo.design-system.colors :as colors] + [status-im.ui.components.animation :as animation] [status-im.ui.components.icons.icons :as icons] [status-im.ui.components.react :as react] [status-im.ui.components.tooltip.animations :as animations] [status-im.ui.components.tooltip.styles :as styles]) (:require-macros [status-im.utils.views :as views])) -(views/defview tooltip [label & [{:keys [bottom-value color font-size container-style] - :or {bottom-value 30 color colors/white font-size 15}} - accessibility-label]] +(views/defview tooltip + [label & + [{:keys [bottom-value color font-size container-style] + :or {bottom-value 30 color colors/white font-size 15}} + accessibility-label]] (views/letsubs [bottom-anim-value (animation/create-value bottom-value) opacity-value (animation/create-value 0)] {:component-did-mount (animations/animate-tooltip bottom-value bottom-anim-value opacity-value -10)} @@ -21,22 +23,27 @@ {:style (styles/tooltip-text font-size) :accessibility-label accessibility-label} label]]) - #_[icons/icon :icons/tooltip-tip (assoc - styles/tooltip-triangle - :color color)]]])) + #_[icons/icon :icons/tooltip-tip + (assoc + styles/tooltip-triangle + :color + color)]]])) -(views/defview bottom-tooltip-info [label on-close] +(views/defview bottom-tooltip-info + [label on-close] (views/letsubs [bottom-anim-value (animation/create-value 75) opacity-value (animation/create-value 0)] {:component-did-mount (animations/animate-tooltip 75 bottom-anim-value opacity-value 10)} [react/view styles/bottom-tooltip-container [react/animated-view {:style (styles/tooltip-animated bottom-anim-value opacity-value)} - [icons/icon :icons/tooltip-tip (assoc - styles/tooltip-triangle - :color colors/gray - :container-style {:transform [{:rotate "180deg"}]})] + [icons/icon :icons/tooltip-tip + (assoc + styles/tooltip-triangle + :color colors/gray + :container-style {:transform [{:rotate "180deg"}]})] [react/view styles/bottom-tooltip-text-container [react/text {:style styles/bottom-tooltip-text} label] - [react/touchable-highlight {:on-press on-close - :style styles/close-icon} + [react/touchable-highlight + {:on-press on-close + :style styles/close-icon} [icons/icon :main-icons/close {:color colors/white}]]]]])) diff --git a/src/status_im/ui/components/topbar.cljs b/src/status_im/ui/components/topbar.cljs index 478d2699db..20f5fc8358 100644 --- a/src/status_im/ui/components/topbar.cljs +++ b/src/status_im/ui/components/topbar.cljs @@ -1,11 +1,12 @@ (ns status-im.ui.components.topbar - (:require [re-frame.core :as re-frame] - [quo.core :as quo] - [quo2.foundations.colors :as quo2.colors])) + (:require [quo.core :as quo] + [quo2.foundations.colors :as quo2.colors] + [re-frame.core :as re-frame])) (def default-button-width 48) -(defn default-navigation [modal? {:keys [on-press label icon]}] +(defn default-navigation + [modal? {:keys [on-press label icon]}] (cond-> {:icon (if modal? :main-icons/close :main-icons/arrow-left) :accessibility-label :back-button :on-press #(re-frame/dispatch [:navigate-back])} @@ -21,23 +22,28 @@ label (assoc :label label))) -(defn topbar [{:keys [navigation use-insets right-accessories modal? content border-bottom? new-ui?] ;; remove new-ui? key, temp fix - :or {border-bottom? true - new-ui? false} - :as props}] +(defn topbar + [{:keys [navigation use-insets right-accessories modal? content border-bottom? new-ui?] ;; remove + ;; new-ui? key, + ;; temp fix + :or {border-bottom? true + new-ui? false} + :as props}] (let [navigation (if (= navigation :none) nil [(default-navigation modal? navigation)])] [quo/safe-area-consumer (fn [insets] - [quo/header (merge {:left-accessories navigation - :title-component content - :insets (when use-insets insets) - :left-width (when navigation - default-button-width) - :border-bottom border-bottom?} - props - (when (seq right-accessories) - {:right-accessories right-accessories}) - (when new-ui? - {:background (quo2.colors/theme-colors quo2.colors/neutral-5 quo2.colors/neutral-95)}))])])) \ No newline at end of file + [quo/header + (merge {:left-accessories navigation + :title-component content + :insets (when use-insets insets) + :left-width (when navigation + default-button-width) + :border-bottom border-bottom?} + props + (when (seq right-accessories) + {:right-accessories right-accessories}) + (when new-ui? + {:background (quo2.colors/theme-colors quo2.colors/neutral-5 + quo2.colors/neutral-95)}))])])) \ No newline at end of file diff --git a/src/status_im/ui/components/typography.cljs b/src/status_im/ui/components/typography.cljs index 891bf32bc2..90a839febd 100644 --- a/src/status_im/ui/components/typography.cljs +++ b/src/status_im/ui/components/typography.cljs @@ -2,7 +2,8 @@ (:require [quo.design-system.colors :as colors])) (def default-font-family "Inter") -(defn default-style [] +(defn default-style + [] {:color colors/black :font-weight "400" :font-size 15}) @@ -14,13 +15,13 @@ :title-bold {:font-weight "700" :font-size 17} - :title {:font-size 17} + :title {:font-size 17} :main-semibold {:font-weight "600"} :main-medium {:font-weight "500"} - :caption {:font-size 12} + :caption {:font-size 12} :timestamp {:font-size 10 :text-transform :uppercase}}) @@ -29,14 +30,15 @@ [{:keys [typography] :as style}] {:pre [(or (nil? typography) (contains? typography-styles typography))]} (let [{:keys [font-weight font-style] - :as style} + :as style} (merge (default-style) (get typography-styles typography) (dissoc style :typography :nested?))] (-> style (assoc :font-family - (str default-font-family "-" + (str default-font-family + "-" (case font-weight "400" (when-not (= font-style :italic) "Regular") @@ -57,7 +59,8 @@ (cond-> (dissoc style :font-weight :font-style) (or font-weight font-style) (assoc :font-family - (str default-font-family "-" + (str default-font-family + "-" (case font-weight "500" "Medium" "600" "SemiBold" diff --git a/src/status_im/ui/components/unviewed_indicator.cljs b/src/status_im/ui/components/unviewed_indicator.cljs index bc0c37caa9..fad12670d5 100644 --- a/src/status_im/ui/components/unviewed_indicator.cljs +++ b/src/status_im/ui/components/unviewed_indicator.cljs @@ -2,9 +2,11 @@ (:require [status-im.ui.components.badge :as badge] [status-im.ui.components.react :as react])) -(defn unviewed-indicator [c] +(defn unviewed-indicator + [c] (when (pos? c) - [react/view {:padding-left 16 - :justify-content :flex-end - :align-items :flex-end} + [react/view + {:padding-left 16 + :justify-content :flex-end + :align-items :flex-end} [badge/message-counter c]])) diff --git a/src/status_im/ui/components/webview.cljs b/src/status_im/ui/components/webview.cljs index 36ad1aea8c..aeb7ad2194 100644 --- a/src/status_im/ui/components/webview.cljs +++ b/src/status_im/ui/components/webview.cljs @@ -1,13 +1,14 @@ (ns status-im.ui.components.webview - (:require [reagent.core :as reagent] + (:require ["react-native-webview" :default rn-webview] + [reagent.core :as reagent] [status-im.utils.config :as config] - [status-im.utils.platform :as platform] - ["react-native-webview" :default rn-webview])) + [status-im.utils.platform :as platform])) (def webview-class (reagent/adapt-react-class rn-webview)) -(defn webview [{:keys [dapp?] :as opts}] +(defn webview + [{:keys [dapp?] :as opts}] (if (and config/cached-webviews-enabled? platform/android? dapp?) (reagent/create-class (let [dapp-name-sent? (reagent/atom false)] @@ -16,7 +17,8 @@ ;; unfortunately it's impossible to pass some initial params ;; to view, that's why we have to pass dapp-name to the module ;; before showing webview - #_(.setCurrentDapp (module) dapp-name + #_(.setCurrentDapp (module) + dapp-name (fn [] (reset! dapp-name-sent? true)))) :reagent-render (fn [opts] diff --git a/src/status_im/ui/screens/about_app/views.cljs b/src/status_im/ui/screens/about_app/views.cljs index 9c1d819eae..40e199c710 100644 --- a/src/status_im/ui/screens/about_app/views.cljs +++ b/src/status_im/ui/screens/about_app/views.cljs @@ -1,16 +1,17 @@ (ns status-im.ui.screens.about-app.views - (:require [re-frame.core :as re-frame] - [status-im.i18n.i18n :as i18n] + (:require [quo.core :as quo] [quo.design-system.colors :as colors] + [re-frame.core :as re-frame] + [status-im.constants :refer [principles-link privacy-policy-link terms-of-service-link]] + [status-im.i18n.i18n :as i18n] [status-im.ui.components.copyable-text :as copyable-text] [status-im.ui.components.icons.icons :as icons] - [status-im.ui.components.webview :refer [webview]] - [status-im.constants :refer [privacy-policy-link principles-link terms-of-service-link]] - [quo.core :as quo] - [status-im.ui.components.react :as react]) + [status-im.ui.components.react :as react] + [status-im.ui.components.webview :refer [webview]]) (:require-macros [status-im.utils.views :as views])) -(views/defview about-app [] +(views/defview about-app + [] (views/letsubs [app-version [:get-app-short-version] commit-hash [:get-commit-hash] node-version [:get-app-node-version]] @@ -26,7 +27,7 @@ {:size :small :title (i18n/label :t/terms-of-service) :accessibility-label :terms-of-service - :on-press #(re-frame/dispatch [:navigate-to :terms-of-service]) + :on-press #(re-frame/dispatch [:navigate-to :terms-of-service]) :chevron true}] [copyable-text/copyable-text-view {:copied-text app-version} @@ -53,30 +54,38 @@ :accessory :text :accessory-text node-version}]]])) -(views/defview learn-more-sheet [] +(views/defview learn-more-sheet + [] (views/letsubs [{:keys [title content]} [:bottom-sheet/options]] - [react/view {:style {:padding-left 16 :padding-top 16 - :padding-right 34 :padding-bottom 0}} + [react/view + {:style {:padding-left 16 + :padding-top 16 + :padding-right 34 + :padding-bottom 0}} [react/view {:style {:align-items :center :flex-direction :row :margin-bottom 16}} - [icons/icon :main-icons/info {:color colors/blue - :container-style {:margin-right 13}}] + [icons/icon :main-icons/info + {:color colors/blue + :container-style {:margin-right 13}}] [react/text {:style {:typography :title-bold}} title]] [react/text {:style {:color colors/gray}} content]])) (def learn-more {:content learn-more-sheet}) -(views/defview privacy-policy [] +(views/defview privacy-policy + [] [webview {:source {:uri privacy-policy-link} :java-script-enabled true}]) -(views/defview tos [] +(views/defview tos + [] [webview {:source {:uri terms-of-service-link} :java-script-enabled true}]) -(views/defview principles [] +(views/defview principles + [] [webview {:source {:uri principles-link} :java-script-enabled true}]) diff --git a/src/status_im/ui/screens/add_new/new_chat/views.cljs b/src/status_im/ui/screens/add_new/new_chat/views.cljs index 091636b6ab..3a66ed60fb 100644 --- a/src/status_im/ui/screens/add_new/new_chat/views.cljs +++ b/src/status_im/ui/screens/add_new/new_chat/views.cljs @@ -1,32 +1,33 @@ (ns status-im.ui.screens.add-new.new-chat.views - (:require [re-frame.core :as re-frame] + (:require [clojure.string :as string] + [quo.core :as quo] + [quo.design-system.colors :as colors] + [quo.platform :as platform] + [quo.react-native :as rn] + [re-frame.core :as re-frame] + [reagent.core :as reagent] + [status-im.ethereum.ens :as ens] [status-im.i18n.i18n :as i18n] [status-im.multiaccounts.core :as multiaccounts] - [status-im.ui.components.chat-icon.screen :as chat-icon] - [quo.design-system.colors :as colors] - [status-im.ui.components.icons.icons :as icons] - [quo.core :as quo] - [status-im.utils.gfycat.core :as gfycat] [status-im.qr-scanner.core :as qr-scanner] + [status-im.ui.components.animation :as animation] + [status-im.ui.components.chat-icon.screen :as chat-icon] + [status-im.ui.components.icons.icons :as icons] + [status-im.ui.components.invite.views :as invite] + [status-im.ui.components.keyboard-avoid-presentation :as kb-presentation] [status-im.ui.components.list.views :as list] [status-im.ui.components.react :as react] [status-im.ui.components.topbar :as topbar] - [utils.debounce :as debounce] - [status-im.utils.utils :as utils] - [reagent.core :as reagent] - [quo.react-native :as rn] - [clojure.string :as string] - [status-im.ethereum.ens :as ens] - [quo.platform :as platform] - [status-im.utils.identicon :as identicon] - [status-im.ui.components.keyboard-avoid-presentation :as kb-presentation] - [status-im.ui.components.animation :as animation] [status-im.ui.screens.chat.photos :as photos] [status-im.utils.db :as utils.db] - [status-im.ui.components.invite.views :as invite]) + [status-im.utils.gfycat.core :as gfycat] + [status-im.utils.identicon :as identicon] + [status-im.utils.utils :as utils] + [utils.debounce :as debounce]) (:require-macros [status-im.utils.views :as views])) -(defn- render-row [row] +(defn- render-row + [row] (let [first-name (first (multiaccounts/contact-two-names row false))] [quo/list-item {:title first-name @@ -35,7 +36,8 @@ :on-press #(re-frame/dispatch [:chat.ui/start-chat (:public-key row)])}])) -(defn- icon-wrapper [color icon] +(defn- icon-wrapper + [color icon] [react/view {:style {:width 32 :height 32 @@ -55,7 +57,9 @@ (and (= state :valid) (not blocked?)) [react/touchable-highlight - {:on-press #(debounce/dispatch-and-chill [:contact.ui/contact-code-submitted new-contact? entered-nickname] 3000)} + {:on-press #(debounce/dispatch-and-chill [:contact.ui/contact-code-submitted new-contact? + entered-nickname] + 3000)} [icon-wrapper colors/blue [icons/icon icon {:color colors/white-persist}]]] @@ -63,82 +67,104 @@ [icon-wrapper colors/gray [icons/icon icon {:color colors/white-persist}]]))) -(defn get-validation-label [value] +(defn get-validation-label + [value] (case value :invalid (i18n/label :t/profile-not-found) :yourself (i18n/label :t/can-not-add-yourself))) -(defn search-contacts [filter-text {:keys [name alias nickname]}] +(defn search-contacts + [filter-text {:keys [name alias nickname]}] (or (string/includes? (string/lower-case (str name)) filter-text) (string/includes? (string/lower-case (str alias)) filter-text) (when nickname (string/includes? (string/lower-case (str nickname)) filter-text)))) -(defn filter-contacts [filter-text contacts] +(defn filter-contacts + [filter-text contacts] (let [lower-filter-text (string/lower-case filter-text)] (if filter-text (filter (partial search-contacts lower-filter-text) contacts) contacts))) -(defn is-public-key? [k] +(defn is-public-key? + [k] (and (string? k) (string/starts-with? k "0x"))) -(defn is-valid-username? [username] +(defn is-valid-username? + [username] (let [is-chat-key? (and (is-public-key? username) (= (count username) 132)) - is-ens? (ens/valid-eth-name-prefix? username)] + is-ens? (ens/valid-eth-name-prefix? username)] (or is-chat-key? is-ens?))) -(defn translate-anim [translate-y-value translate-y-anim-value] +(defn translate-anim + [translate-y-value translate-y-anim-value] (animation/start - (animation/timing translate-y-anim-value {:toValue translate-y-value - :duration 200 - :useNativeDriver true}))) -(views/defview new-chat [] - (views/letsubs [contacts [:contacts/active] + (animation/timing translate-y-anim-value + {:toValue translate-y-value + :duration 200 + :useNativeDriver true}))) +(views/defview new-chat + [] + (views/letsubs [contacts [:contacts/active] {:keys [state ens-name public-key error]} [:contacts/new-identity] - search-value (reagent/atom "") - account @(re-frame/subscribe [:multiaccount]) - on-share #(re-frame/dispatch [:show-popover - {:view :share-chat-key - :address (account :public-key) - :ens-name (account :preferred-name)}]) - my-profile-button-anim-y (animation/create-value 0) - keyboard-show-listener (atom nil) - keyboard-hide-listener (atom nil) - on-keyboard-show (fn [] - ;; 42 is the bottom position so we translate it by 32 pts to leave 10 as margin - (translate-anim 32 my-profile-button-anim-y)) - on-keyboard-hide (fn [] - (translate-anim 0 my-profile-button-anim-y)) - keyboard-show-event (if platform/android? "keyboardDidShow" "keyboardWillShow") - keyboard-hide-event (if platform/android? "keyboardDidHide" "keyboardWillHide")] + search-value (reagent/atom "") + account @(re-frame/subscribe [:multiaccount]) + on-share #(re-frame/dispatch + [:show-popover + {:view :share-chat-key + :address (account :public-key) + :ens-name (account :preferred-name)}]) + my-profile-button-anim-y (animation/create-value 0) + keyboard-show-listener (atom nil) + keyboard-hide-listener (atom nil) + on-keyboard-show (fn [] + ;; 42 is the bottom position so we + ;; translate it by 32 pts to leave 10 as + ;; margin + (translate-anim 32 + my-profile-button-anim-y)) + on-keyboard-hide (fn [] + (translate-anim 0 + my-profile-button-anim-y)) + keyboard-show-event (if platform/android? + "keyboardDidShow" + "keyboardWillShow") + keyboard-hide-event (if platform/android? + "keyboardDidHide" + "keyboardWillHide")] {:component-did-mount (fn [_] (reset! keyboard-show-listener (.addListener react/keyboard keyboard-show-event on-keyboard-show)) - (reset! keyboard-hide-listener (.addListener react/keyboard keyboard-hide-event on-keyboard-hide))) + (reset! keyboard-hide-listener (.addListener react/keyboard + keyboard-hide-event + on-keyboard-hide))) :component-will-unmount (fn [] - (some-> ^js @keyboard-show-listener .remove) - (some-> ^js @keyboard-hide-listener .remove))} + (some-> ^js @keyboard-show-listener + .remove) + (some-> ^js @keyboard-hide-listener + .remove))} [kb-presentation/keyboard-avoiding-view {:style {:flex 1}} [react/view {:style {:flex 1}} [topbar/topbar - {:title (i18n/label :t/new-chat) - :modal? true + {:title (i18n/label :t/new-chat) + :modal? true :right-accessories [{:icon :qr :accessibility-label :scan-contact-code-button :on-press #(re-frame/dispatch [::qr-scanner/scan-code {:title (i18n/label :t/new-chat) :handler :contact/qr-code-scanned}])}]}] - [react/view {:flex-direction :row - :padding 16} + [react/view + {:flex-direction :row + :padding 16} [react/view {:flex 1} [quo/text-input {:on-change-text @@ -156,55 +182,65 @@ :return-key-type :go :monospace true :auto-correct false}]]] - [react/scroll-view {:style {:flex 1} - :keyboard-dismiss-mode :on-drag - :keyboard-should-persist-taps :handled} - [react/view (when (and - (= (count contacts) 0) - (= @search-value "")) - {:flex 1}) + [react/scroll-view + {:style {:flex 1} + :keyboard-dismiss-mode :on-drag + :keyboard-should-persist-taps :handled} + [react/view + (when (and + (= (count contacts) 0) + (= @search-value "")) + {:flex 1}) (if (and (= (count contacts) 0) (= @search-value "")) - [react/view {:flex 1 - :align-items :center - :padding-horizontal 58 - :padding-top 160} - [quo/text {:size :base - :align :center - :color :secondary} + [react/view + {:flex 1 + :align-items :center + :padding-horizontal 58 + :padding-top 160} + [quo/text + {:size :base + :align :center + :color :secondary} (i18n/label :t/you-dont-have-contacts-invite-friends)] [invite/button]] - [list/flat-list {:data (filter-contacts @search-value contacts) - :key-fn :address - :render-fn render-row}])] + [list/flat-list + {:data (filter-contacts @search-value contacts) + :key-fn :address + :render-fn render-row}])] (when-not (= @search-value "") [react/view - [quo/text {:style {:margin-horizontal 16 - :margin-vertical 14} - :size :base - :align :left - :color :secondary} + [quo/text + {:style {:margin-horizontal 16 + :margin-vertical 14} + :size :base + :align :left + :color :secondary} (i18n/label :t/non-contacts)] (when (and (= state :searching) (is-valid-username? @search-value)) - [rn/activity-indicator {:color colors/gray - :size (if platform/android? :large :small)}]) + [rn/activity-indicator + {:color colors/gray + :size (if platform/android? :large :small)}]) (if (= state :valid) [quo/list-item (merge {:title (or ens-name (gfycat/generate-gfy public-key)) - :subtitle (if ens-name (gfycat/generate-gfy public-key) (utils/get-shortened-address public-key)) + :subtitle (if ens-name + (gfycat/generate-gfy public-key) + (utils/get-shortened-address public-key)) :icon [chat-icon/contact-icon-contacts-tab (identicon/identicon public-key)] :on-press #(do (debounce/dispatch-and-chill [:contact.ui/contact-code-submitted false] 3000) (re-frame/dispatch [:search/home-filter-changed nil]))} (when ens-name {:subtitle-secondary public-key}))] - [quo/text {:style {:margin-horizontal 16} - :size :base - :align :center - :color :secondary} + [quo/text + {:style {:margin-horizontal 16} + :size :base + :align :center + :color :secondary} (if (is-valid-username? @search-value) (when (= state :error) (get-validation-label error)) @@ -212,37 +248,42 @@ (when-not (and (= (count contacts) 0) (= @search-value "")) - [react/animated-view {:style {:height 36 - :width 124 - :position :absolute - :bottom 42 - :transform [{:translateY my-profile-button-anim-y}] - :align-self :center}} - [react/touchable-opacity {:style {:padding-horizontal 2 - :height 36 - :width 124 - :background-color colors/blue - :border-radius 18 - :elevation 4 - :shadow-offset {:width 0 :height 4} - :shadow-color "rgba(0, 34, 51, 0.16)" - :shadow-radius 4 - :shadow-opacity 1} - :on-press on-share} - [react/view {:style {:flex 1 - :flex-direction :row - :align-items :center}} + [react/animated-view + {:style {:height 36 + :width 124 + :position :absolute + :bottom 42 + :transform [{:translateY my-profile-button-anim-y}] + :align-self :center}} + [react/touchable-opacity + {:style {:padding-horizontal 2 + :height 36 + :width 124 + :background-color colors/blue + :border-radius 18 + :elevation 4 + :shadow-offset {:width 0 :height 4} + :shadow-color "rgba(0, 34, 51, 0.16)" + :shadow-radius 4 + :shadow-opacity 1} + :on-press on-share} + [react/view + {:style {:flex 1 + :flex-direction :row + :align-items :center}} [photos/photo (multiaccounts/displayed-photo account) - {:size 32 + {:size 32 :accessibility-label :current-account-photo}] - [quo/text {:size :base - :weight :medium - :color :inverse - :style {:margin-left 6}} + [quo/text + {:size :base + :weight :medium + :color :inverse + :style {:margin-left 6}} (i18n/label :t/my-profile)]]]])]])) -(defn- nickname-input [entered-nickname] +(defn- nickname-input + [entered-nickname] [quo/text-input {:on-change-text #(reset! entered-nickname %) :auto-capitalize :none @@ -253,17 +294,19 @@ :return-key-type :done :auto-correct false}]) -(defn new-contact [] +(defn new-contact + [] (let [entered-nickname (reagent/atom "")] (fn [] (let [{:keys [state ens-name public-key error]} @(re-frame/subscribe [:contacts/new-identity]) - blocked? (and - (utils.db/valid-public-key? (or public-key "")) - @(re-frame/subscribe [:contacts/contact-blocked? public-key]))] + blocked? (and + (utils.db/valid-public-key? (or public-key "")) + @(re-frame/subscribe [:contacts/contact-blocked? + public-key]))] [react/view {:style {:flex 1}} [topbar/topbar - {:title (i18n/label :t/new-contact) - :modal? true + {:title (i18n/label :t/new-contact) + :modal? true :right-accessories [{:icon :qr :accessibility-label :scan-contact-code-button @@ -272,10 +315,12 @@ :handler :contact/qr-code-scanned :new-contact? true :nickname @entered-nickname}])}]}] - [react/view {:flex-direction :row - :padding 16} - [react/view {:flex 1 - :padding-right 16} + [react/view + {:flex-direction :row + :padding 16} + [react/view + {:flex 1 + :padding-right 16} [quo/text-input {:on-change-text #(do @@ -283,30 +328,35 @@ (debounce/debounce-and-dispatch [:new-chat/set-new-identity %] 600)) :on-submit-editing #(when (= state :valid) - (debounce/dispatch-and-chill [:contact.ui/contact-code-submitted true @entered-nickname] 3000)) + (debounce/dispatch-and-chill [:contact.ui/contact-code-submitted true @entered-nickname] + 3000)) :placeholder (i18n/label :t/enter-contact-code) :show-cancel false :accessibility-label :enter-contact-code-input :auto-capitalize :none :return-key-type :go}]] - [react/view {:justify-content :center - :align-items :center} + [react/view + {:justify-content :center + :align-items :center} [input-icon state true @entered-nickname blocked?]]] [react/view {:min-height 30 :justify-content :flex-end :margin-bottom 16} - [quo/text {:style {:margin-horizontal 16} - :size :small - :align :center - :color :secondary} + [quo/text + {:style {:margin-horizontal 16} + :size :small + :align :center + :color :secondary} (cond (= state :error) (get-validation-label error) (= state :valid) (str (when ens-name (str ens-name " • ")) (utils/get-shortened-address public-key)) - :else "")]] + :else "")]] [react/text {:style {:margin-horizontal 16 :color colors/gray}} (i18n/label :t/nickname-description)] [react/view {:padding 16} [nickname-input entered-nickname] - [react/text {:style {:align-self :flex-end :margin-top 16 - :color colors/gray}} + [react/text + {:style {:align-self :flex-end + :margin-top 16 + :color colors/gray}} (str (count @entered-nickname) " / 32")]]])))) diff --git a/src/status_im/ui/screens/add_new/new_public_chat/view.cljs b/src/status_im/ui/screens/add_new/new_public_chat/view.cljs index 0219c806c2..46ac12d0c6 100644 --- a/src/status_im/ui/screens/add_new/new_public_chat/view.cljs +++ b/src/status_im/ui/screens/add_new/new_public_chat/view.cljs @@ -1,24 +1,27 @@ (ns status-im.ui.screens.add-new.new-public-chat.view - (:require [re-frame.core :as re-frame] + (:require [quo.core :as quo] + [quo.design-system.colors :as colors] + [re-frame.core :as re-frame] + [status-im.add-new.db :as db] + [status-im.chat.models :as chat.models] [status-im.i18n.i18n :as i18n] [status-im.i18n.i18n-resources :as i18n-resources] [status-im.react-native.resources :as resources] - [quo.design-system.colors :as colors] - [status-im.ui.components.react :as react] - [quo.core :as quo] - [status-im.add-new.db :as db] - [status-im.chat.models :as chat.models] - [status-im.ui.components.icons.icons :as icons]) + [status-im.ui.components.icons.icons :as icons] + [status-im.ui.components.react :as react]) (:require-macros [status-im.utils.views :as views])) -(defn- start-chat [topic] +(defn- start-chat + [topic] (re-frame/dispatch [:chat.ui/start-public-chat topic]) (re-frame/dispatch [:set :public-group-topic nil])) -(defn- hash-icon [] +(defn- hash-icon + [] [icons/icon :main-icons/channel {:color colors/gray}]) -(defn- chat-name-input [topic error] +(defn- chat-name-input + [topic error] [quo/text-input {:on-change-text #(re-frame/dispatch [:set :public-group-topic %]) :on-submit-editing #(when (db/valid-topic? topic) (start-chat topic)) @@ -34,56 +37,69 @@ :auto-correct false :error error}]) -(defn render-topic [topic] - [react/touchable-highlight {:on-press #(start-chat topic) - :accessibility-label :chat-item} - [react/view {:border-color colors/gray-lighter :border-radius 36 :border-width 1 :padding-horizontal 8 - :padding-vertical 5 :margin-right 8 :margin-vertical 8} +(defn render-topic + [topic] + [react/touchable-highlight + {:on-press #(start-chat topic) + :accessibility-label :chat-item} + [react/view + {:border-color colors/gray-lighter + :border-radius 36 + :border-width 1 + :padding-horizontal 8 + :padding-vertical 5 + :margin-right 8 + :margin-vertical 8} [react/text {:style {:color colors/blue :typography :main-medium}} (str "#" topic)]]]) -(def lang-names {"es" "status-espanol" - "pt" "statusbrasil" - "de" "status-german" - "fr" "status-french" - "it" "status-italiano" - "ru" "status-russian" - "zh" "status-chinese" - "ko" "status-korean" - "ja" "status-japanese" - "fa" "status-farsi" - "tr" "status-turkish" - "id" "indonesian" - "in" "indonesian" - "hi" "status-indian" - "ar" "status-arabic" - "fil" "status-filipino" - "nl" "status-dutch"}) +(def lang-names + {"es" "status-espanol" + "pt" "statusbrasil" + "de" "status-german" + "fr" "status-french" + "it" "status-italiano" + "ru" "status-russian" + "zh" "status-chinese" + "ko" "status-korean" + "ja" "status-japanese" + "fa" "status-farsi" + "tr" "status-turkish" + "id" "indonesian" + "in" "indonesian" + "hi" "status-indian" + "ar" "status-arabic" + "fil" "status-filipino" + "nl" "status-dutch"}) -(defn get-language-topic [] - (let [lang (subs (name i18n-resources/default-device-language) 0 2) - lang3 (subs (name i18n-resources/default-device-language) 0 3) +(defn get-language-topic + [] + (let [lang (subs (name i18n-resources/default-device-language) 0 2) + lang3 (subs (name i18n-resources/default-device-language) 0 3) lang-name (or (get lang-names lang3) (get lang-names lang))] (when-not (= lang "en") (or lang-name (str "status-" lang))))) (def section-featured "Featured") -(defn featured-public-chats [] +(defn featured-public-chats + [] (let [lang-topic (get-language-topic) - chats (some #(when (= section-featured (first %)) (second %)) (chat.models/chats))] + chats (some #(when (= section-featured (first %)) (second %)) (chat.models/chats))] (if lang-topic (conj chats lang-topic) chats))) -(views/defview new-public-chat [] +(views/defview new-public-chat + [] (views/letsubs [topic [:public-group-topic] error [:public-chat.new/topic-error-message]] [react/scroll-view {:style {:flex 1}} [react/view {:padding-horizontal 16} [react/view {:align-items :center :padding-vertical 8} - [react/image {:source (:new-chat-header resources/ui) - :style {:width 160 :height 160}}]] + [react/image + {:source (:new-chat-header resources/ui) + :style {:width 160 :height 160}}]] [react/text {:style {:text-align :center :margin-bottom 16 :line-height 22}} (i18n/label :t/public-chat-description)] [chat-name-input topic error]]])) diff --git a/src/status_im/ui/screens/advanced_settings/views.cljs b/src/status_im/ui/screens/advanced_settings/views.cljs index f8f54f40ab..75335d1dbd 100644 --- a/src/status_im/ui/screens/advanced_settings/views.cljs +++ b/src/status_im/ui/screens/advanced_settings/views.cljs @@ -1,20 +1,21 @@ (ns status-im.ui.screens.advanced-settings.views - (:require [re-frame.core :as re-frame] + (:require [quo.core :as quo] + [re-frame.core :as re-frame] [status-im.i18n.i18n :as i18n] - [quo.core :as quo] - [status-im2.setup.config :as config] - [status-im.ui.components.list.views :as list]) + [status-im.ui.components.list.views :as list] + [status-im2.setup.config :as config]) (:require-macros [status-im.utils.views :as views])) -(defn- normal-mode-settings-data [{:keys [network-name - current-log-level - waku-bloom-filter-mode - communities-enabled? - transactions-management-enabled? - wakuv2-flag - current-fleet - webview-debug - mutual-contact-requests-enabled?]}] +(defn- normal-mode-settings-data + [{:keys [network-name + current-log-level + waku-bloom-filter-mode + communities-enabled? + transactions-management-enabled? + wakuv2-flag + current-fleet + webview-debug + mutual-contact-requests-enabled?]}] (keep identity [{:size :small @@ -78,7 +79,7 @@ :chevron true} ;; If it's enabled in the config, we don't show the option (when (not config/communities-enabled?) - {:size :small + {:size :small :title (i18n/label :t/communities-enabled) :accessibility-label :communities-enabled :container-margin-bottom 8 @@ -87,16 +88,17 @@ [:multiaccounts.ui/switch-communities-enabled (not communities-enabled?)]) :accessory :switch :active communities-enabled?}) - {:size :small + {:size :small :title (i18n/label :t/transactions-management-enabled) :accessibility-label :transactions-management-enabled :container-margin-bottom 8 :on-press #(re-frame/dispatch - [:multiaccounts.ui/switch-transactions-management-enabled (not transactions-management-enabled?)]) + [:multiaccounts.ui/switch-transactions-management-enabled + (not transactions-management-enabled?)]) :accessory :switch :active transactions-management-enabled?} - {:size :small + {:size :small :title "Webview debug" :accessibility-label :webview-debug-switch :container-margin-bottom 8 @@ -120,19 +122,23 @@ :container-margin-bottom 8 :on-press #(re-frame/dispatch - [:multiaccounts.ui/switch-mutual-contact-requests-enabled (not mutual-contact-requests-enabled?)]) + [:multiaccounts.ui/switch-mutual-contact-requests-enabled + (not mutual-contact-requests-enabled?)]) :accessory :switch :active mutual-contact-requests-enabled?}])) -(defn- flat-list-data [options] +(defn- flat-list-data + [options] (normal-mode-settings-data options)) -(defn- render-item [props] +(defn- render-item + [props] (if (= (:type props) :section-header) [quo/list-header (:title props)] [quo/list-item props])) -(views/defview advanced-settings [] +(views/defview advanced-settings + [] (views/letsubs [{:keys [webview-debug]} [:multiaccount] network-name [:network-name] waku-bloom-filter-mode [:waku/bloom-filter-mode] diff --git a/src/status_im/ui/screens/appearance/views.cljs b/src/status_im/ui/screens/appearance/views.cljs index 38e79e34f6..1ddc44a027 100644 --- a/src/status_im/ui/screens/appearance/views.cljs +++ b/src/status_im/ui/screens/appearance/views.cljs @@ -1,33 +1,39 @@ (ns status-im.ui.screens.appearance.views (:require-macros [status-im.utils.views :as views]) - (:require [re-frame.core :as re-frame] - [status-im.ui.components.react :as react] - [status-im.react-native.resources :as resources] - [quo.core :as quo] + (:require [quo.core :as quo] [quo.design-system.colors :as colors] + [re-frame.core :as re-frame] [status-im.i18n.i18n :as i18n] + [status-im.react-native.resources :as resources] + [status-im.ui.components.react :as react] [status-im.utils.config :as config])) -(defn button [label icon theme selected?] +(defn button + [label icon theme selected?] [react/touchable-highlight {:on-press (fn [] (re-frame/dispatch [:multiaccounts.ui/appearance-switched theme]) (re-frame/dispatch (if config/new-ui-enabled? [:reload-new-ui] [:init-root :chat-stack])) (re-frame/dispatch [:navigate-change-tab :profile]) (js/setTimeout #(re-frame/dispatch [:navigate-to :appearance]) 1000))} - [react/view (merge {:align-items :center :padding 8 :border-radius 20} - (when selected? - {:background-color colors/blue-light})) + [react/view + (merge {:align-items :center :padding 8 :border-radius 20} + (when selected? + {:background-color colors/blue-light})) [react/image {:source (get resources/ui icon)}] [react/text {:style {:margin-top 8}} (i18n/label label)]]]) -(views/defview appearance [] +(views/defview appearance + [] (views/letsubs [{:keys [appearance]} [:multiaccount]] [:<> [quo/list-header (i18n/label :t/preference)] - [react/view {:flex-direction :row :padding-horizontal 8 - :justify-content :space-between :margin-vertical 16} + [react/view + {:flex-direction :row + :padding-horizontal 8 + :justify-content :space-between + :margin-vertical 16} [button :t/light :theme-light 1 (= 1 appearance)] [button :t/dark :theme-dark 2 (= 2 appearance)] [button :t/system :theme-system 0 (= 0 appearance)]]])) diff --git a/src/status_im/ui/screens/backup_settings/view.cljs b/src/status_im/ui/screens/backup_settings/view.cljs index 67f482f74b..62d03c5504 100644 --- a/src/status_im/ui/screens/backup_settings/view.cljs +++ b/src/status_im/ui/screens/backup_settings/view.cljs @@ -1,23 +1,26 @@ (ns status-im.ui.screens.backup-settings.view (:require-macros [status-im.utils.views :as views]) - (:require [status-im.ui.components.react :as react] + (:require [quo.core :as quo] [quo.design-system.colors :as colors] - [status-im.utils.datetime :as datetime] + [re-frame.core :as re-frame] [status-im.i18n.i18n :as i18n] - [quo.core :as quo] - [re-frame.core :as re-frame])) + [status-im.ui.components.react :as react] + [status-im.utils.datetime :as datetime])) -(defn perform-backup! [] +(defn perform-backup! + [] (re-frame/dispatch [:multiaccounts.ui/perform-backup-pressed])) -(defn footer [performing-backup?] - [react/touchable-highlight {:on-press (when-not performing-backup? - perform-backup!) - :style {:height 52}} +(defn footer + [performing-backup?] + [react/touchable-highlight + {:on-press (when-not performing-backup? + perform-backup!) + :style {:height 52}} [react/view - {:style {:justify-content :center - :flex 1 - :align-items :center}} + {:style {:justify-content :center + :flex 1 + :align-items :center}} [react/text {:color colors/blue :text-align :center} @@ -25,15 +28,16 @@ (i18n/label :t/backing-up) (i18n/label :t/perform-backup))]]]) -(views/defview backup-settings [] +(views/defview backup-settings + [] (views/letsubs [{:keys [last-backup backup-enabled?]} [:multiaccount] - performing-backup? [:backup/performing-backup]] + performing-backup? [:backup/performing-backup]] [:<> [react/scroll-view [quo/list-item - {:size :small + {:size :small :title (i18n/label :t/backup-through-waku) :accessibility-label :backup-enabled :container-margin-bottom 8 @@ -43,10 +47,10 @@ :accessory :switch :active backup-enabled?}] [quo/list-item - {:size :small - :title (i18n/label :t/last-backup-performed) - :accessory :text - :subtitle (if (pos? last-backup) - (datetime/time-ago-long (datetime/to-date (* 1000 last-backup))) - (i18n/label :t/never))}]] + {:size :small + :title (i18n/label :t/last-backup-performed) + :accessory :text + :subtitle (if (pos? last-backup) + (datetime/time-ago-long (datetime/to-date (* 1000 last-backup))) + (i18n/label :t/never))}]] [footer performing-backup?]])) diff --git a/src/status_im/ui/screens/biometric/views.cljs b/src/status_im/ui/screens/biometric/views.cljs index df9751fb49..340e625368 100644 --- a/src/status_im/ui/screens/biometric/views.cljs +++ b/src/status_im/ui/screens/biometric/views.cljs @@ -1,16 +1,18 @@ (ns status-im.ui.screens.biometric.views - (:require [status-im.ui.components.react :as react] - [quo.core :as quo] - [re-frame.core :as re-frame] - [status-im.multiaccounts.biometric.core :as biometric] + (:require [quo.core :as quo] [quo.design-system.colors :as colors] + [re-frame.core :as re-frame] + [status-im.i18n.i18n :as i18n] + [status-im.multiaccounts.biometric.core :as biometric] [status-im.ui.components.icons.icons :as icons] - [status-im.i18n.i18n :as i18n])) + [status-im.ui.components.react :as react])) -(defn get-supported-biometric-auth [] +(defn get-supported-biometric-auth + [] @(re-frame/subscribe [:supported-biometric-auth])) -(defn get-bio-type-label [] +(defn get-bio-type-label + [] (biometric/get-label (get-supported-biometric-auth))) (defn biometric-popover @@ -18,28 +20,33 @@ ok-button-label cancel-button-label on-cancel on-confirm]}] (let [supported-biometric-auth (get-supported-biometric-auth) bio-type-label (get-bio-type-label)] - [react/view {:margin-top 24 - :align-items :center - :padding-horizontal 24} - [react/view {:width 32 - :height 32 - :background-color colors/blue-light - :border-radius 16 - :align-items :center - :justify-content :center} - [icons/icon (if (= supported-biometric-auth :FaceID) - :main-icons/faceid - :main-icons/print) + [react/view + {:margin-top 24 + :align-items :center + :padding-horizontal 24} + [react/view + {:width 32 + :height 32 + :background-color colors/blue-light + :border-radius 16 + :align-items :center + :justify-content :center} + [icons/icon + (if (= supported-biometric-auth :FaceID) + :main-icons/faceid + :main-icons/print) {:color colors/blue}]] - [react/text {:style {:typography :title-bold - :margin-top 16}} + [react/text + {:style {:typography :title-bold + :margin-top 16}} (str (i18n/label title-label {:bio-type-label bio-type-label}))] (vec (concat - [react/nested-text {:style {:margin-top 8 - :color colors/gray - :text-align :center}}] + [react/nested-text + {:style {:margin-top 8 + :color colors/gray + :text-align :center}}] (if description-label [(i18n/label description-label {:bio-type-label bio-type-label})] description-text))) @@ -48,32 +55,37 @@ [quo/button {:on-press #(re-frame/dispatch [on-confirm])} (i18n/label ok-button-label {:bio-type-label bio-type-label})]] - [quo/button {:type :secondary - :on-press #(re-frame/dispatch [(or on-cancel :hide-popover)])} + [quo/button + {:type :secondary + :on-press #(re-frame/dispatch [(or on-cancel :hide-popover)])} (or cancel-button-label (i18n/label :t/cancel))]]])) -(defn disable-password-saving-popover [] +(defn disable-password-saving-popover + [] (let [bio-label-type (get-bio-type-label)] [biometric-popover - {:title-label :t/biometric-disable-password-title - :ok-button-label :t/continue - :on-confirm :biometric/disable + {:title-label :t/biometric-disable-password-title + :ok-button-label :t/continue + :on-confirm :biometric/disable :description-text [[{:style {:color colors/gray}} (i18n/label :t/biometric-disable-password-description)] - [{} (i18n/label :t/biometric-disable-bioauth - {:bio-type-label bio-label-type})]]}])) + [{} + (i18n/label :t/biometric-disable-bioauth + {:bio-type-label bio-label-type})]]}])) -(defn enable-biometric-popover [] +(defn enable-biometric-popover + [] [biometric-popover {:title-label :t/biometric-secure-with :description-label :t/to-enable-biometric :ok-button-label :t/biometric-enable-button :on-confirm :biometric-logout}]) -(defn secure-with-biometric-popover [] +(defn secure-with-biometric-popover + [] (let [keycard-account? @(re-frame/subscribe [:multiaccounts.login/keycard-account?])] [biometric-popover diff --git a/src/status_im/ui/screens/bootnodes_settings/edit_bootnode/styles.cljs b/src/status_im/ui/screens/bootnodes_settings/edit_bootnode/styles.cljs index 0da5ae506e..d36baaee31 100644 --- a/src/status_im/ui/screens/bootnodes_settings/edit_bootnode/styles.cljs +++ b/src/status_im/ui/screens/bootnodes_settings/edit_bootnode/styles.cljs @@ -13,11 +13,11 @@ :margin-horizontal 16}) (styles/def button - {:height 52 - :align-items :center - :justify-content :center - :border-radius 8 - :ios {:opacity 0.9}}) + {:height 52 + :align-items :center + :justify-content :center + :border-radius 8 + :ios {:opacity 0.9}}) (def button-label {:color colors/white-persist @@ -25,4 +25,5 @@ (def delete-button (assoc button - :background-color colors/red)) + :background-color + colors/red)) diff --git a/src/status_im/ui/screens/bootnodes_settings/edit_bootnode/views.cljs b/src/status_im/ui/screens/bootnodes_settings/edit_bootnode/views.cljs index 15590dad00..ff20c54b44 100644 --- a/src/status_im/ui/screens/bootnodes_settings/edit_bootnode/views.cljs +++ b/src/status_im/ui/screens/bootnodes_settings/edit_bootnode/views.cljs @@ -1,26 +1,27 @@ (ns status-im.ui.screens.bootnodes-settings.edit-bootnode.views (:require [clojure.string :as string] + [quo.core :as quo] [re-frame.core :as re-frame] [status-im.i18n.i18n :as i18n] + [status-im.qr-scanner.core :as qr-scanner] [status-im.ui.components.react :as react] [status-im.ui.components.toolbar :as toolbar] - [quo.core :as quo] - [status-im.qr-scanner.core :as qr-scanner] [status-im.ui.components.topbar :as topbar] - [status-im.ui.screens.bootnodes-settings.edit-bootnode.styles - :as - styles]) + [status-im.ui.screens.bootnodes-settings.edit-bootnode.styles :as styles]) (:require-macros [status-im.utils.views :as views])) -(defn delete-button [id] +(defn delete-button + [id] [react/touchable-highlight {:on-press #(re-frame/dispatch [:bootnodes.ui/delete-pressed id])} [react/view styles/button-container - [react/view {:style styles/delete-button - :accessibility-label :bootnode-delete-button} + [react/view + {:style styles/delete-button + :accessibility-label :bootnode-delete-button} [react/text {:style styles/button-label} (i18n/label :t/delete)]]]]) -(views/defview edit-bootnode [] +(views/defview edit-bootnode + [] (views/letsubs [manage-bootnode [:get-manage-bootnode] validation-errors [:wakuv2-nodes/validation-errors]] (let [url (get-in manage-bootnode [:url :value]) @@ -28,8 +29,9 @@ name (get-in manage-bootnode [:name :value]) is-valid? (empty? validation-errors) invalid-url? (contains? validation-errors :url)] - [react/keyboard-avoiding-view {:style {:flex 1} - :ignore-offset true} + [react/keyboard-avoiding-view + {:style {:flex 1} + :ignore-offset true} [topbar/topbar {:title (i18n/label (if id :t/bootnode-details :t/add-bootnode))}] [react/scroll-view {:keyboard-should-persist-taps :handled} [react/view styles/edit-bootnode-view @@ -57,16 +59,17 @@ {:format (i18n/label :t/bootnode-format)})) :bottom-value 0 :after {:icon :main-icons/qr - :on-press #(re-frame/dispatch [::qr-scanner/scan-code - {:title (i18n/label :t/add-bootnode) - :handler :bootnodes.callback/qr-code-scanned}])}})]] + :on-press #(re-frame/dispatch + [::qr-scanner/scan-code + {:title (i18n/label :t/add-bootnode) + :handler :bootnodes.callback/qr-code-scanned}])}})]] (when id [delete-button id])]] [toolbar/toolbar {:right [quo/button - {:type :secondary - :after :main-icon/next - :disabled (not is-valid?) - :on-press #(re-frame/dispatch [:bootnodes.ui/save-pressed])} + {:type :secondary + :after :main-icon/next + :disabled (not is-valid?) + :on-press #(re-frame/dispatch [:bootnodes.ui/save-pressed])} (i18n/label :t/save)]}]]))) diff --git a/src/status_im/ui/screens/bootnodes_settings/styles.cljs b/src/status_im/ui/screens/bootnodes_settings/styles.cljs index 77b9a18de9..b9c0d3934a 100644 --- a/src/status_im/ui/screens/bootnodes_settings/styles.cljs +++ b/src/status_im/ui/screens/bootnodes_settings/styles.cljs @@ -2,7 +2,7 @@ (:require [status-im.utils.styles :as styles])) (def wrapper - {:flex 1}) + {:flex 1}) (def bootnode-item-inner {:padding-horizontal 16}) @@ -15,8 +15,8 @@ :android {:height 56}}) (def bootnode-item-name-text - {:font-size 17}) + {:font-size 17}) (def switch-container - {:height 50 - :padding-left 15}) + {:height 50 + :padding-left 15}) diff --git a/src/status_im/ui/screens/bootnodes_settings/views.cljs b/src/status_im/ui/screens/bootnodes_settings/views.cljs index 7341523c72..b8999b90fe 100644 --- a/src/status_im/ui/screens/bootnodes_settings/views.cljs +++ b/src/status_im/ui/screens/bootnodes_settings/views.cljs @@ -4,14 +4,16 @@ [status-im.i18n.i18n :as i18n] [status-im.ui.components.list.views :as list] [status-im.ui.components.react :as react] - [status-im.ui.screens.profile.components.views :as profile.components] + [status-im.ui.components.topbar :as topbar] [status-im.ui.screens.bootnodes-settings.styles :as styles] - [status-im.ui.components.topbar :as topbar])) + [status-im.ui.screens.profile.components.views :as profile.components])) -(defn navigate-to-add-bootnode [id] +(defn navigate-to-add-bootnode + [id] (re-frame/dispatch [:bootnodes.ui/add-bootnode-pressed id])) -(defn render-row [{:keys [name id]}] +(defn render-row + [{:keys [name id]}] [react/touchable-highlight {:on-press #(navigate-to-add-bootnode id) :accessibility-label :bootnode-item} @@ -20,22 +22,25 @@ [react/text {:style styles/bootnode-item-name-text} name]]]]) -(views/defview bootnodes-settings [] +(views/defview bootnodes-settings + [] (views/letsubs [bootnodes-enabled [:custom-bootnodes/enabled?] bootnodes [:custom-bootnodes/network-bootnodes]] [:<> - [topbar/topbar {:title (i18n/label :t/bootnodes-settings) - :right-accessories - [{:icon :main-icons/add - :accessibility-label :add-bootnode - :on-press #(navigate-to-add-bootnode nil)}]}] + [topbar/topbar + {:title (i18n/label :t/bootnodes-settings) + :right-accessories + [{:icon :main-icons/add + :accessibility-label :add-bootnode + :on-press #(navigate-to-add-bootnode nil)}]}] [react/view styles/switch-container [profile.components/settings-switch-item {:label-kw :t/bootnodes-enabled :value bootnodes-enabled :action-fn #(re-frame/dispatch [:bootnodes.ui/custom-bootnodes-switch-toggled %])}]] [react/view styles/wrapper - [list/flat-list {:data (vals bootnodes) - :default-separator? false - :key-fn :id - :render-fn render-row}]]])) + [list/flat-list + {:data (vals bootnodes) + :default-separator? false + :key-fn :id + :render-fn render-row}]]])) diff --git a/src/status_im/ui/screens/bottom_sheets/views.cljs b/src/status_im/ui/screens/bottom_sheets/views.cljs index df9eed6dc0..5c935aa52c 100644 --- a/src/status_im/ui/screens/bottom_sheets/views.cljs +++ b/src/status_im/ui/screens/bottom_sheets/views.cljs @@ -9,7 +9,8 @@ [status-im.ui.screens.multiaccounts.recover.views :as recover.views] [status-im2.contexts.chat.messages.pin.list.view :as pin.list])) -(defn bottom-sheet [] +(defn bottom-sheet + [] (let [{:keys [show? view options]} @(re-frame/subscribe [:bottom-sheet]) {:keys [content] :as opts} diff --git a/src/status_im/ui/screens/browser/accounts.cljs b/src/status_im/ui/screens/browser/accounts.cljs index d989b37046..bd0c0869ca 100644 --- a/src/status_im/ui/screens/browser/accounts.cljs +++ b/src/status_im/ui/screens/browser/accounts.cljs @@ -1,13 +1,14 @@ (ns status-im.ui.screens.browser.accounts - (:require [status-im.ui.components.list.views :as list] + (:require [quo.core :as quo] + [re-frame.core :as re-frame] [status-im.i18n.i18n :as i18n] - [status-im.ui.components.react :as react] - [quo.core :as quo] [status-im.ui.components.chat-icon.screen :as chat-icon] - [status-im.utils.utils :as utils] - [re-frame.core :as re-frame])) + [status-im.ui.components.list.views :as list] + [status-im.ui.components.react :as react] + [status-im.utils.utils :as utils])) -(defn render-account [account _ _ dapps-account] +(defn render-account + [account _ _ dapps-account] [quo/list-item (merge {:accessory :radio :active (= (:address dapps-account) (:address account)) @@ -15,14 +16,16 @@ :title (:name account) :subtitle (utils/get-shortened-checksum-address (:address account))} (when (not= (:address dapps-account) (:address account)) - {:on-press #(re-frame/dispatch [:dapps-account-selected (:address account)])}))]) + {:on-press #(re-frame/dispatch [:dapps-account-selected (:address account)])}))]) -(defn accounts-list [accounts dapps-account] +(defn accounts-list + [accounts dapps-account] (fn [] [react/view {:flex 1} [react/text {:style {:margin 16 :text-align :center}} (i18n/label :t/select-account-dapp)] - [list/flat-list {:data accounts - :key-fn :address - :render-data dapps-account - :render-fn render-account}]])) + [list/flat-list + {:data accounts + :key-fn :address + :render-data dapps-account + :render-fn render-account}]])) diff --git a/src/status_im/ui/screens/browser/bookmarks/views.cljs b/src/status_im/ui/screens/browser/bookmarks/views.cljs index 833ec10546..48c14c4f39 100644 --- a/src/status_im/ui/screens/browser/bookmarks/views.cljs +++ b/src/status_im/ui/screens/browser/bookmarks/views.cljs @@ -1,21 +1,23 @@ (ns status-im.ui.screens.browser.bookmarks.views - (:require [status-im.ui.components.keyboard-avoid-presentation :as kb-presentation] - [status-im.ui.components.react :as react] - [status-im.ui.components.topbar :as topbar] - [status-im.i18n.i18n :as i18n] - [status-im.ui.components.toolbar :as toolbar] + (:require [clojure.string :as string] [quo.core :as quo] [quo.design-system.colors :as colors] + [re-frame.core :as re-frame] [reagent.core :as reagent] - [clojure.string :as string] - [re-frame.core :as re-frame])) + [status-im.i18n.i18n :as i18n] + [status-im.ui.components.keyboard-avoid-presentation :as kb-presentation] + [status-im.ui.components.react :as react] + [status-im.ui.components.toolbar :as toolbar] + [status-im.ui.components.topbar :as topbar])) -(defn screen [{:keys [url name new]}] +(defn screen + [{:keys [url name new]}] (let [input-name (reagent/atom name)] (fn [] (let [edit? (not new)] - [kb-presentation/keyboard-avoiding-view {:style {:flex 1} - :ignore-offset true} + [kb-presentation/keyboard-avoiding-view + {:style {:flex 1} + :ignore-offset true} [topbar/topbar {:modal? true :border-bottom true @@ -40,10 +42,13 @@ :type :secondary :disabled (string/blank? @input-name) :on-press #(do (if edit? - (re-frame/dispatch [:browser/update-bookmark {:url url :name @input-name}]) - (re-frame/dispatch [:browser/store-bookmark {:url url :name @input-name}])) + (re-frame/dispatch [:browser/update-bookmark + {:url url :name @input-name}]) + (re-frame/dispatch [:browser/store-bookmark + {:url url :name @input-name}])) (re-frame/dispatch [:navigate-back]))} (if edit? (i18n/label :t/save) (i18n/label :t/add-favourite))]}]])))) -(defn new-bookmark [] +(defn new-bookmark + [] [screen @(re-frame/subscribe [:get-screen-params])]) \ No newline at end of file diff --git a/src/status_im/ui/screens/browser/eip3085/sheet.cljs b/src/status_im/ui/screens/browser/eip3085/sheet.cljs index 6d03a9005e..cf6fbe5253 100644 --- a/src/status_im/ui/screens/browser/eip3085/sheet.cljs +++ b/src/status_im/ui/screens/browser/eip3085/sheet.cljs @@ -1,16 +1,17 @@ (ns status-im.ui.screens.browser.eip3085.sheet - (:require [re-frame.core :as re-frame] + (:require [quo.core :as quo] + [quo.design-system.colors :as colors] + [re-frame.core :as re-frame] [status-im.i18n.i18n :as i18n] [status-im.ui.components.chat-icon.screen :as chat-icon.screen] + [status-im.ui.components.copyable-text :as copyable-text] [status-im.ui.components.icons.icons :as icons] [status-im.ui.components.react :as react] - [status-im.ui.screens.browser.styles :as styles] - [quo.design-system.colors :as colors] - [quo.core :as quo] - [status-im.ui.components.copyable-text :as copyable-text]) + [status-im.ui.screens.browser.styles :as styles]) (:require-macros [status-im.utils.views :as views])) -(views/defview permissions-panel [dapp-name message-id params] +(views/defview permissions-panel + [dapp-name message-id params] (views/letsubs [{:keys [dapp? dapp]} [:get-current-browser]] [react/view {} [react/view styles/permissions-panel-icons-container @@ -36,7 +37,8 @@ [icons/icon :main-icons/wallet {:color colors/white}]]] [react/text {:style styles/permissions-panel-title-label :number-of-lines 2} (str "\"" dapp-name "\" Allow this site to add a network?")] - [react/text {:style styles/permissions-panel-description-label :number-of-lines 4} "This will allow this network to be used within Status.Status does not verify custom networks.Learn about scams and network security risks."] + [react/text {:style styles/permissions-panel-description-label :number-of-lines 4} + "This will allow this network to be used within Status.Status does not verify custom networks.Learn about scams and network security risks."] [react/scroll-view [copyable-text/copyable-text-view {:copied-text (:name (:new-network params))} @@ -62,18 +64,21 @@ :title "Chain ID" :accessory :text :accessory-text (str (get-in params [:new-network :config :NetworkId]))}]]] - [react/view {:style {:flex-direction :row - :justify-content :center - :margin-horizontal 8 - :margin-top 24}} - [react/view {:flex 1 - :margin-horizontal 8} + [react/view + {:style {:flex-direction :row + :justify-content :center + :margin-horizontal 8 + :margin-top 24}} + [react/view + {:flex 1 + :margin-horizontal 8} [quo/button {:theme :negative :on-press #(re-frame/dispatch [:eip3085.ui/dapp-permission-denied message-id params])} (i18n/label :t/deny)]] - [react/view {:flex 1 - :margin-horizontal 8} + [react/view + {:flex 1 + :margin-horizontal 8} [quo/button {:theme :positive :style {:margin-horizontal 8} diff --git a/src/status_im/ui/screens/browser/eip3326/sheet.cljs b/src/status_im/ui/screens/browser/eip3326/sheet.cljs index 5cb88848a8..9f7b5feb28 100644 --- a/src/status_im/ui/screens/browser/eip3326/sheet.cljs +++ b/src/status_im/ui/screens/browser/eip3326/sheet.cljs @@ -1,17 +1,18 @@ (ns status-im.ui.screens.browser.eip3326.sheet - (:require [re-frame.core :as re-frame] + (:require [quo.core :as quo] + [quo.design-system.colors :as colors] + [re-frame.core :as re-frame] [status-im.i18n.i18n :as i18n] + [status-im.network.core :as network] [status-im.ui.components.chat-icon.screen :as chat-icon.screen] [status-im.ui.components.icons.icons :as icons] [status-im.ui.components.react :as react] [status-im.ui.screens.browser.styles :as styles] - [utils.debounce :as debounce] - [status-im.network.core :as network] - [quo.design-system.colors :as colors] - [quo.core :as quo]) + [utils.debounce :as debounce]) (:require-macros [status-im.utils.views :as views])) -(views/defview permissions-panel [dapp-name message-id {:keys [network-from network-to target-network-id] :as params}] +(views/defview permissions-panel + [dapp-name message-id {:keys [network-from network-to target-network-id] :as params}] (views/letsubs [{:keys [dapp? dapp]} [:get-current-browser]] [react/view {} [react/view styles/permissions-panel-icons-container @@ -38,21 +39,29 @@ [react/text {:style styles/permissions-panel-title-label :number-of-lines 2} (str "\"" dapp-name "\" Allow this site to switch the network?")] [react/text {:style styles/permissions-panel-description-label :number-of-lines 5} - (str "This will switch the selected network within Status to a previously added network:\n" network-from " -> " network-to "\nit will require login/logout")] - [react/view {:style {:flex-direction :row - :justify-content :center - :margin-horizontal 8 - :margin-top 24}} - [react/view {:flex 1 - :margin-horizontal 8} + (str "This will switch the selected network within Status to a previously added network:\n" + network-from + " -> " + network-to + "\nit will require login/logout")] + [react/view + {:style {:flex-direction :row + :justify-content :center + :margin-horizontal 8 + :margin-top 24}} + [react/view + {:flex 1 + :margin-horizontal 8} [quo/button {:theme :negative :on-press #(re-frame/dispatch [:eip3326.ui/dapp-permission-denied message-id params])} (i18n/label :t/deny)]] - [react/view {:flex 1 - :margin-horizontal 8} + [react/view + {:flex 1 + :margin-horizontal 8} [quo/button {:theme :positive :style {:margin-horizontal 8} - :on-press #(debounce/dispatch-and-chill [::network/connect-network-pressed target-network-id] 1000)} + :on-press #(debounce/dispatch-and-chill [::network/connect-network-pressed target-network-id] + 1000)} (i18n/label :t/allow)]]]])) diff --git a/src/status_im/ui/screens/browser/empty_tab/styles.cljs b/src/status_im/ui/screens/browser/empty_tab/styles.cljs index 482d5a026e..86c826f4ce 100644 --- a/src/status_im/ui/screens/browser/empty_tab/styles.cljs +++ b/src/status_im/ui/screens/browser/empty_tab/styles.cljs @@ -9,7 +9,8 @@ {:margin-horizontal 16 :margin-vertical 10}) -(defn browser-icon-container [] +(defn browser-icon-container + [] {:width 40 :height 40 :border-radius 20 @@ -17,7 +18,8 @@ :align-items :center :justify-content :center}) -(defn dapp-store-container [] +(defn dapp-store-container + [] {:margin 16 :border-color colors/gray-lighter :margin-top 18 @@ -33,7 +35,8 @@ :font-weight "500" :line-height 22}) -(defn dapps-account [color] +(defn dapps-account + [color] {:flex-direction :row :background-color color :border-radius 36 diff --git a/src/status_im/ui/screens/browser/empty_tab/views.cljs b/src/status_im/ui/screens/browser/empty_tab/views.cljs index 0b457eb6a4..d54d1ca72d 100644 --- a/src/status_im/ui/screens/browser/empty_tab/views.cljs +++ b/src/status_im/ui/screens/browser/empty_tab/views.cljs @@ -1,77 +1,88 @@ (ns status-im.ui.screens.browser.empty-tab.views - (:require [re-frame.core :as re-frame] + (:require [quo.core :as quo] + [quo.design-system.colors :as colors] + [re-frame.core :as re-frame] + [reagent.core :as reagent] [status-im.i18n.i18n :as i18n] [status-im.react-native.resources :as resources] - [quo.design-system.colors :as colors] [status-im.ui.components.common.common :as components.common] [status-im.ui.components.icons.icons :as icons] - [quo.core :as quo] [status-im.ui.components.list.views :as list] [status-im.ui.components.react :as react] [status-im.ui.screens.browser.accounts :as accounts] [status-im.ui.screens.browser.empty-tab.styles :as styles] - [status-im.ui.screens.wallet.components.views :as components] [status-im.ui.screens.browser.views :as browser] - [status-im.utils.http :as http] - [reagent.core :as reagent]) + [status-im.ui.screens.wallet.components.views :as components] + [status-im.utils.http :as http]) (:require-macros [status-im.utils.views :as views])) -(defn hide-sheet-and-dispatch [event] +(defn hide-sheet-and-dispatch + [event] (re-frame/dispatch [:bottom-sheet/hide]) (js/setTimeout #(re-frame/dispatch event) 200)) -(defn list-item [_] +(defn list-item + [_] (let [loaded (reagent/atom nil)] (fn [{:keys [name url] :as bookmark}] [quo/list-item {:accessibility-label (keyword (str "fav-item" name)) - :on-press #(re-frame/dispatch [:browser.ui/open-url url]) - :on-long-press (fn [] - (re-frame/dispatch - [:bottom-sheet/show-sheet - {:content (fn [] - [react/view {:flex 1} - [quo/list-item - {:theme :accent - :title (i18n/label :t/open-in-new-tab) - :accessibility-label :open-in-new-tab - :icon :main-icons/tabs - :on-press #(hide-sheet-and-dispatch [:browser.ui/open-url url])}] - [quo/list-item - {:theme :accent - :title (i18n/label :t/edit) - :accessibility-label :edit-bookmark - :icon :main-icons/edit - :on-press #(hide-sheet-and-dispatch [:open-modal :new-bookmark bookmark])}] - [quo/list-item - {:theme :negative - :title (i18n/label :t/delete) - :accessibility-label :delete-bookmark - :icon :main-icons/delete - :on-press #(hide-sheet-and-dispatch [:browser/delete-bookmark url])}]])}])) - :title name - :subtitle (or url (i18n/label :t/dapp)) - :icon [react/view {:width 40 - :height 40 - :align-items :center - :justify-content :center} - (when (or (nil? @loaded) @loaded) - [react/image {:onLoad #(reset! loaded true) - :style {:width 32 :height 32 :position :absolute :top 4 :left 4} - :source {:uri (str "https://" (http/url-host url) "/favicon.ico")}}]) - (when-not @loaded - [react/view {:width 40 - :height 40 - :border-radius 20 - :background-color colors/gray-lighter - :align-items :center - :justify-content :center} - [icons/icon :main-icons/browser {:color colors/gray}]])]}]))) + :on-press #(re-frame/dispatch [:browser.ui/open-url url]) + :on-long-press (fn [] + (re-frame/dispatch + [:bottom-sheet/show-sheet + {:content (fn [] + [react/view {:flex 1} + [quo/list-item + {:theme :accent + :title (i18n/label :t/open-in-new-tab) + :accessibility-label :open-in-new-tab + :icon :main-icons/tabs + :on-press #(hide-sheet-and-dispatch + [:browser.ui/open-url url])}] + [quo/list-item + {:theme :accent + :title (i18n/label :t/edit) + :accessibility-label :edit-bookmark + :icon :main-icons/edit + :on-press #(hide-sheet-and-dispatch + [:open-modal :new-bookmark + bookmark])}] + [quo/list-item + {:theme :negative + :title (i18n/label :t/delete) + :accessibility-label :delete-bookmark + :icon :main-icons/delete + :on-press #(hide-sheet-and-dispatch + [:browser/delete-bookmark + url])}]])}])) + :title name + :subtitle (or url (i18n/label :t/dapp)) + :icon [react/view + {:width 40 + :height 40 + :align-items :center + :justify-content :center} + (when (or (nil? @loaded) @loaded) + [react/image + {:onLoad #(reset! loaded true) + :style {:width 32 :height 32 :position :absolute :top 4 :left 4} + :source {:uri (str "https://" (http/url-host url) "/favicon.ico")}}]) + (when-not @loaded + [react/view + {:width 40 + :height 40 + :border-radius 20 + :background-color colors/gray-lighter + :align-items :center + :justify-content :center} + [icons/icon :main-icons/browser {:color colors/gray}]])]}]))) (def dapp-image-data {:image (resources/get-image :dapp-store) :width 768 :height 333}) (defn dapp-image [] [components.common/image-contain nil dapp-image-data]) -(defn list-header [empty?] +(defn list-header + [empty?] [react/view [react/touchable-highlight {:on-press #(re-frame/dispatch [:browser.ui/open-url "https://dap.ps"])} [react/view (styles/dapp-store-container) @@ -83,55 +94,64 @@ [react/text {:style {:line-height 22 :font-size 15 :color colors/gray}} (i18n/label :t/favourites)]])]) -(views/defview select-account [] - (views/letsubs [accounts [:accounts-without-watch-only] +(views/defview select-account + [] + (views/letsubs [accounts [:accounts-without-watch-only] {:keys [name color] :as dapps-account} [:dapps-account]] - [react/view {:position :absolute - :z-index 2 - :align-items :center - :bottom 16 - :left 0 - :right 0 - :padding-horizontal 32} + [react/view + {:position :absolute + :z-index 2 + :align-items :center + :bottom 16 + :left 0 + :right 0 + :padding-horizontal 32} [quo/button {:accessibility-label :select-account :type :scale :on-press #(re-frame/dispatch [:bottom-sheet/show-sheet - {:content (accounts/accounts-list accounts dapps-account)}])} + {:content (accounts/accounts-list accounts + dapps-account)}])} [react/view (styles/dapps-account color) [icons/icon :main-icons/account {:color colors/white-persist}] [react/view {:flex-shrink 1} - [react/text {:numberOfLines 1 - :style {:margin-horizontal 6 :color colors/white-persist - :typography :main-medium}} + [react/text + {:numberOfLines 1 + :style {:margin-horizontal 6 + :color colors/white-persist + :typography :main-medium}} name]] [icons/icon :main-icons/dropdown {:color colors/white-transparent-persist}]]]])) -(views/defview empty-tab [] - (views/letsubs [bookmarks [:bookmarks/active] +(views/defview empty-tab + [] + (views/letsubs [bookmarks [:bookmarks/active] dapps-account [:dapps-account] - url-text (atom nil)] + url-text (atom nil)] (let [bookmarks (vals bookmarks)] - [react/keyboard-avoiding-view {:style {:flex 1} - :ignore-offset true} - [quo/text-input {:on-change-text #(reset! url-text %) - :on-submit-editing #(re-frame/dispatch [:browser.ui/open-url @url-text]) - :placeholder (i18n/label :t/enter-url) - :auto-capitalize :none - :auto-correct false - :style styles/input - :container-style styles/input-container-style - :accessibility-label :dapp-url-input - :return-key-type :go}] + [react/keyboard-avoiding-view + {:style {:flex 1} + :ignore-offset true} + [quo/text-input + {:on-change-text #(reset! url-text %) + :on-submit-editing #(re-frame/dispatch [:browser.ui/open-url @url-text]) + :placeholder (i18n/label :t/enter-url) + :auto-capitalize :none + :auto-correct false + :style styles/input + :container-style styles/input-container-style + :accessibility-label :dapp-url-input + :return-key-type :go}] [components/separator-dark] - [list/flat-list {:header [list-header (empty? bookmarks)] - :data bookmarks - :key-fn :browser-id - :empty-component [react/view {:align-items :center :margin-top 20} - [icons/icon :main-icons/favourite {:color colors/gray}] - [react/text {:style {:color colors/gray :margin-top 4}} - (i18n/label :t/favourite-description)]] - :render-fn list-item}] + [list/flat-list + {:header [list-header (empty? bookmarks)] + :data bookmarks + :key-fn :browser-id + :empty-component [react/view {:align-items :center :margin-top 20} + [icons/icon :main-icons/favourite {:color colors/gray}] + [react/text {:style {:color colors/gray :margin-top 4}} + (i18n/label :t/favourite-description)]] + :render-fn list-item}] [browser/navigation {:dapps-account dapps-account :empty-tab true}]]))) diff --git a/src/status_im/ui/screens/browser/options/views.cljs b/src/status_im/ui/screens/browser/options/views.cljs index 5f70c6898d..d534105863 100644 --- a/src/status_im/ui/screens/browser/options/views.cljs +++ b/src/status_im/ui/screens/browser/options/views.cljs @@ -1,23 +1,25 @@ (ns status-im.ui.screens.browser.options.views - (:require [status-im.ui.components.react :as react] - [quo.core :as quo] - [status-im.i18n.i18n :as i18n] - [re-frame.core :as re-frame] - [status-im.ui.components.chat-icon.screen :as chat-icon] + (:require [quo.core :as quo] [quo.design-system.colors :as colors] - [status-im.ui.screens.wallet.components.views :as components] + [re-frame.core :as re-frame] [status-im.browser.core :as browser] - [status-im.utils.http :as http] - [status-im.utils.utils :as utils] - [status-im.ui.components.icons.icons :as icons] [status-im.constants :as constants] - [status-im.qr-scanner.core :as qr-scanner])) + [status-im.i18n.i18n :as i18n] + [status-im.qr-scanner.core :as qr-scanner] + [status-im.ui.components.chat-icon.screen :as chat-icon] + [status-im.ui.components.icons.icons :as icons] + [status-im.ui.components.react :as react] + [status-im.ui.screens.wallet.components.views :as components] + [status-im.utils.http :as http] + [status-im.utils.utils :as utils])) -(defn hide-sheet-and-dispatch [event] +(defn hide-sheet-and-dispatch + [event] (re-frame/dispatch [:bottom-sheet/hide]) (re-frame/dispatch event)) -(defn wallet-connection [host account] +(defn wallet-connection + [host account] (fn [] [react/view {:flex 1} [react/text {:style {:align-self :center :margin-horizontal 16 :margin-vertical 8}} @@ -36,22 +38,25 @@ :icon :main-icons/cancel :on-press #(hide-sheet-and-dispatch [:browser/revoke-dapp-permissions host])}]])) -(defn browser-options [url account empty-tab name] +(defn browser-options + [url account empty-tab name] (fn [] - (let [topic (http/topic-from-url url) - bookmarks @(re-frame/subscribe [:bookmarks/active]) + (let [topic (http/topic-from-url url) + bookmarks @(re-frame/subscribe [:bookmarks/active]) permissions @(re-frame/subscribe [:dapps/permissions]) - fav? (get bookmarks url) - connected? (some #{constants/dapp-permission-web3} (get-in permissions [(http/url-host url) :permissions]))] + fav? (get bookmarks url) + connected? (some #{constants/dapp-permission-web3} + (get-in permissions [(http/url-host url) :permissions]))] [react/view {:flex 1} - [quo/button {:style {:align-self :flex-end - :margin-right 15} - :type :icon - :theme :icon - :accessibility-label :universal-qr-scanner - :on-press #(hide-sheet-and-dispatch - [::qr-scanner/scan-code - {:handler ::qr-scanner/on-scan-success}])} + [quo/button + {:style {:align-self :flex-end + :margin-right 15} + :type :icon + :theme :icon + :accessibility-label :universal-qr-scanner + :on-press #(hide-sheet-and-dispatch + [::qr-scanner/scan-code + {:handler ::qr-scanner/on-scan-success}])} :main-icons/qr] (when-not empty-tab [:<> @@ -66,9 +71,10 @@ :title (if fav? (i18n/label :t/remove-favourite) (i18n/label :t/add-favourite)) :accessibility-label :add-remove-fav :icon (if fav? :main-icons/delete :main-icons/favourite) - :on-press #(hide-sheet-and-dispatch (if fav? - [:browser/delete-bookmark url] - [:open-modal :new-bookmark {:url url :name name :new true}]))}] + :on-press #(hide-sheet-and-dispatch + (if fav? + [:browser/delete-bookmark url] + [:open-modal :new-bookmark {:url url :name name :new true}]))}] [quo/list-item {:theme :accent :title (i18n/label :t/share) @@ -96,12 +102,14 @@ :subtitle (i18n/label :t/connected) :accessibility-label :connected-account :chevron true - :on-press #(hide-sheet-and-dispatch [:bottom-sheet/show-sheet - {:content (wallet-connection (http/url-host url) account)}])}] + :on-press #(hide-sheet-and-dispatch + [:bottom-sheet/show-sheet + {:content (wallet-connection (http/url-host url) account)}])}] [quo/list-item {:theme :accent :title (i18n/label :t/connect-wallet) :accessibility-label :connect-account :icon :main-icons/wallet - :on-press #(hide-sheet-and-dispatch [:browser/bridge-message-received - "{\"type\":\"api-request\",\"permission\":\"web3\"}"])}])]))) + :on-press #(hide-sheet-and-dispatch + [:browser/bridge-message-received + "{\"type\":\"api-request\",\"permission\":\"web3\"}"])}])]))) diff --git a/src/status_im/ui/screens/browser/permissions/views.cljs b/src/status_im/ui/screens/browser/permissions/views.cljs index fc1046f324..8befa3dbf0 100644 --- a/src/status_im/ui/screens/browser/permissions/views.cljs +++ b/src/status_im/ui/screens/browser/permissions/views.cljs @@ -1,5 +1,7 @@ (ns status-im.ui.screens.browser.permissions.views - (:require [re-frame.core :as re-frame] + (:require [quo.core :as quo] + [quo.design-system.colors :as colors] + [re-frame.core :as re-frame] [reagent.core :as reagent] [status-im.browser.permissions :as browser.permissions] [status-im.i18n.i18n :as i18n] @@ -7,35 +9,39 @@ [status-im.ui.components.chat-icon.screen :as chat-icon.screen] [status-im.ui.components.icons.icons :as icons] [status-im.ui.components.react :as react] - [status-im.ui.screens.browser.styles :as styles] - [quo.design-system.colors :as colors] - [quo.core :as quo]) + [status-im.ui.screens.browser.styles :as styles]) (:require-macros [status-im.utils.views :as views])) (defn hide-panel-anim [bottom-anim-value alpha-value] (anim/start (anim/parallel - [(anim/spring bottom-anim-value {:toValue styles/panel-height - :useNativeDriver true}) - (anim/timing alpha-value {:toValue 0 - :duration 500 - :useNativeDriver true})]))) + [(anim/spring bottom-anim-value + {:toValue styles/panel-height + :useNativeDriver true}) + (anim/timing alpha-value + {:toValue 0 + :duration 500 + :useNativeDriver true})]))) (defn show-panel-anim [bottom-anim-value alpha-value] (anim/start (anim/parallel - [(anim/spring bottom-anim-value {:toValue 20 - :useNativeDriver true}) - (anim/timing alpha-value {:toValue 0.6 - :duration 500 - :useNativeDriver true})]))) + [(anim/spring bottom-anim-value + {:toValue 20 + :useNativeDriver true}) + (anim/timing alpha-value + {:toValue 0.6 + :duration 500 + :useNativeDriver true})]))) -(defn permission-details [requested-permission] +(defn permission-details + [requested-permission] (get browser.permissions/supported-permissions requested-permission)) -(views/defview permissions-panel [[dapp? dapp dapps-account] {:keys [dapp-name]}] +(views/defview permissions-panel + [[dapp? dapp dapps-account] {:keys [dapp-name]}] (views/letsubs [bottom-anim-value (anim/create-value styles/panel-height) alpha-value (anim/create-value 0) current-permission (reagent/atom nil) @@ -43,7 +49,8 @@ {:UNSAFE_componentWillUpdate (fn [_ [_ _ {:keys [requested-permission]}]] (cond @update? - ;; the component has been updated with a new permission, we show the panel + ;; the component has been updated with a new permission, we show the + ;; panel (do (reset! update? false) (show-panel-anim bottom-anim-value alpha-value)) @@ -51,27 +58,30 @@ ;; a permission has been accepted/denied by the user, and there is ;; another permission that needs to be processed by the user ;; we hide the processed permission with an animation and update - ;; `current-permission` with a delay so that the information is still + ;; `current-permission` with a delay so that the information is + ;; still ;; available during the animation (do (reset! update? true) (js/setTimeout #(reset! current-permission - (permission-details requested-permission)) + (permission-details requested-permission)) 600) (hide-panel-anim bottom-anim-value alpha-value)) requested-permission - ;; the dapp is asking for a permission, we put it in current-permission + ;; the dapp is asking for a permission, we put it in + ;; current-permission ;; and start the show-animation (do (reset! current-permission - (get browser.permissions/supported-permissions - requested-permission)) + (get browser.permissions/supported-permissions + requested-permission)) (show-panel-anim bottom-anim-value alpha-value)) :else ;; a permission has been accepted/denied by the user, and there is ;; no other permission that needs to be processed by the user ;; we hide the processed permission with an animation and update - ;; `current-permission` with a delay so that the information is still + ;; `current-permission` with a delay so that the information is + ;; still ;; available during the animation (do (js/setTimeout #(reset! current-permission nil) 500) (hide-panel-anim bottom-anim-value alpha-value))))} @@ -110,24 +120,30 @@ [react/view styles/permissions-account [icons/icon :main-icons/account {:color (:color dapps-account)}] [react/view {:flex-shrink 1} - [react/text {:numberOfLines 1 - :style {:margin-horizontal 6 :color (:color dapps-account) :typography :main-medium - :font-size 13}} + [react/text + {:numberOfLines 1 + :style {:margin-horizontal 6 + :color (:color dapps-account) + :typography :main-medium + :font-size 13}} (:name dapps-account)]]]) [react/text {:style styles/permissions-panel-description-label :number-of-lines 2} description] - [react/view {:style {:flex-direction :row - :justify-content :center - :margin-horizontal 8 - :margin-top 24}} - [react/view {:flex 1 - :margin-horizontal 8} + [react/view + {:style {:flex-direction :row + :justify-content :center + :margin-horizontal 8 + :margin-top 24}} + [react/view + {:flex 1 + :margin-horizontal 8} [quo/button {:theme :negative :on-press #(re-frame/dispatch [:browser.permissions.ui/dapp-permission-denied])} (i18n/label :t/deny)]] - [react/view {:flex 1 - :margin-horizontal 8} + [react/view + {:flex 1 + :margin-horizontal 8} [quo/button {:theme :positive :style {:margin-horizontal 8} diff --git a/src/status_im/ui/screens/browser/site_blocked/styles.cljs b/src/status_im/ui/screens/browser/site_blocked/styles.cljs index 62095e0d7d..838dccd6a6 100644 --- a/src/status_im/ui/screens/browser/site_blocked/styles.cljs +++ b/src/status_im/ui/screens/browser/site_blocked/styles.cljs @@ -2,8 +2,8 @@ (:require [quo.design-system.colors :as colors])) (def container - {:justify-content :center - :flex 1}) + {:justify-content :center + :flex 1}) (def container-root-view {:flex 1 @@ -18,11 +18,11 @@ :text-align :center}) (def description-text - {:color colors/gray - :text-align :center}) + {:color colors/gray + :text-align :center}) (def chat-link-text {:color colors/blue}) (def buttons-container - {:margin 24}) + {:margin 24}) diff --git a/src/status_im/ui/screens/browser/site_blocked/views.cljs b/src/status_im/ui/screens/browser/site_blocked/views.cljs index b48199cf48..a3538d6974 100644 --- a/src/status_im/ui/screens/browser/site_blocked/views.cljs +++ b/src/status_im/ui/screens/browser/site_blocked/views.cljs @@ -1,17 +1,19 @@ (ns status-im.ui.screens.browser.site-blocked.views - (:require [re-frame.core :as re-frame] - [status-im.i18n.i18n :as i18n] + (:require [quo.core :as quo] [quo.design-system.colors :as colors] + [re-frame.core :as re-frame] + [status-im.i18n.i18n :as i18n] [status-im.ui.components.icons.icons :as icons] [status-im.ui.components.react :as react] - [status-im.ui.screens.browser.site-blocked.styles :as styles] - [quo.core :as quo]) + [status-im.ui.screens.browser.site-blocked.styles :as styles]) (:require-macros [status-im.utils.views :as views])) -(views/defview view [{:keys [can-go-back?]}] - [react/scroll-view {:keyboard-should-persist-taps :always - :bounces false - :content-container-style styles/container} +(views/defview view + [{:keys [can-go-back?]}] + [react/scroll-view + {:keyboard-should-persist-taps :always + :bounces false + :content-container-style styles/container} [react/view styles/container-root-view [icons/icon :main-icons/info {:color colors/red}] [react/text {:style styles/title-text} @@ -23,12 +25,14 @@ "#status"] (i18n/label :t/browsing-site-blocked-description2)] [react/view styles/buttons-container - [quo/button {:on-press (fn [] - (let [handler (if can-go-back? - :browser.ui/previous-page-button-pressed - :navigate-back)] - (re-frame/dispatch [handler])))} + [quo/button + {:on-press (fn [] + (let [handler (if can-go-back? + :browser.ui/previous-page-button-pressed + :navigate-back)] + (re-frame/dispatch [handler])))} (i18n/label :t/browsing-site-blocked-go-back)]] - [quo/button {:theme :negative - :on-press #(re-frame/dispatch [:browser/ignore-unsafe])} + [quo/button + {:theme :negative + :on-press #(re-frame/dispatch [:browser/ignore-unsafe])} (i18n/label :t/continue-anyway)]]]) diff --git a/src/status_im/ui/screens/browser/styles.cljs b/src/status_im/ui/screens/browser/styles.cljs index 89b4d41d8c..a2ca5cabbb 100644 --- a/src/status_im/ui/screens/browser/styles.cljs +++ b/src/status_im/ui/screens/browser/styles.cljs @@ -3,7 +3,8 @@ (def browser {:flex 1}) -(defn navbar [] +(defn navbar + [] {:background-color colors/white :height 51 :flex-direction :row @@ -32,7 +33,8 @@ :line-height 22 :text-align :center}) -(defn toolbar-content [] +(defn toolbar-content + [] {:flex-direction :row :flex 1 :border-radius 8 @@ -69,14 +71,16 @@ :right 0 :left 0}) -(defn permissions-panel-background [alpha-value] +(defn permissions-panel-background + [alpha-value] {:flex 1 :background-color colors/black :opacity alpha-value}) (def panel-height 354) -(defn permissions-panel [bottom-anim-value] +(defn permissions-panel + [bottom-anim-value] {:height panel-height :position :absolute :transform [{:translateY bottom-anim-value}] diff --git a/src/status_im/ui/screens/browser/tabs/views.cljs b/src/status_im/ui/screens/browser/tabs/views.cljs index b4e2888fda..67df23883c 100644 --- a/src/status_im/ui/screens/browser/tabs/views.cljs +++ b/src/status_im/ui/screens/browser/tabs/views.cljs @@ -1,75 +1,82 @@ (ns status-im.ui.screens.browser.tabs.views (:require-macros [status-im.utils.views :as views]) - (:require [status-im.ui.components.react :as react] - [status-im.ui.components.topbar :as topbar] - [status-im.i18n.i18n :as i18n] + (:require [quo.core :as quo] [quo.design-system.colors :as colors] - [status-im.ui.components.plus-button :as components.plus-button] - [status-im.ui.components.list.views :as list] - [quo.core :as quo] - [status-im.ui.components.icons.icons :as icons] - [status-im.ui.screens.wallet.components.views :as components] [re-frame.core :as re-frame] - [status-im.utils.http :as http] - [reagent.core :as reagent])) + [reagent.core :as reagent] + [status-im.i18n.i18n :as i18n] + [status-im.ui.components.icons.icons :as icons] + [status-im.ui.components.list.views :as list] + [status-im.ui.components.plus-button :as components.plus-button] + [status-im.ui.components.react :as react] + [status-im.ui.components.topbar :as topbar] + [status-im.ui.screens.wallet.components.views :as components] + [status-im.utils.http :as http])) -(defn list-item [_] +(defn list-item + [_] (let [loaded (reagent/atom nil)] (fn [{:keys [browser-id name url empty-tab]}] [react/view {:flex-direction :row :flex 1} [react/view {:flex 1} [quo/list-item - {:on-press #(if empty-tab - (re-frame/dispatch [:browser.ui/open-empty-tab]) - (re-frame/dispatch [:browser.ui/browser-item-selected browser-id])) - :title name - :subtitle (when-not empty-tab (or url (i18n/label :t/dapp))) + {:on-press #(if empty-tab + (re-frame/dispatch [:browser.ui/open-empty-tab]) + (re-frame/dispatch [:browser.ui/browser-item-selected browser-id])) + :title name + :subtitle (when-not empty-tab (or url (i18n/label :t/dapp))) :accessibility-label (keyword (str "tab-item" name)) - :icon [react/view {:width 40 - :height 40 - :align-items :center - :justify-content :center} - (when (or (nil? @loaded) @loaded) - [react/image {:onLoad #(reset! loaded true) - :style {:width 32 :height 32 :position :absolute :top 4 :left 4} - :source {:uri (str "https://" (http/url-host url) "/favicon.ico")}}]) - (when-not @loaded - [react/view {:width 40 + :icon [react/view + {:width 40 + :height 40 + :align-items :center + :justify-content :center} + (when (or (nil? @loaded) @loaded) + [react/image + {:onLoad #(reset! loaded true) + :style {:width 32 :height 32 :position :absolute :top 4 :left 4} + :source {:uri (str "https://" (http/url-host url) "/favicon.ico")}}]) + (when-not @loaded + [react/view + {:width 40 :height 40 :border-radius 20 :background-color colors/gray-lighter :align-items :center :justify-content :center} - [icons/icon :main-icons/browser {:color colors/gray}]])]}]] + [icons/icon :main-icons/browser {:color colors/gray}]])]}]] (when-not empty-tab [react/touchable-highlight - {:style {:width 60 :justify-content :center :align-items :center} + {:style {:width 60 :justify-content :center :align-items :center} :accessibility-label :empty-tab - :on-press #(re-frame/dispatch [:browser.ui/remove-browser-pressed browser-id])} + :on-press #(re-frame/dispatch [:browser.ui/remove-browser-pressed browser-id])} [icons/icon :main-icons/close-circle {:color colors/gray}]])]))) -(views/defview tabs [] +(views/defview tabs + [] (views/letsubs [browsers [:browser/browsers-vals]] [react/view {:flex 1} [topbar/topbar - {:modal? true - :border-bottom false - :navigation :none + {:modal? true + :border-bottom false + :navigation :none :right-accessories - [{:label (i18n/label :t/close-all) + [{:label (i18n/label :t/close-all) :accessibility-label :close-all - :on-press #(do (re-frame/dispatch [:browser.ui/clear-all-browsers-pressed]) - (re-frame/dispatch [:browser.ui/open-empty-tab]))}] - :title (i18n/label :t/tabs)}] + :on-press #(do (re-frame/dispatch [:browser.ui/clear-all-browsers-pressed]) + (re-frame/dispatch [:browser.ui/open-empty-tab]))}] + :title (i18n/label :t/tabs)}] [components/separator-dark] - [list/flat-list {:data (conj browsers {:empty-tab true - :name (i18n/label :t/empty-tab) - :url ""}) - :footer [react/view - {:style {:height 64 - :align-self :stretch}}] - :key-fn :browser-id - :render-fn list-item}] + [list/flat-list + {:data (conj browsers + {:empty-tab true + :name (i18n/label :t/empty-tab) + :url ""}) + :footer [react/view + {:style {:height 64 + :align-self :stretch}}] + :key-fn :browser-id + :render-fn list-item}] [components.plus-button/plus-button-old {:on-press #(re-frame/dispatch [:browser.ui/open-empty-tab])}]])) \ No newline at end of file diff --git a/src/status_im/ui/screens/browser/views.cljs b/src/status_im/ui/screens/browser/views.cljs index 77bd7fa6ab..1d77d6ac47 100644 --- a/src/status_im/ui/screens/browser/views.cljs +++ b/src/status_im/ui/screens/browser/views.cljs @@ -1,92 +1,104 @@ (ns status-im.ui.screens.browser.views - (:require [re-frame.core :as re-frame] + (:require [quo.core :as quo] + [quo.design-system.colors :as colors] + [re-frame.core :as re-frame] [reagent.core :as reagent] [status-im.browser.core :as browser] [status-im.browser.webview-ref :as webview-ref] [status-im.i18n.i18n :as i18n] + [status-im.qr-scanner.core :as qr-scanner] [status-im.ui.components.chat-icon.screen :as chat-icon] - [quo.design-system.colors :as colors] [status-im.ui.components.connectivity.view :as connectivity] [status-im.ui.components.icons.icons :as icons] + [status-im.ui.components.permissions :as components.permissions] [status-im.ui.components.react :as react] [status-im.ui.components.tooltip.views :as tooltip] [status-im.ui.components.webview :as components.webview] [status-im.ui.screens.browser.accounts :as accounts] + [status-im.ui.screens.browser.options.views :as options] [status-im.ui.screens.browser.permissions.views :as permissions.views] [status-im.ui.screens.browser.site-blocked.views :as site-blocked.views] [status-im.ui.screens.browser.styles :as styles] - [utils.debounce :as debounce] + [status-im.ui.screens.wallet.components.views :as components] [status-im.utils.http :as http] [status-im.utils.js-resources :as js-res] - [status-im.ui.components.permissions :as components.permissions] - [quo.core :as quo] - [status-im.ui.screens.wallet.components.views :as components] - [status-im.ui.screens.browser.options.views :as options] - [status-im.qr-scanner.core :as qr-scanner]) + [utils.debounce :as debounce]) (:require-macros [status-im.utils.views :as views])) -(defn toolbar-content [url url-original secure? url-editing? unsafe?] +(defn toolbar-content + [url url-original secure? url-editing? unsafe?] (let [url-text (atom url)] [react/view (styles/toolbar-content) - [react/touchable-highlight {:on-press #(re-frame/dispatch [:browser.ui/lock-pressed secure?]) - :accessibility-label :security-icon} + [react/touchable-highlight + {:on-press #(re-frame/dispatch [:browser.ui/lock-pressed secure?]) + :accessibility-label :security-icon} (if secure? [icons/tiny-icon :tiny-icons/tiny-lock {:color colors/green}] [icons/tiny-icon :tiny-icons/tiny-lock-broken {:color colors/black}])] (if url-editing? - [react/text-input {:on-change-text #(reset! url-text %) - :on-blur #(re-frame/dispatch [:browser.ui/url-input-blured]) - :on-submit-editing #(re-frame/dispatch [:browser.ui/url-submitted @url-text]) - :placeholder (i18n/label :t/enter-url) - :auto-capitalize :none - :auto-correct false - :auto-focus true - :default-value url - :ellipsize :end - :style styles/url-input - :accessibility-label :browser-input}] - [react/touchable-highlight {:style styles/url-text-container - :on-press #(re-frame/dispatch [:browser.ui/url-input-pressed])} + [react/text-input + {:on-change-text #(reset! url-text %) + :on-blur #(re-frame/dispatch [:browser.ui/url-input-blured]) + :on-submit-editing #(re-frame/dispatch [:browser.ui/url-submitted @url-text]) + :placeholder (i18n/label :t/enter-url) + :auto-capitalize :none + :auto-correct false + :auto-focus true + :default-value url + :ellipsize :end + :style styles/url-input + :accessibility-label :browser-input}] + [react/touchable-highlight + {:style styles/url-text-container + :on-press #(re-frame/dispatch [:browser.ui/url-input-pressed])} [react/text {:number-of-lines 1} (http/url-host url-original)]]) (when-not unsafe? - [react/touchable-highlight {:on-press #(.reload ^js @webview-ref/webview-ref) - :accessibility-label :refresh-page-button} + [react/touchable-highlight + {:on-press #(.reload ^js @webview-ref/webview-ref) + :accessibility-label :refresh-page-button} [icons/icon :main-icons/refresh]])])) -(defn- web-view-error [_ _ desc] +(defn- web-view-error + [_ _ desc] (reagent/as-element [react/view styles/web-view-error - [react/image {:style {:width 140 :height 140 :margin-bottom 16} - :source {:uri "https://bafybeiabxprplp52amc2hpocar753rd64wk2len55zq54yca77bekzre4e.ipfs.cf-ipfs.com"}}] + [react/image + {:style {:width 140 :height 140 :margin-bottom 16} + :source {:uri + "https://bafybeiabxprplp52amc2hpocar753rd64wk2len55zq54yca77bekzre4e.ipfs.cf-ipfs.com"}}] [react/i18n-text {:style styles/web-view-error-text :key :web-view-error}] [react/text {:style styles/web-view-error-text} (str desc)]])) -(views/defview navigation [{:keys [url can-go-back? can-go-forward? dapps-account empty-tab browser-id name]}] +(views/defview navigation + [{:keys [url can-go-back? can-go-forward? dapps-account empty-tab browser-id name]}] (views/letsubs [accounts [:visible-accounts-without-watch-only]] [react/view (styles/navbar) - [react/touchable-highlight {:on-press #(if can-go-back? - (re-frame/dispatch [:browser.ui/previous-page-button-pressed]) - (do - (re-frame/dispatch [:browser.ui/remove-browser-pressed browser-id]) - (re-frame/dispatch [:browser.ui/open-empty-tab]))) - :disabled empty-tab - :style (when empty-tab styles/disabled-button) - :accessibility-label :previous-page-button} + [react/touchable-highlight + {:on-press #(if can-go-back? + (re-frame/dispatch [:browser.ui/previous-page-button-pressed]) + (do + (re-frame/dispatch [:browser.ui/remove-browser-pressed browser-id]) + (re-frame/dispatch [:browser.ui/open-empty-tab]))) + :disabled empty-tab + :style (when empty-tab styles/disabled-button) + :accessibility-label :previous-page-button} [icons/icon :main-icons/arrow-left {:color colors/black}]] - [react/touchable-highlight {:on-press #(re-frame/dispatch [:browser.ui/next-page-button-pressed]) - :disabled (not can-go-forward?) - :style (when-not can-go-forward? styles/disabled-button) - :accessibility-label :next-page-button} + [react/touchable-highlight + {:on-press #(re-frame/dispatch [:browser.ui/next-page-button-pressed]) + :disabled (not can-go-forward?) + :style (when-not can-go-forward? styles/disabled-button) + :accessibility-label :next-page-button} [icons/icon :main-icons/arrow-right {:color colors/black}]] [react/touchable-highlight {:accessibility-label :select-account :on-press #(re-frame/dispatch [:bottom-sheet/show-sheet - {:content (accounts/accounts-list accounts dapps-account)}])} + {:content (accounts/accounts-list accounts + dapps-account)}])} [chat-icon/custom-icon-view-list (:name dapps-account) (:color dapps-account) 32]] [react/touchable-highlight - {:on-press #(re-frame/dispatch [:set-stack-root :browser-stack :browser-tabs]) + {:on-press #(re-frame/dispatch [:set-stack-root :browser-stack :browser-tabs]) :accessibility-label :browser-open-tabs} [icons/icon :main-icons/tabs {:color colors/black}]] @@ -98,20 +110,22 @@ {:handler ::qr-scanner/on-scan-success}])} [icons/icon :main-icons/qr {:color colors/black}]] [react/touchable-highlight - {:on-press #(re-frame/dispatch - [:bottom-sheet/show-sheet - {:content (options/browser-options - url - dapps-account - empty-tab - name)}]) + {:on-press #(re-frame/dispatch + [:bottom-sheet/show-sheet + {:content (options/browser-options + url + dapps-account + empty-tab + name)}]) :accessibility-label :browser-options} [icons/icon :main-icons/more {:color colors/black}]])])) -(def resources-to-permissions-map {"android.webkit.resource.VIDEO_CAPTURE" :camera - "android.webkit.resource.AUDIO_CAPTURE" :record-audio}) +(def resources-to-permissions-map + {"android.webkit.resource.VIDEO_CAPTURE" :camera + "android.webkit.resource.AUDIO_CAPTURE" :record-audio}) -(views/defview request-resources-panel [resources url webview-ref] +(views/defview request-resources-panel + [resources url webview-ref] [react/view styles/blocked-access-container [react/view styles/blocked-access-icon-container [icons/icon :main-icons/camera styles/blocked-access-camera-icon]] @@ -127,7 +141,7 @@ (components.permissions/request-permissions {:permissions (map resources-to-permissions-map resources) :on-allowed #(.answerPermissionRequest ^js webview-ref true resources) - :on-denied #(.answerPermissionRequest ^js webview-ref false)}) + :on-denied #(.answerPermissionRequest ^js webview-ref false)}) (re-frame/dispatch [:bottom-sheet/hide]))} (i18n/label :t/allow)]] [react/view styles/blocked-access-button-wrapper @@ -139,7 +153,8 @@ (re-frame/dispatch [:bottom-sheet/hide]))} (i18n/label :t/deny)]]]]) -(views/defview block-resources-panel [url] +(views/defview block-resources-panel + [url] [react/view styles/blocked-access-container [react/view styles/blocked-access-icon-container [icons/icon :main-icons/camera styles/blocked-access-camera-icon]] @@ -147,16 +162,18 @@ [react/text {:style styles/blocked-access-text} (str url " " (i18n/label :t/page-camera-request-blocked))]]]) -(defn request-resources-access-for-page [resources url webview-ref] +(defn request-resources-access-for-page + [resources url webview-ref] (re-frame/dispatch [:bottom-sheet/show-sheet - {:content (fn [] [request-resources-panel resources url webview-ref]) + {:content (fn [] [request-resources-panel resources url webview-ref]) :show-handle? false :backdrop-dismiss? false :disable-drag? true :back-button-cancel false}])) -(defn block-resources-access-and-notify-user [url] +(defn block-resources-access-and-notify-user + [url] (.answerPermissionRequest ^js @webview-ref/webview-ref false) (re-frame/dispatch [:bottom-sheet/show-sheet {:content (fn [] [block-resources-panel url])}])) @@ -170,12 +187,14 @@ {:should-component-update (fn [_ _ args] (let [[_ props] args] (not (nil? (:url props)))))} - [react/view {:flex 1 - :elevation -10} + [react/view + {:flex 1 + :elevation -10} [react/view {:flex 1} (if (and unsafe? (not= (http/url-host url) ignore-unsafe)) - [site-blocked.views/view {:can-go-back? can-go-back? - :site browser-id}] + [site-blocked.views/view + {:can-go-back? can-go-back? + :site browser-id}] [components.webview/webview {:dapp? dapp? :dapp-name name @@ -187,26 +206,33 @@ :set-support-multiple-windows false :render-error web-view-error :on-navigation-state-change #(do - (re-frame/dispatch [:set-in [:ens/registration :state] :searching]) + (re-frame/dispatch [:set-in + [:ens/registration :state] + :searching]) (debounce/debounce-and-dispatch [:browser/navigation-state-changed % error?] 500)) :on-permission-request #(if resources-permission? - (request-resources-access-for-page (-> ^js % .-nativeEvent .-resources) url @webview-ref/webview-ref) + (request-resources-access-for-page + (-> ^js % .-nativeEvent .-resources) + url + @webview-ref/webview-ref) (block-resources-access-and-notify-user url)) ;; Extract event data here due to ;; https://reactjs.org/docs/events.html#event-pooling - :on-message #(re-frame/dispatch [:browser/bridge-message-received (.. ^js % -nativeEvent -data)]) + :on-message #(re-frame/dispatch [:browser/bridge-message-received + (.. ^js % -nativeEvent -data)]) :on-load #(re-frame/dispatch [:browser/loading-started]) :on-error #(re-frame/dispatch [:browser/error-occured]) :injected-java-script-before-content-loaded (js-res/ethereum-provider (str network-id))}])] - [navigation {:url url-original - :name name - :can-go-back? can-go-back? - :can-go-forward? can-go-forward? - :dapps-account dapps-account - :browser-id browser-id}] + [navigation + {:url url-original + :name name + :can-go-back? can-go-back? + :can-go-forward? can-go-forward? + :dapps-account dapps-account + :browser-id browser-id}] [permissions.views/permissions-panel [dapp? dapp dapps-account] show-permission] (when show-tooltip [tooltip/bottom-tooltip-info @@ -215,10 +241,13 @@ (i18n/label :t/browser-not-secure)) #(re-frame/dispatch [:browser.ui/close-tooltip-pressed])])]) -(views/defview browser [] +(views/defview browser + [] (views/letsubs [window-width [:dimensions/window-width] - {:keys [browser-id dapp? dapp name unsafe? ignore-unsafe secure?] :as browser} [:get-current-browser] - {:keys [url error? loading? url-editing? show-tooltip show-permission resolving?]} [:browser/options] + {:keys [browser-id dapp? dapp name unsafe? ignore-unsafe secure?] :as browser} + [:get-current-browser] + {:keys [url error? loading? url-editing? show-tooltip show-permission resolving?]} + [:browser/options] dapps-account [:dapps-account] network-id [:chain-id] {:keys [webview-allow-permission-requests?]} [:multiaccount]] @@ -231,20 +260,21 @@ [react/view (when loading? [connectivity/loading-indicator-anim window-width])] - [browser-component {:dapp? dapp? - :dapp dapp - :error? error? - :url url - :url-original url-original - :browser-id browser-id - :unsafe? unsafe? - :ignore-unsafe ignore-unsafe - :can-go-back? can-go-back? - :can-go-forward? can-go-forward? - :resolving? resolving? - :network-id network-id - :show-permission show-permission - :show-tooltip show-tooltip - :name name - :dapps-account dapps-account - :resources-permission? webview-allow-permission-requests?}]]))) + [browser-component + {:dapp? dapp? + :dapp dapp + :error? error? + :url url + :url-original url-original + :browser-id browser-id + :unsafe? unsafe? + :ignore-unsafe ignore-unsafe + :can-go-back? can-go-back? + :can-go-forward? can-go-forward? + :resolving? resolving? + :network-id network-id + :show-permission show-permission + :show-tooltip show-tooltip + :name name + :dapps-account dapps-account + :resources-permission? webview-allow-permission-requests?}]]))) diff --git a/src/status_im/ui/screens/bug_report.cljs b/src/status_im/ui/screens/bug_report.cljs index f6bd2e0219..665b806a72 100644 --- a/src/status_im/ui/screens/bug_report.cljs +++ b/src/status_im/ui/screens/bug_report.cljs @@ -5,18 +5,20 @@ [status-im.i18n.i18n :as i18n] [status-im.ui.components.topbar :as topbar])) -(defn bug-report [] +(defn bug-report + [] (let [{:keys [description steps]} @(re-frame/subscribe [:bug-report/details])] [react-native/view {:style {:flex 1}} [topbar/topbar {:title (i18n/label :t/bug-report) :modal? true}] - [react-native/view {:style {:flex 1 - :padding-top 8 - :padding-horizontal 16}} + [react-native/view + {:style {:flex 1 + :padding-top 8 + :padding-horizontal 16}} [quo/text-input {:label (i18n/label :t/bug-report-description) - :default-value description + :default-value description :placeholder (i18n/label :t/bug-report-description-placeholder) :style {:margin-bottom 8} :multiline true @@ -39,7 +41,7 @@ (i18n/label :t/bug-report-submit-email)] [react-native/view {:style {:margin-vertical 16 - :align-items :center}} + :align-items :center}} [quo/text (i18n/label :t/or)]] [quo/button {:type :primary diff --git a/src/status_im/ui/screens/chat/audio_message/styles.cljs b/src/status_im/ui/screens/chat/audio_message/styles.cljs index 4acb1f70d9..16d064558e 100644 --- a/src/status_im/ui/screens/chat/audio_message/styles.cljs +++ b/src/status_im/ui/screens/chat/audio_message/styles.cljs @@ -7,17 +7,17 @@ :justify-content :space-around}) (def timer - {:font-size 28 + {:font-size 28 :line-height 38 - :align-self :center}) + :align-self :center}) (def buttons-container - {:flex 1 - :max-height 80 - :flex-direction :row - :align-items :center - :justify-content :space-around - :align-self :stretch + {:flex 1 + :max-height 80 + :flex-direction :row + :align-items :center + :justify-content :space-around + :align-self :stretch :padding-horizontal 80}) (def rec-button-base-size 61) @@ -27,7 +27,8 @@ :height rec-button-base-size :align-items "center"}) -(defn rec-outer-circle [scale-anim] +(defn rec-outer-circle + [scale-anim] {:position "absolute" :width rec-button-base-size :height rec-button-base-size @@ -37,7 +38,8 @@ :border-color colors/red-audio-recorder :border-radius rec-button-base-size}) -(defn rec-inner-circle [scale-anim border-radius-anim] +(defn rec-inner-circle + [scale-anim border-radius-anim] {:position "absolute" :top 6 :left 6 diff --git a/src/status_im/ui/screens/chat/audio_message/views.cljs b/src/status_im/ui/screens/chat/audio_message/views.cljs index 64e28007da..420b3084af 100644 --- a/src/status_im/ui/screens/chat/audio_message/views.cljs +++ b/src/status_im/ui/screens/chat/audio_message/views.cljs @@ -1,23 +1,22 @@ (ns status-im.ui.screens.chat.audio-message.views (:require-macros [status-im.utils.views :refer [defview letsubs]]) - (:require - [goog.string :as gstring] - [reagent.core :as reagent] - [status-im.audio.core :as audio] - [status-im.ui.components.react :as react] - [re-frame.core :as re-frame] - [status-im.i18n.i18n :as i18n] - [quo.components.animated.pressable :as pressable] - [status-im.native-module.core :as status] - [status-im.ui.screens.chat.components.input :as input] - [status-im.ui.screens.chat.components.style :as input.style] - [status-im.ui.screens.chat.audio-message.styles :as styles] - [quo.design-system.colors :as colors] - [status-im.ui.components.animation :as anim] - [status-im.ui.components.icons.icons :as icons] - [status-im.utils.utils :as utils.utils] - [status-im.utils.fs :as fs] - [status-im.utils.fx :as fx])) + (:require [goog.string :as gstring] + [quo.components.animated.pressable :as pressable] + [quo.design-system.colors :as colors] + [re-frame.core :as re-frame] + [reagent.core :as reagent] + [status-im.audio.core :as audio] + [status-im.i18n.i18n :as i18n] + [status-im.native-module.core :as status] + [status-im.ui.components.animation :as anim] + [status-im.ui.components.icons.icons :as icons] + [status-im.ui.components.react :as react] + [status-im.ui.screens.chat.audio-message.styles :as styles] + [status-im.ui.screens.chat.components.input :as input] + [status-im.ui.screens.chat.components.style :as input.style] + [status-im.utils.fs :as fs] + [status-im.utils.fx :as fx] + [status-im.utils.utils :as utils.utils])) ;; reference db levels (def total-silence-db -160) @@ -31,20 +30,23 @@ ;;ensure animation finishes before next meter update (defonce metering-anim-duration (int (* metering-interval 0.9))) -(defn update-meter [meter-data] +(defn update-meter + [meter-data] (let [value (if meter-data (.-value ^js meter-data) total-silence-db)] - (anim/start (anim/timing visual-target-value {:toValue value - :duration metering-anim-duration - :useNativeDriver true})))) + (anim/start (anim/timing visual-target-value + {:toValue value + :duration metering-anim-duration + :useNativeDriver true})))) (def base-filename "am.") (def default-format "aac") -(def rec-options (merge - audio/default-recorder-options - {:filename (str base-filename default-format) - :meteringInterval metering-interval})) +(def rec-options + (merge + audio/default-recorder-options + {:filename (str base-filename default-format) + :meteringInterval metering-interval})) ;; maximum 1 minute of recordings time to keep data at certain size (def max-recording-ms (* 1 60 1000)) @@ -53,11 +55,13 @@ (defonce recorder-ref (atom nil)) (defonce player-ref (atom nil)) -(defn destroy-recorder [] +(defn destroy-recorder + [] (audio/destroy-recorder @recorder-ref) (reset! recorder-ref nil)) -(defn destroy-player [] +(defn destroy-player + [] (audio/destroy-player @player-ref) (reset! player-ref nil)) @@ -83,32 +87,38 @@ (defonce recording-backlog-ms (atom 0)) ;; updates timer UI -(defn update-timer [timer] +(defn update-timer + [timer] (let [ms (if @recording-start-ts (+ (- (js/Date.now) @recording-start-ts) @recording-backlog-ms) @recording-backlog-ms) - s (quot ms 1000)] + s (quot ms 1000)] (if (> ms max-recording-ms) (@max-recording-reached-cb) (reset! timer (gstring/format "%d:%02d" (quot s 60) (mod s 60)))))) -(defn reset-timer [timer] +(defn reset-timer + [timer] (reset! timer "0:00") (reset! recording-backlog-ms 0)) -(defn animate-buttons [rec? show-ctrl? {:keys [rec-button-anim-value ctrl-buttons-anim-value]}] +(defn animate-buttons + [rec? show-ctrl? {:keys [rec-button-anim-value ctrl-buttons-anim-value]}] (anim/start (anim/parallel - [(anim/timing rec-button-anim-value {:toValue (if rec? 1 0) - :duration 100 - :useNativeDriver true}) - (anim/timing ctrl-buttons-anim-value {:toValue (if show-ctrl? 1 0) - :duration 100 - :useNativeDriver true})]))) + [(anim/timing rec-button-anim-value + {:toValue (if rec? 1 0) + :duration 100 + :useNativeDriver true}) + (anim/timing ctrl-buttons-anim-value + {:toValue (if show-ctrl? 1 0) + :duration 100 + :useNativeDriver true})]))) -(defn start-recording [{:keys [timer] :as params}] +(defn start-recording + [{:keys [timer] :as params}] (if (> @recording-backlog-ms max-recording-ms) (@max-recording-reached-cb) (do @@ -120,7 +130,8 @@ @state-cb #(utils.utils/show-popup (i18n/label :t/audio-recorder-error) (:message %)))))) -(defn reload-recorder [] +(defn reload-recorder + [] (when @recorder-ref (destroy-recorder)) (reset! recorder-ref (audio/new-recorder rec-options #(update-meter %) @state-cb)) @@ -134,7 +145,7 @@ (destroy-player)) (reset! player-ref (audio/new-player (:filename rec-options) - {:autoDestroy false + {:autoDestroy false :continuesToPlayInBackground false} @state-cb)) (audio/prepare-player @@ -142,7 +153,8 @@ #(do (@state-cb) (when on-success (on-success))) #(utils.utils/show-popup (i18n/label :t/audio-recorder-error) (:message %))))) -(defn stop-recording [{:keys [on-success timer max-recording-reached?] :as params}] +(defn stop-recording + [{:keys [on-success timer max-recording-reached?] :as params}] (when @recording-timer (utils.utils/clear-interval @recording-timer) (reset! recording-timer nil)) @@ -158,7 +170,8 @@ #(utils.utils/show-popup (i18n/label :t/audio-recorder-error) (:message %))) (animate-buttons false max-recording-reached? params)) -(defn pause-recording [{:keys [timer] :as params}] +(defn pause-recording + [{:keys [timer] :as params}] (when @recording-timer (utils.utils/clear-interval @recording-timer) (reset! recording-backlog-ms (+ @recording-backlog-ms (- (js/Date.now) @recording-start-ts))) @@ -181,28 +194,29 @@ - :recording-paused - :ready-to-record" [state-ref] - (let [player-state (audio/get-state @player-ref) - recorder-state (audio/get-state @recorder-ref) - output-file (or - (audio/get-recorder-file-path @recorder-ref) - (:output-file @state-ref)) - general (cond - (= recorder-state audio/RECORDING) :recording - (= player-state audio/PLAYING) :playing - (= player-state audio/PREPARED) :ready-to-send - (= recorder-state audio/PAUSED) :recording-paused - :else :ready-to-record) - new-state {:general general - :cancel-disabled? (nil? (#{:recording :recording-paused :ready-to-send} general)) - :output-file output-file - :duration (audio/get-player-duration @player-ref)}] + (let [player-state (audio/get-state @player-ref) + recorder-state (audio/get-state @recorder-ref) + output-file (or + (audio/get-recorder-file-path @recorder-ref) + (:output-file @state-ref)) + general (cond + (= recorder-state audio/RECORDING) :recording + (= player-state audio/PLAYING) :playing + (= player-state audio/PREPARED) :ready-to-send + (= recorder-state audio/PAUSED) :recording-paused + :else :ready-to-record) + new-state {:general general + :cancel-disabled? (nil? (#{:recording :recording-paused :ready-to-send} general)) + :output-file output-file + :duration (audio/get-player-duration @player-ref)}] (if (#{:recording :recording-paused} general) (status/activate-keep-awake) (status/deactivate-keep-awake)) (when (not= @state-ref new-state) (reset! state-ref new-state)))) -(defn send-audio-msessage [state-ref] +(defn send-audio-msessage + [state-ref] (re-frame/dispatch [:chat/send-audio (:output-file @state-ref) (int (:duration @state-ref))]) @@ -210,52 +224,65 @@ (@state-cb)) ;; rec-button-anim-value 0 => stopped, 1 => recording -(defview rec-button-view [{:keys [rec-button-anim-value state] :as params}] - (letsubs [outer-scale (anim/interpolate visual-target-value {:inputRange [total-silence-db silence-db 0] - :outputRange [1 0.8 1.2]}) - inner-scale (anim/interpolate rec-button-anim-value {:inputRange [0 1] - :outputRange [1 0.5]}) - inner-border-radius (anim/interpolate rec-button-anim-value {:inputRange [0 1] - :outputRange [styles/rec-button-base-size 16]})] - [react/touchable-highlight {:on-press #(if (= (:general @state) :recording) - (pause-recording params) - (start-recording params)) - :accessibility-label :start-stop-audio-recording-button} +(defview rec-button-view + [{:keys [rec-button-anim-value state] :as params}] + (letsubs [outer-scale (anim/interpolate visual-target-value + {:inputRange [total-silence-db silence-db 0] + :outputRange [1 0.8 1.2]}) + inner-scale (anim/interpolate rec-button-anim-value + {:inputRange [0 1] + :outputRange [1 0.5]}) + inner-border-radius (anim/interpolate rec-button-anim-value + {:inputRange [0 1] + :outputRange [styles/rec-button-base-size 16]})] + [react/touchable-highlight + {:on-press #(if (= (:general @state) :recording) + (pause-recording params) + (start-recording params)) + :accessibility-label :start-stop-audio-recording-button} [react/view {:style styles/rec-button-container} [react/animated-view {:style (styles/rec-outer-circle outer-scale)}] [react/animated-view {:style (styles/rec-inner-circle inner-scale inner-border-radius)}]]])) -(defn- cancel-button [disabled? on-press contact-request] - [pressable/pressable {:type :scale - :disabled disabled? - :on-press on-press} +(defn- cancel-button + [disabled? on-press contact-request] + [pressable/pressable + {:type :scale + :disabled disabled? + :on-press on-press} [react/view {:style (input.style/send-message-button)} [icons/icon :main-icons/close - {:container-style (merge (input.style/send-message-container contact-request) {:background-color colors/gray}) + {:container-style (merge (input.style/send-message-container contact-request) + {:background-color colors/gray}) :accessibility-label :cancel-message-button :color colors/white-persist}]]]) -(defview audio-message-view [] - (letsubs [rec-button-anim-value (anim/create-value 0) +(defview audio-message-view + [] + (letsubs [rec-button-anim-value (anim/create-value 0) ctrl-buttons-anim-value (anim/create-value 0) - timer (reagent/atom "") - state (reagent/atom nil)] - {:component-did-mount (fn [] - (reset-timer timer) - (reset! state-cb #(update-state state)) - (reset! max-recording-reached-cb #(do - (when (= (:general @state) :recording) - (stop-recording {:rec-button-anim-value rec-button-anim-value - :ctrl-buttons-anim-value ctrl-buttons-anim-value - :timer timer - :max-recording-reached? true})) - (utils.utils/show-popup (i18n/label :t/audio-recorder) - (i18n/label :t/audio-recorder-max-ms-reached)))) - (reset! on-background-cb #(when (= (:general @state) :recording) - (pause-recording {:rec-button-anim-value rec-button-anim-value - :ctrl-buttons-anim-value ctrl-buttons-anim-value - :timer timer}))) - (reload-recorder)) + timer (reagent/atom "") + state (reagent/atom nil)] + {:component-did-mount (fn [] + (reset-timer timer) + (reset! state-cb #(update-state state)) + (reset! max-recording-reached-cb + #(do + (when (= (:general @state) :recording) + (stop-recording {:rec-button-anim-value rec-button-anim-value + :ctrl-buttons-anim-value ctrl-buttons-anim-value + :timer timer + :max-recording-reached? true})) + (utils.utils/show-popup (i18n/label :t/audio-recorder) + (i18n/label + :t/audio-recorder-max-ms-reached)))) + (reset! on-background-cb #(when (= (:general @state) :recording) + (pause-recording + {:rec-button-anim-value rec-button-anim-value + :ctrl-buttons-anim-value + ctrl-buttons-anim-value + :timer timer}))) + (reload-recorder)) :component-will-unmount (fn [] (when @recording-timer (utils.utils/clear-interval @recording-timer) @@ -268,26 +295,29 @@ (reset! state-cb nil) (reset! max-recording-reached-cb nil) (reset! on-background-cb nil))} - (let [base-params {:rec-button-anim-value rec-button-anim-value - :ctrl-buttons-anim-value ctrl-buttons-anim-value - :timer timer} + (let [base-params {:rec-button-anim-value rec-button-anim-value + :ctrl-buttons-anim-value ctrl-buttons-anim-value + :timer timer} contact-request @(re-frame/subscribe [:chats/sending-contact-request])] [react/view {:style styles/container} - [react/text {:style styles/timer - :accessibility-label :audio-message-recorded-time} @timer] + [react/text + {:style styles/timer + :accessibility-label :audio-message-recorded-time} @timer] [react/view {:style styles/buttons-container} [react/animated-view {:style {:opacity ctrl-buttons-anim-value}} [cancel-button (:cancel-disabled? @state) #(stop-recording base-params) contact-request]] [rec-button-view (merge base-params {:state state})] [react/animated-view {:style {:opacity ctrl-buttons-anim-value}} - [input/send-button (fn [] (cond - (= :ready-to-send (:general @state)) - (do - (reset-timer timer) - (animate-buttons false false base-params) - (send-audio-msessage state)) + [input/send-button + (fn [] + (cond + (= :ready-to-send (:general @state)) + (do + (reset-timer timer) + (animate-buttons false false base-params) + (send-audio-msessage state)) - (#{:recording :recording-paused} (:general @state)) - (stop-recording (merge base-params - {:on-success - #(send-audio-msessage state)}))))]]]]))) + (#{:recording :recording-paused} (:general @state)) + (stop-recording (merge base-params + {:on-success + #(send-audio-msessage state)}))))]]]]))) diff --git a/src/status_im/ui/screens/chat/components/accessory.cljs b/src/status_im/ui/screens/chat/components/accessory.cljs index 7817720683..723d5f0bdc 100644 --- a/src/status_im/ui/screens/chat/components/accessory.cljs +++ b/src/status_im/ui/screens/chat/components/accessory.cljs @@ -1,36 +1,39 @@ (ns status-im.ui.screens.chat.components.accessory - (:require [quo.animated :as animated] - [reagent.core :as reagent] - [cljs-bean.core :as bean] + (:require [cljs-bean.core :as bean] + [quo.animated :as animated] + [quo.components.safe-area :refer [use-safe-area]] [quo.design-system.colors :as colors] - [status-im.ui.screens.chat.components.hooks :refer [use-keyboard-dimension]] - [quo.react :as react] [quo.platform :as platform] + [quo.react :as react] [quo.react-native :as rn] + [reagent.core :as reagent] [status-im.ui.components.tabbar.core :as tabbar] - [quo.components.safe-area :refer [use-safe-area]])) + [status-im.ui.screens.chat.components.hooks :refer [use-keyboard-dimension]])) (def duration 250) -(defn create-pan-responder [y pan-active] +(defn create-pan-responder + [y pan-active] (when-not platform/android? (js->clj (.-panHandlers - ^js (.create - ^js rn/pan-responder - #js {:onPanResponderGrant (fn [] - (animated/set-value pan-active 1)) - :onPanResponderMove (fn [_ ^js state] - (animated/set-value y (.-moveY state))) - :onPanResponderRelease (fn [] - (animated/set-value pan-active 0) - (js/setTimeout - #(animated/set-value y 0) - 100)) - :onPanResponderTerminate (fn [] - (animated/set-value pan-active 0) - (js/setTimeout - #(animated/set-value y 0) - 100))}))))) + ^js + (.create + ^js rn/pan-responder + #js + {:onPanResponderGrant (fn [] + (animated/set-value pan-active 1)) + :onPanResponderMove (fn [_ ^js state] + (animated/set-value y (.-moveY state))) + :onPanResponderRelease (fn [] + (animated/set-value pan-active 0) + (js/setTimeout + #(animated/set-value y 0) + 100)) + :onPanResponderTerminate (fn [] + (animated/set-value pan-active 0) + (js/setTimeout + #(animated/set-value y 0) + 100))}))))) (def ios-view (reagent/adapt-react-class @@ -41,39 +44,42 @@ pan-state :panState on-close :onClose has-panel :hasPanel - children :children} (bean/bean props) + children :children} + (bean/bean props) {keyboard-height :height keyboard-max-height :max-height - keyboard-end-position :end-position} (use-keyboard-dimension) - {:keys [bottom]} (use-safe-area) + keyboard-end-position :end-position} + (use-keyboard-dimension) + {:keys [bottom]} (use-safe-area) {on-layout :on-layout - bar-height :height} (rn/use-layout) + bar-height :height} + (rn/use-layout) - visible (or has-panel (pos? keyboard-height)) - anim-visible (animated/use-value visible) - kb-on-screen (if platform/android? 0 (* -1 (- keyboard-height bottom (tabbar/get-height)))) + visible (or has-panel (pos? keyboard-height)) + anim-visible (animated/use-value visible) + kb-on-screen (if platform/android? 0 (* -1 (- keyboard-height bottom (tabbar/get-height)))) panel-on-screen (* -1 (- keyboard-max-height bottom (tabbar/get-height))) - max-delta (min 0 (if has-panel panel-on-screen kb-on-screen)) - panel-height (* -1 max-delta) - end-position (- keyboard-end-position (when has-panel keyboard-max-height)) - delay (+ (/ (- keyboard-max-height panel-height) - (/ keyboard-max-height duration)) - 16) - drag-diff (animated/clamp (animated/sub y end-position) 0 keyboard-max-height) - animated-y (react/use-memo - (fn [] - (animated/mix - (animated/with-timing-transition anim-visible - {:duration (- duration delay) - :easing (:keyboard animated/easings)}) - 0 - panel-on-screen)) - [panel-on-screen]) - delta-y (animated/clamp (animated/add drag-diff animated-y) max-delta 0) - on-update (fn [] - (when on-update-inset - (on-update-inset (+ bar-height panel-height)))) - children (react/get-children children)] + max-delta (min 0 (if has-panel panel-on-screen kb-on-screen)) + panel-height (* -1 max-delta) + end-position (- keyboard-end-position (when has-panel keyboard-max-height)) + delay (+ (/ (- keyboard-max-height panel-height) + (/ keyboard-max-height duration)) + 16) + drag-diff (animated/clamp (animated/sub y end-position) 0 keyboard-max-height) + animated-y (react/use-memo + (fn [] + (animated/mix + (animated/with-timing-transition anim-visible + {:duration (- duration delay) + :easing (:keyboard animated/easings)}) + 0 + panel-on-screen)) + [panel-on-screen]) + delta-y (animated/clamp (animated/add drag-diff animated-y) max-delta 0) + on-update (fn [] + (when on-update-inset + (on-update-inset (+ bar-height panel-height)))) + children (react/get-children children)] (react/effect! on-update [panel-height bar-height]) (animated/code! (fn [] @@ -90,16 +96,18 @@ (if visible delay 0))) [visible keyboard-max-height delay]) (reagent/as-element - [animated/view {:style {:position :absolute - :left 0 - :right 0 - :background-color (:ui-background @colors/theme) - :bottom max-delta - :transform [{:translateY delta-y}]}} + [animated/view + {:style {:position :absolute + :left 0 + :right 0 + :background-color (:ui-background @colors/theme) + :bottom max-delta + :transform [{:translateY delta-y}]}} [rn/view {:on-layout on-layout} (first children)] - [rn/view {:style {:flex 1 - :height (when (pos? panel-height) panel-height)}} + [rn/view + {:style {:flex 1 + :height (when (pos? panel-height) panel-height)}} (second children)]])))))) (def android-view @@ -109,20 +117,22 @@ (let [{on-update-inset :onUpdateInset on-close :onClose has-panel :hasPanel - children :children} (bean/bean props) + children :children} + (bean/bean props) {keyboard-max-height :max-height} (use-keyboard-dimension) - {:keys [bottom]} (use-safe-area) + {:keys [bottom]} (use-safe-area) {on-layout :on-layout - bar-height :height} (rn/use-layout) + bar-height :height} + (rn/use-layout) - visible has-panel + visible has-panel panel-on-screen (* -1 (- keyboard-max-height bottom (tabbar/get-height))) - max-delta (min 0 (if has-panel panel-on-screen 0)) - panel-height (* -1 max-delta) - on-update (fn [] - (when on-update-inset - (on-update-inset (+ bar-height panel-height)))) - children (react/get-children children)] + max-delta (min 0 (if has-panel panel-on-screen 0)) + panel-height (* -1 max-delta) + on-update (fn [] + (when on-update-inset + (on-update-inset (+ bar-height panel-height)))) + children (react/get-children children)] (react/effect! on-update [panel-height bar-height]) (rn/use-back-handler (fn [] @@ -130,15 +140,17 @@ (on-close)) visible)) (reagent/as-element - [animated/view {:style {:position :absolute - :left 0 - :right 0 - :bottom 0 - :background-color (:ui-background @colors/theme)}} + [animated/view + {:style {:position :absolute + :left 0 + :right 0 + :bottom 0 + :background-color (:ui-background @colors/theme)}} [rn/view {:on-layout on-layout} (first children)] - [rn/view {:style {:flex 1 - :height (when (pos? panel-height) panel-height)}} + [rn/view + {:style {:flex 1 + :height (when (pos? panel-height) panel-height)}} (second children)]])))))) (def view (if platform/android? android-view ios-view)) diff --git a/src/status_im/ui/screens/chat/components/contact_request.cljs b/src/status_im/ui/screens/chat/components/contact_request.cljs index f6f0813863..d90d84e497 100644 --- a/src/status_im/ui/screens/chat/components/contact_request.cljs +++ b/src/status_im/ui/screens/chat/components/contact_request.cljs @@ -1,21 +1,24 @@ (ns status-im.ui.screens.chat.components.contact-request - (:require [quo.core :as quo] + (:require [clojure.string :as string] + [quo.core :as quo] + [quo.design-system.colors :as quo.colors] [quo.react :as quo.react] [quo.react-native :as rn] - [quo.design-system.colors :as quo.colors] - [status-im.i18n.i18n :as i18n] - [status-im.ethereum.stateofus :as stateofus] - [status-im.ui.screens.chat.components.style :as styles] [re-frame.core :as re-frame] - [clojure.string :as string]) + [status-im.ethereum.stateofus :as stateofus] + [status-im.i18n.i18n :as i18n] + [status-im.ui.screens.chat.components.style :as styles]) (:require-macros [status-im.utils.views :refer [defview letsubs]])) (def ^:private contact-request-symbol "↪ ") -(defn input-focus [text-input-ref] - (some-> ^js (quo.react/current-ref text-input-ref) .focus)) +(defn input-focus + [text-input-ref] + (some-> ^js (quo.react/current-ref text-input-ref) + .focus)) -(defn format-author [contact-name] +(defn format-author + [contact-name] (let [author (if (or (= (aget contact-name 0) "@") ;; in case of replies (= (aget contact-name 1) "@")) @@ -24,12 +27,14 @@ contact-name)] (i18n/label :contact-requesting-to {:author author}))) -(defn format-contact-request-author [from username current-public-key] +(defn format-contact-request-author + [from username current-public-key] (or (and (= from current-public-key) (str contact-request-symbol (i18n/label :t/You))) (str contact-request-symbol (format-author username)))) -(defn get-quoted-text-with-mentions [parsed-text] +(defn get-quoted-text-with-mentions + [parsed-text] (string/join (mapv (fn [{:keys [type literal children]}] (cond @@ -46,40 +51,47 @@ literal)) parsed-text))) -(defn contact-request-message [their-public-key] +(defn contact-request-message + [their-public-key] (let [{:keys [input-text]} @(re-frame/subscribe [:chats/current-chat-inputs])] [rn/view {:style {:flex-direction :row}} [rn/view {:style (styles/contact-request-content)} - [quo/button {:type :secondary - :weight :medium - :number-of-lines 1 - :style {:line-height 18} - :on-press #(re-frame/dispatch [:chat.ui/cancel-contact-request])} + [quo/button + {:type :secondary + :weight :medium + :number-of-lines 1 + :style {:line-height 18} + :on-press #(re-frame/dispatch [:chat.ui/cancel-contact-request])} (i18n/label :t/cancel)] - [quo/button {:type :secondary - :disabled (string/blank? input-text) - :weight :medium - :after :main-icons/send - :on-press #(re-frame/dispatch [:contacts/send-contact-request their-public-key input-text]) - :style {:line-height 18}} + [quo/button + {:type :secondary + :disabled (string/blank? input-text) + :weight :medium + :after :main-icons/send + :on-press #(re-frame/dispatch [:contacts/send-contact-request their-public-key input-text]) + :style {:line-height 18}} (i18n/label :t/send-request)]]])) -(defn focus-input-on-contact-request [contact-request had-contact-request text-input-ref] +(defn focus-input-on-contact-request + [contact-request had-contact-request text-input-ref] ;;when we show contact-request we focus input (when-not (= contact-request @had-contact-request) (reset! had-contact-request contact-request) (when contact-request (js/setTimeout #(input-focus text-input-ref) 250)))) -(defn contact-request-message-wrapper [contact-request] - [rn/view {:style {:padding-horizontal 15 - :border-top-width 1 - :border-top-color (:ui-01 @quo.colors/theme) - :padding-vertical 8}} +(defn contact-request-message-wrapper + [contact-request] + [rn/view + {:style {:padding-horizontal 15 + :border-top-width 1 + :border-top-color (:ui-01 @quo.colors/theme) + :padding-vertical 8}} [contact-request-message contact-request]]) -(defview contact-request-message-auto-focus-wrapper [text-input-ref] - (letsubs [had-reply (atom nil) +(defview contact-request-message-auto-focus-wrapper + [text-input-ref] + (letsubs [had-reply (atom nil) contact-request @(re-frame/subscribe [:chats/sending-contact-request])] {:component-did-mount #(focus-input-on-contact-request contact-request had-reply text-input-ref)} (when contact-request diff --git a/src/status_im/ui/screens/chat/components/edit.cljs b/src/status_im/ui/screens/chat/components/edit.cljs index 3f1bb4fe95..13618acc4f 100644 --- a/src/status_im/ui/screens/chat/components/edit.cljs +++ b/src/status_im/ui/screens/chat/components/edit.cljs @@ -1,46 +1,56 @@ (ns status-im.ui.screens.chat.components.edit - (:require [quo.core :as quo] + (:require [quo.components.animated.pressable :as pressable] + [quo.core :as quo] + [quo.design-system.colors :as quo.colors] [quo.react :as quo.react] [quo.react-native :as rn] - [quo.design-system.colors :as quo.colors] + [re-frame.core :as re-frame] [status-im.i18n.i18n :as i18n] - [quo.components.animated.pressable :as pressable] [status-im.ui.components.icons.icons :as icons] - [status-im.ui.screens.chat.components.style :as styles] - [re-frame.core :as re-frame])) + [status-im.ui.screens.chat.components.style :as styles])) -(defn input-focus [text-input-ref] - (some-> ^js (quo.react/current-ref text-input-ref) .focus)) +(defn input-focus + [text-input-ref] + (some-> ^js (quo.react/current-ref text-input-ref) + .focus)) -(defn edit-message [] +(defn edit-message + [] [rn/view {:style {:flex-direction :row}} [rn/view {} [icons/icon :tiny-icons/tiny-edit {:container-style {:margin-top 5}}]] [rn/view {:style (styles/reply-content)} - [quo/text {:weight :medium - :number-of-lines 1} + [quo/text + {:weight :medium + :number-of-lines 1} (i18n/label :t/editing-message)]] [rn/view - [pressable/pressable {:on-press #(re-frame/dispatch [:chat.ui/cancel-message-edit]) - :accessibility-label :cancel-message-reply} - [icons/icon :main-icons/close-circle {:container-style (styles/close-button) - :color (:icon-02 @quo.colors/theme)}]]]]) + [pressable/pressable + {:on-press #(re-frame/dispatch [:chat.ui/cancel-message-edit]) + :accessibility-label :cancel-message-reply} + [icons/icon :main-icons/close-circle + {:container-style (styles/close-button) + :color (:icon-02 @quo.colors/theme)}]]]]) -(defn focus-input-on-edit [edit had-edit text-input-ref] +(defn focus-input-on-edit + [edit had-edit text-input-ref] ;;when we show edit we focus input (when-not (= edit @had-edit) (reset! had-edit edit) (when edit (js/setTimeout #(input-focus text-input-ref) 250)))) -(defn edit-message-wrapper [edit] - [rn/view {:style {:padding-horizontal 15 - :border-top-width 1 - :border-top-color (:ui-01 @quo.colors/theme) - :padding-vertical 8}} +(defn edit-message-wrapper + [edit] + [rn/view + {:style {:padding-horizontal 15 + :border-top-width 1 + :border-top-color (:ui-01 @quo.colors/theme) + :padding-vertical 8}} [edit-message edit]]) -(defn edit-message-auto-focus-wrapper [text-input-ref] +(defn edit-message-auto-focus-wrapper + [text-input-ref] (let [had-edit (atom nil)] (fn [] (let [edit @(re-frame/subscribe [:chats/edit-message])] diff --git a/src/status_im/ui/screens/chat/components/hooks.cljs b/src/status_im/ui/screens/chat/components/hooks.cljs index 25885fa14d..0b6a11d817 100644 --- a/src/status_im/ui/screens/chat/components/hooks.cljs +++ b/src/status_im/ui/screens/chat/components/hooks.cljs @@ -1,7 +1,7 @@ (ns status-im.ui.screens.chat.components.hooks - (:require [quo.react :as react] + (:require [quo.components.safe-area :refer [use-safe-area]] [quo.platform :as platform] - [quo.components.safe-area :refer [use-safe-area]] + [quo.react :as react] [quo.react-native :refer [use-window-dimensions] :as rn])) (def ^:private keyboard-change-event (if platform/android? "keyboardDidShow" "keyboardWillChangeFrame")) @@ -9,14 +9,16 @@ (def default-kb-height (if platform/ios? 258 272)) (def min-duration 100) -(defn use-keyboard-dimension [] +(defn use-keyboard-dimension + [] (let [{:keys [height]} (use-window-dimensions) {:keys [bottom]} (use-safe-area) keyboard-listener (atom nil) - keyboard (react/state {:height 0 - :duration min-duration - :end-position height - :max-height (+ (if platform/ios? bottom 0) default-kb-height)})] + keyboard (react/state + {:height 0 + :duration min-duration + :end-position height + :max-height (+ (if platform/ios? bottom 0) default-kb-height)})] (react/effect! (fn [] (letfn [(dimensions-change [evt] @@ -29,9 +31,11 @@ (when-not (= new-height (:height @keyboard)) (when (and duration easing platform/ios?) (rn/configure-next - #js {:duration (max min-duration duration) - :update #js {:duration (max min-duration duration) - :type (-> ^js rn/layout-animation .-Types (aget easing))}}))) + #js + {:duration (max min-duration duration) + :update #js + {:duration (max min-duration duration) + :type (-> ^js rn/layout-animation .-Types (aget easing))}}))) (reset! keyboard {:height new-height :end-position screen-y :duration (max min-duration duration) @@ -40,5 +44,6 @@ (reset! keyboard-listener (.addListener rn/keyboard keyboard-change-event keyboard-dimensions)) (fn [] (.removeEventListener rn/dimensions "change" dimensions-change) - (some-> ^js @keyboard-listener .remove))))) + (some-> ^js @keyboard-listener + .remove))))) @keyboard)) diff --git a/src/status_im/ui/screens/chat/components/input.cljs b/src/status_im/ui/screens/chat/components/input.cljs index e009cfe163..9da02484c6 100644 --- a/src/status_im/ui/screens/chat/components/input.cljs +++ b/src/status_im/ui/screens/chat/components/input.cljs @@ -1,47 +1,54 @@ (ns status-im.ui.screens.chat.components.input - (:require [status-im.ui.components.icons.icons :as icons] - [quo.react-native :as rn] - [quo.react :as quo.react] - [quo.platform :as platform] + (:require [clojure.string :as string] + [quo.components.animated.pressable :as pressable] + [quo.components.list.item :as list-item] [quo.components.text :as text] [quo.design-system.colors :as colors] - [status-im.ui.screens.chat.components.style :as styles] - [status-im.utils.fx :as fx] - [status-im.ui.screens.chat.components.reply :as reply] - [status-im.multiaccounts.core :as multiaccounts] - [status-im.chat.constants :as chat.constants] - [status-im.utils.utils :as utils.utils] - [quo.components.animated.pressable :as pressable] + [quo.platform :as platform] + [quo.react :as quo.react] + [quo.react-native :as rn] [re-frame.core :as re-frame] - [status-im.i18n.i18n :as i18n] - [status-im.chat.models.mentions :as mentions] - [status-im.ui.components.list.views :as list] - [quo.components.list.item :as list-item] - [status-im.ui.screens.chat.photos :as photos] [reagent.core :as reagent] - [clojure.string :as string])) + [status-im.chat.constants :as chat.constants] + [status-im.chat.models.mentions :as mentions] + [status-im.i18n.i18n :as i18n] + [status-im.multiaccounts.core :as multiaccounts] + [status-im.ui.components.icons.icons :as icons] + [status-im.ui.components.list.views :as list] + [status-im.ui.screens.chat.components.reply :as reply] + [status-im.ui.screens.chat.components.style :as styles] + [status-im.ui.screens.chat.photos :as photos] + [status-im.utils.fx :as fx] + [status-im.utils.utils :as utils.utils])) -(defn input-focus [text-input-ref] - (some-> ^js (quo.react/current-ref text-input-ref) .focus)) +(defn input-focus + [text-input-ref] + (some-> ^js (quo.react/current-ref text-input-ref) + .focus)) -(def panel->icons {:extensions :main-icons/commands - :images :main-icons/photo}) +(def panel->icons + {:extensions :main-icons/commands + :images :main-icons/photo}) -(defn touchable-icon [{:keys [panel active set-active accessibility-label]}] - [pressable/pressable {:type :scale - :accessibility-label accessibility-label - :on-press #(set-active (when-not (= active panel) panel))} +(defn touchable-icon + [{:keys [panel active set-active accessibility-label]}] + [pressable/pressable + {:type :scale + :accessibility-label accessibility-label + :on-press #(set-active (when-not (= active panel) panel))} [rn/view {:style (styles/touchable-icon)} [icons/icon (panel->icons panel) (styles/icon (= active panel))]]]) -(defn touchable-stickers-icon [{:keys [panel active set-active accessibility-label input-focus]}] - [pressable/pressable {:type :scale - :accessibility-label accessibility-label - :on-press #(if (= active panel) - (input-focus) - (set-active panel))} +(defn touchable-stickers-icon + [{:keys [panel active set-active accessibility-label input-focus]}] + [pressable/pressable + {:type :scale + :accessibility-label accessibility-label + :on-press #(if (= active panel) + (input-focus) + (set-active panel))} [rn/view {:style (styles/in-input-touchable-icon)} (if (= active panel) [icons/icon :main-icons/keyboard (styles/icon false)] @@ -49,7 +56,8 @@ ;; TODO(Ferossgp): Move this into audio panel. ;; Instead of not changing panel we can show a placeholder with no permission -(defn- request-record-audio-permission [set-active panel] +(defn- request-record-audio-permission + [set-active panel] (re-frame/dispatch [:request-permissions {:permissions [:record-audio] @@ -63,18 +71,21 @@ (i18n/label :t/audio-recorder-permissions-error))) 50)}])) -(defn touchable-audio-icon [{:keys [panel active set-active accessibility-label input-focus]}] - [pressable/pressable {:type :scale - :accessibility-label accessibility-label - :on-press #(if (= active panel) - (input-focus) - (request-record-audio-permission set-active panel))} +(defn touchable-audio-icon + [{:keys [panel active set-active accessibility-label input-focus]}] + [pressable/pressable + {:type :scale + :accessibility-label accessibility-label + :on-press #(if (= active panel) + (input-focus) + (request-record-audio-permission set-active panel))} [rn/view {:style (styles/in-input-touchable-icon)} (if (= active panel) [icons/icon :main-icons/keyboard (styles/icon false)] [icons/icon :main-icons/speech (styles/icon false)])]]) -(defn send-button [on-send contact-request] +(defn send-button + [on-send contact-request] [rn/touchable-opacity {:on-press-in on-send} [rn/view {:style (styles/send-message-button)} (when-not contact-request @@ -83,22 +94,23 @@ :accessibility-label :send-message-button :color (styles/send-icon-color)}])]]) -(defn on-selection-change [timeout-id last-text-change mentionable-users args] +(defn on-selection-change + [timeout-id last-text-change mentionable-users args] (let [selection (.-selection ^js (.-nativeEvent ^js args)) - start (.-start selection) - end (.-end selection)] + start (.-start selection) + end (.-end selection)] ;; NOTE(rasom): on iOS we do not dispatch this event immediately ;; because it is needed only in case if selection is changed without ;; typing. Timeout might be canceled on `on-change`. (when platform/ios? (reset! - timeout-id - (utils.utils/set-timeout - #(re-frame/dispatch [::mentions/on-selection-change - {:start start - :end end} - mentionable-users]) - 50))) + timeout-id + (utils.utils/set-timeout + #(re-frame/dispatch [::mentions/on-selection-change + {:start start + :end end} + mentionable-users]) + 50))) ;; NOTE(rasom): on Android we dispatch event only in case if there ;; was no text changes during last 50ms. `on-selection-change` is ;; dispatched after `on-change`, that's why there is no another way @@ -128,42 +140,51 @@ [] (swap! chat-input-key inc)) -(defn show-send [{:keys [actions-ref send-ref sticker-ref]}] +(defn show-send + [{:keys [actions-ref send-ref sticker-ref]}] (when actions-ref (quo.react/set-native-props actions-ref #js {:width 0 :left -88})) (quo.react/set-native-props send-ref #js {:width nil :right nil}) (when sticker-ref (quo.react/set-native-props sticker-ref #js {:width 0 :right -100}))) -(defn hide-send [{:keys [actions-ref send-ref sticker-ref]}] +(defn hide-send + [{:keys [actions-ref send-ref sticker-ref]}] (when actions-ref (quo.react/set-native-props actions-ref #js {:width nil :left nil})) (quo.react/set-native-props send-ref #js {:width 0 :right -100}) (when sticker-ref (quo.react/set-native-props sticker-ref #js {:width nil :right nil}))) -(defn reset-input [refs chat-id] - (some-> ^js (quo.react/current-ref (:text-input-ref refs)) .clear) +(defn reset-input + [refs chat-id] + (some-> ^js (quo.react/current-ref (:text-input-ref refs)) + .clear) (swap! mentions-enabled update :render not) (swap! input-texts dissoc chat-id)) -(defn clear-input [chat-id refs] +(defn clear-input + [chat-id refs] (hide-send refs) (if (get @mentions-enabled chat-id) (do (swap! mentions-enabled dissoc chat-id) - ;;we need this timeout, because if we clear text input and first index was a mention object with blue color, - ;;after clearing text will be typed with this blue color, so we render white text first and then clear it + ;;we need this timeout, because if we clear text input and first index was a mention object with + ;;blue color, + ;;after clearing text will be typed with this blue color, so we render white text first and then + ;;clear it (js/setTimeout #(reset-input refs chat-id) 50)) (reset-input refs chat-id))) -(defn on-text-change [val chat-id] +(defn on-text-change + [val chat-id] (swap! input-texts assoc chat-id val) ;;we still store it in app-db for mentions, we don't have reactions in views (re-frame/dispatch [:chat.ui/set-chat-input-text val])) -(defn on-change [last-text-change timeout-id mentionable-users refs chat-id sending-image args] - (let [text (.-text ^js (.-nativeEvent ^js args)) +(defn on-change + [last-text-change timeout-id mentionable-users refs chat-id sending-image args] + (let [text (.-text ^js (.-nativeEvent ^js args)) prev-text (get @input-texts chat-id)] (when (and (seq prev-text) (empty? text) (not sending-image)) (hide-send refs)) @@ -197,15 +218,21 @@ chat (get-in db [:chats chat-id]) current-multiaccount (:multiaccount db) mentionable-users (mentions/get-mentionable-users - chat all-contacts current-multiaccount nil) + chat + all-contacts + current-multiaccount + nil) hydrated-mentions (map (fn [[t mention :as e]] (if (= t :mention) (let [mention (multiaccounts/displayed-name (get mentionable-users mention))] - [:mention (if (string/starts-with? mention "@") - mention (str "@" mention))]) - e)) text-with-mentions) + [:mention + (if (string/starts-with? mention "@") + mention + (str "@" mention))]) + e)) + text-with-mentions) info (mentions/->info hydrated-mentions) new-text (string/join (map second hydrated-mentions))] {:set-text-input-value [chat-id new-text] @@ -215,13 +242,14 @@ (assoc-in [:chat/inputs-with-mentions chat-id] hydrated-mentions) (assoc-in [:chats/mentions chat-id :mentions] info))})) -(defn on-text-input [mentionable-users chat-id args] - (let [native-event (.-nativeEvent ^js args) - text (.-text ^js native-event) +(defn on-text-input + [mentionable-users chat-id args] + (let [native-event (.-nativeEvent ^js args) + text (.-text ^js native-event) previous-text (.-previousText ^js native-event) - range (.-range ^js native-event) - start (.-start ^js range) - end (.-end ^js range)] + range (.-range ^js native-event) + start (.-start ^js range) + end (.-end ^js range)] (when (and (not (get @mentions-enabled chat-id)) (string/index-of text "@")) (swap! mentions-enabled assoc chat-id true)) @@ -237,13 +265,14 @@ (when platform/android? (re-frame/dispatch [::mentions/calculate-suggestions mentionable-users])))) -(defn text-input [{:keys [set-active-panel refs chat-id sending-image]}] +(defn text-input + [{:keys [set-active-panel refs chat-id sending-image]}] (let [cooldown-enabled? @(re-frame/subscribe [:chats/current-chat-cooldown-enabled?]) mentionable-users @(re-frame/subscribe [:chats/mentionable-users]) - timeout-id (atom nil) - last-text-change (atom nil) - mentions-enabled (get @mentions-enabled chat-id) - contact-request @(re-frame/subscribe [:chats/sending-contact-request])] + timeout-id (atom nil) + last-text-change (atom nil) + mentions-enabled (get @mentions-enabled chat-id) + contact-request @(re-frame/subscribe [:chats/sending-contact-request])] [rn/text-input {:style (styles/text-input contact-request) @@ -263,8 +292,12 @@ (i18n/label :t/type-a-message)) :underline-color-android :transparent :auto-capitalize :sentences - :on-selection-change (partial on-selection-change timeout-id last-text-change mentionable-users) - :on-change (partial on-change last-text-change timeout-id mentionable-users refs chat-id sending-image) + :on-selection-change (partial on-selection-change + timeout-id + last-text-change + mentionable-users) + :on-change + (partial on-change last-text-change timeout-id mentionable-users refs chat-id sending-image) :on-text-input (partial on-text-input mentionable-users chat-id)} (if mentions-enabled (for [[idx [type text]] (map-indexed @@ -280,39 +313,41 @@ [[public-key {:keys [alias name nickname] :as user}] _ _ text-input-ref] (let [ens-name? (not= alias name)] [list-item/list-item - (cond-> {:icon [photos/member-photo public-key] - :size :small - :text-size :small - :title - [text/text - {:weight :medium - :ellipsize-mode :tail - :number-of-lines 1 - :size :small} - (if nickname - nickname - name) - (when nickname - [text/text - {:weight :regular - :color :secondary - :ellipsize-mode :tail - :size :small} - " " - (when ens-name? - "@") - name])] - :title-text-weight :medium - :on-press - (fn [] - (re-frame/dispatch [:chat.ui/select-mention text-input-ref user]))} + (cond-> + {:icon [photos/member-photo public-key] + :size :small + :text-size :small + :title + [text/text + {:weight :medium + :ellipsize-mode :tail + :number-of-lines 1 + :size :small} + (if nickname + nickname + name) + (when nickname + [text/text + {:weight :regular + :color :secondary + :ellipsize-mode :tail + :size :small} + " " + (when ens-name? + "@") + name])] + :title-text-weight :medium + :on-press + (fn [] + (re-frame/dispatch [:chat.ui/select-mention text-input-ref user]))} ens-name? (assoc :subtitle alias))])) (def chat-toolbar-height (reagent/atom nil)) -(defn autocomplete-mentions [text-input-ref bottom] +(defn autocomplete-mentions + [text-input-ref bottom] (let [suggestions @(re-frame/subscribe [:chat/mention-suggestions])] (when (seq suggestions) (let [height (+ 16 (* 52 (min 4.5 (count suggestions))))] @@ -330,36 +365,44 @@ :render-data text-input-ref :render-fn mention-item}]]])))) -(defn on-chat-toolbar-layout [^js ev] +(defn on-chat-toolbar-layout + [^js ev] (reset! chat-toolbar-height (-> ev .-nativeEvent .-layout .-height))) -(defn send-image [] +(defn send-image + [] (let [sending-image @(re-frame/subscribe [:chats/sending-image])] (when (seq sending-image) [reply/send-image sending-image]))) -(defn actions [extensions image show-send actions-ref active-panel set-active-panel contact-request] - [rn/view {:style (styles/actions-wrapper (and (not contact-request) show-send)) - :ref actions-ref} +(defn actions + [extensions image show-send actions-ref active-panel set-active-panel contact-request] + [rn/view + {:style (styles/actions-wrapper (and (not contact-request) show-send)) + :ref actions-ref} (when extensions - [touchable-icon {:panel :extensions - :accessibility-label :show-extensions-icon - :active active-panel - :set-active set-active-panel}]) + [touchable-icon + {:panel :extensions + :accessibility-label :show-extensions-icon + :active active-panel + :set-active set-active-panel}]) (when image - [touchable-icon {:panel :images - :accessibility-label :show-photo-icon - :active active-panel - :set-active set-active-panel}])]) + [touchable-icon + {:panel :images + :accessibility-label :show-photo-icon + :active active-panel + :set-active set-active-panel}])]) -(defn chat-toolbar [{:keys [chat-id]}] - (let [actions-ref (quo.react/create-ref) - send-ref (quo.react/create-ref) - sticker-ref (quo.react/create-ref) +(defn chat-toolbar + [{:keys [chat-id]}] + (let [actions-ref (quo.react/create-ref) + send-ref (quo.react/create-ref) + sticker-ref (quo.react/create-ref) toolbar-options (re-frame/subscribe [:chats/chat-toolbar]) - show-send (seq (get @input-texts chat-id))] + show-send (seq (get @input-texts chat-id))] (fn [{:keys [active-panel set-active-panel text-input-ref chat-id]}] - (let [;we want to control components on native level, so instead of RN state we set native props via reference + (let [;we want to control components on native level, so instead of RN state we set native props + ;via reference ;we don't react on input text in this view, @input-texts below is a regular atom refs {:actions-ref actions-ref :send-ref send-ref @@ -368,37 +411,43 @@ {:keys [send stickers image extensions audio sending-image]} @toolbar-options show-send (or show-send sending-image) contact-request @(re-frame/subscribe [:chats/sending-contact-request])] - [rn/view {:style (styles/toolbar) - :on-layout on-chat-toolbar-layout} + [rn/view + {:style (styles/toolbar) + :on-layout on-chat-toolbar-layout} ;; EXTENSIONS and IMAGE buttons [actions extensions image show-send actions-ref active-panel set-active-panel contact-request] [rn/view {:style (styles/input-container contact-request)} [send-image] [rn/view {:style styles/input-row} - [text-input {:chat-id chat-id - :sending-image sending-image - :refs refs - :set-active-panel set-active-panel}] + [text-input + {:chat-id chat-id + :sending-image sending-image + :refs refs + :set-active-panel set-active-panel}] ;; SEND button [rn/view {:ref send-ref :style (when-not show-send {:width 0 :right -100})} (when send - [send-button #(do (clear-input chat-id refs) - (re-frame/dispatch [:chat.ui/send-current-message])) + [send-button + #(do (clear-input chat-id refs) + (re-frame/dispatch [:chat.ui/send-current-message])) contact-request])] ;; STICKERS and AUDIO buttons (when-not @(re-frame/subscribe [:chats/edit-message]) - [rn/view {:style (merge {:flex-direction :row} (when show-send {:width 0 :right -100})) - :ref sticker-ref} + [rn/view + {:style (merge {:flex-direction :row} (when show-send {:width 0 :right -100})) + :ref sticker-ref} (when stickers - [touchable-stickers-icon {:panel :stickers - :accessibility-label :show-stickers-icon - :active active-panel - :input-focus #(input-focus text-input-ref) - :set-active set-active-panel}]) + [touchable-stickers-icon + {:panel :stickers + :accessibility-label :show-stickers-icon + :active active-panel + :input-focus #(input-focus text-input-ref) + :set-active set-active-panel}]) (when audio - [touchable-audio-icon {:panel :audio - :accessibility-label :show-audio-message-icon - :active active-panel - :input-focus #(input-focus text-input-ref) - :set-active set-active-panel}])])]]])))) + [touchable-audio-icon + {:panel :audio + :accessibility-label :show-audio-message-icon + :active active-panel + :input-focus #(input-focus text-input-ref) + :set-active set-active-panel}])])]]])))) diff --git a/src/status_im/ui/screens/chat/components/reply.cljs b/src/status_im/ui/screens/chat/components/reply.cljs index e158f72dc5..fe1bc669f3 100644 --- a/src/status_im/ui/screens/chat/components/reply.cljs +++ b/src/status_im/ui/screens/chat/components/reply.cljs @@ -1,22 +1,25 @@ (ns status-im.ui.screens.chat.components.reply - (:require [quo.core :as quo] + (:require [clojure.string :as string] + [quo.components.animated.pressable :as pressable] + [quo.core :as quo] + [quo.design-system.colors :as quo.colors] [quo.react :as quo.react] [quo.react-native :as rn] - [quo.design-system.colors :as quo.colors] - [status-im.i18n.i18n :as i18n] - [quo.components.animated.pressable :as pressable] - [status-im.ui.components.icons.icons :as icons] - [status-im.ethereum.stateofus :as stateofus] - [status-im.ui.screens.chat.components.style :as styles] [re-frame.core :as re-frame] - [clojure.string :as string])) + [status-im.ethereum.stateofus :as stateofus] + [status-im.i18n.i18n :as i18n] + [status-im.ui.components.icons.icons :as icons] + [status-im.ui.screens.chat.components.style :as styles])) (def ^:private reply-symbol "↪ ") -(defn input-focus [text-input-ref] - (some-> ^js (quo.react/current-ref text-input-ref) .focus)) +(defn input-focus + [text-input-ref] + (some-> ^js (quo.react/current-ref text-input-ref) + .focus)) -(defn format-author [contact-name] +(defn format-author + [contact-name] (let [author (if (or (= (aget contact-name 0) "@") ;; in case of replies (= (aget contact-name 1) "@")) @@ -25,12 +28,14 @@ contact-name)] (i18n/label :replying-to {:author author}))) -(defn format-reply-author [from username current-public-key] +(defn format-reply-author + [from username current-public-key] (or (and (= from current-public-key) (str reply-symbol (i18n/label :t/You))) (str reply-symbol (format-author username)))) -(defn get-quoted-text-with-mentions [parsed-text] +(defn get-quoted-text-with-mentions + [parsed-text] (string/join (mapv (fn [{:keys [type literal children]}] (cond @@ -47,39 +52,49 @@ literal)) parsed-text))) -(defn reply-message [{:keys [from]}] +(defn reply-message + [{:keys [from]}] (let [contact-name @(re-frame/subscribe [:contacts/contact-name-by-identity from]) current-public-key @(re-frame/subscribe [:multiaccount/public-key])] [rn/view {:style {:flex-direction :row}} [rn/view {:style (styles/reply-content)} - [quo/text {:weight :medium - :number-of-lines 1 - :style {:line-height 18}} + [quo/text + {:weight :medium + :number-of-lines 1 + :style {:line-height 18}} (format-reply-author from contact-name current-public-key)]] [rn/view - [pressable/pressable {:on-press #(re-frame/dispatch [:chat.ui/cancel-message-reply]) - :accessibility-label :cancel-message-reply} - [icons/icon :main-icons/close-circle {:container-style (styles/close-button) - :color (:icon-02 @quo.colors/theme)}]]]])) + [pressable/pressable + {:on-press #(re-frame/dispatch [:chat.ui/cancel-message-reply]) + :accessibility-label :cancel-message-reply} + [icons/icon :main-icons/close-circle + {:container-style (styles/close-button) + :color (:icon-02 @quo.colors/theme)}]]]])) -(defn send-image [images] +(defn send-image + [images] [rn/view {:style (styles/reply-container-image)} - [rn/scroll-view {:horizontal true - :style (styles/reply-content)} + [rn/scroll-view + {:horizontal true + :style (styles/reply-content)} (for [{:keys [uri]} (vals images)] ^{:key uri} - [rn/image {:source {:uri uri} - :style {:width 56 - :height 56 - :border-radius 4 - :margin-right 4}}])] + [rn/image + {:source {:uri uri} + :style {:width 56 + :height 56 + :border-radius 4 + :margin-right 4}}])] [rn/view - [pressable/pressable {:on-press #(re-frame/dispatch [:chat.ui/cancel-sending-image]) - :accessibility-label :cancel-send-image} - [icons/icon :main-icons/close-circle {:container-style (styles/close-button) - :color quo.colors/white}]]]]) + [pressable/pressable + {:on-press #(re-frame/dispatch [:chat.ui/cancel-sending-image]) + :accessibility-label :cancel-send-image} + [icons/icon :main-icons/close-circle + {:container-style (styles/close-button) + :color quo.colors/white}]]]]) -(defn focus-input-on-reply [reply had-reply text-input-ref] +(defn focus-input-on-reply + [reply had-reply text-input-ref] ;;when we show reply we focus input (when-not (= reply @had-reply) (reset! had-reply reply) @@ -87,14 +102,17 @@ ;; A setTimeout of 0 is necessary to ensure the statement is enqueued and will get executed ASAP. (js/setTimeout #(input-focus text-input-ref) 0)))) -(defn reply-message-wrapper [reply] - [rn/view {:style {:padding-horizontal 15 - :border-top-width 1 - :border-top-color (:ui-01 @quo.colors/theme) - :padding-vertical 8}} +(defn reply-message-wrapper + [reply] + [rn/view + {:style {:padding-horizontal 15 + :border-top-width 1 + :border-top-color (:ui-01 @quo.colors/theme) + :padding-vertical 8}} [reply-message reply]]) -(defn reply-message-auto-focus-wrapper [text-input-ref] +(defn reply-message-auto-focus-wrapper + [text-input-ref] (let [had-reply (atom nil)] (fn [] (let [reply @(re-frame/subscribe [:chats/reply-message])] diff --git a/src/status_im/ui/screens/chat/components/style.cljs b/src/status_im/ui/screens/chat/components/style.cljs index b07cb83f00..f09f25f230 100644 --- a/src/status_im/ui/screens/chat/components/style.cljs +++ b/src/status_im/ui/screens/chat/components/style.cljs @@ -1,9 +1,10 @@ (ns status-im.ui.screens.chat.components.style - (:require [quo.platform :as platform] - [quo.design-system.colors :as colors] - [quo.design-system.typography :as typography])) + (:require [quo.design-system.colors :as colors] + [quo.design-system.typography :as typography] + [quo.platform :as platform])) -(defn toolbar [] +(defn toolbar + [] {:min-height 52 :padding-vertical 8 :border-top-width 1 @@ -11,7 +12,8 @@ :align-items :flex-end :flex-direction :row}) -(defn input-container [contact-request] +(defn input-container + [contact-request] {:background-color (:ui-01 @colors/theme) :flex 1 :height (when contact-request 44) @@ -26,7 +28,8 @@ :overflow :hidden :align-items :flex-end}) -(defn text-input-wrapper [] +(defn text-input-wrapper + [] (merge {:flex-direction :row :align-items :flex-start :flex 1 @@ -35,7 +38,8 @@ (when platform/ios? {:padding-top 2}))) -(defn text-input [contact-request] +(defn text-input + [contact-request] (merge typography/font-regular typography/base {:flex 1 @@ -50,31 +54,36 @@ {:padding-top (if contact-request 10 2) :padding-bottom (if contact-request 5 6)}))) -(defn actions-wrapper [show-send] +(defn actions-wrapper + [show-send] (merge (when show-send {:width 0 :left -88}) {:flex-direction :row :padding-left 4 :min-height 34})) -(defn touchable-icon [] +(defn touchable-icon + [] {:padding-horizontal 10 :padding-vertical 5 :justify-content :center :align-items :center}) -(defn in-input-touchable-icon [] +(defn in-input-touchable-icon + [] {:padding-horizontal 6 :padding-vertical 5 :justify-content :center :align-items :center}) -(defn icon [active] +(defn icon + [active] {:color (if active (:icon-04 @colors/theme) (:icon-02 @colors/theme))}) -(defn reply-container-image [] +(defn reply-container-image + [] {:border-top-left-radius 14 :border-top-right-radius 14 :border-bottom-right-radius 4 @@ -83,35 +92,43 @@ :flex-direction :row :background-color (:ui-03 @colors/theme)}) -(defn reply-container [] +(defn reply-container + [] {:flex-direction :row}) -(defn reply-content [] +(defn reply-content + [] {:padding-vertical 6 :padding-horizontal 10 :flex 1}) -(defn quoted-message [pin?] +(defn quoted-message + [pin?] (merge {:flex-direction :row - :align-items :center - :width "45%"} - (when-not pin? {:position :absolute - :left 34 - :top 3}))) + :align-items :center + :width "45%"} + (when-not pin? + {:position :absolute + :left 34 + :top 3}))) -(defn contact-request-content [] +(defn contact-request-content + [] {:flex 1 :flex-direction :row :justify-content :space-between}) -(defn close-button [] +(defn close-button + [] {:margin-top 3}) -(defn send-message-button [] +(defn send-message-button + [] {:margin-vertical 4 :margin-horizontal 5}) -(defn send-message-container [contact-request] +(defn send-message-container + [contact-request] {:background-color (:interactive-01 @colors/theme) :width 26 :height (if contact-request 44 26) @@ -119,10 +136,12 @@ :justify-content :center :align-items :center}) -(defn send-icon-color [] +(defn send-icon-color + [] colors/white) -(defn autocomplete-container [bottom] +(defn autocomplete-container + [bottom] {:position :absolute :left 0 :right 0 diff --git a/src/status_im/ui/screens/chat/extensions/views.cljs b/src/status_im/ui/screens/chat/extensions/views.cljs index 3eeff82693..48c17da149 100644 --- a/src/status_im/ui/screens/chat/extensions/views.cljs +++ b/src/status_im/ui/screens/chat/extensions/views.cljs @@ -1,29 +1,53 @@ (ns status-im.ui.screens.chat.extensions.views - (:require [status-im.ui.components.react :as react] + (:require [quo.design-system.colors :as colors] [re-frame.core :as re-frame] - [quo.design-system.colors :as colors] + [status-im.i18n.i18n :as i18n] [status-im.ui.components.icons.icons :as icons] - [status-im.i18n.i18n :as i18n])) + [status-im.ui.components.react :as react])) -(defn extensions-view [] - [react/view {:style {:background-color colors/white - :flex 1}} +(defn extensions-view + [] + [react/view + {:style {:background-color colors/white + :flex 1}} [react/view {:style {:flex-direction :row}} [react/touchable-highlight {:on-press #(re-frame/dispatch [:wallet/prepare-transaction-from-chat])} - [react/view {:width 128 :height 128 :justify-content :space-between - :padding-horizontal 10 :padding-vertical 12 - :background-color (colors/alpha colors/purple 0.2) :border-radius 16 :margin-left 8} - [react/view {:background-color colors/purple :width 40 :height 40 :border-radius 20 :align-items :center - :justify-content :center} + [react/view + {:width 128 + :height 128 + :justify-content :space-between + :padding-horizontal 10 + :padding-vertical 12 + :background-color (colors/alpha colors/purple 0.2) + :border-radius 16 + :margin-left 8} + [react/view + {:background-color colors/purple + :width 40 + :height 40 + :border-radius 20 + :align-items :center + :justify-content :center} [icons/icon :main-icons/send {:color colors/white}]] [react/text {:typography :medium} (i18n/label :t/send-transaction)]]] [react/touchable-highlight {:on-press #(re-frame/dispatch [:wallet/prepare-request-transaction-from-chat])} - [react/view {:width 128 :height 128 :justify-content :space-between - :padding-horizontal 10 :padding-vertical 12 - :background-color (colors/alpha colors/orange 0.2) :border-radius 16 :margin-left 8} - [react/view {:background-color colors/orange :width 40 :height 40 :border-radius 20 :align-items :center - :justify-content :center} + [react/view + {:width 128 + :height 128 + :justify-content :space-between + :padding-horizontal 10 + :padding-vertical 12 + :background-color (colors/alpha colors/orange 0.2) + :border-radius 16 + :margin-left 8} + [react/view + {:background-color colors/orange + :width 40 + :height 40 + :border-radius 20 + :align-items :center + :justify-content :center} [icons/icon :main-icons/receive {:color colors/white}]] [react/text {:typography :medium} (i18n/label :t/request-transaction)]]]]]) diff --git a/src/status_im/ui/screens/chat/group.cljs b/src/status_im/ui/screens/chat/group.cljs index c00b54e86a..f62bacce0a 100644 --- a/src/status_im/ui/screens/chat/group.cljs +++ b/src/status_im/ui/screens/chat/group.cljs @@ -1,42 +1,52 @@ (ns status-im.ui.screens.chat.group - (:require [re-frame.core :as re-frame] - [quo.core :as quo] - [status-im.ui.components.react :as react] + (:require [quo.core :as quo] + [quo.design-system.colors :as colors] + [re-frame.core :as re-frame] [status-im.constants :as constants] - [status-im.utils.universal-links.utils :as links] - [status-im.ui.screens.chat.styles.main :as style] [status-im.i18n.i18n :as i18n] [status-im.ui.components.list-selection :as list-selection] - [quo.design-system.colors :as colors] - [utils.debounce :as debounce] - [status-im.utils.platform :as platform]) + [status-im.ui.components.react :as react] + [status-im.ui.screens.chat.styles.main :as style] + [status-im.utils.platform :as platform] + [status-im.utils.universal-links.utils :as links] + [utils.debounce :as debounce]) (:require-macros [status-im.utils.views :refer [defview letsubs]])) -(defn decline-chat [chat-id] +(defn decline-chat + [chat-id] [quo/button {:type :secondary :accessibility-label :decline-chat-button - :on-press #(debounce/dispatch-and-chill [:group-chats.ui/leave-chat-confirmed chat-id] 2000)} + :on-press #(debounce/dispatch-and-chill [:group-chats.ui/leave-chat-confirmed chat-id] + 2000)} (i18n/label :t/group-chat-decline-invitation)]) (def message-max-length 100) -(defn request-membership [{:keys [state introduction-message] :as invitation}] +(defn request-membership + [{:keys [state introduction-message] :as invitation}] (let [{:keys [message retry?]} @(re-frame/subscribe [:chats/current-chat-membership]) - message-length (count message)] + message-length (count message)] [react/view {:margin-horizontal 16 :margin-top 10} (cond (and invitation (= constants/invitation-state-requested state) (not retry?)) [react/view [react/text (i18n/label :t/introduce-yourself)] - [react/text {:style {:margin-top 10 :margin-bottom 16 :min-height 66 - :padding-horizontal 16 :padding-vertical 11 - :border-color colors/gray-lighter :border-width 1 - :border-radius 8 - :color colors/gray}} + [react/text + {:style {:margin-top 10 + :margin-bottom 16 + :min-height 66 + :padding-horizontal 16 + :padding-vertical 11 + :border-color colors/gray-lighter + :border-width 1 + :border-radius 8 + :color colors/gray}} introduction-message] - [react/text {:style {:align-self :flex-end :margin-bottom 30 - :color colors/gray}} + [react/text + {:style {:align-self :flex-end + :margin-bottom 30 + :color colors/gray}} (str (count introduction-message) "/100")]] (and invitation (= constants/invitation-state-rejected state) (not retry?)) @@ -47,16 +57,17 @@ :else [react/view [react/text (i18n/label :t/introduce-yourself)] - [quo/text-input {:placeholder (i18n/label :t/message) - :on-change-text #(re-frame/dispatch [:group-chats.ui/update-membership-message %]) - :max-length (if platform/android? - message-max-length - (when (>= message-length message-max-length) - message-length)) - :multiline true - :default-value message - :accessibility-label :introduce-yourself-input - :container-style {:margin-top 10 :margin-bottom 16}}] + [quo/text-input + {:placeholder (i18n/label :t/message) + :on-change-text #(re-frame/dispatch [:group-chats.ui/update-membership-message %]) + :max-length (if platform/android? + message-max-length + (when (>= message-length message-max-length) + message-length)) + :multiline true + :default-value message + :accessibility-label :introduce-yourself-input + :container-style {:margin-top 10 :margin-bottom 16}}] [react/text {:style {:align-self :flex-end :margin-bottom 30}} (str (count message) "/100")]])])) @@ -67,17 +78,20 @@ [request-membership (first invitations)]))) (def group-chat-description-loading - [react/view {:style (merge style/intro-header-description-container - {:margin-bottom 36 - :height 44})} + [react/view + {:style (merge style/intro-header-description-container + {:margin-bottom 36 + :height 44})} [react/text {:style style/intro-header-description} (i18n/label :t/loading)] - [react/activity-indicator {:animating true - :size :small - :color colors/gray}]]) + [react/activity-indicator + {:animating true + :size :small + :color colors/gray}]]) -(defn calculate-quiet-time [synced-to - synced-from] +(defn calculate-quiet-time + [synced-to + synced-from] (when synced-from (let [quiet-hours (quot (- synced-to synced-from) (* 60 60))] @@ -87,23 +101,27 @@ (i18n/label :t/quiet-days {:quiet-days (quot quiet-hours 24)}))))) -(defview no-messages-community-chat-description-container [chat-id] +(defview no-messages-community-chat-description-container + [chat-id] (letsubs [{:keys [synced-to synced-from]} [:chats/synced-to-and-from chat-id]] - [react/text {:style (merge style/intro-header-description - {:margin-bottom 36})} + [react/text + {:style (merge style/intro-header-description + {:margin-bottom 36})} (let [quiet-time (calculate-quiet-time synced-to synced-from)] (i18n/label :t/empty-chat-description-community {:quiet-hours quiet-time}))])) -(defview no-messages-private-group-chat-description-container [chat-id] +(defview no-messages-private-group-chat-description-container + [chat-id] (letsubs [{:keys [synced-to synced-from]} [:chats/synced-to-and-from chat-id]] (let [quiet-time (calculate-quiet-time synced-to synced-from)] - [react/nested-text {:style (merge style/intro-header-description - {:margin-bottom 36})} + [react/nested-text + {:style (merge style/intro-header-description + {:margin-bottom 36})} (if quiet-time (i18n/label :t/empty-chat-description-public {:quiet-hours quiet-time}) @@ -112,7 +130,8 @@ :on-press #(list-selection/open-share {:message (i18n/label - :t/share-public-chat-text {:link (links/generate-link :public-chat :external chat-id)})})} + :t/share-public-chat-text + {:link (links/generate-link :public-chat :external chat-id)})})} (i18n/label :t/empty-chat-description-public-share-this)]]))) (defview pending-invitation-description @@ -133,12 +152,14 @@ :group-name chat-name}) [{:style {:color colors/black}} inviter-name]])) -(defn created-group-chat-description [chat-name] +(defn created-group-chat-description + [chat-name] [react/text {:style style/intro-header-description} (i18n/label :t/created-group-chat-description {:group-name chat-name})]) -(defview group-chat-inviter-description-container [chat-id chat-name] +(defview group-chat-inviter-description-container + [chat-id chat-name] (letsubs [{:keys [member? inviter-pk]} [:group-chat/inviter-info chat-id]] (cond @@ -149,7 +170,8 @@ :else [created-group-chat-description chat-name]))) -(defn group-chat-membership-description [] +(defn group-chat-membership-description + [] [react/text {:style {:text-align :center :margin-horizontal 30}} (i18n/label :t/membership-description)]) @@ -160,17 +182,18 @@ chat-type loading-messages? no-messages?]}] - (cond loading-messages? - group-chat-description-loading + (cond + loading-messages? + group-chat-description-loading - (and no-messages? (= chat-type constants/public-chat-type)) - [no-messages-private-group-chat-description-container chat-id] + (and no-messages? (= chat-type constants/public-chat-type)) + [no-messages-private-group-chat-description-container chat-id] - (and no-messages? (= chat-type constants/community-chat-type)) - [no-messages-community-chat-description-container chat-id] + (and no-messages? (= chat-type constants/community-chat-type)) + [no-messages-community-chat-description-container chat-id] - invitation-admin - [group-chat-membership-description] + invitation-admin + [group-chat-membership-description] - (= chat-type constants/private-group-chat-type) - [group-chat-inviter-description-container chat-id chat-name])) + (= chat-type constants/private-group-chat-type) + [group-chat-inviter-description-container chat-id chat-name])) diff --git a/src/status_im/ui/screens/chat/image/preview/views.cljs b/src/status_im/ui/screens/chat/image/preview/views.cljs index 40609c512f..45a08b872b 100644 --- a/src/status_im/ui/screens/chat/image/preview/views.cljs +++ b/src/status_im/ui/screens/chat/image/preview/views.cljs @@ -1,80 +1,95 @@ (ns status-im.ui.screens.chat.image.preview.views - (:require [quo.design-system.colors :as colors] - [status-im.ui.components.react :as react] - [reagent.core :as reagent] - [re-frame.core :as re-frame] - [quo.platform :as platform] - [status-im.ui.components.icons.icons :as icons] + (:require ["react-native-image-viewing" :default image-viewing] [quo.components.safe-area :as safe-area] - ["react-native-image-viewing" :default image-viewing] + [quo.design-system.colors :as colors] + [quo.platform :as platform] + [re-frame.core :as re-frame] + [reagent.core :as reagent] + [status-im.chat.models.images :as images] + [status-im.ui.components.icons.icons :as icons] + [status-im.ui.components.react :as react] [status-im.utils.share :as share] - [taoensso.timbre :as log] - [status-im.chat.models.images :as images])) + [taoensso.timbre :as log])) -(defn share [path] - (share/open {:url (str (when platform/android? "file://") path) +(defn share + [path] + (share/open {:url (str (when platform/android? "file://") path) :type "image/jpeg"} #(log/debug "image shared successfully") #(log/error "could not share image"))) -(defn header-options [] +(defn header-options + [] (fn [{:keys [message on-close]}] - ;; FIXME(Ferossgp): Bottom sheet doesn't work on Android because of https://github.com/software-mansion/react-native-gesture-handler/issues/139 - [react/view {:style {:flex-direction :row - :background-color colors/black-transparent-86 - :border-radius 44 - :padding-vertical 8 - :padding-horizontal 12 - :position :absolute - :right 0}} + ;; FIXME(Ferossgp): Bottom sheet doesn't work on Android because of + ;; https://github.com/software-mansion/react-native-gesture-handler/issues/139 + [react/view + {:style {:flex-direction :row + :background-color colors/black-transparent-86 + :border-radius 44 + :padding-vertical 8 + :padding-horizontal 12 + :position :absolute + :right 0}} [react/touchable-opacity - {:on-press (fn [] - (on-close) - (re-frame/dispatch [:chat.ui/save-image-to-gallery (get-in message [:content :image])])) - :style {:margin-right 10} + {:on-press (fn [] + (on-close) + (re-frame/dispatch [:chat.ui/save-image-to-gallery + (get-in message [:content :image])])) + :style {:margin-right 10} :accessibility-label :save-button} - [icons/icon :main-icons/download {:container-style {:width 24 - :height 24} - :color colors/white-persist}]] + [icons/icon :main-icons/download + {:container-style {:width 24 + :height 24} + :color colors/white-persist}]] [react/touchable-opacity - {:on-press #(images/download-image-http (get-in message [:content :image]) share) - :style {:margin-left 10} + {:on-press #(images/download-image-http (get-in message [:content :image]) share) + :style {:margin-left 10} :accessibility-label :share-button} - [icons/icon :main-icons/share-default {:container-style {:width 24 - :height 24} - :color colors/white-persist}]]])) + [icons/icon :main-icons/share-default + {:container-style {:width 24 + :height 24} + :color colors/white-persist}]]])) -(defn header [{:keys [on-close] :as props}] +(defn header + [{:keys [on-close] :as props}] [safe-area/consumer (fn [insets] - [react/view {:style {:padding-horizontal 15 - :padding-top (+ (:bottom insets) 50)}} + [react/view + {:style {:padding-horizontal 15 + :padding-top (+ (:bottom insets) 50)}} [react/view {:style {:justify-content :center}} - [react/touchable-opacity {:on-press on-close - :style {:padding-vertical 11 - :border-radius 44} - :accessibility-label :close-button} - [react/view {:style {:background-color colors/black-transparent-86 - :border-radius 20 - :width 40 - :height 40 - :justify-content :center - :align-items :center}} - [icons/icon :main-icons/close {:container-style {:width 24 - :height 24} - :color colors/white-persist}]]] + [react/touchable-opacity + {:on-press on-close + :style {:padding-vertical 11 + :border-radius 44} + :accessibility-label :close-button} + [react/view + {:style {:background-color colors/black-transparent-86 + :border-radius 20 + :width 40 + :height 40 + :justify-content :center + :align-items :center}} + [icons/icon :main-icons/close + {:container-style {:width 24 + :height 24} + :color colors/white-persist}]]] [header-options props]]])]) -(defn preview-image [{{:keys [content] :as message} :message - visible :visible - on-close :on-close}] - [:> image-viewing {:images #js [#js {:uri (:image content)}] - :on-request-close on-close - :hide-header-on-zoom false - :hide-footer-on-zoom false - :swipe-to-close-enabled platform/ios? - :presentation-style "overFullScreen" - :HeaderComponent #(reagent/as-element [header {:on-close on-close - :message message}]) - :FooterComponent #(reagent/as-element [:<>]) - :visible visible}]) +(defn preview-image + [{{:keys [content] :as message} :message + visible :visible + on-close :on-close}] + [:> image-viewing + {:images #js [#js {:uri (:image content)}] + :on-request-close on-close + :hide-header-on-zoom false + :hide-footer-on-zoom false + :swipe-to-close-enabled platform/ios? + :presentation-style "overFullScreen" + :HeaderComponent #(reagent/as-element [header + {:on-close on-close + :message message}]) + :FooterComponent #(reagent/as-element [:<>]) + :visible visible}]) diff --git a/src/status_im/ui/screens/chat/image/views.cljs b/src/status_im/ui/screens/chat/image/views.cljs index 7e5febe102..1869e5a0f9 100644 --- a/src/status_im/ui/screens/chat/image/views.cljs +++ b/src/status_im/ui/screens/chat/image/views.cljs @@ -1,18 +1,19 @@ (ns status-im.ui.screens.chat.image.views (:require-macros [status-im.utils.views :refer [defview letsubs]]) - (:require [status-im.ui.components.react :as react] + (:require [quo.components.animated.pressable :as pressable] + [quo.core :as quo] + [quo.design-system.colors :as colors] + [re-frame.core :as re-frame] + [reagent.core :as reagent] + [status-im.i18n.i18n :as i18n] [status-im.ui.components.icons.icons :as icons] [status-im.ui.components.permissions :as permissions] + [status-im.ui.components.react :as react] [status-im.utils.config :as config] - [reagent.core :as reagent] - [quo.components.animated.pressable :as pressable] - [re-frame.core :as re-frame] - [quo.design-system.colors :as colors] - [quo.core :as quo] - [status-im.utils.utils :as utils] - [status-im.i18n.i18n :as i18n])) + [status-im.utils.utils :as utils])) -(defn take-picture [] +(defn take-picture + [] (permissions/request-permissions {:permissions [:camera] :on-allowed #(re-frame/dispatch [:chat.ui/show-image-picker-camera]) @@ -22,7 +23,8 @@ (i18n/label :t/camera-access-error)) 50))})) -(defn show-image-picker [] +(defn show-image-picker + [] (permissions/request-permissions {:permissions [:read-external-storage :write-external-storage] :on-allowed #(re-frame/dispatch [:chat.ui/open-image-picker]) @@ -32,60 +34,70 @@ (i18n/label :t/external-storage-denied)) 50))})) -(defn buttons [] +(defn buttons + [] [react/view - [pressable/pressable {:type :scale - :accessibility-label :take-picture - :on-press take-picture} + [pressable/pressable + {:type :scale + :accessibility-label :take-picture + :on-press take-picture} [react/view {:style {:padding 10}} [icons/icon :main-icons/camera]]] [react/view {:style {:padding-top 8}} - [pressable/pressable {:on-press show-image-picker - :accessibility-label :open-gallery - :type :scale} + [pressable/pressable + {:on-press show-image-picker + :accessibility-label :open-gallery + :type :scale} [react/view {:style {:padding 10}} [icons/icon :main-icons/gallery]]]]]) -(defn image-preview [uri all-selected first? panel-height] +(defn image-preview + [uri all-selected first? panel-height] (let [wh (/ (- panel-height 8) 2) selected (get all-selected uri) max-selected (>= (count all-selected) config/max-images-batch)] - [react/touchable-highlight {:on-press #(if selected - (re-frame/dispatch [:chat.ui/image-unselected uri]) - (re-frame/dispatch [:chat.ui/camera-roll-pick uri])) - :disabled (and max-selected (not selected))} - [react/view {:style (merge {:width wh - :height wh - :border-radius 4 - :overflow :hidden} - (when (and (not selected) max-selected) - {:opacity 0.5}) - (when first? - {:margin-bottom 4}))} - [react/image {:style (merge {:width wh - :height wh - :background-color :black - :resize-mode :cover - :border-radius 4}) - :source {:uri uri}}] + [react/touchable-highlight + {:on-press #(if selected + (re-frame/dispatch [:chat.ui/image-unselected uri]) + (re-frame/dispatch [:chat.ui/camera-roll-pick uri])) + :disabled (and max-selected (not selected))} + [react/view + {:style (merge {:width wh + :height wh + :border-radius 4 + :overflow :hidden} + (when (and (not selected) max-selected) + {:opacity 0.5}) + (when first? + {:margin-bottom 4}))} + [react/image + {:style (merge {:width wh + :height wh + :background-color :black + :resize-mode :cover + :border-radius 4}) + :source {:uri uri}}] (when selected - [react/view {:style {:position :absolute - :top 0 - :bottom 0 - :left 0 - :right 0 - :padding 10 - :background-color (:highlight @colors/theme) - :align-items :flex-end}} + [react/view + {:style {:position :absolute + :top 0 + :bottom 0 + :left 0 + :right 0 + :padding 10 + :background-color (:highlight @colors/theme) + :align-items :flex-end}} [quo/radio {:value true}]])]])) -(defview photos [] +(defview photos + [] (letsubs [camera-roll-photos [:camera-roll/photos] - selected [:chats/sending-image] - panel-height (reagent/atom nil)] - [react/view {:style {:flex 1 - :flex-direction :row} - :on-layout #(reset! panel-height (.-nativeEvent.layout.height ^js %))} + selected [:chats/sending-image] + panel-height (reagent/atom nil)] + [react/view + {:style {:flex 1 + :flex-direction :row} + :on-layout #(reset! panel-height (.-nativeEvent.layout.height ^js %))} (let [height @panel-height] (for [[first-img second-img] (partition 2 camera-roll-photos)] ^{:key (str "image" first-img)} @@ -95,7 +107,8 @@ (when second-img [image-preview second-img selected false height])]))])) -(defview image-view [] +(defview image-view + [] {:component-did-mount (fn [] (permissions/request-permissions @@ -106,8 +119,9 @@ #(utils/show-popup (i18n/label :t/error) (i18n/label :t/external-storage-denied)) 50))}))} - [react/animated-view {:style {:background-color (:ui-background @colors/theme) - :flex 1}} + [react/animated-view + {:style {:background-color (:ui-background @colors/theme) + :flex 1}} [react/scroll-view {:horizontal true :style {:flex 1}} [react/view {:flex 1 :flex-direction :row :margin-horizontal 4} [buttons] diff --git a/src/status_im/ui/screens/chat/message/audio.cljs b/src/status_im/ui/screens/chat/message/audio.cljs index add702036b..5a3b59e604 100644 --- a/src/status_im/ui/screens/chat/message/audio.cljs +++ b/src/status_im/ui/screens/chat/message/audio.cljs @@ -1,17 +1,17 @@ (ns status-im.ui.screens.chat.message.audio (:require-macros [status-im.utils.views :refer [defview letsubs]]) - (:require [status-im.utils.utils :as utils] - [reagent.core :as reagent] + (:require ["react-native-blob-util" :default ReactNativeBlobUtil] [goog.string :as gstring] - [status-im.audio.core :as audio] - [status-im.ui.screens.chat.styles.message.audio :as style] - [status-im.ui.components.animation :as anim] [quo.design-system.colors :as colors] + [reagent.core :as reagent] + [status-im.audio.core :as audio] + [status-im.ui.components.animation :as anim] [status-im.ui.components.icons.icons :as icons] [status-im.ui.components.react :as react] [status-im.ui.components.slider :as slider] - ["react-native-blob-util" :default ReactNativeBlobUtil] + [status-im.ui.screens.chat.styles.message.audio :as style] [status-im.utils.platform :as platform] + [status-im.utils.utils :as utils] [taoensso.timbre :as log])) (defonce player-ref (atom nil)) @@ -19,7 +19,8 @@ (defonce current-active-state-ref-ref (atom nil)) (defonce progress-timer (atom nil)) -(defn start-stop-progress-timer [{:keys [state-ref progress-ref progress-anim]} start?] +(defn start-stop-progress-timer + [{:keys [state-ref progress-ref progress-anim]} start?] (when @progress-timer (utils/clear-interval @progress-timer) (when-not start? @@ -27,48 +28,61 @@ (when start? (when @progress-timer (utils/clear-interval @progress-timer)) - (reset! progress-timer (utils/set-interval - #(when (and @state-ref (not (:slider-seeking @state-ref))) - (let [ct (audio/get-player-current-time @player-ref)] - (reset! progress-ref ct) - (when ct - (anim/start (anim/timing progress-anim {:toValue @progress-ref - :duration 100 - :easing (.-linear ^js anim/easing) - :useNativeDriver true}))))) - 100)))) + (reset! progress-timer + (utils/set-interval + #(when (and @state-ref (not (:slider-seeking @state-ref))) + (let [ct (audio/get-player-current-time @player-ref)] + (reset! progress-ref ct) + (when ct + (anim/start (anim/timing progress-anim + {:toValue @progress-ref + :duration 100 + :easing (.-linear ^js anim/easing) + :useNativeDriver true}))))) + 100)))) -(defn update-state [{:keys [state-ref progress-ref progress-anim message-id seek-to-ms audio-duration-ms slider-new-state-seeking? unloaded? error]}] - (let [player-state (audio/get-state @player-ref) +(defn update-state + [{:keys [state-ref progress-ref progress-anim message-id seek-to-ms audio-duration-ms + slider-new-state-seeking? unloaded? error]}] + (let [player-state (audio/get-state @player-ref) slider-seeking (if (some? slider-new-state-seeking?) slider-new-state-seeking? (:slider-seeking @state-ref)) - general (cond - (some? error) :error - (or unloaded? (not= message-id @current-player-message-id)) :not-loaded - slider-seeking (:general @state-ref) ; persist player state at the time user started sliding - (= player-state audio/PLAYING) :playing - (= player-state audio/PAUSED) :paused - (= player-state audio/SEEKING) :seeking - (= player-state audio/PREPARED) :ready-to-play - :else :preparing) - new-state {:general general - :error-msg error - :duration (cond (not (#{:preparing :not-loaded :error} general)) - (audio/get-player-duration @player-ref) + general (cond + (some? error) :error + (or unloaded? (not= message-id @current-player-message-id)) :not-loaded + slider-seeking (:general + @state-ref) ; persist + ; player + ; state + ; at + ; the + ; time + ; user + ; started + ; sliding + (= player-state audio/PLAYING) :playing + (= player-state audio/PAUSED) :paused + (= player-state audio/SEEKING) :seeking + (= player-state audio/PREPARED) :ready-to-play + :else :preparing) + new-state {:general general + :error-msg error + :duration (cond (not (#{:preparing :not-loaded :error} general)) + (audio/get-player-duration @player-ref) - audio-duration-ms audio-duration-ms + audio-duration-ms audio-duration-ms - :else (:duration @state-ref)) - :progress-ref (or progress-ref (:progress-ref @state-ref)) - :progress-anim (or progress-anim (:progress-anim @state-ref)) - :slider-seeking slider-seeking + :else (:duration @state-ref)) + :progress-ref (or progress-ref (:progress-ref @state-ref)) + :progress-anim (or progress-anim (:progress-anim @state-ref)) + :slider-seeking slider-seeking - ; persist seek-to-ms while seeking or audio is not loaded - :seek-to-ms (when (or - slider-seeking - (#{:preparing :not-loaded :error} general)) - (or seek-to-ms (:seek-to-ms @state-ref)))}] + ; persist seek-to-ms while seeking or audio is not loaded + :seek-to-ms (when (or + slider-seeking + (#{:preparing :not-loaded :error} general)) + (or seek-to-ms (:seek-to-ms @state-ref)))}] ; update state if needed (when (not= @state-ref new-state) (reset! state-ref new-state)) @@ -86,9 +100,11 @@ (reset! (:progress-ref new-state) 0) (anim/set-value (:progress-anim new-state) 0)))) -(defn destroy-player [{:keys [message-id reloading?]}] - (when (and @player-ref (or reloading? - (= message-id @current-player-message-id))) +(defn destroy-player + [{:keys [message-id reloading?]}] + (when (and @player-ref + (or reloading? + (= message-id @current-player-message-id))) (audio/destroy-player @player-ref) (reset! player-ref nil) (when @current-active-state-ref-ref @@ -98,7 +114,8 @@ (defonce last-seek (atom (js/Date.now))) -(defn seek [{:keys [message-id] :as params} value immediate? on-success] +(defn seek + [{:keys [message-id] :as params} value immediate? on-success] (when (and @player-ref (= message-id @current-player-message-id)) (let [now (js/Date.now)] (when (or immediate? (> (- now @last-seek) 200)) @@ -112,13 +129,15 @@ #(update-state (merge params {:error (:message %)})))))) (update-state (merge params {:seek-to-ms value}))) -(defn download-audio-http [base64-uri on-success] +(defn download-audio-http + [base64-uri on-success] (-> (.config ReactNativeBlobUtil (clj->js {:trusty platform/ios?})) (.fetch "GET" (str base64-uri)) (.then #(on-success (.base64 ^js %))) (.catch #(log/error "could not fetch audio")))) -(defn reload-player [{:keys [message-id state-ref] :as params} audio-url on-success] +(defn reload-player + [{:keys [message-id state-ref] :as params} audio-url on-success] ;; to avoid reloading player while is initializing, ;; we go ahead only if there is no player or ;; if it is already prepared @@ -130,7 +149,7 @@ (fn [base64-data] (reset! player-ref (audio/new-player (str "data:audio/acc;base64," base64-data) - {:autoDestroy false + {:autoDestroy false :continuesToPlayInBackground false} #(seek params 0 true nil))) (audio/prepare-player @@ -141,7 +160,8 @@ (reset! current-active-state-ref-ref state-ref) (update-state params))))) -(defn play-pause [{:keys [message-id state-ref] :as params} audio] +(defn play-pause + [{:keys [message-id state-ref] :as params} audio] (if (not= message-id @current-player-message-id) ;; player has audio from another message, we need to reload (reload-player params @@ -170,31 +190,34 @@ (update-state params)) #(update-state (merge params {:error (:message %)})))))) -(defn- play-pause-button [state-ref on-press] +(defn- play-pause-button + [state-ref on-press] (let [color colors/blue] - (if (= (:general @state-ref) :preparing) - [react/view {:style (style/play-pause-container true)} + (if (= (:general @state-ref) :preparing) + [react/view {:style (style/play-pause-container true)} [react/small-loading-indicator color]] [react/touchable-highlight {:on-press on-press} - [icons/icon (case (:general @state-ref) - :playing :main-icons/pause - :main-icons/play) + [icons/icon + (case (:general @state-ref) + :playing :main-icons/pause + :main-icons/play) {:container-style (style/play-pause-container false) :accessibility-label :play-pause-audio-message-button :color color}]]))) -(defview message-content [{:keys [audio audio-duration-ms message-id]}] - (letsubs [state (reagent/atom nil) - progress (reagent/atom 0) +(defview message-content + [{:keys [audio audio-duration-ms message-id]}] + (letsubs [state (reagent/atom nil) + progress (reagent/atom 0) progress-anim (anim/create-value 0) - width [:dimensions/window-width]] - {:component-did-mount (fn [] - (update-state {:state-ref state - :audio-duration-ms audio-duration-ms - :message-id message-id - :unloaded? true - :progress-ref progress - :progress-anim progress-anim})) + width [:dimensions/window-width]] + {:component-did-mount (fn [] + (update-state {:state-ref state + :audio-duration-ms audio-duration-ms + :message-id message-id + :unloaded? true + :progress-ref progress + :progress-anim progress-anim})) :component-will-unmount (fn [] (destroy-player {:state-ref state :message-id message-id}) (when (= @current-player-message-id message-id) @@ -202,27 +225,38 @@ (reset! current-player-message-id nil)) (reset! state nil))} - (let [base-params {:state-ref state :message-id message-id :progress-ref progress :progress-anim progress-anim}] + (let [base-params {:state-ref state + :message-id message-id + :progress-ref progress + :progress-anim progress-anim}] (if (= (:general @state) :error) - [react/text {:style {:typography :main-medium - :margin-bottom 16}} (:error-msg @state)] + [react/text + {:style {:typography :main-medium + :margin-bottom 16}} (:error-msg @state)] [react/view (style/container width) [react/view style/play-pause-slider-container [play-pause-button state #(play-pause base-params audio)] [react/view style/slider-container - [slider/animated-slider (merge (style/slider) - {:minimum-value 0 - :maximum-value (:duration @state) - :value progress-anim - :on-value-change #(seek base-params % false nil) - :on-sliding-start #(seek (merge base-params {:slider-new-state-seeking? true}) % true nil) - :on-sliding-complete #(seek (merge base-params {:slider-new-state-seeking? false}) % true nil)})]]] + [slider/animated-slider + (merge (style/slider) + {:minimum-value 0 + :maximum-value (:duration @state) + :value progress-anim + :on-value-change #(seek base-params % false nil) + :on-sliding-start #(seek (merge base-params {:slider-new-state-seeking? true}) + % + true + nil) + :on-sliding-complete #(seek (merge base-params {:slider-new-state-seeking? false}) + % + true + nil)})]]] [react/view style/times-container - [react/text {:style (style/timestamp)} + [react/text {:style (style/timestamp)} (let [time (cond (or (:slider-seeking @state) (> (:seek-to-ms @state) 0)) (:seek-to-ms @state) - (#{:playing :paused :seeking} (:general @state)) @progress - :else (:duration @state)) - s (quot time 1000)] + (#{:playing :paused :seeking} (:general @state)) @progress + :else (:duration @state)) + s (quot time 1000)] (gstring/format "%02d:%02d" (quot s 60) (mod s 60)))]]])))) diff --git a/src/status_im/ui/screens/chat/message/audio_old.cljs b/src/status_im/ui/screens/chat/message/audio_old.cljs index a0612a5762..97bd62b740 100644 --- a/src/status_im/ui/screens/chat/message/audio_old.cljs +++ b/src/status_im/ui/screens/chat/message/audio_old.cljs @@ -1,18 +1,18 @@ (ns status-im.ui.screens.chat.message.audio-old (:require-macros [status-im.utils.views :refer [defview letsubs]]) - (:require [status-im.utils.utils :as utils] - [reagent.core :as reagent] + (:require ["react-native-blob-util" :default ReactNativeBlobUtil] [goog.string :as gstring] - [status-im.audio.core :as audio] - [status-im.utils.fx :as fx] - [status-im.ui.screens.chat.styles.message.audio-old :as style] - [status-im.ui.components.animation :as anim] [quo.design-system.colors :as colors] + [reagent.core :as reagent] + [status-im.audio.core :as audio] + [status-im.ui.components.animation :as anim] [status-im.ui.components.icons.icons :as icons] [status-im.ui.components.react :as react] [status-im.ui.components.slider :as slider] - ["react-native-blob-util" :default ReactNativeBlobUtil] + [status-im.ui.screens.chat.styles.message.audio-old :as style] + [status-im.utils.fx :as fx] [status-im.utils.platform :as platform] + [status-im.utils.utils :as utils] [taoensso.timbre :as log])) (defonce player-ref (atom nil)) @@ -20,7 +20,8 @@ (defonce current-active-state-ref-ref (atom nil)) (defonce progress-timer (atom nil)) -(defn start-stop-progress-timer [{:keys [state-ref progress-ref progress-anim]} start?] +(defn start-stop-progress-timer + [{:keys [state-ref progress-ref progress-anim]} start?] (when @progress-timer (utils/clear-interval @progress-timer) (when-not start? @@ -28,48 +29,61 @@ (when start? (when @progress-timer (utils/clear-interval @progress-timer)) - (reset! progress-timer (utils/set-interval - #(when (and @state-ref (not (:slider-seeking @state-ref))) - (let [ct (audio/get-player-current-time @player-ref)] - (reset! progress-ref ct) - (when ct - (anim/start (anim/timing progress-anim {:toValue @progress-ref - :duration 100 - :easing (.-linear ^js anim/easing) - :useNativeDriver true}))))) - 100)))) + (reset! progress-timer + (utils/set-interval + #(when (and @state-ref (not (:slider-seeking @state-ref))) + (let [ct (audio/get-player-current-time @player-ref)] + (reset! progress-ref ct) + (when ct + (anim/start (anim/timing progress-anim + {:toValue @progress-ref + :duration 100 + :easing (.-linear ^js anim/easing) + :useNativeDriver true}))))) + 100)))) -(defn update-state [{:keys [state-ref progress-ref progress-anim message-id seek-to-ms audio-duration-ms slider-new-state-seeking? unloaded? error]}] - (let [player-state (audio/get-state @player-ref) +(defn update-state + [{:keys [state-ref progress-ref progress-anim message-id seek-to-ms audio-duration-ms + slider-new-state-seeking? unloaded? error]}] + (let [player-state (audio/get-state @player-ref) slider-seeking (if (some? slider-new-state-seeking?) slider-new-state-seeking? (:slider-seeking @state-ref)) - general (cond - (some? error) :error - (or unloaded? (not= message-id @current-player-message-id)) :not-loaded - slider-seeking (:general @state-ref) ; persist player state at the time user started sliding - (= player-state audio/PLAYING) :playing - (= player-state audio/PAUSED) :paused - (= player-state audio/SEEKING) :seeking - (= player-state audio/PREPARED) :ready-to-play - :else :preparing) - new-state {:general general - :error-msg error - :duration (cond (not (#{:preparing :not-loaded :error} general)) - (audio/get-player-duration @player-ref) + general (cond + (some? error) :error + (or unloaded? (not= message-id @current-player-message-id)) :not-loaded + slider-seeking (:general + @state-ref) ; persist + ; player + ; state + ; at + ; the + ; time + ; user + ; started + ; sliding + (= player-state audio/PLAYING) :playing + (= player-state audio/PAUSED) :paused + (= player-state audio/SEEKING) :seeking + (= player-state audio/PREPARED) :ready-to-play + :else :preparing) + new-state {:general general + :error-msg error + :duration (cond (not (#{:preparing :not-loaded :error} general)) + (audio/get-player-duration @player-ref) - audio-duration-ms audio-duration-ms + audio-duration-ms audio-duration-ms - :else (:duration @state-ref)) - :progress-ref (or progress-ref (:progress-ref @state-ref)) - :progress-anim (or progress-anim (:progress-anim @state-ref)) - :slider-seeking slider-seeking + :else (:duration @state-ref)) + :progress-ref (or progress-ref (:progress-ref @state-ref)) + :progress-anim (or progress-anim (:progress-anim @state-ref)) + :slider-seeking slider-seeking - ; persist seek-to-ms while seeking or audio is not loaded - :seek-to-ms (when (or - slider-seeking - (#{:preparing :not-loaded :error} general)) - (or seek-to-ms (:seek-to-ms @state-ref)))}] + ; persist seek-to-ms while seeking or audio is not loaded + :seek-to-ms (when (or + slider-seeking + (#{:preparing :not-loaded :error} general)) + (or seek-to-ms (:seek-to-ms @state-ref)))}] ; update state if needed (when (not= @state-ref new-state) (reset! state-ref new-state)) @@ -87,9 +101,11 @@ (reset! (:progress-ref new-state) 0) (anim/set-value (:progress-anim new-state) 0)))) -(defn destroy-player [{:keys [message-id reloading?]}] - (when (and @player-ref (or reloading? - (= message-id @current-player-message-id))) +(defn destroy-player + [{:keys [message-id reloading?]}] + (when (and @player-ref + (or reloading? + (= message-id @current-player-message-id))) (audio/destroy-player @player-ref) (reset! player-ref nil) (when @current-active-state-ref-ref @@ -99,7 +115,8 @@ (defonce last-seek (atom (js/Date.now))) -(defn seek [{:keys [message-id] :as params} value immediate? on-success] +(defn seek + [{:keys [message-id] :as params} value immediate? on-success] (when (and @player-ref (= message-id @current-player-message-id)) (let [now (js/Date.now)] (when (or immediate? (> (- now @last-seek) 200)) @@ -113,13 +130,15 @@ #(update-state (merge params {:error (:message %)})))))) (update-state (merge params {:seek-to-ms value}))) -(defn download-audio-http [base64-uri on-success] +(defn download-audio-http + [base64-uri on-success] (-> (.config ReactNativeBlobUtil (clj->js {:trusty platform/ios?})) (.fetch "GET" (str base64-uri)) (.then #(on-success (.base64 ^js %))) (.catch #(log/error "could not fetch audio")))) -(defn reload-player [{:keys [message-id state-ref] :as params} audio-url on-success] +(defn reload-player + [{:keys [message-id state-ref] :as params} audio-url on-success] ;; to avoid reloading player while is initializing, ;; we go ahead only if there is no player or ;; if it is already prepared @@ -131,7 +150,7 @@ (fn [base64-data] (reset! player-ref (audio/new-player (str "data:audio/acc;base64," base64-data) - {:autoDestroy false + {:autoDestroy false :continuesToPlayInBackground false} #(seek params 0 true nil))) (audio/prepare-player @@ -142,7 +161,8 @@ (reset! current-active-state-ref-ref state-ref) (update-state params))))) -(defn play-pause [{:keys [message-id state-ref] :as params} audio] +(defn play-pause + [{:keys [message-id state-ref] :as params} audio] (if (not= message-id @current-player-message-id) ;; player has audio from another message, we need to reload (reload-player params @@ -171,15 +191,17 @@ (update-state params)) #(update-state (merge params {:error (:message %)})))))) -(defn- play-pause-button [state-ref outgoing on-press] +(defn- play-pause-button + [state-ref outgoing on-press] (let [color (if outgoing colors/blue colors/white-persist)] - (if (= (:general @state-ref) :preparing) - [react/view {:style (style/play-pause-container outgoing true)} + (if (= (:general @state-ref) :preparing) + [react/view {:style (style/play-pause-container outgoing true)} [react/small-loading-indicator color]] [react/touchable-highlight {:on-press on-press} - [icons/icon (case (:general @state-ref) - :playing :main-icons/pause - :main-icons/play) + [icons/icon + (case (:general @state-ref) + :playing :main-icons/pause + :main-icons/play) {:container-style (style/play-pause-container outgoing false) :accessibility-label :play-pause-audio-message-button :color color}]]))) @@ -189,22 +211,23 @@ [_] (when (and @current-active-state-ref-ref @@current-active-state-ref-ref) - (update-state {:state-ref @current-active-state-ref-ref + (update-state {:state-ref @current-active-state-ref-ref :message-id @current-player-message-id})) nil) -(defview message-content [{:keys [audio audio-duration-ms message-id outgoing]}] - (letsubs [state (reagent/atom nil) - progress (reagent/atom 0) +(defview message-content + [{:keys [audio audio-duration-ms message-id outgoing]}] + (letsubs [state (reagent/atom nil) + progress (reagent/atom 0) progress-anim (anim/create-value 0) - width [:dimensions/window-width]] - {:component-did-mount (fn [] - (update-state {:state-ref state - :audio-duration-ms audio-duration-ms - :message-id message-id - :unloaded? true - :progress-ref progress - :progress-anim progress-anim})) + width [:dimensions/window-width]] + {:component-did-mount (fn [] + (update-state {:state-ref state + :audio-duration-ms audio-duration-ms + :message-id message-id + :unloaded? true + :progress-ref progress + :progress-anim progress-anim})) :component-will-unmount (fn [] (destroy-player {:state-ref state :message-id message-id}) (when (= @current-player-message-id message-id) @@ -212,27 +235,38 @@ (reset! current-player-message-id nil)) (reset! state nil))} - (let [base-params {:state-ref state :message-id message-id :progress-ref progress :progress-anim progress-anim}] + (let [base-params {:state-ref state + :message-id message-id + :progress-ref progress + :progress-anim progress-anim}] (if (= (:general @state) :error) - [react/text {:style {:typography :main-medium - :margin-bottom 16}} (:error-msg @state)] + [react/text + {:style {:typography :main-medium + :margin-bottom 16}} (:error-msg @state)] [react/view (style/container width) [react/view style/play-pause-slider-container [play-pause-button state outgoing #(play-pause base-params audio)] [react/view style/slider-container - [slider/animated-slider (merge (style/slider outgoing) - {:minimum-value 0 - :maximum-value (:duration @state) - :value progress-anim - :on-value-change #(seek base-params % false nil) - :on-sliding-start #(seek (merge base-params {:slider-new-state-seeking? true}) % true nil) - :on-sliding-complete #(seek (merge base-params {:slider-new-state-seeking? false}) % true nil)})]]] + [slider/animated-slider + (merge (style/slider outgoing) + {:minimum-value 0 + :maximum-value (:duration @state) + :value progress-anim + :on-value-change #(seek base-params % false nil) + :on-sliding-start #(seek (merge base-params {:slider-new-state-seeking? true}) + % + true + nil) + :on-sliding-complete #(seek (merge base-params {:slider-new-state-seeking? false}) + % + true + nil)})]]] [react/view style/times-container - [react/text {:style (style/timestamp outgoing)} + [react/text {:style (style/timestamp outgoing)} (let [time (cond (or (:slider-seeking @state) (> (:seek-to-ms @state) 0)) (:seek-to-ms @state) - (#{:playing :paused :seeking} (:general @state)) @progress - :else (:duration @state)) - s (quot time 1000)] + (#{:playing :paused :seeking} (:general @state)) @progress + :else (:duration @state)) + s (quot time 1000)] (gstring/format "%02d:%02d" (quot s 60) (mod s 60)))]]])))) diff --git a/src/status_im/ui/screens/chat/message/command.cljs b/src/status_im/ui/screens/chat/message/command.cljs index 02813eb396..253fc86b5c 100644 --- a/src/status_im/ui/screens/chat/message/command.cljs +++ b/src/status_im/ui/screens/chat/message/command.cljs @@ -1,42 +1,45 @@ (ns status-im.ui.screens.chat.message.command - (:require [re-frame.core :as re-frame] + (:require [quo.design-system.colors :as colors] + [re-frame.core :as re-frame] [status-im.commands.core :as commands] [status-im.constants :as constants] [status-im.ethereum.transactions.core :as transactions] [status-im.i18n.i18n :as i18n] [status-im.ui.components.chat-icon.screen :as chat-icon] - [quo.design-system.colors :as colors] [status-im.ui.components.icons.icons :as icons] [status-im.ui.components.react :as react] [status-im.utils.money :as money] [status-im.utils.utils :as utils])) -(defn- final-status? [command-state] +(defn- final-status? + [command-state] (or (= command-state constants/command-state-request-address-for-transaction-declined) (= command-state constants/command-state-request-transaction-declined) (= command-state constants/command-state-transaction-sent))) (defn- command-pending-status [command-state direction to transaction-type] - [react/view {:style {:flex-direction :row - :height 28 - :align-items :center - :border-width 1 - :border-color colors/gray-lighter - :border-radius 16 - :padding-horizontal 8 - :margin-right 12 - :margin-bottom 2}} + [react/view + {:style {:flex-direction :row + :height 28 + :align-items :center + :border-width 1 + :border-color colors/gray-lighter + :border-radius 16 + :padding-horizontal 8 + :margin-right 12 + :margin-bottom 2}} [icons/icon :tiny-icons/tiny-pending - {:width 16 - :height 16 - :color colors/gray + {:width 16 + :height 16 + :color colors/gray :container-style {:margin-right 6}}] - [react/text {:style {:color colors/gray - :font-weight "500" - :line-height 16 - :margin-right 4 - :font-size 13}} + [react/text + {:style {:color colors/gray + :font-weight "500" + :line-height 16 + :margin-right 4 + :font-size 13}} (if (and (or (= command-state constants/command-state-request-transaction) (= command-state constants/command-state-request-address-for-transaction-accepted)) (= direction :incoming)) @@ -55,65 +58,70 @@ (= command-state constants/command-state-transaction-sent) (case transaction-type :pending :t/status-pending - :failed :t/transaction-failed + :failed :t/transaction-failed :t/status-confirmed) (= command-state constants/command-state-request-transaction) :t/address-received)))]]) (defn- command-final-status [command-state _ transaction-type] - [react/view {:style {:flex-direction :row - :height 28 - :align-items :center - :border-width 1 - :border-color colors/gray-lighter - :border-radius 16 - :padding-horizontal 8 - :margin-right 12 - :margin-bottom 2}} + [react/view + {:style {:flex-direction :row + :height 28 + :align-items :center + :border-width 1 + :border-color colors/gray-lighter + :border-radius 16 + :padding-horizontal 8 + :margin-right 12 + :margin-bottom 2}} (if (or (= command-state constants/command-state-request-address-for-transaction-declined) (= command-state constants/command-state-request-transaction-declined) (= :failed transaction-type)) [icons/icon :tiny-icons/tiny-warning-background - {:width 16 - :height 16 + {:width 16 + :height 16 :container-style {:margin-right 6}}] (if (= :pending transaction-type) [icons/icon :tiny-icons/tiny-pending - {:color colors/gray - :width 16 - :height 16 + {:color colors/gray + :width 16 + :height 16 :container-style {:margin-right 6}}] [icons/icon :tiny-icons/tiny-check - {:width 16 - :height 16 + {:width 16 + :height 16 :container-style {:margin-right 6}}])) - [react/text {:style (merge {:margin-right 4 - :line-height 16 - :font-size 13} - (if (= transaction-type :pending) - {:color colors/gray} - {:font-weight "500"}))} - (i18n/label (if (or (= command-state constants/command-state-request-address-for-transaction-declined) - (= command-state constants/command-state-request-transaction-declined)) - :t/transaction-declined - (case transaction-type - :pending :t/status-pending - :failed :t/transaction-failed - :t/status-confirmed)))]]) + [react/text + {:style (merge {:margin-right 4 + :line-height 16 + :font-size 13} + (if (= transaction-type :pending) + {:color colors/gray} + {:font-weight "500"}))} + (i18n/label + (if (or (= command-state constants/command-state-request-address-for-transaction-declined) + (= command-state constants/command-state-request-transaction-declined)) + :t/transaction-declined + (case transaction-type + :pending :t/status-pending + :failed :t/transaction-failed + :t/status-confirmed)))]]) (defn- command-status-and-timestamp [command-state direction to timestamp-str transaction-type] - [react/view {:style {:flex-direction :row - :align-items :flex-end - :justify-content :space-between}} + [react/view + {:style {:flex-direction :row + :align-items :flex-end + :justify-content :space-between}} (if (final-status? command-state) [command-final-status command-state direction transaction-type] [command-pending-status command-state direction to transaction-type]) - [react/text {:style {:font-size 10 - :line-height 12 - :text-align-vertical :bottom - :color colors/gray}} + [react/text + {:style {:font-size 10 + :line-height 12 + :text-align-vertical :bottom + :color colors/gray}} timestamp-str]]) (defn- command-actions @@ -122,29 +130,31 @@ [react/touchable-highlight {:on-press #(do (react/dismiss-keyboard!) (on-accept)) - :style {:border-color colors/gray-lighter - :border-top-width 1 - :margin-top 8 - :margin-horizontal -12 - :padding-horizontal 15 - :padding-vertical 10}} - [react/text {:style {:text-align :center - :color colors/blue - :font-weight "500" - :font-size 15 - :line-height 22}} + :style {:border-color colors/gray-lighter + :border-top-width 1 + :margin-top 8 + :margin-horizontal -12 + :padding-horizontal 15 + :padding-vertical 10}} + [react/text + {:style {:text-align :center + :color colors/blue + :font-weight "500" + :font-size 15 + :line-height 22}} (i18n/label accept-label)]] (when on-decline [react/touchable-highlight {:on-press on-decline - :style {:border-color colors/gray-lighter - :border-top-width 1 - :margin-horizontal -12 - :padding-top 10}} - [react/text {:style {:text-align :center - :color colors/blue - :font-size 15 - :line-height 22}} + :style {:border-color colors/gray-lighter + :border-top-width 1 + :margin-horizontal -12 + :padding-top 10}} + [react/text + {:style {:text-align :center + :color colors/blue + :font-size 15 + :line-height 22}} (i18n/label :t/decline)]])]) (defn- command-transaction-info @@ -161,43 +171,52 @@ @(re-frame/subscribe [:wallet/currency]) prices @(re-frame/subscribe [:prices]) amount-fiat (money/fiat-amount-value amount symbol (keyword code) prices)] - [react/view {:style {:flex-direction :row - :margin-top 8 - :margin-bottom 12}} + [react/view + {:style {:flex-direction :row + :margin-top 8 + :margin-bottom 12}} (if icon - [react/image (-> icon - (assoc-in [:style :height] 24) - (assoc-in [:style :width] 24))] - [react/view {:style {:margin-right 14 - :padding-vertical 2 - :justify-content :flex-start - :max-width 40 - :align-items :center - :align-self :stretch}} + [react/image + (-> icon + (assoc-in [:style :height] 24) + (assoc-in [:style :width] 24))] + [react/view + {:style {:margin-right 14 + :padding-vertical 2 + :justify-content :flex-start + :max-width 40 + :align-items :center + :align-self :stretch}} [chat-icon/custom-icon-view-list (:name token) color 24]]) [react/view {:style {:margin-left 6}} - [react/text {:style {:margin-bottom 2 - :font-size 20 - :line-height 24}} + [react/text + {:style {:margin-bottom 2 + :font-size 20 + :line-height 24}} (str (money/to-fixed amount) " " (name symbol))] - [react/text {:style {:font-size 12 - :line-height 16 - :color colors/gray}} + [react/text + {:style {:font-size 12 + :line-height 16 + :color colors/gray}} (str amount-fiat " " code)]]])) -(defn calculate-direction [outgoing command-state] +(defn calculate-direction + [outgoing command-state] (if (#{constants/command-state-request-address-for-transaction-accepted constants/command-state-request-address-for-transaction-declined - constants/command-state-request-transaction} command-state) + constants/command-state-request-transaction} + command-state) (if outgoing :incoming :outgoing) (if outgoing :outgoing :incoming))) (defn command-content - [wrapper {:keys [message-id - chat-id - outgoing - command-parameters - timestamp-str] :as message}] + [wrapper + {:keys [message-id + chat-id + outgoing + command-parameters + timestamp-str] + :as message}] (let [{:keys [contract value address command-state transaction-hash]} command-parameters direction (calculate-direction outgoing command-state) transaction (when transaction-hash @@ -209,21 +228,24 @@ {:on-press #(when (:address transaction) (re-frame/dispatch [:wallet.ui/show-transaction-details transaction-hash (:address transaction)]))} - [react/view {:padding-horizontal 12 - :padding-bottom 10 - :padding-top 10 - :margin-top 4 - :border-width 1 - :border-color colors/gray-lighter - :border-radius 16 - (case direction - :outgoing :border-bottom-right-radius - :incoming :border-bottom-left-radius) 4 - :background-color colors/white} - [react/text {:style {:font-size 13 - :line-height 18 - :font-weight "500" - :color colors/gray}} + [react/view + {:padding-horizontal 12 + :padding-bottom 10 + :padding-top 10 + :margin-top 4 + :border-width 1 + :border-color colors/gray-lighter + :border-radius 16 + (case direction + :outgoing :border-bottom-right-radius + :incoming :border-bottom-left-radius) + 4 + :background-color colors/white} + [react/text + {:style {:font-size 13 + :line-height 18 + :font-weight "500" + :color colors/gray}} (case direction :outgoing (str "↑ " (i18n/label :t/outgoing-transaction)) :incoming (str "↓ " (i18n/label :t/incoming-transaction)))] diff --git a/src/status_im/ui/screens/chat/message/datemark.cljs b/src/status_im/ui/screens/chat/message/datemark.cljs index 8db3f0587e..731554638f 100644 --- a/src/status_im/ui/screens/chat/message/datemark.cljs +++ b/src/status_im/ui/screens/chat/message/datemark.cljs @@ -1,17 +1,19 @@ (ns status-im.ui.screens.chat.message.datemark - (:require [status-im.ui.components.react :as react] - [clojure.string :as string] - [status-im.ui.screens.chat.styles.message.datemark :as style] + (:require [clojure.string :as string] [quo2.components.markdown.text :as quo2.text] - [quo2.foundations.colors :as quo2.colors])) + [quo2.foundations.colors :as quo2.colors] + [status-im.ui.components.react :as react] + [status-im.ui.screens.chat.styles.message.datemark :as style])) -(defn chat-datemark [value] +(defn chat-datemark + [value] [react/touchable-without-feedback {:on-press (fn [_] (react/dismiss-keyboard!))} [react/view style/datemark-mobile - [quo2.text/text {:weight :medium - :accessibility-label :message-datemark-text - :style {:color quo2.colors/neutral-50}} + [quo2.text/text + {:weight :medium + :accessibility-label :message-datemark-text + :style {:color quo2.colors/neutral-50}} (string/capitalize value)] [react/view (style/divider)]]]) diff --git a/src/status_im/ui/screens/chat/message/datemark_old.cljs b/src/status_im/ui/screens/chat/message/datemark_old.cljs index 4d8e206897..26f21b110b 100644 --- a/src/status_im/ui/screens/chat/message/datemark_old.cljs +++ b/src/status_im/ui/screens/chat/message/datemark_old.cljs @@ -1,9 +1,10 @@ (ns status-im.ui.screens.chat.message.datemark-old - (:require [status-im.ui.components.react :as react] - [clojure.string :as string] + (:require [clojure.string :as string] + [status-im.ui.components.react :as react] [status-im.ui.screens.chat.styles.message.datemark-old :as style])) -(defn chat-datemark [value] +(defn chat-datemark + [value] [react/touchable-without-feedback {:on-press (fn [_] (react/dismiss-keyboard!))} diff --git a/src/status_im/ui/screens/chat/message/gap.cljs b/src/status_im/ui/screens/chat/message/gap.cljs index 68db77a5ad..230dd16e7a 100644 --- a/src/status_im/ui/screens/chat/message/gap.cljs +++ b/src/status_im/ui/screens/chat/message/gap.cljs @@ -1,11 +1,11 @@ (ns status-im.ui.screens.chat.message.gap (:require-macros [status-im.utils.views :as views]) - (:require [status-im.ui.components.react :as react] + (:require [quo2.core :as quo2] [re-frame.core :as re-frame] [status-im.i18n.i18n :as i18n] - [status-im.utils.datetime :as datetime] + [status-im.ui.components.react :as react] [status-im.ui.screens.chat.styles.input.gap :as style] - [quo2.core :as quo2])) + [status-im.utils.datetime :as datetime])) (defn on-press [chat-id gap-ids] @@ -14,13 +14,13 @@ (views/defview gap [{:keys [gap-ids chat-id gap-parameters public? community?]}] - (views/letsubs [in-progress? [:chats/fetching-gap-in-progress? - gap-ids - chat-id] - connected? [:mailserver/connected?] + (views/letsubs [in-progress? [:chats/fetching-gap-in-progress? + gap-ids + chat-id] + connected? [:mailserver/connected?] use-status-nodes? [:mailserver/use-status-nodes?] - first-gap? (= gap-ids #{:first-gap}) - window-height [:dimensions/window-height]] + first-gap? (= gap-ids #{:first-gap}) + window-height [:dimensions/window-height]] (when (or (not first-gap?) public? community?) [react/view {:style (when-not in-progress? style/gap-container)} [react/touchable-highlight diff --git a/src/status_im/ui/screens/chat/message/link_preview.cljs b/src/status_im/ui/screens/chat/message/link_preview.cljs index 2f170d8294..ec7caac776 100644 --- a/src/status_im/ui/screens/chat/message/link_preview.cljs +++ b/src/status_im/ui/screens/chat/message/link_preview.cljs @@ -2,76 +2,89 @@ (:require [clojure.string :as string] [quo.core :as quo] [re-frame.core :as re-frame] - [status-im.ui.components.react :as react] - [utils.security.core :as security] - [status-im.i18n.i18n :as i18n] - [status-im.ui.screens.chat.message.styles :as styles] - [status-im.react-native.resources :as resources] [status-im.chat.models.link-preview :as link-preview] + [status-im.constants :as constants] + [status-im.i18n.i18n :as i18n] + [status-im.react-native.resources :as resources] + [status-im.ui.components.react :as react] + [status-im.ui.screens.chat.message.styles :as styles] [status-im.ui.screens.communities.icon :as communities.icon] - [status-im.constants :as constants]) + [utils.security.core :as security]) (:require-macros [status-im.utils.views :refer [defview letsubs]])) -(defn link-belongs-to-domain [link domain] +(defn link-belongs-to-domain + [link domain] (cond - (string/starts-with? link (str "https://" domain)) true + (string/starts-with? link (str "https://" domain)) true (string/starts-with? link (str "https://www." domain)) true - :else false)) + :else false)) -(defn community-id-from-link [link] +(defn community-id-from-link + [link] (nth (re-find constants/regx-community-universal-link link) 4)) -(defn domain-info-if-whitelisted [link whitelist] +(defn domain-info-if-whitelisted + [link whitelist] (first (filter #(link-belongs-to-domain link (:address %)) whitelist))) -(defn link-extended-info [link whitelist enabled-list] - (let [domain-info (domain-info-if-whitelisted link whitelist) +(defn link-extended-info + [link whitelist enabled-list] + (let [domain-info (domain-info-if-whitelisted link whitelist) community-id (community-id-from-link link)] (if-not community-id {:whitelisted (not (nil? domain-info)) - :enabled (contains? enabled-list (:title domain-info)) - :link link} + :enabled (contains? enabled-list (:title domain-info)) + :link link} {:whitelisted true - :enabled true - :link link - :community true}))) + :enabled true + :link link + :community true}))) -(defn previewable-link [links whitelist enabled-list] +(defn previewable-link + [links whitelist enabled-list] (->> links (map #(link-extended-info % whitelist enabled-list)) (filter #(:whitelisted %)) (first))) -(defview link-preview-enable-request [] +(defview link-preview-enable-request + [] [react/view (styles/link-preview-request-wrapper) [react/view {:margin 12} - [react/image {:source (resources/get-theme-image :unfurl) - :style styles/link-preview-request-image}] - [quo/text {:size :small - :align :center - :style {:margin-top 6}} + [react/image + {:source (resources/get-theme-image :unfurl) + :style styles/link-preview-request-image}] + [quo/text + {:size :small + :align :center + :style {:margin-top 6}} (i18n/label :t/enable-link-previews)] - [quo/text {:size :small - :color :secondary - :align :center - :style {:margin-top 2}} + [quo/text + {:size :small + :color :secondary + :align :center + :style {:margin-top 2}} (i18n/label :t/once-enabled-share-metadata)]] [quo/separator] - [quo/button {:on-press #(re-frame/dispatch [:open-modal :link-preview-settings]) - :type :secondary} + [quo/button + {:on-press #(re-frame/dispatch [:open-modal :link-preview-settings]) + :type :secondary} (i18n/label :enable)] [quo/separator] - [quo/button {:on-press #(re-frame/dispatch - [::link-preview/should-suggest-link-preview false]) - :type :secondary} + [quo/button + {:on-press #(re-frame/dispatch + [::link-preview/should-suggest-link-preview false]) + :type :secondary} (i18n/label :t/dont-ask)]]) -(defn is-gif? [url] +(defn is-gif? + [url] (string/ends-with? url ".gif")) -(defview link-preview-loader [link outgoing timeline] +(defview link-preview-loader + [link outgoing timeline] (letsubs [cache [:link-preview/cache]] (let [{:keys [site title thumbnailUrl error] :as preview-data} (get cache link)] (if (not preview-data) @@ -81,58 +94,70 @@ nil) (when-not error [react/touchable-highlight - {:style (when-not (is-gif? thumbnailUrl) {:align-self :stretch}) + {:style (when-not (is-gif? thumbnailUrl) {:align-self :stretch}) :on-press #(when (security/safe-link? link) (re-frame/dispatch [:browser.ui/message-link-pressed link]))} [react/view (styles/link-preview-wrapper outgoing timeline) (when-not (string/blank? thumbnailUrl) - [react/image {:source {:uri thumbnailUrl} - :style (styles/link-preview-image outgoing (select-keys preview-data [:height :width])) - :accessibility-label :member-photo}]) + [react/image + {:source {:uri thumbnailUrl} + :style (styles/link-preview-image outgoing + (select-keys preview-data + [:height :width])) + :accessibility-label :member-photo}]) (when-not (is-gif? thumbnailUrl) [:<> - [quo/text {:size :small - :style styles/link-preview-title} + [quo/text + {:size :small + :style styles/link-preview-title} title] - [quo/text {:size :small - :color :secondary - :style styles/link-preview-site} + [quo/text + {:size :small + :color :secondary + :style styles/link-preview-site} site]])]]))))) -(defview community-preview [community outgoing timeline] +(defview community-preview + [community outgoing timeline] (let [{:keys [name members description verified]} community - members-count (count members)] + members-count (count members)] [react/view (styles/link-preview-wrapper outgoing timeline) - (if verified [quo/text {:size :small - :color :link - :style styles/community-preview-header} - (i18n/label :t/verified-community)] - [quo/text {:size :small - :color :secondary - :style styles/community-preview-header} - (i18n/label :t/community)]) + (if verified + [quo/text + {:size :small + :color :link + :style styles/community-preview-header} + (i18n/label :t/verified-community)] + [quo/text + {:size :small + :color :secondary + :style styles/community-preview-header} + (i18n/label :t/community)]) [quo/separator] [react/view {:flex-direction :row :align-self :flex-start :margin 12} [communities.icon/community-icon community] [react/view {:flex 1 :flex-direction :column :margin-left 12} [quo/text {:weight :bold :size :large} name] [quo/text description] - [quo/text {:size :small - :color :secondary} + [quo/text + {:size :small + :color :secondary} (i18n/label-pluralize members-count :t/community-members {:count members-count})]]] [quo/separator] - [quo/button {:on-press #(re-frame/dispatch [:navigate-to - :community - {:from-chat true - :community-id (:id community)}]) - :type :secondary} + [quo/button + {:on-press #(re-frame/dispatch [:navigate-to + :community + {:from-chat true + :community-id (:id community)}]) + :type :secondary} (i18n/label :t/view)]])) -(defview community-preview-loader [community-link outgoing timeline] +(defview community-preview-loader + [community-link outgoing timeline] (letsubs [cache [:link-preview/cache]] {:component-did-mount (fn [] - (let [community (get cache community-link) + (let [community (get cache community-link) community-id (community-id-from-link community-link)] (when-not community (re-frame/dispatch @@ -140,16 +165,17 @@ (when-let [community (get cache community-link)] [community-preview community outgoing timeline]))) -(defview link-preview-wrapper [links outgoing timeline] +(defview link-preview-wrapper + [links outgoing timeline] (letsubs - [ask-user? [:link-preview/link-preview-request-enabled] - whitelist [:link-previews-whitelist] - enabled-sites [:link-preview/enabled-sites]] + [ask-user? [:link-preview/link-preview-request-enabled] + whitelist [:link-previews-whitelist] + enabled-sites [:link-preview/enabled-sites]] (when links - (let [link-info (previewable-link links whitelist enabled-sites) + (let [link-info (previewable-link links whitelist enabled-sites) {:keys [link whitelisted enabled community]} link-info - link-whitelisted (and link whitelisted)] + link-whitelisted (and link whitelisted)] (cond - community [community-preview-loader link outgoing timeline] - (and link-whitelisted enabled) [link-preview-loader link outgoing timeline] + community [community-preview-loader link outgoing timeline] + (and link-whitelisted enabled) [link-preview-loader link outgoing timeline] (and link-whitelisted ask-user?) [link-preview-enable-request]))))) diff --git a/src/status_im/ui/screens/chat/message/message.cljs b/src/status_im/ui/screens/chat/message/message.cljs index 5451fd739a..cb44a7a899 100644 --- a/src/status_im/ui/screens/chat/message/message.cljs +++ b/src/status_im/ui/screens/chat/message/message.cljs @@ -1,31 +1,31 @@ (ns status-im.ui.screens.chat.message.message - (:require [re-frame.core :as re-frame] + (:require [quo.core :as quo] + [quo.design-system.colors :as colors] + [re-frame.core :as re-frame] + [reagent.core :as reagent] + [status-im.chat.models.images :as images] + [status-im.chat.models.reactions :as models.reactions] [status-im.constants :as constants] [status-im.i18n.i18n :as i18n] [status-im.react-native.resources :as resources] - [quo.design-system.colors :as colors] + [status-im.ui.components.animation :as animation] + [status-im.ui.components.fast-image :as fast-image] [status-im.ui.components.icons.icons :as icons] [status-im.ui.components.react :as react] + [status-im.ui.screens.chat.components.reply :as components.reply] + [status-im.ui.screens.chat.image.preview.views :as preview] [status-im.ui.screens.chat.message.audio-old :as message.audio] - [status-im.chat.models.reactions :as models.reactions] [status-im.ui.screens.chat.message.command :as message.command] + [status-im.ui.screens.chat.message.gap :as message.gap] + [status-im.ui.screens.chat.message.link-preview :as link-preview] + [status-im.ui.screens.chat.message.reactions-old :as reactions] [status-im.ui.screens.chat.photos :as photos] [status-im.ui.screens.chat.sheets :as sheets] - [status-im.ui.screens.chat.message.gap :as message.gap] [status-im.ui.screens.chat.styles.message.message-old :as style] [status-im.ui.screens.chat.utils :as chat.utils] - [utils.security.core :as security] - [status-im.ui.screens.chat.message.reactions-old :as reactions] - [status-im.ui.screens.chat.image.preview.views :as preview] - [quo.core :as quo] - [status-im.utils.config :as config] - [reagent.core :as reagent] - [status-im.ui.screens.chat.components.reply :as components.reply] - [status-im.ui.screens.chat.message.link-preview :as link-preview] [status-im.ui.screens.communities.icon :as communities.icon] - [status-im.ui.components.animation :as animation] - [status-im.chat.models.images :as images] - [status-im.ui.components.fast-image :as fast-image]) + [status-im.utils.config :as config] + [utils.security.core :as security]) (:require-macros [status-im.utils.views :refer [defview letsubs]])) (defn message-timestamp-anim @@ -44,15 +44,18 @@ :delay 2000 :duration 100 :easing (.-ease ^js animation/easing) - :useNativeDriver true})]) #(reset! show-timestamp? false))) + :useNativeDriver true})]) + #(reset! show-timestamp? false))) -(defview mention-element [from] +(defview mention-element + [from] (letsubs [contact-name [:contacts/contact-name-by-identity from]] contact-name)) (def edited-at-text (str " ⌫ " (i18n/label :t/edited))) -(defn message-status [{:keys [outgoing content outgoing-status pinned edited-at in-popover?]}] +(defn message-status + [{:keys [outgoing content outgoing-status pinned edited-at in-popover?]}] (when-not in-popover? ;; We keep track if showing this message in a list in pin-limit-popover [react/view {:align-self :flex-end @@ -62,17 +65,19 @@ :flex-direction :row :align-items :flex-end} (when outgoing - [icons/icon (case outgoing-status - :sending :tiny-icons/tiny-pending - :sent :tiny-icons/tiny-sent - :not-sent :tiny-icons/tiny-warning - :delivered :tiny-icons/tiny-delivered - :tiny-icons/tiny-pending) + [icons/icon + (case outgoing-status + :sending :tiny-icons/tiny-pending + :sent :tiny-icons/tiny-sent + :not-sent :tiny-icons/tiny-warning + :delivered :tiny-icons/tiny-delivered + :tiny-icons/tiny-pending) {:width 16 :height 12 :color (if pinned colors/gray colors/white) :accessibility-label (name outgoing-status)}]) - (when edited-at [react/text {:style (style/message-status-text (and outgoing (not pinned)))} edited-at-text])])) + (when edited-at + [react/text {:style (style/message-status-text (and outgoing (not pinned)))} edited-at-text])])) (defn message-timestamp [{:keys [timestamp-str in-popover?] :as message} show-timestamp?] @@ -86,9 +91,11 @@ timestamp-str]]))) (defn quoted-message - [_ {:keys [from parsed-text image audio sticker id] :as message} outgoing current-public-key public? pinned chat-id] - (let [contact-name [:contacts/contact-name-by-identity from] - replied-message (get @(re-frame/subscribe [:chats/chat-messages chat-id]) id)] ;; the message replied to + [_ {:keys [from parsed-text image audio sticker id] :as message} outgoing current-public-key public? + pinned chat-id] + (let [contact-name [:contacts/contact-name-by-identity from] + replied-message (get @(re-frame/subscribe [:chats/chat-messages chat-id]) id)] ;; the message + ;; replied to [react/view {:style (style/quoted-message-container (and outgoing (not pinned)))} [react/view {:style style/quoted-message-author-container} [chat.utils/format-reply-author @@ -99,11 +106,12 @@ (and outgoing (not pinned))]] (cond (and image (not public?)) ;; Disabling images for public-chats - [fast-image/fast-image {:style {:width 56 - :height 56 - :background-color :black - :border-radius 4} - :source {:uri image}}] + [fast-image/fast-image + {:style {:width 56 + :height 56 + :background-color :black + :border-radius 4} + :source {:uri image}}] (and audio (not public?)) ;; Disabling audio for public-chats [react/view {:style (style/message-view message) :accessibility-label :audio-message} [react/view {:style (style/message-view-content)} @@ -112,27 +120,34 @@ [message-status message]]]] sticker (if replied-message - [fast-image/fast-image {:style {:margin 4 :width 56 :height 56} - ;; Get sticker url of the message replied to - :source {:uri (((replied-message :content) :sticker) :url)}}] + [fast-image/fast-image + {:style {:margin 4 :width 56 :height 56} + ;; Get sticker url of the message replied to + :source {:uri (((replied-message :content) :sticker) :url)}}] ;; Let the user know if the message was deleted [react/text {:style (style/quoted-message-text (and outgoing (not pinned)))} - ;; This hardcorded text can be modified to come from the parsed-text. Also, translations can be added. + ;; This hardcorded text can be modified to come from the parsed-text. Also, translations can be + ;; added. "This message was deleted!"]) - :else [react/text {:style (style/quoted-message-text (and outgoing (not pinned))) - :number-of-lines 5} - (components.reply/get-quoted-text-with-mentions parsed-text)])])) + :else [react/text + {:style (style/quoted-message-text (and outgoing + (not pinned))) + :number-of-lines 5} + (components.reply/get-quoted-text-with-mentions parsed-text)])])) -(defn render-inline [message-text outgoing pinned content-type acc {:keys [type literal destination]}] +(defn render-inline + [message-text outgoing pinned content-type acc {:keys [type literal destination]}] (case type "" (conj acc literal) "code" - (conj acc [quo/text {:max-font-size-multiplier react/max-font-size-multiplier - :style (style/inline-code-style) - :monospace true} - literal]) + (conj acc + [quo/text + {:max-font-size-multiplier react/max-font-size-multiplier + :style (style/inline-code-style) + :monospace true} + literal]) "emph" (conj acc [react/text-class (style/emph-style (and outgoing (not pinned))) literal]) @@ -160,58 +175,69 @@ destination]) "mention" - (conj acc [react/text-class - {:style {:color (cond - (= content-type constants/content-type-system-text) colors/black - (and outgoing (not pinned)) colors/mention-outgoing - :else colors/mention-incoming)} - :on-press (when-not (= content-type constants/content-type-system-text) - #(re-frame/dispatch [:chat.ui/show-profile literal]))} - [mention-element literal]]) + (conj acc + [react/text-class + {:style {:color (cond + (= content-type constants/content-type-system-text) colors/black + (and outgoing (not pinned)) colors/mention-outgoing + :else colors/mention-incoming)} + :on-press (when-not (= content-type constants/content-type-system-text) + #(re-frame/dispatch [:chat.ui/show-profile literal]))} + [mention-element literal]]) "status-tag" - (conj acc [react/text-class - {:style {:color (if (and outgoing (not pinned)) colors/white-persist colors/blue) - :text-decoration-line :underline} - :on-press - #(re-frame/dispatch - [:chat.ui/start-public-chat literal])} - "#" - literal]) + (conj + acc + [react/text-class + {:style {:color (if (and outgoing (not pinned)) colors/white-persist colors/blue) + :text-decoration-line :underline} + :on-press + #(re-frame/dispatch + [:chat.ui/start-public-chat literal])} + "#" + literal]) (conj acc literal))) -(defn render-block [{:keys [content outgoing content-type pinned in-popover?]} acc - {:keys [type ^js literal children]}] +(defn render-block + [{:keys [content outgoing content-type pinned in-popover?]} acc + {:keys [type ^js literal children]}] (case type "paragraph" - (conj acc (reduce - (fn [acc e] (render-inline (:text content) outgoing pinned content-type acc e)) - [react/text-class (style/text-style (and outgoing (not pinned)) content-type in-popover?)] - children)) + (conj acc + (reduce + (fn [acc e] (render-inline (:text content) outgoing pinned content-type acc e)) + [react/text-class (style/text-style (and outgoing (not pinned)) content-type in-popover?)] + children)) "blockquote" - (conj acc [react/view (style/blockquote-style (and outgoing (not pinned))) - [react/text-class (style/blockquote-text-style (and outgoing (not pinned))) - (.substring literal 0 (.-length literal))]]) + (conj acc + [react/view (style/blockquote-style (and outgoing (not pinned))) + [react/text-class (style/blockquote-text-style (and outgoing (not pinned))) + (.substring literal 0 (.-length literal))]]) "codeblock" - (conj acc [react/view {:style style/codeblock-style} - [quo/text {:max-font-size-multiplier react/max-font-size-multiplier - :style style/codeblock-text-style - :monospace true} - (.substring literal 0 (dec (.-length literal)))]]) + (conj acc + [react/view {:style style/codeblock-style} + [quo/text + {:max-font-size-multiplier react/max-font-size-multiplier + :style style/codeblock-text-style + :monospace true} + (.substring literal 0 (dec (.-length literal)))]]) acc)) -(defn render-parsed-text [message tree] +(defn render-parsed-text + [message tree] (reduce (fn [acc e] (render-block message acc e)) [:<>] tree)) -(defn render-parsed-text-with-message-status [{:keys [outgoing edited-at in-popover?] :as message} tree] - (let [elements (render-parsed-text message tree) +(defn render-parsed-text-with-message-status + [{:keys [outgoing edited-at in-popover?] :as message} tree] + (let [elements (render-parsed-text message tree) message-status [react/text {:style (style/message-status-placeholder)} - (str (if (and outgoing (not in-popover?)) " " " ") (when (and (not in-popover?) edited-at) edited-at-text))] - last-element (peek elements)] + (str (if (and outgoing (not in-popover?)) " " " ") + (when (and (not in-popover?) edited-at) edited-at-text))] + last-element (peek elements)] ;; Using `nth` here as slightly faster than `first`, roughly 30% ;; It's worth considering pure js structures for this code path as ;; it's perfomance critical @@ -246,34 +272,42 @@ [react/view style/not-sent-icon [icons/icon :main-icons/warning {:color colors/red}]]]]) -(defn pin-author-name [pinned-by] - (let [user-contact @(re-frame/subscribe [:multiaccount/contact]) +(defn pin-author-name + [pinned-by] + (let [user-contact @(re-frame/subscribe [:multiaccount/contact]) contact-names @(re-frame/subscribe [:contacts/contact-two-names-by-identity pinned-by])] - ;; We append empty spaces to the name as a workaround to make one-line and multi-line label components show correctly - (str " " (if (= pinned-by (user-contact :public-key)) (i18n/label :t/You) (first contact-names))))) + ;; We append empty spaces to the name as a workaround to make one-line and multi-line label + ;; components show correctly + (str " " + (if (= pinned-by (user-contact :public-key)) (i18n/label :t/You) (first contact-names))))) (def pin-icon-width 9) (def pin-icon-height 15) -(defn pinned-by-indicator [outgoing display-photo? pinned-by] - [react/view {:style (style/pin-indicator outgoing display-photo?) - :accessibility-label :pinned-by} +(defn pinned-by-indicator + [outgoing display-photo? pinned-by] + [react/view + {:style (style/pin-indicator outgoing display-photo?) + :accessibility-label :pinned-by} [react/view {:style (style/pinned-by-text-icon-container)} [react/view {:style (style/pin-icon-container)} - [icons/icon :main-icons/pin {:color colors/gray - :height pin-icon-height - :width pin-icon-width - :background-color :red}]] - [quo/text {:weight :regular - :size :small - :color :main - :style (style/pinned-by-text)} + [icons/icon :main-icons/pin + {:color colors/gray + :height pin-icon-height + :width pin-icon-width + :background-color :red}]] + [quo/text + {:weight :regular + :size :small + :color :main + :style (style/pinned-by-text)} (i18n/label :t/pinned-by)]] - [quo/text {:weight :medium - :size :small - :color :main - :style (style/pin-author-text)} + [quo/text + {:weight :medium + :size :small + :color :main + :style (style/pin-author-text)} (pin-author-name pinned-by)]]) (defn message-delivery-status @@ -282,44 +316,53 @@ (= outgoing-status :not-sent)) [message-not-sent-text chat-id message-id])) -(defview message-author-name [from opts] +(defview message-author-name + [from opts] (letsubs [contact-with-names [:contacts/contact-by-identity from]] (chat.utils/format-author-old contact-with-names opts))) -(defview message-my-name [opts] +(defview message-my-name + [opts] (letsubs [contact-with-names [:multiaccount/contact]] (chat.utils/format-author-old contact-with-names opts))) -(defview community-content [{:keys [community-id] :as message}] +(defview community-content + [{:keys [community-id] :as message}] (letsubs [{:keys [name description verified] :as community} [:communities/community community-id] - communities-enabled? [:communities/enabled?]] + communities-enabled? [:communities/enabled?]] (when (and communities-enabled? community) - [react/view {:style (assoc (style/message-wrapper message) - :margin-vertical 10 - :margin-left 8 - :width 271)} + [react/view + {:style (assoc (style/message-wrapper message) + :margin-vertical 10 + :margin-left 8 + :width 271)} (when verified [react/view (style/community-verified) - [react/text {:style {:font-size 13 - :color colors/blue}} (i18n/label :t/communities-verified)]]) + [react/text + {:style {:font-size 13 + :color colors/blue}} (i18n/label :t/communities-verified)]]) [react/view (style/community-message verified) - [react/view {:width 62 - :padding-left 14} + [react/view + {:width 62 + :padding-left 14} (if (= community-id constants/status-community-id) - [react/image {:source (resources/get-image :status-logo) - :style {:width 40 - :height 40}}] + [react/image + {:source (resources/get-image :status-logo) + :style {:width 40 + :height 40}}] [communities.icon/community-icon community])] [react/view {:padding-right 14 :flex 1} [react/text {:style {:font-weight "700" :font-size 17}} name] [react/text description]]] [react/view (style/community-view-button) - [react/touchable-highlight {:on-press #(re-frame/dispatch [:navigate-to - :community - {:community-id (:id community)}])} - [react/text {:style {:text-align :center - :color colors/blue}} (i18n/label :t/view)]]]]))) + [react/touchable-highlight + {:on-press #(re-frame/dispatch [:navigate-to + :community + {:community-id (:id community)}])} + [react/text + {:style {:text-align :center + :color colors/blue}} (i18n/label :t/view)]]]]))) (defn message-content-wrapper "Author, userpic and delivery wrapper" @@ -327,23 +370,27 @@ identicon from outgoing in-popover?] :as message} content {:keys [modal close-modal]}] - [react/view {:style (style/message-wrapper message) - :pointer-events :box-none - :accessibility-label :chat-item} - [react/view {:style (style/message-body message) - :pointer-events :box-none} + [react/view + {:style (style/message-wrapper message) + :pointer-events :box-none + :accessibility-label :chat-item} + [react/view + {:style (style/message-body message) + :pointer-events :box-none} (when display-photo? [react/view (style/message-author-userpic outgoing) (when first-in-group? - [react/touchable-highlight {:on-press #(do (when modal (close-modal)) - (re-frame/dispatch [:chat.ui/show-profile from]))} + [react/touchable-highlight + {:on-press #(do (when modal (close-modal)) + (re-frame/dispatch [:chat.ui/show-profile from]))} [photos/member-photo from identicon]])]) [react/view {:style (style/message-author-wrapper outgoing display-photo? in-popover?)} (when display-username? - [react/touchable-opacity {:style style/message-author-touchable - :disabled in-popover? - :on-press #(do (when modal (close-modal)) - (re-frame/dispatch [:chat.ui/show-profile from]))} + [react/touchable-opacity + {:style style/message-author-touchable + :disabled in-popover? + :on-press #(do (when modal (close-modal)) + (re-frame/dispatch [:chat.ui/show-profile from]))} [message-author-name from {:modal modal}]]) ;;MESSAGE CONTENT content @@ -355,9 +402,10 @@ (def image-max-width 260) (def image-max-height 192) -(defn image-set-size [dimensions] +(defn image-set-size + [dimensions] (fn [evt] - (let [width (.-width (.-nativeEvent evt)) + (let [width (.-width (.-nativeEvent evt)) height (.-height (.-nativeEvent evt))] (if (< width height) ;; if width less than the height we reduce width proportionally to height @@ -370,25 +418,28 @@ [{:keys [content outgoing in-popover?] :as message} {:keys [on-long-press]}] (let [dimensions (reagent/atom {:width image-max-width :height image-max-height :loaded false}) - visible (reagent/atom false) - uri (:image content)] + visible (reagent/atom false) + uri (:image content)] (fn [] (let [style-opts {:outgoing outgoing - :opacity (if (:loaded @dimensions) 1 0) - :width (:width @dimensions) - :height (:height @dimensions)}] + :opacity (if (:loaded @dimensions) 1 0) + :width (:width @dimensions) + :height (:height @dimensions)}] [:<> - [preview/preview-image {:message message - :visible @visible - :on-close #(do (reset! visible false) - (reagent/flush))}] - [react/touchable-highlight {:on-press (fn [] - (reset! visible true) - (react/dismiss-keyboard!)) - :on-long-press on-long-press - :disabled in-popover?} - [react/view {:style (style/image-message style-opts) - :accessibility-label :image-message} + [preview/preview-image + {:message message + :visible @visible + :on-close #(do (reset! visible false) + (reagent/flush))}] + [react/touchable-highlight + {:on-press (fn [] + (reset! visible true) + (react/dismiss-keyboard!)) + :on-long-press on-long-press + :disabled in-popover?} + [react/view + {:style (style/image-message style-opts) + :accessibility-label :image-message} (when (or (:error @dimensions) (not (:loaded @dimensions))) [react/view (merge (dissoc style-opts :opacity) @@ -396,10 +447,11 @@ (if (:error @dimensions) [icons/icon :main-icons/cancel] [react/activity-indicator {:animating true}])]) - [fast-image/fast-image {:style (dissoc style-opts :outgoing) - :on-load (image-set-size dimensions) - :on-error #(swap! dimensions assoc :error true) - :source {:uri uri}}] + [fast-image/fast-image + {:style (dissoc style-opts :outgoing) + :on-load (image-set-size dimensions) + :on-error #(swap! dimensions assoc :error true) + :source {:uri uri}}] [react/view {:style (style/image-message-border style-opts)}]]]])))) (defmulti ->message :content-type) @@ -412,7 +464,8 @@ [message] [message.gap/gap message]) -(defmethod ->message constants/content-type-system-text [{:keys [content] :as message}] +(defmethod ->message constants/content-type-system-text + [{:keys [content] :as message}] [react/view {:accessibility-label :chat-item} [react/view (style/system-message-body message) [react/view (style/message-view message) @@ -422,7 +475,8 @@ (def message-height-px 200) (def max-message-height-px 150) -(defn pin-message [{:keys [chat-id pinned] :as message}] +(defn pin-message + [{:keys [chat-id pinned] :as message}] (let [pinned-messages @(re-frame/subscribe [:chats/pinned chat-id])] (if (and (not pinned) (> (count pinned-messages) 2)) (do @@ -430,7 +484,9 @@ (re-frame/dispatch [:pin-message/show-pin-limit-modal chat-id])) (re-frame/dispatch [:pin-message/send-pin-message (assoc message :pinned (not pinned))])))) -(defn on-long-press-fn [on-long-press {:keys [pinned message-pin-enabled outgoing edit-enabled show-input?] :as message} content] +(defn on-long-press-fn + [on-long-press {:keys [pinned message-pin-enabled outgoing edit-enabled show-input?] :as message} + content] (on-long-press (concat (when (and outgoing edit-enabled) @@ -455,11 +511,13 @@ :label (i18n/label :t/delete) :id :delete}])))) -(defn collapsible-text-message [{:keys [mentioned]} _] - (let [collapsed? (reagent/atom false) - collapsible? (reagent/atom false) +(defn collapsible-text-message + [{:keys [mentioned]} _] + (let [collapsed? (reagent/atom false) + collapsible? (reagent/atom false) show-timestamp? (reagent/atom false)] - (fn [{:keys [content outgoing current-public-key public? pinned in-popover? chat-id] :as message} on-long-press modal] + (fn [{:keys [content outgoing current-public-key public? pinned in-popover? chat-id] :as message} + on-long-press modal] (let [max-height (when-not (or outgoing modal) (if @collapsible? (if @collapsed? message-height-px nil) @@ -473,41 +531,53 @@ :on-long-press (fn [] (if @collapsed? (do (reset! collapsed? false) - (js/setTimeout #(on-long-press-fn on-long-press message content) 200)) + (js/setTimeout #(on-long-press-fn on-long-press message content) + 200)) (on-long-press-fn on-long-press message content))) :disabled in-popover?}) [react/view (style/message-view-wrapper outgoing) [message-timestamp message show-timestamp?] [react/view {:style (style/message-view message)} - [react/view {:style (style/message-view-content) - :max-height max-height} + [react/view + {:style (style/message-view-content) + :max-height max-height} (let [response-to (:response-to content)] - [react/view {:on-layout - #(when (and (> (.-nativeEvent.layout.height ^js %) max-message-height-px) - (not @collapsible?) - (not outgoing) - (not modal)) - (reset! collapsed? true) - (reset! collapsible? true))} + [react/view + {:on-layout + #(when (and (> (.-nativeEvent.layout.height ^js %) max-message-height-px) + (not @collapsible?) + (not outgoing) + (not modal)) + (reset! collapsed? true) + (reset! collapsible? true))} (when (and (seq response-to) (:quoted-message message)) - [quoted-message response-to (:quoted-message message) outgoing current-public-key public? pinned chat-id]) + [quoted-message response-to (:quoted-message message) outgoing current-public-key + public? pinned chat-id]) [render-parsed-text-with-message-status message (:parsed-text content)]]) (when-not @collapsible? [message-status message]) (when (and @collapsible? (not modal)) (if @collapsed? - (let [color (if pinned colors/pin-background (if mentioned colors/mentioned-background colors/blue-light))] + (let [color (if pinned + colors/pin-background + (if mentioned colors/mentioned-background colors/blue-light))] [react/touchable-highlight {:on-press #(swap! collapsed? not) :style {:position :absolute :bottom 0 :left 0 :right 0 :height 72}} - [react/linear-gradient {:colors [(str color "00") color] - :start {:x 0 :y 0} :end {:x 0 :y 0.9}} - [react/view {:height 72 :align-self :center :justify-content :flex-end - :padding-bottom 10} + [react/linear-gradient + {:colors [(str color "00") color] + :start {:x 0 :y 0} + :end {:x 0 :y 0.9}} + [react/view + {:height 72 + :align-self :center + :justify-content :flex-end + :padding-bottom 10} [react/view (style/collapse-button) [icons/icon :main-icons/dropdown {:color colors/white}]]]]]) - [react/touchable-highlight {:on-press #(swap! collapsed? not) - :style {:align-self :center :margin 5}} + [react/touchable-highlight + {:on-press #(swap! collapsed? not) + :style {:align-self :center :margin 5}} [react/view (style/collapse-button) [icons/icon :main-icons/dropdown-up {:color colors/white}]]]))]]]])))) @@ -532,9 +602,12 @@ [react/text-class {:style (style/status-text)}] (-> content :parsed-text peek :children))]]]) -(defmethod ->message constants/content-type-emoji [] +(defmethod ->message constants/content-type-emoji + [] (let [show-timestamp? (reagent/atom false)] - (fn [{:keys [content current-public-key outgoing edit-enabled public? pinned in-popover? message-pin-enabled content-type edited-at] :as message} + (fn [{:keys [content current-public-key outgoing edit-enabled public? pinned in-popover? + message-pin-enabled content-type edited-at] + :as message} {:keys [on-long-press modal] :as reaction-picker}] ;; Makes sure to render a text-message and not an emoji-message if it has been edited with text @@ -543,38 +616,44 @@ [collapsible-text-message message on-long-press modal] reaction-picker] (let [response-to (:response-to content)] [message-content-wrapper message - [react/touchable-highlight (when-not modal - {:disabled in-popover? - :on-press (fn [] - (react/dismiss-keyboard!) - (reset! show-timestamp? true)) - :delay-long-press 100 - :on-long-press (fn [] - (on-long-press - (concat - (when (and outgoing edit-enabled) - [{:on-press #(re-frame/dispatch [:chat.ui/edit-message message]) - :label (i18n/label :t/edit) - :id :edit}]) - [{:on-press #(re-frame/dispatch [:chat.ui/reply-to-message message]) - :id :reply - :label (i18n/label :t/message-reply)} - {:on-press #(react/copy-to-clipboard (get content :text)) - :id :copy - :label (i18n/label :t/sharing-copy-to-clipboard)}] - (when message-pin-enabled [{:on-press #(pin-message message) - :label (if pinned (i18n/label :t/unpin) (i18n/label :t/pin))}]) - (when (and outgoing config/delete-message-enabled?) - [{:on-press #(re-frame/dispatch [:chat.ui/soft-delete-message message]) - :label (i18n/label :t/delete) - :id :delete}]))))}) + [react/touchable-highlight + (when-not modal + {:disabled in-popover? + :on-press (fn [] + (react/dismiss-keyboard!) + (reset! show-timestamp? true)) + :delay-long-press 100 + :on-long-press (fn [] + (on-long-press + (concat + (when (and outgoing edit-enabled) + [{:on-press #(re-frame/dispatch [:chat.ui/edit-message message]) + :label (i18n/label :t/edit) + :id :edit}]) + [{:on-press #(re-frame/dispatch [:chat.ui/reply-to-message message]) + :id :reply + :label (i18n/label :t/message-reply)} + {:on-press #(react/copy-to-clipboard (get content :text)) + :id :copy + :label (i18n/label :t/sharing-copy-to-clipboard)}] + (when message-pin-enabled + [{:on-press #(pin-message message) + :label (if pinned + (i18n/label :t/unpin) + (i18n/label :t/pin))}]) + (when (and outgoing config/delete-message-enabled?) + [{:on-press #(re-frame/dispatch [:chat.ui/soft-delete-message + message]) + :label (i18n/label :t/delete) + :id :delete}]))))}) [react/view (style/message-view-wrapper outgoing) [message-timestamp message show-timestamp?] [react/view (style/message-view message) [react/view {:style (style/message-view-content)} [react/view {:style (style/style-message-text outgoing)} (when (and (seq response-to) (:quoted-message message)) - [quoted-message response-to (:quoted-message message) outgoing current-public-key public? pinned]) + [quoted-message response-to (:quoted-message message) outgoing current-public-key + public? pinned]) [react/text {:style (style/emoji-message message)} (if edited-at (str (content :text) " ") (str (content :text)))]] [message-status message]]]]] @@ -587,31 +666,34 @@ :as reaction-picker}] (let [pack (get-in content [:sticker :pack])] [message-content-wrapper message - [react/touchable-highlight (when-not modal - {:disabled in-popover? - :accessibility-label :sticker-message - :on-press (fn [_] - (when pack - (re-frame/dispatch [:stickers/open-sticker-pack (str pack)])) - (react/dismiss-keyboard!)) - :delay-long-press 100 - :on-long-press (fn [] - (on-long-press - (concat - (when-not outgoing - [{:on-press #(when pack - (re-frame/dispatch [:chat.ui/show-profile from])) - :label (i18n/label :t/view-details)}]) - [{:on-press #(re-frame/dispatch [:chat.ui/reply-to-message message]) - :id :reply - :label (i18n/label :t/message-reply)}] - (if (and outgoing config/delete-message-enabled?) - [{:on-press #(re-frame/dispatch [:chat.ui/soft-delete-message message]) - :label (i18n/label :t/delete) - :id :delete}] - []))))}) - [fast-image/fast-image {:style {:margin 10 :width 140 :height 140} - :source {:uri (str (-> content :sticker :url) "&download=true")}}]] + [react/touchable-highlight + (when-not modal + {:disabled in-popover? + :accessibility-label :sticker-message + :on-press (fn [_] + (when pack + (re-frame/dispatch [:stickers/open-sticker-pack (str pack)])) + (react/dismiss-keyboard!)) + :delay-long-press 100 + :on-long-press (fn [] + (on-long-press + (concat + (when-not outgoing + [{:on-press #(when pack + (re-frame/dispatch [:chat.ui/show-profile from])) + :label (i18n/label :t/view-details)}]) + [{:on-press #(re-frame/dispatch [:chat.ui/reply-to-message message]) + :id :reply + :label (i18n/label :t/message-reply)}] + (if (and outgoing config/delete-message-enabled?) + [{:on-press #(re-frame/dispatch [:chat.ui/soft-delete-message + message]) + :label (i18n/label :t/delete) + :id :delete}] + []))))}) + [fast-image/fast-image + {:style {:margin 10 :width 140 :height 140} + :source {:uri (str (-> content :sticker :url) "&download=true")}}]] reaction-picker])) (defmethod ->message constants/content-type-image @@ -620,28 +702,32 @@ :as reaction-picker}] [message-content-wrapper message [message-content-image message - {:modal modal - :disabled in-popover? + {:modal modal + :disabled in-popover? :delay-long-press 100 - :on-long-press (fn [] - (on-long-press - (concat [{:on-press #(re-frame/dispatch [:chat.ui/reply-to-message message]) - :id :reply - :label (i18n/label :t/message-reply)} - {:on-press #(re-frame/dispatch [:chat.ui/save-image-to-gallery (:image content)]) - :id :save - :label (i18n/label :t/save)} - {:on-press #(images/download-image-http - (get-in message [:content :image]) preview/share) - :id :share - :label (i18n/label :t/share)}] - (when (and outgoing config/delete-message-enabled?) - [{:on-press #(re-frame/dispatch [:chat.ui/soft-delete-message message]) - :label (i18n/label :t/delete) - :id :delete}]))))}] + :on-long-press (fn [] + (on-long-press + (concat + [{:on-press #(re-frame/dispatch [:chat.ui/reply-to-message message]) + :id :reply + :label (i18n/label :t/message-reply)} + {:on-press #(re-frame/dispatch [:chat.ui/save-image-to-gallery + (:image content)]) + :id :save + :label (i18n/label :t/save)} + {:on-press #(images/download-image-http + (get-in message [:content :image]) + preview/share) + :id :share + :label (i18n/label :t/share)}] + (when (and outgoing config/delete-message-enabled?) + [{:on-press #(re-frame/dispatch [:chat.ui/soft-delete-message message]) + :label (i18n/label :t/delete) + :id :delete}]))))}] reaction-picker]) -(defmethod ->message constants/content-type-audio [] +(defmethod ->message constants/content-type-audio + [] (let [show-timestamp? (reagent/atom false)] (fn [{:keys [outgoing] :as message} {:keys [on-long-press modal] @@ -661,8 +747,8 @@ :label (i18n/label :t/delete) :id :delete}]) []))) - :on-press (fn [] - (reset! show-timestamp? true))}) + :on-press (fn [] + (reset! show-timestamp? true))}) [react/view (style/message-view-wrapper (:outgoing message)) [message-timestamp message show-timestamp?] [react/view {:style (style/message-view message) :accessibility-label :audio-message} @@ -670,27 +756,35 @@ [message.audio/message-content message] [message-status message]]]]] reaction-picker]))) -(defn contact-request-status-pending [] +(defn contact-request-status-pending + [] [react/view {:style {:flex-direction :row}} - [quo/text {:style {:margin-right 5.27} - :weight :medium - :color :secondary} + [quo/text + {:style {:margin-right 5.27} + :weight :medium + :color :secondary} (i18n/label :t/contact-request-pending)] - [react/activity-indicator {:animating true - :size :small - :color colors/gray}]]) + [react/activity-indicator + {:animating true + :size :small + :color colors/gray}]]) -(defn contact-request-status-accepted [] - [quo/text {:style {:color colors/green} - :weight :medium} +(defn contact-request-status-accepted + [] + [quo/text + {:style {:color colors/green} + :weight :medium} (i18n/label :t/contact-request-accepted)]) -(defn contact-request-status-declined [] - [quo/text {:style {:color colors/red} - :weight :medium} +(defn contact-request-status-declined + [] + [quo/text + {:style {:color colors/red} + :weight :medium} (i18n/label :t/contact-request-declined)]) -(defn contact-request-status-label [state] +(defn contact-request-status-label + [state] [react/view {:style (style/contact-request-status-label state)} (case state constants/contact-request-message-state-pending [contact-request-status-pending] @@ -700,28 +794,34 @@ (defmethod ->message constants/content-type-contact-request [{:keys [outgoing] :as message} _] [react/view {:style (style/content-type-contact-request outgoing)} - [react/image {:source (resources/get-image :hand-wave) - :style {:width 112 - :height 97}}] - [quo/text {:style {:margin-top 6} - :weight :bold - :size :large} + [react/image + {:source (resources/get-image :hand-wave) + :style {:width 112 + :height 97}}] + [quo/text + {:style {:margin-top 6} + :weight :bold + :size :large} (i18n/label :t/contact-request)] [react/view {:style {:padding-horizontal 16}} - [quo/text {:style {:margin-top 2 - :margin-bottom 14}} + [quo/text + {:style {:margin-top 2 + :margin-bottom 14}} (get-in message [:content :text])]] [contact-request-status-label (:contact-request-state message)]]) -(defmethod ->message :default [message] +(defmethod ->message :default + [message] [message-content-wrapper message [unknown-content-type message]]) -(defn chat-message [{:keys [outgoing display-photo? pinned pinned-by] :as message} space-keeper] +(defn chat-message + [{:keys [outgoing display-photo? pinned pinned-by] :as message} space-keeper] [:<> [reactions/with-reaction-picker {:message message - :reactions @(re-frame/subscribe [:chats/message-reactions (:message-id message) (:chat-id message)]) + :reactions @(re-frame/subscribe [:chats/message-reactions (:message-id message) + (:chat-id message)]) :picker-on-open (fn [] (space-keeper true)) :picker-on-close (fn [] diff --git a/src/status_im/ui/screens/chat/message/pinned_message.cljs b/src/status_im/ui/screens/chat/message/pinned_message.cljs index 2850f9ff43..ba2855cdb0 100644 --- a/src/status_im/ui/screens/chat/message/pinned_message.cljs +++ b/src/status_im/ui/screens/chat/message/pinned_message.cljs @@ -1,87 +1,101 @@ (ns status-im.ui.screens.chat.message.pinned-message - (:require [re-frame.core :as re-frame] - [status-im.i18n.i18n :as i18n] + (:require [quo.core :as quo] [quo.design-system.colors :as colors] - [status-im.ui.components.react :as react] - [quo.core :as quo] + [re-frame.core :as re-frame] [reagent.core :as reagent] + [status-im.i18n.i18n :as i18n] [status-im.ui.components.list.views :as list] - [status-im.utils.handlers :refer [ ref react/current-ref (measure-in-window @@ -19,30 +21,34 @@ :width width :height height}))))) -(defn extract-id [reactions id] +(defn extract-id + [reactions id] (->> reactions (filter (fn [{:keys [emoji-id]}] (= emoji-id id))) first :emoji-reaction-id)) -(defn with-reaction-picker [] +(defn with-reaction-picker + [] (let [ref (react/create-ref) animated-state (animated/value 0) spring-animation (animated/with-spring-transition - animated-state - (:jump animated/springs)) + animated-state + (:jump animated/springs)) animation (animated/with-timing-transition - animated-state - {:duration reaction-picker/animation-duration - :easing (:ease-in-out animated/easings)}) + animated-state + {:duration reaction-picker/animation-duration + :easing (:ease-in-out animated/easings)}) visible (reagent/atom false) actions (reagent/atom nil) position (reagent/atom {})] - (fn [{:keys [message reactions outgoing outgoing-status render send-emoji retract-emoji picker-on-open + (fn [{:keys [message reactions outgoing outgoing-status render send-emoji retract-emoji + picker-on-open picker-on-close timeline]}] (let [own-reactions (reduce (fn [acc {:keys [emoji-id own]}] (if own (conj acc emoji-id) acc)) - [] reactions) + [] + reactions) on-emoji-press (fn [emoji-id] (let [active ((set own-reactions) emoji-id)] (if active @@ -61,35 +67,40 @@ (reset! position pos) (reset! visible true))] [:<> - [rn/view {:ref ref - :collapsable false} - [render message {:modal false - :on-long-press (fn [act] - (when (or (not outgoing) - (and outgoing (= outgoing-status :sent))) - (reset! actions act) - (get-picker-position ref on-open)))}] + [rn/view + {:ref ref + :collapsable false} + [render message + {:modal false + :on-long-press (fn [act] + (when (or (not outgoing) + (and outgoing (= outgoing-status :sent))) + (reset! actions act) + (get-picker-position ref on-open)))}] [reaction-row/message-reactions message reactions timeline #(on-emoji-press %) on-open]] (when @visible - [rn/modal {:on-request-close on-close - :on-show (fn [] - (js/requestAnimationFrame - #(animated/set-value animated-state 1))) - :transparent true} - [reaction-picker/modal {:outgoing (:outgoing message) - :display-photo (:display-photo? message) - :animation animation - :spring spring-animation - :top (:top @position) - :message-height (:height @position) - :on-close on-close - :actions @actions - :own-reactions own-reactions - :timeline timeline - :send-emoji (fn [emoji] - (on-close) - (js/setTimeout #(on-emoji-press emoji) - reaction-picker/animation-duration))} - [render message {:modal true - :on-long-press #() - :close-modal on-close}]]])])))) + [rn/modal + {:on-request-close on-close + :on-show (fn [] + (js/requestAnimationFrame + #(animated/set-value animated-state 1))) + :transparent true} + [reaction-picker/modal + {:outgoing (:outgoing message) + :display-photo (:display-photo? message) + :animation animation + :spring spring-animation + :top (:top @position) + :message-height (:height @position) + :on-close on-close + :actions @actions + :own-reactions own-reactions + :timeline timeline + :send-emoji (fn [emoji] + (on-close) + (js/setTimeout #(on-emoji-press emoji) + reaction-picker/animation-duration))} + [render message + {:modal true + :on-long-press #() + :close-modal on-close}]]])])))) diff --git a/src/status_im/ui/screens/chat/message/reactions_old.cljs b/src/status_im/ui/screens/chat/message/reactions_old.cljs index 854f425aba..0d37f1b5f1 100644 --- a/src/status_im/ui/screens/chat/message/reactions_old.cljs +++ b/src/status_im/ui/screens/chat/message/reactions_old.cljs @@ -1,15 +1,17 @@ (ns status-im.ui.screens.chat.message.reactions-old - (:require [status-im.ui.screens.chat.message.reactions-picker :as reaction-picker] - [status-im.ui.screens.chat.message.reactions-row-old :as reaction-row] - [reagent.core :as reagent] - [quo.react-native :as rn] + (:require [quo.animated :as animated] [quo.react :as react] - [quo.animated :as animated])) + [quo.react-native :as rn] + [reagent.core :as reagent] + [status-im.ui.screens.chat.message.reactions-picker :as reaction-picker] + [status-im.ui.screens.chat.message.reactions-row-old :as reaction-row])) -(defn measure-in-window [ref cb] +(defn measure-in-window + [ref cb] (.measureInWindow ^js ref cb)) -(defn get-picker-position [^js ref cb] +(defn get-picker-position + [^js ref cb] (some-> ref react/current-ref (measure-in-window @@ -19,30 +21,34 @@ :width width :height height}))))) -(defn- extract-id [reactions id] +(defn- extract-id + [reactions id] (->> reactions (filter (fn [{:keys [emoji-id]}] (= emoji-id id))) first :emoji-reaction-id)) -(defn with-reaction-picker [] +(defn with-reaction-picker + [] (let [ref (react/create-ref) animated-state (animated/value 0) spring-animation (animated/with-spring-transition - animated-state - (:jump animated/springs)) + animated-state + (:jump animated/springs)) animation (animated/with-timing-transition - animated-state - {:duration reaction-picker/animation-duration - :easing (:ease-in-out animated/easings)}) + animated-state + {:duration reaction-picker/animation-duration + :easing (:ease-in-out animated/easings)}) visible (reagent/atom false) actions (reagent/atom nil) position (reagent/atom {})] - (fn [{:keys [message reactions outgoing outgoing-status render send-emoji retract-emoji picker-on-open + (fn [{:keys [message reactions outgoing outgoing-status render send-emoji retract-emoji + picker-on-open picker-on-close timeline]}] (let [own-reactions (reduce (fn [acc {:keys [emoji-id own]}] (if own (conj acc emoji-id) acc)) - [] reactions) + [] + reactions) on-emoji-press (fn [emoji-id] (let [active ((set own-reactions) emoji-id)] (if active @@ -61,35 +67,40 @@ (reset! position pos) (reset! visible true))] [:<> - [rn/view {:ref ref - :collapsable false} - [render message {:modal false - :on-long-press (fn [act] - (when (or (not outgoing) - (and outgoing (= outgoing-status :sent))) - (reset! actions act) - (get-picker-position ref on-open)))}] + [rn/view + {:ref ref + :collapsable false} + [render message + {:modal false + :on-long-press (fn [act] + (when (or (not outgoing) + (and outgoing (= outgoing-status :sent))) + (reset! actions act) + (get-picker-position ref on-open)))}] [reaction-row/message-reactions message reactions timeline]] (when @visible - [rn/modal {:on-request-close on-close - :on-show (fn [] - (js/requestAnimationFrame - #(animated/set-value animated-state 1))) - :transparent true} - [reaction-picker/modal {:outgoing (:outgoing message) - :display-photo (:display-photo? message) - :animation animation - :spring spring-animation - :top (:top @position) - :message-height (:height @position) - :on-close on-close - :actions @actions - :own-reactions own-reactions - :timeline timeline - :send-emoji (fn [emoji] - (on-close) - (js/setTimeout #(on-emoji-press emoji) - reaction-picker/animation-duration))} - [render message {:modal true - :on-long-press #() - :close-modal on-close}]]])])))) + [rn/modal + {:on-request-close on-close + :on-show (fn [] + (js/requestAnimationFrame + #(animated/set-value animated-state 1))) + :transparent true} + [reaction-picker/modal + {:outgoing (:outgoing message) + :display-photo (:display-photo? message) + :animation animation + :spring spring-animation + :top (:top @position) + :message-height (:height @position) + :on-close on-close + :actions @actions + :own-reactions own-reactions + :timeline timeline + :send-emoji (fn [emoji] + (on-close) + (js/setTimeout #(on-emoji-press emoji) + reaction-picker/animation-duration))} + [render message + {:modal true + :on-long-press #() + :close-modal on-close}]]])])))) diff --git a/src/status_im/ui/screens/chat/message/reactions_picker.cljs b/src/status_im/ui/screens/chat/message/reactions_picker.cljs index 85375f369a..c6b5ed7756 100644 --- a/src/status_im/ui/screens/chat/message/reactions_picker.cljs +++ b/src/status_im/ui/screens/chat/message/reactions_picker.cljs @@ -15,9 +15,9 @@ (def animation-duration 150) -(def scale 0.8) -(def translate-x 27) -(def translate-y -24) +(def scale 0.8) +(def translate-x 27) +(def translate-y -24) (def id-icon {"edit" :main-icons/edit @@ -25,35 +25,40 @@ "unpin" :main-icons/pin "copy" :main-icons/copy "reply" :main-icons/reply - "save" :main-icons/download + "save" :main-icons/download "share" :main-icons/share-default "delete" :main-icons/delete}) -(defn picker [{:keys [outgoing actions own-reactions on-close send-emoji timeline]}] +(defn picker + [{:keys [outgoing actions own-reactions on-close send-emoji timeline]}] [rn/view {:style (styles/container-style {:outgoing outgoing :timeline timeline})} [rn/view {:style (styles/reactions-picker-row)} (doall (for [[id resource] constants/reactions-old :let [active (own-reactions id)]] ^{:key id} - [rn/touchable-opacity {:accessibility-label (str "pick-emoji-" id) - :on-press #(send-emoji id)} + [rn/touchable-opacity + {:accessibility-label (str "pick-emoji-" id) + :on-press #(send-emoji id)} [rn/view {:style (styles/reaction-button active)} - [rn/image {:source resource - :style {:height 32 - :width 32}}]]]))] + [rn/image + {:source resource + :style {:height 32 + :width 32}}]]]))] (when (seq actions) [rn/view {:style (styles/quick-actions-container)} (doall (for [action actions :let [{:keys [id label on-press]} (bean/bean action)]] ^{:key id} - [rn/touchable-opacity {:on-press (fn [] - (on-close) - (js/setTimeout on-press animation-duration))} + [rn/touchable-opacity + {:on-press (fn [] + (on-close) + (js/setTimeout on-press animation-duration))} [rn/view {:style (styles/quick-actions-row)} - [quo/text {:color (if (= id "delete") :negative :link) - :weight :medium} label] + [quo/text + {:color (if (= id "delete") :negative :link) + :weight :medium} label] (when-let [icon (get id-icon id)] [icons/icon icon {:color (if (= id "delete") :red :blue)}])]]))])]) @@ -74,52 +79,59 @@ children :children timeline :timeline} (bean/bean props) - {bottom-inset :bottom} (safe-area/use-safe-area) + {bottom-inset :bottom} (safe-area/use-safe-area) {window-height :height} (rn/use-window-dimensions) {picker-height :height - on-picker-layout :on-layout} (rn/use-layout) + on-picker-layout :on-layout} + (rn/use-layout) - full-height (+ message-height picker-height top) - max-height (- window-height bottom-inset tabbar-height text-input-height) - top-delta (max 0 (- full-height max-height)) + full-height (+ message-height picker-height top) + max-height (- window-height bottom-inset tabbar-height text-input-height) + top-delta (max 0 (- full-height max-height)) translation-x (if (and outgoing (not timeline)) translate-x (* -1 translate-x))] (reagent/as-element [:<> - [rn/view {:style {:position :absolute - :flex 1 - :top 0 - :bottom 0 - :left 0 - :right 0}} + [rn/view + {:style {:position :absolute + :flex 1 + :top 0 + :bottom 0 + :left 0 + :right 0}} [rn/touchable-without-feedback {:on-press on-close} - [animated/view {:style {:flex 1 - :opacity animation - :background-color "rgba(0,0,0,0.5)"}}]]] - [animated/view {:pointer-events :box-none - :style {:top (- top top-delta) - :left 0 - :right 0 - :position :absolute - :opacity animation - :transform [{:translateY (animated/mix animation top-delta 0)}]}} + [animated/view + {:style {:flex 1 + :opacity animation + :background-color "rgba(0,0,0,0.5)"}}]]] + [animated/view + {:pointer-events :box-none + :style {:top (- top top-delta) + :left 0 + :right 0 + :position :absolute + :opacity animation + :transform [{:translateY (animated/mix animation top-delta 0)}]}} (into [:<>] (react/get-children children)) - [animated/view {:on-layout on-picker-layout - :pointer-events :box-none - :style (merge (styles/picker-wrapper-style {:display-photo? display-photo - :timeline timeline - :outgoing (and outgoing (not timeline))}) - {:opacity animation - :transform [{:translateX (animated/mix spring translation-x 0)} - {:translateY (animated/mix spring translate-y 0)} - {:scale (animated/mix spring scale 1)}]})} - [picker {:outgoing (and outgoing (not timeline)) - :timeline timeline - :actions actions - :on-close on-close - :own-reactions (into #{} (js->clj own-reactions)) - :send-emoji send-emoji - :animation animation}]]]]))))) + [animated/view + {:on-layout on-picker-layout + :pointer-events :box-none + :style (merge (styles/picker-wrapper-style + {:display-photo? display-photo + :timeline timeline + :outgoing (and outgoing (not timeline))}) + {:opacity animation + :transform [{:translateX (animated/mix spring translation-x 0)} + {:translateY (animated/mix spring translate-y 0)} + {:scale (animated/mix spring scale 1)}]})} + [picker + {:outgoing (and outgoing (not timeline)) + :timeline timeline + :actions actions + :on-close on-close + :own-reactions (into #{} (js->clj own-reactions)) + :send-emoji send-emoji + :animation animation}]]]]))))) diff --git a/src/status_im/ui/screens/chat/message/reactions_row.cljs b/src/status_im/ui/screens/chat/message/reactions_row.cljs index 5c92c01117..287f33f0ca 100644 --- a/src/status_im/ui/screens/chat/message/reactions_row.cljs +++ b/src/status_im/ui/screens/chat/message/reactions_row.cljs @@ -1,26 +1,30 @@ (ns status-im.ui.screens.chat.message.reactions-row - (:require [status-im.constants :as constants] - [status-im.ui.screens.chat.message.styles :as styles] - [quo.react-native :as rn] - [quo2.components.reactions.reaction :as quo2.reaction])) + (:require [quo.react-native :as rn] + [quo2.components.reactions.reaction :as quo2.reaction] + [status-im.constants :as constants] + [status-im.ui.screens.chat.message.styles :as styles])) (def default-reaction-margin-top 5) (def text-reaction-margin-top -3) -(defn message-reactions [{:keys [content-type]} reactions timeline on-emoji-press on-open] +(defn message-reactions + [{:keys [content-type]} reactions timeline on-emoji-press on-open] (when (seq reactions) - [rn/view {:style (styles/reactions-row - timeline - (if (= content-type constants/content-type-text) - text-reaction-margin-top default-reaction-margin-top))} + [rn/view + {:style (styles/reactions-row + timeline + (if (= content-type constants/content-type-text) + text-reaction-margin-top + default-reaction-margin-top))} (for [{:keys [own emoji-id quantity] :as emoji-reaction} reactions] ^{:key (str emoji-reaction)} [rn/view {:style {:margin-right 6 :margin-top 5}} - [quo2.reaction/reaction {:emoji (get constants/reactions emoji-id) - :neutral? own - :clicks quantity - :on-press #(on-emoji-press emoji-id) - :accessibility-label (str "emoji-reaction-" emoji-id)}]]) - ;; on-press won't work until we integrate Message Context Drawer + [quo2.reaction/reaction + {:emoji (get constants/reactions emoji-id) + :neutral? own + :clicks quantity + :on-press #(on-emoji-press emoji-id) + :accessibility-label (str "emoji-reaction-" emoji-id)}]]) + ;; on-press won't work until we integrate Message Context Drawer [quo2.reaction/open-reactions-menu (when @on-open {:on-press @on-open})]])) diff --git a/src/status_im/ui/screens/chat/message/reactions_row_old.cljs b/src/status_im/ui/screens/chat/message/reactions_row_old.cljs index fe05351041..2c94265ab3 100644 --- a/src/status_im/ui/screens/chat/message/reactions_row_old.cljs +++ b/src/status_im/ui/screens/chat/message/reactions_row_old.cljs @@ -1,21 +1,26 @@ (ns status-im.ui.screens.chat.message.reactions-row-old - (:require [status-im.constants :as constants] - [status-im.ui.screens.chat.message.styles :as styles] + (:require [quo.core :as quo] [quo.react-native :as rn] - [quo.core :as quo])) + [status-im.constants :as constants] + [status-im.ui.screens.chat.message.styles :as styles])) -(defn reaction [{:keys [outgoing]} {:keys [own emoji-id quantity]} timeline] - [rn/view {:style (styles/reaction-style {:outgoing (and outgoing (not timeline)) - :own own})} - [rn/image {:source (get constants/reactions-old emoji-id) - :style {:width 16 - :height 16 - :margin-right 4}}] - [quo/text {:accessibility-label (str "emoji-" emoji-id "-is-own-" own) - :style (styles/reaction-quantity-style {:own own})} +(defn reaction + [{:keys [outgoing]} {:keys [own emoji-id quantity]} timeline] + [rn/view + {:style (styles/reaction-style {:outgoing (and outgoing (not timeline)) + :own own})} + [rn/image + {:source (get constants/reactions-old emoji-id) + :style {:width 16 + :height 16 + :margin-right 4}}] + [quo/text + {:accessibility-label (str "emoji-" emoji-id "-is-own-" own) + :style (styles/reaction-quantity-style {:own own})} quantity]]) -(defn message-reactions [message reactions timeline] +(defn message-reactions + [message reactions timeline] (when (seq reactions) [rn/view {:style (styles/reactions-row-old message timeline)} (for [emoji-reaction reactions] diff --git a/src/status_im/ui/screens/chat/message/styles.cljs b/src/status_im/ui/screens/chat/message/styles.cljs index 492fe1d94c..127cb7f0bb 100644 --- a/src/status_im/ui/screens/chat/message/styles.cljs +++ b/src/status_im/ui/screens/chat/message/styles.cljs @@ -3,7 +3,8 @@ [status-im.ui.components.react :as react] [status-im.ui.screens.chat.styles.photos :as photos])) -(defn picker-wrapper-style [{:keys [display-photo? outgoing timeline]}] +(defn picker-wrapper-style + [{:keys [display-photo? outgoing timeline]}] (merge {:flex-direction :row :flex 1 :padding-top 4 @@ -16,31 +17,35 @@ {:padding-left (+ 16 photos/default-size)} {:padding-left 8})))) -(defn container-style [{:keys [outgoing timeline]}] +(defn container-style + [{:keys [outgoing timeline]}] (merge {:border-top-left-radius 16 :border-top-right-radius 16 :border-bottom-right-radius 16 :border-bottom-left-radius 16 :background-color (:ui-background @colors/theme)} (if timeline - {:border-top-left-radius 16 + {:border-top-left-radius 16 :border-top-right-radius 4} (if outgoing {:border-top-right-radius 4} {:border-top-left-radius 4})))) -(defn reactions-picker-row [] +(defn reactions-picker-row + [] {:flex-direction :row :padding-vertical 8 :padding-horizontal 8}) -(defn quick-actions-container [] +(defn quick-actions-container + [] {:flex-direction :column :justify-content :space-evenly :border-top-width 1 :border-top-color (:ui-01 @colors/theme)}) -(defn quick-actions-row [] +(defn quick-actions-row + [] {:flex-direction :row :padding-horizontal 16 :padding-vertical 12 @@ -48,7 +53,8 @@ :border-top-width 1 :border-top-color (:ui-01 @colors/theme)}) -(defn reaction-style [{:keys [outgoing own]}] +(defn reaction-style + [{:keys [outgoing own]}] (merge {:border-top-left-radius 10 :border-top-right-radius 10 :border-bottom-right-radius 10 @@ -67,7 +73,8 @@ {:border-top-left-radius 2 :margin-right 4}))) -(defn reaction-quantity-style [{:keys [own]}] +(defn reaction-quantity-style + [{:keys [own]}] {:font-size 12 :line-height 16 :color (if own @@ -79,7 +86,8 @@ react/get-dimensions :width)) -(defn reactions-row-old [{:keys [outgoing display-photo?]} timeline] +(defn reactions-row-old + [{:keys [outgoing display-photo?]} timeline] (merge {:flex-direction :row :padding-right 8} (if (and outgoing (not timeline)) @@ -89,17 +97,19 @@ {:padding-left (+ 30 photos/default-size (when timeline 8))} {:padding-left 30}))) -(defn reactions-row [timeline margin-top] +(defn reactions-row + [timeline margin-top] {:flex-direction :row :padding-right 8 - :padding-bottom 8 + :padding-bottom 8 :justify-content :flex-start :margin-top margin-top :flex-wrap :wrap :max-width (- screen-width (+ 30 photos/default-size (when timeline 8))) :margin-left (+ 30 photos/default-size (when timeline 8))}) -(defn reaction-button [active] +(defn reaction-button + [active] (merge {:width 40 :height 40 :border-radius 20 @@ -113,7 +123,8 @@ ;; FIXME: Use broder color here :border-color "rgba(67, 96, 223, 0.2)"}))) -(defn link-preview-request-wrapper [] +(defn link-preview-request-wrapper + [] {:border-radius 16 :border-width 1 :border-color colors/gray-lighter @@ -121,14 +132,15 @@ :background-color (:ui-background @colors/theme)}) (def link-preview-request-image - {:width 132 - :height 94 + {:width 132 + :height 94 :align-self :center}) (def community-preview-header {:margin 8 :margin-left 12}) -(defn link-preview-wrapper [outgoing timeline] +(defn link-preview-wrapper + [outgoing timeline] {:overflow :hidden :border-top-left-radius 16 :border-top-right-radius 16 @@ -150,7 +162,8 @@ {:width max-width :height (* aspect-ratio max-width)}))) -(defn link-preview-image [outgoing {:keys [height width] :as dimensions}] +(defn link-preview-image + [outgoing {:keys [height width] :as dimensions}] (merge (if (and (pos? height) (pos? width)) (scale-dimensions dimensions) diff --git a/src/status_im/ui/screens/chat/photos.cljs b/src/status_im/ui/screens/chat/photos.cljs index 57953014f8..215b6840b7 100644 --- a/src/status_im/ui/screens/chat/photos.cljs +++ b/src/status_im/ui/screens/chat/photos.cljs @@ -1,25 +1,28 @@ (ns status-im.ui.screens.chat.photos - (:require [re-frame.core :as re-frame] - [status-im.ui.components.react :as react] - [status-im.ui.screens.chat.styles.photos :as style] + (:require [quo.design-system.colors :as colors] + [re-frame.core :as re-frame] [status-im.multiaccounts.core :as multiaccounts] - [quo.design-system.colors :as colors] - [status-im.ui.components.fast-image :as fast-image])) + [status-im.ui.components.fast-image :as fast-image] + [status-im.ui.components.react :as react] + [status-im.ui.screens.chat.styles.photos :as style])) (def memo-photo-rend (memoize (fn [photo-path size accessibility-label _] [react/view {:style (style/photo-container size)} - [fast-image/fast-image {:source {:uri photo-path} - :style (style/photo size) - :accessibility-label (or accessibility-label :chat-icon)}] + [fast-image/fast-image + {:source {:uri photo-path} + :style (style/photo size) + :accessibility-label (or accessibility-label :chat-icon)}] [react/view {:style (style/photo-border size)}]]))) ;; "(colors/dark?)" is passed to memoized function to avoid previous themes cache -(defn photo [photo-path {:keys [size accessibility-label]}] +(defn photo + [photo-path {:keys [size accessibility-label]}] [memo-photo-rend photo-path size accessibility-label (colors/dark?)]) -;; We optionally pass identicon for perfomance reason, so it does not have to be calculated for each message +;; We optionally pass identicon for perfomance reason, so it does not have to be calculated for each +;; message (defn member-photo ([pub-key] (member-photo pub-key nil)) @@ -27,18 +30,23 @@ (member-photo pub-key identicon style/default-size)) ([pub-key identicon size] (let [path @(re-frame/subscribe [:chats/photo-path pub-key identicon])] - [photo path {:size size - :accessibility-label :member-photo}]))) + [photo path + {:size size + :accessibility-label :member-photo}]))) -(defn account-photo [account] +(defn account-photo + [account] (let [path (multiaccounts/displayed-photo account)] - [photo path {:size style/default-size - :accessibility-label :own-account-photo}])) + [photo path + {:size style/default-size + :accessibility-label :own-account-photo}])) -(defn member-identicon [identicon] +(defn member-identicon + [identicon] (let [size style/default-size] [react/view {:style (style/photo-container size)} - [fast-image/fast-image {:source {:uri identicon} - :style (style/photo size) - :accessibility-label :member-photo}] + [fast-image/fast-image + {:source {:uri identicon} + :style (style/photo size) + :accessibility-label :member-photo}] [react/view {:style (style/photo-border size)}]])) diff --git a/src/status_im/ui/screens/chat/pinned_messages.cljs b/src/status_im/ui/screens/chat/pinned_messages.cljs index 2e7cc0c848..16421e62a4 100644 --- a/src/status_im/ui/screens/chat/pinned_messages.cljs +++ b/src/status_im/ui/screens/chat/pinned_messages.cljs @@ -1,31 +1,36 @@ (ns status-im.ui.screens.chat.pinned-messages - (:require [re-frame.core :as re-frame] + (:require [quo.animated :as animated] + [quo.react :as quo.react] + [re-frame.core :as re-frame] [reagent.core :as reagent] [status-im.i18n.i18n :as i18n] [status-im.ui.components.connectivity.view :as connectivity] - [status-im.ui.components.react :as react] - [quo.animated :as animated] - [status-im.ui.screens.chat.styles.main :as style] - [status-im.ui.screens.chat.components.accessory :as accessory] - [status-im.utils.platform :as platform] - [quo.react :as quo.react] - [status-im.ui.components.topbar :as topbar] - [status-im.ui.screens.chat.views :as chat] [status-im.ui.components.list.views :as list] + [status-im.ui.components.react :as react] + [status-im.ui.components.topbar :as topbar] + [status-im.ui.screens.chat.components.accessory :as accessory] [status-im.ui.screens.chat.message.message :as message] - [status-im.utils.datetime :as time])) + [status-im.ui.screens.chat.styles.main :as style] + [status-im.ui.screens.chat.views :as chat] + [status-im.utils.datetime :as time] + [status-im.utils.platform :as platform])) -(defn pins-topbar [chat] +(defn pins-topbar + [chat] (let [{:keys [group-chat chat-id chat-name]} chat - pinned-messages @(re-frame/subscribe [:chats/pinned chat-id]) - [first-name _] (when-not group-chat @(re-frame.core/subscribe [:contacts/contact-two-names-by-identity chat-id]))] - [topbar/topbar {:show-border? true - :title (if group-chat chat-name first-name) - :subtitle (if (= (count pinned-messages) 0) - (i18n/label :t/no-pinned-messages) - (i18n/label-pluralize (count pinned-messages) :t/pinned-messages-count))}])) + pinned-messages @(re-frame/subscribe [:chats/pinned chat-id]) + [first-name _] (when-not group-chat + @(re-frame.core/subscribe + [:contacts/contact-two-names-by-identity chat-id]))] + [topbar/topbar + {:show-border? true + :title (if group-chat chat-name first-name) + :subtitle (if (= (count pinned-messages) 0) + (i18n/label :t/no-pinned-messages) + (i18n/label-pluralize (count pinned-messages) :t/pinned-messages-count))}])) -(defn get-space-keeper-ios [bottom-space panel-space active-panel text-input-ref] +(defn get-space-keeper-ios + [bottom-space panel-space active-panel text-input-ref] (fn [state] ;; NOTE: Only iOS now because we use soft input resize screen on android (when platform/ios? @@ -38,14 +43,17 @@ (and (not state) (< @panel-space @bottom-space)) (do - (some-> ^js (quo.react/current-ref text-input-ref) .focus) + (some-> ^js (quo.react/current-ref text-input-ref) + .focus) (reset! panel-space @bottom-space) (reset! bottom-space 0)))))) -(defn pinned-messages-empty [] - [react/view {:style {:flex 1 - :align-items :center - :justify-content :center}} +(defn pinned-messages-empty + [] + [react/view + {:style {:flex 1 + :align-items :center + :justify-content :center}} [react/text {:style style/intro-header-description} (i18n/label :t/pinned-messages-empty)]]) @@ -55,28 +63,32 @@ (def list-key-fn #(or (:message-id %) (:value %))) -(defn render-fn [{:keys [outgoing whisper-timestamp] :as message} - _ - _ - {:keys [group-chat public? current-public-key space-keeper show-input? message-pin-enabled edit-enabled in-pinned-view?]}] +(defn render-fn + [{:keys [outgoing whisper-timestamp] :as message} + _ + _ + {:keys [group-chat public? current-public-key space-keeper show-input? message-pin-enabled + edit-enabled in-pinned-view?]}] [react/view {:style (when (and platform/android? (not in-pinned-view?)) {:scaleY -1})} [message/chat-message (assoc message - :incoming-group (and group-chat (not outgoing)) - :group-chat group-chat - :public? public? - :current-public-key current-public-key - :show-input? show-input? + :incoming-group (and group-chat (not outgoing)) + :group-chat group-chat + :public? public? + :current-public-key current-public-key + :show-input? show-input? :message-pin-enabled message-pin-enabled - :edit-enabled edit-enabled - :display-username? (not outgoing) - :pinned true - :timestamp-str (time/timestamp->time whisper-timestamp)) + :edit-enabled edit-enabled + :display-username? (not outgoing) + :pinned true + :timestamp-str (time/timestamp->time whisper-timestamp)) space-keeper]]) -(defn pinned-messages-view [{:keys [chat pan-responder space-keeper]}] +(defn pinned-messages-view + [{:keys [chat pan-responder space-keeper]}] (let [{:keys [group-chat chat-id public? community-id admins]} chat - pinned-messages @(re-frame/subscribe [:chats/pinned-sorted-list chat-id])] + pinned-messages @(re-frame/subscribe + [:chats/pinned-sorted-list chat-id])] (if (= (count pinned-messages) 0) [pinned-messages-empty] ;;do not use anonymous functions for handlers @@ -96,24 +108,26 @@ :edit-enabled false :in-pinned-view? true}) :render-fn render-fn - :content-container-style {:padding-top 16 + :content-container-style {:padding-top 16 :padding-bottom 16}})]))) -(defn pinned-messages [] +(defn pinned-messages + [] (let [{:keys [chat-id]} @(re-frame/subscribe [:get-screen-params])] (fn [] - (let [bottom-space (reagent/atom 0) - panel-space (reagent/atom 52) - active-panel (reagent/atom nil) - position-y (animated/value 0) - pan-state (animated/value 0) + (let [bottom-space (reagent/atom 0) + panel-space (reagent/atom 52) + active-panel (reagent/atom nil) + position-y (animated/value 0) + pan-state (animated/value 0) text-input-ref (quo.react/create-ref) - pan-responder (accessory/create-pan-responder position-y pan-state) - space-keeper (get-space-keeper-ios bottom-space panel-space active-panel text-input-ref) - chat @(re-frame/subscribe [:chat-by-id chat-id])] + pan-responder (accessory/create-pan-responder position-y pan-state) + space-keeper (get-space-keeper-ios bottom-space panel-space active-panel text-input-ref) + chat @(re-frame/subscribe [:chat-by-id chat-id])] [:<> [pins-topbar chat] [connectivity/loading-indicator] - [pinned-messages-view {:chat chat - :pan-responder pan-responder - :space-keeper space-keeper}]])))) + [pinned-messages-view + {:chat chat + :pan-responder pan-responder + :space-keeper space-keeper}]])))) diff --git a/src/status_im/ui/screens/chat/sheets.cljs b/src/status_im/ui/screens/chat/sheets.cljs index d6aca7a282..3e201e8494 100644 --- a/src/status_im/ui/screens/chat/sheets.cljs +++ b/src/status_im/ui/screens/chat/sheets.cljs @@ -1,20 +1,22 @@ (ns status-im.ui.screens.chat.sheets - (:require [re-frame.core :as re-frame] - [status-im.i18n.i18n :as i18n] - [status-im.ui.components.react :as react] + (:require [quo.core :as quo] + [re-frame.core :as re-frame] [status-im.constants :as constants] - [status-im.ui.components.list-selection :as list-selection] - [status-im.utils.universal-links.utils :as universal-links] - [status-im.ui.components.chat-icon.screen :as chat-icon] + [status-im.i18n.i18n :as i18n] [status-im.multiaccounts.core :as multiaccounts] + [status-im.ui.components.chat-icon.screen :as chat-icon] + [status-im.ui.components.list-selection :as list-selection] + [status-im.ui.components.react :as react] [status-im.ui.screens.chat.styles.message.sheets :as sheets.styles] - [quo.core :as quo])) + [status-im.utils.universal-links.utils :as universal-links])) -(defn hide-sheet-and-dispatch [event] +(defn hide-sheet-and-dispatch + [event] (re-frame/dispatch [:bottom-sheet/hide]) (re-frame/dispatch event)) -(defn one-to-one-chat-accents [chat-id] +(defn one-to-one-chat-accents + [chat-id] (let [photo @(re-frame/subscribe [:chats/photo-path chat-id]) contact-name @(re-frame/subscribe [:contacts/contact-name-by-identity chat-id])] [react/view @@ -26,7 +28,7 @@ :accessibility-label :view-chat-details-button :chevron true :on-press #(do - (hide-sheet-and-dispatch [:chat.ui/show-profile chat-id]) + (hide-sheet-and-dispatch [:chat.ui/show-profile chat-id]) (re-frame/dispatch [:pin-message/load-pin-messages chat-id]))}] [quo/list-item {:theme :accent @@ -41,7 +43,8 @@ :icon :main-icons/delete :on-press #(re-frame/dispatch [:chat.ui/remove-chat-pressed chat-id])}]])) -(defn public-chat-accents [chat-id] +(defn public-chat-accents + [chat-id] (let [link (universal-links/generate-link :public-chat :external chat-id) message (i18n/label :t/share-public-chat-text {:link link})] [react/view @@ -75,7 +78,8 @@ :icon :main-icons/delete :on-press #(re-frame/dispatch [:chat.ui/remove-chat-pressed chat-id])}]])) -(defn community-chat-accents [] +(defn community-chat-accents + [] (fn [{:keys [chat-id group-chat chat-name color emoji]}] [react/view [quo/list-item @@ -87,7 +91,8 @@ :chevron true :accessibility-label :view-community-channel-details :on-press #(do - (hide-sheet-and-dispatch [:navigate-to :community-channel-details {:chat-id chat-id}]) + (hide-sheet-and-dispatch [:navigate-to :community-channel-details + {:chat-id chat-id}]) (re-frame/dispatch [:pin-message/load-pin-messages chat-id]))}] [quo/list-item {:theme :accent @@ -96,17 +101,19 @@ :icon :main-icons/check :on-press #(hide-sheet-and-dispatch [:chat.ui/mark-all-read-pressed chat-id])}]])) -(defn group-chat-accents [] +(defn group-chat-accents + [] (fn [{:keys [chat-id group-chat chat-name color invitation-admin]}] (let [{:keys [member?]} @(re-frame/subscribe [:group-chat/inviter-info chat-id]) - removed? @(re-frame/subscribe [:group-chat/removed-from-current-chat?])] + removed? @(re-frame/subscribe [:group-chat/removed-from-current-chat?])] (if invitation-admin [quo/list-item {:theme :accent :title (i18n/label :t/remove) :accessibility-label :remove-group-chat :icon :main-icons/delete - :on-press #(hide-sheet-and-dispatch [:group-chats.ui/remove-chat-confirmed chat-id])}] + :on-press #(hide-sheet-and-dispatch [:group-chats.ui/remove-chat-confirmed + chat-id])}] [react/view [quo/list-item {:theme :accent @@ -137,14 +144,17 @@ :title (i18n/label :t/remove) :accessibility-label :remove-group-chat :icon :main-icons/delete - :on-press #(hide-sheet-and-dispatch [:group-chats.ui/remove-chat-confirmed chat-id])}])])))) + :on-press #(hide-sheet-and-dispatch [:group-chats.ui/remove-chat-confirmed + chat-id])}])])))) -(defn actions [{:keys [chat-type chat-id] - :as current-chat}] +(defn actions + [{:keys [chat-type chat-id] + :as current-chat}] (cond (#{constants/public-chat-type constants/profile-chat-type - constants/timeline-chat-type} chat-type) + constants/timeline-chat-type} + chat-type) [public-chat-accents chat-id] (= chat-type constants/community-chat-type) @@ -153,15 +163,18 @@ (= chat-type constants/private-group-chat-type) [group-chat-accents current-chat] - :else [one-to-one-chat-accents chat-id])) + :else [one-to-one-chat-accents chat-id])) -(defn current-chat-actions [] +(defn current-chat-actions + [] [actions @(re-frame/subscribe [:chats/current-chat])]) -(defn chat-actions [chat-id] +(defn chat-actions + [chat-id] [actions @(re-frame/subscribe [:chat-by-id chat-id])]) -(defn options [chat-id message-id] +(defn options + [chat-id message-id] (fn [] [react/view [react/i18n-text {:style sheets.styles/sheet-text :key :message-not-sent}] @@ -176,9 +189,11 @@ :title (i18n/label :t/delete-message) :icon :main-icons/delete :accessibility-label :delete-transaccent-button - :on-press #(hide-sheet-and-dispatch [:chat.ui/delete-message-not-used-any-more chat-id message-id])}]])) + :on-press #(hide-sheet-and-dispatch [:chat.ui/delete-message-not-used-any-more chat-id + message-id])}]])) -(defn image-long-press [{:keys [content identicon from outgoing cant-be-replied] :as message} hide] +(defn image-long-press + [{:keys [content identicon from outgoing cant-be-replied] :as message} hide] (let [contact-name @(re-frame/subscribe [:contacts/contact-name-by-identity from])] [react/view (when-not outgoing diff --git a/src/status_im/ui/screens/chat/state.cljs b/src/status_im/ui/screens/chat/state.cljs index 3ed3b20039..f83cde1e0a 100644 --- a/src/status_im/ui/screens/chat/state.cljs +++ b/src/status_im/ui/screens/chat/state.cljs @@ -4,11 +4,14 @@ (defonce scrolling (atom nil)) -(defn start-scrolling [] +(defn start-scrolling + [] (reset! scrolling true)) -(defn stop-scrolling [] +(defn stop-scrolling + [] (reset! scrolling false)) -(defn reset-visible-item [] +(defn reset-visible-item + [] (reset! first-not-visible-item nil)) diff --git a/src/status_im/ui/screens/chat/stickers/styles.cljs b/src/status_im/ui/screens/chat/stickers/styles.cljs index dd82c0e52c..beff895e40 100644 --- a/src/status_im/ui/screens/chat/stickers/styles.cljs +++ b/src/status_im/ui/screens/chat/stickers/styles.cljs @@ -1,8 +1,10 @@ (ns status-im.ui.screens.chat.stickers.styles) -(def stickers-panel {:flex 1 :margin 5 :flex-direction :row :justify-content :space-between :flex-wrap :wrap}) +(def stickers-panel + {:flex 1 :margin 5 :flex-direction :row :justify-content :space-between :flex-wrap :wrap}) -(defn pack-icon [background-color icon-size icon-horizontal-margin] +(defn pack-icon + [background-color icon-size icon-horizontal-margin] {:background-color background-color :margin-vertical 5 :margin-horizontal icon-horizontal-margin diff --git a/src/status_im/ui/screens/chat/stickers/views.cljs b/src/status_im/ui/screens/chat/stickers/views.cljs index 5dfebee103..b9702f7aae 100644 --- a/src/status_im/ui/screens/chat/stickers/views.cljs +++ b/src/status_im/ui/screens/chat/stickers/views.cljs @@ -1,61 +1,72 @@ (ns status-im.ui.screens.chat.stickers.views (:require-macros [status-im.utils.views :refer [defview letsubs]]) - (:require [re-frame.core :as re-frame] - [reagent.core :as reagent] - [status-im.ui.components.react :as react] - [status-im.ui.components.icons.icons :as icons] + (:require [quo.core :as quo] [quo.design-system.colors :as colors] + [re-frame.core :as re-frame] + [reagent.core :as reagent] [status-im.i18n.i18n :as i18n] - [quo.core :as quo] + [status-im.ui.components.fast-image :as fast-image] + [status-im.ui.components.icons.icons :as icons] + [status-im.ui.components.react :as react] [status-im.ui.screens.chat.stickers.styles :as styles] - [utils.debounce :as debounce] - [status-im.ui.components.fast-image :as fast-image])) + [utils.debounce :as debounce])) (def icon-size 28) (def icon-horizontal-margin 8) (def indicator-width 16) (def dx (- (+ icon-horizontal-margin (/ icon-size 2)) (/ indicator-width 2))) -(def icon-container (+ (* icon-horizontal-margin 2) icon-size)) +(def icon-container (+ (* icon-horizontal-margin 2) icon-size)) (def scroll-x (reagent/atom 0)) -(defn- no-stickers-yet-panel [] +(defn- no-stickers-yet-panel + [] [react/view {:style {:flex 1 :align-items :center :justify-content :center}} - [icons/icon :stickers-icons/stickers-big {:color colors/gray - :width 64 - :height 64}] + [icons/icon :stickers-icons/stickers-big + {:color colors/gray + :width 64 + :height 64}] [react/text {:style {:margin-vertical 8 :font-size 17}} (i18n/label :t/you-dont-have-stickers)] - [quo/button {:type :secondary - :on-press #(re-frame/dispatch [:navigate-to :stickers])} + [quo/button + {:type :secondary + :on-press #(re-frame/dispatch [:navigate-to :stickers])} (i18n/label :t/get-stickers)]]) -(defn- stickers-panel [stickers window-width] +(defn- stickers-panel + [stickers window-width] [react/view {:width window-width :flex 1} [react/scroll-view [react/view {:style styles/stickers-panel} (for [{:keys [url] :as sticker} stickers] ^{:key (str url)} - [react/touchable-highlight {:style {:height 75 :width 75 :margin 5} - :on-press #(debounce/dispatch-and-chill [:chat/send-sticker sticker] 1000)} - [fast-image/fast-image {:style {:width "100%" :height "100%"} - :accessibility-label :sticker-icon - :source {:uri (str url "&download=true")}}]])]]]) + [react/touchable-highlight + {:style {:height 75 :width 75 :margin 5} + :on-press #(debounce/dispatch-and-chill [:chat/send-sticker sticker] 1000)} + [fast-image/fast-image + {:style {:width "100%" :height "100%"} + :accessibility-label :sticker-icon + :source {:uri (str url "&download=true")}}]])]]]) -(defview recent-stickers-panel [window-width] +(defview recent-stickers-panel + [window-width] (letsubs [stickers [:stickers/recent-stickers]] (if (seq stickers) [stickers-panel stickers window-width] - [react/view {:style {:flex 1 - :align-items :center - :justify-content :center - :width window-width}} - [icons/icon :stickers-icons/sticker-history {:width 64 - :height 64 - :color colors/gray}] - [react/text {:style {:margin-top 12 - :font-size 17}} + [react/view + {:style {:flex 1 + :align-items :center + :justify-content :center + :width window-width}} + [icons/icon :stickers-icons/sticker-history + {:width 64 + :height 64 + :color colors/gray}] + [react/text + {:style {:margin-top 12 + :font-size 17}} (i18n/label :t/recently-used-stickers)]]))) -(defn update-scroll-position [^js ref installed-packs selected-pack window-width animated?] +(defn update-scroll-position + [^js ref installed-packs selected-pack window-width animated?] (when ref ;; bug on Android https://github.com/facebook/react-native/issues/24531 (js/setTimeout @@ -68,7 +79,8 @@ (.scrollTo ref #js {:x x :animated animated?}))) 1))) -(defn on-scroll [^js e installed-packs window-width] +(defn on-scroll + [^js e installed-packs window-width] (let [num (/ (.-nativeEvent.contentOffset.x e) window-width) pack-id (if (zero? num) :recent @@ -76,68 +88,81 @@ (when pack-id (re-frame/dispatch [:stickers/select-pack pack-id])))) -(defview stickers-paging-panel [installed-packs selected-pack] +(defview stickers-paging-panel + [installed-packs selected-pack] (letsubs [ref (atom nil) width [:dimensions/window-width]] - {:UNSAFE_componentWillUpdate (fn [_ [_ installed-packs selected-pack]] - (update-scroll-position @ref installed-packs selected-pack width true)) - :component-did-mount #(update-scroll-position @ref installed-packs selected-pack width false)} - [react/scroll-view {:style {:flex 1} - :horizontal true - :paging-enabled true - :ref #(reset! ref %) - :shows-horizontal-scroll-indicator false - :on-momentum-scroll-end #(on-scroll % installed-packs width) - :scroll-event-throttle 8 - :scroll-to-overflow-enabled true - :on-scroll #(reset! scroll-x (.-nativeEvent.contentOffset.x ^js %))} + {:UNSAFE_componentWillUpdate + (fn [_ [_ installed-packs selected-pack]] + (update-scroll-position @ref installed-packs selected-pack width true)) + :component-did-mount #(update-scroll-position @ref installed-packs selected-pack width false)} + [react/scroll-view + {:style {:flex 1} + :horizontal true + :paging-enabled true + :ref #(reset! ref %) + :shows-horizontal-scroll-indicator false + :on-momentum-scroll-end #(on-scroll % installed-packs width) + :scroll-event-throttle 8 + :scroll-to-overflow-enabled true + :on-scroll #(reset! scroll-x (.-nativeEvent.contentOffset.x ^js %))} ^{:key "recent"} [recent-stickers-panel width] (for [{:keys [stickers id]} installed-packs] ^{:key (str "sticker" id)} [stickers-panel (map #(assoc % :pack id) stickers) width])])) -(defn pack-icon [{:keys [id on-press background-color] - :or {on-press #(re-frame/dispatch [:stickers/select-pack id])}} - icon] +(defn pack-icon + [{:keys [id on-press background-color] + :or {on-press #(re-frame/dispatch [:stickers/select-pack id])}} + icon] [react/touchable-highlight {:on-press on-press} [react/view {:style {:align-items :center}} [react/view {:style (styles/pack-icon background-color icon-size icon-horizontal-margin)} icon]]]) -(defview scroll-indicator [] +(defview scroll-indicator + [] (letsubs [window-width [:dimensions/window-width]] - [react/view {:style {:height 2 - :width indicator-width - :border-radius 1 - :margin-left (+ dx (* icon-container (/ @scroll-x window-width))) - :background-color colors/blue}}])) + [react/view + {:style {:height 2 + :width indicator-width + :border-radius 1 + :margin-left (+ dx (* icon-container (/ @scroll-x window-width))) + :background-color colors/blue}}])) -(defview stickers-view [] - (letsubs [selected-pack [:stickers/selected-pack] - installed-packs [:stickers/installed-packs]] - [react/view {:style {:background-color colors/white - :flex 1}} +(defview stickers-view + [] + (letsubs [selected-pack [:stickers/selected-pack] + installed-packs [:stickers/installed-packs]] + [react/view + {:style {:background-color colors/white + :flex 1}} (cond (= selected-pack :recent) [stickers-paging-panel installed-packs selected-pack] (not (seq installed-packs)) [no-stickers-yet-panel] :else [stickers-paging-panel installed-packs selected-pack]) [react/view {:style {:flex-direction :row :padding-horizontal 4}} - [pack-icon {:on-press #(re-frame/dispatch [:navigate-to :stickers]) - :selected? false :background-color colors/blue} + [pack-icon + {:on-press #(re-frame/dispatch [:navigate-to :stickers]) + :selected? false + :background-color colors/blue} [icons/icon :main-icons/add {:width 20 :height 20 :color colors/white-persist}]] [react/view {:width 2}] [react/scroll-view {:horizontal true :style {:padding-left 2}} [react/view [react/view {:style {:flex-direction :row}} [pack-icon {:id :recent :background-color colors/white} - [icons/icon :stickers-icons/recent {:color colors/gray - :width 44 - :height 44}]] + [icons/icon :stickers-icons/recent + {:color colors/gray + :width 44 + :height 44}]] (for [{:keys [id thumbnail]} installed-packs] ^{:key (str "pack-icon" id)} - [pack-icon {:id id - :background-color colors/white} - [fast-image/fast-image {:style {:width icon-size :height icon-size :border-radius (/ icon-size 2)} - :source {:uri (str thumbnail "&download=true")}}]])] + [pack-icon + {:id id + :background-color colors/white} + [fast-image/fast-image + {:style {:width icon-size :height icon-size :border-radius (/ icon-size 2)} + :source {:uri (str thumbnail "&download=true")}}]])] [scroll-indicator]]]]])) diff --git a/src/status_im/ui/screens/chat/styles/input/gap.cljs b/src/status_im/ui/screens/chat/styles/input/gap.cljs index 9b9cd58822..58c48328e8 100644 --- a/src/status_im/ui/screens/chat/styles/input/gap.cljs +++ b/src/status_im/ui/screens/chat/styles/input/gap.cljs @@ -1,7 +1,8 @@ (ns status-im.ui.screens.chat.styles.input.gap (:require [quo.design-system.colors :as colors])) -(defn gap-container [] +(defn gap-container + [] {:align-self :stretch :margin-top 24 :margin-bottom 24 @@ -18,7 +19,8 @@ :justify-content :center :text-align :center}) -(defn gap-text [connected?] +(defn gap-text + [connected?] {:text-align :center :color (if connected? colors/blue diff --git a/src/status_im/ui/screens/chat/styles/main.cljs b/src/status_im/ui/screens/chat/styles/main.cljs index f037a9d1d8..661d2b5e36 100644 --- a/src/status_im/ui/screens/chat/styles/main.cljs +++ b/src/status_im/ui/screens/chat/styles/main.cljs @@ -28,7 +28,8 @@ ;; this map looks a bit strange ;; but this way of setting elevation seems to be the only way to set z-index (in RN 0.30) -(defn add-contact [] +(defn add-contact + [] {:flex-direction :row :align-items :center :justify-content :center @@ -50,16 +51,17 @@ (defn intro-header-container [loading-messages? no-messages?] (if (or loading-messages? no-messages?) - {:flex 1 - :flex-direction :column - :justify-content :center - :align-items :center} - {:flex 1 - :flex-direction :column - :justify-content :center - :align-items :center})) + {:flex 1 + :flex-direction :column + :justify-content :center + :align-items :center} + {:flex 1 + :flex-direction :column + :justify-content :center + :align-items :center})) -(defn intro-header-icon [diameter color] +(defn intro-header-icon + [diameter color] {:width diameter :height diameter :align-items :center @@ -76,11 +78,13 @@ :opacity 0.8 :line-height 72}) -(defn emoji-intro-header-icon-text [size] - {:font-size (emoji-utils/emoji-font-size size) - :margin-top (emoji-utils/emoji-top-margin-for-vertical-alignment size)}) ;; Required for vertical alignment bug - Check function defination for more info +(defn emoji-intro-header-icon-text + [size] + {:font-size (emoji-utils/emoji-font-size size) + :margin-top (emoji-utils/emoji-top-margin-for-vertical-alignment size)}) ;; Required for vertical alignment bug - Check function defination for more info -(defn intro-header-chat-name [] +(defn intro-header-chat-name + [] {:font-size 22 :font-weight "700" :line-height 28 @@ -144,9 +148,9 @@ :font-size 15}) (def tribute-received-note - {:font-size 13 + {:font-size 13 :line-height 18 - :text-align :center}) + :text-align :center}) (def contact-request {:width "100%" diff --git a/src/status_im/ui/screens/chat/styles/message/audio.cljs b/src/status_im/ui/screens/chat/styles/message/audio.cljs index a5120cdfaf..febd8ff6b3 100644 --- a/src/status_im/ui/screens/chat/styles/message/audio.cljs +++ b/src/status_im/ui/screens/chat/styles/message/audio.cljs @@ -3,29 +3,32 @@ [status-im.ui.screens.chat.styles.message.message :as message.style] [status-im.utils.platform :as platform])) -(defn container [window-width] - {:width (* window-width 0.60) - :flex-direction :column +(defn container + [window-width] + {:width (* window-width 0.60) + :flex-direction :column :justify-content :space-between}) (def play-pause-slider-container {:flex-direction :row - :align-items :center}) + :align-items :center}) (def slider-container {:flex-direction :column - :align-items :stretch - :flex-grow 1}) + :align-items :stretch + :flex-grow 1}) -(defn slider [] - {:style (merge {:margin-left 12 - :height 34} - (when platform/ios? {:margin-bottom 4})) - :thumb-tint-color colors/white +(defn slider + [] + {:style (merge {:margin-left 12 + :height 34} + (when platform/ios? {:margin-bottom 4})) + :thumb-tint-color colors/white :minimum-track-tint-color colors/white :maximum-track-tint-color colors/white-transparent}) -(defn play-pause-container [loading?] +(defn play-pause-container + [loading?] {:background-color colors/white-persist :width 28 :height 28 @@ -33,9 +36,10 @@ :border-radius 15}) (def times-container - {:flex-direction :row + {:flex-direction :row :justify-content :space-between}) -(defn timestamp [] +(defn timestamp + [] (merge (message.style/audio-message-timestamp-text) {:margin-left 40})) \ No newline at end of file diff --git a/src/status_im/ui/screens/chat/styles/message/audio_old.cljs b/src/status_im/ui/screens/chat/styles/message/audio_old.cljs index a0b219c52b..cccc477380 100644 --- a/src/status_im/ui/screens/chat/styles/message/audio_old.cljs +++ b/src/status_im/ui/screens/chat/styles/message/audio_old.cljs @@ -3,27 +3,29 @@ [status-im.ui.screens.chat.styles.message.message-old :as message.style] [status-im.utils.platform :as platform])) -(defn container [window-width] - {:width (* window-width 0.60) - :flex-direction :column +(defn container + [window-width] + {:width (* window-width 0.60) + :flex-direction :column :justify-content :space-between}) (def play-pause-slider-container {:flex-direction :row - :align-items :center}) + :align-items :center}) (def slider-container {:flex-direction :column - :align-items :stretch - :flex-grow 1}) + :align-items :stretch + :flex-grow 1}) -(defn slider [outgoing] - {:style (merge {:margin-left 12 - :height 34} - (when platform/ios? {:margin-bottom 4})) - :thumb-tint-color (if outgoing - colors/white - colors/blue) +(defn slider + [outgoing] + {:style (merge {:margin-left 12 + :height 34} + (when platform/ios? {:margin-bottom 4})) + :thumb-tint-color (if outgoing + colors/white + colors/blue) :minimum-track-tint-color (if outgoing colors/white colors/blue) @@ -31,7 +33,8 @@ colors/white-transparent colors/gray-transparent-40)}) -(defn play-pause-container [outgoing? loading?] +(defn play-pause-container + [outgoing? loading?] {:background-color (if outgoing? colors/white-persist colors/blue) :width 28 :height 28 @@ -39,9 +42,10 @@ :border-radius 15}) (def times-container - {:flex-direction :row + {:flex-direction :row :justify-content :space-between}) -(defn timestamp [outgoing] +(defn timestamp + [outgoing] (merge (message.style/audio-message-timestamp-text outgoing) {:margin-left 40})) \ No newline at end of file diff --git a/src/status_im/ui/screens/chat/styles/message/datemark.cljs b/src/status_im/ui/screens/chat/styles/message/datemark.cljs index fd74b58f5f..ca6aadfda2 100644 --- a/src/status_im/ui/screens/chat/styles/message/datemark.cljs +++ b/src/status_im/ui/screens/chat/styles/message/datemark.cljs @@ -2,21 +2,23 @@ (:require [quo2.foundations.colors :as quo2.colors])) (def datemark-mobile - {:flex 1 + {:flex 1 :justify-content :center :margin-vertical 16 - :padding-left 65}) + :padding-left 65}) -(defn divider [] - {:flex 1 - :width "100%" - :height 1 - :padding-left 53 +(defn divider + [] + {:flex 1 + :width "100%" + :height 1 + :padding-left 53 :background-color (quo2.colors/theme-colors quo2.colors/divider-light quo2.colors/divider-dark) - :margin-top 5}) + :margin-top 5}) -(defn datemark-text [] - {:color quo2.colors/neutral-50 - :font-size 14 +(defn datemark-text + [] + {:color quo2.colors/neutral-50 + :font-size 14 :line-height 16 :font-weight "500"}) diff --git a/src/status_im/ui/screens/chat/styles/message/datemark_old.cljs b/src/status_im/ui/screens/chat/styles/message/datemark_old.cljs index b2098f57fb..3e8e9960bc 100644 --- a/src/status_im/ui/screens/chat/styles/message/datemark_old.cljs +++ b/src/status_im/ui/screens/chat/styles/message/datemark_old.cljs @@ -7,5 +7,6 @@ :margin-top 16 :height 22}) -(defn datemark-text [] +(defn datemark-text + [] {:color colors/gray}) \ No newline at end of file diff --git a/src/status_im/ui/screens/chat/styles/message/message.cljs b/src/status_im/ui/screens/chat/styles/message/message.cljs index f5dceed131..388496d053 100644 --- a/src/status_im/ui/screens/chat/styles/message/message.cljs +++ b/src/status_im/ui/screens/chat/styles/message/message.cljs @@ -1,10 +1,10 @@ (ns status-im.ui.screens.chat.styles.message.message - (:require [status-im.constants :as constants] - [quo.design-system.colors :as colors] - [status-im.ui.components.react :as react] - [status-im.ui.screens.chat.styles.photos :as photos] + (:require [quo.design-system.colors :as colors] [quo2.foundations.colors :as quo2.colors] - [quo2.foundations.typography :as typography])) + [quo2.foundations.typography :as typography] + [status-im.constants :as constants] + [status-im.ui.components.react :as react] + [status-im.ui.screens.chat.styles.photos :as photos])) (defn style-message-text [] @@ -12,11 +12,11 @@ (defn system-message-body [_] - {:margin-top 4 - :margin-left 8 - :margin-right 8 - :align-self :center - :align-items :center}) + {:margin-top 4 + :margin-left 8 + :margin-right 8 + :align-self :center + :align-items :center}) (defn message-body [] @@ -28,25 +28,28 @@ :align-items align})) (def message-timestamp - {:font-size 10}) + {:font-size 10}) (defn message-status-placeholder [] (merge message-timestamp {:opacity 0 :color "rgba(0,0,0,0)"})) -(defn message-timestamp-wrapper [] +(defn message-timestamp-wrapper + [] {:justify-content :center - :margin-left 12 ;; horizontal margin is only required at the adjust side of the message. - :margin-top 0 - :opacity 0}) + :margin-left 12 ;; horizontal margin is only required at the adjust side of the message. + :margin-top 0 + :opacity 0}) -(defn message-timestamp-text [] +(defn message-timestamp-text + [] (merge message-timestamp - {:color colors/gray + {:color colors/gray :text-align :center})) -(defn message-status-text [] - {:font-size 10 +(defn message-status-text + [] + {:font-size 10 :line-height 10 :color colors/gray}) @@ -56,66 +59,76 @@ {:line-height 10 :color colors/gray})) -(defn message-wrapper [{:keys [in-popover?]}] +(defn message-wrapper + [{:keys [in-popover?]}] (if (not in-popover?) - {:margin-left 10 + {:margin-left 10 :padding-right 5} {:margin-right 10})) -(defn message-author-wrapper [] +(defn message-author-wrapper + [] {:flex-direction :column - :flex-shrink 1 - :align-items :flex-start - :margin-left 4}) + :flex-shrink 1 + :align-items :flex-start + :margin-left 4}) -(defn delivery-status [] - {:align-self :flex-start - :padding-left 8}) +(defn delivery-status + [] + {:align-self :flex-start + :padding-left 8}) -(defn pin-indicator [] +(defn pin-indicator + [] (merge {:flex-direction :row})) -(defn pin-indicator-container [] - {:margin-top 4 - :margin-left 54 - :top 4 +(defn pin-indicator-container + [] + {:margin-top 4 + :margin-left 54 + :top 4 :justify-content :center - :align-self :flex-start - :align-items :flex-start}) + :align-self :flex-start + :align-items :flex-start}) -(defn pinned-by-text-icon-container [] +(defn pinned-by-text-icon-container + [] {:flex-direction :row :align-items :flex-start :top 5 :left 8 :position :absolute}) -(defn pin-icon-container [] +(defn pin-icon-container + [] {:flex-direction :row :margin-top 1}) -(defn pin-author-text [] +(defn pin-author-text + [] (merge typography/font-medium - {:color quo2.colors/primary-50 + {:color quo2.colors/primary-50 :bottom 2})) -(defn pinned-by-text [] +(defn pinned-by-text + [] {:margin-left 5}) (def message-author-touchable {:margin-left 0 :flex-direction :row}) -(defn message-author-userpic [] +(defn message-author-userpic + [] (merge - {:width (+ 16 photos/default-size)} ;; 16 is for the padding - {:padding-left 0 - :padding-right 8})) + {:width (+ 16 photos/default-size)} ;; 16 is for the padding + {:padding-left 0 + :padding-right 8})) (def delivery-text - {:color colors/gray - :margin-top 2 - :font-size 12}) + {:color colors/gray + :margin-top 2 + :font-size 12}) (def not-sent-view {:flex-direction :row @@ -124,8 +137,8 @@ (def not-sent-text (assoc delivery-text - :color colors/red - :text-align :right + :color colors/red + :text-align :right :padding-top 4)) (def not-sent-icon @@ -139,17 +152,22 @@ :margin-right 0 ;; Margin to display outgoing message status :margin-top (if incoming-group 4 0)}) -(defn collapse-button [] - {:height 24 :width 24 :background-color colors/blue - :border-radius 12 :align-items :center :justify-content :center - :elevation 4 - :shadow-opacity 1 - :shadow-radius 16 - :shadow-color (:shadow-01 @colors/theme) - :shadow-offset {:width 0 :height 4}}) +(defn collapse-button + [] + {:height 24 + :width 24 + :background-color colors/blue + :border-radius 12 + :align-items :center + :justify-content :center + :elevation 4 + :shadow-opacity 1 + :shadow-radius 16 + :shadow-color (:shadow-01 @colors/theme) + :shadow-offset {:width 0 :height 4}}) (def message-view-wrapper - {:align-self :flex-end + {:align-self :flex-end :flex-direction :row-reverse}) (defn message-view @@ -159,26 +177,30 @@ (when (= content-type constants/content-type-emoji) {:flex-direction :row}))) -(defn message-view-content [] +(defn message-view-content + [] {:padding-bottom 6 :overflow :hidden}) (def status-container {:padding-horizontal 5}) -(defn status-text [] - {:margin-top 9 - :font-size 14 - :color colors/gray}) +(defn status-text + [] + {:margin-top 9 + :font-size 14 + :color colors/gray}) -(defn message-author-name [chosen?] +(defn message-author-name + [chosen?] {:font-size (if chosen? 13 12) :font-weight (if chosen? "500" "400") :padding-top 6 :padding-left 12 :text-align-vertical :center}) -(defn quoted-message-container [] +(defn quoted-message-container + [] {:margin-bottom 6 :margin-top 5 :padding-horizontal 15}) @@ -188,85 +210,116 @@ :align-items :center :justify-content :flex-start}) -(defn quoted-message-author [chosen?] +(defn quoted-message-author + [chosen?] (assoc (message-author-name chosen?) - :padding-bottom 6 - :padding-top 0 - :padding-left 0 - :line-height 18 + :padding-bottom 6 + :padding-top 0 + :padding-left 0 + :line-height 18 :font-weight "500" - :color colors/gray)) + :color colors/gray)) -(defn quoted-message-text [] +(defn quoted-message-text + [] {:font-size 14 - :color colors/gray}) + :color colors/gray}) -(defn message-default-style [] - {:font-family "Inter-Regular" - :color (quo2.colors/theme-colors quo2.colors/neutral-100 quo2.colors/white) - :font-size 15 - :line-height 21.75 +(defn message-default-style + [] + {:font-family "Inter-Regular" + :color (quo2.colors/theme-colors quo2.colors/neutral-100 quo2.colors/white) + :font-size 15 + :line-height 21.75 :letter-spacing -0.135}) ;; Markdown styles -(defn default-text-style [] +(defn default-text-style + [] {:max-font-size-multiplier react/max-font-size-multiplier - :style (message-default-style)}) + :style (message-default-style)}) -(defn outgoing-text-style [] - (update (default-text-style) :style - assoc :color colors/white-persist)) +(defn outgoing-text-style + [] + (update (default-text-style) + :style + assoc + :color colors/white-persist)) -(defn system-text-style [] - (update (default-text-style) :style assoc - :color colors/gray +(defn system-text-style + [] + (update (default-text-style) + :style assoc + :color colors/gray :line-height 20 - :font-size 14 - :text-align :center + :font-size 14 + :text-align :center :font-weight "400")) -(defn text-style [content-type in-popover?] +(defn text-style + [content-type in-popover?] (merge (when in-popover? {:number-of-lines 2}) (cond (= content-type constants/content-type-system-text) (system-text-style) - :else (default-text-style)))) + :else (default-text-style)))) -(defn emph-text-style [] - (update (default-text-style) :style - assoc :font-style :italic)) +(defn emph-text-style + [] + (update (default-text-style) + :style + assoc + :font-style :italic)) -(defn outgoing-emph-text-style [] - (update (emph-text-style) :style - assoc :color colors/white-persist)) +(defn outgoing-emph-text-style + [] + (update (emph-text-style) + :style + assoc + :color colors/white-persist)) -(defn emph-style [] +(defn emph-style + [] (emph-text-style)) -(defn strong-text-style [] - (update (default-text-style) :style - assoc :font-weight "700")) +(defn strong-text-style + [] + (update (default-text-style) + :style + assoc + :font-weight "700")) -(defn outgoing-strong-text-style [] - (update (strong-text-style) :style - assoc :color colors/white-persist)) +(defn outgoing-strong-text-style + [] + (update (strong-text-style) + :style + assoc + :color colors/white-persist)) -(defn strong-style [] +(defn strong-style + [] (outgoing-strong-text-style) (strong-text-style)) -(defn strong-emph-style [] - (update (strong-style) :style - assoc :font-style :italic)) +(defn strong-emph-style + [] + (update (strong-style) + :style + assoc + :font-style :italic)) -(defn strikethrough-style [] - (cond-> (update (default-text-style) :style - assoc :text-decoration-line :line-through))) +(defn strikethrough-style + [] + (cond-> (update (default-text-style) + :style + assoc + :text-decoration-line :line-through))) (def code-block-background "#2E386B") -(defn inline-code-style [] +(defn inline-code-style + [] {:color colors/white-persist :background-color code-block-background}) @@ -278,32 +331,41 @@ (def codeblock-text-style {:color colors/white-persist}) -(defn default-blockquote-style [] +(defn default-blockquote-style + [] {:style {:border-left-width 2 - :padding-left 3 + :padding-left 3 :border-left-color colors/gray-transparent-40}}) -(defn outgoing-blockquote-style [] - (update (default-blockquote-style) :style +(defn outgoing-blockquote-style + [] + (update (default-blockquote-style) + :style assoc :border-left-color colors/white-transparent-70-persist)) -(defn blockquote-style [] +(defn blockquote-style + [] (default-blockquote-style)) -(defn default-blockquote-text-style [] - (update (default-text-style) :style +(defn default-blockquote-text-style + [] + (update (default-text-style) + :style assoc :line-height 19 - :font-size 14 - :color colors/black-transparent-50)) + :font-size 14 + :color colors/black-transparent-50)) -(defn outgoing-blockquote-text-style [] - (update (default-blockquote-text-style) :style +(defn outgoing-blockquote-text-style + [] + (update (default-blockquote-text-style) + :style assoc :color colors/white-transparent-70-persist)) -(defn blockquote-text-style [] +(defn blockquote-text-style + [] (outgoing-blockquote-text-style) (default-blockquote-text-style)) @@ -314,7 +376,8 @@ :width width :height height}) -(defn image-message-border [opts] +(defn image-message-border + [opts] (merge (image-message opts) {:opacity (:opacity opts) :border-width 1 @@ -324,7 +387,8 @@ :background-color :transparent :border-color colors/black-transparent})) -(defn community-verified [] +(defn community-verified + [] {:border-right-width 1 :border-left-width 1 :border-top-width 1 @@ -336,7 +400,8 @@ :padding-horizontal 15 :border-top-color colors/gray-lighter}) -(defn community-message [verified] +(defn community-message + [verified] {:flex-direction :row :padding-vertical 12 :border-top-left-radius (when-not verified 10) @@ -346,14 +411,16 @@ :border-top-width 1 :border-color colors/gray-lighter}) -(defn community-view-button [] +(defn community-view-button + [] {:border-width 1 :padding-vertical 8 :border-bottom-left-radius 10 :border-bottom-right-radius 10 :border-color colors/gray-lighter}) -(defn contact-request-status-label [state] +(defn contact-request-status-label + [state] {:width 136 :border-radius 8 :flex 1 @@ -365,11 +432,12 @@ :border-color (case 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) :padding-vertical 10 :padding-horizontal 16}) -(defn content-type-contact-request [] +(defn content-type-contact-request + [] {:width 168 :min-height 224.71 :border-radius 8 diff --git a/src/status_im/ui/screens/chat/styles/message/message_old.cljs b/src/status_im/ui/screens/chat/styles/message/message_old.cljs index 5d1e9a0988..19e0bf0021 100644 --- a/src/status_im/ui/screens/chat/styles/message/message_old.cljs +++ b/src/status_im/ui/screens/chat/styles/message/message_old.cljs @@ -1,9 +1,9 @@ (ns status-im.ui.screens.chat.styles.message.message-old - (:require [status-im.constants :as constants] - [quo.design-system.colors :as colors] + (:require [quo.design-system.colors :as colors] + [status-im.constants :as constants] [status-im.ui.components.react :as react] - [status-im.ui.screens.chat.styles.photos :as photos] - [status-im.ui.components.typography :as typography])) + [status-im.ui.components.typography :as typography] + [status-im.ui.screens.chat.styles.photos :as photos])) (defn style-message-text [outgoing] @@ -11,11 +11,11 @@ (defn system-message-body [_] - {:margin-top 4 - :margin-left 8 - :margin-right 8 - :align-self :center - :align-items :center}) + {:margin-top 4 + :margin-left 8 + :margin-right 8 + :align-self :center + :align-items :center}) (defn message-body [{:keys [outgoing]}] @@ -27,29 +27,33 @@ :align-items align})) (def message-timestamp - {:font-size 10}) + {:font-size 10}) (defn message-status-placeholder [] (merge message-timestamp {:opacity 0 :color "rgba(0,0,0,0)"})) -(defn message-timestamp-wrapper [{:keys [last-in-group? outgoing group-chat]}] - {:justify-content :center - (if outgoing :margin-right :margin-left) 12 ;; horizontal margin is only required at the adjust side of the message. - :margin-top (if (and last-in-group? - (or outgoing - (not group-chat))) - 16 - 0) ;; Add gap between message groups - :opacity 0}) +(defn message-timestamp-wrapper + [{:keys [last-in-group? outgoing group-chat]}] + {:justify-content :center + (if outgoing :margin-right :margin-left) 12 ;; horizontal margin is only required at the adjust side + ;; of the message. + :margin-top (if (and last-in-group? + (or outgoing + (not group-chat))) + 16 + 0) ;; Add gap between message groups + :opacity 0}) -(defn message-timestamp-text [] +(defn message-timestamp-text + [] (merge message-timestamp - {:color colors/gray + {:color colors/gray :text-align :center})) -(defn message-status-text [outgoing] - {:font-size 10 +(defn message-status-text + [outgoing] + {:font-size 10 :line-height 10 :color (if outgoing colors/white-transparent-70-persist @@ -63,7 +67,8 @@ colors/white-transparent-70-persist colors/gray)})) -(defn message-wrapper [{:keys [outgoing in-popover?]}] +(defn message-wrapper + [{:keys [outgoing in-popover?]}] (if (and outgoing (not in-popover?)) {:margin-left 96} {:margin-right 96})) @@ -79,14 +84,16 @@ (when-not display-photo? {:margin-left 8}))))) -(defn delivery-status [outgoing] +(defn delivery-status + [outgoing] (if outgoing {:align-self :flex-end :padding-right 8} - {:align-self :flex-start - :padding-left 8})) + {:align-self :flex-start + :padding-left 8})) -(defn pin-indicator [outgoing display-photo?] +(defn pin-indicator + [outgoing display-photo?] (merge {:flex-direction :row :border-top-left-radius (if outgoing 12 4) @@ -107,7 +114,8 @@ (when display-photo? {:margin-left 44}))) -(defn pin-indicator-container [outgoing] +(defn pin-indicator-container + [outgoing] (merge {:margin-top 2 :align-items :center @@ -120,35 +128,40 @@ :align-items :flex-start :padding-left 8}))) -(defn pinned-by-text-icon-container [] +(defn pinned-by-text-icon-container + [] {:flex-direction :row :align-items :flex-start :top 5 :left 8 :position :absolute}) -(defn pin-icon-container [] +(defn pin-icon-container + [] {:flex-direction :row :margin-top 1}) -(defn pin-author-text [] - {:margin-left 2 - :margin-right 12 - :padding-right 0 - :left 12 +(defn pin-author-text + [] + {:margin-left 2 + :margin-right 12 + :padding-right 0 + :left 12 :flex-direction :row - :flex-shrink 1 - :align-self :flex-start - :overflow :hidden}) + :flex-shrink 1 + :align-self :flex-start + :overflow :hidden}) -(defn pinned-by-text [] +(defn pinned-by-text + [] {:margin-left 5}) (def message-author-touchable {:margin-left 12 :flex-direction :row}) -(defn message-author-userpic [outgoing] +(defn message-author-userpic + [outgoing] (merge {:width (+ 16 photos/default-size) ;; 16 is for the padding :align-self :flex-end} @@ -158,9 +171,9 @@ :padding-right 8}))) (def delivery-text - {:color colors/gray - :margin-top 2 - :font-size 12}) + {:color colors/gray + :margin-top 2 + :font-size 12}) (def not-sent-view {:flex-direction :row @@ -169,8 +182,8 @@ (def not-sent-text (assoc delivery-text - :color colors/red - :text-align :right + :color colors/red + :text-align :right :padding-top 4)) (def not-sent-icon @@ -184,17 +197,23 @@ :margin-right (if outgoing 18 0) ;; Margin to display outgoing message status :margin-top (if incoming-group 4 0)}) -(defn collapse-button [] - {:height 24 :width 24 :background-color colors/blue - :border-radius 12 :align-items :center :justify-content :center - :elevation 4 - :shadow-opacity 1 - :shadow-radius 16 - :shadow-color (:shadow-01 @colors/theme) - :shadow-offset {:width 0 :height 4}}) +(defn collapse-button + [] + {:height 24 + :width 24 + :background-color colors/blue + :border-radius 12 + :align-items :center + :justify-content :center + :elevation 4 + :shadow-opacity 1 + :shadow-radius 16 + :shadow-color (:shadow-01 @colors/theme) + :shadow-offset {:width 0 :height 4}}) -(defn message-view-wrapper [outgoing] - {:align-self :flex-end +(defn message-view-wrapper + [outgoing] + {:align-self :flex-end :flex-direction (if outgoing :row :row-reverse)}) (defn message-view @@ -217,37 +236,41 @@ {:border-bottom-left-radius 4}) (cond - pinned {:background-color colors/pin-background} + pinned {:background-color colors/pin-background} (= content-type constants/content-type-system-text) nil outgoing {:background-color colors/blue} mentioned {:background-color colors/mentioned-background - :border-color colors/mentioned-border - :border-width 1} + :border-color colors/mentioned-border + :border-width 1} :else {:background-color colors/blue-light}) (when (= content-type constants/content-type-emoji) {:flex-direction :row}))) -(defn message-view-content [] +(defn message-view-content + [] {:padding-bottom 6 :overflow :hidden}) (def status-container {:padding-horizontal 5}) -(defn status-text [] - {:margin-top 9 - :font-size 14 - :color colors/gray}) +(defn status-text + [] + {:margin-top 9 + :font-size 14 + :color colors/gray}) -(defn message-author-name [chosen?] +(defn message-author-name + [chosen?] {:font-size (if chosen? 13 12) :font-weight (if chosen? "500" "400") :padding-top 6 :padding-left 12 :text-align-vertical :center}) -(defn quoted-message-container [outgoing] +(defn quoted-message-container + [outgoing] {:margin-bottom 6 :padding-bottom 6 :border-bottom-color (if outgoing @@ -262,89 +285,120 @@ :align-items :center :justify-content :flex-start}) -(defn quoted-message-author [outgoing chosen?] +(defn quoted-message-author + [outgoing chosen?] (assoc (message-author-name chosen?) - :padding-bottom 6 - :padding-top 0 - :padding-left 0 - :line-height 18 + :padding-bottom 6 + :padding-top 0 + :padding-left 0 + :line-height 18 :font-weight "500" - :color (if outgoing - colors/white-transparent-70-persist - colors/gray))) + :color (if outgoing + colors/white-transparent-70-persist + colors/gray))) -(defn quoted-message-text [outgoing] +(defn quoted-message-text + [outgoing] {:font-size 14 - :color (if outgoing - colors/white-transparent-70-persist - colors/gray)}) + :color (if outgoing + colors/white-transparent-70-persist + colors/gray)}) ;; Markdown styles -(defn default-text-style [] +(defn default-text-style + [] {:max-font-size-multiplier react/max-font-size-multiplier - :style (assoc (typography/default-style) - :line-height 22)}) + :style (assoc (typography/default-style) + :line-height + 22)}) -(defn outgoing-text-style [] - (update (default-text-style) :style - assoc :color colors/white-persist)) +(defn outgoing-text-style + [] + (update (default-text-style) + :style + assoc + :color colors/white-persist)) -(defn system-text-style [] - (update (default-text-style) :style assoc - :color colors/gray +(defn system-text-style + [] + (update (default-text-style) + :style assoc + :color colors/gray :line-height 20 - :font-size 14 - :text-align :center + :font-size 14 + :text-align :center :font-weight "400")) -(defn text-style [outgoing content-type in-popover?] +(defn text-style + [outgoing content-type in-popover?] (merge (when in-popover? {:number-of-lines 2}) (cond (= content-type constants/content-type-system-text) (system-text-style) - outgoing (outgoing-text-style) - :else (default-text-style)))) + outgoing (outgoing-text-style) + :else (default-text-style)))) -(defn emph-text-style [] - (update (default-text-style) :style - assoc :font-style :italic)) +(defn emph-text-style + [] + (update (default-text-style) + :style + assoc + :font-style :italic)) -(defn outgoing-emph-text-style [] - (update (emph-text-style) :style - assoc :color colors/white-persist)) +(defn outgoing-emph-text-style + [] + (update (emph-text-style) + :style + assoc + :color colors/white-persist)) -(defn emph-style [outgoing] +(defn emph-style + [outgoing] (if outgoing (outgoing-emph-text-style) (emph-text-style))) -(defn strong-text-style [] - (update (default-text-style) :style - assoc :font-weight "700")) +(defn strong-text-style + [] + (update (default-text-style) + :style + assoc + :font-weight "700")) -(defn outgoing-strong-text-style [] - (update (strong-text-style) :style - assoc :color colors/white-persist)) +(defn outgoing-strong-text-style + [] + (update (strong-text-style) + :style + assoc + :color colors/white-persist)) -(defn strong-style [outgoing] +(defn strong-style + [outgoing] (if outgoing (outgoing-strong-text-style) (strong-text-style))) -(defn strong-emph-style [outgoing] - (update (strong-style outgoing) :style - assoc :font-style :italic)) +(defn strong-emph-style + [outgoing] + (update (strong-style outgoing) + :style + assoc + :font-style :italic)) -(defn strikethrough-style [outgoing] - (cond-> (update (default-text-style) :style - assoc :text-decoration-line :line-through) +(defn strikethrough-style + [outgoing] + (cond-> (update (default-text-style) + :style + assoc + :text-decoration-line :line-through) outgoing (update :style assoc :color colors/white-persist))) (def code-block-background "#2E386B") -(defn inline-code-style [] +(defn inline-code-style + [] {:color colors/white-persist :background-color code-block-background}) @@ -356,34 +410,43 @@ (def codeblock-text-style {:color colors/white-persist}) -(defn default-blockquote-style [] +(defn default-blockquote-style + [] {:style {:border-left-width 2 - :padding-left 3 + :padding-left 3 :border-left-color colors/gray-transparent-40}}) -(defn outgoing-blockquote-style [] - (update (default-blockquote-style) :style +(defn outgoing-blockquote-style + [] + (update (default-blockquote-style) + :style assoc :border-left-color colors/white-transparent-70-persist)) -(defn blockquote-style [outgoing] +(defn blockquote-style + [outgoing] (if outgoing (outgoing-blockquote-style) (default-blockquote-style))) -(defn default-blockquote-text-style [] - (update (default-text-style) :style +(defn default-blockquote-text-style + [] + (update (default-text-style) + :style assoc :line-height 19 - :font-size 14 - :color colors/black-transparent-50)) + :font-size 14 + :color colors/black-transparent-50)) -(defn outgoing-blockquote-text-style [] - (update (default-blockquote-text-style) :style +(defn outgoing-blockquote-text-style + [] + (update (default-blockquote-text-style) + :style assoc :color colors/white-transparent-70-persist)) -(defn blockquote-text-style [outgoing] +(defn blockquote-text-style + [outgoing] (if outgoing (outgoing-blockquote-text-style) (default-blockquote-text-style))) @@ -398,7 +461,8 @@ :width width :height height}) -(defn image-message-border [opts] +(defn image-message-border + [opts] (merge (image-message opts) {:opacity (:opacity opts) :border-width 1 @@ -408,7 +472,8 @@ :background-color :transparent :border-color colors/black-transparent})) -(defn community-verified [] +(defn community-verified + [] {:border-right-width 1 :border-left-width 1 :border-top-width 1 @@ -420,7 +485,8 @@ :padding-horizontal 15 :border-top-color colors/gray-lighter}) -(defn community-message [verified] +(defn community-message + [verified] {:flex-direction :row :padding-vertical 12 :border-top-left-radius (when-not verified 10) @@ -430,14 +496,16 @@ :border-top-width 1 :border-color colors/gray-lighter}) -(defn community-view-button [] +(defn community-view-button + [] {:border-width 1 :padding-vertical 8 :border-bottom-left-radius 10 :border-bottom-right-radius 10 :border-color colors/gray-lighter}) -(defn contact-request-status-label [state] +(defn contact-request-status-label + [state] {:width 136 :border-radius 8 :flex 1 @@ -449,11 +517,12 @@ :border-color (case 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) :padding-vertical 10 :padding-horizontal 16}) -(defn content-type-contact-request [outgoing] +(defn content-type-contact-request + [outgoing] {:width 168 :min-height 224.71 :border-radius 8 diff --git a/src/status_im/ui/screens/chat/styles/photos.cljs b/src/status_im/ui/screens/chat/styles/photos.cljs index 6fb7f86fda..cda13ab59b 100644 --- a/src/status_im/ui/screens/chat/styles/photos.cljs +++ b/src/status_im/ui/screens/chat/styles/photos.cljs @@ -5,7 +5,8 @@ (defn radius [size] (/ size 2)) -(defn photo-container [size] +(defn photo-container + [size] {:position :relative :border-radius (radius size)}) @@ -19,7 +20,8 @@ :border-width 1 :border-radius (radius size)})) -(defn photo [size] +(defn photo + [size] {:border-radius (radius size) :width size :height size diff --git a/src/status_im/ui/screens/chat/toolbar_content.cljs b/src/status_im/ui/screens/chat/toolbar_content.cljs index 8f62a87c2f..de8b93bb67 100644 --- a/src/status_im/ui/screens/chat/toolbar_content.cljs +++ b/src/status_im/ui/screens/chat/toolbar_content.cljs @@ -1,12 +1,13 @@ (ns status-im.ui.screens.chat.toolbar-content - (:require [status-im.i18n.i18n :as i18n] - [status-im.constants :as constants] - [status-im.ui.components.chat-icon.screen :as chat-icon.screen] - [status-im.ui.screens.chat.styles.main :as st] + (:require [quo.react-native :as rn] [re-frame.core :as re-frame] - [quo.react-native :as rn])) + [status-im.constants :as constants] + [status-im.i18n.i18n :as i18n] + [status-im.ui.components.chat-icon.screen :as chat-icon.screen] + [status-im.ui.screens.chat.styles.main :as st])) -(defn- group-last-activity [{:keys [contacts public?]}] +(defn- group-last-activity + [{:keys [contacts public?]}] [rn/view {:flex-direction :row} [rn/text {:style st/toolbar-subtitle} (if public? @@ -16,14 +17,17 @@ (i18n/label :members-active-none) (i18n/label-pluralize cnt :t/members-active))))]]) -(defn one-to-one-name [from] +(defn one-to-one-name + [from] (let [[first-name _] @(re-frame.core/subscribe [:contacts/contact-two-names-by-identity from])] - [rn/text {:style st/chat-name-text - :number-of-lines 1 - :accessibility-label :chat-name-text} + [rn/text + {:style st/chat-name-text + :number-of-lines 1 + :accessibility-label :chat-name-text} first-name])) -(defn contact-indicator [contact-id] +(defn contact-indicator + [contact-id] (let [added? @(re-frame/subscribe [:contacts/contact-added? contact-id])] [rn/view {:flex-direction :row} [rn/text {:style st/toolbar-subtitle} @@ -31,7 +35,8 @@ (i18n/label :chat-is-a-contact) (i18n/label :chat-is-not-a-contact))]])) -(defn toolbar-content-view-inner [chat-info] +(defn toolbar-content-view-inner + [chat-info] (let [{:keys [group-chat invitation-admin color chat-id contacts chat-type chat-name public? emoji]} chat-info] [rn/view {:style st/toolbar-container} @@ -39,13 +44,15 @@ [chat-icon.screen/chat-icon-view-toolbar chat-id group-chat chat-name color emoji 36]] [rn/view {:style st/chat-name-view} (if group-chat - [rn/text {:style st/chat-name-text - :number-of-lines 1 - :accessibility-label :chat-name-text} + [rn/text + {:style st/chat-name-text + :number-of-lines 1 + :accessibility-label :chat-name-text} chat-name] [one-to-one-name chat-id]) (when-not group-chat [contact-indicator chat-id]) (when (and group-chat (not invitation-admin) (not= chat-type constants/community-chat-type)) - [group-last-activity {:contacts contacts - :public? public?}])]])) + [group-last-activity + {:contacts contacts + :public? public?}])]])) diff --git a/src/status_im/ui/screens/chat/utils.cljs b/src/status_im/ui/screens/chat/utils.cljs index b7062b88b6..1dbabc5403 100644 --- a/src/status_im/ui/screens/chat/utils.cljs +++ b/src/status_im/ui/screens/chat/utils.cljs @@ -1,9 +1,9 @@ (ns status-im.ui.screens.chat.utils - (:require [status-im.ethereum.stateofus :as stateofus] + (:require [quo.design-system.colors :as colors] + [status-im.ethereum.stateofus :as stateofus] [status-im.i18n.i18n :as i18n] - [status-im.ui.components.react :as react] - [quo.design-system.colors :as colors] - [status-im.multiaccounts.core :as multiaccounts])) + [status-im.multiaccounts.core :as multiaccounts] + [status-im.ui.components.react :as react])) (def ^:private reply-symbol "↪ ") @@ -11,13 +11,14 @@ ([contact] (format-author-old contact nil)) ([{:keys [names] :as contact} {:keys [modal profile? you?]}] (let [{:keys [nickname ens-name]} names - [first-name second-name] (multiaccounts/contact-two-names contact false)] + [first-name second-name] (multiaccounts/contact-two-names contact false)] (if (or nickname ens-name) - [react/nested-text {:number-of-lines 2 - :style {:color (if modal colors/white-persist colors/blue) - :font-size (if profile? 15 13) - :line-height (if profile? 22 18) - :font-weight "500"}} + [react/nested-text + {:number-of-lines 2 + :style {:color (if modal colors/white-persist colors/blue) + :font-size (if profile? 15 13) + :line-height (if profile? 22 18) + :font-weight "500"}} (subs first-name 0 81) (when you? [{:style {:color colors/gray :font-weight "400" :font-size 13}} @@ -25,24 +26,26 @@ (when nickname [{:style {:color colors/gray :font-weight "400"}} (str " " (subs second-name 0 81))])] - [react/text {:style {:color (if modal colors/white-persist colors/gray) - :font-size (if profile? 15 12) - :line-height (if profile? 22 18) - :font-weight "400"}} + [react/text + {:style {:color (if modal colors/white-persist colors/gray) + :font-size (if profile? 15 12) + :line-height (if profile? 22 18) + :font-weight "400"}} first-name])))) (defn format-author ([contact] (format-author contact nil nil)) ([{:keys [names] :as contact} {:keys [modal profile? you?]} max-length] (let [{:keys [nickname ens-name]} names - [first-name second-name] (multiaccounts/contact-two-names contact false)] + [first-name second-name] (multiaccounts/contact-two-names contact false)] (if (or nickname ens-name) - [react/nested-text {:number-of-lines 2 - :style {:color (if modal colors/white-persist colors/black) - :font-size (if profile? 15 13) - :line-height (if profile? 22 18) - :letter-spacing -0.2 - :font-weight "600"}} + [react/nested-text + {:number-of-lines 2 + :style {:color (if modal colors/white-persist colors/black) + :font-size (if profile? 15 13) + :line-height (if profile? 22 18) + :letter-spacing -0.2 + :font-weight "600"}} (subs first-name 0 81) (when you? [{:style {:color colors/black-light :font-weight "500" :font-size 13}} @@ -50,17 +53,19 @@ (when nickname [{:style {:color colors/black-light :font-weight "500"}} (str " " (subs second-name 0 81))])] - [react/text {:style {:color (if modal colors/white-persist colors/black) - :font-size (if profile? 15 13) - :line-height (if profile? 22 18) - :font-weight "600" - :letter-spacing -0.2} - :number-of-lines 1} + [react/text + {:style {:color (if modal colors/white-persist colors/black) + :font-size (if profile? 15 13) + :line-height (if profile? 22 18) + :font-weight "600" + :letter-spacing -0.2} + :number-of-lines 1} (if (and max-length (> (count first-name) max-length)) (str (subs first-name 0 max-length) "...") first-name)])))) -(defn format-reply-author [from username current-public-key style outgoing] +(defn format-reply-author + [from username current-public-key style outgoing] (let [contact-name (str reply-symbol username)] (or (and (= from current-public-key) [react/text {:style (style true)} @@ -69,16 +74,18 @@ ;; in case of replies (= (aget contact-name 1) "@")) (let [trimmed-name (subs contact-name 0 81)] - [react/text {:number-of-lines 2 - :style (merge {:color colors/blue - :font-size 13 - :line-height 18 - :font-weight "500"})} + [react/text + {:number-of-lines 2 + :style (merge {:color colors/blue + :font-size 13 + :line-height 18 + :font-weight "500"})} (or (stateofus/username trimmed-name) trimmed-name)]) - [react/text {:style (merge {:color (if outgoing - colors/white-transparent-70-persist - colors/gray) - :font-size 12 - :line-height 18 - :font-weight "400"})} + [react/text + {:style (merge {:color (if outgoing + colors/white-transparent-70-persist + colors/gray) + :font-size 12 + :line-height 18 + :font-weight "400"})} contact-name])))) diff --git a/src/status_im/ui/screens/chat/views.cljs b/src/status_im/ui/screens/chat/views.cljs index 4b3e369d6b..df31ada7ee 100644 --- a/src/status_im/ui/screens/chat/views.cljs +++ b/src/status_im/ui/screens/chat/views.cljs @@ -1,47 +1,48 @@ (ns status-im.ui.screens.chat.views - (:require [re-frame.core :as re-frame] + (:require [clojure.string :as string] + [quo.animated :as animated] + [quo.core :as quo] + [quo.design-system.colors :as colors] + [quo.react :as quo.react] + [quo.react-native :as rn] + [re-frame.core :as re-frame] re-frame.db [reagent.core :as reagent] + [status-im.constants :as constants] [status-im.i18n.i18n :as i18n] + [status-im.react-native.resources :as resources] [status-im.ui.components.chat-icon.screen :as chat-icon.screen] - [quo.design-system.colors :as colors] [status-im.ui.components.connectivity.view :as connectivity] [status-im.ui.components.icons.icons :as icons] [status-im.ui.components.list.views :as list] - [status-im.ui.screens.chat.components.reply :as reply] - [status-im.ui.screens.chat.components.edit :as edit] - [status-im.ui.screens.chat.components.contact-request :as contact-request] [status-im.ui.components.react :as react] - [quo.animated :as animated] - [quo.react-native :as rn] + [status-im.ui.components.toolbar :as toolbar] [status-im.ui.screens.chat.audio-message.views :as audio-message] - [quo.react :as quo.react] + [status-im.ui.screens.chat.components.accessory :as accessory] + [status-im.ui.screens.chat.components.contact-request :as contact-request] + [status-im.ui.screens.chat.components.edit :as edit] + [status-im.ui.screens.chat.components.input :as components] + [status-im.ui.screens.chat.components.reply :as reply] + [status-im.ui.screens.chat.extensions.views :as extensions] + [status-im.ui.screens.chat.group :as chat.group] + [status-im.ui.screens.chat.image.views :as image] + [status-im.ui.screens.chat.message.datemark :as message-datemark] + [status-im.ui.screens.chat.message.gap :as gap] [status-im.ui.screens.chat.message.message :as message] + [status-im.ui.screens.chat.sheets :as sheets] + [status-im.ui.screens.chat.state :as state] [status-im.ui.screens.chat.stickers.views :as stickers] [status-im.ui.screens.chat.styles.main :as style] [status-im.ui.screens.chat.toolbar-content :as toolbar-content] - [status-im.ui.screens.chat.image.views :as image] - [status-im.ui.screens.chat.state :as state] - [status-im.ui.screens.chat.extensions.views :as extensions] - [status-im.ui.screens.chat.group :as chat.group] - [status-im.ui.screens.chat.message.gap :as gap] - [status-im.ui.screens.chat.components.accessory :as accessory] - [status-im.ui.screens.chat.components.input :as components] - [status-im.ui.screens.chat.message.datemark :as message-datemark] - [status-im.ui.components.toolbar :as toolbar] - [quo.core :as quo] - [clojure.string :as string] - [status-im.constants :as constants] [status-im.utils.platform :as platform] [status-im.utils.utils :as utils] - [status-im.ui.screens.chat.sheets :as sheets] - [utils.debounce :as debounce] [status-im2.navigation.state :as navigation.state] - [status-im.react-native.resources :as resources])) + [utils.debounce :as debounce])) -(defn invitation-requests [chat-id admins] +(defn invitation-requests + [chat-id admins] (let [current-pk @(re-frame/subscribe [:multiaccount/public-key]) - admin? (get admins current-pk)] + admin? (get admins current-pk)] (when admin? (let [invitations @(re-frame/subscribe [:group-chat/pending-invitations-by-chat-id chat-id])] (when (seq invitations) @@ -52,7 +53,8 @@ [react/text {:style style/add-contact-text} (i18n/label :t/group-membership-request)]]]))))) -(defn add-contact-bar [public-key] +(defn add-contact-bar + [public-key] (when-not (or @(re-frame/subscribe [:contacts/contact-added? public-key]) @(re-frame/subscribe [:contacts/contact-blocked? public-key])) [react/touchable-highlight @@ -64,46 +66,54 @@ {:color colors/blue}] [react/i18n-text {:style style/add-contact-text :key :add-to-contacts}]]])) -(defn contact-request [] +(defn contact-request + [] (let [contact-request @(re-frame/subscribe [:chats/sending-contact-request])] [react/view {:style style/contact-request} - [react/image {:source (resources/get-image :hand-wave) - :style {:width 112 - :height 96.71 - :margin-top 17}}] - [quo/text {:style {:margin-top 14} - :weight :bold - :size :large} + [react/image + {:source (resources/get-image :hand-wave) + :style {:width 112 + :height 96.71 + :margin-top 17}}] + [quo/text + {:style {:margin-top 14} + :weight :bold + :size :large} (i18n/label :t/say-hi)] - [quo/text {:style {:margin-top 2 - :margin-bottom 14}} + [quo/text + {:style {:margin-top 2 + :margin-bottom 14}} (i18n/label :t/send-contact-request-message)] (when-not contact-request - [react/view {:style {:padding-horizontal 16 - :padding-bottom 8}} + [react/view + {:style {:padding-horizontal 16 + :padding-bottom 8}} [quo/button {:style {:width "100%"} :accessibility-label :contact-request--button :on-press #(re-frame/dispatch [:chat.ui/send-contact-request])} (i18n/label :t/contact-request)]])])) -(defn chat-intro [{:keys [chat-id - chat-name - chat-type - group-chat - invitation-admin - mutual-contact-requests-enabled? - contact-name - color - loading-messages? - no-messages? - contact-request-state - emoji]}] - [react/view {:style (style/intro-header-container loading-messages? no-messages?) - :accessibility-label :history-chat} +(defn chat-intro + [{:keys [chat-id + chat-name + chat-type + group-chat + invitation-admin + mutual-contact-requests-enabled? + contact-name + color + loading-messages? + no-messages? + contact-request-state + emoji]}] + [react/view + {:style (style/intro-header-container loading-messages? no-messages?) + :accessibility-label :history-chat} ;; Icon section - [react/view {:style {:margin-top 52 - :margin-bottom 24}} + [react/view + {:style {:margin-top 52 + :margin-bottom 24}} [chat-icon.screen/emoji-chat-intro-icon-view chat-name chat-id group-chat emoji {:default-chat-icon (style/intro-header-icon 120 color) @@ -116,12 +126,13 @@ (if group-chat chat-name contact-name)] ;; Description section (if group-chat - [chat.group/group-chat-description-container {:chat-id chat-id - :invitation-admin invitation-admin - :loading-messages? loading-messages? - :chat-name chat-name - :chat-type chat-type - :no-messages? no-messages?}] + [chat.group/group-chat-description-container + {:chat-id chat-id + :invitation-admin invitation-admin + :loading-messages? loading-messages? + :chat-name chat-name + :chat-type chat-type + :no-messages? no-messages?}] [react/text {:style (assoc style/intro-header-description :margin-bottom 32)} (str (i18n/label :t/empty-chat-description-one-to-one) contact-name)]) (when (and mutual-contact-requests-enabled? @@ -131,14 +142,17 @@ (= contact-request-state constants/contact-request-state-dismissed))) [contact-request])]) -(defn chat-intro-one-to-one [{:keys [chat-id] :as opts}] - (let [contact @(re-frame/subscribe [:contacts/contact-by-identity chat-id]) +(defn chat-intro-one-to-one + [{:keys [chat-id] :as opts}] + (let [contact @(re-frame/subscribe [:contacts/contact-by-identity chat-id]) mutual-contact-requests-enabled? @(re-frame/subscribe [:mutual-contact-requests/enabled?]) - contact-names @(re-frame/subscribe [:contacts/contact-two-names-by-identity chat-id])] - [chat-intro (assoc opts - :mutual-contact-requests-enabled? mutual-contact-requests-enabled? - :contact-name (first contact-names) - :contact-request-state (or (:contact-request-state contact) + contact-names @(re-frame/subscribe [:contacts/contact-two-names-by-identity + chat-id])] + [chat-intro + (assoc opts + :mutual-contact-requests-enabled? mutual-contact-requests-enabled? + :contact-name (first contact-names) + :contact-request-state (or (:contact-request-state contact) constants/contact-request-state-none))])) (defn chat-intro-header-container @@ -170,21 +184,24 @@ (defonce messages-list-ref (atom nil)) -(defn on-viewable-items-changed [^js e] +(defn on-viewable-items-changed + [^js e] (when @messages-list-ref (reset! state/first-not-visible-item - (when-let [^js last-visible-element (aget (.-viewableItems e) (dec (.-length ^js (.-viewableItems e))))] - (let [index (.-index last-visible-element) - ;; Get first not visible element, if it's a datemark/gap - ;; we might unnecessarely add messages on receiving as - ;; they do not have a clock value, but most of the times - ;; it will be a message - first-not-visible (aget (.-data ^js (.-props ^js @messages-list-ref)) (inc index))] - (when (and first-not-visible - (= :message (:type first-not-visible))) - first-not-visible)))))) + (when-let [^js last-visible-element (aget (.-viewableItems e) + (dec (.-length ^js (.-viewableItems e))))] + (let [index (.-index last-visible-element) + ;; Get first not visible element, if it's a datemark/gap + ;; we might unnecessarely add messages on receiving as + ;; they do not have a clock value, but most of the times + ;; it will be a message + first-not-visible (aget (.-data ^js (.-props ^js @messages-list-ref)) (inc index))] + (when (and first-not-visible + (= :message (:type first-not-visible))) + first-not-visible)))))) -(defn bottom-sheet [input-bottom-sheet] +(defn bottom-sheet + [input-bottom-sheet] (case input-bottom-sheet :stickers [stickers/stickers-view] @@ -196,47 +213,52 @@ [audio-message/audio-message-view] nil)) -(defn invitation-bar [chat-id] +(defn invitation-bar + [chat-id] (let [{:keys [state chat-id] :as invitation} (first @(re-frame/subscribe [:group-chat/invitations-by-chat-id chat-id])) - {:keys [retry? message]} @(re-frame/subscribe [:chats/current-chat-membership]) - message-length (count message)] + {:keys [retry? message]} @(re-frame/subscribe [:chats/current-chat-membership]) + message-length (count message)] [react/view {:margin-horizontal 16 :margin-top 10} (cond (and invitation (= constants/invitation-state-requested state) (not retry?)) - [toolbar/toolbar {:show-border? true - :center - [quo/button - {:type :secondary - :disabled true} - (i18n/label :t/request-pending)]}] + [toolbar/toolbar + {:show-border? true + :center + [quo/button + {:type :secondary + :disabled true} + (i18n/label :t/request-pending)]}] (and invitation (= constants/invitation-state-rejected state) (not retry?)) - [toolbar/toolbar {:show-border? true - :right - [quo/button - {:type :secondary - :accessibility-label :retry-button - :on-press #(re-frame/dispatch [:group-chats.ui/membership-retry])} - (i18n/label :t/mailserver-retry)] - :left - [quo/button - {:type :secondary - :accessibility-label :remove-group-button - :on-press #(re-frame/dispatch [:group-chats.ui/remove-chat-confirmed chat-id])} - (i18n/label :t/remove-group)]}] + [toolbar/toolbar + {:show-border? true + :right + [quo/button + {:type :secondary + :accessibility-label :retry-button + :on-press #(re-frame/dispatch [:group-chats.ui/membership-retry])} + (i18n/label :t/mailserver-retry)] + :left + [quo/button + {:type :secondary + :accessibility-label :remove-group-button + :on-press #(re-frame/dispatch [:group-chats.ui/remove-chat-confirmed chat-id])} + (i18n/label :t/remove-group)]}] :else - [toolbar/toolbar {:show-border? true - :center - [quo/button - {:type :secondary - :accessibility-label :introduce-yourself-button - :disabled (or (string/blank? message) - (> message-length chat.group/message-max-length)) - :on-press #(re-frame/dispatch [:send-group-chat-membership-request])} - (i18n/label :t/request-membership)]}])])) + [toolbar/toolbar + {:show-border? true + :center + [quo/button + {:type :secondary + :accessibility-label :introduce-yourself-button + :disabled (or (string/blank? message) + (> message-length chat.group/message-max-length)) + :on-press #(re-frame/dispatch [:send-group-chat-membership-request])} + (i18n/label :t/request-membership)]}])])) -(defn get-space-keeper-ios [bottom-space panel-space active-panel text-input-ref] +(defn get-space-keeper-ios + [bottom-space panel-space active-panel text-input-ref] (fn [state] ;; NOTE: Only iOs now because we use soft input resize screen on android (when platform/ios? @@ -249,11 +271,13 @@ (and (not state) (< @panel-space @bottom-space)) (do - (some-> ^js (quo.react/current-ref text-input-ref) .focus) + (some-> ^js (quo.react/current-ref text-input-ref) + .focus) (reset! panel-space @bottom-space) (reset! bottom-space 0)))))) -(defn get-set-active-panel [active-panel] +(defn get-set-active-panel + [active-panel] (fn [panel] (rn/configure-next (:ease-opacity-200 rn/custom-animations)) @@ -262,26 +286,29 @@ (when panel (js/setTimeout #(react/dismiss-keyboard!) 100)))) -(defn list-footer [{:keys [chat-id] :as chat}] +(defn list-footer + [{:keys [chat-id] :as chat}] (let [loading-messages? @(re-frame/subscribe [:chats/loading-messages? chat-id]) - no-messages? @(re-frame/subscribe [:chats/chat-no-messages? chat-id]) - all-loaded? @(re-frame/subscribe [:chats/all-loaded? chat-id])] + no-messages? @(re-frame/subscribe [:chats/chat-no-messages? chat-id]) + all-loaded? @(re-frame/subscribe [:chats/all-loaded? chat-id])] [react/view {:style (when platform/android? {:scaleY -1})} (if (or loading-messages? (not chat-id) (not all-loaded?)) [react/view {:height 324 :align-items :center :justify-content :center} [react/activity-indicator {:animating true}]] [chat-intro-header-container chat no-messages?])])) -(defn list-header [{:keys [chat-id chat-type invitation-admin]}] +(defn list-header + [{:keys [chat-id chat-type invitation-admin]}] (when (= chat-type constants/private-group-chat-type) [react/view {:style (when platform/android? {:scaleY -1})} [chat.group/group-chat-footer chat-id invitation-admin]])) -(defn render-fn [{:keys [outgoing type] :as message} - idx - _ - {:keys [group-chat public? community? current-public-key space-keeper - chat-id show-input? message-pin-enabled edit-enabled in-pinned-view?]}] +(defn render-fn + [{:keys [outgoing type] :as message} + idx + _ + {:keys [group-chat public? community? current-public-key space-keeper + chat-id show-input? message-pin-enabled edit-enabled in-pinned-view?]}] [react/view {:style (when (and platform/android? (not in-pinned-view?)) {:scaleY -1})} (if (= type :datemark) [message-datemark/chat-datemark (:value message)] @@ -290,33 +317,39 @@ ; message content [message/chat-message (assoc message - :incoming-group (and group-chat (not outgoing)) - :group-chat group-chat - :public? public? - :community? community? - :current-public-key current-public-key - :show-input? show-input? + :incoming-group (and group-chat (not outgoing)) + :group-chat group-chat + :public? public? + :community? community? + :current-public-key current-public-key + :show-input? show-input? :message-pin-enabled message-pin-enabled - :edit-enabled edit-enabled) + :edit-enabled edit-enabled) space-keeper]))]) (def list-key-fn #(or (:message-id %) (:value %))) (def list-ref #(reset! messages-list-ref %)) -;;TODO this is not really working in pair with inserting new messages because we stop inserting new messages -;;if they outside the viewarea, but we load more here because end is reached,so its slowdown UI because we -;;load and render 20 messages more, but we can't prevent this , because otherwise :on-end-reached will work wrong -(defn list-on-end-reached [] +;;TODO this is not really working in pair with inserting new messages because we stop inserting new +;;messages +;;if they outside the viewarea, but we load more here because end is reached,so its slowdown UI because +;;we +;;load and render 20 messages more, but we can't prevent this , because otherwise :on-end-reached will +;;work wrong +(defn list-on-end-reached + [] (if @state/scrolling (re-frame/dispatch [:chat.ui/load-more-messages-for-current-chat]) (utils/set-timeout #(re-frame/dispatch [:chat.ui/load-more-messages-for-current-chat]) (if platform/low-device? 700 200)))) -(defn get-render-data [{:keys [group-chat chat-id public? community-id admins space-keeper show-input? edit-enabled in-pinned-view?]}] - (let [current-public-key @(re-frame/subscribe [:multiaccount/public-key]) - community @(re-frame/subscribe [:communities/community community-id]) - group-admin? (get admins current-public-key) - community-admin? (when community (community :admin)) +(defn get-render-data + [{:keys [group-chat chat-id public? community-id admins space-keeper show-input? edit-enabled + in-pinned-view?]}] + (let [current-public-key @(re-frame/subscribe [:multiaccount/public-key]) + community @(re-frame/subscribe [:communities/community community-id]) + group-admin? (get admins current-public-key) + community-admin? (when community (community :admin)) message-pin-enabled (and (not public?) (or (not group-chat) (and group-chat @@ -333,16 +366,17 @@ :edit-enabled edit-enabled :in-pinned-view? in-pinned-view?})) -(defn messages-view [{:keys [chat - bottom-space - pan-responder - mutual-contact-requests-enabled? - space-keeper - show-input?]}] +(defn messages-view + [{:keys [chat + bottom-space + pan-responder + mutual-contact-requests-enabled? + space-keeper + show-input?]}] (let [{:keys [group-chat chat-type chat-id public? community-id admins]} chat messages @(re-frame/subscribe [:chats/raw-chat-messages-stream chat-id]) - one-to-one? (= chat-type constants/one-to-one-chat-type) + one-to-one? (= chat-type constants/one-to-one-chat-type) contact-added? (when one-to-one? @(re-frame/subscribe [:contacts/contact-added? chat-id])) should-send-contact-request? (and @@ -373,62 +407,78 @@ :on-viewable-items-changed on-viewable-items-changed :on-end-reached list-on-end-reached :on-scroll-to-index-failed identity ;;don't remove this - :content-container-style {:padding-top (+ bottom-space 16) + :content-container-style {:padding-top (+ bottom-space 16) :padding-bottom 16} :scroll-indicator-insets {:top bottom-space} ;;ios only :keyboard-dismiss-mode :interactive :keyboard-should-persist-taps :handled :onMomentumScrollBegin state/start-scrolling :onMomentumScrollEnd state/stop-scrolling - ;;TODO https://github.com/facebook/react-native/issues/30034 + ;;TODO https://github.com/facebook/react-native/issues/30034 :inverted (when platform/ios? true) :style (when platform/android? {:scaleY -1})})])) -(defn navigate-back-handler [] +(defn navigate-back-handler + [] (when (and (not @navigation.state/curr-modal) (= (get @re-frame.db/app-db :view-id) :chat)) (react/hw-back-remove-listener navigate-back-handler) (re-frame/dispatch [:close-chat]) (re-frame/dispatch [:navigate-back]))) -(defn topbar-content [] - (let [window-width @(re-frame/subscribe [:dimensions/window-width]) +(defn topbar-content + [] + (let [window-width @(re-frame/subscribe [:dimensions/window-width]) {:keys [group-chat chat-id] :as chat-info} @(re-frame/subscribe [:chats/current-chat])] - [react/touchable-highlight {:on-press #(when-not group-chat - (debounce/dispatch-and-chill [:chat.ui/show-profile chat-id] 1000)) - :style {:flex 1 :width (- window-width 120)}} + [react/touchable-highlight + {:on-press #(when-not group-chat + (debounce/dispatch-and-chill [:chat.ui/show-profile chat-id] 1000)) + :style {:flex 1 :width (- window-width 120)}} [toolbar-content/toolbar-content-view-inner chat-info]])) -(defn topbar [] +(defn topbar + [] ;;we don't use topbar component, because we want chat view as simple (fast) as possible [react/view {:height 56} - [react/touchable-highlight {:on-press-in navigate-back-handler - :accessibility-label :back-button - :style {:height 56 :width 40 :align-items :center :justify-content :center - :padding-left 16}} + [react/touchable-highlight + {:on-press-in navigate-back-handler + :accessibility-label :back-button + :style {:height 56 + :width 40 + :align-items :center + :justify-content :center + :padding-left 16}} [icons/icon :main-icons/arrow-left {:color colors/black}]] [react/view {:flex 1 :left 52 :right 52 :top 0 :bottom 0 :position :absolute} [topbar-content]] - [react/touchable-highlight {:on-press-in #(re-frame/dispatch [:bottom-sheet/show-sheet - {:content (fn [] [sheets/current-chat-actions]) - :height 256}]) - :accessibility-label :chat-menu-button - :style {:right 0 :top 0 :bottom 0 :position :absolute - :height 56 :width 40 :align-items :center :justify-content :center - :padding-right 16}} + [react/touchable-highlight + {:on-press-in #(re-frame/dispatch [:bottom-sheet/show-sheet + {:content (fn [] [sheets/current-chat-actions]) + :height 256}]) + :accessibility-label :chat-menu-button + :style {:right 0 + :top 0 + :bottom 0 + :position :absolute + :height 56 + :width 40 + :align-items :center + :justify-content :center + :padding-right 16}} [icons/icon :main-icons/more {:color colors/black}]]]) -(defn chat-render [] - (let [bottom-space (reagent/atom 0) - panel-space (reagent/atom 52) - active-panel (reagent/atom nil) - position-y (animated/value 0) - pan-state (animated/value 0) - text-input-ref (quo.react/create-ref) - on-update #(when-not (zero? %) (reset! panel-space %)) - pan-responder (accessory/create-pan-responder position-y pan-state) - space-keeper (get-space-keeper-ios bottom-space panel-space active-panel text-input-ref) +(defn chat-render + [] + (let [bottom-space (reagent/atom 0) + panel-space (reagent/atom 52) + active-panel (reagent/atom nil) + position-y (animated/value 0) + pan-state (animated/value 0) + text-input-ref (quo.react/create-ref) + on-update #(when-not (zero? %) (reset! panel-space %)) + pan-responder (accessory/create-pan-responder position-y pan-state) + space-keeper (get-space-keeper-ios bottom-space panel-space active-panel text-input-ref) set-active-panel (get-set-active-panel active-panel) - on-close #(set-active-panel nil)] + on-close #(set-active-panel nil)] (fn [] (let [{:keys [chat-id show-input? group-chat admins invitation-admin] :as chat} ;;we want to react only on these fields, do not use full chat map here @@ -443,24 +493,27 @@ [invitation-requests chat-id admins] (when-not mutual-contact-requests-enabled? [add-contact-bar chat-id]))) ;;MESSAGES LIST - [messages-view {:chat chat - :bottom-space max-bottom-space - :pan-responder pan-responder - :mutual-contact-requests-enabled? mutual-contact-requests-enabled? - :space-keeper space-keeper - :show-input? show-input?}] + [messages-view + {:chat chat + :bottom-space max-bottom-space + :pan-responder pan-responder + :mutual-contact-requests-enabled? mutual-contact-requests-enabled? + :space-keeper space-keeper + :show-input? show-input?}] (when (and group-chat invitation-admin) - [accessory/view {:y position-y - :on-update-inset on-update} + [accessory/view + {:y position-y + :on-update-inset on-update} [invitation-bar chat-id]]) [components/autocomplete-mentions text-input-ref max-bottom-space] (when show-input? ;; NOTE: this only accepts two children - [accessory/view {:y position-y - :pan-state pan-state - :has-panel (boolean @active-panel) - :on-close on-close - :on-update-inset on-update} + [accessory/view + {:y position-y + :pan-state pan-state + :has-panel (boolean @active-panel) + :on-close on-close + :on-update-inset on-update} [react/view [edit/edit-message-auto-focus-wrapper text-input-ref] [reply/reply-message-auto-focus-wrapper text-input-ref] @@ -475,10 +528,11 @@ [contact-request/contact-request-message-auto-focus-wrapper text-input-ref]] [bottom-sheet @active-panel]])])))) -(defn chat [] +(defn chat + [] (reagent/create-class - {:component-did-mount (fn [] - (react/hw-back-remove-listener navigate-back-handler) - (react/hw-back-add-listener navigate-back-handler)) + {:component-did-mount (fn [] + (react/hw-back-remove-listener navigate-back-handler) + (react/hw-back-add-listener navigate-back-handler)) :component-will-unmount (fn [] (react/hw-back-remove-listener navigate-back-handler)) - :reagent-render chat-render})) + :reagent-render chat-render})) diff --git a/src/status_im/ui/screens/communities/channel_details.cljs b/src/status_im/ui/screens/communities/channel_details.cljs index a81bce4196..ece73cbbb4 100644 --- a/src/status_im/ui/screens/communities/channel_details.cljs +++ b/src/status_im/ui/screens/communities/channel_details.cljs @@ -1,59 +1,67 @@ (ns status-im.ui.screens.communities.channel-details - (:require [quo.core :as quo] - [status-im.utils.handlers :refer [>evt evt]])) -(defn view [] +(defn view + [] (let [{:keys [chat-id]} (evt [:navigate-back])}] - :right-accessories (when admin [{:icon :edit - :accessibility-label :invite-button - :on-press #(>evt [::communities/edit-channel-pressed - community-id - chat-name - description - color - emoji - chat-id - (:id category) - position])}]) - :extended-header (profile-header/extended-header - {:title chat-name - :color color - :emoji emoji - :subtitle (i18n/label :t/public-channel)}) - :use-insets true} + {:keys [position]} (evt [:navigate-back])}] + :right-accessories (when admin + [{:icon :edit + :accessibility-label :invite-button + :on-press #(>evt [::communities/edit-channel-pressed + community-id + chat-name + description + color + emoji + chat-id + (:id category) + position])}]) + :extended-header (profile-header/extended-header + {:title chat-name + :color color + :emoji emoji + :subtitle (i18n/label :t/public-channel)}) + :use-insets true} (when-not (string/blank? description) [:<> [quo/list-footer {:color :main} description] [quo/separator {:style {:margin-vertical 8}}] (when admin - [quo/list-item {:title (i18n/label :t/category) - :on-press #(>evt [:open-modal :select-category {:chat current-chat - :category category - :community-id community-id}]) - :chevron true - :accessory :text - :accessory-text (if category - (:name category) - (i18n/label :t/none))}]) + [quo/list-item + {:title (i18n/label :t/category) + :on-press #(>evt [:open-modal :select-category + {:chat current-chat + :category category + :community-id community-id}]) + :chevron true + :accessory :text + :accessory-text (if category + (:name category) + (i18n/label :t/none))}]) [quo/list-item - {:title (i18n/label :t/pinned-messages) - :accessory :text - :accessory-text (count pinned-messages) - :chevron true - :on-press #(re-frame/dispatch [:contact.ui/pinned-messages-pressed chat-id])}]])])))) + {:title (i18n/label :t/pinned-messages) + :accessory :text + :accessory-text (count pinned-messages) + :chevron true + :on-press #(re-frame/dispatch [:contact.ui/pinned-messages-pressed chat-id])}]])])))) diff --git a/src/status_im/ui/screens/communities/community.cljs b/src/status_im/ui/screens/communities/community.cljs index e33623455a..698d149c2a 100644 --- a/src/status_im/ui/screens/communities/community.cljs +++ b/src/status_im/ui/screens/communities/community.cljs @@ -1,47 +1,50 @@ (ns status-im.ui.screens.communities.community (:require [i18n.i18n :as i18n] - [utils.re-frame :as rf] + [quo.core :as quo] + [quo.design-system.colors :as colors] + [quo2.components.community.style :as styles] ;; TODO reimplement with quo2 library and new + ;; designs + [quo2.components.navigation.floating-shell-button :as floating-shell-button] [react-native.core :as rn] - [quo2.components.community.style :as styles] - - ;; TODO reimplement with quo2 library and new designs - [status-im.ui.components.topbar :as topbar] - [status-im.ui.components.toolbar :as toolbar] - [status-im.constants :as constants] [status-im.chat.models.link-preview :as link-preview] - [status-im.utils.datetime :as datetime] [status-im.communities.core :as communities] - [status-im.ui.screens.home.views.inner-item :as inner-item] - [status-im.ui.screens.chat.photos :as photos] + [status-im.constants :as constants] [status-im.react-native.resources :as resources] + [status-im.ui.components.accordion :as accordion] [status-im.ui.components.chat-icon.screen :as chat-icon.screen] [status-im.ui.components.icons.icons :as icons] - [status-im.utils.core :as utils] - [status-im.ui.components.plus-button :as components.plus-button] - [status-im.ui.screens.chat.sheets :as sheets] - [status-im.ui.components.accordion :as accordion] [status-im.ui.components.list.views :as list] + [status-im.ui.components.plus-button :as components.plus-button] [status-im.ui.components.react :as react] - [quo2.components.navigation.floating-shell-button :as floating-shell-button] - [quo.core :as quo] - [quo.design-system.colors :as colors])) + [status-im.ui.components.toolbar :as toolbar] + [status-im.ui.components.topbar :as topbar] + [status-im.ui.screens.chat.photos :as photos] + [status-im.ui.screens.chat.sheets :as sheets] + [status-im.ui.screens.home.views.inner-item :as inner-item] + [status-im.utils.core :as utils] + [status-im.utils.datetime :as datetime] + [utils.re-frame :as rf])) (def request-cooldown-ms (* 24 60 60 1000)) -(defn can-request-access-again? [requested-at] +(defn can-request-access-again? + [requested-at] (> (datetime/timestamp) (+ (* requested-at 1000) request-cooldown-ms))) -(defn toolbar-content [id display-name color images show-members-count? members] +(defn toolbar-content + [id display-name color images show-members-count? members] (let [thumbnail-image (get-in images [:thumbnail :uri])] - [rn/view {:style {:flex 1 - :align-items :center - :flex-direction :row}} + [rn/view + {:style {:flex 1 + :align-items :center + :flex-direction :row}} [rn/view {:padding-right 10} (cond (= id constants/status-community-id) - [rn/image {:source (resources/get-image :status-logo) - :style {:width 40 - :height 40}}] + [rn/image + {:source (resources/get-image :status-logo) + :style {:width 40 + :height 40}}] (seq thumbnail-image) [photos/photo thumbnail-image {:size 40}] @@ -52,23 +55,28 @@ display-name (or color (rand-nth colors/chat-colors)) nil 36])] [rn/view {:style {:flex 1 :justify-content :center}} - [quo/text {:number-of-lines 1 - :accessibility-label :community-name-text} + [quo/text + {:number-of-lines 1 + :accessibility-label :community-name-text} display-name] - [quo/text {:number-of-lines 1 - :size :small - :color :secondary} + [quo/text + {:number-of-lines 1 + :size :small + :color :secondary} (if show-members-count? (i18n/label-pluralize members :t/community-members {:count members}) (i18n/label :t/open-membership))]]])) -(defn hide-sheet-and-dispatch [event] +(defn hide-sheet-and-dispatch + [event] (rf/dispatch [:bottom-sheet/hide]) (rf/dispatch event)) -(defn community-plus-actions [{:keys [id permissions can-manage-users?]}] - (let [can-invite? (and can-manage-users? (not= (:access permissions) constants/community-no-membership-access)) - can-share? (not= (:access permissions) constants/community-invitation-only-access)] +(defn community-plus-actions + [{:keys [id permissions can-manage-users?]}] + (let [can-invite? (and can-manage-users? + (not= (:access permissions) constants/community-no-membership-access)) + can-share? (not= (:access permissions) constants/community-invitation-only-access)] [:<> [quo/list-item {:theme :accent @@ -81,7 +89,8 @@ :title (i18n/label :t/create-category) :accessibility-label :community-create-category :icon :main-icons/channel-category - :on-press #(hide-sheet-and-dispatch [:open-modal :create-community-category {:community-id id}])}] + :on-press #(hide-sheet-and-dispatch [:open-modal :create-community-category + {:community-id id}])}] [quo/separator] (when can-invite? [quo/list-item @@ -98,7 +107,8 @@ :accessibility-label :community-share :on-press #(rf/dispatch [:communities/share-community-pressed id])}])])) -(defn community-actions [{:keys [id name images color can-manage-users?]}] +(defn community-actions + [{:keys [id name images color can-manage-users?]}] (let [thumbnail-image (get-in images [:thumbnail :uri])] [:<> [quo/list-item @@ -108,9 +118,10 @@ :chevron true :icon (cond (= id constants/status-community-id) - [rn/image {:source (resources/get-image :status-logo) - :style {:width 40 - :height 40}}] + [rn/image + {:source (resources/get-image :status-logo) + :style {:width 40 + :height 40}}] (seq thumbnail-image) [photos/photo thumbnail-image {:size 40}] @@ -143,13 +154,17 @@ :on-press #(hide-sheet-and-dispatch [:open-modal :community-reorder-categories {:community-id id}])}]])])) -(defn blank-page [text] - [rn/view {:style {:padding 16 :flex 1 :flex-direction :row :align-items :center :justify-content :center}} - [quo/text {:align :center - :color :secondary} +(defn blank-page + [text] + [rn/view + {:style {:padding 16 :flex 1 :flex-direction :row :align-items :center :justify-content :center}} + [quo/text + {:align :center + :color :secondary} text]]) -(defn community-chat-item [{:keys [chat-id] :as home-item} _ _ _] +(defn community-chat-item + [{:keys [chat-id] :as home-item} _ _ _] [inner-item/home-list-item-old ;; We want communities to behave as public chats when it comes to ;; unread indicator @@ -162,7 +177,8 @@ {:content (fn [] [sheets/actions home-item])}])}]) -(defn categories-accordion [community-id chats categories data] +(defn categories-accordion + [community-id chats categories data] [:<> (for [{:keys [name id state]} categories] ^{:key (str "cat" name id)} @@ -180,7 +196,8 @@ [community-chat-item chat nil nil data])]}] [quo/separator]])]) -(defn community-chat-list [community-id categories from-chat] +(defn community-chat-list + [community-id categories from-chat] (let [chats (rf/sub [:chats/sorted-categories-by-community-id community-id])] (if (and (empty? categories) (empty? chats)) [blank-page (i18n/label :t/welcome-community-blank-message)] @@ -191,18 +208,21 @@ :data (get chats "") :render-data {:from-chat from-chat} :render-fn community-chat-item - :header [categories-accordion community-id chats categories {:from-chat from-chat}] + :header [categories-accordion community-id chats categories + {:from-chat from-chat}] :footer [rn/view {:height 68}]}]))) -(defn channel-preview-item [{:keys [id name]}] +(defn channel-preview-item + [{:keys [id name]}] (let [color colors/default-community-color] [quo/list-item {:icon [chat-icon.screen/chat-icon-view-chat-list id true name color false false] - :title [rn/view {:flex-direction :row - :flex 1 - :padding-right 16 - :align-items :center} + :title [rn/view + {:flex-direction :row + :flex 1 + :padding-right 16 + :align-items :center} [icons/icon :main-icons/tiny-group {:color colors/black :width 15 @@ -210,14 +230,16 @@ :container-style {:width 15 :height 15 :margin-right 2}}] - [quo/text {:weight :medium - :accessibility-label :chat-name-text - :ellipsize-mode :tail - :number-of-lines 1} + [quo/text + {:weight :medium + :accessibility-label :chat-name-text + :ellipsize-mode :tail + :number-of-lines 1} (utils/truncate-str name 30)]] :title-accessibility-label :chat-name-text}])) -(defn community-channel-preview-list [_ chats-without-id] +(defn community-channel-preview-list + [_ chats-without-id] (let [chats (reduce-kv (fn [acc k v] (conj acc (assoc v :id (name k)))) @@ -230,26 +252,31 @@ :data chats :render-fn channel-preview-item}])) -(defn unknown-community [community-id] +(defn unknown-community + [community-id] (let [fetching (rf/sub [:communities/fetching-community community-id])] [:<> {:style {:flex 1}} - [topbar/topbar {:title (if fetching (i18n/label :t/fetching-community) (i18n/label :t/not-found))}] - [rn/view {:style {:padding 16 :flex 1 :flex-direction :row :align-items :center :justify-content :center}} + [topbar/topbar {:title (if fetching (i18n/label :t/fetching-community) (i18n/label :t/not-found))}] + [rn/view + {:style {:padding 16 :flex 1 :flex-direction :row :align-items :center :justify-content :center}} - [quo/button {:on-press (when-not fetching #(rf/dispatch [::link-preview/resolve-community-info community-id])) - :disabled fetching - :color :secondary} + [quo/button + {:on-press (when-not fetching #(rf/dispatch [::link-preview/resolve-community-info community-id])) + :disabled fetching + :color :secondary} (if fetching [react/small-loading-indicator] (i18n/label :t/fetch-community))]]])) -(defn community [] +(defn community + [] (let [{:keys [community-id from-chat]} (rf/sub [:get-screen-params :community])] (fn [] (let [{:keys [id chats name images members permissions color joined can-request-access? can-join? requested-to-join-at admin] - :as community} (rf/sub [:communities/community community-id]) - categories (rf/sub [:communities/sorted-categories community-id])] + :as community} + (rf/sub [:communities/community community-id]) + categories (rf/sub [:communities/sorted-categories community-id])] (if community [rn/view {:style {:flex 1}} [topbar/topbar @@ -265,42 +292,46 @@ (when (or admin joined) [{:icon :main-icons/more :accessibility-label :community-menu-button - :on-press #(rf/dispatch [:bottom-sheet/show-sheet - {:content (fn [] - [community-actions community])}])}])}] + :on-press #(rf/dispatch [:bottom-sheet/show-sheet + {:content (fn [] + [community-actions community])}])}])}] (if joined [community-chat-list id categories false from-chat] [community-channel-preview-list id chats]) (when admin [components.plus-button/plus-button-old - {:on-press #(rf/dispatch [:bottom-sheet/show-sheet - {:content (fn [] - [community-plus-actions community])}]) + {:on-press #(rf/dispatch [:bottom-sheet/show-sheet + {:content (fn [] + [community-plus-actions community])}]) :accessibility-label :new-chat-button}]) (when-not joined (cond can-join? [toolbar/toolbar {:show-border? true - :center [quo/button {:on-press #(rf/dispatch [:communities/join id]) - :type :secondary} + :center [quo/button + {:on-press #(rf/dispatch [:communities/join id]) + :type :secondary} (i18n/label :t/join)]}] can-request-access? (if (and (pos? requested-to-join-at) (not (can-request-access-again? requested-to-join-at))) [toolbar/toolbar {:show-border? true - :left [quo/text {:color :secondary} (i18n/label :t/membership-request-pending)]}] + :left [quo/text {:color :secondary} + (i18n/label :t/membership-request-pending)]}] [toolbar/toolbar {:show-border? true - :center [quo/button {:on-press #(rf/dispatch [::communities/request-to-join id]) - :type :secondary} + :center [quo/button + {:on-press #(rf/dispatch [::communities/request-to-join id]) + :type :secondary} (i18n/label :t/request-access)]}]) :else [toolbar/toolbar {:show-border? true - :center [quo/button {:on-press #(rf/dispatch [:communities/join id]) - :type :secondary} + :center [quo/button + {:on-press #(rf/dispatch [:communities/join id]) + :type :secondary} (i18n/label :t/follow)]}])) [floating-shell-button/floating-shell-button {:jump-to {:on-press #(rf/dispatch [:shell/navigate-to-jump-to]) diff --git a/src/status_im/ui/screens/communities/community_emoji_thumbnail_picker.cljs b/src/status_im/ui/screens/communities/community_emoji_thumbnail_picker.cljs index e3f9600e89..af7bf4a4d3 100644 --- a/src/status_im/ui/screens/communities/community_emoji_thumbnail_picker.cljs +++ b/src/status_im/ui/screens/communities/community_emoji_thumbnail_picker.cljs @@ -1,44 +1,52 @@ (ns status-im.ui.screens.communities.community-emoji-thumbnail-picker - (:require [quo.react-native :as rn] + (:require [clojure.string :as string] [quo.core :as quo] - [clojure.string :as string] - [status-im.ui.components.react :as react] - [status-im.utils.handlers :refer [>evt evt]])) -(defn thumbnail-preview-section [] +(defn thumbnail-preview-section + [] (let [{:keys [color emoji]} (evt [::communities/create-channel-field :color item-color])} + item-color (:color item) + key (:key key) + color-selected? (= (string/lower-case item-color) (string/lower-case color))] + [react/touchable-opacity + {:key key + :accessibility-label :color-circle + :on-press #(>evt [::communities/create-channel-field :color item-color])} [rn/view {:style (styles/emoji-picker-color-border item-color color-selected?)} [rn/view {:style (styles/emoji-picker-color item-color)}]]])) -(defn update-emoji [emoji] +(defn update-emoji + [emoji] (when-not (string/blank? emoji) (>evt [::communities/create-channel-field :emoji emoji]))) -(defn emoji-keyboard-section [] +(defn emoji-keyboard-section + [] (let [{:keys [width height]} ( width height) 400 (- height styles/emoji-picker-upper-components-size))] + keyboard_height (if (> width height) + 400 + (- height styles/emoji-picker-upper-components-size))] [rn/view {:style {:height keyboard_height} :accessibility-label :emoji-keyboard-container} [rn/emoji-keyboard (styles/emoji-keyboard #(update-emoji (.-emoji ^js %)))]])) -(defn view [] +(defn view + [] [kb-presentation/keyboard-avoiding-view {:style {:flex 1}} [rn/scroll-view [quo/separator] diff --git a/src/status_im/ui/screens/communities/create.cljs b/src/status_im/ui/screens/communities/create.cljs index 0f37cc22ce..1a2f997b40 100644 --- a/src/status_im/ui/screens/communities/create.cljs +++ b/src/status_im/ui/screens/communities/create.cljs @@ -1,34 +1,37 @@ (ns status-im.ui.screens.communities.create - (:require [quo.react-native :as rn] - [status-im.i18n.i18n :as i18n] + (:require [clojure.string :as string] [quo.core :as quo] - [clojure.string :as string] - [status-im.utils.handlers :refer [>evt evt]] + [status-im.utils.image :as utils.image] [utils.debounce :as debounce])) (def max-name-length 30) (def max-description-length 140) -(defn valid? [community-name community-description] +(defn valid? + [community-name community-description] (and (not (string/blank? community-name)) (not (string/blank? community-description)) (<= (count community-name) max-name-length) (<= (count community-description) max-description-length))) (def crop-size 1000) -(def crop-opts {:cropping true - :cropperCircleOverlay true - :width crop-size - :height crop-size}) +(def crop-opts + {:cropping true + :cropperCircleOverlay true + :width crop-size + :height crop-size}) -(defn pick-pic [] +(defn pick-pic + [] ;;we need timeout because first we need to close bottom sheet and then open picker (js/setTimeout (fn [] @@ -39,7 +42,8 @@ crop-opts)) 300)) -(defn take-pic [] +(defn take-pic + [] ;;we need timeout because first we need to close bottom sheet and then open picker (js/setTimeout (fn [] @@ -49,107 +53,128 @@ crop-opts)) 300)) -(defn bottom-sheet [has-picture editing?] +(defn bottom-sheet + [has-picture editing?] (fn [] [:<> - [quo/list-item {:accessibility-label :take-photo - :theme :accent - :icon :main-icons/camera - :title (i18n/label :t/community-image-take) - :on-press #(do - (>evt [:bottom-sheet/hide]) - (take-pic))}] - [quo/list-item {:accessibility-label :pick-photo - :icon :main-icons/gallery - :theme :accent - :title (i18n/label :t/community-image-pick) - :on-press #(do - (>evt [:bottom-sheet/hide]) - (pick-pic))}] + [quo/list-item + {:accessibility-label :take-photo + :theme :accent + :icon :main-icons/camera + :title (i18n/label :t/community-image-take) + :on-press #(do + (>evt [:bottom-sheet/hide]) + (take-pic))}] + [quo/list-item + {:accessibility-label :pick-photo + :icon :main-icons/gallery + :theme :accent + :title (i18n/label :t/community-image-pick) + :on-press #(do + (>evt [:bottom-sheet/hide]) + (pick-pic))}] (when (and has-picture (not editing?)) - [quo/list-item {:accessibility-label :remove-photo - :icon :main-icons/delete - :theme :accent - :title (i18n/label :t/community-image-remove) - :on-press #(do - (>evt [:bottom-sheet/hide]) - (>evt [::communities/remove-field :image]))}])])) + [quo/list-item + {:accessibility-label :remove-photo + :icon :main-icons/delete + :theme :accent + :title (i18n/label :t/community-image-remove) + :on-press #(do + (>evt [:bottom-sheet/hide]) + (>evt [::communities/remove-field :image]))}])])) -(defn photo-picker [] +(defn photo-picker + [] (let [{:keys [image editing?]} (evt [:bottom-sheet/show-sheet - {:content (bottom-sheet (boolean image) editing?)}])} - [rn/view {:style {:width 128 - :height 128}} - [rn/view {:style {:flex 1 - :border-radius 64 - :background-color (colors/get-color :ui-01) - :justify-content :center - :align-items :center}} + [rn/view + {:style {:padding-top 16 + :align-items :center}} + [rn/touchable-opacity + {:on-press #(>evt [:bottom-sheet/show-sheet + {:content (bottom-sheet (boolean image) editing?)}])} + [rn/view + {:style {:width 128 + :height 128}} + [rn/view + {:style {:flex 1 + :border-radius 64 + :background-color (colors/get-color :ui-01) + :justify-content :center + :align-items :center}} (if image - [rn/image {:source (utils.image/source image) - :style {:width 128 - :height 128 - :border-radius 64} - :resize-mode :cover - :accessibility-label :community-image}] + [rn/image + {:source (utils.image/source image) + :style {:width 128 + :height 128 + :border-radius 64} + :resize-mode :cover + :accessibility-label :community-image}] [:<> [icons/icon :main-icons/photo {:color (colors/get-color :icon-02)}] [quo/text {:color :secondary} (i18n/label :t/community-thumbnail-upload)]])] - [rn/view {:style {:position :absolute - :top 0 - :right 7}} - [rn/view {:style {:width 40 - :height 40 - :background-color (colors/get-color :interactive-01) - :border-radius 20 - :align-items :center - :justify-content :center - :shadow-offset {:width 0 :height 1} - :shadow-radius 6 - :shadow-opacity 1 - :shadow-color (colors/get-color :shadow-01) - :elevation 2}} + [rn/view + {:style {:position :absolute + :top 0 + :right 7}} + [rn/view + {:style {:width 40 + :height 40 + :background-color (colors/get-color :interactive-01) + :border-radius 20 + :align-items :center + :justify-content :center + :shadow-offset {:width 0 :height 1} + :shadow-radius 6 + :shadow-opacity 1 + :shadow-color (colors/get-color :shadow-01) + :elevation 2}} [icons/icon :main-icons/add {:color colors/white}]]]]]])) -(defn countable-label [{:keys [label text max-length]}] - [rn/view {:style {:padding-bottom 10 - :justify-content :space-between - :align-items :flex-end - :flex-direction :row - :flex-wrap :nowrap}} +(defn countable-label + [{:keys [label text max-length]}] + [rn/view + {:style {:padding-bottom 10 + :justify-content :space-between + :align-items :flex-end + :flex-direction :row + :flex-wrap :nowrap}} [quo/text label] - [quo/text {:size :small - :color (if (> (count text) max-length) - :negative - :secondary)} + [quo/text + {:size :small + :color (if (> (count text) max-length) + :negative + :secondary)} (str (count text) "/" max-length)]]) -(defn form [] +(defn form + [] (let [{:keys [name description membership editing?]} (evt [::communities/create-field :name %]) + :on-change-text #(>evt [::communities/create-field :name %]) :auto-focus true}]] - [rn/view {:style {:padding-bottom 16 - :padding-top 10 - :padding-horizontal 16}} - [countable-label {:label (i18n/label :t/give-a-short-description-community) - :text description - :max-length max-description-length}] + [rn/view + {:style {:padding-bottom 16 + :padding-top 10 + :padding-horizontal 16}} + [countable-label + {:label (i18n/label :t/give-a-short-description-community) + :text description + :max-length max-description-length}] [quo/text-input {:placeholder (i18n/label :t/give-a-short-description-community) :multiline true @@ -158,25 +183,32 @@ [quo/list-header {:color :main} (i18n/label :t/community-thumbnail-image)] [photo-picker] - (when-not editing? [:<> - [quo/separator {:style {:margin-vertical 10}}] - [quo/list-item {:title (i18n/label :t/membership-button) - :accessory-text (i18n/label (get-in memberships/options [membership :title] :t/membership-none)) - :accessory :text - :on-press #(>evt [:navigate-to :community-membership]) - :chevron true - :size :small}] - [quo/list-footer - (i18n/label (get-in memberships/options [membership :description] :t/membership-none-placeholder))]])])) + (when-not editing? + [:<> + [quo/separator {:style {:margin-vertical 10}}] + [quo/list-item + {:title (i18n/label :t/membership-button) + :accessory-text (i18n/label (get-in memberships/options + [membership :title] + :t/membership-none)) + :accessory :text + :on-press #(>evt [:navigate-to :community-membership]) + :chevron true + :size :small}] + [quo/list-footer + (i18n/label + (get-in memberships/options [membership :description] :t/membership-none-placeholder))]])])) -(defn view [] +(defn view + [] (let [{:keys [name description]} (evt evt]] + [utils.debounce :as debounce])) -(defn valid? [channel-name channel-description] +(defn valid? + [channel-name channel-description] (and (not (string/blank? channel-name)) (not (string/blank? channel-description)) (<= (count channel-name) create/max-name-length) (<= (count channel-description) create/max-description-length))) -(defn thumbnail [] +(defn thumbnail + [] (let [{:keys [color emoji]} (evt [:open-modal :community-emoji-thumbnail-picker nil])]])) -(defn form [] +(defn form + [] (let [{:keys [name description]} ( [thumbnail] [rn/view {:padding-horizontal 16} [quo/text-input {:placeholder (i18n/label :t/name-your-channel-placeholder) - :on-change-text #(>evt [::communities/create-channel-field :name %]) + :on-change-text #(>evt [::communities/create-channel-field :name %]) :default-value name :auto-focus false}]] [quo/separator {:style {:margin-vertical 16}}] [rn/view {:padding-horizontal 16} - [create/countable-label {:label (i18n/label :t/description) - :text description - :max-length create/max-description-length}] + [create/countable-label + {:label (i18n/label :t/description) + :text description + :max-length create/max-description-length}] [quo/text-input {:placeholder (i18n/label :t/give-a-short-description-community) :multiline true :default-value description - :on-change-text #(>evt [::communities/create-channel-field :description %])}]]]])) + :on-change-text #(>evt [::communities/create-channel-field :description %])}]]]])) -(defn view [] +(defn view + [] (let [{:keys [name description]} (evt evt]])) -(defn edit [] +(defn edit + [] (let [{:keys [name description]} ( [community.create/form] [toolbar/toolbar {:show-border? true :center - [quo/button {:disabled (not (community.create/valid? name description)) - :type :secondary - :on-press #(>evt [::communities/edit-confirmation-pressed])} + [quo/button + {:disabled (not (community.create/valid? name description)) + :type :secondary + :on-press #(>evt [::communities/edit-confirmation-pressed])} (i18n/label :t/save)]}]])) diff --git a/src/status_im/ui/screens/communities/edit_channel.cljs b/src/status_im/ui/screens/communities/edit_channel.cljs index d7a730ea84..3c2f95b003 100644 --- a/src/status_im/ui/screens/communities/edit_channel.cljs +++ b/src/status_im/ui/screens/communities/edit_channel.cljs @@ -1,17 +1,19 @@ (ns status-im.ui.screens.communities.edit-channel (:require [clojure.string :as string] [quo.core :as quo] + [status-im.communities.core :as communities] [status-im.i18n.i18n :as i18n] [status-im.ui.components.toolbar :as toolbar] - [status-im.communities.core :as communities] - [utils.debounce :as debounce] + [status-im.ui.screens.communities.create-channel :as create-channel] [status-im.utils.handlers :refer [ @@ -19,9 +21,10 @@ [toolbar/toolbar {:show-border? true :center - [quo/button {:disabled (not (valid? name)) - :type :secondary - :on-press #(debounce/dispatch-and-chill - [::communities/edit-channel-confirmation-pressed] - 3000)} + [quo/button + {:disabled (not (valid? name)) + :type :secondary + :on-press #(debounce/dispatch-and-chill + [::communities/edit-channel-confirmation-pressed] + 3000)} (i18n/label :t/save)]}]]))) diff --git a/src/status_im/ui/screens/communities/icon.cljs b/src/status_im/ui/screens/communities/icon.cljs index 48dd081c72..86f2646149 100644 --- a/src/status_im/ui/screens/communities/icon.cljs +++ b/src/status_im/ui/screens/communities/icon.cljs @@ -2,18 +2,20 @@ (:require [quo.design-system.colors :as colors] [status-im.constants :as constants] [status-im.react-native.resources :as resources] + [status-im.ui.components.chat-icon.screen :as chat-icon.screen] [status-im.ui.components.react :as react] - [status-im.ui.screens.chat.photos :as photos] - [status-im.ui.components.chat-icon.screen :as chat-icon.screen])) + [status-im.ui.screens.chat.photos :as photos])) -(defn community-icon [{:keys [id name images color]}] - (let [color (or color (rand-nth colors/chat-colors)) +(defn community-icon + [{:keys [id name images color]}] + (let [color (or color (rand-nth colors/chat-colors)) thumbnail-image (get-in images [:thumbnail :uri])] (cond (= id constants/status-community-id) - [react/image {:source (resources/get-image :status-logo) ;; TODO replace with real data (or remove this code) - :style {:width 40 - :height 40}}] + [react/image + {:source (resources/get-image :status-logo) ;; TODO replace with real data (or remove this code) + :style {:width 40 + :height 40}}] (seq thumbnail-image) [photos/photo thumbnail-image {:size 40}] @@ -21,15 +23,18 @@ [chat-icon.screen/chat-icon-view-chat-list id true name color false false]))) -;; TODO (flexsurfer) reimplement with new design, its still old design, photos and chat-icon components from old design -(defn community-icon-redesign [{:keys [id name images color]} size] - (let [color (or color (rand-nth colors/chat-colors)) +;; TODO (flexsurfer) reimplement with new design, its still old design, photos and chat-icon components +;; from old design +(defn community-icon-redesign + [{:keys [id name images color]} size] + (let [color (or color (rand-nth colors/chat-colors)) thumbnail-image (get-in images [:thumbnail :uri])] (cond (= id constants/status-community-id) - [react/image {:source (resources/get-image :status-logo) ;; TODO replace with real data - :style {:width size - :height size}}] + [react/image + {:source (resources/get-image :status-logo) ;; TODO replace with real data + :style {:width size + :height size}}] (seq thumbnail-image) [photos/photo thumbnail-image {:size size}] diff --git a/src/status_im/ui/screens/communities/import.cljs b/src/status_im/ui/screens/communities/import.cljs index 8bb88d8652..0c03bec2c6 100644 --- a/src/status_im/ui/screens/communities/import.cljs +++ b/src/status_im/ui/screens/communities/import.cljs @@ -1,20 +1,23 @@ (ns status-im.ui.screens.communities.import - (:require [quo.react-native :as rn] + (:require [quo.core :as quo] + [quo.react-native :as rn] [reagent.core :as reagent] - [quo.core :as quo] - [status-im.i18n.i18n :as i18n] - [status-im.utils.handlers :refer [>evt]] [status-im.communities.core :as communities] - [status-im.ui.components.toolbar :as toolbar])) + [status-im.i18n.i18n :as i18n] + [status-im.ui.components.toolbar :as toolbar] + [status-im.utils.handlers :refer [>evt]])) -(defn view [] +(defn view + [] (let [community-key (reagent/atom "")] (fn [] [:<> - [rn/scroll-view {:style {:flex 1} - :content-container-style {:padding 16}} - [rn/view {:style {:padding-bottom 16 - :padding-top 10}} + [rn/scroll-view + {:style {:flex 1} + :content-container-style {:padding 16}} + [rn/view + {:style {:padding-bottom 16 + :padding-top 10}} [quo/text-input {:label (i18n/label :t/community-key) :placeholder (i18n/label :t/community-key-placeholder) @@ -24,7 +27,8 @@ [toolbar/toolbar {:show-border? true - :center [quo/button {:disabled (= @community-key "") - :type :secondary - :on-press #(>evt [::communities/import @community-key])} + :center [quo/button + {:disabled (= @community-key "") + :type :secondary + :on-press #(>evt [::communities/import @community-key])} (i18n/label :t/import)]}]]))) diff --git a/src/status_im/ui/screens/communities/invite.cljs b/src/status_im/ui/screens/communities/invite.cljs index 967797d2af..0613940ff3 100644 --- a/src/status_im/ui/screens/communities/invite.cljs +++ b/src/status_im/ui/screens/communities/invite.cljs @@ -1,21 +1,23 @@ (ns status-im.ui.screens.communities.invite - (:require [reagent.core :as reagent] - [quo.react-native :as rn] + (:require [clojure.string :as string] [quo.core :as quo] - [status-im.i18n.i18n :as i18n] - [status-im.constants :as constants] - [status-im.ui.components.toolbar :as toolbar] - [status-im.utils.handlers :refer [evt-once]] + [quo.react-native :as rn] + [reagent.core :as reagent] [status-im.communities.core :as communities] - [status-im.ui.components.topbar :as topbar] - [status-im.ui.components.chat-icon.screen :as chat-icon.screen] + [status-im.constants :as constants] + [status-im.i18n.i18n :as i18n] [status-im.multiaccounts.core :as multiaccounts] - [clojure.string :as string])) + [status-im.ui.components.chat-icon.screen :as chat-icon.screen] + [status-im.ui.components.toolbar :as toolbar] + [status-im.ui.components.topbar :as topbar] + [status-im.utils.handlers :refer [evt-once]])) -(defn header [user-pk] +(defn header + [user-pk] [:<> - [rn/view {:style {:padding-horizontal 16 - :padding-vertical 8}} + [rn/view + {:style {:padding-horizontal 16 + :padding-vertical 8}} [quo/text-input {:label (i18n/label :t/enter-user-pk) :placeholder (i18n/label :t/enter-user-pk) @@ -25,7 +27,8 @@ [quo/separator {:style {:margin-vertical 8}}] [quo/list-header (i18n/label :t/contacts)]]) -(defn contacts-list-item [{:keys [public-key active] :as contact} _ _ {:keys [selected]}] +(defn contacts-list-item + [{:keys [public-key active] :as contact} _ _ {:keys [selected]}] (let [[first-name second-name] (multiaccounts/contact-two-names contact true)] [quo/list-item {:title first-name @@ -39,44 +42,50 @@ (swap! selected disj public-key) (swap! selected conj public-key)))}])) -(defn invite [] +(defn invite + [] (let [user-pk (reagent/atom "") contacts-selected (reagent/atom #{}) {:keys [invite?]} ( - [topbar/topbar {:title (i18n/label (if can-invite? - :t/invite-people - :t/community-share-title)) - :modal? true}] - [rn/flat-list {:style {:flex 1} - :content-container-style {:padding-vertical 16} - ;:header [header user-pk] - :render-data {:selected contacts-selected} - :render-fn contacts-list-item - :key-fn (fn [{:keys [active public-key]}] - (str public-key active)) - :data contacts}] + [topbar/topbar + {:title (i18n/label (if can-invite? + :t/invite-people + :t/community-share-title)) + :modal? true}] + [rn/flat-list + {:style {:flex 1} + :content-container-style {:padding-vertical 16} + ;:header [header user-pk] + :render-data {:selected contacts-selected} + :render-fn contacts-list-item + :key-fn (fn [{:keys [active public-key]}] + (str public-key active)) + :data contacts}] [toolbar/toolbar {:show-border? true :center - [quo/button {:disabled (and (string/blank? @user-pk) + [quo/button + {:disabled (and (string/blank? @user-pk) (zero? (count selected))) - :accessibility-label :share-community-link - :type :secondary - :on-press #(>evt-once + :accessibility-label :share-community-link + :type :secondary + :on-press #(>evt-once [(if can-invite? ::communities/invite-people-confirmation-pressed - ::communities/share-community-confirmation-pressed) @user-pk selected])} + ::communities/share-community-confirmation-pressed) @user-pk + selected])} (i18n/label (if can-invite? :t/invite :t/share))]}]])))) diff --git a/src/status_im/ui/screens/communities/members.cljs b/src/status_im/ui/screens/communities/members.cljs index c64d5b73ef..f2f6bec4d3 100644 --- a/src/status_im/ui/screens/communities/members.cljs +++ b/src/status_im/ui/screens/communities/members.cljs @@ -1,22 +1,24 @@ (ns status-im.ui.screens.communities.members - (:require [quo.react-native :as rn] - [quo.core :as quo] + (:require [quo.core :as quo] + [quo.react-native :as rn] [reagent.core :as reagent] + [status-im.communities.core :as communities] [status-im.constants :as constants] - [status-im.ui.components.react :as react] - [status-im.utils.handlers :refer [evt]] - [status-im.ui.components.chat-icon.screen :as chat-icon] - [status-im.ui.components.unviewed-indicator :as unviewed-indicator] - [status-im.multiaccounts.core :as multiaccounts] - [status-im.ui.components.topbar :as topbar] [status-im.i18n.i18n :as i18n] - [status-im.communities.core :as communities])) + [status-im.multiaccounts.core :as multiaccounts] + [status-im.ui.components.chat-icon.screen :as chat-icon] + [status-im.ui.components.react :as react] + [status-im.ui.components.topbar :as topbar] + [status-im.ui.components.unviewed-indicator :as unviewed-indicator] + [status-im.utils.handlers :refer [evt]])) -(defn hide-sheet-and-dispatch [event] +(defn hide-sheet-and-dispatch + [event] (>evt [:bottom-sheet/hide]) (>evt event)) -(defn member-sheet [first-name {:keys [public-key] :as member} community-id can-kick-users? can-manage-users? admin?] +(defn member-sheet + [first-name {:keys [public-key] :as member} community-id can-kick-users? can-manage-users? admin?] [:<> [quo/list-item {:theme :accent @@ -30,29 +32,35 @@ (when can-kick-users? [:<> [quo/separator {:style {:margin-vertical 8}}] - [quo/list-item {:theme :negative - :icon :main-icons/arrow-left - :title (i18n/label :t/member-kick) - :on-press #(>evt [::communities/member-kick community-id public-key])}]]) + [quo/list-item + {:theme :negative + :icon :main-icons/arrow-left + :title (i18n/label :t/member-kick) + :on-press #(>evt [::communities/member-kick community-id public-key])}]]) (when can-manage-users? [:<> - [quo/list-item {:theme :negative - :icon :main-icons/cancel - :title (i18n/label :t/member-ban) - :on-press #(>evt [::communities/member-ban community-id public-key])}]]) + [quo/list-item + {:theme :negative + :icon :main-icons/cancel + :title (i18n/label :t/member-ban) + :on-press #(>evt [::communities/member-ban community-id public-key])}]]) (when admin? [:<> - [quo/list-item {:theme :accent - :icon :main-icons/make-admin - :title (i18n/label :t/make-moderator) - :on-press #(>evt [:community.member/add-role community-id public-key constants/community-member-role-moderator])}]])]) + [quo/list-item + {:theme :accent + :icon :main-icons/make-admin + :title (i18n/label :t/make-moderator) + :on-press #(>evt [:community.member/add-role community-id public-key + constants/community-member-role-moderator])}]])]) -(defn render-member [public-key _ _ {:keys [community-id - my-public-key - can-manage-users? - can-kick-users? - admin?]}] - (let [member (evt [:bottom-sheet/show-sheet - {:content (fn [] - [member-sheet first-name member community-id can-kick-users? can-manage-users? admin?])}]) - :type :icon - :theme :icon - :accessibility-label :menu-option} + [quo/button + {:on-press + #(>evt [:bottom-sheet/show-sheet + {:content (fn [] + [member-sheet first-name member community-id + can-kick-users? can-manage-users? admin?])}]) + :type :icon + :theme :icon + :accessibility-label :menu-option} :main-icons/more])}])) -(defn header [community-id] +(defn header + [community-id] [:<> - [quo/list-item {:icon :main-icons/share - :title (i18n/label :t/invite-people) - :accessibility-label :community-invite-people - :theme :accent - :on-press #(>evt [:communities/invite-people-pressed community-id])}] + [quo/list-item + {:icon :main-icons/share + :title (i18n/label :t/invite-people) + :accessibility-label :community-invite-people + :theme :accent + :on-press #(>evt [:communities/invite-people-pressed community-id])}] [quo/separator {:style {:margin-vertical 8}}]]) -(defn requests-to-join [community-id] +(defn requests-to-join + [community-id] (let [requests ( - [quo/list-item {:chevron true - :accessory - [react/view {:flex-direction :row} - (when (pos? requests-count) - [unviewed-indicator/unviewed-indicator requests-count])] - :on-press #(>evt [:navigate-to :community-requests-to-join {:community-id community-id}]) - :title (i18n/label :t/membership-requests)}] + [quo/list-item + {:chevron true + :accessory + [react/view {:flex-direction :row} + (when (pos? requests-count) + [unviewed-indicator/unviewed-indicator requests-count])] + :on-press #(>evt [:navigate-to :community-requests-to-join {:community-id community-id}]) + :title (i18n/label :t/membership-requests)}] [quo/separator {:style {:margin-vertical 8}}]])) -(defn members [] +(defn members + [] (let [{:keys [community-id]} ( - [topbar/topbar {:title (i18n/label :t/community-members-title) - :subtitle (str (count sorted-members))}] + [topbar/topbar + {:title (i18n/label :t/community-members-title) + :subtitle (str (count sorted-members))}] [header community-id] (when (and can-manage-users? (= constants/community-on-request-access (:access permissions))) [requests-to-join community-id]) - [rn/flat-list {:data (keys sorted-members) - :render-data {:community-id community-id - :my-public-key my-public-key - :can-kick-users? (and can-manage-users? - (not= (:access permissions) - constants/community-no-membership-access)) - :can-manage-users? can-manage-users? - :admin? admin} - :key-fn identity - :render-fn render-member}]])))) + [rn/flat-list + {:data (keys sorted-members) + :render-data {:community-id community-id + :my-public-key my-public-key + :can-kick-users? (and can-manage-users? + (not= (:access permissions) + constants/community-no-membership-access)) + :can-manage-users? can-manage-users? + :admin? admin} + :key-fn identity + :render-fn render-member}]])))) -(defn members-container [] +(defn members-container + [] (reagent/create-class {:display-name "community-members-view" :component-did-mount (fn [] - (communities/fetch-requests-to-join! (get (evt evt]])) -(def options {constants/community-on-request-access - {:title :t/membership-approval - :description :t/membership-approval-description} - constants/community-no-membership-access - {:title :t/membership-free - :description :t/membership-free-description}}) +(def options + {constants/community-on-request-access + {:title :t/membership-approval + :description :t/membership-approval-description} + constants/community-no-membership-access + {:title :t/membership-free + :description :t/membership-free-description}}) -(defn option [{:keys [title description]} {:keys [selected on-select]}] +(defn option + [{:keys [title description]} {:keys [selected on-select]}] [:<> - [quo/list-item {:title (i18n/label title) - :size :small - :accessory :radio - :active selected - :on-press on-select}] + [quo/list-item + {:title (i18n/label title) + :size :small + :accessory :radio + :active selected + :on-press on-select}] [quo/list-footer (i18n/label description)] [quo/separator {:style {:margin-vertical 8}}]]) -(defn membership [] +(defn membership + [] (let [{:keys [membership]} ( [rn/scroll-view {} (doall (for [[id o] options] ^{:key (str "option-" id)} - [option o {:selected (= id membership) - :on-select #(>evt [::communities/create-field :membership id])}]))] + [option o + {:selected (= id membership) + :on-select #(>evt [::communities/create-field :membership id])}]))] [toolbar/toolbar {:show-border? true - :center [quo/button {:type :secondary - :on-press #(>evt [:navigate-back])} + :center [quo/button + {:type :secondary + :on-press #(>evt [:navigate-back])} (i18n/label :t/done)]}]])) diff --git a/src/status_im/ui/screens/communities/profile.cljs b/src/status_im/ui/screens/communities/profile.cljs index e99c4f0f6b..cd34fd6a93 100644 --- a/src/status_im/ui/screens/communities/profile.cljs +++ b/src/status_im/ui/screens/communities/profile.cljs @@ -1,52 +1,57 @@ (ns status-im.ui.screens.communities.profile - (:require [quo.core :as quo] - [status-im.utils.handlers :refer [>evt evt]])) -(defn management [] +(defn management + [] (let [{:keys [community-id]} ( - [quo/animated-header {:left-accessories [{:icon :main-icons/arrow-left - :accessibility-label :back-button - :on-press #(>evt [:navigate-back])}] - :right-accessories [{:icon :main-icons/share - :accessibility-label :invite-button - :on-press #(>evt [:communities/share-community-pressed community-id])}] - :extended-header (profile-header/extended-header - {:title name - :color (or color (rand-nth colors/chat-colors)) - :photo (if (= community-id constants/status-community-id) - (:uri - (rn/resolve-asset-source - (resources/get-image :status-logo))) - (get-in community [:images :large :uri])) - :membership (when request-membership? - (i18n/label :t/membership-approval)) - :subtitle (if show-members-count? - (i18n/label-pluralize members-count :t/community-members {:count members-count}) - (i18n/label :t/open-membership)) - :community? true}) - :use-insets true} + [quo/animated-header + {:left-accessories [{:icon :main-icons/arrow-left + :accessibility-label :back-button + :on-press #(>evt [:navigate-back])}] + :right-accessories [{:icon :main-icons/share + :accessibility-label :invite-button + :on-press #(>evt [:communities/share-community-pressed + community-id])}] + :extended-header (profile-header/extended-header + {:title name + :color (or color (rand-nth colors/chat-colors)) + :photo (if (= community-id constants/status-community-id) + (:uri + (rn/resolve-asset-source + (resources/get-image :status-logo))) + (get-in community [:images :large :uri])) + :membership (when request-membership? + (i18n/label :t/membership-approval)) + :subtitle (if show-members-count? + (i18n/label-pluralize members-count + :t/community-members + {:count members-count}) + (i18n/label :t/open-membership)) + :community? true}) + :use-insets true} [:<> (when-not (string/blank? description) [:<> @@ -55,57 +60,67 @@ [quo/separator {:style {:margin-vertical 8}}]]) [:<> (let [link (communities/universal-link community-id)] - [react/view {:padding-vertical 10 - :padding-horizontal 16} + [react/view + {:padding-vertical 10 + :padding-horizontal 16} [react/view {:margin-bottom 20} [quo/text {:color :secondary} (i18n/label :t/community-link)]] [copyable-text/copyable-text-view {:copied-text link} - [react/view {:border-radius 16 - :padding-horizontal 16 - :padding-vertical 11 - :background-color colors/blue-light} + [react/view + {:border-radius 16 + :padding-horizontal 16 + :padding-vertical 11 + :background-color colors/blue-light} [quo/text {:color :link} (subs link 8)]]]]) [quo/separator {:style {:margin-vertical 8}}]] (when show-members-count? - [quo/list-item {:chevron true - :accessory - [react/view {:flex-direction :row} - (when (pos? members-count) - [quo/text {:color :secondary} (str members-count)]) - [unviewed-indicator/unviewed-indicator (count requests-to-join)]] - :on-press #(>evt [:navigate-to :community-members {:community-id community-id}]) - :title (i18n/label :t/members-label) - :icon :main-icons/group-chat}]) + [quo/list-item + {:chevron true + :accessory + [react/view {:flex-direction :row} + (when (pos? members-count) + [quo/text {:color :secondary} (str members-count)]) + [unviewed-indicator/unviewed-indicator (count requests-to-join)]] + :on-press #(>evt [:navigate-to :community-members {:community-id community-id}]) + :title (i18n/label :t/members-label) + :icon :main-icons/group-chat}]) (when (and admin roles) - [quo/list-item {:chevron true - :title (i18n/label :t/commonuity-role) - :icon :main-icons/objects}]) + [quo/list-item + {:chevron true + :title (i18n/label :t/commonuity-role) + :icon :main-icons/objects}]) (when notifications - [quo/list-item {:chevron true - :title (i18n/label :t/chat-notification-preferences) - :icon :main-icons/notification}]) + [quo/list-item + {:chevron true + :title (i18n/label :t/chat-notification-preferences) + :icon :main-icons/notification}]) (when (or show-members-count? notifications (and admin roles)) [quo/separator {:style {:margin-vertical 8}}]) (when admin - [quo/list-item {:theme :accent - :icon :main-icons/edit - :title (i18n/label :t/edit-community) - :on-press #(>evt [::communities/open-edit-community community-id])}]) - [quo/list-item {:theme :accent - :icon :main-icons/arrow-left - :title (i18n/label :t/leave-community) - :on-press #(>evt [:communities/leave community-id])}] + [quo/list-item + {:theme :accent + :icon :main-icons/edit + :title (i18n/label :t/edit-community) + :on-press #(>evt [::communities/open-edit-community community-id])}]) + [quo/list-item + {:theme :accent + :icon :main-icons/arrow-left + :title (i18n/label :t/leave-community) + :on-press #(>evt [:communities/leave community-id])}] ;; Disable as not implemented yet (when false - [quo/list-item {:theme :negative - :icon :main-icons/delete - :title (i18n/label :t/delete) - :on-press #(>evt [::communities/delete-community community-id])}])]]])))) + [quo/list-item + {:theme :negative + :icon :main-icons/delete + :title (i18n/label :t/delete) + :on-press #(>evt [::communities/delete-community community-id])}])]]])))) -(defn management-container [] +(defn management-container + [] (reagent/create-class - {:display-name "community-profile-view" + {:display-name "community-profile-view" :component-did-mount (fn [] - (communities/fetch-requests-to-join! (get (evt evt]] + [status-im.utils.platform :as platform] + [status-im.utils.utils :as utils])) (def data (reagent/atom [])) (def state (reagent/atom {:tab :chats})) -(defn show-delete-chat-confirmation [community-id chat-id] +(defn show-delete-chat-confirmation + [community-id chat-id] (utils/show-confirmation {:title (i18n/label :t/delete-confirmation) :content (i18n/label :t/delete-chat-confirmation) :confirm-button-text (i18n/label :t/delete) :on-accept #(>evt [:delete-community-chat community-id chat-id])})) -(defn show-delete-category-confirmation [community-id category-id] +(defn show-delete-category-confirmation + [community-id category-id] (utils/show-confirmation {:title (i18n/label :t/delete-confirmation) :content (i18n/label :t/delete-category-confirmation) :confirm-button-text (i18n/label :t/delete) :on-accept #(>evt [:delete-community-category community-id category-id])})) -(defn categories-tab? [] +(defn categories-tab? + [] (let [{:keys [tab]} @state] (= tab :categories))) @@ -46,19 +49,21 @@ (let [chat-id (string/replace id community-id "") background-color (if is-active? colors/gray-lighter colors/white) home-item (clojure.set/rename-keys home-item {:id :chat-id})] - [rn/view {:accessibility-label :chat-item - :style (merge styles/category-item - {:background-color background-color})} + [rn/view + {:accessibility-label :chat-item + :style (merge styles/category-item + {:background-color background-color})} [rn/touchable-opacity {:accessibility-label :delete-community-chat :on-press #(show-delete-chat-confirmation community-id chat-id)} [icons/icon :main-icons/delete-circle {:no-color true}]] [rn/view {:flex 1} [inner-item/home-list-item-old (assoc home-item :edit? true) {:active-opacity 1}]] - [rn/touchable-opacity {:on-long-press drag - :delay-long-press 100 - :accessibility-label :chat-drag-handle - :style {:padding 20}} + [rn/touchable-opacity + {:on-long-press drag + :delay-long-press 100 + :accessibility-label :chat-drag-handle + :style {:padding 20}} [icons/icon :main-icons/reorder-handle {:no-color true :width 18 :height 12}]]])) (defn category-item @@ -67,9 +72,10 @@ category-none? (string/blank? id)] [:<> [quo/separator] - [rn/view {:accessibility-label :category-item - :style (merge styles/category-item - {:background-color background-color})} + [rn/view + {:accessibility-label :category-item + :style (merge styles/category-item + {:background-color background-color})} (if (not (categories-tab?)) [icons/icon :main-icons/channel-category {:color colors/gray}] [rn/touchable-opacity @@ -79,10 +85,11 @@ [rn/view {:flex 1} [rn/text {:style {:font-size 17 :margin-left 10 :color colors/black}} name]] (when (and (not category-none?) (categories-tab?)) - [rn/touchable-opacity {:accessibility-label :category-drag-handle - :on-long-press drag - :delay-long-press 100 - :style {:padding 20}} + [rn/touchable-opacity + {:accessibility-label :category-drag-handle + :on-long-press drag + :delay-long-press 100 + :style {:padding 20}} [icons/icon :main-icons/reorder-handle {:no-color true :width 18 :height 12}]])]])) (defn render-fn @@ -98,80 +105,105 @@ (if going-up? (if (= chat-type constants/community-chat-type) [categoryID (if second-call? (inc position) position)] - (if second-call? [id 0] - (calculate-chat-new-position-and-category (dec to) true old-category true))) + (if second-call? + [id 0] + (calculate-chat-new-position-and-category (dec to) true old-category true))) (if (= chat-type constants/community-chat-type) (if (= categoryID old-category) [categoryID position] - [categoryID (inc position)]) [id 0]))] + [categoryID (inc position)]) + [id 0]))] [new-category new-position])) -(defn update-local-atom [data-js] +(defn update-local-atom + [data-js] (reset! data data-js) (reagent/flush)) -(defn on-drag-end-chat [from to data-js] +(defn on-drag-end-chat + [from to data-js] (let [{:keys [id community-id categoryID position]} (get @data from) [new-category new-position] (calculate-chat-new-position-and-category - to false categoryID (> from to)) + to + false + categoryID + (> from to)) chat-id (string/replace id community-id "")] (when-not (and (= new-position position) (= new-category categoryID)) (update-local-atom data-js) (>evt [::communities/reorder-community-category-chat community-id new-category chat-id new-position])))) -(defn on-drag-end-category [from to data-js] +(defn on-drag-end-category + [from to data-js] (let [{:keys [id community-id position]} (get @data from)] (when (and (< to (count @data)) (not= position to) (not= id "")) (update-local-atom data-js) (>evt [::communities/reorder-community-category community-id id to])))) -(defn on-drag-end-fn [from to data-js] +(defn on-drag-end-fn + [from to data-js] (if (categories-tab?) (on-drag-end-category from to data-js) (when-not (= to 0) (on-drag-end-chat from to data-js)))) -(defn reset-data [categories chats] - (reset! data (if (categories-tab?) - categories - (walk/postwalk-replace - {:chat-id :id} - (reduce (fn [acc category] - (-> acc - (conj category) - (into (get chats (:id category))))) [] categories))))) +(defn reset-data + [categories chats] + (reset! data + (if (categories-tab?) + categories + (walk/postwalk-replace + {:chat-id :id} + (reduce (fn [acc category] + (-> acc + (conj category) + (into (get chats (:id category))))) + [] + categories))))) -(defn draggable-list [] +(defn draggable-list + [] [rn/draggable-flat-list {:key-fn :id :data @data :render-fn render-fn :autoscroll-threshold (if platform/android? 150 250) :autoscroll-speed (if platform/android? 10 150) ;; TODO - Use same speed for both ios and android - :container-style {:margin-bottom 108} ;; after bumping react native version to > 0.64 + :container-style {:margin-bottom 108} ;; after bumping react native version to > + ;; 0.64 :on-drag-end-fn on-drag-end-fn}]) -(defn chats_and_categories [] +(defn chats_and_categories + [] (let [{:keys [tab]} @state] [:<> - [react/view {:flex-direction :row :margin-horizontal 16 :margin-vertical 8 - :border-radius 8 :background-color colors/blue-light} + [react/view + {:flex-direction :row + :margin-horizontal 16 + :margin-vertical 8 + :border-radius 8 + :background-color colors/blue-light} [tabs/tab-button state :chats (i18n/label :t/edit-chats) (= tab :chats)] [tabs/tab-button state :categories (i18n/label :t/edit-categories) (= tab :categories)]]])) -(defn view [] - (let [{:keys [community-id]} ( [topbar/topbar diff --git a/src/status_im/ui/screens/communities/requests_to_join.cljs b/src/status_im/ui/screens/communities/requests_to_join.cljs index f18838bda5..a761a751cf 100644 --- a/src/status_im/ui/screens/communities/requests_to_join.cljs +++ b/src/status_im/ui/screens/communities/requests_to_join.cljs @@ -1,33 +1,42 @@ (ns status-im.ui.screens.communities.requests-to-join - (:require [quo.react-native :as rn] + (:require [quo.components.animated.pressable :as animation] [quo.core :as quo] - [reagent.core :as reagent] - [re-frame.core :as re-frame] [quo.design-system.colors :as colors] - [status-im.ui.components.react :as react] - [status-im.utils.handlers :refer [ - [topbar/topbar {:title (i18n/label :t/community-requests-to-join-title) - :subtitle (str (count requests))}] - [rn/flat-list {:data requests - :render-data {:community-id community-id - :can-manage-users? can-manage-users?} - :key-fn :id - :render-fn render-request}]])))) + [topbar/topbar + {:title (i18n/label :t/community-requests-to-join-title) + :subtitle (str (count requests))}] + [rn/flat-list + {:data requests + :render-data {:community-id community-id + :can-manage-users? can-manage-users?} + :key-fn :id + :render-fn render-request}]])))) -(defn requests-to-join-container [] +(defn requests-to-join-container + [] (reagent/create-class - {:display-name "community-requests-to-join-view" + {:display-name "community-requests-to-join-view" :component-did-mount (fn [] - (communities/fetch-requests-to-join! (get (evt evt]] + [utils.debounce :as debounce])) (def selected-item (reagent/atom "")) -(defn render-fn [{:keys [name id]}] - [quo/list-item {:title name - :accessory :radio - :on-press #(reset! selected-item id) - :active (= id @selected-item) - :icon [icons/icon :main-icons/channel-category {:color colors/gray}]}]) +(defn render-fn + [{:keys [name id]}] + [quo/list-item + {:title name + :accessory :radio + :on-press #(reset! selected-item id) + :active (= id @selected-item) + :icon [icons/icon :main-icons/channel-category {:color colors/gray}]}]) -(defn view [] +(defn view + [] (let [{:keys [community-id chat]} ( - [topbar/topbar {:title (str "#" (:chat-name chat)) - :subtitle (i18n/label :t/public-channel) - :modal? true}] + [topbar/topbar + {:title (str "#" (:chat-name chat)) + :subtitle (i18n/label :t/public-channel) + :modal? true}] [react/view {:flex 1} [quo/list-header (i18n/label :t/category)] [list/flat-list @@ -39,23 +43,25 @@ :content-container-style {:padding-vertical 8} :keyboard-should-persist-taps :always :footer [quo/list-item - {:theme :accent - :icon :main-icons/channel-category + {:theme :accent + :icon :main-icons/channel-category :on-press #(>evt [:open-modal :create-community-category {:community-id community-id}]) - :title (i18n/label :t/create-category)}] + :title (i18n/label :t/create-category)}] :data (conj categories {:name (i18n/label :t/none) :id ""}) :render-fn render-fn}]] [toolbar/toolbar {:show-border? true :center - [quo/button {:type :secondary - :on-press #(debounce/dispatch-and-chill - [::communities/change-category-confirmation-pressed - community-id - @selected-item - (assoc comm-chat :position - (count (get chats @selected-item)))] ;; Add as last item in new category - 3000)} + [quo/button + {:type :secondary + :on-press #(debounce/dispatch-and-chill + [::communities/change-category-confirmation-pressed + community-id + @selected-item + (assoc comm-chat + :position + (count (get chats @selected-item)))] ;; Add as last item in new category + 3000)} (i18n/label :t/done)]}]])))) diff --git a/src/status_im/ui/screens/communities/views.cljs b/src/status_im/ui/screens/communities/views.cljs index b76567c6ec..2fdab7730b 100644 --- a/src/status_im/ui/screens/communities/views.cljs +++ b/src/status_im/ui/screens/communities/views.cljs @@ -1,104 +1,120 @@ (ns status-im.ui.screens.communities.views - (:require - [quo.core :as quo] - [status-im.i18n.i18n :as i18n] - [status-im.utils.core :as utils] - [status-im.constants :as constants] - [status-im.communities.core :as communities] - [status-im.utils.handlers :refer [>evt evt]])) -(defn hide-sheet-and-dispatch [event] +(defn hide-sheet-and-dispatch + [event] (>evt [:bottom-sheet/hide]) (>evt event)) -(defn community-unviewed-count [id] - (let [{:keys [unviewed-messages-count unviewed-mentions-count]} (evt [:communities/load-category-states id]) - (>evt [:dismiss-keyboard]) - (>evt [:navigate-to :community {:community-id id}])) - :on-long-press #(>evt [:bottom-sheet/show-sheet - {:content (fn [] - [community/community-actions community])}])} +(defn community-home-list-item + [{:keys [id name last?] :as community}] + [react/touchable-opacity + {:style (merge {:height 64} + (when last? + {:border-bottom-color (quo.colors/get-color :ui-01) + :border-bottom-width 1})) + :on-press (fn [] + (>evt [:communities/load-category-states id]) + (>evt [:dismiss-keyboard]) + (>evt [:navigate-to :community {:community-id id}])) + :on-long-press #(>evt [:bottom-sheet/show-sheet + {:content (fn [] + [community/community-actions community])}])} [:<> [react/view {:top 12 :left 16 :position :absolute} [communities.icon/community-icon community]] - [react/view {:style {:margin-left 72 - :flex-direction :row - :flex 1} - :accessibility-label :chat-name-text} - [react/view {:flex-direction :row - :flex 1 - :padding-right 16 - :align-items :center} - [quo/text {:weight :medium - :accessibility-label :chat-name-text - :font-size 17 - :ellipsize-mode :tail - :number-of-lines 1} + [react/view + {:style {:margin-left 72 + :flex-direction :row + :flex 1} + :accessibility-label :chat-name-text} + [react/view + {:flex-direction :row + :flex 1 + :padding-right 16 + :align-items :center} + [quo/text + {:weight :medium + :accessibility-label :chat-name-text + :font-size 17 + :ellipsize-mode :tail + :number-of-lines 1} name]] - [react/view {:flex-direction :row - :flex 1 - :margin-right 15 - :justify-content :flex-end - :align-items :center} + [react/view + {:flex-direction :row + :flex 1 + :margin-right 15 + :justify-content :flex-end + :align-items :center} [community-unviewed-count id]]]]]) -(defn community-list-item [{:keys [id permissions members name description] :as community}] - (let [members-count (count members) +(defn community-list-item + [{:keys [id permissions members name description] :as community}] + (let [members-count (count members) show-members-count? (not= (:access permissions) constants/community-no-membership-access)] [quo/list-item {:icon [communities.icon/community-icon community] - :title [react/view {:flex-direction :row - :flex 1 - :padding-right 16 - :align-items :center} - [quo/text {:weight :medium - :accessibility-label :community-name-text - :ellipsize-mode :tail - :number-of-lines 1} + :title [react/view + {:flex-direction :row + :flex 1 + :padding-right 16 + :align-items :center} + [quo/text + {:weight :medium + :accessibility-label :community-name-text + :ellipsize-mode :tail + :number-of-lines 1} (utils/truncate-str name 30)]] :title-accessibility-label :community-name-text :subtitle [react/view [quo/text {:number-of-lines 1} description] - [quo/text {:number-of-lines 1 - :color :secondary} + [quo/text + {:number-of-lines 1 + :color :secondary} (if show-members-count? - (i18n/label-pluralize members-count :t/community-members {:count members-count}) + (i18n/label-pluralize members-count + :t/community-members + {:count members-count}) (i18n/label :t/open-membership))]] :on-press #(do (>evt [:dismiss-keyboard]) (>evt [:navigate-to :community {:community-id id}]))}])) -(defn communities-actions [] +(defn communities-actions + [] [:<> [quo/list-item {:theme :accent @@ -113,7 +129,8 @@ :icon :main-icons/add :on-press #(hide-sheet-and-dispatch [::communities/open-create-community])}]]) -(defn communities-list [communities] +(defn communities-list + [communities] [list/section-list {:content-container-style {:padding-vertical 8} :key-fn :id @@ -123,39 +140,47 @@ :render-section-header-fn quo/list-index :render-fn community-list-item}]) -(defn communities [] - (let [communities ( - [topbar/topbar (cond-> {:title (i18n/label :t/communities)} - communities-enabled? - (assoc :right-accessories [{:icon :main-icons/more - :accessibility-label :chat-menu-button - :on-press - #(>evt [:bottom-sheet/show-sheet - {:content (fn [] - [communities-actions]) - :height 256}])}]))] + [topbar/topbar + (cond-> {:title (i18n/label :t/communities)} + communities-enabled? + (assoc :right-accessories + [{:icon :main-icons/more + :accessibility-label :chat-menu-button + :on-press + #(>evt [:bottom-sheet/show-sheet + {:content (fn [] + [communities-actions]) + :height 256}])}]))] [communities-list communities] (when communities-enabled? [toolbar/toolbar {:show-border? true - :center [quo/button {:on-press #(>evt [::communities/open-create-community]) - :type :secondary} + :center [quo/button + {:on-press #(>evt [::communities/open-create-community]) + :type :secondary} (i18n/label :t/create-community)]}])])) -(defn export-community [] +(defn export-community + [] (let [{:keys [community-key]} ( [react/view {:flex 1} - [react/view {:padding-horizontal 16 - :padding-vertical 10} + [react/view + {:padding-horizontal 16 + :padding-vertical 10} [search-input/search-input-old {:search-active? search-active? :search-filter search-filter @@ -40,10 +43,11 @@ (re-frame/dispatch [:search/currency-filter-changed ""]))) :on-change (fn [text] (re-frame/dispatch [:search/currency-filter-changed text]))}]] - [list/flat-list {:data (->> currencies - vals - (sort #(compare (:code %1) (:code %2)))) - :key-fn :code - :render-data currency-id - :render-fn render-currency - :keyboardShouldPersistTaps :always}]]])) + [list/flat-list + {:data (->> currencies + vals + (sort #(compare (:code %1) (:code %2)))) + :key-fn :code + :render-data currency-id + :render-fn render-currency + :keyboardShouldPersistTaps :always}]]])) diff --git a/src/status_im/ui/screens/dapps_permissions/views.cljs b/src/status_im/ui/screens/dapps_permissions/views.cljs index d9d380806d..33777dd2ee 100644 --- a/src/status_im/ui/screens/dapps_permissions/views.cljs +++ b/src/status_im/ui/screens/dapps_permissions/views.cljs @@ -1,27 +1,31 @@ (ns status-im.ui.screens.dapps-permissions.views (:require-macros [status-im.utils.views :as views]) - (:require [re-frame.core :as re-frame] - [status-im.i18n.i18n :as i18n] - [status-im.constants :as constants] - [status-im.ui.components.react :as react] - [status-im.ui.components.list.views :as list] + (:require [quo.core :as quo] [quo.design-system.colors :as colors] - [status-im.ui.screens.dapps-permissions.styles :as styles] - [quo.core :as quo] + [re-frame.core :as re-frame] + [status-im.constants :as constants] + [status-im.i18n.i18n :as i18n] [status-im.ui.components.icons.icons :as icons] - [status-im.ui.components.topbar :as topbar])) + [status-im.ui.components.list.views :as list] + [status-im.ui.components.react :as react] + [status-im.ui.components.topbar :as topbar] + [status-im.ui.screens.dapps-permissions.styles :as styles])) -(defn d-icon [] +(defn d-icon + [] [react/view styles/icon-container [icons/icon :main-icons/dapp {:color colors/gray}]]) -(defn prepare-items [{:keys [dapp permissions]}] +(defn prepare-items + [{:keys [dapp permissions]}] {:title dapp :chevron true - :on-press #(re-frame/dispatch [:navigate-to :manage-dapps-permissions {:dapp dapp :permissions permissions}]) + :on-press #(re-frame/dispatch [:navigate-to :manage-dapps-permissions + {:dapp dapp :permissions permissions}]) :icon [d-icon]}) -(defn prepare-items-manage [name] +(defn prepare-items-manage + [name] (fn [permission] {:title (cond (= permission constants/dapp-permission-web3) @@ -31,24 +35,28 @@ :size :small :accessory [icons/icon :main-icons/check {}]})) -(views/defview dapps-permissions [] +(views/defview dapps-permissions + [] (views/letsubs [permissions [:dapps/permissions]] [list/flat-list {:data (vec (map prepare-items (vals permissions))) :key-fn (fn [_ i] (str i)) :render-fn quo/list-item}])) -(views/defview manage [] +(views/defview manage + [] (views/letsubs [{:keys [dapp permissions]} [:get-screen-params] - {:keys [name]} [:dapps-account]] + {:keys [name]} [:dapps-account]] [:<> [topbar/topbar {:title dapp}] [list/flat-list {:data (vec (map (prepare-items-manage name) permissions)) :key-fn (fn [_ i] (str i)) :render-fn quo/list-item}] - [react/view {:padding-vertical 16 - :padding-horizontal 16} - [quo/button {:theme :negative - :on-press #(re-frame/dispatch [:dapps/revoke-access dapp])} + [react/view + {:padding-vertical 16 + :padding-horizontal 16} + [quo/button + {:theme :negative + :on-press #(re-frame/dispatch [:dapps/revoke-access dapp])} (i18n/label :t/revoke-access)]]])) diff --git a/src/status_im/ui/screens/default_sync_period_settings/view.cljs b/src/status_im/ui/screens/default_sync_period_settings/view.cljs index b43308d1d4..75d9aad916 100644 --- a/src/status_im/ui/screens/default_sync_period_settings/view.cljs +++ b/src/status_im/ui/screens/default_sync_period_settings/view.cljs @@ -1,26 +1,29 @@ (ns status-im.ui.screens.default-sync-period-settings.view (:require-macros [status-im.utils.views :as views]) - (:require [status-im.ui.components.react :as react] - [status-im.i18n.i18n :as i18n] + (:require [quo.core :as quo] + [re-frame.core :as re-frame] [status-im.constants :as constants] - [status-im.utils.config :as config] - [quo.core :as quo] - [re-frame.core :as re-frame])) + [status-im.i18n.i18n :as i18n] + [status-im.ui.components.react :as react] + [status-im.utils.config :as config])) -(def titles {constants/two-mins (i18n/label :t/two-minutes) - constants/one-day (i18n/label :t/one-day) - constants/three-days (i18n/label :t/three-days) - constants/one-week (i18n/label :t/one-week) - constants/one-month (i18n/label :t/one-month)}) +(def titles + {constants/two-mins (i18n/label :t/two-minutes) + constants/one-day (i18n/label :t/one-day) + constants/three-days (i18n/label :t/three-days) + constants/one-week (i18n/label :t/one-week) + constants/one-month (i18n/label :t/one-month)}) -(defn radio-item [id value] +(defn radio-item + [id value] [quo/list-item {:active (= value id) :accessory :radio :title (get titles id) :on-press #(re-frame/dispatch [:multiaccounts.ui/default-sync-period-switched id])}]) -(views/defview default-sync-period-settings [] +(views/defview default-sync-period-settings + [] (views/letsubs [{:keys [default-sync-period] :or {default-sync-period constants/one-day}} [:multiaccount]] diff --git a/src/status_im/ui/screens/ens/views.cljs b/src/status_im/ui/screens/ens/views.cljs index cd635ad2a5..1e2eca5895 100644 --- a/src/status_im/ui/screens/ens/views.cljs +++ b/src/status_im/ui/screens/ens/views.cljs @@ -1,53 +1,56 @@ (ns status-im.ui.screens.ens.views - (:require [re-frame.core :as re-frame] + (:require [clojure.string :as string] + [quo.core :as quo] + [quo.design-system.colors :as colors] + [re-frame.core :as re-frame] [reagent.core :as reagent] [status-im.constants :as constants] [status-im.ens.core :as ens] [status-im.ethereum.core :as ethereum] [status-im.ethereum.ens :as ethereum.ens] [status-im.ethereum.stateofus :as stateofus] + [status-im.ethereum.tokens :as tokens] [status-im.i18n.i18n :as i18n] [status-im.multiaccounts.core :as multiaccounts] [status-im.react-native.resources :as resources] + [status-im.ui.components.chat-icon.screen :as chat-icon] [status-im.ui.components.checkbox.view :as checkbox] - [quo.design-system.colors :as colors] [status-im.ui.components.common.common :as components.common] [status-im.ui.components.icons.icons :as icons] [status-im.ui.components.react :as react] - [status-im.ui.components.topbar :as topbar] - [status-im.ui.screens.chat.utils :as chat.utils] [status-im.ui.components.toolbar :as toolbar] + [status-im.ui.components.topbar :as topbar] [status-im.ui.screens.chat.message.message :as message] [status-im.ui.screens.chat.photos :as photos] + [status-im.ui.screens.chat.utils :as chat.utils] [status-im.ui.screens.profile.components.views :as profile.components] - [utils.debounce :as debounce] - [clojure.string :as string] - [status-im.ethereum.tokens :as tokens] - [quo.core :as quo] - [status-im.ui.components.chat-icon.screen :as chat-icon] + [status-im.ui.screens.wallet.send.sheets :as sheets] [status-im.utils.utils :as utils] - [status-im.ui.screens.wallet.send.sheets :as sheets]) + [utils.debounce :as debounce]) (:require-macros [status-im.utils.views :as views])) (defn- link [{:keys [on-press]} label] - [react/touchable-opacity {:on-press on-press - :style {:justify-content :center}} + [react/touchable-opacity + {:on-press on-press + :style {:justify-content :center}} [react/text {:style {:color colors/blue}} label]]) (defn- section [{:keys [title content]}] - [react/view {:style {:margin-horizontal 16 - :align-items :flex-start}} + [react/view + {:style {:margin-horizontal 16 + :align-items :flex-start}} [react/text {:style {:color colors/gray :font-size 15}} title] - [react/view {:margin-top 8 - :padding-horizontal 16 - :padding-vertical 12 - :border-width 1 - :border-radius 12 - :border-color colors/gray-lighter} + [react/view + {:margin-top 8 + :padding-horizontal 16 + :padding-vertical 12 + :border-width 1 + :border-radius 12 + :border-color colors/gray-lighter} [quo/text {:monospace true} content]]]) @@ -65,14 +68,15 @@ (defn- big-blue-icon [state] - [react/view {:style {:margin-top 68 - :margin-bottom 24 - :width 60 - :height 60 - :border-radius 30 - :background-color colors/blue - :align-items :center - :justify-content :center}} + [react/view + {:style {:margin-top 68 + :margin-bottom 24 + :width 60 + :height 60 + :border-radius 30 + :background-color colors/blue + :align-items :center + :justify-content :center}} [icons/icon (case state (:available :connected :connected-with-different-key :owned) @@ -86,15 +90,17 @@ ;;; SEARCH SCREEN ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(defn- icon-wrapper [color icon] - [react/view {:style {:margin-right 16 - :margin-top 11 - :width 32 - :height 32 - :border-radius 25 - :align-items :center - :justify-content :center - :background-color color}} +(defn- icon-wrapper + [color icon] + [react/view + {:style {:margin-right 16 + :margin-top 11 + :width 32 + :height 32 + :border-radius 25 + :align-items :center + :justify-content :center + :background-color color}} icon]) (defn- input-icon @@ -115,18 +121,20 @@ (defn help-message-text-element ([label] - [react/text {:style {:flex 1 - :margin-top 16 - :margin-horizontal 16 - :font-size 14 - :text-align :center}} + [react/text + {:style {:flex 1 + :margin-top 16 + :margin-horizontal 16 + :font-size 14 + :text-align :center}} (i18n/label label)]) ([label second-label] - [react/nested-text {:style {:flex 1 - :margin-top 16 - :margin-horizontal 16 - :font-size 14 - :text-align :center}} + [react/nested-text + {:style {:flex 1 + :margin-top 16 + :margin-horizontal 16 + :font-size 14 + :text-align :center}} (i18n/label label) " " [{:style {:font-weight "700"}} (i18n/label second-label)]])) @@ -192,7 +200,8 @@ {:ref #(reset! input-ref %) :on-change-text #(do (re-frame/dispatch [:set-in [:ens/registration :state] :searching]) - (debounce/debounce-and-dispatch [::ens/set-username-candidate %] 600)) + (debounce/debounce-and-dispatch [::ens/set-username-candidate %] + 600)) :on-submit-editing #(re-frame/dispatch [::ens/input-submitted]) :auto-capitalize :none :auto-complete-type "off" @@ -207,42 +216,49 @@ :padding-left 48}}] [input-icon state]]))) -(views/defview search [] +(views/defview search + [] (views/letsubs [{:keys [state custom-domain? username]} [:ens/search-screen]] [react/keyboard-avoiding-view {:flex 1} - [react/scroll-view {:style {:flex 1} - ;;NOTE required so that switching custom-domain - ;;works on first tap and persists keyboard - ;;instead of dismissing keyboard and requiring two taps - :keyboardShouldPersistTaps :always} + [react/scroll-view + {:style {:flex 1} + ;;NOTE required so that switching custom-domain + ;;works on first tap and persists keyboard + ;;instead of dismissing keyboard and requiring two taps + :keyboardShouldPersistTaps :always} [react/view {:style {:flex 1}} - [react/view {:style {:flex 1 - :align-items :center - :justify-content :center}} + [react/view + {:style {:flex 1 + :align-items :center + :justify-content :center}} [big-blue-icon state] - [username-input username state (if custom-domain? - "vitalik94.domain.eth" - "vitalik94")] - [react/view {:style {:height 36 - :align-items :center - :justify-content :space-between - :padding-horizontal 12 - :margin-top 24 - :margin-horizontal 16 - :border-color colors/gray-lighter - :border-radius 20 - :border-width 1 - :flex-direction :row}} - [react/text {:style {:font-size 13 - :typography :main-medium}} + [username-input username state + (if custom-domain? + "vitalik94.domain.eth" + "vitalik94")] + [react/view + {:style {:height 36 + :align-items :center + :justify-content :space-between + :padding-horizontal 12 + :margin-top 24 + :margin-horizontal 16 + :border-color colors/gray-lighter + :border-radius 20 + :border-width 1 + :flex-direction :row}} + [react/text + {:style {:font-size 13 + :typography :main-medium}} (domain-label custom-domain?)] [react/view {:flex 1 :min-width 24}] [react/touchable-highlight {:on-press #(re-frame/dispatch [::ens/switch-domain-type])} - [react/text {:style {:color colors/blue - :font-size 12 - :typography :main-medium} - :number-of-lines 2} + [react/text + {:style {:color colors/blue + :font-size 12 + :typography :main-medium} + :number-of-lines 2} (domain-switch-label custom-domain?)]]]] [help-message state custom-domain?]]]])) @@ -250,16 +266,19 @@ ;;; CHECKOUT SCREEN ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(defn- agreement [checked contract] - [react/view {:flex-direction :row - :margin-left 26 ;; 10 for checkbox + 16 - :margin-right 16 - :margin-top 14 - :align-items :flex-start - :justify-content :center} - [checkbox/checkbox {:checked? @checked - :style {:padding 0} - :on-value-change #(reset! checked %)}] +(defn- agreement + [checked contract] + [react/view + {:flex-direction :row + :margin-left 26 ;; 10 for checkbox + 16 + :margin-right 16 + :margin-top 14 + :align-items :flex-start + :justify-content :center} + [checkbox/checkbox + {:checked? @checked + :style {:padding 0} + :on-value-change #(reset! checked %)}] [react/nested-text {:style {:margin-left 10}} (i18n/label :t/ens-agree-to) [{:style {:color colors/blue} @@ -268,7 +287,8 @@ "\n" (i18n/label :t/ens-understand)]]) -(defn render-account [address] +(defn render-account + [address] (let [account @(re-frame/subscribe [:account-by-address address])] [quo/list-item {:icon [chat-icon/custom-icon-view-list (:name account) (:color account)] @@ -276,7 +296,8 @@ :subtitle (utils/get-shortened-checksum-address (:address account)) :chevron true :on-press #(re-frame/dispatch [:bottom-sheet/show-sheet - {:content (fn [] [sheets/accounts-list :from ::ens/change-address])}])}])) + {:content (fn [] [sheets/accounts-list :from + ::ens/change-address])}])}])) (defn- registration [checked contract address public-key] @@ -285,38 +306,45 @@ (i18n/label :t/wallet)] [render-account address] [react/view {:style {:margin-top 14}} - [section {:title (i18n/label :t/key) - :content public-key}]] + [section + {:title (i18n/label :t/key) + :content public-key}]] [agreement checked contract]]) -(defn checkout [] - (let [checked? (reagent/atom false)] +(defn checkout + [] + (let [checked? (reagent/atom false)] (fn [] (let [{:keys [username address custom-domain? public-key chain amount-label sufficient-funds?]} @(re-frame/subscribe [:ens/checkout-screen])] [react/keyboard-avoiding-view {:flex 1} [react/scroll-view {:style {:flex 1}} - [react/view {:style {:flex 1 - :align-items :center - :justify-content :center}} + [react/view + {:style {:flex 1 + :align-items :center + :justify-content :center}} [big-blue-icon nil] - [react/text {:text-align :center - :style {:flex 1 - :font-size 22 - :padding-horizontal 48}} + [react/text + {:text-align :center + :style {:flex 1 + :font-size 22 + :padding-horizontal 48}} username] - [react/view {:style {:height 36 - :align-items :center - :justify-content :space-between - :padding-horizontal 12 - :margin-top 24 - :margin-horizontal 16 - :border-color colors/gray-lighter :border-radius 20 - :border-width 1 - :flex-direction :row}} - [react/text {:style {:font-size 13 - :typography :main-medium}} + [react/view + {:style {:height 36 + :align-items :center + :justify-content :space-between + :padding-horizontal 12 + :margin-top 24 + :margin-horizontal 16 + :border-color colors/gray-lighter + :border-radius 20 + :border-width 1 + :flex-direction :row}} + [react/text + {:style {:font-size 13 + :typography :main-medium}} (domain-label custom-domain?)] [react/view {:flex 1 :min-width 24}]]] [registration checked? (stateofus/get-cached-registrar chain) address public-key]] @@ -324,8 +352,9 @@ {:show-border? true :size :large :left [react/view {:flex-direction :row :align-items :center} - [react/image {:source tokens/snt-icon-source - :style {:width 36 :height 36}}] + [react/image + {:source tokens/snt-icon-source + :style {:width 36 :height 36}}] [react/view {:flex-direction :column :margin 8} [react/text {:style {:font-size 15}} amount-label] @@ -334,7 +363,8 @@ :right [react/view {:padding-horizontal 8} [quo/button {:disabled (or (not @checked?) (not sufficient-funds?)) - :on-press #(debounce/dispatch-and-chill [::ens/register-name-pressed address] 2000)} + :on-press #(debounce/dispatch-and-chill [::ens/register-name-pressed address] + 2000)} (if sufficient-funds? (i18n/label :t/ens-register) (i18n/label :t/not-enough-snt))]]}]])))) @@ -347,11 +377,21 @@ [state] (case state :registration-failed - [react/view {:style {:width 40 :height 40 :border-radius 30 :background-color colors/red-light - :align-items :center :justify-content :center}} + [react/view + {:style {:width 40 + :height 40 + :border-radius 30 + :background-color colors/red-light + :align-items :center + :justify-content :center}} [icons/icon :main-icons/warning {:color colors/red}]] - [react/view {:style {:width 40 :height 40 :border-radius 30 :background-color colors/gray-lighter - :align-items :center :justify-content :center}} + [react/view + {:style {:width 40 + :height 40 + :border-radius 30 + :background-color colors/gray-lighter + :align-items :center + :justify-content :center}} [icons/icon :main-icons/check {:color colors/blue}]])) (defn- final-state-label @@ -391,29 +431,33 @@ (i18n/label :t/ens-registration-failed)] nil)) -(views/defview confirmation [] +(views/defview confirmation + [] (views/letsubs [{:keys [state username]} [:ens/confirmation-screen]] [react/keyboard-avoiding-view {:flex 1} - [react/view {:style {:flex 1 - :align-items :center - :justify-content :center}} + [react/view + {:style {:flex 1 + :align-items :center + :justify-content :center}} [finalized-icon state] - [react/text {:style {:typography :header - :margin-top 32 - :margin-horizontal 32 - :text-align :center}} + [react/text + {:style {:typography :header + :margin-top 32 + :margin-horizontal 32 + :text-align :center}} (final-state-label state username)] - [react/view {:align-items :center - :margin-horizontal 32 - :margin-top 12 - :margin-bottom 20 - :justify-content :center} + [react/view + {:align-items :center + :margin-horizontal 32 + :margin-top 12 + :margin-bottom 20 + :justify-content :center} [final-state-details state username]] (if (= state :registration-failed) [react/view [quo/button {:on-press #(re-frame/dispatch [::ens/retry-pressed])} (i18n/label :t/retry)] - [quo/button {:on-press #(re-frame/dispatch [::ens/cancel-pressed])} + [quo/button {:on-press #(re-frame/dispatch [::ens/cancel-pressed])} (i18n/label :t/cancel)]] [quo/button {:on-press #(re-frame/dispatch [::ens/got-it-pressed])} (i18n/label :t/ens-got-it)])]])) @@ -422,20 +466,27 @@ ;;; TERMS SCREEN ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(defn- term-point [content] +(defn- term-point + [content] [react/view {:style {:flex 1 :margin-top 24 :margin-horizontal 16 :flex-direction :row}} [react/view {:style {:width 16 :margin-top 8}} [react/view {:style {:background-color colors/gray :width 4 :height 4 :border-radius 25}}]] [react/text {:style {:flex 1 :font-size 15}} content]]) -(defn- etherscan-url [address] +(defn- etherscan-url + [address] (str "https://etherscan.io/address/" address)) -(views/defview terms [] +(views/defview terms + [] (views/letsubs [{:keys [contract]} [:get-screen-params :ens-terms]] [react/scroll-view {:style {:flex 1}} - [react/view {:style {:height 136 :background-color colors/gray-lighter :justify-content :center :align-items :center}} + [react/view + {:style {:height 136 + :background-color colors/gray-lighter + :justify-content :center + :align-items :center}} [react/text {:style {:text-align :center :typography :header :letter-spacing -0.275}} (i18n/label :t/ens-terms-header)]] [react/view @@ -464,7 +515,8 @@ [term-point (i18n/label :t/ens-terms-point-10)] [react/view {:style {:align-items :center :margin-top 16 :margin-bottom 8}} - [link {:on-press #(.openURL ^js react/linking (etherscan-url (:mainnet ethereum.ens/ens-registries)))} + [link + {:on-press #(.openURL ^js react/linking (etherscan-url (:mainnet ethereum.ens/ens-registries)))} (i18n/label :t/etherscan-lookup)]]]])) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -473,10 +525,12 @@ (def release-instructions-link "https://our.status.im/managing-your-ens-name-in-v1/") -(defn open-release-instructions-link! [] +(defn open-release-instructions-link! + [] (.openURL ^js react/linking release-instructions-link)) -(views/defview name-details [] +(views/defview name-details + [] (views/letsubs [{:keys [name address custom-domain? public-key expiration-date releasable? pending?]} [:ens.name/screen]] @@ -496,68 +550,85 @@ (str (i18n/label :t/ens-10-SNT) ", deposit unlocked"))]]]) [react/view {:style {:margin-top 22}} (when-not pending? - [section {:title (i18n/label :t/wallet-address) - :content (ethereum/normalized-hex address)}]) + [section + {:title (i18n/label :t/wallet-address) + :content (ethereum/normalized-hex address)}]) (when-not pending? [react/view {:style {:margin-top 14}} - [section {:title (i18n/label :t/key) - :content public-key}]]) + [section + {:title (i18n/label :t/key) + :content public-key}]]) [react/view {:style {:margin-top 16 :margin-bottom 32}} ;;TODO this is temporary fix for accounts with failed txs - ;;we still need this for regular ens names (not pending) but we need to detach public key in the contract + ;;we still need this for regular ens names (not pending) but we need to detach public key in the + ;;contract (when pending? - [quo/list-item {:title (i18n/label :t/ens-remove-username) - ;:subtext (i18n/label :t/ens-remove-hints) - :icon :main-icons/close - :theme :negative - :on-press #(re-frame/dispatch [::ens/remove-username name])}]) + [quo/list-item + {:title (i18n/label :t/ens-remove-username) + ;:subtext (i18n/label :t/ens-remove-hints) + :icon :main-icons/close + :theme :negative + :on-press #(re-frame/dispatch [::ens/remove-username name])}]) (when (and (not custom-domain?) (not pending?)) [react/view {:style {:margin-top 18}} - [quo/list-item {:title (i18n/label :t/ens-release-username) - :theme :accent - :disabled (not releasable?) - :subtitle (when (and expiration-date - (not releasable?)) - (i18n/label :t/ens-locked - {:date expiration-date})) - :icon :main-icons/delete - :on-press #(open-release-instructions-link!)}]])]]]])) + [quo/list-item + {:title (i18n/label :t/ens-release-username) + :theme :accent + :disabled (not releasable?) + :subtitle (when (and expiration-date + (not releasable?)) + (i18n/label :t/ens-locked + {:date expiration-date})) + :icon :main-icons/delete + :on-press #(open-release-instructions-link!)}]])]]]])) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; WELCOME SCREEN ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(defn- welcome-item [{:keys [icon-label title]} content] - [react/view {:style {:flex 1 - :margin-top 24 - :margin-left 16 - :flex-direction :row}} - [react/view {:style {:height 40 - :width 40 - :border-radius 25 - :border-width 1 - :border-color colors/gray-lighter - :align-items :center - :justify-content :center}} +(defn- welcome-item + [{:keys [icon-label title]} content] + [react/view + {:style {:flex 1 + :margin-top 24 + :margin-left 16 + :flex-direction :row}} + [react/view + {:style {:height 40 + :width 40 + :border-radius 25 + :border-width 1 + :border-color colors/gray-lighter + :align-items :center + :justify-content :center}} [react/text {:style {:typography :title}} icon-label]] - [react/view {:style {:flex 1 - :margin-horizontal 16}} - [react/text {:style {:font-size 15 - :typography :main-semibold}} + [react/view + {:style {:flex 1 + :margin-horizontal 16}} + [react/text + {:style {:font-size 15 + :typography :main-semibold}} title] content]]) -(defn- welcome [] +(defn- welcome + [] (let [name (:name @(re-frame/subscribe [:multiaccount]))] [react/view {:style {:flex 1}} [react/scroll-view {:content-container-style {:align-items :center}} - [react/image {:source (resources/get-theme-image :ens-header) - :style {:margin-top 32}}] + [react/image + {:source (resources/get-theme-image :ens-header) + :style {:margin-top 32}}] [react/text {:style {:margin-top 32 :margin-bottom 8 :typography :header}} (i18n/label :t/ens-get-name)] - [react/text {:style {:margin-top 8 :margin-bottom 24 :color colors/gray :font-size 15 :margin-horizontal 16 - :text-align :center}} + [react/text + {:style {:margin-top 8 + :margin-bottom 24 + :color colors/gray + :font-size 15 + :margin-horizontal 16 + :text-align :center}} (i18n/label :t/ens-welcome-hints)] [welcome-item {:icon-label "1" :title (i18n/label :t/ens-welcome-point-customize-title)} [react/view {:flex-direction :row} @@ -576,16 +647,20 @@ [welcome-item {:icon-label "@" :title (i18n/label :t/ens-welcome-point-verify-title)} [react/text {:style {:color colors/gray}} (i18n/label :t/ens-welcome-point-verify)]] - [react/text {:style {:margin-top 16 :text-align :center :color colors/gray :typography :caption :padding-bottom 96}} + [react/text + {:style + {:margin-top 16 :text-align :center :color colors/gray :typography :caption :padding-bottom 96}} (i18n/label :t/ens-powered-by)]] [toolbar/toolbar {:show-border? true - :right [quo/button {:on-press #(re-frame/dispatch [::ens/get-started-pressed]) - :type :secondary - :after :main-icons/next} + :right [quo/button + {:on-press #(re-frame/dispatch [::ens/get-started-pressed]) + :type :secondary + :after :main-icons/next} (i18n/label :t/get-started)]}]])) -(defn- name-item [{:keys [name action subtitle]}] +(defn- name-item + [{:keys [name action subtitle]}] (let [stateofus-username (stateofus/username name) s (or stateofus-username name)] [quo/list-item @@ -597,7 +672,8 @@ (when action {:on-press action}))])) -(defn- name-list [names preferred-name] +(defn- name-list + [names preferred-name] [react/view {:style {:flex 1 :margin-top 16}} [react/view {:style {:margin-horizontal 16 :align-items :center :justify-content :center}} [react/nested-text @@ -623,20 +699,23 @@ :accessory :radio :active (= name preferred-name)}]))]]) -(views/defview in-progress-registrations [registrations] +(views/defview in-progress-registrations + [registrations] [react/view {:style {:margin-top 8}} (for [[hash {:keys [state username]}] registrations :when (or (= state :submitted) (= state :failure))] ^{:key hash} - [name-item {:name username - :action (when-not (= state :submitted) - #(re-frame/dispatch [:clear-ens-registration hash])) - :subtitle (case state - :submitted (i18n/label :t/ens-registration-in-progress) - :failure (i18n/label :t/ens-registration-failure) - nil)}])]) + [name-item + {:name username + :action (when-not (= state :submitted) + #(re-frame/dispatch [:clear-ens-registration hash])) + :subtitle (case state + :submitted (i18n/label :t/ens-registration-in-progress) + :failure (i18n/label :t/ens-registration-failure) + nil)}])]) -(views/defview my-name [] +(views/defview my-name + [] (views/letsubs [contact-name [:multiaccount/preferred-name]] (when-not (string/blank? contact-name) (chat.utils/format-author-old {:names {:ens-name @@ -644,7 +723,8 @@ (or (stateofus/username contact-name) contact-name))}})))) -(views/defview registered [names {:keys [preferred-name] :as account} _ registrations] +(views/defview registered + [names {:keys [preferred-name] :as account} _ registrations] [react/view {:style {:flex 1}} [react/scroll-view [react/view {:style {:margin-top 8}} @@ -662,10 +742,13 @@ [react/view {:style {:margin-top 8}} (for [name names] ^{:key name} - [name-item {:name name - :action #(re-frame/dispatch [::ens/navigate-to-name name])}])] - [react/text {:style {:color colors/gray :font-size 15 - :margin-horizontal 16}} + [name-item + {:name name + :action #(re-frame/dispatch [::ens/navigate-to-name name])}])] + [react/text + {:style {:color colors/gray + :font-size 15 + :margin-horizontal 16}} (i18n/label :t/ens-no-usernames)])] [react/view {:style {:padding-vertical 22 :border-color colors/gray-lighter :border-top-width 1}} (when (> (count names) 1) @@ -691,7 +774,8 @@ [photos/photo (multiaccounts/displayed-photo account) {:size 36}]] [message/->message message {:on-long-press identity}]]])]]) -(views/defview main [] +(views/defview main + [] (views/letsubs [{:keys [names multiaccount show? registrations]} [:ens.main/screen]] [react/keyboard-avoiding-view {:style {:flex 1}} (if (or (seq names) registrations) diff --git a/src/status_im/ui/screens/fleet_settings/styles.cljs b/src/status_im/ui/screens/fleet_settings/styles.cljs index 4f6d169b04..e445e5e5ff 100644 --- a/src/status_im/ui/screens/fleet_settings/styles.cljs +++ b/src/status_im/ui/screens/fleet_settings/styles.cljs @@ -3,7 +3,7 @@ [status-im.utils.styles :as styles])) (def wrapper - {:flex 1}) + {:flex 1}) (def fleet-item-inner {:padding-horizontal 16}) @@ -18,7 +18,8 @@ (def fleet-item-name-text {:font-size 17}) -(defn fleet-icon-container [current?] +(defn fleet-icon-container + [current?] {:width 40 :height 40 :border-radius 20 @@ -28,6 +29,7 @@ :align-items :center :justify-content :center}) -(defn fleet-icon [current?] +(defn fleet-icon + [current?] (hash-map :color (if current? colors/white-persist colors/gray))) diff --git a/src/status_im/ui/screens/fleet_settings/views.cljs b/src/status_im/ui/screens/fleet_settings/views.cljs index e3d23ad375..d21513ea96 100644 --- a/src/status_im/ui/screens/fleet_settings/views.cljs +++ b/src/status_im/ui/screens/fleet_settings/views.cljs @@ -7,18 +7,21 @@ [status-im.ui.screens.fleet-settings.styles :as styles]) (:require-macros [status-im.utils.views :as views])) -(defn- fleet-icon [current?] +(defn- fleet-icon + [current?] [react/view (styles/fleet-icon-container current?) [icons/icon :main-icons/mailserver (styles/fleet-icon current?)]]) -(defn change-fleet [fleet] +(defn change-fleet + [fleet] (re-frame/dispatch [:fleet.ui/fleet-selected fleet])) -(defn render-row [fleet _ _ current-fleet] +(defn render-row + [fleet _ _ current-fleet] (let [current? (= fleet current-fleet)] [react/touchable-highlight - {:on-press #(change-fleet fleet) + {:on-press #(change-fleet fleet) :accessibility-label :fleet-item} [react/view styles/fleet-item [fleet-icon current?] @@ -26,14 +29,17 @@ [react/text {:style styles/fleet-item-name-text} fleet]]]])) -(defn fleets [custom-fleets] +(defn fleets + [custom-fleets] (map name (keys (node/fleets {:custom-fleets custom-fleets})))) -(views/defview fleet-settings [] +(views/defview fleet-settings + [] (views/letsubs [custom-fleets [:fleets/custom-fleets] current-fleet [:fleets/current-fleet]] - [list/flat-list {:data (fleets custom-fleets) - :default-separator? false - :key-fn identity - :render-data (name current-fleet) - :render-fn render-row}])) + [list/flat-list + {:data (fleets custom-fleets) + :default-separator? false + :key-fn identity + :render-data (name current-fleet) + :render-fn render-row}])) diff --git a/src/status_im/ui/screens/glossary/view.cljs b/src/status_im/ui/screens/glossary/view.cljs index e83cf04273..9fdc054b38 100644 --- a/src/status_im/ui/screens/glossary/view.cljs +++ b/src/status_im/ui/screens/glossary/view.cljs @@ -1,45 +1,53 @@ (ns status-im.ui.screens.glossary.view - (:require [status-im.ui.components.react :as react] + (:require [quo.design-system.colors :as colors] + [status-im.i18n.i18n :as i18n] [status-im.ui.components.list.views :as list] - [quo.design-system.colors :as colors] - [status-im.i18n.i18n :as i18n])) + [status-im.ui.components.react :as react])) -(def messages [{:title :t/account-title - :content :t/account-content} - {:title :t/wallet-key-title - :content :t/wallet-key-content} - {:title :t/chat-key-title - :content :t/chat-key-content} - {:title :t/chat-name-title - :content :t/chat-name-content} - {:title :t/ens-name-title - :content :t/ens-name-content} - {:title :t/mailserver-title - :content :t/mailserver-content} - {:title :t/peer-title - :content :t/peer-content} - {:title :t/seed-phrase-title - :content :t/seed-phrase-content}]) +(def messages + [{:title :t/account-title + :content :t/account-content} + {:title :t/wallet-key-title + :content :t/wallet-key-content} + {:title :t/chat-key-title + :content :t/chat-key-content} + {:title :t/chat-name-title + :content :t/chat-name-content} + {:title :t/ens-name-title + :content :t/ens-name-content} + {:title :t/mailserver-title + :content :t/mailserver-content} + {:title :t/peer-title + :content :t/peer-content} + {:title :t/seed-phrase-title + :content :t/seed-phrase-content}]) -(defn render-section-header [{:keys [title]}] - [react/view {:style {:position "absolute" - :width 24 - :padding-vertical 16 - :background-color colors/white}} - [react/text {:style {:color colors/blue - :font-weight "700"}} +(defn render-section-header + [{:keys [title]}] + [react/view + {:style {:position "absolute" + :width 24 + :padding-vertical 16 + :background-color colors/white}} + [react/text + {:style {:color colors/blue + :font-weight "700"}} title]]) -(defn render-element [{:keys [title content]}] - [react/view {:style {:margin-left 24 - :margin-top 16}} - [react/text {:style {:font-weight "700" - :margin-bottom 6}} +(defn render-element + [{:keys [title content]}] + [react/view + {:style {:margin-left 24 + :margin-top 16}} + [react/text + {:style {:font-weight "700" + :margin-bottom 6}} (i18n/label title)] [react/text (i18n/label content)]]) -(defn glossary [] +(defn glossary + [] (let [sections (->> messages (group-by (comp first i18n/label :title)) seq diff --git a/src/status_im/ui/screens/group/styles.cljs b/src/status_im/ui/screens/group/styles.cljs index f914e2d97a..988a2d0f34 100644 --- a/src/status_im/ui/screens/group/styles.cljs +++ b/src/status_im/ui/screens/group/styles.cljs @@ -5,7 +5,8 @@ {:flex 1 :flex-direction :column}) -(defn no-contact-text [] +(defn no-contact-text + [] {:margin-bottom 20 :margin-horizontal 50 :text-align :center @@ -14,21 +15,24 @@ (def toolbar-header-container {:align-items :center}) -(defn toolbar-sub-header [] +(defn toolbar-sub-header + [] {:color colors/gray}) (def no-contacts - {:flex 1 + {:flex 1 :justify-content :center - :align-items :center}) + :align-items :center}) -(defn search-container [] +(defn search-container + [] {:border-bottom-color colors/gray-lighter :border-bottom-width 1 :padding-horizontal 16 :padding-vertical 10}) -(defn members-title [] +(defn members-title + [] {:color colors/gray :margin-top 14 :margin-bottom 4}) diff --git a/src/status_im/ui/screens/group/views.cljs b/src/status_im/ui/screens/group/views.cljs index ed74e726e3..0e61906500 100644 --- a/src/status_im/ui/screens/group/views.cljs +++ b/src/status_im/ui/screens/group/views.cljs @@ -1,26 +1,25 @@ (ns status-im.ui.screens.group.views (:require [clojure.string :as string] + [quo.core :as quo] [re-frame.core :as re-frame] [reagent.core :as reagent] [status-im.constants :as constants] [status-im.i18n.i18n :as i18n] - [status-im.ui.components.chat-icon.screen :as chat-icon] [status-im.multiaccounts.core :as multiaccounts] - [status-im.ui.components.keyboard-avoid-presentation - :as - kb-presentation] + [status-im.ui.components.chat-icon.screen :as chat-icon] [status-im.ui.components.invite.views :as invite] + [status-im.ui.components.keyboard-avoid-presentation :as kb-presentation] [status-im.ui.components.list.views :as list] [status-im.ui.components.react :as react] [status-im.ui.components.search-input.view :as search] [status-im.ui.components.toolbar :as toolbar] [status-im.ui.components.topbar :as topbar] [status-im.ui.screens.group.styles :as styles] - [quo.core :as quo] [utils.debounce :as debounce]) (:require-macros [status-im.utils.views :as views])) -(defn- render-contact [row] +(defn- render-contact + [row] (let [[first-name second-name] (multiaccounts/contact-two-names row false)] [quo/list-item {:title first-name @@ -28,7 +27,8 @@ :icon [chat-icon/contact-icon-contacts-tab (multiaccounts/displayed-photo row)]}])) -(defn- on-toggle [allow-new-users? checked? public-key] +(defn- on-toggle + [allow-new-users? checked? public-key] (cond checked? @@ -39,77 +39,90 @@ allow-new-users?) (re-frame/dispatch [:select-contact public-key allow-new-users?]))) -(defn- on-toggle-participant [allow-new-users? checked? public-key] +(defn- on-toggle-participant + [allow-new-users? checked? public-key] (cond checked? (re-frame/dispatch [:deselect-participant public-key allow-new-users?]) - ;; Only allow new users if not reached the maximum + ;; Only allow new users if not reached the maximum (and (not checked?) allow-new-users?) (re-frame/dispatch [:select-participant public-key allow-new-users?]))) -(defn- toggle-item [] +(defn- toggle-item + [] (fn [allow-new-users? subs-name {:keys [public-key] :as contact} on-toggle] - (let [contact-selected? @(re-frame/subscribe [subs-name public-key]) + (let [contact-selected? @(re-frame/subscribe [subs-name public-key]) [first-name second-name] (multiaccounts/contact-two-names contact true)] [quo/list-item {:title first-name :subtitle second-name :icon [chat-icon/contact-icon-contacts-tab - (multiaccounts/displayed-photo contact)] + (multiaccounts/displayed-photo contact)] :on-press #(on-toggle allow-new-users? contact-selected? public-key) :active contact-selected? :accessory :checkbox}]))) -(defn- group-toggle-contact [contact _ _ allow-new-users?] +(defn- group-toggle-contact + [contact _ _ allow-new-users?] [toggle-item allow-new-users? :is-contact-selected? contact on-toggle]) -(defn- group-toggle-participant [contact _ _ allow-new-users?] +(defn- group-toggle-participant + [contact _ _ allow-new-users?] [toggle-item allow-new-users? :is-participant-selected? contact on-toggle-participant]) -(defn toggle-list [{:keys [contacts render-fn render-data]}] - [list/flat-list {:data contacts - :key-fn :public-key - :render-data render-data - :render-fn render-fn - :keyboardShouldPersistTaps :always}]) +(defn toggle-list + [{:keys [contacts render-fn render-data]}] + [list/flat-list + {:data contacts + :key-fn :public-key + :render-data render-data + :render-fn render-fn + :keyboardShouldPersistTaps :always}]) -(defn no-contacts [{:keys [no-contacts]}] +(defn no-contacts + [{:keys [no-contacts]}] [react/view {:style styles/no-contacts} [react/text {:style (styles/no-contact-text)} no-contacts] [invite/button]]) -(defn filter-contacts [filter-text contacts] +(defn filter-contacts + [filter-text contacts] (let [lower-filter-text (string/lower-case (str filter-text)) filter-fn (fn [{:keys [name alias nickname]}] (or (string/includes? (string/lower-case (str name)) lower-filter-text) (string/includes? (string/lower-case (str alias)) lower-filter-text) (when nickname - (string/includes? (string/lower-case (str nickname)) lower-filter-text))))] + (string/includes? (string/lower-case (str nickname)) + lower-filter-text))))] (if filter-text (filter filter-fn contacts) contacts))) ;; Set name of new group-chat -(views/defview new-group [] +(views/defview new-group + [] (views/letsubs [contacts [:selected-group-contacts] group-name [:new-chat-name]] (let [group-name-empty? (not (and (string? group-name) (not-empty group-name)))] - [react/keyboard-avoiding-view {:style styles/group-container - :ignore-offset true} + [react/keyboard-avoiding-view + {:style styles/group-container + :ignore-offset true} [react/view {:flex 1} - [topbar/topbar {:use-insets false - :title (i18n/label :t/new-group-chat) - :subtitle (i18n/label :t/group-chat-members-count - {:selected (inc (count contacts)) - :max constants/max-group-chat-participants})}] - [react/view {:style {:padding-top 16 - :flex 1}} + [topbar/topbar + {:use-insets false + :title (i18n/label :t/new-group-chat) + :subtitle (i18n/label :t/group-chat-members-count + {:selected (inc (count contacts)) + :max constants/max-group-chat-participants})}] + [react/view + {:style {:padding-top 16 + :flex 1}} [react/view {:style {:padding-horizontal 16}} [quo/text-input {:auto-focus true @@ -119,57 +132,69 @@ :accessibility-label :chat-name-input}] [react/text {:style (styles/members-title)} (i18n/label :t/members-title)]] - [react/view {:style {:margin-top 8 - :flex 1}} - [list/flat-list {:data contacts - :key-fn :address - :render-fn render-contact - :bounces false - :keyboard-should-persist-taps :always - :enable-empty-sections true}]]] + [react/view + {:style {:margin-top 8 + :flex 1}} + [list/flat-list + {:data contacts + :key-fn :address + :render-fn render-contact + :bounces false + :keyboard-should-persist-taps :always + :enable-empty-sections true}]]] [toolbar/toolbar {:show-border? true :left - [quo/button {:type :secondary - :before :main-icon/back - :accessibility-label :previous-button - :on-press #(re-frame/dispatch [:navigate-back])} + [quo/button + {:type :secondary + :before :main-icon/back + :accessibility-label :previous-button + :on-press #(re-frame/dispatch [:navigate-back])} (i18n/label :t/back)] :right - [quo/button {:type :secondary - :accessibility-label :create-group-chat-button - :disabled group-name-empty? - :on-press #(debounce/dispatch-and-chill [:group-chats.ui/create-pressed group-name] - 300)} + [quo/button + {:type :secondary + :accessibility-label :create-group-chat-button + :disabled group-name-empty? + :on-press #(debounce/dispatch-and-chill [:group-chats.ui/create-pressed + group-name] + 300)} (i18n/label :t/create-group-chat)]}]]]))) -(defn searchable-contact-list [] +(defn searchable-contact-list + [] (let [search-value (reagent/atom nil)] (fn [{:keys [contacts no-contacts-label toggle-fn allow-new-users?]}] [react/view {:style {:flex 1}} [react/view {:style (styles/search-container)} - [search/search-input-old {:on-cancel #(reset! search-value nil) - :on-change #(reset! search-value %)}]] - [react/view {:style {:flex 1 - :padding-vertical 8}} + [search/search-input-old + {:on-cancel #(reset! search-value nil) + :on-change #(reset! search-value %)}]] + [react/view + {:style {:flex 1 + :padding-vertical 8}} (if (seq contacts) - [toggle-list {:contacts (filter-contacts @search-value contacts) - :render-data allow-new-users? - :render-fn toggle-fn}] + [toggle-list + {:contacts (filter-contacts @search-value contacts) + :render-data allow-new-users? + :render-fn toggle-fn}] [no-contacts {:no-contacts no-contacts-label}])]]))) ;; Start group chat -(views/defview contact-toggle-list [] +(views/defview contact-toggle-list + [] (views/letsubs [contacts [:contacts/active] selected-contacts-count [:selected-contacts-count]] - [react/keyboard-avoiding-view {:style styles/group-container - :ignore-offset true} - [topbar/topbar {:use-insets false - :border-bottom false - :title (i18n/label :t/new-group-chat) - :subtitle (i18n/label :t/group-chat-members-count - {:selected (inc selected-contacts-count) - :max constants/max-group-chat-participants})}] + [react/keyboard-avoiding-view + {:style styles/group-container + :ignore-offset true} + [topbar/topbar + {:use-insets false + :border-bottom false + :title (i18n/label :t/new-group-chat) + :subtitle (i18n/label :t/group-chat-members-count + {:selected (inc selected-contacts-count) + :max constants/max-group-chat-participants})}] [searchable-contact-list {:contacts contacts :no-contacts-label (i18n/label :t/group-chat-no-contacts) @@ -179,25 +204,28 @@ [toolbar/toolbar {:show-border? true :right - [quo/button {:type :secondary - :after :main-icon/next - :accessibility-label :next-button - :on-press #(re-frame/dispatch [:navigate-to :new-group])} + [quo/button + {:type :secondary + :after :main-icon/next + :accessibility-label :next-button + :on-press #(re-frame/dispatch [:navigate-to :new-group])} (i18n/label :t/next)]}]])) ;; Add participants to existing group chat -(views/defview add-participants-toggle-list [] - (views/letsubs [contacts [:contacts/all-contacts-not-in-current-chat] - current-chat [:chats/current-chat] +(views/defview add-participants-toggle-list + [] + (views/letsubs [contacts [:contacts/all-contacts-not-in-current-chat] + current-chat [:chats/current-chat] selected-contacts-count [:selected-participants-count]] (let [current-participants-count (count (:contacts current-chat))] - [kb-presentation/keyboard-avoiding-view {:style styles/group-container} - [topbar/topbar {:use-insets false - :border-bottom false - :title (i18n/label :t/add-members) - :subtitle (i18n/label :t/group-chat-members-count - {:selected (+ current-participants-count selected-contacts-count) - :max constants/max-group-chat-participants})}] + [kb-presentation/keyboard-avoiding-view {:style styles/group-container} + [topbar/topbar + {:use-insets false + :border-bottom false + :title (i18n/label :t/add-members) + :subtitle (i18n/label :t/group-chat-members-count + {:selected (+ current-participants-count selected-contacts-count) + :max constants/max-group-chat-participants})}] [searchable-contact-list {:contacts contacts :no-contacts-label (i18n/label :t/group-chat-all-contacts-invited) @@ -208,23 +236,27 @@ [toolbar/toolbar {:show-border? true :center - [quo/button {:type :secondary - :accessibility-label :next-button - :disabled (zero? selected-contacts-count) - :on-press #(re-frame/dispatch [:group-chats.ui/add-members-pressed])} + [quo/button + {:type :secondary + :accessibility-label :next-button + :disabled (zero? selected-contacts-count) + :on-press #(re-frame/dispatch [:group-chats.ui/add-members-pressed])} (i18n/label :t/add)]}]]))) -(views/defview edit-group-chat-name [] +(views/defview edit-group-chat-name + [] (views/letsubs [{:keys [name chat-id]} [:chats/current-chat] - new-group-chat-name (reagent/atom nil)] - [kb-presentation/keyboard-avoiding-view {:style styles/group-container} - [react/scroll-view {:style {:padding 16 - :flex 1}} + new-group-chat-name (reagent/atom nil)] + [kb-presentation/keyboard-avoiding-view {:style styles/group-container} + [react/scroll-view + {:style {:padding 16 + :flex 1}} [quo/text-input {:on-change-text #(reset! new-group-chat-name %) :default-value name :on-submit-editing #(when (seq @new-group-chat-name) - (re-frame/dispatch [:group-chats.ui/name-changed chat-id @new-group-chat-name])) + (re-frame/dispatch [:group-chats.ui/name-changed chat-id + @new-group-chat-name])) :placeholder (i18n/label :t/enter-contact-code) :accessibility-label :new-chat-name :return-key-type :go}]] @@ -232,14 +264,16 @@ [toolbar/toolbar {:show-border? true :center - [quo/button {:type :secondary - :accessibility-label :done - :disabled (and (<= (count @new-group-chat-name) 1) - (not (nil? @new-group-chat-name))) - :on-press #(cond - (< 1 (count @new-group-chat-name)) - (re-frame/dispatch [:group-chats.ui/name-changed chat-id @new-group-chat-name]) + [quo/button + {:type :secondary + :accessibility-label :done + :disabled (and (<= (count @new-group-chat-name) 1) + (not (nil? @new-group-chat-name))) + :on-press #(cond + (< 1 (count @new-group-chat-name)) + (re-frame/dispatch [:group-chats.ui/name-changed chat-id + @new-group-chat-name]) - (nil? @new-group-chat-name) - (re-frame/dispatch [:navigate-back]))} + (nil? @new-group-chat-name) + (re-frame/dispatch [:navigate-back]))} (i18n/label :t/done)]}]])) diff --git a/src/status_im/ui/screens/help_center/views.cljs b/src/status_im/ui/screens/help_center/views.cljs index 81621707cf..454d409b32 100644 --- a/src/status_im/ui/screens/help_center/views.cljs +++ b/src/status_im/ui/screens/help_center/views.cljs @@ -1,10 +1,10 @@ (ns status-im.ui.screens.help-center.views - (:require [re-frame.core :as re-frame] + (:require [quo.core :as quo] + [re-frame.core :as re-frame] + [status-im.constants :as constants] [status-im.i18n.i18n :as i18n] - [quo.core :as quo] - [status-im.ui.components.react :as react] [status-im.ui.components.list.views :as list] - [status-im.constants :as constants])) + [status-im.ui.components.react :as react])) (def data [{:size :small @@ -34,7 +34,8 @@ "support"]) :chevron true}]) -(defn help-center [] +(defn help-center + [] [list/flat-list {:data data :key-fn (fn [_ i] (str i)) diff --git a/src/status_im/ui/screens/home/sheet/views.cljs b/src/status_im/ui/screens/home/sheet/views.cljs index 9dafbd19c7..5775232860 100644 --- a/src/status_im/ui/screens/home/sheet/views.cljs +++ b/src/status_im/ui/screens/home/sheet/views.cljs @@ -1,32 +1,37 @@ (ns status-im.ui.screens.home.sheet.views - (:require [re-frame.core :as re-frame] - [status-im.ui.components.react :as react] - [status-im.qr-scanner.core :as qr-scanner] - [quo.core :as quo] + (:require [quo.core :as quo] + [re-frame.core :as re-frame] [status-im.i18n.i18n :as i18n] - [status-im.utils.config :as config] - [status-im.ui.components.invite.views :as invite])) + [status-im.qr-scanner.core :as qr-scanner] + [status-im.ui.components.invite.views :as invite] + [status-im.ui.components.react :as react] + [status-im.utils.config :as config])) -(defn hide-sheet-and-dispatch [event] +(defn hide-sheet-and-dispatch + [event] (re-frame/dispatch [:bottom-sheet/hide]) (re-frame/dispatch event)) -(defn add-new-view [] +(defn add-new-view + [] [react/view - [react/view {:style {:flex-direction :row - :padding-left 16 - :padding-right 8 - :justify-content :space-between - :align-items :center}} - [quo/text {:size :large - :weight :bold} + [react/view + {:style {:flex-direction :row + :padding-left 16 + :padding-right 8 + :justify-content :space-between + :align-items :center}} + [quo/text + {:size :large + :weight :bold} (i18n/label :t/open-home)] - [quo/button {:type :icon - :theme :icon - :accessibility-label :universal-qr-scanner - :on-press #(hide-sheet-and-dispatch - [::qr-scanner/scan-code - {:handler ::qr-scanner/on-scan-success}])} + [quo/button + {:type :icon + :theme :icon + :accessibility-label :universal-qr-scanner + :on-press #(hide-sheet-and-dispatch + [::qr-scanner/scan-code + {:handler ::qr-scanner/on-scan-success}])} :main-icons/qr]] [quo/list-item {:theme :accent diff --git a/src/status_im/ui/screens/home/styles.cljs b/src/status_im/ui/screens/home/styles.cljs index 8d298931af..0fdd6667f2 100644 --- a/src/status_im/ui/screens/home/styles.cljs +++ b/src/status_im/ui/screens/home/styles.cljs @@ -25,7 +25,8 @@ :top 10 :right 16}) -(defn chat-tooltip [] +(defn chat-tooltip + [] {:align-items :center :border-color colors/gray-lighter :border-width 1 @@ -51,14 +52,16 @@ {:align-items :center :justify-content :center}) -(defn hr-wrapper [] +(defn hr-wrapper + [] {:position :absolute :width "100%" :height 1 :top 10 :background-color colors/gray-lighter}) -(defn or-text [] +(defn or-text + [] {:width 40 :background-color colors/white :font-size 12 @@ -67,10 +70,11 @@ :color colors/gray}) (def tags-wrapper - {:margin-top 10 - :margin-bottom 18}) + {:margin-top 10 + :margin-bottom 18}) -(defn close-icon-container [] +(defn close-icon-container + [] {:width 24 :height 24 :border-radius 12 @@ -78,7 +82,8 @@ :align-items :center :justify-content :center}) -(defn counter-public-container [] +(defn counter-public-container + [] {:right 2 :top 0 :position :absolute diff --git a/src/status_im/ui/screens/home/views.cljs b/src/status_im/ui/screens/home/views.cljs index f0d7c60f87..aa5cc19fcd 100644 --- a/src/status_im/ui/screens/home/views.cljs +++ b/src/status_im/ui/screens/home/views.cljs @@ -1,85 +1,102 @@ (ns status-im.ui.screens.home.views - (:require [re-frame.core :as re-frame] - [reagent.core :as reagent] - [status-im.i18n.i18n :as i18n] - [status-im.react-native.resources :as resources] - [status-im.ui.components.connectivity.view :as connectivity] - [status-im.ui.components.icons.icons :as icons] - [status-im.ui.components.list.views :as list] - [status-im.ui.components.react :as react] - [status-im.ui.screens.home.styles :as styles] - [status-im.ui.screens.communities.views :as communities.views] - [status-im.ui.screens.home.views.inner-item :as inner-item] + (:require [quo.core :as quo] [quo.design-system.colors :as colors] - [quo.core :as quo] - [status-im.add-new.core :as new-chat] - [status-im.ui.components.search-input.view :as search-input] - [status-im.add-new.db :as db] - [utils.debounce :as debounce] - [status-im.utils.utils :as utils] - [status-im.ui.components.topbar :as topbar] - [status-im.ui.components.plus-button :as components.plus-button] - [status-im.ui.screens.chat.sheets :as sheets] - [status-im.ui.components.invite.views :as invite] - [status-im2.setup.config :as config] + [quo2.components.buttons.button :as quo2.button] [quo2.components.markdown.text :as quo2.text] + [quo2.foundations.colors :as quo2.colors] + [re-frame.core :as re-frame] + [reagent.core :as reagent] + [status-im.add-new.core :as new-chat] + [status-im.add-new.db :as db] + [status-im.i18n.i18n :as i18n] [status-im.qr-scanner.core :as qr-scanner] + [status-im.react-native.resources :as resources] [status-im.ui.components.chat-icon.screen :as chat-icon.screen] [status-im.ui.components.chat-icon.styles :as chat-icon.styles] - [quo2.foundations.colors :as quo2.colors] - [quo2.components.buttons.button :as quo2.button]) + [status-im.ui.components.connectivity.view :as connectivity] + [status-im.ui.components.icons.icons :as icons] + [status-im.ui.components.invite.views :as invite] + [status-im.ui.components.list.views :as list] + [status-im.ui.components.plus-button :as components.plus-button] + [status-im.ui.components.react :as react] + [status-im.ui.components.search-input.view :as search-input] + [status-im.ui.components.topbar :as topbar] + [status-im.ui.screens.chat.sheets :as sheets] + [status-im.ui.screens.communities.views :as communities.views] + [status-im.ui.screens.home.styles :as styles] + [status-im.ui.screens.home.views.inner-item :as inner-item] + [status-im.utils.utils :as utils] + [status-im2.setup.config :as config] + [utils.debounce :as debounce]) (:require-macros [status-im.utils.views :as views])) -(defn home-tooltip-view [] +(defn home-tooltip-view + [] [react/view (styles/chat-tooltip) - [react/view {:style {:width 66 :position :absolute :top -6 :background-color colors/white - :align-items :center}} - [react/image {:source (resources/get-image :empty-chats-header) - :style {:width 50 :height 50}}]] + [react/view + {:style {:width 66 + :position :absolute + :top -6 + :background-color colors/white + :align-items :center}} + [react/image + {:source (resources/get-image :empty-chats-header) + :style {:width 50 :height 50}}]] [react/touchable-highlight - {:style {:position :absolute :right 0 :top 0 - :width 44 :height 44 :align-items :center :justify-content :center} + {:style {:position :absolute + :right 0 + :top 0 + :width 44 + :height 44 + :align-items :center + :justify-content :center} :on-press #(re-frame/dispatch [:multiaccounts.ui/hide-home-tooltip]) :accessibility-label :hide-home-button} [icons/icon :main-icons/close-circle {:color colors/gray}]] [react/i18n-text {:style styles/no-chats-text :key :chat-and-transact}] - [react/view {:align-items :center - :margin-top 8 - :margin-bottom 12} + [react/view + {:align-items :center + :margin-top 8 + :margin-bottom 12} [invite/button]]]) -(defn welcome-blank-page [] +(defn welcome-blank-page + [] [react/view {:style {:flex 1 :flex-direction :row :align-items :center :justify-content :center}} [react/i18n-text {:style styles/welcome-blank-text :key :welcome-blank-message}]]) (defonce search-active? (reagent/atom false)) -(defn search-input-wrapper [search-filter chats-empty] - [react/view {:padding-horizontal 16 - :padding-vertical 10} +(defn search-input-wrapper + [search-filter chats-empty] + [react/view + {:padding-horizontal 16 + :padding-vertical 10} [search-input/search-input - {:search-active? search-active? - :placeholder (i18n/label :t/search) - :border-radius 10 - :search-filter search-filter - :before true - :on-cancel #(re-frame/dispatch [:search/home-filter-changed nil]) - :on-blur (fn [] - (when chats-empty - (re-frame/dispatch [:search/home-filter-changed nil])) - (re-frame/dispatch [::new-chat/clear-new-identity])) - :on-focus (fn [search-filter] - (when-not search-filter - (re-frame/dispatch [:search/home-filter-changed ""]) - (re-frame/dispatch [::new-chat/clear-new-identity]))) - :on-change (fn [text] - (re-frame/dispatch [:search/home-filter-changed text]) - (re-frame/dispatch [:set-in [:contacts/new-identity :state] :searching]) - (debounce/debounce-and-dispatch [:new-chat/set-new-identity text] 300))}]]) + {:search-active? search-active? + :placeholder (i18n/label :t/search) + :border-radius 10 + :search-filter search-filter + :before true + :on-cancel #(re-frame/dispatch [:search/home-filter-changed nil]) + :on-blur (fn [] + (when chats-empty + (re-frame/dispatch [:search/home-filter-changed nil])) + (re-frame/dispatch [::new-chat/clear-new-identity])) + :on-focus (fn [search-filter] + (when-not search-filter + (re-frame/dispatch [:search/home-filter-changed ""]) + (re-frame/dispatch [::new-chat/clear-new-identity]))) + :on-change (fn [text] + (re-frame/dispatch [:search/home-filter-changed text]) + (re-frame/dispatch [:set-in [:contacts/new-identity :state] :searching]) + (debounce/debounce-and-dispatch [:new-chat/set-new-identity text] 300))}]]) -(defn search-input-wrapper-old [search-filter chats-empty] - [react/view {:padding-horizontal 16 - :padding-vertical 10} +(defn search-input-wrapper-old + [search-filter chats-empty] + [react/view + {:padding-horizontal 16 + :padding-vertical 10} [search-input/search-input-old {:search-active? search-active? :search-filter search-filter @@ -97,33 +114,37 @@ (re-frame/dispatch [:set-in [:contacts/new-identity :state] :searching]) (debounce/debounce-and-dispatch [:new-chat/set-new-identity text] 300))}]]) -(defn start-suggestion [search-value] +(defn start-suggestion + [search-value] (let [{:keys [state ens-name public-key]} @(re-frame/subscribe [:contacts/new-identity]) - valid-private? (= state :valid) - valid-public? (db/valid-topic? search-value)] + valid-private? (= state :valid) + valid-public? (db/valid-topic? search-value)] (when (or valid-public? valid-private?) [react/view [quo/list-header (i18n/label :t/search-no-chat-found)] (when valid-private? - [quo/list-item {:theme :accent - :icon :main-icons/private-chat - :title (or ens-name (utils/get-shortened-address public-key)) - :subtitle (i18n/label :t/join-new-private-chat) - :on-press (fn [] - (debounce/dispatch-and-chill [:contact.ui/contact-code-submitted false] 3000) - (re-frame/dispatch [:search/home-filter-changed nil]))}]) + [quo/list-item + {:theme :accent + :icon :main-icons/private-chat + :title (or ens-name (utils/get-shortened-address public-key)) + :subtitle (i18n/label :t/join-new-private-chat) + :on-press (fn [] + (debounce/dispatch-and-chill [:contact.ui/contact-code-submitted false] 3000) + (re-frame/dispatch [:search/home-filter-changed nil]))}]) (when valid-public? - [quo/list-item {:theme :accent - :icon :main-icons/public-chat - :title (str "#" search-value) - :subtitle (i18n/label :t/join-new-public-chat) - :on-press (fn [] - (re-frame/dispatch [:chat.ui/start-public-chat search-value]) - (re-frame/dispatch [:set :public-group-topic nil]) - (re-frame/dispatch [:search/home-filter-changed nil]))}])]))) + [quo/list-item + {:theme :accent + :icon :main-icons/public-chat + :title (str "#" search-value) + :subtitle (i18n/label :t/join-new-public-chat) + :on-press (fn [] + (re-frame/dispatch [:chat.ui/start-public-chat search-value]) + (re-frame/dispatch [:set :public-group-topic nil]) + (re-frame/dispatch [:search/home-filter-changed nil]))}])]))) -(defn render-fn [{:keys [chat-id] :as home-item}] +(defn render-fn + [{:keys [chat-id] :as home-item}] ;; We use `chat-id` to distinguish communities from chats (if chat-id [inner-item/home-list-item @@ -139,7 +160,8 @@ [sheets/actions home-item])}])}] [communities.views/community-home-list-item home-item])) -(defn render-fn-old [{:keys [chat-id] :as home-item}] +(defn render-fn-old + [{:keys [chat-id] :as home-item}] ;; We use `chat-id` to distinguish communities from chats (if chat-id [inner-item/home-list-item-old @@ -155,15 +177,18 @@ [sheets/actions home-item])}])}] [communities.views/community-home-list-item home-item])) -(defn chat-list-key-fn [item] +(defn chat-list-key-fn + [item] (or (:chat-id item) (:id item))) -(defn get-item-layout [_ index] +(defn get-item-layout + [_ index] #js {:length 64 :offset (* 64 index) :index index}) -(views/defview communities-and-chats [] +(views/defview communities-and-chats + [] (views/letsubs [{:keys [items search-filter]} [:home-items] - hide-home-tooltip? [:hide-home-tooltip?]] + hide-home-tooltip? [:hide-home-tooltip?]] (if (and (empty? items) (empty? search-filter) hide-home-tooltip? @@ -186,7 +211,8 @@ [home-tooltip-view] [react/view {:height 68}])}]))) -(views/defview communities-and-chats-old [] +(views/defview communities-and-chats-old + [] (views/letsubs [{:keys [items search-filter]} [:home-items] hide-home-tooltip? [:hide-home-tooltip?]] (if (and (empty? items) @@ -211,7 +237,8 @@ [home-tooltip-view] [react/view {:height 68}])}]))) -(views/defview chats-list [] +(views/defview chats-list + [] (views/letsubs [loading? [:chats/loading?]] [:<> [connectivity/loading-indicator] @@ -220,7 +247,8 @@ [react/activity-indicator {:animating true}]] [communities-and-chats])])) -(views/defview chats-list-old [] +(views/defview chats-list-old + [] (views/letsubs [loading? [:chats/loading?]] [:<> [connectivity/loading-indicator] @@ -229,95 +257,118 @@ [react/activity-indicator {:animating true}]] [communities-and-chats-old])])) -(views/defview plus-button [] +(views/defview plus-button + [] (views/letsubs [logging-in? [:multiaccounts/login]] [components.plus-button/plus-button-old - {:on-press (when-not logging-in? - #(re-frame/dispatch [:bottom-sheet/show-sheet :add-new {}])) - :loading logging-in? + {:on-press (when-not logging-in? + #(re-frame/dispatch [:bottom-sheet/show-sheet :add-new {}])) + :loading logging-in? :accessibility-label :new-chat-button}])) -(views/defview plus-button-old [] +(views/defview plus-button-old + [] (views/letsubs [logging-in? [:multiaccounts/login]] [components.plus-button/plus-button-old - {:on-press (when-not logging-in? - #(re-frame/dispatch [:bottom-sheet/show-sheet :add-new {}])) - :loading logging-in? + {:on-press (when-not logging-in? + #(re-frame/dispatch [:bottom-sheet/show-sheet :add-new {}])) + :loading logging-in? :accessibility-label :new-chat-button}])) -(views/defview notifications-button [] +(views/defview notifications-button + [] (views/letsubs [notif-count [:activity-center/unread-count]] [react/view - [quo2.button/button {:type :grey - :size 32 - :width 32 - :style {:margin-left 12} - :accessibility-label :notifications-button - :on-press #(re-frame/dispatch [:activity-center/open])} - [icons/icon :main-icons/notification2 {:color (quo2.colors/theme-colors quo2.colors/neutral-100 quo2.colors/white)}]] + [quo2.button/button + {:type :grey + :size 32 + :width 32 + :style {:margin-left 12} + :accessibility-label :notifications-button + :on-press #(re-frame/dispatch [:activity-center/open])} + [icons/icon :main-icons/notification2 + {:color (quo2.colors/theme-colors quo2.colors/neutral-100 quo2.colors/white)}]] (when (pos? notif-count) - [react/view {:style (merge (styles/counter-public-container) {:top 5 :right 5}) - :pointer-events :none} - [react/view {:style styles/counter-public - :accessibility-label :notifications-unread-badge}]])])) + [react/view + {:style (merge (styles/counter-public-container) {:top 5 :right 5}) + :pointer-events :none} + [react/view + {:style styles/counter-public + :accessibility-label :notifications-unread-badge}]])])) -(defn qr-button [] - [quo2.button/button {:type :grey - :accessibility-label "qr-button" - :size 32 - :width 32 - :style {:margin-left 12} - :on-press #(do - (re-frame/dispatch [::qr-scanner/scan-code - {:handler ::qr-scanner/on-scan-success}]))} - [icons/icon :main-icons/qr2 {:color (quo2.colors/theme-colors quo2.colors/neutral-100 quo2.colors/white)}]]) +(defn qr-button + [] + [quo2.button/button + {:type :grey + :accessibility-label "qr-button" + :size 32 + :width 32 + :style {:margin-left 12} + :on-press #(do + (re-frame/dispatch [::qr-scanner/scan-code + {:handler ::qr-scanner/on-scan-success}]))} + [icons/icon :main-icons/qr2 + {:color (quo2.colors/theme-colors quo2.colors/neutral-100 quo2.colors/white)}]]) -(defn scan-button [] - [quo2.button/button {:type :grey - :size 32 - :width 32 - :accessibility-label "scan-button" - :on-press #(do - (re-frame/dispatch [::qr-scanner/scan-code - {:handler ::qr-scanner/on-scan-success}]))} - [icons/icon :main-icons/scan2 {:color (quo2.colors/theme-colors quo2.colors/neutral-100 quo2.colors/white)}]]) +(defn scan-button + [] + [quo2.button/button + {:type :grey + :size 32 + :width 32 + :accessibility-label "scan-button" + :on-press #(do + (re-frame/dispatch [::qr-scanner/scan-code + {:handler ::qr-scanner/on-scan-success}]))} + [icons/icon :main-icons/scan2 + {:color (quo2.colors/theme-colors quo2.colors/neutral-100 quo2.colors/white)}]]) -(views/defview profile-button [] +(views/defview profile-button + [] (views/letsubs [{:keys [public-key preferred-name emoji]} [:multiaccount]] [react/view [chat-icon.screen/emoji-chat-icon-view public-key false preferred-name emoji - {:size 28 - :chat-icon chat-icon.styles/chat-icon-chat-list}]])) + {:size 28 + :chat-icon chat-icon.styles/chat-icon-chat-list}]])) -(defn home [] - [react/keyboard-avoiding-view {:style {:flex 1 :background-color (quo2.colors/theme-colors quo2.colors/neutral-5 quo2.colors/neutral-95)} - :ignore-offset true} - [topbar/topbar {:navigation :none - :use-insets true - :background (quo2.colors/theme-colors quo2.colors/neutral-5 quo2.colors/neutral-95) - :left-component [react/view {:flex-direction :row :margin-left 16} - [profile-button]] - :right-component [react/view {:flex-direction :row :margin-right 16} - [scan-button] - [qr-button] - [notifications-button]] - :border-bottom false}] - [react/view {:flex-direction :row - :justify-content :space-between - :align-items :center - :margin-horizontal 16 - :margin-top 15 - :margin-bottom 8} +(defn home + [] + [react/keyboard-avoiding-view + {:style {:flex 1 + :background-color (quo2.colors/theme-colors quo2.colors/neutral-5 + quo2.colors/neutral-95)} + :ignore-offset true} + [topbar/topbar + {:navigation :none + :use-insets true + :background (quo2.colors/theme-colors quo2.colors/neutral-5 quo2.colors/neutral-95) + :left-component [react/view {:flex-direction :row :margin-left 16} + [profile-button]] + :right-component [react/view {:flex-direction :row :margin-right 16} + [scan-button] + [qr-button] + [notifications-button]] + :border-bottom false}] + [react/view + {:flex-direction :row + :justify-content :space-between + :align-items :center + :margin-horizontal 16 + :margin-top 15 + :margin-bottom 8} [quo2.text/text {:size :heading-1 :weight :semi-bold} (i18n/label :t/messages)] [plus-button]] [chats-list]]) -(defn home-old [] - [react/keyboard-avoiding-view {:style {:flex 1} - :ignore-offset true} - [topbar/topbar {:title (i18n/label :t/chat) - :navigation :none - :right-component [react/view {:flex-direction :row :margin-right 16} - [connectivity/connectivity-button]]}] +(defn home-old + [] + [react/keyboard-avoiding-view + {:style {:flex 1} + :ignore-offset true} + [topbar/topbar + {:title (i18n/label :t/chat) + :navigation :none + :right-component [react/view {:flex-direction :row :margin-right 16} + [connectivity/connectivity-button]]}] [chats-list-old] [plus-button-old]]) diff --git a/src/status_im/ui/screens/home/views/inner_item.cljs b/src/status_im/ui/screens/home/views/inner_item.cljs index ca51de8b82..bb3c6063d7 100644 --- a/src/status_im/ui/screens/home/views/inner_item.cljs +++ b/src/status_im/ui/screens/home/views/inner_item.cljs @@ -1,37 +1,41 @@ (ns status-im.ui.screens.home.views.inner-item (:require [clojure.string :as string] + [quo.core :as quo] + [quo.design-system.colors :as colors] + [quo2.components.markdown.text :as quo2.text] + [quo2.foundations.colors :as quo2.colors] [re-frame.core :as re-frame] [status-im.constants :as constants] [status-im.i18n.i18n :as i18n] [status-im.ui.components.badge :as badge] - [quo.design-system.colors :as colors] [status-im.ui.components.chat-icon.screen :as chat-icon.screen] - [quo.core :as quo] + [status-im.ui.components.chat-icon.styles :as chat-icon.styles] + [status-im.ui.components.icons.icons :as icons] [status-im.ui.components.react :as react] [status-im.ui.screens.home.styles :as styles] - [status-im.ui.components.icons.icons :as icons] [status-im.utils.core :as utils] [status-im.utils.datetime :as time] - [status-im.ui.components.chat-icon.styles :as chat-icon.styles] - [quo2.components.markdown.text :as quo2.text] - [status-im.utils.utils :as utils.utils] - [quo2.foundations.colors :as quo2.colors])) + [status-im.utils.utils :as utils.utils])) -(defn preview-label [label-key label-fn] - [react/text {:style styles/last-message-text - :accessibility-label :no-messages-text - :number-of-lines 1} +(defn preview-label + [label-key label-fn] + [react/text + {:style styles/last-message-text + :accessibility-label :no-messages-text + :number-of-lines 1} (i18n/label label-key label-fn)]) (def max-subheader-length 100) -(defn truncate-literal [literal] +(defn truncate-literal + [literal] (when literal (let [size (min max-subheader-length (.-length literal))] {:components (.substring literal 0 size) :length size}))) -(defn add-parsed-to-subheader [acc {:keys [type destination literal children]}] +(defn add-parsed-to-subheader + [acc {:keys [type destination literal children]}] (let [result (case type "paragraph" (reduce @@ -44,7 +48,8 @@ children) "mention" - {:components [react/text-class @(re-frame/subscribe [:contacts/contact-name-by-identity literal])] + {:components [react/text-class + @(re-frame/subscribe [:contacts/contact-name-by-identity literal])] :length 4} ;; we can't predict name length so take the smallest possible "status-tag" @@ -66,19 +71,22 @@ (if (>= length max-subheader-length) (reduced acc-text) (add-parsed-to-subheader acc-text new-text-chunk))) - {:components [react/text-class {:style styles/last-message-text - :number-of-lines 1 - :ellipsize-mode :tail - :accessibility-label :chat-message-text}] + {:components [react/text-class + {:style styles/last-message-text + :number-of-lines 1 + :ellipsize-mode :tail + :accessibility-label :chat-message-text}] :length 0} parsed-text)] (:components result))) -(defn content-type-community-invite? [content-type community-id] +(defn content-type-community-invite? + [content-type community-id] (and (= constants/content-type-community content-type) (not (string/blank? community-id)))) -(defn message-content-text [{:keys [content content-type community-id]} absolute] +(defn message-content-text + [{:keys [content content-type community-id]} absolute] [react/view (when absolute {:position :absolute :left 72 :top 32 :right 80}) (cond (not (and content content-type)) @@ -90,10 +98,11 @@ (= constants/content-type-command content-type)) (not (string/blank? (:text content)))) (if (string/blank? (:parsed-text content)) - [react/text-class {:style styles/last-message-text - :number-of-lines 1 - :ellipsize-mode :tail - :accessibility-label :chat-message-text} + [react/text-class + {:style styles/last-message-text + :number-of-lines 1 + :ellipsize-mode :tail + :accessibility-label :chat-message-text} (:text content)] [render-subheader (:parsed-text content)]) @@ -116,15 +125,17 @@ (fn [timestamp] (string/upper-case (time/to-short-str timestamp))))) -(defn unviewed-indicator [{:keys [unviewed-mentions-count - unviewed-messages-count - public?]}] +(defn unviewed-indicator + [{:keys [unviewed-mentions-count + unviewed-messages-count + public?]}] (when (pos? unviewed-messages-count) [react/view {:position :absolute :right 16} (cond (and public? (not (pos? unviewed-mentions-count))) - [react/view {:style styles/public-unread - :accessibility-label :unviewed-messages-public}] + [react/view + {:style styles/public-unread + :accessibility-label :unviewed-messages-public}] (and public? (pos? unviewed-mentions-count)) [badge/message-counter unviewed-mentions-count] @@ -132,15 +143,17 @@ :else [badge/message-counter unviewed-messages-count])])) -(defn unviewed-indicator-old [{:keys [unviewed-mentions-count - unviewed-messages-count - public?]}] +(defn unviewed-indicator-old + [{:keys [unviewed-mentions-count + unviewed-messages-count + public?]}] (when (pos? unviewed-messages-count) [react/view {:position :absolute :right 16 :bottom 12} (cond (and public? (not (pos? unviewed-mentions-count))) - [react/view {:style styles/public-unread - :accessibility-label :unviewed-messages-public}] + [react/view + {:style styles/public-unread + :accessibility-label :unviewed-messages-public}] (and public? (pos? unviewed-mentions-count)) [badge/message-counter unviewed-mentions-count] @@ -148,17 +161,20 @@ :else [badge/message-counter unviewed-messages-count])])) -(defn icon-style [] +(defn icon-style + [] {:color colors/black :width 15 :height 15 - :container-style {:top 13 :left 72 + :container-style {:top 13 + :left 72 :position :absolute :width 15 :height 15 :margin-right 2}}) -(defn chat-item-icon [muted private-group? public-group?] +(defn chat-item-icon + [muted private-group? public-group?] (cond muted [icons/icon :main-icons/tiny-muted (assoc (icon-style) :color colors/gray)] @@ -169,16 +185,18 @@ :else [icons/icon :main-icons/tiny-new-contact (icon-style)])) -(defn chat-item-title [chat-id muted group-chat chat-name edit?] - [quo2.text/text {:weight :semi-bold - :color (when muted :secondary) - :accessibility-label :chat-name-text - :ellipsize-mode :tail - :number-of-lines 1 - :style {:position :absolute - :left 72 - :top 10 - :right (if edit? 50 90)}} +(defn chat-item-title + [chat-id muted group-chat chat-name edit?] + [quo2.text/text + {:weight :semi-bold + :color (when muted :secondary) + :accessibility-label :chat-name-text + :ellipsize-mode :tail + :number-of-lines 1 + :style {:position :absolute + :left 72 + :top 10 + :right (if edit? 50 90)}} (if group-chat (utils/truncate-str chat-name 30) ;; This looks a bit odd, but I would like only to subscribe @@ -186,16 +204,18 @@ ;; won't be applied correctly. (first @(re-frame/subscribe [:contacts/contact-two-names-by-identity chat-id])))]) -(defn chat-item-title-old [chat-id muted group-chat chat-name edit?] - [quo/text {:weight :medium - :color (when muted :secondary) - :accessibility-label :chat-name-text - :ellipsize-mode :tail - :number-of-lines 1 - :style {:position :absolute - :left 92 - :top 10 - :right (if edit? 50 90)}} +(defn chat-item-title-old + [chat-id muted group-chat chat-name edit?] + [quo/text + {:weight :medium + :color (when muted :secondary) + :accessibility-label :chat-name-text + :ellipsize-mode :tail + :number-of-lines 1 + :style {:position :absolute + :left 92 + :top 10 + :right (if edit? 50 90)}} (if group-chat (utils/truncate-str chat-name 30) ;; This looks a bit odd, but I would like only to subscribe @@ -203,8 +223,11 @@ ;; won't be applied correctly. (first @(re-frame/subscribe [:contacts/contact-two-names-by-identity chat-id])))]) -(defn home-list-item [home-item opts] - (let [{:keys [chat-id chat-name color group-chat muted emoji highlight edit? public? unviewed-messages-count contacts users members]} home-item +(defn home-list-item + [home-item opts] + (let [{:keys [chat-id chat-name color group-chat muted emoji highlight edit? public? + unviewed-messages-count contacts users members]} + home-item background-color (when highlight (colors/get-color :interactive-02)) group-members-public-keys (->> (concat (keys users) contacts (map #(:id %) members)) (into #{}) @@ -212,10 +235,19 @@ [react/touchable-opacity (merge {:style {:height 64 :background-color background-color}} opts) [:<> (when (pos? unviewed-messages-count) - [react/view {:position :absolute :top 2 :left 8 :right 8 :bottom 2 :border-radius 16 :background-color quo2.colors/primary-50-opa-5}]) + [react/view + {:position :absolute + :top 2 + :left 8 + :right 8 + :bottom 2 + :border-radius 16 + :background-color quo2.colors/primary-50-opa-5}]) [chat-icon.screen/emoji-chat-icon-view chat-id group-chat chat-name emoji {:container (assoc chat-icon.styles/container-chat-list - :top 12 :left 20 :position :absolute) + :top 12 + :left 20 + :position :absolute) :size 32 :chat-icon chat-icon.styles/chat-icon-chat-list :default-chat-icon (chat-icon.styles/default-chat-icon-chat-list color) @@ -228,17 +260,20 @@ [unviewed-indicator home-item]]) [react/view {:position :absolute :left 72 :top 32 :right 80} (if public? - [quo2.text/text {:color :secondary - :number-of-lines 1 - :ellipsize-mode :middle - :weight :medium - :style {:color (quo2.colors/theme-colors quo2.colors/neutral-50 quo2.colors/neutral-40)}} + [quo2.text/text + {:color :secondary + :number-of-lines 1 + :ellipsize-mode :middle + :weight :medium + :style {:color (quo2.colors/theme-colors quo2.colors/neutral-50 + quo2.colors/neutral-40)}} (i18n/label :t/public)] (if group-chat - [react/view {:flex-direction :row - :flex 1 - :padding-right 16 - :align-items :center} + [react/view + {:flex-direction :row + :flex 1 + :padding-right 16 + :align-items :center} [icons/icon :main-icons/tiny-group2 {:width 16 :height 16 @@ -246,25 +281,33 @@ :container-style {:width 16 :height 16 :margin-right 4}}] - [quo2.text/text {:weight :medium - :style {:color (quo2.colors/theme-colors quo2.colors/neutral-50 quo2.colors/neutral-40)}} + [quo2.text/text + {:weight :medium + :style {:color (quo2.colors/theme-colors quo2.colors/neutral-50 quo2.colors/neutral-40)}} (i18n/label :t/members-count {:count (count group-members-public-keys)})]] - [quo2.text/text {:monospace true - :weight :medium - :style {:color (quo2.colors/theme-colors quo2.colors/neutral-50 quo2.colors/neutral-40)} - :number-of-lines 1 - :ellipsize-mode :middle} + [quo2.text/text + {:monospace true + :weight :medium + :style {:color (quo2.colors/theme-colors quo2.colors/neutral-50 + quo2.colors/neutral-40)} + :number-of-lines 1 + :ellipsize-mode :middle} (utils.utils/get-shortened-address chat-id)]))]]])) -(defn home-list-item-old [home-item opts] - (let [{:keys [chat-id chat-name color group-chat public? timestamp last-message muted emoji highlight edit?]} home-item +(defn home-list-item-old + [home-item opts] + (let [{:keys [chat-id chat-name color group-chat public? timestamp last-message muted emoji highlight + edit?]} + home-item background-color (when highlight (colors/get-color :interactive-02))] [react/touchable-opacity (merge {:style {:height 64 :background-color background-color}} opts) [:<> [chat-item-icon muted (and group-chat (not public?)) (and group-chat public?)] [chat-icon.screen/emoji-chat-icon-view chat-id group-chat chat-name emoji {:container (assoc chat-icon.styles/container-chat-list - :top 12 :left 16 :position :absolute) + :top 12 + :left 16 + :position :absolute) :size 40 :chat-icon chat-icon.styles/chat-icon-chat-list :default-chat-icon (chat-icon.styles/default-chat-icon-chat-list color) @@ -274,9 +317,10 @@ [chat-item-title-old chat-id muted group-chat chat-name edit?] (when-not edit? [:<> - [react/text {:style styles/datetime-text - :number-of-lines 1 - :accessibility-label :last-message-time-text} + [react/text + {:style styles/datetime-text + :number-of-lines 1 + :accessibility-label :last-message-time-text} ;;TODO (perf) move to event (memo-timestamp (if (pos? (:whisper-timestamp last-message)) (:whisper-timestamp last-message) diff --git a/src/status_im/ui/screens/keycard/authentication_method/views.cljs b/src/status_im/ui/screens/keycard/authentication_method/views.cljs index d4c6daeb23..b58aecf448 100644 --- a/src/status_im/ui/screens/keycard/authentication_method/views.cljs +++ b/src/status_im/ui/screens/keycard/authentication_method/views.cljs @@ -1,43 +1,48 @@ (ns status-im.ui.screens.keycard.authentication-method.views - (:require [re-frame.core :as re-frame] + (:require [quo.components.separator :as separator] + [quo.design-system.colors :as colors] + [re-frame.core :as re-frame] [status-im.i18n.i18n :as i18n] [status-im.react-native.resources :as resources] - [quo.design-system.colors :as colors] [status-im.ui.components.icons.icons :as icons] [status-im.ui.components.react :as react] [status-im.ui.components.topbar :as topbar] - [status-im.ui.screens.keycard.authentication-method.styles - :as - styles] - [quo.components.separator :as separator])) + [status-im.ui.screens.keycard.authentication-method.styles :as styles])) -(defn authentication-method-row [{:keys [title on-press icon]}] +(defn authentication-method-row + [{:keys [title on-press icon]}] [react/touchable-highlight {:on-press on-press} [react/view styles/authentication-method-row [react/view styles/authentication-method-row-icon-container [icons/icon icon {:color colors/blue}]] [react/view styles/authentication-method-row-wrapper - [react/text {:style styles/choose-authentication-method-row-text - :number-of-lines 1} + [react/text + {:style styles/choose-authentication-method-row-text + :number-of-lines 1} title]] [icons/icon :main-icons/next {:color colors/gray}]]]) -(defn keycard-authentication-method [] +(defn keycard-authentication-method + [] [react/view styles/container [react/view {:flex 1} [topbar/topbar] [separator/separator] [react/view styles/choose-authentication-method [react/view styles/lock-image-container - [react/image {:source (resources/get-image :keycard-lock) - :style styles/lock-image}]] - [react/text {:style styles/choose-authentication-method-text - :number-of-lines 3} + [react/image + {:source (resources/get-image :keycard-lock) + :style styles/lock-image}]] + [react/text + {:style styles/choose-authentication-method-text + :number-of-lines 3} (i18n/label :t/choose-authentication-method)]] [react/view styles/authentication-methods - [authentication-method-row {:title (i18n/label :t/keycard) - :icon :main-icons/keycard - :on-press #(re-frame/dispatch [:onboarding.ui/keycard-option-pressed])}] - [authentication-method-row {:title (i18n/label :t/password) - :icon :main-icons/password - :on-press #(re-frame/dispatch [:keycard.ui/password-option-pressed])}]]]]) + [authentication-method-row + {:title (i18n/label :t/keycard) + :icon :main-icons/keycard + :on-press #(re-frame/dispatch [:onboarding.ui/keycard-option-pressed])}] + [authentication-method-row + {:title (i18n/label :t/password) + :icon :main-icons/password + :on-press #(re-frame/dispatch [:keycard.ui/password-option-pressed])}]]]]) diff --git a/src/status_im/ui/screens/keycard/components/description.cljs b/src/status_im/ui/screens/keycard/components/description.cljs index 230c1183a6..303495a4da 100644 --- a/src/status_im/ui/screens/keycard/components/description.cljs +++ b/src/status_im/ui/screens/keycard/components/description.cljs @@ -4,7 +4,8 @@ [status-im.ui.components.react :as react] [status-im.ui.screens.keycard.components.style :as styles])) -(defn text-block-style [animated] +(defn text-block-style + [animated] {:height 66 :margin-bottom 8 :opacity (animation/interpolate animated @@ -18,7 +19,8 @@ (defonce animating (atom nil)) -(defn animate-description [animated] +(defn animate-description + [animated] (when-not @animating (reset! animating true) ;; TODO; Animate exit @@ -30,7 +32,8 @@ :easing easing}) #(reset! animating false)))) -(defn animated-description [] +(defn animated-description + [] (let [current-text (reagent/atom nil) animated-value (animation/create-value 0)] (fn [{:keys [title description]}] @@ -38,9 +41,11 @@ (reset! current-text [title description]) (animate-description animated-value)) [react/animated-view {:style (text-block-style animated-value)} - [react/text {:style styles/title-style - :number-of-lines 1} + [react/text + {:style styles/title-style + :number-of-lines 1} title] - [react/text {:style styles/helper-text-style - :number-of-lines 2} + [react/text + {:style styles/helper-text-style + :number-of-lines 2} description]]))) diff --git a/src/status_im/ui/screens/keycard/components/keycard_animation.cljs b/src/status_im/ui/screens/keycard/components/keycard_animation.cljs index bf66399b2e..4ee8f736ef 100644 --- a/src/status_im/ui/screens/keycard/components/keycard_animation.cljs +++ b/src/status_im/ui/screens/keycard/components/keycard_animation.cljs @@ -1,13 +1,14 @@ (ns status-im.ui.screens.keycard.components.keycard-animation - (:require [reagent.core :as reagent] + (:require [quo.design-system.colors :as colors] + [reagent.core :as reagent] [status-im.keycard.card :as keycard-nfc] [status-im.react-native.resources :as resources] [status-im.ui.components.animation :as animation] - [quo.design-system.colors :as colors] [status-im.ui.components.icons.icons :as icons] [status-im.ui.components.react :as react])) -(defn circle [{:keys [animation-value color size]}] +(defn circle + [{:keys [animation-value color size]}] [react/animated-view {:style {:width size :height size @@ -16,14 +17,15 @@ :border-radius (/ size 2) :opacity (animation/interpolate animation-value - {:inputRange [0 1 2] + {:inputRange [0 1 2] :outputRange [0.7 1 0]}) :transform [{:scale (animation/interpolate animation-value {:inputRange [0 1] :outputRange [0.9 1]})}]}}]) -(defn indicator-container [anim children] +(defn indicator-container + [anim children] [react/animated-view {:style {:position "absolute" :justify-content :center @@ -46,32 +48,38 @@ :outputRange [0 1]})}]}} children]) -(defn indicator [{:keys [state animation-value]}] +(defn indicator + [{:keys [state animation-value]}] [indicator-container animation-value (case @state :error - [icons/icon :main-icons/close {:color colors/red - :height 28 - :width 28}] + [icons/icon :main-icons/close + {:color colors/red + :height 28 + :width 28}] :success - [icons/icon :main-icons/check {:color colors/green - :height 28 - :width 28}] + [icons/icon :main-icons/check + {:color colors/green + :height 28 + :width 28}] :connected - [icons/icon :main-icons/check {:color colors/blue - :height 28 - :width 28}] + [icons/icon :main-icons/check + {:color colors/blue + :height 28 + :width 28}] :processing [react/activity-indicator {:color colors/blue}] nil)]) -(defn animate-card-position [card-scale animation-value] +(defn animate-card-position + [card-scale animation-value] {:transform [{:scale card-scale} {:translateX (animation/x animation-value)} {:translateY (animation/y animation-value)}]}) -(defn card-colors [state] +(defn card-colors + [state] (case state (:init :awaiting) {:card-color "#2D2D2D" @@ -91,10 +99,12 @@ :chip-color colors/white} nil)) -(defn card [{:keys [card-scale state indicator-value animation-value]}] +(defn card + [{:keys [card-scale state indicator-value animation-value]}] (let [{:keys [card-color chip-color - key-color]} (card-colors @state)] + key-color]} + (card-colors @state)] [react/animated-view {:style (merge (animate-card-position card-scale animation-value) @@ -126,30 +136,34 @@ {:color key-color :width 25 :height 42}]] - [indicator {:state state - :animation-value indicator-value}]])) + [indicator + {:state state + :animation-value indicator-value}]])) -(defn phone [{:keys [animation-value]}] - [react/animated-view {:style {:position :absolute - :bottom 0 - :elevation 9 - :opacity (animation/interpolate - animation-value - {:inputRange [0 1] - :outputRange [0 0.9]}) - :transform [{:translateY (animation/interpolate - animation-value - {:inputRange [0 1] - :outputRange [125 10]})}]}} +(defn phone + [{:keys [animation-value]}] + [react/animated-view + {:style {:position :absolute + :bottom 0 + :elevation 9 + :opacity (animation/interpolate + animation-value + {:inputRange [0 1] + :outputRange [0 0.9]}) + :transform [{:translateY (animation/interpolate + animation-value + {:inputRange [0 1] + :outputRange [125 10]})}]}} [react/image {:source (resources/get-image :onboarding-phone) :style {:height 125 :width 86}}]]) -(def circle-easing (animation/bezier 0.455 0.03 0.515 0.955)) -(def card-easing (animation/bezier 0.77 0 0.175 1)) +(def circle-easing (animation/bezier 0.455 0.03 0.515 0.955)) +(def card-easing (animation/bezier 0.77 0 0.175 1)) -(defn- circle-animation [animation-value to delay] +(defn- circle-animation + [animation-value to delay] (animation/timing animation-value {:toValue to :delay delay @@ -169,8 +183,9 @@ card-loop (animation/anim-loop (animation/anim-sequence [(animation/timing card - {:toValue #js {:x -30 - :y 30} + {:toValue #js + {:x -30 + :y 30} :duration 1000 :easing card-easing}) (animation/timing card @@ -180,8 +195,9 @@ :delay 2000 :easing card-easing}) (animation/timing card - {:toValue #js {:x -30 - :y 105} + {:toValue #js + {:x -30 + :y 105} :duration 1000 :delay 2000 :easing card-easing}) @@ -219,10 +235,12 @@ phone-enter-at) (animation/start animation))) -(defn on-error [{:keys [state restart]}] +(defn on-error + [{:keys [state restart]}] (reset! state :error) (js/setTimeout #(when (= @state :error) - (restart)) 3000)) + (restart)) + 3000)) (defn on-connect [{:keys [state card small indicator @@ -253,8 +271,9 @@ :timing 1000 :easing card-easing}) (animation/timing card - {:toValue #js {:x 0 - :y 0} + {:toValue #js + {:x 0 + :y 0} :timing 3000 :easing card-easing})])] (reset! state :connected) @@ -262,12 +281,14 @@ 2000) (animation/start connect-animation))) -(defn animated-circles [{:keys [state connected? on-card-connected on-card-disconnected]}] +(defn animated-circles + [{:keys [state connected? on-card-connected on-card-disconnected]}] (let [animation-small (animation/create-value 0) animation-medium (animation/create-value 0) animation-big (animation/create-value 0) - animation-card (animation/create-value-xy #js {:x 0 - :y 0}) + animation-card (animation/create-value-xy #js + {:x 0 + :y 0}) card-scale (animation/create-value 0.66) animation-phone (animation/create-value 0) animation-indicator (animation/create-value 0) @@ -316,27 +337,32 @@ (keycard-nfc/remove-event-listener listener))) :render (fn [] - [react/view {:style {:position :absolute - :top 0 - :bottom 0 - :left 0 - :right 0 - :justify-content :center - :align-items :center}} + [react/view + {:style {:position :absolute + :top 0 + :bottom 0 + :left 0 + :right 0 + :justify-content :center + :align-items :center}} - [circle {:animation-value animation-big - :size 200 - :color "#F1F4FF"}] - [circle {:animation-value animation-medium - :size 140 - :color "#E3E8FA"}] - [circle {:animation-value animation-small - :size 80 - :color "#D2D9F0"}] + [circle + {:animation-value animation-big + :size 200 + :color "#F1F4FF"}] + [circle + {:animation-value animation-medium + :size 140 + :color "#E3E8FA"}] + [circle + {:animation-value animation-small + :size 80 + :color "#D2D9F0"}] - [card {:animation-value animation-card - :state state - :indicator-value animation-indicator - :card-scale card-scale}] + [card + {:animation-value animation-card + :state state + :indicator-value animation-indicator + :card-scale card-scale}] [phone {:animation-value animation-phone}]])}))) diff --git a/src/status_im/ui/screens/keycard/components/style.cljs b/src/status_im/ui/screens/keycard/components/style.cljs index 891a34f1fc..1a0799f6a5 100644 --- a/src/status_im/ui/screens/keycard/components/style.cljs +++ b/src/status_im/ui/screens/keycard/components/style.cljs @@ -1,17 +1,21 @@ (ns status-im.ui.screens.keycard.components.style (:require [quo.design-system.colors :as colors])) -(def wrapper-style {:flex 1 - :align-items :center - :justify-content :center}) +(def wrapper-style + {:flex 1 + :align-items :center + :justify-content :center}) -(def container-style {:flex-direction :column - :align-items :center - :padding-horizontal 40}) +(def container-style + {:flex-direction :column + :align-items :center + :padding-horizontal 40}) -(def helper-text-style {:text-align :center - :color colors/gray - :line-height 22}) +(def helper-text-style + {:text-align :center + :color colors/gray + :line-height 22}) -(def title-style {:text-align :center - :line-height 22}) +(def title-style + {:text-align :center + :line-height 22}) diff --git a/src/status_im/ui/screens/keycard/components/turn_nfc.cljs b/src/status_im/ui/screens/keycard/components/turn_nfc.cljs index 7034a854c8..2acc540c5c 100644 --- a/src/status_im/ui/screens/keycard/components/turn_nfc.cljs +++ b/src/status_im/ui/screens/keycard/components/turn_nfc.cljs @@ -1,25 +1,29 @@ -(ns status-im.ui.screens.keycard.components.turn-nfc - (:require [re-frame.core :as re-frame] +(ns status-im.ui.screens.keycard.components.turn-nfc + (:require [quo.core :as quo] + [quo.design-system.colors :as colors] + [re-frame.core :as re-frame] [status-im.i18n.i18n :as i18n] [status-im.ui.components.icons.icons :as icons] [status-im.ui.components.react :as react] - [quo.design-system.colors :as colors] - [quo.core :as quo] [status-im.ui.screens.keycard.components.style :as styles])) -(defn turn-nfc-on [] +(defn turn-nfc-on + [] [react/view {:style styles/wrapper-style} [react/view {:style styles/container-style} - [icons/icon :main-icons/union-nfc {:color colors/blue - :height 36 - :width 36}] + [icons/icon :main-icons/union-nfc + {:color colors/blue + :height 36 + :width 36}] [react/view {:margin-top 16} [react/text {:style {:typography :title-bold}} (i18n/label :t/turn-nfc-on)]] - [react/view {:margin-top 8 - :margin-bottom 16} - [react/text {:number-of-lines 2 - :style styles/helper-text-style} + [react/view + {:margin-top 8 + :margin-bottom 16} + [react/text + {:number-of-lines 2 + :style styles/helper-text-style} (i18n/label :t/turn-nfc-description)]] [quo/button {:on-press #(re-frame/dispatch [:keycard.onboarding.nfc-on/open-nfc-settings-pressed])} (i18n/label :t/open-nfc-settings)]]]) diff --git a/src/status_im/ui/screens/keycard/frozen_card/view.cljs b/src/status_im/ui/screens/keycard/frozen_card/view.cljs index bf75bd4750..01db6d893c 100644 --- a/src/status_im/ui/screens/keycard/frozen_card/view.cljs +++ b/src/status_im/ui/screens/keycard/frozen_card/view.cljs @@ -1,37 +1,44 @@ (ns status-im.ui.screens.keycard.frozen-card.view (:require-macros [status-im.utils.views :as views]) - (:require [status-im.ui.components.react :as react] + (:require [quo.core :as quo] [quo.design-system.colors :as colors] - [status-im.ui.components.icons.icons :as icons] - [quo.core :as quo] - [status-im.keycard.login :as login] + [re-frame.core :as re-frame] [status-im.i18n.i18n :as i18n] - [re-frame.core :as re-frame])) + [status-im.keycard.login :as login] + [status-im.ui.components.icons.icons :as icons] + [status-im.ui.components.react :as react])) (views/defview frozen-card [{:keys [show-dismiss-button?] :or {show-dismiss-button? true}}] - [react/view {:style (when-not show-dismiss-button? - {:flex 1})} - [react/view {:margin-top 24 - :margin-horizontal 24 - :align-items :center} - [react/view {:background-color colors/blue-light - :width 32 :height 32 - :border-radius 16 - :align-items :center - :justify-content :center} + [react/view + {:style (when-not show-dismiss-button? + {:flex 1})} + [react/view + {:margin-top 24 + :margin-horizontal 24 + :align-items :center} + [react/view + {:background-color colors/blue-light + :width 32 + :height 32 + :border-radius 16 + :align-items :center + :justify-content :center} [icons/icon :main-icons/warning {:color colors/blue}]] - [react/text {:style {:typography :title-bold - :margin-top 16 - :margin-bottom 8}} + [react/text + {:style {:typography :title-bold + :margin-top 16 + :margin-bottom 8}} (i18n/label :t/keycard-is-frozen-title)] - [react/text {:style {:color colors/gray - :text-align :center}} + [react/text + {:style {:color colors/gray + :text-align :center}} (i18n/label :t/keycard-is-frozen-details)]] - [react/view {:margin-bottom 24 - :margin-horizontal 24 - :align-items :center} + [react/view + {:margin-bottom 24 + :margin-horizontal 24 + :align-items :center} [react/view {:style {:margin-top 24}} [quo/button {:on-press #(re-frame/dispatch [::login/reset-pin])} diff --git a/src/status_im/ui/screens/keycard/keycard_interaction.cljs b/src/status_im/ui/screens/keycard/keycard_interaction.cljs index 7cc0feb7b6..24551e96f7 100644 --- a/src/status_im/ui/screens/keycard/keycard_interaction.cljs +++ b/src/status_im/ui/screens/keycard/keycard_interaction.cljs @@ -1,14 +1,13 @@ (ns status-im.ui.screens.keycard.keycard-interaction - (:require [reagent.core :as reagent] + (:require [quo.design-system.colors :as colors] [re-frame.core :as re-frame] + [reagent.core :as reagent] [status-im.i18n.i18n :as i18n] [status-im.ui.components.react :as react] - [status-im.ui.screens.keycard.components.keycard-animation - :refer [animated-circles]] - [quo.design-system.colors :as colors] [status-im.ui.screens.keycard.components.description :as description] - [status-im.ui.screens.keycard.components.turn-nfc :as turn-nfc] - [status-im.ui.screens.keycard.components.style :as styles])) + [status-im.ui.screens.keycard.components.keycard-animation :refer [animated-circles]] + [status-im.ui.screens.keycard.components.style :as styles] + [status-im.ui.screens.keycard.components.turn-nfc :as turn-nfc])) (def state->translations {:init {:title :t/keycard-init-title @@ -24,54 +23,64 @@ :success {:title :t/keycard-success-title :description :t/keycard-success-description}}) -(defn card-sync-flow [] +(defn card-sync-flow + [] (let [state (reagent/atom nil)] (fn [{:keys [on-card-connected connected? on-card-disconnected params]}] (let [translation (or (get-in params [:state-translations @state]) (get state->translations @state))] - [react/view {:style styles/container-style - :height 286} - [react/view {:height 200 - :margin-bottom 20} - [animated-circles {:state state - :connected? connected? - :on-card-disconnected on-card-disconnected - :on-card-connected on-card-connected}]] + [react/view + {:style styles/container-style + :height 286} + [react/view + {:height 200 + :margin-bottom 20} + [animated-circles + {:state state + :connected? connected? + :on-card-disconnected on-card-disconnected + :on-card-connected on-card-connected}]] (when translation [description/animated-description {:title (i18n/label (:title translation)) :description (i18n/label (:description translation))}])])))) -(defn connect-keycard [{:keys [on-connect on-cancel - connected? on-disconnect - params]}] - [react/view {:style {:flex 1 - :align-items :center - :justify-content :center}} +(defn connect-keycard + [{:keys [on-connect on-cancel + connected? on-disconnect + params]}] + [react/view + {:style {:flex 1 + :align-items :center + :justify-content :center}} (when on-cancel [react/touchable-highlight {:on-press on-cancel :style {:position :absolute :top 0 :right 0}} - [react/text {:style {:line-height 22 - :padding-horizontal 16 - :color colors/blue - :text-align :center}} + [react/text + {:style {:line-height 22 + :padding-horizontal 16 + :color colors/blue + :text-align :center}} (i18n/label :t/cancel)]]) (when (:title params) - [react/view {:style {:align-self :flex-start :padding-left 16 :margin-bottom 24 :position :absolute :top 0 :left 0}} + [react/view + {:style + {:align-self :flex-start :padding-left 16 :margin-bottom 24 :position :absolute :top 0 :left 0}} [react/text {:style {:font-size (if (:small-screen? params) 15 17) :font-weight "700"}} (:title params)]]) (when (:header params) [(:header params)]) (if @(re-frame/subscribe [:keycard/nfc-enabled?]) - [card-sync-flow {:connected? connected? - :params (select-keys params [:state-translations]) - :on-card-disconnected - #(re-frame/dispatch [on-disconnect]) - :on-card-connected - #(re-frame/dispatch [on-connect])}] + [card-sync-flow + {:connected? connected? + :params (select-keys params [:state-translations]) + :on-card-disconnected + #(re-frame/dispatch [on-disconnect]) + :on-card-connected + #(re-frame/dispatch [on-connect])}] [turn-nfc/turn-nfc-on]) (when (:footer params) [(:footer params)])]) diff --git a/src/status_im/ui/screens/keycard/onboarding/views.cljs b/src/status_im/ui/screens/keycard/onboarding/views.cljs index b8573e1777..3553ef958e 100644 --- a/src/status_im/ui/screens/keycard/onboarding/views.cljs +++ b/src/status_im/ui/screens/keycard/onboarding/views.cljs @@ -1,193 +1,237 @@ (ns status-im.ui.screens.keycard.onboarding.views - (:require [re-frame.core :as re-frame] - [status-im.keycard.onboarding :as keycard.onboarding] - [status-im.ui.components.toolbar :as bottom-toolbar] + (:require [quo.core :as quo] [quo.design-system.colors :as colors] - [status-im.ui.components.react :as react] - [status-im.ui.components.icons.icons :as icons] + [re-frame.core :as re-frame] + [status-im.constants :as constants] [status-im.i18n.i18n :as i18n] - [status-im.utils.handlers :refer [= (count pairing-code) 1)) -(defn confirm-pairing-code [pairing-code confirm] +(defn confirm-pairing-code + [pairing-code confirm] (= pairing-code confirm)) -(defn change-pairing-code [] +(defn change-pairing-code + [] (let [pairing-code (reagent/atom nil) confirm (reagent/atom nil) show-error (reagent/atom nil) @@ -27,57 +30,64 @@ (re-frame/dispatch [:keycard/change-pairing-code @pairing-code])) (reset! show-error true)))] [rn/keyboard-avoiding-view {:flex 1} - [rn/scroll-view {:style {:flex 1}} - [rn/view {:style {:flex 1 - :justify-content :space-between - :padding-vertical 16 - :padding-horizontal 16}} + [rn/scroll-view {:style {:flex 1}} + [rn/view + {:style {:flex 1 + :justify-content :space-between + :padding-vertical 16 + :padding-horizontal 16}} [rn/view - [quo/text {:weight :bold - :align :center - :size :x-large} + [quo/text + {:weight :bold + :align :center + :size :x-large} (i18n/label :t/change-pairing-title)]] [rn/view [rn/view {:style {:padding 16}} - [quo/text-input {:secure-text-entry true - :auto-capitalize :none - :auto-focus true - :show-cancel false - :accessibility-label :password-input - :placeholder (i18n/label :t/pairing-code-placeholder) - :on-change-text #(reset! pairing-code (security/mask-data %)) - :return-key-type :next - :on-submit-editing #(when valid-pairing-code - (some-> ^js @confirm-ref .focus))}]] - [rn/view {:style {:padding 16 - :opacity (if-not valid-pairing-code 0.33 1)}} - [quo/text-input {:secure-text-entry true - :get-ref #(reset! confirm-ref %) - :auto-capitalize :none - :show-cancel false - :accessibility-label :password-input - :editable valid-pairing-code - :placeholder (i18n/label :t/confirm-pairing-code-placeholder) - :return-key-type :go - :error (when @show-error (i18n/label :t/pairing-code_error1)) - :blur-on-submit true - :on-focus #(reset! show-error false) - :on-submit-editing on-submit - :on-change-text #(do - (reset! confirm (security/mask-data %)) - (cond - (> (count @pairing-code) (count @confirm)) - (reset! show-error false) + [quo/text-input + {:secure-text-entry true + :auto-capitalize :none + :auto-focus true + :show-cancel false + :accessibility-label :password-input + :placeholder (i18n/label :t/pairing-code-placeholder) + :on-change-text #(reset! pairing-code (security/mask-data %)) + :return-key-type :next + :on-submit-editing #(when valid-pairing-code + (some-> ^js @confirm-ref + .focus))}]] + [rn/view + {:style {:padding 16 + :opacity (if-not valid-pairing-code 0.33 1)}} + [quo/text-input + {:secure-text-entry true + :get-ref #(reset! confirm-ref %) + :auto-capitalize :none + :show-cancel false + :accessibility-label :password-input + :editable valid-pairing-code + :placeholder (i18n/label :t/confirm-pairing-code-placeholder) + :return-key-type :go + :error (when @show-error (i18n/label :t/pairing-code_error1)) + :blur-on-submit true + :on-focus #(reset! show-error false) + :on-submit-editing on-submit + :on-change-text #(do + (reset! confirm (security/mask-data %)) + (cond + (> (count @pairing-code) (count @confirm)) + (reset! show-error false) - (not (confirm-pairing-code @pairing-code @confirm)) - (reset! show-error true) + (not (confirm-pairing-code @pairing-code @confirm)) + (reset! show-error true) - :else (reset! show-error false)))}]]] + :else (reset! show-error false)))}]]] [rn/view - [quo/text {:color :secondary - :align :center - :size :small} + [quo/text + {:color :secondary + :align :center + :size :small} (i18n/label :t/change-pairing-description)]]]] [toolbar/toolbar {:show-border? true diff --git a/src/status_im/ui/screens/keycard/pin/styles.cljs b/src/status_im/ui/screens/keycard/pin/styles.cljs index 33669486c7..f41b611699 100644 --- a/src/status_im/ui/screens/keycard/pin/styles.cljs +++ b/src/status_im/ui/screens/keycard/pin/styles.cljs @@ -6,37 +6,42 @@ :flex-direction :column :justify-content :space-between}) -(defn info-container [small-screen?] - {:height 44 - :width "100%" +(defn info-container + [small-screen?] + {:height 44 + :width "100%" :justify-content :center - :margin-top (if small-screen? 14 10)}) + :margin-top (if small-screen? 14 10)}) -(defn error-container [y-translation opacity] - {:left 0 - :right 0 - :align-items :center - :position :absolute - :transform [{:translateY y-translation}] - :opacity opacity +(defn error-container + [y-translation opacity] + {:left 0 + :right 0 + :align-items :center + :position :absolute + :transform [{:translateY y-translation}] + :opacity opacity :justify-content :center}) -(defn error-text [small-screen?] - {:position :absolute +(defn error-text + [small-screen?] + {:position :absolute :color colors/red :font-size (if small-screen? 12 15) :text-align :center}) -(defn retry-container [y-translation opacity] - {:left 0 - :right 0 - :align-items :center - :position :absolute - :transform [{:translateY y-translation}] - :opacity opacity +(defn retry-container + [y-translation opacity] + {:left 0 + :right 0 + :align-items :center + :position :absolute + :transform [{:translateY y-translation}] + :opacity opacity :justify-content :center}) -(defn center-container [title] +(defn center-container + [title] {:flex-direction :column :align-items :center :margin-top (if title 20 5)}) @@ -53,11 +58,12 @@ (def pin-indicator-container {:flex-direction :row :justify-content :space-between - :align-items :center - :height 22 - :margin-top 5}) + :align-items :center + :height 22 + :margin-top 5}) -(defn pin-indicator [pressed? error?] +(defn pin-indicator + [pressed? error?] {:width 8 :height 8 :background-color (if error? @@ -68,7 +74,8 @@ :border-radius 50 :margin-horizontal 5}) -(defn puk-indicator [error?] +(defn puk-indicator + [error?] {:width 8 :height 8 :background-color (if error? @@ -80,13 +87,15 @@ (def numpad-container {:margin-top 18}) -(defn numpad-row-container [small-screen?] +(defn numpad-row-container + [small-screen?] {:flex-direction :row :justify-content :center :align-items :center :margin-vertical (if small-screen? 4 10)}) -(defn numpad-button [small-screen?] +(defn numpad-button + [small-screen?] {:width (if small-screen? 50 64) :margin-horizontal (if small-screen? 10 14) :height (if small-screen? 50 64) @@ -96,12 +105,15 @@ :border-radius (/ (if small-screen? 50 64) 2) :background-color colors/blue-light}) -(defn numpad-delete-button [small-screen?] +(defn numpad-delete-button + [small-screen?] (assoc (numpad-button small-screen?) :background-color colors/white)) -(defn numpad-empty-button [small-screen?] - (assoc (numpad-button small-screen?) :background-color colors/white - :border-color colors/white)) +(defn numpad-empty-button + [small-screen?] + (assoc (numpad-button small-screen?) + :background-color colors/white + :border-color colors/white)) (def numpad-button-text {:font-size 22 diff --git a/src/status_im/ui/screens/keycard/pin/views.cljs b/src/status_im/ui/screens/keycard/pin/views.cljs index eae9ccd481..1568034c7a 100644 --- a/src/status_im/ui/screens/keycard/pin/views.cljs +++ b/src/status_im/ui/screens/keycard/pin/views.cljs @@ -1,35 +1,38 @@ (ns status-im.ui.screens.keycard.pin.views (:require-macros [status-im.utils.views :refer [defview letsubs]]) - (:require [re-frame.core :as re-frame] + (:require [quo.design-system.colors :as colors] + [re-frame.core :as re-frame] [reagent.core :as reagent] - [status-im.ui.components.animation :as animation] [status-im.i18n.i18n :as i18n] - [quo.design-system.colors :as colors] + [status-im.ui.components.animation :as animation] + [status-im.ui.components.checkbox.view :as checkbox] [status-im.ui.components.icons.icons :as icons] [status-im.ui.components.react :as react] [status-im.ui.screens.keycard.pin.styles :as styles] - [status-im.ui.components.checkbox.view :as checkbox] [status-im.utils.platform :as platform])) (def default-pin-retries-number 3) (def default-puk-retries-number 5) -(defn numpad-button [n step enabled? small-screen?] +(defn numpad-button + [n step enabled? small-screen?] [react/touchable-highlight - {:on-press #(when enabled? - (re-frame/dispatch [:keycard.ui/pin-numpad-button-pressed n step])) + {:on-press #(when enabled? + (re-frame/dispatch [:keycard.ui/pin-numpad-button-pressed n step])) :accessibility-label (str "numpad-button-" n)} [react/view (styles/numpad-button small-screen?) [react/text {:style styles/numpad-button-text} n]]]) -(defn numpad-row [[a b c] step enabled? small-screen?] +(defn numpad-row + [[a b c] step enabled? small-screen?] [react/view (styles/numpad-row-container small-screen?) [numpad-button a step enabled? small-screen?] [numpad-button b step enabled? small-screen?] [numpad-button c step enabled? small-screen?]]) -(defn numpad [step enabled? small-screen?] +(defn numpad + [step enabled? small-screen?] [react/view styles/numpad-container [numpad-row [1 2 3] step enabled? small-screen?] [numpad-row [4 5 6] step enabled? small-screen?] @@ -43,7 +46,8 @@ [react/view (styles/numpad-delete-button small-screen?) [icons/icon :main-icons/backspace {:color colors/blue}]]]]]) -(defn pin-indicators [pin error?] +(defn pin-indicators + [pin error?] [react/view styles/pin-indicator-container (map-indexed (fn [i n] @@ -51,25 +55,30 @@ ^{:key i} [react/view (styles/pin-indicator pressed? error?)])) (concat pin (repeat (- 6 (count pin)) nil)))]) -(defn puk-indicators [puk error?] - [react/view {:margin-top 28 - :flex-direction :row - :justify-content :space-between} +(defn puk-indicators + [puk error?] + [react/view + {:margin-top 28 + :flex-direction :row + :justify-content :space-between} (map-indexed (fn [i puk-group] ^{:key i} - [react/view (merge styles/pin-indicator-container - {:margin-top 8 - :margin 12}) + [react/view + (merge styles/pin-indicator-container + {:margin-top 8 + :margin 12}) (map-indexed (fn [j n] (if (number? n) - ^{:key j} [react/text {:style {:font-size 20 - :width 18 - :color (if error? - colors/red - colors/black)}} - n] + ^{:key j} + [react/text + {:style {:font-size 20 + :width 18 + :color (if error? + colors/red + colors/black)}} + n] ^{:key j} [react/view (styles/puk-indicator error?)])) puk-group)]) (partition 4 @@ -77,9 +86,10 @@ (repeat (- 12 (count puk)) nil))))]) -(defn save-password [] +(defn save-password + [] (let [{:keys [save-password?]} @(re-frame/subscribe [:multiaccounts/login]) - auth-method @(re-frame/subscribe [:auth-method])] + auth-method @(re-frame/subscribe [:auth-method])] (when-not (and platform/android? (not auth-method)) [react/view {:style {:flex-direction :row}} @@ -89,8 +99,9 @@ :on-value-change #(re-frame/dispatch [:multiaccounts/save-password %])}] [react/text (i18n/label :t/keycard-dont-ask-card)]]))) -(defn bezier-easing [] - (.bezier ^js animation/easing 0.77, 0.000, 0.175, 1)) +(defn bezier-easing + [] + (.bezier ^js animation/easing 0.77 0.000 0.175 1)) (defn animate-info-in "animation that makes the error message appear for a few seconds, then @@ -100,36 +111,36 @@ (animation/anim-sequence [(animation/parallel [(animation/timing error-opacity - {:toValue 1 - :easing (bezier-easing) - :duration 400 + {:toValue 1 + :easing (bezier-easing) + :duration 400 :useNativeDriver true}) (animation/timing error-y-translation - {:toValue 0 - :easing (bezier-easing) - :duration 400 + {:toValue 0 + :easing (bezier-easing) + :duration 400 :useNativeDriver true})]) (animation/anim-delay 2200) (animation/parallel [(animation/timing error-opacity - {:toValue 0 - :easing (bezier-easing) - :duration 400 + {:toValue 0 + :easing (bezier-easing) + :duration 400 :useNativeDriver true}) (animation/timing error-y-translation - {:toValue 8 - :easing (bezier-easing) - :duration 400 + {:toValue 8 + :easing (bezier-easing) + :duration 400 :useNativeDriver true}) (animation/timing retries-opacity - {:toValue 1 - :easing (bezier-easing) - :duration 400 + {:toValue 1 + :easing (bezier-easing) + :duration 400 :useNativeDriver true}) (animation/timing retries-y-translation - {:toValue 0 - :easing (bezier-easing) - :duration 400 + {:toValue 0 + :easing (bezier-easing) + :duration 400 :useNativeDriver true})])]))) (defn animate-info-out @@ -137,38 +148,38 @@ (animation/start (animation/parallel [(animation/timing retries-opacity - {:toValue 0 - :easing (bezier-easing) - :duration 400 + {:toValue 0 + :easing (bezier-easing) + :duration 400 :useNativeDriver true}) (animation/timing retries-y-translation - {:toValue -8 - :easing (bezier-easing) - :duration 400 + {:toValue -8 + :easing (bezier-easing) + :duration 400 :useNativeDriver true})]))) (defn pin-view [{:keys [retry-counter]}] - (let [error-y-translation (animation/create-value -8) - error-opacity (animation/create-value 0) + (let [error-y-translation (animation/create-value -8) + error-opacity (animation/create-value 0) retries-y-translation (animation/create-value (if retry-counter 0 -8)) - retries-opacity (animation/create-value (if retry-counter 1 0)) - !error? (reagent/atom false)] + retries-opacity (animation/create-value (if retry-counter 1 0)) + !error? (reagent/atom false)] (reagent/create-class {:component-did-update (fn [this [_ previous-props]] - (let [[_ props] (.-argv (.-props ^js this)) + (let [[_ props] (.-argv (.-props ^js this)) previous-status (:status previous-props) - new-status (:status props)] + new-status (:status props)] (case new-status - :error (when (or (nil? previous-status) - (= :verifying previous-status)) - (reset! !error? true) - (animate-info-in error-y-translation - error-opacity - retries-y-translation - retries-opacity) - (js/setTimeout (fn [] (reset! !error? false)) 3000)) + :error (when (or (nil? previous-status) + (= :verifying previous-status)) + (reset! !error? true) + (animate-info-in error-y-translation + error-opacity + retries-y-translation + retries-opacity) + (js/setTimeout (fn [] (reset! !error? false)) 3000)) :verifying (do (animation/set-value error-y-translation -8) (animate-info-out retries-y-translation @@ -179,7 +190,7 @@ retry-counter small-screen? save-password-checkbox?]}] (let [enabled? (and (not= status :verifying) (not @!error?)) - puk? (or (= step :puk) (= step :puk-original) (= step :puk-confirmation))] + puk? (or (= step :puk) (= step :puk-original) (= step :puk-confirmation))] [react/scroll-view [react/view styles/pin-container [react/view (styles/center-container title-label) @@ -187,8 +198,9 @@ [react/text {:style styles/center-title-text} (i18n/label title-label)]) (when description-label - [react/text {:style styles/create-pin-text - :number-of-lines 2} + [react/text + {:style styles/create-pin-text + :number-of-lines 2} (i18n/label description-label)]) (when save-password-checkbox? [save-password]) @@ -197,26 +209,31 @@ [react/animated-view {:style (styles/error-container error-y-translation error-opacity)} [react/text {:style (styles/error-text small-screen?)} (i18n/label error-label)]]) - [react/animated-view {:style (styles/retry-container retries-y-translation retries-opacity)} + [react/animated-view + {:style (styles/retry-container retries-y-translation retries-opacity)} (cond (and retry-counter (= retry-counter 1)) - [react/nested-text {:style {:text-align :center - :color colors/gray}} + [react/nested-text + {:style {:text-align :center + :color colors/gray}} (i18n/label (if puk? :t/pin-one-attempt-blocked-before :t/pin-one-attempt-frozen-before)) - [{:style {:color colors/black + [{:style {:color colors/black :font-weight "700"}} (i18n/label :t/pin-one-attempt)] (i18n/label (if puk? :t/pin-one-attempt-blocked-after :t/pin-one-attempt-frozen-after))] - (and retry-counter (< retry-counter (if puk? - default-puk-retries-number - default-pin-retries-number))) - [react/text {:style {:text-align :center - :color colors/gray}} + (and retry-counter + (< retry-counter + (if puk? + default-puk-retries-number + default-pin-retries-number))) + [react/text + {:style {:text-align :center + :color colors/gray}} (i18n/label :t/pin-retries-left {:number retry-counter})] :else nil)]] @@ -228,41 +245,43 @@ (def pin-retries 3) (def puk-retries 5) -(defview enter-pin [] - (letsubs [pin [:keycard/pin] - step [:keycard/pin-enter-step] - status [:keycard/pin-status] +(defview enter-pin + [] + (letsubs [pin [:keycard/pin] + step [:keycard/pin-enter-step] + status [:keycard/pin-status] pin-retry-counter [:keycard/pin-retry-counter] puk-retry-counter [:keycard/puk-retry-counter] - error-label [:keycard/pin-error-label]] + error-label [:keycard/pin-error-label]] (let [;; TODO(rasom): retarded hack to prevent state mess on opening pin ;; sheet on another tab and returning back to this screen. Should be ;; properly rewritten so that different instances of pin-view do not ;; mess with state unrelated to them. step (or step :current)] - [pin-view {:pin pin - :retry-counter (if (= step :puk) - (when (< puk-retry-counter puk-retries) puk-retry-counter) - (when (< pin-retry-counter pin-retries) pin-retry-counter)) - :title-label (case step - :current :t/current-pin - :login :t/current-pin - :import-multiaccount :t/current-pin - :original :t/create-a-pin - :confirmation :t/repeat-pin - :puk :t/enter-puk-code - :puk-original :t/create-a-puk - :puk-confirmation :t/repeat-puk - :t/current-pin) - :description-label (case step - :current :t/current-pin-description - :sign :t/current-pin-description - :import-multiaccount :t/current-pin-description - :login :t/login-pin-description - :puk :t/enter-puk-code-description - :puk-original :t/new-puk-description - :puk-confirmation :t/new-puk-description - :t/new-pin-description) - :step step - :status status - :error-label error-label}]))) + [pin-view + {:pin pin + :retry-counter (if (= step :puk) + (when (< puk-retry-counter puk-retries) puk-retry-counter) + (when (< pin-retry-counter pin-retries) pin-retry-counter)) + :title-label (case step + :current :t/current-pin + :login :t/current-pin + :import-multiaccount :t/current-pin + :original :t/create-a-pin + :confirmation :t/repeat-pin + :puk :t/enter-puk-code + :puk-original :t/create-a-puk + :puk-confirmation :t/repeat-puk + :t/current-pin) + :description-label (case step + :current :t/current-pin-description + :sign :t/current-pin-description + :import-multiaccount :t/current-pin-description + :login :t/login-pin-description + :puk :t/enter-puk-code-description + :puk-original :t/new-puk-description + :puk-confirmation :t/new-puk-description + :t/new-pin-description) + :step step + :status status + :error-label error-label}]))) diff --git a/src/status_im/ui/screens/keycard/recovery/views.cljs b/src/status_im/ui/screens/keycard/recovery/views.cljs index 4652c043ac..fff853dd40 100644 --- a/src/status_im/ui/screens/keycard/recovery/views.cljs +++ b/src/status_im/ui/screens/keycard/recovery/views.cljs @@ -1,74 +1,88 @@ (ns status-im.ui.screens.keycard.recovery.views - (:require [re-frame.core :as re-frame] - [status-im.keycard.recovery :as keycard.recovery] - [status-im.i18n.i18n :as i18n] - [status-im.react-native.resources :as resources] - [status-im.ui.components.react :as react] - [status-im.ui.components.icons.icons :as icons] + (:require [quo.core :as quo] [quo.design-system.colors :as colors] + [re-frame.core :as re-frame] + [status-im.constants :as constants] + [status-im.i18n.i18n :as i18n] + [status-im.keycard.recovery :as keycard.recovery] + [status-im.react-native.resources :as resources] + [status-im.ui.components.icons.icons :as icons] + [status-im.ui.components.react :as react] [status-im.ui.components.toolbar :as bottom-toolbar] [status-im.ui.components.tooltip.views :as tooltip] [status-im.ui.components.topbar :as topbar] [status-im.ui.screens.keycard.pin.views :as pin.views] [status-im.ui.screens.keycard.styles :as styles] + [status-im.ui.screens.keycard.views :as keycard.views] [status-im.utils.core :as utils.core] [status-im.utils.gfycat.core :as gfy] - [status-im.constants :as constants] - [quo.core :as quo] - [status-im.ui.screens.keycard.views :as keycard.views] [status-im.utils.identicon :as identicon]) (:require-macros [status-im.utils.views :refer [defview letsubs]])) -(defn intro [] +(defn intro + [] [:<> - [react/view {:flex 1 - :flex-direction :column - :justify-content :space-between - :align-items :center} - [react/view {:flex-direction :column - :align-items :center} + [react/view + {:flex 1 + :flex-direction :column + :justify-content :space-between + :align-items :center} + [react/view + {:flex-direction :column + :align-items :center} - [react/view {:margin-top 16 - :width 311} - [react/text {:style {:typography :header - :text-align :center}} + [react/view + {:margin-top 16 + :width 311} + [react/text + {:style {:typography :header + :text-align :center}} (i18n/label :t/keycard-recovery-intro-header)]] - [react/view {:margin-top 16 - :width 311} - [react/text {:style {:font-size 15 - :line-height 22 - :color colors/gray - :text-align :center}} + [react/view + {:margin-top 16 + :width 311} + [react/text + {:style {:font-size 15 + :line-height 22 + :color colors/gray + :text-align :center}} (i18n/label :t/keycard-recovery-intro-text)]] [react/view {:margin-top 33} - [react/touchable-highlight {:on-press #(.openURL ^js react/linking - constants/keycard-integration-link)} - [react/view {:flex-direction :row - :align-items :center - :justify-content :center} - [react/text {:style {:text-align :center - :color colors/blue}} + [react/touchable-highlight + {:on-press #(.openURL ^js react/linking + constants/keycard-integration-link)} + [react/view + {:flex-direction :row + :align-items :center + :justify-content :center} + [react/text + {:style {:text-align :center + :color colors/blue}} (i18n/label :t/learn-more-about-keycard)] - [icons/tiny-icon :tiny-icons/tiny-external {:color colors/blue - :container-style {:margin-left 5}}]]]]] + [icons/tiny-icon :tiny-icons/tiny-external + {:color colors/blue + :container-style {:margin-left 5}}]]]]] - [react/view {:align-items :center - :justify-content :center} - [react/image {:source (resources/get-image :keycard) - :style {:width 144 - :height 114}}]] + [react/view + {:align-items :center + :justify-content :center} + [react/image + {:source (resources/get-image :keycard) + :style {:width 144 + :height 114}}]] [react/view {:margin-bottom 50} [quo/button {:on-press #(re-frame/dispatch [:keycard.recovery.intro.ui/begin-recovery-pressed])} (i18n/label :t/keycard-recovery-intro-button-text)]]]]) -(defview pin [] - (letsubs [pin [:keycard/pin] - status [:keycard/pin-status] - error-label [:keycard/pin-error-label] +(defview pin + [] + (letsubs [pin [:keycard/pin] + status [:keycard/pin-status] + error-label [:keycard/pin-error-label] small-screen? [:dimensions/small-screen?] retry-counter [:keycard/retry-counter]] [react/view styles/container @@ -76,8 +90,9 @@ {:navigation {:on-press #(re-frame/dispatch [::keycard.recovery/cancel-pressed]) :label (i18n/label :t/cancel)} :title (when-not (#{:frozen-card :blocked-card} status) - (i18n/label :t/step-i-of-n {:number 2 - :step 2}))}] + (i18n/label :t/step-i-of-n + {:number 2 + :step 2}))}] (case status :frozen-card [keycard.views/frozen-card] @@ -85,15 +100,18 @@ :blocked-card [keycard.views/blocked-card] - [react/view {:flex 1 - :flex-direction :column - :justify-content :space-between - :align-items :center} - [react/view {:flex-direction :column - :align-items :center} + [react/view + {:flex 1 + :flex-direction :column + :justify-content :space-between + :align-items :center} + [react/view + {:flex-direction :column + :align-items :center} [react/view {:margin-top 16} - [react/text {:style {:typography :header - :text-align :center}} + [react/text + {:style {:typography :header + :text-align :center}} (i18n/label :t/enter-your-code)]]] [pin.views/pin-view {:pin pin @@ -103,138 +121,170 @@ :error-label error-label :step :import-multiaccount}]])])) -(defview pair [] - (letsubs [pair-code [:keycard-pair-code] - error [:keycard-setup-error] +(defview pair + [] + (letsubs [pair-code [:keycard-pair-code] + error [:keycard-setup-error] {:keys [free-pairing-slots]} [:keycard-application-info]] - [react/view {:flex 1 - :flex-direction :column - :justify-content :space-between - :align-items :center} - [react/view {:flex-direction :column - :align-items :center} + [react/view + {:flex 1 + :flex-direction :column + :justify-content :space-between + :align-items :center} + [react/view + {:flex-direction :column + :align-items :center} [react/view {:margin-top 16} - [react/text {:style {:typography :header - :text-align :center}} + [react/text + {:style {:typography :header + :text-align :center}} (i18n/label :t/enter-pair-code)]] - [react/view {:margin-top 16 - :width "85%" - :align-items :center} - [react/text {:style {:color colors/gray - :text-align :center}} + [react/view + {:margin-top 16 + :width "85%" + :align-items :center} + [react/text + {:style {:color colors/gray + :text-align :center}} (i18n/label :t/enter-pair-code-description)]] (when free-pairing-slots - [react/view {:align-items :center - :margin-top 20} - [react/text {:style {:text-align :center - :color (if (> 3 free-pairing-slots) colors/red colors/gray)}} + [react/view + {:align-items :center + :margin-top 20} + [react/text + {:style {:text-align :center + :color (if (> 3 free-pairing-slots) colors/red colors/gray)}} (i18n/label :t/keycard-free-pairing-slots {:n free-pairing-slots})]])] [react/view - [react/view {:padding 16 - :justify-content :center - :margin-bottom 100} + [react/view + {:padding 16 + :justify-content :center + :margin-bottom 100} [quo/text-input {:on-change-text #(re-frame/dispatch [:keycard.onboarding.pair.ui/input-changed %]) :auto-focus true :on-submit-editing #(re-frame/dispatch [:keycard.onboarding.pair.ui/input-submitted]) :placeholder (i18n/label :t/pair-code-placeholder) :monospace true}]] - [react/view {:margin-top 5 - :width 250} + [react/view + {:margin-top 5 + :width 250} [tooltip/tooltip error]]] [bottom-toolbar/toolbar {:right - [quo/button {:on-press #(re-frame/dispatch [:keycard.onboarding.pair.ui/next-pressed]) - :disabled (empty? pair-code) - :type :secondary - :after :main-icon/next} + [quo/button + {:on-press #(re-frame/dispatch [:keycard.onboarding.pair.ui/next-pressed]) + :disabled (empty? pair-code) + :type :secondary + :after :main-icon/next} (i18n/label :t/pair-card)]}]])) -(defview success [] - (letsubs [address [:keycard-multiaccount-wallet-address] +(defview success + [] + (letsubs [address [:keycard-multiaccount-wallet-address] whisper-public-key [:keycard-multiaccount-whisper-public-key]] [react/view styles/container [topbar/topbar {:navigation :none}] - [react/view {:flex 1 - :flex-direction :column - :justify-content :space-between - :align-items :center} - [react/view {:flex-direction :column - :align-items :center} + [react/view + {:flex 1 + :flex-direction :column + :justify-content :space-between + :align-items :center} + [react/view + {:flex-direction :column + :align-items :center} [react/view {:margin-top 16} - [react/text {:style {:typography :header - :text-align :center}} + [react/text + {:style {:typography :header + :text-align :center}} (i18n/label :t/keycard-recovery-success-header)]]] - [react/view {:flex-direction :column - :flex 1 - :justify-content :center - :align-items :center} - [react/view {:margin-horizontal 16 - :flex-direction :column} - [react/view {:justify-content :center - :align-items :center - :margin-bottom 11} - [react/image {:source {:uri (identicon/identicon whisper-public-key)} - :style {:width 61 - :height 61 - :border-radius 30 - :border-width 1 - :border-color colors/black-transparent}}]] - [react/text {:style {:text-align :center - :color colors/black - :font-weight "500"} - :number-of-lines 1 - :ellipsize-mode :middle} + [react/view + {:flex-direction :column + :flex 1 + :justify-content :center + :align-items :center} + [react/view + {:margin-horizontal 16 + :flex-direction :column} + [react/view + {:justify-content :center + :align-items :center + :margin-bottom 11} + [react/image + {:source {:uri (identicon/identicon whisper-public-key)} + :style {:width 61 + :height 61 + :border-radius 30 + :border-width 1 + :border-color colors/black-transparent}}]] + [react/text + {:style {:text-align :center + :color colors/black + :font-weight "500"} + :number-of-lines 1 + :ellipsize-mode :middle} (gfy/generate-gfy whisper-public-key)] - [quo/text {:style {:margin-top 4} - :monospace true - :align :center - :color :secondary - :number-of-lines 1 - :ellipsize-mode :middle} + [quo/text + {:style {:margin-top 4} + :monospace true + :align :center + :color :secondary + :number-of-lines 1 + :ellipsize-mode :middle} (utils.core/truncate-str address 14 true)]]] [react/view {:margin-bottom 50} [quo/button {:on-press #(re-frame/dispatch [:keycard.recovery.success/finish-pressed])} (i18n/label :t/finish)]]]])) -(defview no-key [] +(defview no-key + [] (letsubs [card-state [:keycard-card-state]] [react/view styles/container [topbar/topbar {:navigation :none}] - [react/view {:flex 1 - :flex-direction :column - :justify-content :space-between - :align-items :center} - [react/view {:flex-direction :column - :align-items :center} + [react/view + {:flex 1 + :flex-direction :column + :justify-content :space-between + :align-items :center} + [react/view + {:flex-direction :column + :align-items :center} [react/view {:margin-top 16} - [react/text {:style {:typography :header - :text-align :center}} + [react/text + {:style {:typography :header + :text-align :center}} (i18n/label :t/keycard-recovery-no-key-header)]] - [react/view {:margin-top 16 - :width "85%" - :align-items :center} - [react/text {:style {:color colors/gray - :text-align :center}} + [react/view + {:margin-top 16 + :width "85%" + :align-items :center} + [react/text + {:style {:color colors/gray + :text-align :center}} (i18n/label :t/keycard-recovery-no-key-text)]]] - [react/view {:flex-direction :column - :flex 1 - :justify-content :center - :align-items :center} - [react/view {:margin-horizontal 16 - :flex-direction :column} - [react/view {:align-items :center - :justify-content :center} + [react/view + {:flex-direction :column + :flex 1 + :justify-content :center + :align-items :center} + [react/view + {:margin-horizontal 16 + :flex-direction :column} + [react/view + {:align-items :center + :justify-content :center} (if (= card-state :init) - [react/image {:source (resources/get-image :keycard) - :style {:width 144 - :height 114}}] - [react/image {:source (resources/get-image :keycard-empty) - :style {:width 165 - :height 110}}])]]] + [react/image + {:source (resources/get-image :keycard) + :style {:width 144 + :height 114}}] + [react/image + {:source (resources/get-image :keycard-empty) + :style {:width 165 + :height 110}}])]]] [react/view {:margin-bottom 50} [quo/button - {:test-id :generate-new-key + {:test-id :generate-new-key :on-press #(re-frame/dispatch [:keycard.recovery.no-key.ui/generate-key-pressed])} (i18n/label :t/generate-new-key)] [quo/button diff --git a/src/status_im/ui/screens/keycard/settings/views.cljs b/src/status_im/ui/screens/keycard/settings/views.cljs index ad50906bc8..e075edaddc 100644 --- a/src/status_im/ui/screens/keycard/settings/views.cljs +++ b/src/status_im/ui/screens/keycard/settings/views.cljs @@ -1,72 +1,87 @@ (ns status-im.ui.screens.keycard.settings.views (:require-macros [status-im.utils.views :refer [defview letsubs]]) - (:require [re-frame.core :as re-frame] - [status-im.i18n.i18n :as i18n] - [status-im.ui.components.react :as react] - [status-im.react-native.resources :as resources] + (:require [quo.core :as quo] [quo.design-system.colors :as colors] - [quo.core :as quo] + [re-frame.core :as re-frame] [status-im.constants :as constants] + [status-im.i18n.i18n :as i18n] + [status-im.react-native.resources :as resources] + [status-im.ui.components.react :as react] [status-im.ui.screens.keycard.views :as keycard.views])) -(defn- activity-indicator [loading?] +(defn- activity-indicator + [loading?] (when loading? [react/view {:margin-top 35} - [react/activity-indicator {:animating true - :size :large}]])) + [react/activity-indicator + {:animating true + :size :large}]])) -(defn- reset-card-next-button [disabled?] - [react/view {:margin-right 6 - :margin-bottom 8} +(defn- reset-card-next-button + [disabled?] + [react/view + {:margin-right 6 + :margin-bottom 8} [quo/button ;; TODO: Should have label?: - {:on-press #(re-frame/dispatch [:keycard-settings.ui/reset-card-next-button-pressed]) - :disabled disabled? - :type :secondary - :after :main-icon/next}]]) + {:on-press #(re-frame/dispatch [:keycard-settings.ui/reset-card-next-button-pressed]) + :disabled disabled? + :type :secondary + :after :main-icon/next}]]) -(defview reset-card [] +(defview reset-card + [] (letsubs [disabled? [:keycard-reset-card-disabled?]] [:<> - [react/view {:margin-top 71 - :flex 1 - :align-items :center} - [react/image {:source (resources/get-image :warning-sign) - :style {:width 160 - :height 160}}]] - [react/view {:flex 1 - :padding-horizontal 30} - [react/text {:style {:typography :header - :text-align :center}} + [react/view + {:margin-top 71 + :flex 1 + :align-items :center} + [react/image + {:source (resources/get-image :warning-sign) + :style {:width 160 + :height 160}}]] + [react/view + {:flex 1 + :padding-horizontal 30} + [react/text + {:style {:typography :header + :text-align :center}} (i18n/label :t/reset-card-description)] [activity-indicator disabled?]] - [react/view {:flex-direction :row - :justify-content :space-between - :align-items :center - :width "100%" - :height 68 - :border-top-width 1 - :border-color colors/black-transparent} + [react/view + {:flex-direction :row + :justify-content :space-between + :align-items :center + :width "100%" + :height 68 + :border-top-width 1 + :border-color colors/black-transparent} [react/view {:flex 1}] [reset-card-next-button disabled?]]])) -(defn- card-blocked [] +(defn- card-blocked + [] [react/view - [react/text {:style {:font-size 20 - :text-align :center - :padding-horizontal 40}} + [react/text + {:style {:font-size 20 + :text-align :center + :padding-horizontal 40}} (i18n/label :t/keycard-blocked)]]) -(defview keycard-settings [] - (letsubs [paired-on [:keycard-paired-on] +(defview keycard-settings + [] + (letsubs [paired-on [:keycard-paired-on] puk-retry-counter [:keycard/puk-retry-counter] - pairing [:keycard-multiaccount-pairing]] + pairing [:keycard-multiaccount-pairing]] [react/scroll-view {:flex 1} - [react/view {:margin-top 47 - :align-items :center} - [react/image {:source (resources/get-image :keycard-card) - :style {:width 255 - :height 160}}] + [react/view + {:margin-top 47 + :align-items :center} + [react/image + {:source (resources/get-image :keycard-card) + :style {:width 255 + :height 160}}] (when paired-on [react/view {:margin-top 27} [react/text @@ -75,42 +90,52 @@ (if (zero? puk-retry-counter) [card-blocked] [:<> - [quo/list-item {:icon :main-icons/help - :size :small - :title (i18n/label :t/help-capitalized) - :on-press #(.openURL ^js react/linking - constants/faq-keycard)}] + [quo/list-item + {:icon :main-icons/help + :size :small + :title (i18n/label :t/help-capitalized) + :on-press #(.openURL ^js react/linking + constants/faq-keycard)}] (when pairing [:<> - [quo/list-item {:icon :main-icons/add - :size :small - :title (i18n/label :t/change-pin) - :on-press #(re-frame/dispatch [:keycard-settings.ui/change-credentials-pressed :pin])}] - [quo/list-item {:icon :main-icons/security - :size :small - :title (i18n/label :t/change-puk) - :accessibility-label "change-puk" - :on-press #(re-frame/dispatch [:keycard-settings.ui/change-credentials-pressed :puk])}] - [quo/list-item {:icon :main-icons/password - :size :small - :title (i18n/label :t/change-pairing) - :accessibility-label "change-pairing" - :on-press #(re-frame/dispatch [:keycard-settings.ui/change-credentials-pressed :pairing])}] - [quo/list-item {:icon :main-icons/keycard - :size :small - :title (i18n/label :t/keycard-backup) - :accessibility-label "create-backup-keycard" - :on-press #(re-frame/dispatch [:keycard-settings.ui/backup-card-pressed :backup-card])}] + [quo/list-item + {:icon :main-icons/add + :size :small + :title (i18n/label :t/change-pin) + :on-press #(re-frame/dispatch [:keycard-settings.ui/change-credentials-pressed :pin])}] + [quo/list-item + {:icon :main-icons/security + :size :small + :title (i18n/label :t/change-puk) + :accessibility-label "change-puk" + :on-press #(re-frame/dispatch [:keycard-settings.ui/change-credentials-pressed + :puk])}] + [quo/list-item + {:icon :main-icons/password + :size :small + :title (i18n/label :t/change-pairing) + :accessibility-label "change-pairing" + :on-press #(re-frame/dispatch [:keycard-settings.ui/change-credentials-pressed + :pairing])}] + [quo/list-item + {:icon :main-icons/keycard + :size :small + :title (i18n/label :t/keycard-backup) + :accessibility-label "create-backup-keycard" + :on-press #(re-frame/dispatch [:keycard-settings.ui/backup-card-pressed + :backup-card])}] ;; TODO(rasom): uncomment this when unpairing will be enabled ;; https://github.com/status-im/status-mobile/issues/9227 - #_[quo/list-item {:icon :main-icons/close - :size :small - :title (i18n/label :t/unpair-card) - :on-press #(re-frame/dispatch [:keycard-settings.ui/unpair-card-pressed])}]])])]])) + #_[quo/list-item + {:icon :main-icons/close + :size :small + :title (i18n/label :t/unpair-card) + :on-press #(re-frame/dispatch [:keycard-settings.ui/unpair-card-pressed])}]])])]])) -(defn reset-pin [] +(defn reset-pin + [] [keycard.views/login-pin {:back-button-handler :navigate-back :hide-login-actions? true - :default-enter-step :reset}]) + :default-enter-step :reset}]) diff --git a/src/status_im/ui/screens/keycard/styles.cljs b/src/status_im/ui/screens/keycard/styles.cljs index 84b4d01da5..d82390bb39 100644 --- a/src/status_im/ui/screens/keycard/styles.cljs +++ b/src/status_im/ui/screens/keycard/styles.cljs @@ -1,5 +1,5 @@ (ns status-im.ui.screens.keycard.styles) (def container - {:flex 1 - :justify-content :space-between}) + {:flex 1 + :justify-content :space-between}) diff --git a/src/status_im/ui/screens/keycard/views.cljs b/src/status_im/ui/screens/keycard/views.cljs index 701ff1fdcf..ae4ce6a590 100644 --- a/src/status_im/ui/screens/keycard/views.cljs +++ b/src/status_im/ui/screens/keycard/views.cljs @@ -1,172 +1,208 @@ (ns status-im.ui.screens.keycard.views - (:require [re-frame.core :as re-frame] - [reagent.core :as reagent] - [clojure.string :as string] - [status-im.i18n.i18n :as i18n] - [status-im.multiaccounts.core :as multiaccounts] - [status-im.react-native.resources :as resources] - [quo.design-system.colors :as colors] - [status-im.ui.components.icons.icons :as icons] + (:require [clojure.string :as string] [quo.core :as quo] - [status-im.ui.components.toolbar :as toolbar] + [quo.design-system.colors :as colors] + [re-frame.core :as re-frame] + [reagent.core :as reagent] + [status-im.bottom-sheet.core :as bottom-sheet] + [status-im.constants :as constants] + [status-im.i18n.i18n :as i18n] + [status-im.keycard.login :as keycard.login] + [status-im.multiaccounts.core :as multiaccounts] + [status-im.multiaccounts.create.core :as multiaccounts.create] + [status-im.react-native.resources :as resources] + [status-im.ui.components.icons.icons :as icons] [status-im.ui.components.react :as react] + [status-im.ui.components.toolbar :as toolbar] [status-im.ui.components.topbar :as topbar] [status-im.ui.screens.chat.photos :as photos] + [status-im.ui.screens.keycard.frozen-card.view :as frozen-card.view] [status-im.ui.screens.keycard.pin.views :as pin.views] [status-im.ui.screens.keycard.styles :as styles] - [status-im.constants :as constants] - [status-im.keycard.login :as keycard.login] [status-im.utils.fx :as fx] - [status-im.ui.screens.keycard.frozen-card.view :as frozen-card.view] - [status-im.multiaccounts.create.core :as multiaccounts.create] - [status-im.bottom-sheet.core :as bottom-sheet] [status-im2.navigation.events :as navigation]) (:require-macros [status-im.utils.views :refer [defview letsubs]])) ;; NOTE(Ferossgp): Seems like it should be in popover -(defn blank [] - [react/view {:flex 1 - :justify-content :center - :align-items :center - :background-color colors/gray-transparent-40} - [react/view {:background-color colors/white - :height 433 - :width "85%" - :border-radius 16 - :flex-direction :column - :justify-content :space-between - :align-items :center} - [react/view {:margin-top 32 - :padding-horizontal 34} - [react/text {:style {:typography :title-bold - :text-align :center}} +(defn blank + [] + [react/view + {:flex 1 + :justify-content :center + :align-items :center + :background-color colors/gray-transparent-40} + [react/view + {:background-color colors/white + :height 433 + :width "85%" + :border-radius 16 + :flex-direction :column + :justify-content :space-between + :align-items :center} + [react/view + {:margin-top 32 + :padding-horizontal 34} + [react/text + {:style {:typography :title-bold + :text-align :center}} (i18n/label :t/blank-keycard-title)] [react/view {:margin-top 16} - [react/text {:style {:color colors/gray - :line-height 22 - :text-align :center}} + [react/text + {:style {:color colors/gray + :line-height 22 + :text-align :center}} (i18n/label :t/blank-keycard-text)]]] [react/view - [react/image {:source (resources/get-image :keycard) - :resize-mode :center - :style {:width 144 - :height 114}}]] + [react/image + {:source (resources/get-image :keycard) + :resize-mode :center + :style {:width 144 + :height 114}}]] [react/view {:margin-bottom 32} [quo/button {:on-press #(re-frame/dispatch [:navigate-back])} (i18n/label :t/ok-got-it)]]]]) ;; NOTE(Ferossgp): Seems like it should be in popover -(defn wrong [] - [react/view {:flex 1 - :justify-content :center - :align-items :center - :background-color colors/gray-transparent-40} - [react/view {:background-color colors/white - :height 413 - :width "85%" - :border-radius 16 - :flex-direction :column - :justify-content :space-between - :align-items :center} - [react/view {:margin-top 32 - :padding-horizontal 34} - [react/text {:style {:typography :title-bold - :text-align :center}} +(defn wrong + [] + [react/view + {:flex 1 + :justify-content :center + :align-items :center + :background-color colors/gray-transparent-40} + [react/view + {:background-color colors/white + :height 413 + :width "85%" + :border-radius 16 + :flex-direction :column + :justify-content :space-between + :align-items :center} + [react/view + {:margin-top 32 + :padding-horizontal 34} + [react/text + {:style {:typography :title-bold + :text-align :center}} (i18n/label :t/wrong-keycard-title)] [react/view {:margin-top 16} - [react/text {:style {:color colors/gray - :line-height 22 - :text-align :center}} + [react/text + {:style {:color colors/gray + :line-height 22 + :text-align :center}} (i18n/label :t/wrong-keycard-text)]]] [react/view - [react/image {:source (resources/get-image :keycard-wrong) - :style {:width 255 - :height 124}}]] + [react/image + {:source (resources/get-image :keycard-wrong) + :style {:width 255 + :height 124}}]] [react/view {:margin-bottom 32} [quo/button {:on-press #(re-frame/dispatch [:navigate-back])} (i18n/label :t/ok-got-it)]]]]) -(defn unpaired [] - [react/view {:flex 1 - :justify-content :center - :align-items :center - :background-color colors/gray-transparent-40} - [react/view {:background-color colors/white - :height 433 - :width "85%" - :border-radius 16 - :flex-direction :column - :justify-content :space-between - :align-items :center} - [react/view {:margin-top 32 - :padding-horizontal 34} - [react/text {:style {:typography :title-bold - :text-align :center}} +(defn unpaired + [] + [react/view + {:flex 1 + :justify-content :center + :align-items :center + :background-color colors/gray-transparent-40} + [react/view + {:background-color colors/white + :height 433 + :width "85%" + :border-radius 16 + :flex-direction :column + :justify-content :space-between + :align-items :center} + [react/view + {:margin-top 32 + :padding-horizontal 34} + [react/text + {:style {:typography :title-bold + :text-align :center}} (i18n/label :t/unpaired-keycard-title)] [react/view {:margin-top 16} - [react/text {:style {:color colors/gray - :line-height 22 - :text-align :center}} + [react/text + {:style {:color colors/gray + :line-height 22 + :text-align :center}} (i18n/label :t/unpaired-keycard-text)]]] [react/view - [react/image {:source (resources/get-image :keycard-wrong) - :style {:width 255 - :height 124}}]] - [react/view {:margin-bottom 32 - :flex-direction :column - :align-items :center} + [react/image + {:source (resources/get-image :keycard-wrong) + :style {:width 255 + :height 124}}]] + [react/view + {:margin-bottom 32 + :flex-direction :column + :align-items :center} [quo/button {:on-press #(re-frame/dispatch [:keycard.login.ui/pair-card-pressed])} (i18n/label :t/pair-this-card)] [react/view {:margin-top 27} - [quo/button {:type :secondary - :on-press #(re-frame/dispatch [:navigate-back])} + [quo/button + {:type :secondary + :on-press #(re-frame/dispatch [:navigate-back])} (i18n/label :t/dismiss)]]]]]) ;; NOTE(Ferossgp): Seems like it should be in popover -(defn not-keycard [] - [react/view {:flex 1 - :justify-content :center - :align-items :center - :background-color colors/gray-transparent-40} - [react/view {:background-color colors/white - :height 453 - :width "85%" - :border-radius 16 - :flex-direction :column - :justify-content :space-between - :align-items :center} +(defn not-keycard + [] + [react/view + {:flex 1 + :justify-content :center + :align-items :center + :background-color colors/gray-transparent-40} + [react/view + {:background-color colors/white + :height 453 + :width "85%" + :border-radius 16 + :flex-direction :column + :justify-content :space-between + :align-items :center} [react/view {:margin-top 32} - [react/text {:style {:typography :title-bold - :text-align :center}} + [react/text + {:style {:typography :title-bold + :text-align :center}} (i18n/label :t/not-keycard-title)] - [react/view {:margin-top 16 - :padding-horizontal 38} - [react/text {:style {:color colors/gray - :line-height 22 - :text-align :center}} + [react/view + {:margin-top 16 + :padding-horizontal 38} + [react/text + {:style {:color colors/gray + :line-height 22 + :text-align :center}} (i18n/label :t/not-keycard-text)]]] - [react/view {:margin-top 16 - :align-items :center} - [react/image {:source (resources/get-image :not-keycard) - :style {:width 144 - :height 120}}] + [react/view + {:margin-top 16 + :align-items :center} + [react/image + {:source (resources/get-image :not-keycard) + :style {:width 144 + :height 120}}] [react/view {:margin-top 40} - [react/touchable-highlight {:on-press #(.openURL ^js react/linking - constants/keycard-integration-link)} - [react/view {:flex-direction :row - :align-items :center - :justify-content :center} - [react/text {:style {:text-align :center - :color colors/blue}} + [react/touchable-highlight + {:on-press #(.openURL ^js react/linking + constants/keycard-integration-link)} + [react/view + {:flex-direction :row + :align-items :center + :justify-content :center} + [react/text + {:style {:text-align :center + :color colors/blue}} (i18n/label :t/learn-more-about-keycard)] - [icons/tiny-icon :tiny-icons/tiny-external {:color colors/blue - :container-style {:margin-left 5}}]]]]] + [icons/tiny-icon :tiny-icons/tiny-external + {:color colors/blue + :container-style {:margin-left 5}}]]]]] [react/view {:margin-bottom 32} [quo/button {:on-press #(re-frame/dispatch [:navigate-back])} (i18n/label :t/ok-got-it)]]]]) -(defn photo [_ _] +(defn photo + [_ _] (reagent/create-class {:should-component-update (fn [_ [_ old-account] [_ new-account]] @@ -180,7 +216,8 @@ [photos/photo (multiaccounts/displayed-photo account) {:size (if small-screen? 45 61)}])})) -(defn access-is-reset [{:keys [hide-login-actions?]}] +(defn access-is-reset + [{:keys [hide-login-actions?]}] [react/view {:style {:flex 1 :align-items :center}} @@ -207,39 +244,48 @@ [react/view {:style {:width 260 :margin-bottom 15}} - [react/view {:align-items :center - :padding-horizontal 32} - [quo/button {:on-press #(re-frame/dispatch - [::keycard.login/login-after-reset])} + [react/view + {:align-items :center + :padding-horizontal 32} + [quo/button + {:on-press #(re-frame/dispatch + [::keycard.login/login-after-reset])} (i18n/label :t/open)]]])]) -(defn frozen-card [] +(defn frozen-card + [] [frozen-card.view/frozen-card {:show-dismiss-button? false}]) -(defn blocked-card [{:keys [show-dismiss-button?]}] - [react/view {:style (when-not show-dismiss-button? - {:flex 1})} - [react/view {:margin-top 24 - :margin-horizontal 24 - :align-items :center} - [react/view {:background-color colors/red-transparent-10 - :width 32 - :height 32 - :border-radius 16 - :align-items :center - :justify-content :center} +(defn blocked-card + [{:keys [show-dismiss-button?]}] + [react/view + {:style (when-not show-dismiss-button? + {:flex 1})} + [react/view + {:margin-top 24 + :margin-horizontal 24 + :align-items :center} + [react/view + {:background-color colors/red-transparent-10 + :width 32 + :height 32 + :border-radius 16 + :align-items :center + :justify-content :center} [icons/icon :main-icons/cancel {:color colors/red :width 20 :height 20}]] - [react/text {:style {:typography :title-bold - :margin-top 16 - :margin-bottom 8}} + [react/text + {:style {:typography :title-bold + :margin-top 16 + :margin-bottom 8}} (i18n/label :t/keycard-is-blocked-title)] - [react/text {:style {:color colors/gray - :text-align :center}} + [react/text + {:style {:color colors/gray + :text-align :center}} (i18n/label :t/keycard-is-blocked-details)] [react/text "\n"] [react/nested-text @@ -251,28 +297,31 @@ {:on-press #(re-frame/dispatch [:keycard-settings.ui/recovery-card-pressed false])} (i18n/label :t/keycard-is-frozen-factory-reset)]] (when show-dismiss-button? - [react/view {:margin-top 24 - :margin-bottom 24} + [react/view + {:margin-top 24 + :margin-bottom 24} [quo/button {:on-press #(re-frame/dispatch [::keycard.login/frozen-keycard-popover-dismissed]) :type :secondary} (i18n/label :t/dismiss)]])]]) -(defn blocked-card-popover [] +(defn blocked-card-popover + [] [blocked-card {:show-dismiss-button? true}]) -(defview login-pin [{:keys [back-button-handler - hide-login-actions? - default-enter-step] - :or {default-enter-step :login}}] - (letsubs [pin [:keycard/pin] - enter-step [:keycard/pin-enter-step] - status [:keycard/pin-status] - error-label [:keycard/pin-error-label] +(defview login-pin + [{:keys [back-button-handler + hide-login-actions? + default-enter-step] + :or {default-enter-step :login}}] + (letsubs [pin [:keycard/pin] + enter-step [:keycard/pin-enter-step] + status [:keycard/pin-status] + error-label [:keycard/pin-error-label] login-multiaccount [:multiaccounts/login] - multiaccount [:multiaccount] - small-screen? [:dimensions/small-screen?] - retry-counter [:keycard/retry-counter]] + multiaccount [:multiaccount] + small-screen? [:dimensions/small-screen?] + retry-counter [:keycard/retry-counter]] (let [{:keys [name] :as account} (or login-multiaccount multiaccount) ;; TODO(rasom): this hack fixes state mess when more then two ;; pin-view instances are used at the same time. Should be properly @@ -283,67 +332,83 @@ (merge (when-not hide-login-actions? {:right-accessories [{:icon :main-icons/more - :on-press #(re-frame/dispatch [:keycard.login.pin.ui/more-icon-pressed])}]}) - {:title (cond - (#{:reset :reset-confirmation} enter-step) - (i18n/label :t/keycard-reset-passcode) + :on-press #(re-frame/dispatch + [:keycard.login.pin.ui/more-icon-pressed])}]}) + {:title (cond + (#{:reset :reset-confirmation} enter-step) + (i18n/label :t/keycard-reset-passcode) - (and (= :puk enter-step) - (not= :blocked-card status)) - (i18n/label :t/enter-puk-code)) + (and (= :puk enter-step) + (not= :blocked-card status)) + (i18n/label :t/enter-puk-code)) :navigation {:on-press #(re-frame/dispatch [(or back-button-handler :keycard.login.pin.ui/cancel-pressed)])}} (when (#{:reset :reset-confirmation} enter-step) - {:subtitle (i18n/label :t/keycard-enter-new-passcode {:step (if (= :reset enter-step) 1 2)})}))] + {:subtitle (i18n/label :t/keycard-enter-new-passcode + {:step (if (= :reset enter-step) 1 2)})}))] [react/scroll-view {:style {:flex 1}} - [react/view {:flex 1 - :flex-direction :column - :justify-content :space-between - :align-items :center} - [react/view {:flex-direction :column - :justify-content :center - :align-items :center - :height 140} - [react/view {:margin-horizontal 16 - :flex-direction :column} - [react/view {:justify-content :center - :align-items :center - :flex-direction :row} - [react/view {:width (if small-screen? 50 69) - :height (if small-screen? 50 69) - :justify-content :center - :align-items :center} + [react/view + {:flex 1 + :flex-direction :column + :justify-content :space-between + :align-items :center} + [react/view + {:flex-direction :column + :justify-content :center + :align-items :center + :height 140} + [react/view + {:margin-horizontal 16 + :flex-direction :column} + [react/view + {:justify-content :center + :align-items :center + :flex-direction :row} + [react/view + {:width (if small-screen? 50 69) + :height (if small-screen? 50 69) + :justify-content :center + :align-items :center} [photo account small-screen?] - [react/view {:justify-content :center - :align-items :center - :width (if small-screen? 18 24) - :height (if small-screen? 18 24) - :border-radius (if small-screen? 18 24) - :position :absolute - :right 0 - :bottom 0 - :background-color colors/white - :border-width 1 - :border-color colors/black-transparent} - [react/image {:source (resources/get-image :keycard-key) - :style {:width (if small-screen? 6 8) - :height (if small-screen? 11 14)}}]]]] - [react/text {:style {:text-align :center - :margin-top (if small-screen? 8 12) - :color colors/black - :font-weight "500"} - :number-of-lines 1 - :ellipsize-mode :middle} + [react/view + {:justify-content :center + :align-items :center + :width (if small-screen? 18 24) + :height (if small-screen? 18 24) + :border-radius (if small-screen? 18 24) + :position :absolute + :right 0 + :bottom 0 + :background-color colors/white + :border-width 1 + :border-color colors/black-transparent} + [react/image + {:source (resources/get-image :keycard-key) + :style {:width (if small-screen? 6 8) + :height (if small-screen? 11 14)}}]]]] + [react/text + {:style {:text-align :center + :margin-top (if small-screen? 8 12) + :color colors/black + :font-weight "500"} + :number-of-lines 1 + :ellipsize-mode :middle} name]]] - [react/touchable-highlight {:on-press #(re-frame/dispatch [:keycard-settings.ui/recovery-card-pressed (boolean login-multiaccount)])} - [react/view {:flex-direction :row - :align-items :center - :justify-content :center} - [react/text {:style {:text-align :center - :margin-bottom (if small-screen? 8 12) - :color colors/blue}} - (string/lower-case (i18n/label (if login-multiaccount :t/keycard-recover :t/keycard-is-frozen-factory-reset)))]]] + [react/touchable-highlight + {:on-press #(re-frame/dispatch [:keycard-settings.ui/recovery-card-pressed + (boolean login-multiaccount)])} + [react/view + {:flex-direction :row + :align-items :center + :justify-content :center} + [react/text + {:style {:text-align :center + :margin-bottom (if small-screen? 8 12) + :color colors/blue}} + (string/lower-case (i18n/label (if login-multiaccount + :t/keycard-recover + :t/keycard-is-frozen-factory-reset)))]]] (cond (= :after-unblocking status) [access-is-reset @@ -367,11 +432,13 @@ #{:reset :reset-confirmation :puk} enter-step))}]) (if hide-login-actions? - [react/view {:flex-direction :row - :height 32}] + [react/view + {:flex-direction :row + :height 32}] [toolbar/toolbar {:center [quo/button - {:on-press #(re-frame/dispatch [:multiaccounts.recover.ui/recover-multiaccount-button-pressed]) + {:on-press #(re-frame/dispatch + [:multiaccounts.recover.ui/recover-multiaccount-button-pressed]) :type :secondary} (i18n/label :t/recover-key)]}])]]]))) @@ -383,7 +450,8 @@ (bottom-sheet/hide-bottom-sheet) (navigation/navigate-to-cofx :get-your-keys nil))) -(defn- more-sheet-content [] +(defn- more-sheet-content + [] [react/view {:flex 1} [quo/list-item {:theme :accent diff --git a/src/status_im/ui/screens/link_previews_settings/views.cljs b/src/status_im/ui/screens/link_previews_settings/views.cljs index 90f8a3beee..4fdb1ef12f 100644 --- a/src/status_im/ui/screens/link_previews_settings/views.cljs +++ b/src/status_im/ui/screens/link_previews_settings/views.cljs @@ -1,15 +1,16 @@ (ns status-im.ui.screens.link-previews-settings.views (:require-macros [status-im.utils.views :as views]) - (:require [re-frame.core :as re-frame] + (:require [quo.core :as quo] + [re-frame.core :as re-frame] + [status-im.chat.models.link-preview :as link-preview] [status-im.i18n.i18n :as i18n] - [status-im.ui.components.react :as react] - [status-im.ui.components.list.views :as list] - [quo.core :as quo] [status-im.react-native.resources :as resources] - [status-im.ui.screens.link-previews-settings.styles :as styles] - [status-im.chat.models.link-preview :as link-preview])) + [status-im.ui.components.list.views :as list] + [status-im.ui.components.react :as react] + [status-im.ui.screens.link-previews-settings.styles :as styles])) -(defn prepare-urls-items-data [link-previews-enabled-sites] +(defn prepare-urls-items-data + [link-previews-enabled-sites] (fn [{:keys [title address]}] (let [enabled? (contains? link-previews-enabled-sites title)] {:title title @@ -17,35 +18,40 @@ :size :small :accessory :switch :active (contains? link-previews-enabled-sites title) - :on-press #(re-frame/dispatch - [::link-preview/enable title ((complement boolean) enabled?)])}))) + :on-press #(re-frame/dispatch + [::link-preview/enable title ((complement boolean) enabled?)])}))) -(views/defview link-previews-settings [] - (views/letsubs [link-previews-whitelist [:link-previews-whitelist] +(views/defview link-previews-settings + [] + (views/letsubs [link-previews-whitelist [:link-previews-whitelist] link-previews-enabled-sites [:link-preview/enabled-sites]] (let [all-enabled (= (count link-previews-whitelist) (count link-previews-enabled-sites))] [:<> - [react/image {:source (resources/get-theme-image :unfurl) - :style styles/link-preview-settings-image}] + [react/image + {:source (resources/get-theme-image :unfurl) + :style styles/link-preview-settings-image}] [quo/text {:style {:margin 16}} (i18n/label :t/you-can-choose-preview-websites)] - [quo/separator {:style {:margin-vertical 8}}] + [quo/separator {:style {:margin-vertical 8}}] [react/view styles/whitelist-container [quo/list-header (i18n/label :t/websites)] (when (> (count link-previews-whitelist) 1) - [quo/button {:on-press #(re-frame/dispatch [::link-preview/enable-all - link-previews-whitelist - (not all-enabled)]) - :type :secondary - :style styles/enable-all} + [quo/button + {:on-press #(re-frame/dispatch [::link-preview/enable-all + link-previews-whitelist + (not all-enabled)]) + :type :secondary + :style styles/enable-all} (if all-enabled (i18n/label :t/disable-all) (i18n/label :t/enable-all))])] [list/flat-list - {:data (vec (map (prepare-urls-items-data link-previews-enabled-sites) link-previews-whitelist)) + {:data (vec (map (prepare-urls-items-data link-previews-enabled-sites) + link-previews-whitelist)) :key-fn (fn [_ i] (str i)) :render-fn quo/list-item - :footer [quo/text {:color :secondary - :style {:margin 16}} (i18n/label :t/previewing-may-share-metadata)]}]]))) + :footer [quo/text + {:color :secondary + :style {:margin 16}} (i18n/label :t/previewing-may-share-metadata)]}]]))) diff --git a/src/status_im/ui/screens/log_level_settings/styles.cljs b/src/status_im/ui/screens/log_level_settings/styles.cljs index bea3f0b26a..55b643a848 100644 --- a/src/status_im/ui/screens/log_level_settings/styles.cljs +++ b/src/status_im/ui/screens/log_level_settings/styles.cljs @@ -3,7 +3,7 @@ [status-im.utils.styles :as styles])) (def wrapper - {:flex 1}) + {:flex 1}) (def log-level-item-inner {:padding-horizontal 16}) @@ -18,7 +18,8 @@ (def log-level-item-name-text {:typography :title}) -(defn log-level-icon-container [current?] +(defn log-level-icon-container + [current?] {:width 40 :height 40 :border-radius 20 @@ -28,6 +29,7 @@ :align-items :center :justify-content :center}) -(defn log-level-icon [current?] +(defn log-level-icon + [current?] (hash-map :color (if current? colors/white-persist colors/gray))) diff --git a/src/status_im/ui/screens/log_level_settings/views.cljs b/src/status_im/ui/screens/log_level_settings/views.cljs index 9482aa55d5..d18c93489d 100644 --- a/src/status_im/ui/screens/log_level_settings/views.cljs +++ b/src/status_im/ui/screens/log_level_settings/views.cljs @@ -6,18 +6,21 @@ [status-im.ui.screens.log-level-settings.styles :as styles]) (:require-macros [status-im.utils.views :as views])) -(defn- log-level-icon [current?] +(defn- log-level-icon + [current?] [react/view (styles/log-level-icon-container current?) [icons/icon :main-icons/mailserver (styles/log-level-icon current?)]]) -(defn change-log-level [log-level] +(defn change-log-level + [log-level] (re-frame/dispatch [:log-level.ui/log-level-selected log-level])) -(defn render-row [{:keys [name value] :as log-level} _ _ current-log-level] +(defn render-row + [{:keys [name value] :as log-level} _ _ current-log-level] (let [current? (= value current-log-level)] [react/touchable-highlight - {:on-press #(change-log-level log-level) + {:on-press #(change-log-level log-level) :accessibility-label :log-level-item} [react/view styles/log-level-item [log-level-icon current?] @@ -26,23 +29,25 @@ name]]]])) (def log-levels - [{:name "DISABLED" + [{:name "DISABLED" :value ""} - {:name "ERROR" + {:name "ERROR" :value "ERROR"} - {:name "WARN" + {:name "WARN" :value "WARN"} - {:name "INFO" + {:name "INFO" :value "INFO"} - {:name "DEBUG" + {:name "DEBUG" :value "DEBUG"} - {:name "TRACE" + {:name "TRACE" :value "TRACE"}]) -(views/defview log-level-settings [] +(views/defview log-level-settings + [] (views/letsubs [current-log-level [:log-level/current-log-level]] - [list/flat-list {:data log-levels - :default-separator? false - :key-fn :name - :render-data current-log-level - :render-fn render-row}])) + [list/flat-list + {:data log-levels + :default-separator? false + :key-fn :name + :render-data current-log-level + :render-fn render-row}])) diff --git a/src/status_im/ui/screens/mobile_network_settings/sheets.cljs b/src/status_im/ui/screens/mobile_network_settings/sheets.cljs index c8164918d6..53a08222fd 100644 --- a/src/status_im/ui/screens/mobile_network_settings/sheets.cljs +++ b/src/status_im/ui/screens/mobile_network_settings/sheets.cljs @@ -1,29 +1,33 @@ (ns status-im.ui.screens.mobile-network-settings.sheets (:require-macros [status-im.utils.views :as views]) - (:require [status-im.ui.components.react :as react] - [status-im.ui.screens.mobile-network-settings.sheets-styles :as styles] - [status-im.ui.components.checkbox.view :as checkbox] - [status-im.i18n.i18n :as i18n] + (:require [quo.core :as quo] [re-frame.core :as re-frame] - [quo.core :as quo])) + [status-im.i18n.i18n :as i18n] + [status-im.ui.components.checkbox.view :as checkbox] + [status-im.ui.components.react :as react] + [status-im.ui.screens.mobile-network-settings.sheets-styles :as styles])) -(defn title [label] +(defn title + [label] [react/view {:style styles/title} [react/text {:style styles/title-text} (i18n/label label)]]) -(defn details [label] +(defn details + [label] [react/view {:style styles/details} [react/text {:style styles/details-text} (i18n/label label)]]) -(defn separator [] +(defn separator + [] [react/view {:style styles/separator}]) -(defn go-to-settings [] +(defn go-to-settings + [] [react/view {:style styles/go-to-settings-container} [react/text @@ -31,10 +35,11 @@ :on-press #(re-frame/dispatch [:mobile-network/navigate-to-settings])} (i18n/label :mobile-network-go-to-settings)]]) -(views/defview checkbox [] +(views/defview checkbox + [] (views/letsubs [checked? [:mobile-network/remember-choice?]] [react/view - {:style styles/checkbox-line-container + {:style styles/checkbox-line-container :accessibility-label "remember-choice"} [checkbox/checkbox {:checked? checked? @@ -45,7 +50,8 @@ {:style styles/checkbox-text-container} [react/text (i18n/label :mobile-network-sheet-remember-choice)]]])) -(defn settings [] +(defn settings + [] [react/view {:style styles/settings-container} [react/nested-text @@ -55,47 +61,51 @@ [{:style styles/settings-link} (str " " (i18n/label :mobile-network-sheet-settings))]]]) -(defn hide-sheet-and-dispatch [event] +(defn hide-sheet-and-dispatch + [event] (re-frame/dispatch [:bottom-sheet/hide]) (re-frame/dispatch event)) -(views/defview settings-sheet [] +(views/defview settings-sheet + [] [react/view {:flex 1} [react/view {:align-items :center} [title :mobile-syncing-sheet-title] [details :mobile-syncing-sheet-details]] [quo/list-item - {:theme :accent + {:theme :accent :accessibility-label "mobile-network-continue-syncing" - :title (i18n/label :t/mobile-network-continue-syncing) - :subtitle (i18n/label :t/mobile-network-continue-syncing-details) - :subtitle-max-lines 2 - :icon :main-icons/network - :on-press #(hide-sheet-and-dispatch [:mobile-network/continue-syncing])}] + :title (i18n/label :t/mobile-network-continue-syncing) + :subtitle (i18n/label :t/mobile-network-continue-syncing-details) + :subtitle-max-lines 2 + :icon :main-icons/network + :on-press #(hide-sheet-and-dispatch [:mobile-network/continue-syncing])}] [quo/list-item - {:theme :negative + {:theme :negative :accessibility-label "mobile-network-stop-syncing" - :title (i18n/label :t/mobile-network-stop-syncing) - :subtitle (i18n/label :t/mobile-network-stop-syncing-details) - :icon :main-icons/cancel - :on-press #(hide-sheet-and-dispatch [:mobile-network/stop-syncing])}] + :title (i18n/label :t/mobile-network-stop-syncing) + :subtitle (i18n/label :t/mobile-network-stop-syncing-details) + :icon :main-icons/cancel + :on-press #(hide-sheet-and-dispatch [:mobile-network/stop-syncing])}] [separator] - [react/view {:flex 1 - :align-self :stretch} + [react/view + {:flex 1 + :align-self :stretch} [checkbox] [settings]]]) -(views/defview offline-sheet [] +(views/defview offline-sheet + [] [react/view {:flex 1} [react/view {:align-items :center} [title :t/mobile-network-sheet-offline] [details :t/mobile-network-sheet-offline-details]] [quo/list-item - {:theme :accent - :title (i18n/label :t/mobile-network-start-syncing) - :subtitle (i18n/label :t/mobile-network-continue-syncing-details) + {:theme :accent + :title (i18n/label :t/mobile-network-start-syncing) + :subtitle (i18n/label :t/mobile-network-continue-syncing-details) :subtitle-max-lines 2 - :icon :main-icons/network - :on-press #(re-frame/dispatch [:mobile-network/continue-syncing])}] + :icon :main-icons/network + :on-press #(re-frame/dispatch [:mobile-network/continue-syncing])}] [separator] [go-to-settings]]) diff --git a/src/status_im/ui/screens/mobile_network_settings/sheets_styles.cljs b/src/status_im/ui/screens/mobile_network_settings/sheets_styles.cljs index 770de8150b..9d18c6f1bc 100644 --- a/src/status_im/ui/screens/mobile_network_settings/sheets_styles.cljs +++ b/src/status_im/ui/screens/mobile_network_settings/sheets_styles.cljs @@ -16,8 +16,8 @@ :margin-bottom 10}) (def details-text - {:color colors/gray - :text-align :center}) + {:color colors/gray + :text-align :center}) (def separator {:background-color colors/gray-lighter diff --git a/src/status_im/ui/screens/mobile_network_settings/style.cljs b/src/status_im/ui/screens/mobile_network_settings/style.cljs index de03b51975..70866515f4 100644 --- a/src/status_im/ui/screens/mobile_network_settings/style.cljs +++ b/src/status_im/ui/screens/mobile_network_settings/style.cljs @@ -2,21 +2,22 @@ (:require [quo.design-system.colors :as colors])) (def container - {:flex 1}) + {:flex 1}) (def switch-container - {:height 52}) + {:height 52}) (def details - {:margin-right 16 - :margin-left 16 - :margin-top 8 - :margin-bottom 16}) + {:margin-right 16 + :margin-left 16 + :margin-top 8 + :margin-bottom 16}) (def use-mobile-data-text {:color colors/gray}) -(defn settings-separator [] +(defn settings-separator + [] {:align-self :stretch :height 1 :background-color colors/gray-lighter}) diff --git a/src/status_im/ui/screens/mobile_network_settings/view.cljs b/src/status_im/ui/screens/mobile_network_settings/view.cljs index 54b49f8555..9a5321b7f6 100644 --- a/src/status_im/ui/screens/mobile_network_settings/view.cljs +++ b/src/status_im/ui/screens/mobile_network_settings/view.cljs @@ -1,30 +1,34 @@ (ns status-im.ui.screens.mobile-network-settings.view (:require-macros [status-im.utils.views :as views]) - (:require [status-im.ui.components.react :as react] - [status-im.ui.screens.mobile-network-settings.style :as styles] + (:require [re-frame.core :as re-frame] [status-im.i18n.i18n :as i18n] - [re-frame.core :as re-frame] status-im.mobile-sync-settings.core - [status-im.ui.screens.profile.components.views :as profile.components] - [status-im.ui.screens.mobile-network-settings.sheets :as sheets])) + [status-im.ui.components.react :as react] + [status-im.ui.screens.mobile-network-settings.sheets :as sheets] + [status-im.ui.screens.mobile-network-settings.style :as styles] + [status-im.ui.screens.profile.components.views :as profile.components])) -(defn hide-sheet-and-dispatch [event] +(defn hide-sheet-and-dispatch + [event] (re-frame/dispatch [:bottom-sheet/hide]) (re-frame/dispatch event)) -(defn settings-separator [] +(defn settings-separator + [] [react/view {:style (styles/settings-separator)}]) ;; TODO(Ferossgp): To refactor, uses outdated components -(views/defview mobile-network-settings [] +(views/defview mobile-network-settings + [] (views/letsubs [{:keys [syncing-on-mobile-network? remember-syncing-choice?]} [:multiaccount]] [:<> - [react/view {:style styles/switch-container - :accessibility-label "mobile-network-use-mobile"} + [react/view + {:style styles/switch-container + :accessibility-label "mobile-network-use-mobile"} [profile.components/settings-switch-item {:label-kw :t/mobile-network-use-mobile :value syncing-on-mobile-network? @@ -32,8 +36,9 @@ [react/view {:style styles/details} [react/text {:style styles/use-mobile-data-text} (i18n/label :t/mobile-network-use-mobile-data)]] - [react/view {:style styles/switch-container - :accessibility-label "mobile-network-ask-me"} + [react/view + {:style styles/switch-container + :accessibility-label "mobile-network-ask-me"} [profile.components/settings-switch-item {:label-kw :t/mobile-network-ask-me :value (not remember-syncing-choice?) @@ -42,9 +47,9 @@ [react/view {:style styles/defaults-container} [react/text - {:style styles/defaults + {:style styles/defaults :accessibility-label "restore-defaults" - :on-press #(hide-sheet-and-dispatch [:mobile-network/restore-defaults])} + :on-press #(hide-sheet-and-dispatch [:mobile-network/restore-defaults])} (i18n/label :t/restore-defaults)]]])) (def settings-sheet diff --git a/src/status_im/ui/screens/multiaccounts/key_storage/views.cljs b/src/status_im/ui/screens/multiaccounts/key_storage/views.cljs index dbe3f9adb7..ccc1cbae9d 100644 --- a/src/status_im/ui/screens/multiaccounts/key_storage/views.cljs +++ b/src/status_im/ui/screens/multiaccounts/key_storage/views.cljs @@ -1,159 +1,196 @@ (ns status-im.ui.screens.multiaccounts.key-storage.views (:require-macros [status-im.utils.views :refer [defview letsubs]]) (:require [quo.core :as quo] + [quo.design-system.colors :as colors] [re-frame.core :as re-frame] [re-frame.db] [status-im.i18n.i18n :as i18n] [status-im.multiaccounts.core :as multiaccounts] [status-im.multiaccounts.key-storage.core :as multiaccounts.key-storage] [status-im.react-native.resources :as resources] - [quo.design-system.colors :as colors] + [status-im.ui.components.accordion :as accordion] + [status-im.ui.components.chat-icon.screen :as chat-icon.screen] [status-im.ui.components.icons.icons :as icons] [status-im.ui.components.react :as react] - [status-im.ui.components.chat-icon.screen :as chat-icon.screen] - [status-im.ui.components.topbar :as topbar] [status-im.ui.components.toolbar :as toolbar] - [status-im.ui.components.accordion :as accordion] - [status-im.ui.screens.multiaccounts.views :as multiaccounts.views] + [status-im.ui.components.topbar :as topbar] [status-im.ui.screens.multiaccounts.key-storage.styles :as styles] + [status-im.ui.screens.multiaccounts.views :as multiaccounts.views] [utils.security.core])) -(defn local-topbar [subtitle action] - [topbar/topbar (merge {:title (i18n/label :t/key-managment) - :subtitle subtitle} - (when action {:navigation {:on-press #(re-frame/dispatch [action])}}))]) +(defn local-topbar + [subtitle action] + [topbar/topbar + (merge {:title (i18n/label :t/key-managment) + :subtitle subtitle} + (when action {:navigation {:on-press #(re-frame/dispatch [action])}}))]) (defonce accordian-data - [{:id :type - :label (i18n/label :t/type) - :value (i18n/label :t/master-account)} - {:id :back-up - :label (i18n/label :t/back-up) - :value (i18n/label :t/recovery-phrase)} - {:id :storage - :label (i18n/label :t/storage) - :value (i18n/label :t/key-on-device)}]) + [{:id :type + :label (i18n/label :t/type) + :value (i18n/label :t/master-account)} + {:id :back-up + :label (i18n/label :t/back-up) + :value (i18n/label :t/recovery-phrase)} + {:id :storage + :label (i18n/label :t/storage) + :value (i18n/label :t/key-on-device)}]) -(defn accordion-content [] - [react/view {:padding-horizontal 16 - :flex-direction :row} - [react/view {:flex-shrink 0 - :margin-right 20} +(defn accordion-content + [] + [react/view + {:padding-horizontal 16 + :flex-direction :row} + [react/view + {:flex-shrink 0 + :margin-right 20} (for [{:keys [id label]} accordian-data] ^{:key (str "left-" id)} - [react/text {:style {:color colors/gray - :padding-vertical 8}} label])] + [react/text + {:style {:color colors/gray + :padding-vertical 8}} label])] [react/view {:flex 1} (for [{:keys [id value]} accordian-data] ^{:key (str "right-" id)} - [react/text {:flex 1 - :flex-wrap :wrap - :style {:padding-vertical 8}} + [react/text + {:flex 1 + :flex-wrap :wrap + :style {:padding-vertical 8}} value])]]) ;; Component to render Key and Storage management screen -(defview actions-base [{:keys [next-title next-event]}] - (letsubs [{:keys [name] :as multiaccount} [:multiaccounts/login] +(defview actions-base + [{:keys [next-title next-event]}] + (letsubs [{:keys [name] :as multiaccount} [:multiaccounts/login] {:keys [move-keystore-checked? reset-db-checked?]} [:multiaccounts/key-storage]] [react/view {:flex 1} [local-topbar (i18n/label :t/choose-actions)] - [accordion/section {:title name - :icon [chat-icon.screen/contact-icon-contacts-tab - (multiaccounts/displayed-photo multiaccount)] - :content [accordion-content]}] - [react/view {:flex 1 - :flex-direction :column - :justify-content :space-between} + [accordion/section + {:title name + :icon [chat-icon.screen/contact-icon-contacts-tab + (multiaccounts/displayed-photo multiaccount)] + :content [accordion-content]}] + [react/view + {:flex 1 + :flex-direction :column + :justify-content :space-between} [react/view [quo/list-header (i18n/label :t/actions)] - [quo/list-item {:title (i18n/label :t/move-keystore-file) - :subtitle (i18n/label :t/select-new-location-for-keys) - :subtitle-max-lines 4 - :accessibility-label :move-keystore-file - :accessory :checkbox - :active move-keystore-checked? - :on-press #(re-frame/dispatch [::multiaccounts.key-storage/move-keystore-checked (not move-keystore-checked?)])}] - [quo/list-item {:title (i18n/label :t/reset-database) - :subtitle (i18n/label :t/reset-database-warning) - :subtitle-max-lines 4 - :active reset-db-checked? - :disabled (not move-keystore-checked?) - :on-press #(re-frame/dispatch [::multiaccounts.key-storage/reset-db-checked (not reset-db-checked?)]) - :accessory :checkbox}]] + [quo/list-item + {:title (i18n/label :t/move-keystore-file) + :subtitle (i18n/label :t/select-new-location-for-keys) + :subtitle-max-lines 4 + :accessibility-label :move-keystore-file + :accessory :checkbox + :active move-keystore-checked? + :on-press #(re-frame/dispatch [::multiaccounts.key-storage/move-keystore-checked + (not move-keystore-checked?)])}] + [quo/list-item + {:title (i18n/label :t/reset-database) + :subtitle (i18n/label :t/reset-database-warning) + :subtitle-max-lines 4 + :active reset-db-checked? + :disabled (not move-keystore-checked?) + :on-press #(re-frame/dispatch [::multiaccounts.key-storage/reset-db-checked + (not reset-db-checked?)]) + :accessory :checkbox}]] (when (and next-title next-event) - [toolbar/toolbar {:show-border? true - :right [quo/button - {:type :secondary - :disabled (not move-keystore-checked?) - :on-press #(re-frame/dispatch next-event) - :after :main-icons/next} - next-title]}])]])) + [toolbar/toolbar + {:show-border? true + :right [quo/button + {:type :secondary + :disabled (not move-keystore-checked?) + :on-press #(re-frame/dispatch next-event) + :after :main-icons/next} + next-title]}])]])) (defn actions-not-logged-in "To be used when the flow is accessed before login, will enter seed phrase next" [] - [actions-base {:next-title (i18n/label :t/enter-seed-phrase) - :next-event [::multiaccounts.key-storage/enter-seed-pressed]}]) + [actions-base + {:next-title (i18n/label :t/enter-seed-phrase) + :next-event [::multiaccounts.key-storage/enter-seed-pressed]}]) (defn actions-logged-in "To be used when the flow is accessed from profile, will choose storage next" [] - [actions-base {:next-title (i18n/label :t/choose-storage) - :next-event [::multiaccounts.key-storage/choose-storage-pressed]}]) + [actions-base + {:next-title (i18n/label :t/choose-storage) + :next-event [::multiaccounts.key-storage/choose-storage-pressed]}]) -(defview seed-phrase [] +(defview seed-phrase + [] (letsubs [{:keys [seed-word-count seed-shape-invalid?]} [:multiaccounts/key-storage] - {:keys [creating-backup?]} [:keycard]] - [react/keyboard-avoiding-view {:style {:flex 1} - :ignore-offset true} + {:keys [creating-backup?]} [:keycard]] + [react/keyboard-avoiding-view + {:style {:flex 1} + :ignore-offset true} [local-topbar (i18n/label :t/enter-seed-phrase) ::multiaccounts.key-storage/navigate-back] [multiaccounts.views/seed-phrase-input {:on-change-event [::multiaccounts.key-storage/seed-phrase-input-changed] :seed-word-count seed-word-count :seed-shape-invalid? seed-shape-invalid?}] - [react/text {:style {:color colors/gray - :font-size 14 - :margin-bottom 8 - :text-align :center}} + [react/text + {:style {:color colors/gray + :font-size 14 + :margin-bottom 8 + :text-align :center}} (i18n/label :t/multiaccounts-recover-enter-phrase-text)] - [toolbar/toolbar {:show-border? true - :right [quo/button - {:type :secondary - :disabled (or seed-shape-invalid? - (nil? seed-shape-invalid?)) - :on-press #(re-frame/dispatch [::multiaccounts.key-storage/choose-storage-pressed]) - :after :main-icons/next} - (i18n/label (if creating-backup? :t/next :t/choose-storage))]}]])) + [toolbar/toolbar + {:show-border? true + :right [quo/button + {:type :secondary + :disabled (or seed-shape-invalid? + (nil? seed-shape-invalid?)) + :on-press #(re-frame/dispatch + [::multiaccounts.key-storage/choose-storage-pressed]) + :after :main-icons/next} + (i18n/label (if creating-backup? :t/next :t/choose-storage))]}]])) -(defn keycard-upsell-banner [] - [react/view {:background-color (if (colors/dark?) "#2C5955" "#DDF8F4") - :border-radius 16 :padding-left 12 :padding-right 50 :margin 16} - [react/touchable-highlight {:on-press #(do - (re-frame/dispatch [:hide-keycard-banner]) - (.openURL ^js react/linking "https://get-keycard.status.im/"))} - [react/view {:padding-vertical 8 - :flex-direction :row} - [react/image {:source (resources/get-theme-image :keycard) - :resize-mode :contain - :style {:width 48 - :height 48}}] - [react/view {:flex 1 - :margin-left 12} - [react/text {:style {:font-size 20 - :font-weight "700"}} +(defn keycard-upsell-banner + [] + [react/view + {:background-color (if (colors/dark?) "#2C5955" "#DDF8F4") + :border-radius 16 + :padding-left 12 + :padding-right 50 + :margin 16} + [react/touchable-highlight + {:on-press #(do + (re-frame/dispatch [:hide-keycard-banner]) + (.openURL ^js react/linking "https://get-keycard.status.im/"))} + [react/view + {:padding-vertical 8 + :flex-direction :row} + [react/image + {:source (resources/get-theme-image :keycard) + :resize-mode :contain + :style {:width 48 + :height 48}}] + [react/view + {:flex 1 + :margin-left 12} + [react/text + {:style {:font-size 20 + :font-weight "700"}} (i18n/label :t/get-a-keycard)] [react/text {:style {:color (colors/alpha colors/text 0.8)}} (i18n/label :t/keycard-upsell-subtitle)]]]] [react/touchable-highlight - {:style {:position :absolute :right 0 :top 0 - :align-items :center :justify-content :center :margin 2} + {:style {:position :absolute + :right 0 + :top 0 + :align-items :center + :justify-content :center + :margin 2} :on-press #(re-frame/dispatch [:hide-keycard-banner]) :accessibility-label :hide-home-button} [icons/icon :main-icons/close-circle {:color colors/gray}]]]) -(defview storage [] +(defview storage + [] (letsubs [{:keys [keycard-storage-selected?]} [:multiaccounts/key-storage]] [react/view {:flex 1} @@ -163,138 +200,170 @@ (i18n/label :t/choose-new-location-for-keystore)]] [react/view [quo/list-header (i18n/label :t/current)] - [quo/list-item {:title (i18n/label :t/this-device) - :text-size :base - :icon :main-icons/mobile - :disabled true}] + [quo/list-item + {:title (i18n/label :t/this-device) + :text-size :base + :icon :main-icons/mobile + :disabled true}] [quo/list-header (i18n/label :t/new)] - [quo/list-item {:title (i18n/label :t/keycard) - :subtitle (i18n/label :t/empty-keycard-required) - :subtitle-max-lines 4 - :icon :main-icons/keycard - :active keycard-storage-selected? - :on-press #(re-frame/dispatch [::multiaccounts.key-storage/keycard-storage-pressed (not keycard-storage-selected?)]) - :accessory :radio}]] - [react/view {:flex 1 - :justify-content :flex-end} + [quo/list-item + {:title (i18n/label :t/keycard) + :subtitle (i18n/label :t/empty-keycard-required) + :subtitle-max-lines 4 + :icon :main-icons/keycard + :active keycard-storage-selected? + :on-press #(re-frame/dispatch [::multiaccounts.key-storage/keycard-storage-pressed + (not keycard-storage-selected?)]) + :accessory :radio}]] + [react/view + {:flex 1 + :justify-content :flex-end} (when-not keycard-storage-selected? [keycard-upsell-banner]) - [toolbar/toolbar {:show-border? true - :right [quo/button - {:type :secondary - :disabled (not keycard-storage-selected?) - :on-press #(re-frame/dispatch [::multiaccounts.key-storage/storage-selected])} - (i18n/label :t/confirm)]}]]])) + [toolbar/toolbar + {:show-border? true + :right [quo/button + {:type :secondary + :disabled (not keycard-storage-selected?) + :on-press #(re-frame/dispatch [::multiaccounts.key-storage/storage-selected])} + (i18n/label :t/confirm)]}]]])) -(defview migrate-account-password-view [] +(defview migrate-account-password-view + [] (letsubs [{:keys [migration-password-error migration-password-valid?]} [:keycard]] [react/view {:flex 1} [react/view styles/header [react/text {:style {:typography :title-bold}} (i18n/label :t/enter-password)] [react/view {:padding-horizontal 24} - [quo/button {:type :secondary - :on-press #(re-frame/dispatch [::multiaccounts.key-storage/skip-password-pressed])} + [quo/button + {:type :secondary + :on-press #(re-frame/dispatch [::multiaccounts.key-storage/skip-password-pressed])} (i18n/label :t/skip)]]] [quo/separator] [react/view {:padding-horizontal 16 :padding-vertical 12} - [react/text {:style {:margin-top 6 :margin-bottom 12 :color colors/gray}} (i18n/label :t/enter-password-migration-prompt)] + [react/text {:style {:margin-top 6 :margin-bottom 12 :color colors/gray}} + (i18n/label :t/enter-password-migration-prompt)] [quo/text-input {:secure-text-entry true :placeholder (i18n/label :t/current-password) - :on-change-text #(re-frame/dispatch [::multiaccounts.key-storage/password-changed (utils.security.core/mask-data %)]) + :on-change-text #(re-frame/dispatch [::multiaccounts.key-storage/password-changed + (utils.security.core/mask-data %)]) :accessibility-label :enter-password-input :auto-capitalize :none :error migration-password-error :show-cancel false}]] [react/view {:padding-horizontal 16 :padding-vertical 12} - [quo/button {:on-press #(re-frame/dispatch [::multiaccounts.key-storage/verify-password]) - :disabled (not migration-password-valid?)} + [quo/button + {:on-press #(re-frame/dispatch [::multiaccounts.key-storage/verify-password]) + :disabled (not migration-password-valid?)} (i18n/label :t/confirm)]]])) (def migrate-account-password {:content migrate-account-password-view}) -(defview seed-key-uid-mismatch-popover [] - (letsubs [{:keys [name]} [:multiaccounts/login] +(defview seed-key-uid-mismatch-popover + [] + (letsubs [{:keys [name]} [:multiaccounts/login] {logged-in-name :name} [:multiaccount]] - [react/view {:margin-top 24 - :margin-horizontal 24 - :align-items :center} - [react/view {:width 32 - :height 32 - :border-radius 16 - :align-items :center - :justify-content :center} + [react/view + {:margin-top 24 + :margin-horizontal 24 + :align-items :center} + [react/view + {:width 32 + :height 32 + :border-radius 16 + :align-items :center + :justify-content :center} [icons/icon :main-icons/warning {:color colors/blue}]] - [react/text {:style {:typography :title-bold - :margin-top 8 - :margin-bottom 24}} + [react/text + {:style {:typography :title-bold + :margin-top 8 + :margin-bottom 24}} (i18n/label :t/seed-key-uid-mismatch)] [react/view styles/popover-body-container [react/view - [react/text {:style (into styles/popover-text - {:margin-bottom 16})} + [react/text + {:style (into styles/popover-text + {:margin-bottom 16})} (i18n/label :t/seed-key-uid-mismatch-desc-1 {:multiaccount-name (or name logged-in-name)})] [react/text {:style styles/popover-text} (i18n/label :t/seed-key-uid-mismatch-desc-2)]]] - [react/view {:margin-vertical 24 - :align-items :center} - [quo/button {:on-press #(re-frame/dispatch [:hide-popover]) - :type :secondary} + [react/view + {:margin-vertical 24 + :align-items :center} + [quo/button + {:on-press #(re-frame/dispatch [:hide-popover]) + :type :secondary} (i18n/label :t/try-again)]]])) -(defview transfer-multiaccount-warning-popover [] - [react/view {:margin-top 24 - :margin-horizontal 24 - :align-items :center} - [react/view {:width 32 - :height 32 - :border-radius 16 - :align-items :center - :justify-content :center} +(defview transfer-multiaccount-warning-popover + [] + [react/view + {:margin-top 24 + :margin-horizontal 24 + :align-items :center} + [react/view + {:width 32 + :height 32 + :border-radius 16 + :align-items :center + :justify-content :center} [icons/icon :main-icons/tiny-warning-background {:color colors/red}]] [react/text {:style styles/popover-title} (i18n/label :t/move-keystore-file-to-keycard)] [react/view styles/popover-body-container [react/text {:style styles/popover-text} (i18n/label :t/database-reset-warning)]] - [react/view {:margin-vertical 24 - :align-items :center} - [quo/button {:on-press #(re-frame/dispatch [::multiaccounts.key-storage/delete-multiaccount-and-init-keycard-onboarding]) - :accessibility-label :move-and-reset-button - :type :primary - :theme :negative} + [react/view + {:margin-vertical 24 + :align-items :center} + [quo/button + {:on-press #(re-frame/dispatch + [::multiaccounts.key-storage/delete-multiaccount-and-init-keycard-onboarding]) + :accessibility-label :move-and-reset-button + :type :primary + :theme :negative} (i18n/label :t/move-and-reset)] - [quo/button {:on-press #(re-frame/dispatch [:hide-popover]) - :accessibility-label :cancel-move-seed-phrase-button - :type :secondary} + [quo/button + {:on-press #(re-frame/dispatch [:hide-popover]) + :accessibility-label :cancel-move-seed-phrase-button + :type :secondary} (i18n/label :t/cancel)]]]) -(defview unknown-error-popover [] - [react/view {:margin-top 24 - :margin-horizontal 24 - :align-items :center} - [react/view {:width 32 - :height 32 - :border-radius 16 - :align-items :center - :justify-content :center} +(defview unknown-error-popover + [] + [react/view + {:margin-top 24 + :margin-horizontal 24 + :align-items :center} + [react/view + {:width 32 + :height 32 + :border-radius 16 + :align-items :center + :justify-content :center} [icons/icon :main-icons/close {:color colors/red}]] - [react/text {:style {:typography :title-bold - :margin-top 8 - :margin-bottom 24}} + [react/text + {:style {:typography :title-bold + :margin-top 8 + :margin-bottom 24}} (i18n/label :t/something-went-wrong)] [react/view styles/popover-body-container [react/view - [react/text {:style (into styles/popover-text - {:margin-bottom 16})} + [react/text + {:style (into styles/popover-text + {:margin-bottom 16})} (i18n/label :t/transfer-ma-unknown-error-desc-1)] [react/text {:style styles/popover-text} (i18n/label :t/transfer-ma-unknown-error-desc-2)]]] - [react/view {:margin-vertical 24 - :align-items :center} - [quo/button {:on-press #(re-frame/dispatch [::multiaccounts.key-storage/hide-popover-and-goto-multiaccounts-screen]) - :type :secondary} + [react/view + {:margin-vertical 24 + :align-items :center} + [quo/button + {:on-press #(re-frame/dispatch + [::multiaccounts.key-storage/hide-popover-and-goto-multiaccounts-screen]) + :type :secondary} (i18n/label :t/okay)]]]) (comment @@ -312,20 +381,28 @@ ;; Enter seed phrase ;; invalid seed shape - #_(re-frame/dispatch [::multiaccounts.key-storage/seed-phrase-input-changed (utils.security.core/mask-data "h h h h h h h h h h h h")]) + #_(re-frame/dispatch [::multiaccounts.key-storage/seed-phrase-input-changed + (utils.security.core/mask-data "h h h h h h h h h h h h")]) ;; valid seed for Trusty Candid Bighornedsheep - ;; If you try to select Dim Venerated Yaffle, but use this seed instead, validate-seed-against-key-uid will fail miserably - #_(re-frame/dispatch [::multiaccounts.key-storage/seed-phrase-input-changed - (utils.security.core/mask-data "disease behave roof exile ghost head carry item tumble census rocket champion")]) + ;; If you try to select Dim Venerated Yaffle, but use this seed instead, + ;; validate-seed-against-key-uid will fail miserably + #_(re-frame/dispatch + [::multiaccounts.key-storage/seed-phrase-input-changed + (utils.security.core/mask-data + "disease behave roof exile ghost head carry item tumble census rocket champion")]) ;; valid seed for Swiffy Warlike Seagull - #_(re-frame/dispatch [::multiaccounts.key-storage/seed-phrase-input-changed - (utils.security.core/mask-data "dirt agent garlic merge tuna leaf congress hedgehog absent dish pizza scrap")]) + #_(re-frame/dispatch + [::multiaccounts.key-storage/seed-phrase-input-changed + (utils.security.core/mask-data + "dirt agent garlic merge tuna leaf congress hedgehog absent dish pizza scrap")]) ;; valid seed for Dim Venerated Yaffle (this is just a test account, okay to leak seed) - (re-frame/dispatch [::multiaccounts.key-storage/seed-phrase-input-changed - (utils.security.core/mask-data "rocket mixed rebel affair umbrella legal resemble scene virus park deposit cargo")]) + (re-frame/dispatch + [::multiaccounts.key-storage/seed-phrase-input-changed + (utils.security.core/mask-data + "rocket mixed rebel affair umbrella legal resemble scene virus park deposit cargo")]) ;; Click choose storage (re-frame/dispatch [::multiaccounts.key-storage/choose-storage-pressed]) @@ -348,10 +425,14 @@ ;; Flow to populate state after multiaccount is deleted (do ;; set seed phrase for Dim Venerated Yaffle - (re-frame/dispatch [:set-in [:multiaccounts/key-storage :seed-phrase] "rocket mixed rebel affair umbrella legal resemble scene virus park deposit cargo"]) + (re-frame/dispatch + [:set-in [:multiaccounts/key-storage :seed-phrase] + "rocket mixed rebel affair umbrella legal resemble scene virus park deposit cargo"]) ;; set seed for Trusty Candid Bighornedsheep - #_(re-frame/dispatch [:set-in [:multiaccounts/key-storage :seed-phrase] "disease behave roof exile ghost head carry item tumble census rocket champion"]) + #_(re-frame/dispatch + [:set-in [:multiaccounts/key-storage :seed-phrase] + "disease behave roof exile ghost head carry item tumble census rocket champion"]) ;; simulate delete multiaccount success (re-frame/dispatch [::multiaccounts.key-storage/delete-multiaccount-success]))) diff --git a/src/status_im/ui/screens/multiaccounts/login/styles.cljs b/src/status_im/ui/screens/multiaccounts/login/styles.cljs index 1d601dc972..9bd8d4654c 100644 --- a/src/status_im/ui/screens/multiaccounts/login/styles.cljs +++ b/src/status_im/ui/screens/multiaccounts/login/styles.cljs @@ -36,17 +36,17 @@ :padding-top 13}}) (def save-password-unavailable-android - {:margin-top 8 - :width "100%" - :color colors/text-gray - :text-align :center + {:margin-top 8 + :width "100%" + :color colors/text-gray + :text-align :center :flex-direction :row - :align-items :center}) + :align-items :center}) (def biometric-button - {:justify-content :center - :align-items :center - :height 40 - :width 40 - :border-radius 20 - :margin-left 16}) + {:justify-content :center + :align-items :center + :height 40 + :width 40 + :border-radius 20 + :margin-left 16}) diff --git a/src/status_im/ui/screens/multiaccounts/login/views.cljs b/src/status_im/ui/screens/multiaccounts/login/views.cljs index 8a5a6e4c9a..cff566c5b0 100644 --- a/src/status_im/ui/screens/multiaccounts/login/views.cljs +++ b/src/status_im/ui/screens/multiaccounts/login/views.cljs @@ -1,58 +1,65 @@ (ns status-im.ui.screens.multiaccounts.login.views - (:require [re-frame.core :as re-frame] + (:require [quo.core :as quo] + [quo.design-system.colors :as colors] + [re-frame.core :as re-frame] [status-im.i18n.i18n :as i18n] [status-im.multiaccounts.core :as multiaccounts] [status-im.ui.components.checkbox.view :as checkbox] + [status-im.ui.components.icons.icons :as icons] [status-im.ui.components.react :as react] + [status-im.ui.components.toolbar :as toolbar] [status-im.ui.screens.chat.photos :as photos] + [status-im.ui.screens.multiaccounts.key-storage.views :as key-storage] [status-im.ui.screens.multiaccounts.login.styles :as styles] [status-im.ui.screens.multiaccounts.styles :as ast] [status-im.utils.platform :as platform] - [utils.security.core :as security] [status-im.utils.utils :as utils] - [quo.core :as quo] - [status-im.ui.components.icons.icons :as icons] - [status-im.ui.components.toolbar :as toolbar] - [quo.design-system.colors :as colors] - [status-im.ui.screens.multiaccounts.key-storage.views :as key-storage]) + [utils.security.core :as security]) (:require-macros [status-im.utils.views :refer [defview letsubs]])) -(defn login-multiaccount [^js password-text-input] +(defn login-multiaccount + [^js password-text-input] (.blur password-text-input) (re-frame/dispatch [:multiaccounts.login.ui/password-input-submitted])) -(defn multiaccount-login-badge [{:keys [public-key name] :as multiaccount}] +(defn multiaccount-login-badge + [{:keys [public-key name] :as multiaccount}] [react/view styles/login-badge [photos/photo (multiaccounts/displayed-photo multiaccount) {:size styles/login-badge-image-size}] [react/view - [react/text {:style styles/login-badge-name - :ellipsize-mode :middle - :numberOfLines 1} + [react/text + {:style styles/login-badge-name + :ellipsize-mode :middle + :numberOfLines 1} name] - [quo/text {:monospace true - :align :center - :color :secondary - :style styles/login-badge-pubkey} + [quo/text + {:monospace true + :align :center + :color :secondary + :style styles/login-badge-pubkey} (utils/get-shortened-address public-key)]]]) -(defn topbar-button [] +(defn topbar-button + [] (react/dismiss-keyboard!) (re-frame/dispatch [:multiaccounts.recover.ui/recover-multiaccount-button-pressed])) -(defview login [] +(defview login + [] (letsubs [{:keys [error processing save-password?] :as multiaccount} [:multiaccounts/login] - password-text-input (atom nil) - sign-in-enabled? [:sign-in-enabled?] - auth-method [:auth-method] - view-id [:view-id] - supported-biometric-auth [:supported-biometric-auth] - keycard? [:keycard-multiaccount?] - banner-hidden [:keycard/banner-hidden]] + password-text-input (atom nil) + sign-in-enabled? [:sign-in-enabled?] + auth-method [:auth-method] + view-id [:view-id] + supported-biometric-auth [:supported-biometric-auth] + keycard? [:keycard-multiaccount?] + banner-hidden [:keycard/banner-hidden]] [react/keyboard-avoiding-view {:style ast/multiaccounts-view} - [react/scroll-view {:keyboardShouldPersistTaps :always - :style styles/login-view} + [react/scroll-view + {:keyboardShouldPersistTaps :always + :style styles/login-view} [react/view styles/login-badge-container [multiaccount-login-badge multiaccount] [react/view {:style styles/password-container} @@ -75,22 +82,26 @@ (when (and supported-biometric-auth (= auth-method "biometric")) [react/touchable-highlight {:on-press #(re-frame/dispatch [:biometric-authenticate])} [react/view {:style styles/biometric-button} - [icons/icon (if (= supported-biometric-auth :FaceID) - :main-icons/faceid - :main-icons/print) + [icons/icon + (if (= supported-biometric-auth :FaceID) + :main-icons/faceid + :main-icons/print) {:color colors/blue}]]])]] (if (and platform/android? (not auth-method)) ;; on Android, there is much more reasons for the password save to be unavailable, ;; so we don't show the checkbox whatsoever but put a label explaining why it happenned. - [react/i18n-text {:style styles/save-password-unavailable-android - :key :save-password-unavailable-android}] - [react/view {:style {:flex-direction :row - :align-items :center - :justify-content :flex-start - :margin-top 19}} - [checkbox/checkbox {:checked? save-password? - :style {:margin-left 3 :margin-right 10} - :on-value-change #(re-frame/dispatch [:multiaccounts/save-password %])}] + [react/i18n-text + {:style styles/save-password-unavailable-android + :key :save-password-unavailable-android}] + [react/view + {:style {:flex-direction :row + :align-items :center + :justify-content :flex-start + :margin-top 19}} + [checkbox/checkbox + {:checked? save-password? + :style {:margin-left 3 :margin-right 10} + :on-value-change #(re-frame/dispatch [:multiaccounts/save-password %])}] [react/text (i18n/label :t/save-password)]])]] (if processing [react/view styles/processing-view @@ -100,7 +111,7 @@ [key-storage/keycard-upsell-banner])) [toolbar/toolbar - {:size :large + {:size :large :center [react/view {:padding-horizontal 8} [quo/button diff --git a/src/status_im/ui/screens/multiaccounts/recover/views.cljs b/src/status_im/ui/screens/multiaccounts/recover/views.cljs index 40663d848c..16097e4959 100644 --- a/src/status_im/ui/screens/multiaccounts/recover/views.cljs +++ b/src/status_im/ui/screens/multiaccounts/recover/views.cljs @@ -1,51 +1,62 @@ (ns status-im.ui.screens.multiaccounts.recover.views (:require-macros [status-im.utils.views :refer [defview letsubs]]) - (:require [re-frame.core :as re-frame] - [status-im.ui.components.react :as react] - [status-im.multiaccounts.recover.core :as multiaccounts.recover] - [status-im.multiaccounts.key-storage.core :as multiaccounts.key-storage] - [status-im.keycard.recovery :as keycard] - [status-im.i18n.i18n :as i18n] - [status-im2.setup.config :as config] - [utils.security.core] + (:require [quo.core :as quo] [quo.design-system.colors :as colors] - [quo.core :as quo] + [re-frame.core :as re-frame] + [status-im.i18n.i18n :as i18n] + [status-im.keycard.recovery :as keycard] + [status-im.multiaccounts.key-storage.core :as multiaccounts.key-storage] + [status-im.multiaccounts.recover.core :as multiaccounts.recover] [status-im.qr-scanner.core :as qr-scanner] [status-im.react-native.resources :as resources] - [status-im.ui.components.icons.icons :as icons])) + [status-im.ui.components.icons.icons :as icons] + [status-im.ui.components.react :as react] + [status-im2.setup.config :as config] + [utils.security.core])) -(defn hide-sheet-and-dispatch [event] +(defn hide-sheet-and-dispatch + [event] (re-frame/dispatch [:bottom-sheet/hide]) (re-frame/dispatch event)) -(defview custom-seed-phrase [] +(defview custom-seed-phrase + [] [react/view [react/view {:margin-top 24 :margin-horizontal 24 :align-items :center} - [react/view {:width 32 :height 32 :border-radius 16 - :align-items :center :justify-content :center} + [react/view + {:width 32 + :height 32 + :border-radius 16 + :align-items :center + :justify-content :center} [icons/icon :main-icons/help {:color colors/blue}]] - [react/text {:style {:typography :title-bold - :margin-top 8 - :margin-bottom 8}} + [react/text + {:style {:typography :title-bold + :margin-top 8 + :margin-bottom 8}} (i18n/label :t/custom-seed-phrase)] - [react/view {:flex-wrap :wrap - :flex-direction :row - :justify-content :center - :text-align :center} + [react/view + {:flex-wrap :wrap + :flex-direction :row + :justify-content :center + :text-align :center} [react/nested-text {:style {:color colors/gray :text-align :center :line-height 22}} (i18n/label :t/custom-seed-phrase-text-1)]] - [react/view {:margin-vertical 24 - :align-items :center} - [quo/button {:on-press #(re-frame/dispatch [:hide-popover]) - :accessibility-label :cancel-custom-seed-phrase - :type :secondary} + [react/view + {:margin-vertical 24 + :align-items :center} + [quo/button + {:on-press #(re-frame/dispatch [:hide-popover]) + :accessibility-label :cancel-custom-seed-phrase + :type :secondary} (i18n/label :t/cancel)]]]]) -(defview bottom-sheet-view [] - (letsubs [view-id [:view-id] +(defview bottom-sheet-view + [] + (letsubs [view-id [:view-id] acc-to-login-keycard-pairing [:intro-wizard/acc-to-login-keycard-pairing]] [react/view {:flex-direction :row} [react/view {} @@ -58,7 +69,8 @@ :title (i18n/label :t/manage-keys-and-storage) :accessibility-label :manage-keys-and-storage-button :icon :main-icons/key - :on-press #(hide-sheet-and-dispatch [::multiaccounts.key-storage/key-and-storage-management-pressed])}]) + :on-press #(hide-sheet-and-dispatch + [::multiaccounts.key-storage/key-and-storage-management-pressed])}]) [quo/list-item {:theme :accent @@ -70,37 +82,44 @@ {:theme :accent :title (i18n/label :t/recover-with-keycard) :accessibility-label :recover-with-keycard-button - :icon [react/view {:border-width 1 - :border-radius 20 - :border-color colors/blue-light - :background-color colors/blue-light - :justify-content :center - :align-items :center - :width 40 - :height 40} - [react/image {:source (resources/get-image :keycard-logo-blue) - :style {:width 24 :height 24}}]] + :icon [react/view + {:border-width 1 + :border-radius 20 + :border-color colors/blue-light + :background-color colors/blue-light + :justify-content :center + :align-items :center + :width 40 + :height 40} + [react/image + {:source (resources/get-image :keycard-logo-blue) + :style {:width 24 :height 24}}]] :on-press #(hide-sheet-and-dispatch [::keycard/recover-with-keycard-pressed])}] (when config/database-management-enabled? - [quo/list-item {:theme :accent - :on-press #(hide-sheet-and-dispatch [:multiaccounts.login.ui/import-db-submitted]) - :icon :main-icons/receive - :title "Import unencrypted"}]) + [quo/list-item + {:theme :accent + :on-press #(hide-sheet-and-dispatch [:multiaccounts.login.ui/import-db-submitted]) + :icon :main-icons/receive + :title "Import unencrypted"}]) (when config/database-management-enabled? - [quo/list-item {:theme :accent - :on-press #(hide-sheet-and-dispatch [:multiaccounts.login.ui/export-db-submitted]) - :icon :main-icons/send - :title "Export unencrypted"}]) + [quo/list-item + {:theme :accent + :on-press #(hide-sheet-and-dispatch [:multiaccounts.login.ui/export-db-submitted]) + :icon :main-icons/send + :title "Export unencrypted"}]) (when config/local-pairing-mode-enabled? [:<> - [quo/list-item {:theme :accent - :on-press #(hide-sheet-and-dispatch [::qr-scanner/scan-code {:handler ::qr-scanner/on-scan-success}]) - :icon :i/key - :title (i18n/label :t/scan-sync-code)}] - [quo/list-item {:theme :accent - :on-press #(hide-sheet-and-dispatch [:navigate-to :multiaccounts]) - :icon :i/key - :title (i18n/label :t/show-existing-keys)}]])]])) + [quo/list-item + {:theme :accent + :on-press #(hide-sheet-and-dispatch [::qr-scanner/scan-code + {:handler ::qr-scanner/on-scan-success}]) + :icon :i/key + :title (i18n/label :t/scan-sync-code)}] + [quo/list-item + {:theme :accent + :on-press #(hide-sheet-and-dispatch [:navigate-to :multiaccounts]) + :icon :i/key + :title (i18n/label :t/show-existing-keys)}]])]])) (def bottom-sheet {:content bottom-sheet-view}) @@ -113,8 +132,10 @@ (re-frame/dispatch [::multiaccounts.recover/enter-phrase-pressed]) ;; Enter seed phrase for Dim Venerated Yaffle - (re-frame/dispatch [:multiaccounts.recover/enter-phrase-input-changed - (utils.security.core/mask-data "rocket mixed rebel affair umbrella legal resemble scene virus park deposit cargo")]) + (re-frame/dispatch + [:multiaccounts.recover/enter-phrase-input-changed + (utils.security.core/mask-data + "rocket mixed rebel affair umbrella legal resemble scene virus park deposit cargo")]) ;; Recover multiaccount (re-frame/dispatch [:multiaccounts.recover/enter-phrase-next-pressed]) diff --git a/src/status_im/ui/screens/multiaccounts/sheets.cljs b/src/status_im/ui/screens/multiaccounts/sheets.cljs index 9e7f963f6d..f4dc639e21 100644 --- a/src/status_im/ui/screens/multiaccounts/sheets.cljs +++ b/src/status_im/ui/screens/multiaccounts/sheets.cljs @@ -1,12 +1,14 @@ (ns status-im.ui.screens.multiaccounts.sheets (:require [quo.core :as quo] - [status-im.i18n.i18n :as i18n] - [re-frame.core :as re-frame])) + [re-frame.core :as re-frame] + [status-im.i18n.i18n :as i18n])) -(defn actions-sheet [] - [quo/list-item {:theme :accent - :on-press #(do (re-frame/dispatch [:bottom-sheet/hide]) - (re-frame/dispatch [:generate-and-derive-addresses])) - :icon :main-icons/add - :accessibility-label :generate-a-new-key - :title (i18n/label :t/generate-a-new-key)}]) +(defn actions-sheet + [] + [quo/list-item + {:theme :accent + :on-press #(do (re-frame/dispatch [:bottom-sheet/hide]) + (re-frame/dispatch [:generate-and-derive-addresses])) + :icon :main-icons/add + :accessibility-label :generate-a-new-key + :title (i18n/label :t/generate-a-new-key)}]) diff --git a/src/status_im/ui/screens/multiaccounts/styles.cljs b/src/status_im/ui/screens/multiaccounts/styles.cljs index 0c8df2410f..081473b201 100644 --- a/src/status_im/ui/screens/multiaccounts/styles.cljs +++ b/src/status_im/ui/screens/multiaccounts/styles.cljs @@ -4,8 +4,8 @@ {:flex 1}) (def multiaccounts-container - {:flex 1 - :justify-content :space-between}) + {:flex 1 + :justify-content :space-between}) (def multiaccount-image-size 40) diff --git a/src/status_im/ui/screens/multiaccounts/views.cljs b/src/status_im/ui/screens/multiaccounts/views.cljs index c18317a7f5..0fb548f041 100644 --- a/src/status_im/ui/screens/multiaccounts/views.cljs +++ b/src/status_im/ui/screens/multiaccounts/views.cljs @@ -1,66 +1,76 @@ (ns status-im.ui.screens.multiaccounts.views (:require-macros [status-im.utils.views :refer [defview letsubs]]) - (:require [re-frame.core :as re-frame] - [status-im.ui.screens.chat.photos :as photos] + (:require [quo.core :as quo] + [quo.design-system.colors :as colors] + [re-frame.core :as re-frame] + [status-im.i18n.i18n :as i18n] [status-im.multiaccounts.core :as multiaccounts] - [status-im.ui.screens.multiaccounts.styles :as styles] + [status-im.react-native.resources :as resources] [status-im.ui.components.list.views :as list] [status-im.ui.components.react :as react] - [utils.security.core :as security] - [status-im.i18n.i18n :as i18n] - [quo.design-system.colors :as colors] [status-im.ui.components.toolbar :as toolbar] - [quo.core :as quo] + [status-im.ui.screens.chat.photos :as photos] [status-im.ui.screens.multiaccounts.sheets :as sheets] - [status-im.react-native.resources :as resources])) + [status-im.ui.screens.multiaccounts.styles :as styles] + [utils.security.core :as security])) (defn multiaccount-view [{:keys [key-uid name keycard-pairing] :as account}] - [quo/list-item {:on-press #(re-frame/dispatch - [:multiaccounts.login.ui/multiaccount-selected key-uid]) - :icon [photos/photo (multiaccounts/displayed-photo account) {:size styles/multiaccount-image-size}] - :title name - :accessory-style (when keycard-pairing {:flex-basis 100}) - :accessory (when keycard-pairing - [react/view {:justify-content :center - :align-items :center - :width 32 - :height 32 - :border-radius 24 - :background-color colors/white - :border-width 1 - :border-color colors/black-transparent} - [react/image {:source (resources/get-image :keycard-key) - :style {:width 11 - :height 19}}]]) - :chevron true}]) + [quo/list-item + {:on-press #(re-frame/dispatch + [:multiaccounts.login.ui/multiaccount-selected key-uid]) + :icon [photos/photo (multiaccounts/displayed-photo account) + {:size styles/multiaccount-image-size}] + :title name + :accessory-style (when keycard-pairing {:flex-basis 100}) + :accessory (when keycard-pairing + [react/view + {:justify-content :center + :align-items :center + :width 32 + :height 32 + :border-radius 24 + :background-color colors/white + :border-width 1 + :border-color colors/black-transparent} + [react/image + {:source (resources/get-image :keycard-key) + :style {:width 11 + :height 19}}]]) + :chevron true}]) -(defn topbar-button [] +(defn topbar-button + [] (re-frame/dispatch [:bottom-sheet/show-sheet {:content sheets/actions-sheet}])) -(defview multiaccounts [] +(defview multiaccounts + [] (letsubs [multiaccounts [:multiaccounts/multiaccounts]] [:<> [react/view styles/multiaccounts-container - [list/flat-list {:data (vals multiaccounts) - :contentContainerStyle styles/multiaccounts-list-container - :key-fn (comp str :address) - :render-fn multiaccount-view}]] + [list/flat-list + {:data (vals multiaccounts) + :contentContainerStyle styles/multiaccounts-list-container + :key-fn (comp str :address) + :render-fn multiaccount-view}]] [toolbar/toolbar {:show-border? true :size :large :center [quo/button - {:on-press #(re-frame/dispatch [:multiaccounts.recover.ui/recover-multiaccount-button-pressed]) + {:on-press #(re-frame/dispatch + [:multiaccounts.recover.ui/recover-multiaccount-button-pressed]) :type :secondary} (i18n/label :t/access-existing-keys)]}]])) -(defn seed-phrase-input [{:keys [on-change-event - seed-word-count - seed-shape-invalid?]}] - [react/view {:flex 1 - :justify-content :center - :padding-horizontal 16} +(defn seed-phrase-input + [{:keys [on-change-event + seed-word-count + seed-shape-invalid?]}] + [react/view + {:flex 1 + :justify-content :center + :padding-horizontal 16} [quo/text-input {:show-cancel false :auto-correct false @@ -73,11 +83,13 @@ :on-change-text #(re-frame/dispatch (conj on-change-event (security/mask-data %)))}] ;; word counter view [react/view {:align-items :flex-end} - [react/view {:flex-direction :row - :align-items :center - :padding-vertical 8 - :opacity (if seed-word-count 1 0)} - [quo/text {:color (if seed-shape-invalid? :secondary :main) - :size :small} + [react/view + {:flex-direction :row + :align-items :center + :padding-vertical 8 + :opacity (if seed-word-count 1 0)} + [quo/text + {:color (if seed-shape-invalid? :secondary :main) + :size :small} (when-not seed-shape-invalid? "✓ ") (i18n/label-pluralize seed-word-count :t/words-n)]]]]) diff --git a/src/status_im/ui/screens/network/edit_network/views.cljs b/src/status_im/ui/screens/network/edit_network/views.cljs index f7a8a73808..dff1995166 100644 --- a/src/status_im/ui/screens/network/edit_network/views.cljs +++ b/src/status_im/ui/screens/network/edit_network/views.cljs @@ -1,25 +1,27 @@ (ns status-im.ui.screens.network.edit-network.views - (:require [re-frame.core :as re-frame] + (:require [quo.core :as quo] + [re-frame.core :as re-frame] [status-im.i18n.i18n :as i18n] [status-im.network.core :as network] - [status-im.ui.screens.network.edit-network.styles :as styles] [status-im.ui.components.list.views :as list] [status-im.ui.components.react :as react] - [quo.core :as quo]) + [status-im.ui.screens.network.edit-network.styles :as styles]) (:require-macros [status-im.utils.views :as views])) -(defn- render-network-type [type _ _ manage-network] +(defn- render-network-type + [type _ _ manage-network] (let [name (case type :mainnet (i18n/label :t/mainnet-network) - :goerli (i18n/label :t/goerli-network) - :custom (i18n/label :t/custom))] + :goerli (i18n/label :t/goerli-network) + :custom (i18n/label :t/custom))] [quo/list-item - {:title name + {:title name :accessory :radio - :active (= (get-in manage-network [:chain :value]) type) - :on-press #(re-frame/dispatch [::network/input-changed :chain type])}])) + :active (= (get-in manage-network [:chain :value]) type) + :on-press #(re-frame/dispatch [::network/input-changed :chain type])}])) -(views/defview edit-network [] +(views/defview edit-network + [] (views/letsubs [manage-network [:networks/manage] is-valid? [:manage-network-valid?]] (let [custom? (= (get-in manage-network [:chain :value]) :custom)] @@ -47,10 +49,11 @@ :default-value (get-in manage-network [:url :value]) :on-change-text #(re-frame/dispatch [::network/input-changed :url %])}]]] [quo/list-header (i18n/label :t/network-chain)] - [list/flat-list {:data [:mainnet :goerli :custom] - :key-fn (fn [_ i] (str i)) - :render-data manage-network - :render-fn render-network-type}] + [list/flat-list + {:data [:mainnet :goerli :custom] + :key-fn (fn [_ i] (str i)) + :render-data manage-network + :render-fn render-network-type}] (when custom? [react/view styles/edit-network-view [quo/text-input @@ -60,8 +63,8 @@ [react/view styles/bottom-container [react/view {:flex 1}] [quo/button - {:after :main-icons/next - :type :secondary - :disabled (not is-valid?) - :on-press #(re-frame/dispatch [::network/save-network-pressed])} + {:after :main-icons/next + :type :secondary + :disabled (not is-valid?) + :on-press #(re-frame/dispatch [::network/save-network-pressed])} (i18n/label :t/save)]]]))) diff --git a/src/status_im/ui/screens/network/network_details/views.cljs b/src/status_im/ui/screens/network/network_details/views.cljs index 77083adfcc..6b8f4dc424 100644 --- a/src/status_im/ui/screens/network/network_details/views.cljs +++ b/src/status_im/ui/screens/network/network_details/views.cljs @@ -1,18 +1,19 @@ (ns status-im.ui.screens.network.network-details.views - (:require [re-frame.core :as re-frame] + (:require [quo.core :as quo] + [re-frame.core :as re-frame] [status-im.i18n.i18n :as i18n] - [quo.core :as quo] [status-im.network.core :as network] + [status-im.ui.components.react :as react] [status-im.ui.screens.network.styles :as st] [status-im.ui.screens.network.views :as network-settings] - [status-im.ui.components.react :as react] [utils.debounce :refer [dispatch-and-chill]]) (:require-macros [status-im.utils.views :as views])) -(views/defview network-details [] +(views/defview network-details + [] (views/letsubs [{:keys [networks/selected-network]} [:get-screen-params] - current-network [:networks/current-network] - networks [:get-networks]] + current-network [:networks/current-network] + networks [:get-networks]] (let [{:keys [id name config]} selected-network connected? (= id current-network) custom? (seq (filter #(= (:id %) id) (:custom networks)))] @@ -22,20 +23,24 @@ {:name name :connected? connected?}] (when-not connected? - [react/touchable-highlight {:on-press #(dispatch-and-chill [::network/connect-network-pressed id] 1000)} + [react/touchable-highlight + {:on-press #(dispatch-and-chill [::network/connect-network-pressed id] 1000)} [react/view st/connect-button-container - [react/view {:style st/connect-button - :accessibility-label :network-connect-button} + [react/view + {:style st/connect-button + :accessibility-label :network-connect-button} [react/text {:style st/connect-button-label} (i18n/label :t/connect)]] - [react/i18n-text {:style st/connect-button-description - :key :connecting-requires-login}]]]) + [react/i18n-text + {:style st/connect-button-description + :key :connecting-requires-login}]]]) [react/view (st/network-config-container) - [react/text {:style st/network-config-text - :accessibility-label :network-details-text} + [react/text + {:style st/network-config-text + :accessibility-label :network-details-text} config]]] (when custom? [react/view st/bottom-container [react/view {:flex 1} - [quo/button {:on-press #(re-frame/dispatch [::network/delete-network-pressed id])} + [quo/button {:on-press #(re-frame/dispatch [::network/delete-network-pressed id])} (i18n/label :t/delete)]]])]))) diff --git a/src/status_im/ui/screens/network/styles.cljs b/src/status_im/ui/screens/network/styles.cljs index 6218071479..2163d2f015 100644 --- a/src/status_im/ui/screens/network/styles.cljs +++ b/src/status_im/ui/screens/network/styles.cljs @@ -3,14 +3,14 @@ [status-im.utils.styles :as styles])) (def wrapper - {:flex 1}) + {:flex 1}) (def badge-name-text {:font-size 17}) (styles/def badge-connected-text - {:color colors/gray - :ios {:margin-top 5}}) + {:color colors/gray + :ios {:margin-top 5}}) (def connect-button-container {:margin-top 8 @@ -34,7 +34,8 @@ :margin-top 8 :height 20}) -(styles/defn network-config-container [] +(styles/defn network-config-container + [] {:height 160 :margin-top 8 :padding-top 16 @@ -46,11 +47,12 @@ :android {:border-radius 4}}) (styles/def network-config-text - {:font-size 17 - :ios {:opacity 0.8} - :android {:opacity 0.4}}) + {:font-size 17 + :ios {:opacity 0.8} + :android {:opacity 0.4}}) -(defn network-icon [connected? size] +(defn network-icon + [connected? size] {:width size :height size :border-radius (/ size 2) diff --git a/src/status_im/ui/screens/network/views.cljs b/src/status_im/ui/screens/network/views.cljs index 6d7b9ab901..5c03b477bf 100644 --- a/src/status_im/ui/screens/network/views.cljs +++ b/src/status_im/ui/screens/network/views.cljs @@ -1,62 +1,70 @@ (ns status-im.ui.screens.network.views - (:require [re-frame.core :as re-frame] + (:require [quo.core :as quo] + [quo.design-system.colors :as colors] + [re-frame.core :as re-frame] [status-im.i18n.i18n :as i18n] [status-im.network.core :as network] - [status-im.ui.screens.network.styles :as styles] [status-im.ui.components.icons.icons :as icons] [status-im.ui.components.list.views :as list] [status-im.ui.components.react :as react] [status-im.ui.components.topbar :as topbar] - [quo.design-system.colors :as colors] - [quo.core :as quo]) + [status-im.ui.screens.network.styles :as styles]) (:require-macros [status-im.utils.views :as views])) -(defn- network-icon [connected? size] +(defn- network-icon + [connected? size] [react/view (styles/network-icon connected? size) [icons/icon :main-icons/network {:color (if connected? colors/white-persist colors/gray)}]]) -(defn network-badge [& [{:keys [name connected?]}]] +(defn network-badge + [& [{:keys [name connected?]}]] [react/view styles/network-badge [network-icon connected? 56] [react/view {:padding-left 16} [react/text {:style styles/badge-name-text} (or name (i18n/label :t/new-network))] (when connected? - [react/i18n-text {:style styles/badge-connected-text - :key :connected}])]]) + [react/i18n-text + {:style styles/badge-connected-text + :key :connected}])]]) (def mainnet? #{"mainnet" "mainnet_rpc"}) -(defn render-network [{:keys [id name] :as network} _ _ current-network] +(defn render-network + [{:keys [id name] :as network} _ _ current-network] (let [connected? (= id current-network)] - [quo/list-item {:on-press #(re-frame/dispatch [::network/network-entry-pressed network]) - :icon :main-icons/network - :icon-bg-color (if connected? colors/blue colors/gray-lighter) - :icon-color (if connected? colors/white-persist colors/gray) - :title name - :subtitle (when connected? (i18n/label :t/connected)) - :chevron true}])) + [quo/list-item + {:on-press #(re-frame/dispatch [::network/network-entry-pressed network]) + :icon :main-icons/network + :icon-bg-color (if connected? colors/blue colors/gray-lighter) + :icon-color (if connected? colors/white-persist colors/gray) + :title name + :subtitle (when connected? (i18n/label :t/connected)) + :chevron true}])) -(views/defview network-settings [] +(views/defview network-settings + [] (views/letsubs [current-network [:networks/current-network] networks [:get-networks]] [:<> - [topbar/topbar {:title (i18n/label :t/network-settings) - :right-accessories - [{:icon :main-icons/add - :on-press #(re-frame/dispatch [::network/add-network-pressed])}]}] + [topbar/topbar + {:title (i18n/label :t/network-settings) + :right-accessories + [{:icon :main-icons/add + :on-press #(re-frame/dispatch [::network/add-network-pressed])}]}] [react/view styles/wrapper - [list/section-list {:sections [{:title (i18n/label :t/main-networks) - :key :mainnet - :data (:mainnet networks)} - {:title (i18n/label :t/test-networks) - :key :testnet - :data (:testnet networks)} - {:title (i18n/label :t/custom-networks) - :key :custom - :data (:custom networks)}] - :key-fn :id - :default-separator? true - :render-data current-network - :render-fn render-network}]]])) + [list/section-list + {:sections [{:title (i18n/label :t/main-networks) + :key :mainnet + :data (:mainnet networks)} + {:title (i18n/label :t/test-networks) + :key :testnet + :data (:testnet networks)} + {:title (i18n/label :t/custom-networks) + :key :custom + :data (:custom networks)}] + :key-fn :id + :default-separator? true + :render-data current-network + :render-fn render-network}]]])) diff --git a/src/status_im/ui/screens/network_info/views.cljs b/src/status_im/ui/screens/network_info/views.cljs index 4bf2cb549a..fa29c93683 100644 --- a/src/status_im/ui/screens/network_info/views.cljs +++ b/src/status_im/ui/screens/network_info/views.cljs @@ -1,28 +1,32 @@ (ns status-im.ui.screens.network-info.views - (:require [status-im.ui.components.react :as react] - [re-frame.core :as re-frame] + (:require [re-frame.core :as re-frame] [reagent.core :as reagent] - [status-im.ethereum.json-rpc :as json-rpc] [status-im.ethereum.decode :as decode] - [status-im.utils.datetime :as time] - [status-im.native-module.core :as status])) + [status-im.ethereum.json-rpc :as json-rpc] + [status-im.native-module.core :as status] + [status-im.ui.components.react :as react] + [status-im.utils.datetime :as time])) -(defn get-block [block callback] +(defn get-block + [block callback] (json-rpc/call {:method "eth_getBlockByNumber" :params [block false] :on-success callback :on-error #(js/alert (str "can't fetch latest block" %))})) -(defn last-loaded-block-number [] +(defn last-loaded-block-number + [] @(re-frame/subscribe [:ethereum/current-block])) -(defn to-date [timestamp] +(defn to-date + [timestamp] (time/timestamp->long-date (* 1000 timestamp))) -(defn check-lag [] - (let [latest-block (reagent/atom nil) +(defn check-lag + [] + (let [latest-block (reagent/atom nil) last-loaded-block (reagent/atom nil) on-press (fn [] @@ -31,8 +35,9 @@ (fn [res] (reset! latest-block res) (get-block - (str "0x" (status/number-to-hex - (last-loaded-block-number))) + (str "0x" + (status/number-to-hex + (last-loaded-block-number))) (fn [res] (reset! last-loaded-block res))))))] (fn [] @@ -68,13 +73,15 @@ "Last loaded block time: " (to-date last-loaded-block-timestamp) "\n" - "Seconds diff: " (- latest-block-timestamp - last-loaded-block-timestamp) + "Seconds diff: " + (- latest-block-timestamp + last-loaded-block-timestamp) "\n" "Blocks diff: " (- latest-block-number last-loaded-block-number) "\n" "PRESS TO REFRESH"))])]))) -(defn network-info [] +(defn network-info + [] [check-lag]) diff --git a/src/status_im/ui/screens/notifications_settings/views.cljs b/src/status_im/ui/screens/notifications_settings/views.cljs index 183b77bbb5..d9bd346ef1 100644 --- a/src/status_im/ui/screens/notifications_settings/views.cljs +++ b/src/status_im/ui/screens/notifications_settings/views.cljs @@ -1,21 +1,23 @@ (ns status-im.ui.screens.notifications-settings.views (:require-macros [status-im.utils.views :refer [defview letsubs]]) - (:require [re-frame.core :as re-frame] + (:require [quo.core :as quo] + [quo.design-system.colors :as quo-colors] + [quo.platform :as platform] + [re-frame.core :as re-frame] [reagent.core :as reagent] [status-im.i18n.i18n :as i18n] - [quo.core :as quo] - [quo.platform :as platform] - [quo.design-system.colors :as quo-colors] [status-im.notifications.core :as notifications] [status-im.ui.components.react :as react])) (defonce server (reagent/atom "")) -(defn local-notifications [] +(defn local-notifications + [] (let [{:keys [enabled]} @(re-frame/subscribe [:notifications/wallet-transactions])] [:<> - [quo/separator {:color (:ui-02 @quo-colors/theme) - :style {:margin-vertical 8}}] + [quo/separator + {:color (:ui-02 @quo-colors/theme) + :style {:margin-vertical 8}}] [quo/list-header (i18n/label :t/local-notifications)] [quo/list-item {:size :small @@ -26,7 +28,8 @@ [::notifications/switch-transaction-notifications enabled]) :accessory :switch}]])) -(defn notifications-settings-ios [] +(defn notifications-settings-ios + [] (let [{:keys [remote-push-notifications-enabled? push-notifications-block-mentions? push-notifications-from-contacts-only?]} @@ -37,10 +40,12 @@ :title (i18n/label :t/show-notifications) :accessibility-label :notifications-button :active remote-push-notifications-enabled? - :on-press #(re-frame/dispatch [::notifications/switch (not remote-push-notifications-enabled?) true]) + :on-press #(re-frame/dispatch [::notifications/switch + (not remote-push-notifications-enabled?) true]) :accessory :switch}] - [quo/separator {:color (:ui-02 @quo-colors/theme) - :style {:margin-vertical 8}}] + [quo/separator + {:color (:ui-02 @quo-colors/theme) + :style {:margin-vertical 8}}] [quo/list-header (i18n/label :t/notifications-preferences)] [quo/list-item {:size :small @@ -49,7 +54,8 @@ :active (and remote-push-notifications-enabled? (not push-notifications-from-contacts-only?)) :on-press #(re-frame/dispatch - [::notifications/switch-non-contacts (not push-notifications-from-contacts-only?)]) + [::notifications/switch-non-contacts + (not push-notifications-from-contacts-only?)]) :accessory :switch}] [quo/list-item {:size :small @@ -58,11 +64,13 @@ :active (and remote-push-notifications-enabled? (not push-notifications-block-mentions?)) :on-press #(re-frame/dispatch - [::notifications/switch-block-mentions (not push-notifications-block-mentions?)]) + [::notifications/switch-block-mentions + (not push-notifications-block-mentions?)]) :accessory :switch}] [local-notifications]])) -(defn notifications-settings-android [] +(defn notifications-settings-android + [] (let [{:keys [notifications-enabled?]} @(re-frame/subscribe [:multiaccount])] [:<> [quo/list-item @@ -75,31 +83,36 @@ :accessory :switch}] [local-notifications]])) -(defn notifications-settings [] - [react/scroll-view {:style {:flex 1} - :content-container-style {:padding-vertical 8}} +(defn notifications-settings + [] + [react/scroll-view + {:style {:flex 1} + :content-container-style {:padding-vertical 8}} (if platform/ios? [notifications-settings-ios] [notifications-settings-android])]) -(defn notifications-advanced-settings [] +(defn notifications-advanced-settings + [] (let [{:keys [remote-push-notifications-enabled? send-push-notifications? push-notifications-server-enabled?]} @(re-frame/subscribe [:multiaccount])] - [react/scroll-view {:style {:flex 1} - :content-container-style {:padding-vertical 8}} + [react/scroll-view + {:style {:flex 1} + :content-container-style {:padding-vertical 8}} [quo/list-item {:size :small :title (i18n/label :t/send-push-notifications) :accessibility-label :send-push-notifications-button :active send-push-notifications? :on-press #(re-frame/dispatch - [::notifications/switch-send-push-notifications (not send-push-notifications?)]) + [::notifications/switch-send-push-notifications + (not send-push-notifications?)]) :accessory :switch}] [quo/list-footer (i18n/label :t/send-push-notifications-description)] - [quo/separator {:style {:margin-vertical 8}}] + [quo/separator {:style {:margin-vertical 8}}] [quo/list-item {:size :small :title (i18n/label :t/push-notifications-server-enabled) @@ -107,7 +120,8 @@ :active (and remote-push-notifications-enabled? push-notifications-server-enabled?) :on-press #(re-frame/dispatch - [::notifications/switch-push-notifications-server-enabled (not push-notifications-server-enabled?)]) + [::notifications/switch-push-notifications-server-enabled + (not push-notifications-server-enabled?)]) :accessory :switch}] [quo/list-item {:size :small @@ -117,24 +131,27 @@ :on-press #(re-frame/dispatch [:navigate-to :notifications-servers])}]])) -(defn server-view [{:keys [public-key type registered]}] +(defn server-view + [{:keys [public-key type registered]}] [quo/list-item - {:size :small - :title (str (subs public-key 0 8) - " " - (if (= type notifications/server-type-custom) - (i18n/label :t/custom) - (i18n/label :t/default)) - " " - (if registered - (i18n/label :t/registered) - (i18n/label :t/not-registered)))}]) + {:size :small + :title (str (subs public-key 0 8) + " " + (if (= type notifications/server-type-custom) + (i18n/label :t/custom) + (i18n/label :t/default)) + " " + (if registered + (i18n/label :t/registered) + (i18n/label :t/not-registered)))}]) -(defview notifications-servers [] +(defview notifications-servers + [] (letsubs [servers [:push-notifications/servers]] {:component-did-mount #(re-frame/dispatch [::notifications/fetch-servers])} - [react/scroll-view {:style {:flex 1} - :content-container-style {:padding-vertical 8}} + [react/scroll-view + {:style {:flex 1} + :content-container-style {:padding-vertical 8}} (map server-view servers) [react/keyboard-avoiding-view {} [react/view {:style {:padding-horizontal 20}} @@ -144,10 +161,11 @@ :value @server :on-change-text #(reset! server %) :auto-focus true}]] - [quo/button {:type :secondary - :after :main-icon/next - :disabled (empty? @server) - :on-press #(do - (re-frame/dispatch [::notifications/add-server @server]) - (reset! server ""))} + [quo/button + {:type :secondary + :after :main-icon/next + :disabled (empty? @server) + :on-press #(do + (re-frame/dispatch [::notifications/add-server @server]) + (reset! server ""))} (i18n/label :t/save)]]])) diff --git a/src/status_im/ui/screens/offline_messaging_settings/edit_mailserver/styles.cljs b/src/status_im/ui/screens/offline_messaging_settings/edit_mailserver/styles.cljs index 74bdb1709c..82213c987f 100644 --- a/src/status_im/ui/screens/offline_messaging_settings/edit_mailserver/styles.cljs +++ b/src/status_im/ui/screens/offline_messaging_settings/edit_mailserver/styles.cljs @@ -16,19 +16,21 @@ :margin-horizontal 16}) (def button - {:height 52 - :align-items :center - :justify-content :center - :border-radius 8 - :ios {:opacity 0.9}}) + {:height 52 + :align-items :center + :justify-content :center + :border-radius 8 + :ios {:opacity 0.9}}) (styles/def connect-button (assoc button - :background-color colors/blue)) + :background-color + colors/blue)) (styles/def delete-button (assoc button - :background-color colors/red)) + :background-color + colors/red)) (def button-label {:color colors/white diff --git a/src/status_im/ui/screens/offline_messaging_settings/edit_mailserver/views.cljs b/src/status_im/ui/screens/offline_messaging_settings/edit_mailserver/views.cljs index f5df062f79..b70767378a 100644 --- a/src/status_im/ui/screens/offline_messaging_settings/edit_mailserver/views.cljs +++ b/src/status_im/ui/screens/offline_messaging_settings/edit_mailserver/views.cljs @@ -1,34 +1,39 @@ (ns status-im.ui.screens.offline-messaging-settings.edit-mailserver.views (:require-macros [status-im.utils.views :as views]) - (:require [re-frame.core :as re-frame] - [status-im.ui.components.react :as react] - [status-im.i18n.i18n :as i18n] - [status-im.ui.components.toolbar :as toolbar] - [status-im.ui.screens.offline-messaging-settings.edit-mailserver.styles :as styles] - [clojure.string :as string] - [status-im.qr-scanner.core :as qr-scanner] + (:require [clojure.string :as string] [quo.core :as quo] - [status-im.ui.components.topbar :as topbar])) + [re-frame.core :as re-frame] + [status-im.i18n.i18n :as i18n] + [status-im.qr-scanner.core :as qr-scanner] + [status-im.ui.components.react :as react] + [status-im.ui.components.toolbar :as toolbar] + [status-im.ui.components.topbar :as topbar] + [status-im.ui.screens.offline-messaging-settings.edit-mailserver.styles :as styles])) -(defn connect-button [id] +(defn connect-button + [id] [react/touchable-highlight {:on-press #(re-frame/dispatch [:mailserver.ui/connect-pressed id])} [react/view styles/button-container - [react/view {:style styles/connect-button - :accessibility-label :mailserver-connect-button} + [react/view + {:style styles/connect-button + :accessibility-label :mailserver-connect-button} [react/text {:style styles/button-label} (i18n/label :t/connect)]]]]) -(defn delete-button [id] +(defn delete-button + [id] [react/touchable-highlight {:on-press #(re-frame/dispatch [:mailserver.ui/delete-pressed id])} [react/view styles/button-container - [react/view {:style styles/delete-button - :accessibility-label :mailserver-delete-button} + [react/view + {:style styles/delete-button + :accessibility-label :mailserver-delete-button} [react/text {:style styles/button-label} (i18n/label :t/delete)]]]]) -(views/defview edit-mailserver [] - (views/letsubs [mailserver [:mailserver.edit/mailserver] - connected? [:mailserver.edit/connected?] +(views/defview edit-mailserver + [] + (views/letsubs [mailserver [:mailserver.edit/mailserver] + connected? [:mailserver.edit/connected?] validation-errors [:mailserver.edit/validation-errors]] (let [url (get-in mailserver [:url :value]) id (get-in mailserver [:id :value]) @@ -37,8 +42,9 @@ (not (string/blank? name)) (empty? validation-errors)) invalid-url? (contains? validation-errors :url)] - [react/keyboard-avoiding-view {:style {:flex 1} - :ignore-offset true} + [react/keyboard-avoiding-view + {:style {:flex 1} + :ignore-offset true} [topbar/topbar {:title (i18n/label (if id :t/mailserver-details :t/add-mailserver))}] [react/scroll-view {:keyboard-should-persist-taps :handled} [react/view styles/edit-mailserver-view @@ -49,8 +55,9 @@ :default-value name :on-change-text #(re-frame/dispatch [:mailserver.ui/input-changed :name %]) :auto-focus true}]] - [react/view {:flex 1 - :padding-vertical 8} + [react/view + {:flex 1 + :padding-vertical 8} [quo/text-input {:label (i18n/label :t/mailserver-address) :placeholder (i18n/label :t/mailserver-format) @@ -63,9 +70,10 @@ (i18n/label :t/invalid-format {:format (i18n/label :t/mailserver-format)})) :after {:icon :main-icons/qr - :on-press #(re-frame/dispatch [::qr-scanner/scan-code - {:title (i18n/label :t/add-mailserver) - :handler :mailserver.callback/qr-code-scanned}])}}]] + :on-press #(re-frame/dispatch + [::qr-scanner/scan-code + {:title (i18n/label :t/add-mailserver) + :handler :mailserver.callback/qr-code-scanned}])}}]] (when (and id (not connected?)) [react/view @@ -73,8 +81,9 @@ [delete-button id]])]] [toolbar/toolbar {:right - [quo/button {:type :secondary - :after :main-icon/next - :disabled (not is-valid?) - :on-press #(re-frame/dispatch [:mailserver.ui/save-pressed])} + [quo/button + {:type :secondary + :after :main-icon/next + :disabled (not is-valid?) + :on-press #(re-frame/dispatch [:mailserver.ui/save-pressed])} (i18n/label :t/save)]}]]))) diff --git a/src/status_im/ui/screens/offline_messaging_settings/styles.cljs b/src/status_im/ui/screens/offline_messaging_settings/styles.cljs index 0731dbb578..afb95cb124 100644 --- a/src/status_im/ui/screens/offline_messaging_settings/styles.cljs +++ b/src/status_im/ui/screens/offline_messaging_settings/styles.cljs @@ -3,9 +3,10 @@ [status-im.utils.styles :as styles])) (def wrapper - {:flex 1}) + {:flex 1}) -(styles/defn mailserver-item [] +(styles/defn mailserver-item + [] {:flex-direction :row :align-items :center :justify-content :space-between @@ -16,7 +17,8 @@ (def mailserver-item-name-text {:typography :title}) -(defn mailserver-icon-container [connected?] +(defn mailserver-icon-container + [connected?] {:width 40 :height 40 :border-radius 20 @@ -32,18 +34,18 @@ (def automatic-selection-container {:border-top-width 1 :border-top-color colors/gray-lighter - :margin-top 16}) + :margin-top 16}) (def explanation-text {:color colors/gray}) (def use-history-explanation-text-container - {:margin-right 16 - :margin-left 16 - :margin-top 8 - :margin-bottom 16}) + {:margin-right 16 + :margin-left 16 + :margin-top 8 + :margin-bottom 16}) (def history-nodes-label - {:color colors/gray + {:color colors/gray :padding-horizontal 16 - :margin-top 48}) + :margin-top 48}) diff --git a/src/status_im/ui/screens/offline_messaging_settings/views.cljs b/src/status_im/ui/screens/offline_messaging_settings/views.cljs index d1c01e49f0..c9e462280c 100644 --- a/src/status_im/ui/screens/offline_messaging_settings/views.cljs +++ b/src/status_im/ui/screens/offline_messaging_settings/views.cljs @@ -1,17 +1,18 @@ (ns status-im.ui.screens.offline-messaging-settings.views (:require-macros [status-im.utils.views :as views]) - (:require [re-frame.core :as re-frame] + (:require [quo.core :as quo] + [quo.design-system.colors :as colors] + [re-frame.core :as re-frame] [status-im.i18n.i18n :as i18n] [status-im.ui.components.icons.icons :as icons] [status-im.ui.components.list.views :as list] [status-im.ui.components.react :as react] - [quo.design-system.colors :as colors] - [status-im.ui.screens.offline-messaging-settings.styles :as styles] - [status-im.ui.screens.profile.components.views :as profile.components] [status-im.ui.components.topbar :as topbar] - [quo.core :as quo])) + [status-im.ui.screens.offline-messaging-settings.styles :as styles] + [status-im.ui.screens.profile.components.views :as profile.components])) -(defn pinned-state [pinned?] +(defn pinned-state + [pinned?] [react/view {:style styles/automatic-selection-container} [react/view {:style styles/switch-container} [profile.components/settings-switch-item @@ -24,18 +25,20 @@ [react/text {:style styles/explanation-text} (i18n/label :t/mailserver-automatic-switch-explanation)]]]) -(defn render-row [{:keys [name id custom]} _ _ {:keys [current-mailserver-id preferred-mailserver-id]}] - (let [pinned? preferred-mailserver-id +(defn render-row + [{:keys [name id custom]} _ _ {:keys [current-mailserver-id preferred-mailserver-id]}] + (let [pinned? preferred-mailserver-id connected? (= id current-mailserver-id) - visible? (or pinned? ; show everything when auto selection is turned off - (and (not pinned?) ; auto selection turned on - (= current-mailserver-id id)))] ; show only the selected server + visible? (or pinned? ; show everything when auto selection is turned off + (and (not pinned?) ; auto selection turned on + (= current-mailserver-id id)))] ; show only the selected server (when visible? [react/touchable-highlight - {:on-press (when pinned? #(if custom - (re-frame/dispatch [:mailserver.ui/custom-mailserver-selected id]) - (re-frame/dispatch [:mailserver.ui/default-mailserver-selected id]))) + {:on-press (when pinned? + #(if custom + (re-frame/dispatch [:mailserver.ui/custom-mailserver-selected id]) + (re-frame/dispatch [:mailserver.ui/default-mailserver-selected id]))) :accessibility-label :mailserver-item} [react/view (styles/mailserver-item) [react/text {:style styles/mailserver-item-name-text} @@ -45,16 +48,17 @@ [quo/radio {:value connected?}] [icons/icon :check {:color colors/blue}])]]))) -(views/defview offline-messaging-settings [] +(views/defview offline-messaging-settings + [] (views/letsubs [current-mailserver-id [:mailserver/current-id] preferred-mailserver-id [:mailserver/preferred-id] mailservers [:mailserver/fleet-mailservers] {:keys [use-mailservers?]} [:multiaccount]] [react/view {:style styles/wrapper} [topbar/topbar - {:title (i18n/label :t/history-nodes) + {:title (i18n/label :t/history-nodes) :right-accessories - [{:icon :main-icons/add-circle + [{:icon :main-icons/add-circle :on-press #(re-frame/dispatch [:mailserver.ui/add-pressed])}]}] [react/scroll-view @@ -62,7 +66,8 @@ [profile.components/settings-switch-item {:label-kw :t/offline-messaging-use-history-nodes :value use-mailservers? - :action-fn #(re-frame/dispatch [:mailserver.ui/use-history-switch-pressed (not use-mailservers?)])}]] + :action-fn #(re-frame/dispatch [:mailserver.ui/use-history-switch-pressed + (not use-mailservers?)])}]] [react/view {:style styles/use-history-explanation-text-container} [react/text {:style styles/explanation-text} (i18n/label :t/offline-messaging-use-history-explanation)]] @@ -73,9 +78,10 @@ [react/text {:style styles/history-nodes-label} (i18n/label :t/history-nodes)] - [list/flat-list {:data (vals mailservers) - :default-separator? false - :key-fn :name - :render-data {:current-mailserver-id current-mailserver-id - :preferred-mailserver-id preferred-mailserver-id} - :render-fn render-row}]])]])) + [list/flat-list + {:data (vals mailservers) + :default-separator? false + :key-fn :name + :render-data {:current-mailserver-id current-mailserver-id + :preferred-mailserver-id preferred-mailserver-id} + :render-fn render-row}]])]])) diff --git a/src/status_im/ui/screens/onboarding/intro/styles.cljs b/src/status_im/ui/screens/onboarding/intro/styles.cljs index c5ba46d44f..0b7b07de3c 100644 --- a/src/status_im/ui/screens/onboarding/intro/styles.cljs +++ b/src/status_im/ui/screens/onboarding/intro/styles.cljs @@ -6,17 +6,19 @@ (def progress-size 36) (def intro-view - {:flex 1 - :justify-content :flex-end - :margin-bottom 12}) + {:flex 1 + :justify-content :flex-end + :margin-bottom 12}) -(defn dot-selector [] +(defn dot-selector + [] {:flex-direction :row :justify-content :space-between - :margin-bottom 16 + :margin-bottom 16 :align-items :center}) -(defn dot-style [active] +(defn dot-style + [active] {:background-color colors/blue-light :overflow :hidden :opacity 1 @@ -25,14 +27,16 @@ :height dot-size :border-radius 3}) -(defn dot-progress [active progress] +(defn dot-progress + [active progress] {:background-color colors/blue :height dot-size :width progress-size :opacity (animated/mix active 0 1) :transform [{:translateX (animated/mix progress (- progress-size) 0)}]}) -(defn wizard-text-with-height [height] +(defn wizard-text-with-height + [height] (merge {:color colors/gray :text-align :center} (when-not (zero? height) diff --git a/src/status_im/ui/screens/onboarding/intro/views.cljs b/src/status_im/ui/screens/onboarding/intro/views.cljs index fe73d067ff..9426cc4e7f 100644 --- a/src/status_im/ui/screens/onboarding/intro/views.cljs +++ b/src/status_im/ui/screens/onboarding/intro/views.cljs @@ -1,23 +1,25 @@ (ns status-im.ui.screens.onboarding.intro.views (:require [quo.animated :as animated] + [quo.core :as quo] + [quo.design-system.colors :as colors] + [quo.design-system.typography :as typography] [quo.react-native :as rn] - [status-im.ui.screens.onboarding.intro.styles :as styles] - [status-im.ui.components.react :as react] + [re-frame.core :as re-frame] [reagent.core :as reagent] [status-im.i18n.i18n :as i18n] [status-im.react-native.resources :as resources] - [quo.core :as quo] - [quo.design-system.typography :as typography] - [re-frame.core :as re-frame] - [quo.design-system.colors :as colors])) + [status-im.ui.components.react :as react] + [status-im.ui.screens.onboarding.intro.styles :as styles])) (defonce index (reagent/atom 0)) -(defn code [val] +(defn code + [val] ^{:key val} [animated/code {:exec val}]) -(defn dot [] +(defn dot + [] (let [active (animated/value 0) active-transition (animated/with-timing-transition active {:duration 100})] (fn [{:keys [selected progress]}] @@ -25,57 +27,66 @@ [code (animated/set active (if selected 1 0))] [animated/view {:style (styles/dot-progress active-transition progress)}]]))) -(defn dots-selector [{:keys [n progress]}] +(defn dots-selector + [{:keys [n progress]}] (let [selected @index] [react/view {:style (styles/dot-selector)} (for [i (range n)] ^{:key i} - [dot {:progress progress - :selected (= selected i)}])])) + [dot + {:progress progress + :selected (= selected i)}])])) -(defn slides-view [slides width] - (let [height (reagent/atom 0) - text-height (reagent/atom 0) - text-temp-height (atom 0) - text-temp-timer (atom nil)] +(defn slides-view + [slides width] + (let [height (reagent/atom 0) + text-height (reagent/atom 0) + text-temp-height (atom 0) + text-temp-timer (atom nil)] (fn [_] [:<> (doall (for [s slides] ^{:key (:title s)} - [react/view {:style {:flex 1 - :width width - :justify-content :flex-end - :align-items :center - :padding-horizontal 32}} + [react/view + {:style {:flex 1 + :width width + :justify-content :flex-end + :align-items :center + :padding-horizontal 32}} (let [size (min width @height)] - [react/view {:style {:flex 1} - :on-layout (fn [^js e] - (let [new-height (-> e .-nativeEvent .-layout .-height)] - (swap! height #(if (pos? %) (min % new-height) new-height))))} - [react/image {:source (:image s) - :resize-mode :contain - :style {:width size - :height size}}]]) - [quo/text {:style styles/wizard-title - :align :center - :weight :bold - :size :x-large} + [react/view + {:style {:flex 1} + :on-layout (fn [^js e] + (let [new-height (-> e .-nativeEvent .-layout .-height)] + (swap! height #(if (pos? %) (min % new-height) new-height))))} + [react/image + {:source (:image s) + :resize-mode :contain + :style {:width size + :height size}}]]) + [quo/text + {:style styles/wizard-title + :align :center + :weight :bold + :size :x-large} (i18n/label (:title s))] - [quo/text {:style (styles/wizard-text-with-height @text-height) - :on-layout - (fn [^js e] - (let [new-height (-> e .-nativeEvent .-layout .-height)] - (when (and (not= new-height @text-temp-height) - (not (zero? new-height)) - (< new-height 200)) - (swap! text-temp-height #(if (pos? %) (max % new-height) new-height)) - (when @text-temp-timer (js/clearTimeout @text-temp-timer)) - (reset! text-temp-timer - (js/setTimeout #(reset! text-height @text-temp-height) 500)))))} + [quo/text + {:style (styles/wizard-text-with-height @text-height) + :on-layout + (fn [^js e] + (let [new-height (-> e .-nativeEvent .-layout .-height)] + (when (and (not= new-height @text-temp-height) + (not (zero? new-height)) + (< new-height 200)) + (swap! text-temp-height #(if (pos? %) (max % new-height) new-height)) + (when @text-temp-timer (js/clearTimeout @text-temp-timer)) + (reset! text-temp-timer + (js/setTimeout #(reset! text-height @text-temp-height) 500)))))} (i18n/label (:text s))]]))]))) -(defn carousel [slides width] +(defn carousel + [slides width] ;;TODO this is really not the best implementation, must be a better way (let [scroll-x (reagent/atom 0) scroll-view-ref (atom nil) @@ -86,12 +97,15 @@ finished (animated/value 0) clock (animated/clock) go-next (fn [] - (let [x (if (>= @scroll-x (- (* (dec (count slides)) - width) 5)) + (let [x (if (>= @scroll-x + (- (* (dec (count slides)) + width) + 5)) 0 (+ @scroll-x width))] (reset! index (Math/round (/ x width))) - (some-> ^js @scroll-view-ref (.scrollTo #js {:x x :animated true})))) + (some-> ^js @scroll-view-ref + (.scrollTo #js {:x x :animated true})))) code (animated/block [(animated/cond* (animated/and* (animated/not* (animated/clock-running clock)) autoscroll) @@ -100,63 +114,71 @@ (animated/not* autoscroll)) [(animated/stop-clock clock) (animated/set finished 1)]) - (animated/set progress (animated/cancelable-loop - {:clock clock - :finished finished - :duration 4000 - :on-reach go-next}))]) + (animated/set progress + (animated/cancelable-loop + {:clock clock + :finished finished + :duration 4000 + :on-reach go-next}))]) cancel-animation (fn [] (reset! manual-scroll true) (animated/set-value autoscroll 0)) restart-animation (fn [] (animated/set-value autoscroll 1))] (fn [_ _] - [react/view {:style {:align-items :center - :flex 1 - :justify-content :flex-end}} + [react/view + {:style {:align-items :center + :flex 1 + :justify-content :flex-end}} [animated/code {:exec code}] - [react/scroll-view {:horizontal true - :paging-enabled true - :ref #(reset! scroll-view-ref %) - :shows-vertical-scroll-indicator false - :shows-horizontal-scroll-indicator false - :pinch-gesture-enabled false - :scroll-event-throttle 16 - :on-scroll #(let [x (.-nativeEvent.contentOffset.x ^js %)] - (when @manual-scroll - ;; NOTE: Will be not synced if velocity is big - (reset! index (Math/round (/ x width)))) - (reset! scroll-x x)) - :on-scroll-begin-drag cancel-animation - :on-scroll-end-drag restart-animation - :on-momentum-scroll-end #(reset! manual-scroll false) - :style {:margin-bottom 16}} + [react/scroll-view + {:horizontal true + :paging-enabled true + :ref #(reset! scroll-view-ref %) + :shows-vertical-scroll-indicator false + :shows-horizontal-scroll-indicator false + :pinch-gesture-enabled false + :scroll-event-throttle 16 + :on-scroll #(let [x (.-nativeEvent.contentOffset.x ^js %)] + (when @manual-scroll + ;; NOTE: Will be not synced if velocity is big + (reset! index (Math/round (/ x width)))) + (reset! scroll-x x)) + :on-scroll-begin-drag cancel-animation + :on-scroll-end-drag restart-animation + :on-momentum-scroll-end #(reset! manual-scroll false) + :style {:margin-bottom 16}} [slides-view slides width]] - [dots-selector {:progress progress - :n (count slides)}]]))) + [dots-selector + {:progress progress + :n (count slides)}]]))) (defonce tos-accepted (reagent/atom false)) -(defn intro [] +(defn intro + [] [react/view {:style styles/intro-view} - [carousel [{:image (resources/get-theme-image :chat) - :title :intro-title1 - :text :intro-text1} - {:image (resources/get-theme-image :wallet) - :title :intro-title2 - :text :intro-text2} - {:image (resources/get-theme-image :browser) - :title :intro-title3 - :text :intro-text3}] + [carousel + [{:image (resources/get-theme-image :chat) + :title :intro-title1 + :text :intro-text1} + {:image (resources/get-theme-image :wallet) + :title :intro-title2 + :text :intro-text2} + {:image (resources/get-theme-image :browser) + :title :intro-title3 + :text :intro-text3}] @(re-frame/subscribe [:dimensions/window-width])] [react/view {:style {:align-items :center}} - [react/view {:flex-direction :row - :justify-content :space-between - :align-items :center - :margin-top 36 - :margin-bottom 24} - [quo/checkbox {:value @tos-accepted - :on-change #(swap! tos-accepted not)}] + [react/view + {:flex-direction :row + :justify-content :space-between + :align-items :center + :margin-top 36 + :margin-bottom 24} + [quo/checkbox + {:value @tos-accepted + :on-change #(swap! tos-accepted not)}] [rn/touchable-opacity {:test-ID :terms-of-service :on-press #(swap! tos-accepted not)} [react/nested-text {:style {:margin-left 12}} (i18n/label :t/accept-status-tos-prefix) @@ -167,12 +189,13 @@ " " (i18n/label :t/terms-of-service)]]]] [react/view {:style {:margin-bottom 24}} - [quo/button {:test-ID :get-started - :disabled (not @tos-accepted) - :on-press #(do (re-frame/dispatch [:init-root :onboarding]) - ;; clear atom state for next use - (reset! tos-accepted false) - (re-frame/dispatch [:hide-terms-of-services-opt-in-screen]))} + [quo/button + {:test-ID :get-started + :disabled (not @tos-accepted) + :on-press #(do (re-frame/dispatch [:init-root :onboarding]) + ;; clear atom state for next use + (reset! tos-accepted false) + (re-frame/dispatch [:hide-terms-of-services-opt-in-screen]))} (i18n/label :t/get-started)]] [react/text {:style {:color colors/blue} diff --git a/src/status_im/ui/screens/onboarding/keys/views.cljs b/src/status_im/ui/screens/onboarding/keys/views.cljs index 399a58c68b..866ba5576a 100644 --- a/src/status_im/ui/screens/onboarding/keys/views.cljs +++ b/src/status_im/ui/screens/onboarding/keys/views.cljs @@ -1,74 +1,84 @@ (ns status-im.ui.screens.onboarding.keys.views - (:require [re-frame.core :as re-frame] + (:require [quo.core :as quo] + [re-frame.core :as re-frame] + [reagent.core :as reagent] [status-im.constants :as constants] [status-im.i18n.i18n :as i18n] [status-im.react-native.resources :as resources] [status-im.ui.components.react :as react] + [status-im.ui.components.topbar :as topbar] [status-im.ui.screens.onboarding.styles :as styles] + [status-im.ui.screens.onboarding.views :as ui] [status-im.utils.gfycat.core :as gfy] [status-im.utils.identicon :as identicon] - [utils.debounce :refer [dispatch-and-chill]] - [quo.core :as quo] [status-im.utils.utils :as utils] - [status-im.ui.screens.onboarding.views :as ui] - [status-im.ui.components.topbar :as topbar] - [reagent.core :as reagent]) + [utils.debounce :refer [dispatch-and-chill]]) (:require-macros [status-im.utils.views :refer [defview letsubs]])) -(defview choose-a-chat-name [] +(defview choose-a-chat-name + [] (letsubs [{:keys [multiaccounts selected-id]} [:intro-wizard/choose-key]] [:<> [topbar/topbar {:border-bottom false - :navigation {:label (i18n/label :t/cancel) - :on-press (fn [] - (utils/show-question - (i18n/label :t/are-you-sure-to-cancel) - (i18n/label :t/you-will-start-from-scratch) - #(re-frame/dispatch [:navigate-back])))}}] + :navigation {:label (i18n/label :t/cancel) + :on-press (fn [] + (utils/show-question + (i18n/label :t/are-you-sure-to-cancel) + (i18n/label :t/you-will-start-from-scratch) + #(re-frame/dispatch [:navigate-back])))}}] [ui/title-with-description :t/intro-wizard-title2 :t/intro-wizard-text2] [ui/learn-more :t/about-names-title :t/about-names-content] - [react/view {:style {:flex 1 - :justify-content :center}} + [react/view + {:style {:flex 1 + :justify-content :center}} [react/scroll-view {:style {:max-height 410} :content-container-style {:justify-content :flex-start}} (for [[acc accessibility-n] (map vector multiaccounts (range (count multiaccounts)))] - (let [selected? (= (:id acc) selected-id) + (let [selected? (= (:id acc) selected-id) public-key (get-in acc [:derived constants/path-whisper-keyword :public-key])] ^{:key public-key} - [quo/list-item {:accessibility-label (keyword (str "select-account-button-" accessibility-n)) - :active selected? - :title [quo/text {:number-of-lines 2 - :weight :medium - :ellipsize-mode :middle - :accessibility-label :username} - (gfy/generate-gfy public-key)] - :subtitle [quo/text {:weight :monospace - :color :secondary} - (utils/get-shortened-address public-key)] - :accessory :radio - :on-press #(re-frame/dispatch [:intro-wizard/on-key-selected (:id acc)]) - :icon [react/image {:source {:uri (identicon/identicon public-key)} - :resize-mode :cover - :style styles/multiaccount-image}]}]))]] + [quo/list-item + {:accessibility-label (keyword (str "select-account-button-" accessibility-n)) + :active selected? + :title [quo/text + {:number-of-lines 2 + :weight :medium + :ellipsize-mode :middle + :accessibility-label :username} + (gfy/generate-gfy public-key)] + :subtitle [quo/text + {:weight :monospace + :color :secondary} + (utils/get-shortened-address public-key)] + :accessory :radio + :on-press #(re-frame/dispatch [:intro-wizard/on-key-selected (:id acc)]) + :icon [react/image + {:source {:uri (identicon/identicon public-key)} + :resize-mode :cover + :style styles/multiaccount-image}]}]))]] [ui/next-button #(dispatch-and-chill [:navigate-to :select-key-storage] 300) false]])) -(defn get-your-keys-image [] +(defn get-your-keys-image + [] (let [dimensions (reagent/atom {})] (fn [] ;;TODO this is not really the best way to do it, resize is visible, we need to find a better way - [react/view {:on-layout (fn [^js e] - (reset! dimensions (js->clj (-> e .-nativeEvent .-layout) :keywordize-keys true))) - :style {:align-items :center - :justify-content :center - :flex 1}} + [react/view + {:on-layout (fn [^js e] + (reset! dimensions (js->clj (-> e .-nativeEvent .-layout) :keywordize-keys true))) + :style {:align-items :center + :justify-content :center + :flex 1}} (let [image-size (- (min (:width @dimensions) (:height @dimensions)) 40)] - [react/image {:source (resources/get-theme-image :keys) - :resize-mode :contain - :style {:width image-size :height image-size}}])]))) + [react/image + {:source (resources/get-theme-image :keys) + :resize-mode :contain + :style {:width image-size :height image-size}}])]))) -(defview get-your-keys [] +(defview get-your-keys + [] (letsubs [{:keys [processing?]} [:intro-wizard/choose-key]] [:<> [ui/title-with-description :t/intro-wizard-title1 :t/intro-wizard-text1] @@ -76,14 +86,15 @@ (if processing? [react/view {:style {:align-items :center}} [react/view {:min-height 46 :max-height 46 :align-self :stretch :margin-bottom 16} - [react/activity-indicator {:animating true - :size :large}]] + [react/activity-indicator + {:animating true + :size :large}]] [react/text {:style (assoc (styles/wizard-text) :margin-top 20 :margin-bottom 16)} (i18n/label :t/generating-keys)]] [react/view {:style {:align-items :center}} [react/view {:style (assoc styles/bottom-button :margin-bottom 16)} [quo/button - {:test-ID :generate-keys + {:test-ID :generate-keys ;:disabled existing-account? :on-press #(re-frame/dispatch [:generate-and-derive-addresses]) :accessibility-label :onboarding-next-button} diff --git a/src/status_im/ui/screens/onboarding/notifications/views.cljs b/src/status_im/ui/screens/onboarding/notifications/views.cljs index 5c928dd58b..dbef92d1ad 100644 --- a/src/status_im/ui/screens/onboarding/notifications/views.cljs +++ b/src/status_im/ui/screens/onboarding/notifications/views.cljs @@ -1,30 +1,40 @@ (ns status-im.ui.screens.onboarding.notifications.views - (:require [status-im.ui.components.react :as react] + (:require [quo.core :as quo] [quo.design-system.colors :as colors] - [status-im.i18n.i18n :as i18n] - [status-im.react-native.resources :as resources] - [re-frame.core :as re-frame] - [quo.core :as quo] [quo.platform :as platform] - [status-im.notifications.core :as notifications])) + [re-frame.core :as re-frame] + [status-im.i18n.i18n :as i18n] + [status-im.notifications.core :as notifications] + [status-im.react-native.resources :as resources] + [status-im.ui.components.react :as react])) -(defn notifications-onboarding [] - [react/view {:flex 1 :background-color colors/white - :align-items :center :padding-bottom 16} - [react/text {:style {:margin-top 72 :margin-bottom 16 - :typography :header}} +(defn notifications-onboarding + [] + [react/view + {:flex 1 + :background-color colors/white + :align-items :center + :padding-bottom 16} + [react/text + {:style {:margin-top 72 + :margin-bottom 16 + :typography :header}} (i18n/label :t/private-notifications)] [react/text {:style {:color colors/gray :text-align :center :margin-horizontal 24}} (i18n/label :t/private-notifications-descr)] [react/view {:flex 1 :align-items :center :justify-content :center} - [react/image {:source (resources/get-image :notifications) - :style {:width 118 - :height 118}}]] - [quo/button {:on-press #(do (re-frame/dispatch [::notifications/switch true platform/ios?]) - (re-frame/dispatch [:init-root :welcome])) - :accessibility-label :enable-notifications} + [react/image + {:source (resources/get-image :notifications) + :style {:width 118 + :height 118}}]] + [quo/button + {:on-press #(do (re-frame/dispatch [::notifications/switch true platform/ios?]) + (re-frame/dispatch [:init-root :welcome])) + :accessibility-label :enable-notifications} (i18n/label :t/intro-wizard-title6)] - [quo/button {:type :secondary :style {:margin-top 8} - :accessibility-label :maybe-later - :on-press #(re-frame/dispatch [:init-root :welcome])} + [quo/button + {:type :secondary + :style {:margin-top 8} + :accessibility-label :maybe-later + :on-press #(re-frame/dispatch [:init-root :welcome])} (i18n/label :t/maybe-later)]]) diff --git a/src/status_im/ui/screens/onboarding/password/views.cljs b/src/status_im/ui/screens/onboarding/password/views.cljs index 49dcdb7658..f12155bb0c 100644 --- a/src/status_im/ui/screens/onboarding/password/views.cljs +++ b/src/status_im/ui/screens/onboarding/password/views.cljs @@ -1,98 +1,113 @@ (ns status-im.ui.screens.onboarding.password.views - (:require [re-frame.core :as re-frame] - [reagent.core :as reagent] - [status-im.ui.components.toolbar :as toolbar] - [status-im.i18n.i18n :as i18n] - [status-im.constants :as const] - [utils.security.core :as security] + (:require [quo.core :as quo] [quo.react-native :as rn] - [quo.core :as quo])) + [re-frame.core :as re-frame] + [reagent.core :as reagent] + [status-im.constants :as const] + [status-im.i18n.i18n :as i18n] + [status-im.ui.components.toolbar :as toolbar] + [utils.security.core :as security])) -(defn validate-password [password] +(defn validate-password + [password] (>= (count password) const/min-password-length)) -(defn confirm-password [password confirm] +(defn confirm-password + [password confirm] (= password confirm)) -(defn screen [] +(defn screen + [] (let [password (reagent/atom nil) confirm (reagent/atom nil) processing? (reagent/atom nil) show-error (reagent/atom nil) confirm-ref (atom nil)] (fn [] - (let [valid-password (validate-password @password) - valid-form (confirm-password @password @confirm) + (let [valid-password (validate-password @password) + valid-form (confirm-password @password @confirm) {:keys [recovering?]} @(re-frame/subscribe [:intro-wizard]) - on-submit (fn [] - (when (not @processing?) - (if (and valid-password valid-form) - (do (reset! show-error false) - (reset! processing? true) - (if recovering? - (re-frame/dispatch [:multiaccounts.recover/enter-password-next-pressed @password]) - (re-frame/dispatch [:create-multiaccount @password]))) - (reset! show-error true))))] + on-submit (fn [] + (when (not @processing?) + (if (and valid-password valid-form) + (do (reset! show-error false) + (reset! processing? true) + (if recovering? + (re-frame/dispatch + [:multiaccounts.recover/enter-password-next-pressed + @password]) + (re-frame/dispatch [:create-multiaccount @password]))) + (reset! show-error true))))] [rn/keyboard-avoiding-view {:flex 1} - [rn/view {:style {:flex 1 - :justify-content :space-between - :padding-vertical 16 - :padding-horizontal 16}} - [quo/text {:weight :bold - :align :center - :size :x-large} + [rn/view + {:style {:flex 1 + :justify-content :space-between + :padding-vertical 16 + :padding-horizontal 16}} + [quo/text + {:weight :bold + :align :center + :size :x-large} (i18n/label :intro-wizard-title-alt4)] [rn/view [rn/view {:style {:padding 16}} - [quo/text-input {:test-ID :password-placeholder - :secure-text-entry true - :auto-capitalize :none - :auto-focus true - :show-cancel false - :accessibility-label :password-input - :placeholder (i18n/label :t/password-placeholder) - :on-change-text #(reset! password (security/mask-data %)) - :return-key-type :next - :on-submit-editing #(when valid-password - (some-> ^js @confirm-ref .focus))}]] - [rn/view {:style {:padding 16 - :opacity (if-not valid-password 0.33 1)}} - [quo/text-input {:test-ID :confirm-password-placeholder - :secure-text-entry true - :get-ref #(reset! confirm-ref %) - :auto-capitalize :none - :show-cancel false - :accessibility-label :password-input - :editable valid-password - :placeholder (i18n/label :t/confirm-password-placeholder) - :return-key-type :go - :error (when @show-error (i18n/label :t/password_error1)) - :blur-on-submit true - :on-focus #(reset! show-error false) - :on-submit-editing on-submit - :on-change-text #(do - (reset! confirm (security/mask-data %)) - (cond - (> (count @password) (count @confirm)) - (reset! show-error false) + [quo/text-input + {:test-ID :password-placeholder + :secure-text-entry true + :auto-capitalize :none + :auto-focus true + :show-cancel false + :accessibility-label :password-input + :placeholder (i18n/label :t/password-placeholder) + :on-change-text #(reset! password (security/mask-data %)) + :return-key-type :next + :on-submit-editing #(when valid-password + (some-> ^js @confirm-ref + .focus))}]] + [rn/view + {:style {:padding 16 + :opacity (if-not valid-password 0.33 1)}} + [quo/text-input + {:test-ID :confirm-password-placeholder + :secure-text-entry true + :get-ref #(reset! confirm-ref %) + :auto-capitalize :none + :show-cancel false + :accessibility-label :password-input + :editable valid-password + :placeholder (i18n/label :t/confirm-password-placeholder) + :return-key-type :go + :error (when @show-error (i18n/label :t/password_error1)) + :blur-on-submit true + :on-focus #(reset! show-error false) + :on-submit-editing on-submit + :on-change-text #(do + (reset! confirm (security/mask-data %)) + (cond + (> (count @password) (count @confirm)) + (reset! show-error false) - (not (confirm-password @password @confirm)) - (reset! show-error true) + (not (confirm-password @password @confirm)) + (reset! show-error true) - :else (reset! show-error false)))}]]] - [quo/text {:color :secondary - :align :center - :size :small} + :else (reset! show-error + false)))}]]] + [quo/text + {:color :secondary + :align :center + :size :small} (i18n/label :t/password-description)]] [toolbar/toolbar (merge {:show-border? true} (if @processing? {:center - [rn/view {:align-items :center - :justify-content :center - :flex-direction :row} - [rn/activity-indicator {:size :small - :animating true}] + [rn/view + {:align-items :center + :justify-content :center + :flex-direction :row} + [rn/activity-indicator + {:size :small + :animating true}] [rn/view {:padding-horizontal 8} [quo/text {:color :secondary} (i18n/label :t/processing)]]]} diff --git a/src/status_im/ui/screens/onboarding/phrase/view.cljs b/src/status_im/ui/screens/onboarding/phrase/view.cljs index db5299863b..231adcad92 100644 --- a/src/status_im/ui/screens/onboarding/phrase/view.cljs +++ b/src/status_im/ui/screens/onboarding/phrase/view.cljs @@ -1,64 +1,75 @@ (ns status-im.ui.screens.onboarding.phrase.view (:require-macros [status-im.utils.views :refer [defview letsubs]]) - (:require [status-im.ui.components.react :as react] + (:require [quo.core :as quo] [quo.design-system.colors :as colors] - [status-im.i18n.i18n :as i18n] [re-frame.core :as re-frame] - [utils.security.core :as security] - [quo.core :as quo] - [status-im.utils.datetime :as datetime] + [reagent.core :as reagent] + [status-im.i18n.i18n :as i18n] + [status-im.ui.components.react :as react] [status-im.ui.screens.onboarding.views :as ui] - [utils.debounce :refer [dispatch-and-chill]] + [status-im.utils.datetime :as datetime] [status-im.utils.utils :as utils] - [reagent.core :as reagent])) + [utils.debounce :refer [dispatch-and-chill]] + [utils.security.core :as security])) -(defview wizard-recovery-success [] +(defview wizard-recovery-success + [] (letsubs [{:keys [pubkey processing? name identicon]} [:intro-wizard/recovery-success] - existing-account? [:intro-wizard/recover-existing-account?]] - [react/view {:style {:flex 1 - :justify-content :space-between}} + existing-account? [:intro-wizard/recover-existing-account?]] + [react/view + {:style {:flex 1 + :justify-content :space-between}} [ui/title-with-description :t/keycard-recovery-success-header :t/recovery-success-text] - [react/view {:flex 1 - :justify-content :space-between - :background-color colors/white} - [react/view {:flex 1 - :justify-content :space-between - :align-items :center} - [react/view {:flex-direction :column - :flex 1 - :justify-content :center - :align-items :center} - [react/view {:margin-horizontal 16 - :flex-direction :column} - [react/view {:justify-content :center - :align-items :center - :margin-bottom 11} - [react/image {:source {:uri identicon} - :style {:width 61 - :height 61 - :border-radius 30 - :border-width 1 - :border-color colors/black-transparent}}]] - [react/text {:style {:text-align :center - :color colors/black - :font-weight "500"} - :number-of-lines 1 - :ellipsize-mode :middle} + [react/view + {:flex 1 + :justify-content :space-between + :background-color colors/white} + [react/view + {:flex 1 + :justify-content :space-between + :align-items :center} + [react/view + {:flex-direction :column + :flex 1 + :justify-content :center + :align-items :center} + [react/view + {:margin-horizontal 16 + :flex-direction :column} + [react/view + {:justify-content :center + :align-items :center + :margin-bottom 11} + [react/image + {:source {:uri identicon} + :style {:width 61 + :height 61 + :border-radius 30 + :border-width 1 + :border-color colors/black-transparent}}]] + [react/text + {:style {:text-align :center + :color colors/black + :font-weight "500"} + :number-of-lines 1 + :ellipsize-mode :middle} name] - [quo/text {:style {:margin-top 4} - :monospace true - :color :secondary - :align :center - :number-of-lines 1 - :ellipsize-mode :middle} + [quo/text + {:style {:margin-top 4} + :monospace true + :color :secondary + :align :center + :number-of-lines 1 + :ellipsize-mode :middle} (utils/get-shortened-address pubkey)]]]]] [ui/next-button #(dispatch-and-chill [:multiaccounts.recover/re-encrypt-pressed] 300) (or processing? existing-account?)]])) -(defn enter-phrase [_] +(defn enter-phrase + [_] (let [show-bip39-password? (reagent/atom false) - pressed-in-at (atom nil)] + pressed-in-at (atom nil)] (fn [] (let [{:keys [processing? passphrase-word-count @@ -66,9 +77,10 @@ passphrase-error]} @(re-frame/subscribe [:intro-wizard/enter-phrase])] [react/keyboard-avoiding-view {:flex 1} - [quo/text {:weight :bold - :align :center - :size :x-large} + [quo/text + {:weight :bold + :align :center + :size :x-large} (i18n/label :t/multiaccounts-recover-enter-phrase-title)] [react/pressable {:style {:background-color colors/white @@ -97,12 +109,14 @@ :keyboard-type :visible-password :monospace true}] [react/view {:align-items :flex-end} - [react/view {:flex-direction :row - :align-items :center - :padding-vertical 8 - :opacity (if passphrase-word-count 1 0)} - [quo/text {:color (if next-button-disabled? :secondary :main) - :size :small} + [react/view + {:flex-direction :row + :align-items :center + :padding-vertical 8 + :opacity (if passphrase-word-count 1 0)} + [quo/text + {:color (if next-button-disabled? :secondary :main) + :size :small} (when-not next-button-disabled? "✓ ") (i18n/label-pluralize passphrase-word-count :t/words-n)]]] @@ -117,20 +131,23 @@ {:on-change-text #(re-frame/dispatch [:multiaccounts.recover/enter-passphrase-input-changed (security/mask-data %)]) - :placeholder (i18n/label :t/bip39-password-placeholder) - :show-cancel false}])]] + :placeholder (i18n/label :t/bip39-password-placeholder) + :show-cancel false}])]] [react/view {:align-items :center} - [react/text {:style {:color colors/gray - :font-size 14 - :margin-bottom 8 - :text-align :center}} + [react/text + {:style {:color colors/gray + :font-size 14 + :margin-bottom 8 + :text-align :center}} (i18n/label :t/multiaccounts-recover-enter-phrase-text)] (when processing? [react/view {:flex 1 :align-items :center} - [react/activity-indicator {:size :large - :animating true}] - [react/text {:style {:color colors/gray - :margin-top 8}} + [react/activity-indicator + {:size :large + :animating true}] + [react/text + {:style {:color colors/gray + :margin-top 8}} (i18n/label :t/processing)]])] [ui/next-button #(dispatch-and-chill [:multiaccounts.recover/enter-phrase-next-pressed] 300) diff --git a/src/status_im/ui/screens/onboarding/storage/views.cljs b/src/status_im/ui/screens/onboarding/storage/views.cljs index d772d3aa2d..cbae1e7ddd 100644 --- a/src/status_im/ui/screens/onboarding/storage/views.cljs +++ b/src/status_im/ui/screens/onboarding/storage/views.cljs @@ -1,41 +1,46 @@ (ns status-im.ui.screens.onboarding.storage.views - (:require [re-frame.core :as re-frame] + (:require [quo.core :as quo] + [re-frame.core :as re-frame] [status-im.i18n.i18n :as i18n] [status-im.ui.components.react :as react] - [utils.debounce :refer [dispatch-and-chill]] - [quo.core :as quo] - [status-im.ui.screens.onboarding.views :as ui]) + [status-im.ui.screens.onboarding.views :as ui] + [utils.debounce :refer [dispatch-and-chill]]) (:require-macros [status-im.utils.views :refer [defview letsubs]])) -(defn storage-entry [{:keys [type icon title desc]} selected-storage-type] +(defn storage-entry + [{:keys [type icon title desc]} selected-storage-type] [:<> [quo/list-header (i18n/label type)] - [quo/list-item {:accessibility-label (keyword (str "select-storage-" type)) - :on-press #(re-frame/dispatch [:intro-wizard/on-key-storage-selected type]) - :icon icon - :accessory :radio - :active (= type selected-storage-type) - :title (i18n/label title) - :subtitle (i18n/label desc) - :subtitle-max-lines 2}]]) + [quo/list-item + {:accessibility-label (keyword (str "select-storage-" type)) + :on-press #(re-frame/dispatch [:intro-wizard/on-key-storage-selected type]) + :icon icon + :accessory :radio + :active (= type selected-storage-type) + :title (i18n/label title) + :subtitle (i18n/label desc) + :subtitle-max-lines 2}]]) -(defview select-key-storage [] +(defview select-key-storage + [] (letsubs [{:keys [selected-storage-type recovering?]} [:intro-wizard/select-key-storage]] [:<> [react/view {:style {:flex 1}} [ui/title-with-description :t/intro-wizard-title3 :t/intro-wizard-text3] [ui/learn-more :t/about-key-storage-title :t/about-key-storage-content] [react/view {:style {:margin-top 60}} - [storage-entry {:type :default - :icon :main-icons/mobile - :title :t/this-device - :desc :t/this-device-desc} + [storage-entry + {:type :default + :icon :main-icons/mobile + :title :t/this-device + :desc :t/this-device-desc} selected-storage-type] [react/view {:style {:height 16}}] - [storage-entry {:type :advanced - :icon :main-icons/keycard - :title :t/keycard - :desc :t/keycard-desc} + [storage-entry + {:type :advanced + :icon :main-icons/keycard + :title :t/keycard + :desc :t/keycard-desc} selected-storage-type]]] [ui/next-button #(dispatch-and-chill diff --git a/src/status_im/ui/screens/onboarding/styles.cljs b/src/status_im/ui/screens/onboarding/styles.cljs index 1d74dc3100..4e19e435ce 100644 --- a/src/status_im/ui/screens/onboarding/styles.cljs +++ b/src/status_im/ui/screens/onboarding/styles.cljs @@ -5,7 +5,8 @@ {:margin-bottom 16 :text-align :center}) -(defn wizard-text [] +(defn wizard-text + [] {:color colors/gray :text-align :center}) @@ -16,13 +17,14 @@ :flex-direction :row}) (def multiaccount-image - {:width 40 - :height 40 - :border-radius 20 - :border-width 1 - :border-color colors/black-transparent}) + {:width 40 + :height 40 + :border-radius 20 + :border-width 1 + :border-color colors/black-transparent}) -(defn list-item [selected?] +(defn list-item + [selected?] {:flex-direction :row :align-items :center :justify-content :space-between diff --git a/src/status_im/ui/screens/onboarding/views.cljs b/src/status_im/ui/screens/onboarding/views.cljs index 9e3194c154..c0fac1417b 100644 --- a/src/status_im/ui/screens/onboarding/views.cljs +++ b/src/status_im/ui/screens/onboarding/views.cljs @@ -1,32 +1,38 @@ (ns status-im.ui.screens.onboarding.views - (:require [status-im.ui.components.react :as react] - [re-frame.core :as re-frame] - [status-im.ui.screens.onboarding.styles :as styles] - [status-im.i18n.i18n :as i18n] + (:require [quo.core :as quo] [quo.design-system.colors :as colors] - [quo.core :as quo] - [status-im.ui.components.toolbar :as toolbar])) + [re-frame.core :as re-frame] + [status-im.i18n.i18n :as i18n] + [status-im.ui.components.react :as react] + [status-im.ui.components.toolbar :as toolbar] + [status-im.ui.screens.onboarding.styles :as styles])) -(defn learn-more [title content] - [react/text {:on-press #(re-frame/dispatch [:bottom-sheet/show-sheet :learn-more - {:title (i18n/label title) - :content (i18n/label content)}]) - :style (merge (styles/wizard-text) {:color colors/blue}) - :accessibility-label :learn-more} +(defn learn-more + [title content] + [react/text + {:on-press #(re-frame/dispatch [:bottom-sheet/show-sheet :learn-more + {:title (i18n/label title) + :content (i18n/label content)}]) + :style (merge (styles/wizard-text) {:color colors/blue}) + :accessibility-label :learn-more} (i18n/label :learn-more)]) -(defn title-with-description [title description] - [react/view {:style {:margin-vertical 16 - :margin-horizontal 32}} - [quo/text {:style styles/wizard-title - :align :center - :weight :bold - :size :x-large} +(defn title-with-description + [title description] + [react/view + {:style {:margin-vertical 16 + :margin-horizontal 32}} + [quo/text + {:style styles/wizard-title + :align :center + :weight :bold + :size :x-large} (i18n/label title)] [react/text {:style (styles/wizard-text)} (i18n/label description)]]) -(defn next-button [handler disabled] +(defn next-button + [handler disabled] [toolbar/toolbar {:show-border? true :right [quo/button diff --git a/src/status_im/ui/screens/onboarding/welcome/views.cljs b/src/status_im/ui/screens/onboarding/welcome/views.cljs index d5507e21e0..cb210aed23 100644 --- a/src/status_im/ui/screens/onboarding/welcome/views.cljs +++ b/src/status_im/ui/screens/onboarding/welcome/views.cljs @@ -1,34 +1,40 @@ (ns status-im.ui.screens.onboarding.welcome.views - (:require [status-im.ui.components.react :as react] - [reagent.core :as reagent] - [cljs-bean.core :as bean] - [status-im.react-native.resources :as resources] - [re-frame.core :as re-frame] - [status-im.i18n.i18n :as i18n] + (:require [cljs-bean.core :as bean] [quo.core :as quo] + [re-frame.core :as re-frame] + [reagent.core :as reagent] + [status-im.i18n.i18n :as i18n] + [status-im.react-native.resources :as resources] + [status-im.ui.components.react :as react] [status-im.ui.screens.onboarding.welcome.styles :as styles])) -(defn welcome-image-wrapper [] +(defn welcome-image-wrapper + [] (let [dimensions (reagent/atom {})] (fn [] - [react/view {:on-layout (fn [^js e] - (reset! dimensions (bean/->clj (-> e .-nativeEvent .-layout)))) - :style {:align-items :center - :justify-content :center - :flex 1}} + [react/view + {:on-layout (fn [^js e] + (reset! dimensions (bean/->clj (-> e .-nativeEvent .-layout)))) + :style {:align-items :center + :justify-content :center + :flex 1}} (let [padding 0 image-size (- (min (:width @dimensions) (:height @dimensions)) padding)] - [react/image {:source (resources/get-theme-image :welcome) - :resize-mode :contain - :style {:width image-size :height image-size}}])]))) + [react/image + {:source (resources/get-theme-image :welcome) + :resize-mode :contain + :style {:width image-size :height image-size}}])]))) -(defn welcome [] +(defn welcome + [] [react/view {:style styles/welcome-view} [welcome-image-wrapper] [react/i18n-text {:style styles/welcome-text :key :welcome-to-status}] - [react/i18n-text {:style styles/welcome-text-description - :key :welcome-to-status-description}] + [react/i18n-text + {:style styles/welcome-text-description + :key :welcome-to-status-description}] [react/view {:align-items :center :margin-bottom 50} - [quo/button {:on-press #(re-frame/dispatch [:welcome-lets-go]) - :accessibility-label :lets-go-button} + [quo/button + {:on-press #(re-frame/dispatch [:welcome-lets-go]) + :accessibility-label :lets-go-button} (i18n/label :t/lets-go)]]]) \ No newline at end of file diff --git a/src/status_im/ui/screens/pairing/styles.cljs b/src/status_im/ui/screens/pairing/styles.cljs index 2c3ee2f187..de9f4dca4c 100644 --- a/src/status_im/ui/screens/pairing/styles.cljs +++ b/src/status_im/ui/screens/pairing/styles.cljs @@ -3,13 +3,13 @@ [status-im.utils.styles :as styles])) (def wrapper - {:flex 1}) + {:flex 1}) (styles/def installation-item - {:flex-direction :row - :align-items :center - :ios {:height 64} - :android {:height 56}}) + {:flex-direction :row + :align-items :center + :ios {:height 64} + :android {:height 56}}) (def installation-list {:padding-horizontal 16 @@ -19,12 +19,14 @@ {:padding-top 10 :padding-horizontal 16}) -(def footer-content {:justify-content :center - :flex 1 - :align-items :center}) +(def footer-content + {:justify-content :center + :flex 1 + :align-items :center}) -(def footer-text {:color colors/blue - :text-align :center}) +(def footer-text + {:color colors/blue + :text-align :center}) (def pair-this-device {:height 80 @@ -42,7 +44,8 @@ {:flex 1 :flex-direction :row}) -(defn pairing-button [enabled?] +(defn pairing-button + [enabled?] {:width 40 :height 40 :background-color (if enabled? @@ -60,7 +63,8 @@ {:color colors/blue :margin-bottom 6}) -(styles/defn pairing-button-icon [enabled?] +(styles/defn pairing-button-icon + [enabled?] (let [color (if enabled? colors/blue colors/gray)] diff --git a/src/status_im/ui/screens/pairing/views.cljs b/src/status_im/ui/screens/pairing/views.cljs index 6992e854b4..c35db94afc 100644 --- a/src/status_im/ui/screens/pairing/views.cljs +++ b/src/status_im/ui/screens/pairing/views.cljs @@ -1,46 +1,54 @@ (ns status-im.ui.screens.pairing.views (:require-macros [status-im.utils.views :as views]) - (:require [re-frame.core :as re-frame] - [status-im.i18n.i18n :as i18n] + (:require [clojure.string :as string] + [quo.core :as quo] + [re-frame.core :as re-frame] [reagent.core :as reagent] - [clojure.string :as string] + [status-im.i18n.i18n :as i18n] [status-im.ui.components.icons.icons :as icons] [status-im.ui.components.list.views :as list] [status-im.ui.components.react :as react] - [quo.core :as quo] [status-im.ui.screens.pairing.styles :as styles])) (def syncing (reagent/atom false)) (def installation-name (reagent/atom "")) -(defn icon-style [style] +(defn icon-style + [style] style) -(defn synchronize-installations! [] +(defn synchronize-installations! + [] (reset! syncing true) ;; Currently we don't know how long it takes, so we just disable for 10s, to avoid ;; spamming (js/setTimeout #(reset! syncing false) 10000) (re-frame/dispatch [:pairing.ui/synchronize-installation-pressed])) -(defn pair! [] +(defn pair! + [] (re-frame/dispatch [:pairing.ui/pair-devices-pressed])) -(defn enable-installation! [installation-id] +(defn enable-installation! + [installation-id] (re-frame/dispatch [:pairing.ui/enable-installation-pressed installation-id])) -(defn disable-installation! [installation-id] +(defn disable-installation! + [installation-id] (re-frame/dispatch [:pairing.ui/disable-installation-pressed installation-id])) -(defn toggle-enabled! [installation-id enabled? _] +(defn toggle-enabled! + [installation-id enabled? _] (if enabled? (disable-installation! installation-id) (enable-installation! installation-id))) -(defn footer [syncing] - [react/touchable-highlight {:on-press (when-not @syncing - synchronize-installations!) - :style {:height 52}} +(defn footer + [syncing] + [react/touchable-highlight + {:on-press (when-not @syncing + synchronize-installations!) + :style {:height 52}} [react/view {:style styles/footer-content} [react/text @@ -49,52 +57,64 @@ (i18n/label :t/syncing-devices) (i18n/label :t/sync-all-devices))]]]) -(defn pair-this-device [] - [react/touchable-highlight {:on-press pair! - :style styles/pair-this-device} +(defn pair-this-device + [] + [react/touchable-highlight + {:on-press pair! + :style styles/pair-this-device} [react/view {:style styles/pair-this-device-actions} [react/view [react/view (styles/pairing-button true) [icons/icon :main-icons/add (icon-style (styles/pairing-button-icon true))]]] [react/view {:style styles/pairing-actions-text} [react/view - [react/text {:style styles/pair-this-device-title - :accessibility-label :advertise-device} (i18n/label :t/pair-this-device)]] + [react/text + {:style styles/pair-this-device-title + :accessibility-label :advertise-device} (i18n/label :t/pair-this-device)]] [react/view [react/text (i18n/label :t/pair-this-device-description)]]]]]) -(defn your-device [{:keys [installation-id name device-type]}] - [quo/list-item {:icon (if (= "desktop" - device-type) - :main-icons/desktop - :main-icons/mobile) - :title (str name " (" (i18n/label :t/you) ", " (subs installation-id 0 5) ")")}]) +(defn your-device + [{:keys [installation-id name device-type]}] + [quo/list-item + {:icon (if (= "desktop" + device-type) + :main-icons/desktop + :main-icons/mobile) + :title (str name " (" (i18n/label :t/you) ", " (subs installation-id 0 5) ")")}]) -(defn render-row [{:keys [name - enabled? - device-type - installation-id]}] - [quo/list-item {:icon (if (= "desktop" device-type) - :main-icons/desktop - :main-icons/mobile) - :title (str (if (string/blank? name) - (i18n/label :t/pairing-no-info) - name) - " (" (subs installation-id 0 5) ")") - :accessory :checkbox - :active enabled? - :on-press (partial toggle-enabled! installation-id enabled?)}]) +(defn render-row + [{:keys [name + enabled? + device-type + installation-id]}] + [quo/list-item + {:icon (if (= "desktop" device-type) + :main-icons/desktop + :main-icons/mobile) + :title (str (if (string/blank? name) + (i18n/label :t/pairing-no-info) + name) + " (" + (subs installation-id 0 5) + ")") + :accessory :checkbox + :active enabled? + :on-press (partial toggle-enabled! installation-id enabled?)}]) -(defn render-rows [installations] +(defn render-rows + [installations] [react/scroll-view {:style styles/wrapper} [your-device (first installations)] (when (seq (rest installations)) - [list/flat-list {:data (rest installations) - :default-separator? false - :key-fn :installation-id - :render-fn render-row}])]) + [list/flat-list + {:data (rest installations) + :default-separator? false + :key-fn :installation-id + :render-fn render-row}])]) -(views/defview edit-installation-name [] +(views/defview edit-installation-name + [] [react/keyboard-avoiding-view styles/edit-installation [react/scroll-view {:keyboard-should-persist-taps :handled} [react/view @@ -108,26 +128,30 @@ [react/view styles/bottom-container [react/view {:flex 1}] [quo/button - {:type :secondary - :after :main-icon/next - :disabled (string/blank? @installation-name) - :on-press #(do - (re-frame/dispatch [:pairing.ui/set-name-pressed @installation-name]) - (reset! installation-name ""))} + {:type :secondary + :after :main-icon/next + :disabled (string/blank? @installation-name) + :on-press #(do + (re-frame/dispatch [:pairing.ui/set-name-pressed @installation-name]) + (reset! installation-name ""))} (i18n/label :t/continue)]]]) -(defn info-section [] +(defn info-section + [] [react/view {:style styles/info-section} - [react/touchable-highlight {:on-press #(.openURL ^js react/linking "https://status.im/user_guides/pairing_devices.html")} + [react/touchable-highlight + {:on-press #(.openURL ^js react/linking "https://status.im/user_guides/pairing_devices.html")} [react/text {:style styles/info-section-text} (i18n/label :t/learn-more)]]]) -(defn installations-list [installations] +(defn installations-list + [installations] [react/view {:style styles/installation-list} [react/view {:style styles/paired-devices-title} [react/text (i18n/label :t/paired-devices)]] (render-rows installations)]) -(views/defview installations [] +(views/defview installations + [] (views/letsubs [installations [:pairing/installations]] [:<> [react/scroll-view diff --git a/src/status_im/ui/screens/peers_stats.cljs b/src/status_im/ui/screens/peers_stats.cljs index 09087ddf7e..ad3a03eb72 100644 --- a/src/status_im/ui/screens/peers_stats.cljs +++ b/src/status_im/ui/screens/peers_stats.cljs @@ -3,10 +3,12 @@ [status-im.i18n.i18n :as i18n] [utils.re-frame :as re-frame])) -(defn peers-stats [] +(defn peers-stats + [] (let [peers-count (re-frame/sub [:peers-count])] - [react-native.core/view {:flex 1 - :margin-horizontal 8} + [react-native.core/view + {:flex 1 + :margin-horizontal 8} [react-native.core/view {:style {:flex-direction :row :margin-vertical 8 diff --git a/src/status_im/ui/screens/popover/views.cljs b/src/status_im/ui/screens/popover/views.cljs index becb4f884b..9a8ba49b0d 100644 --- a/src/status_im/ui/screens/popover/views.cljs +++ b/src/status_im/ui/screens/popover/views.cljs @@ -1,48 +1,53 @@ (ns status-im.ui.screens.popover.views (:require-macros [status-im.utils.views :as views]) - (:require [status-im.ui.components.animation :as anim] - [reagent.core :as reagent] - [status-im.ui.components.react :as react] - [re-frame.core :as re-frame] - [status-im.utils.platform :as platform] - [status-im.ui.screens.wallet.signing-phrase.views :as signing-phrase] - [status-im.ui.screens.communities.views :as communities] - [status-im.ui.screens.wallet.request.views :as request] - [status-im.ui.screens.profile.user.views :as profile.user] - ["react-native" :refer (BackHandler)] - [status-im.ui.screens.reset-password.views :as reset-password.views] - [status-im.ui.screens.multiaccounts.recover.views :as multiaccounts.recover] - [status-im.ui.screens.multiaccounts.key-storage.views :as multiaccounts.key-storage] - [status-im.ui.screens.signing.views :as signing] - [status-im.ui.screens.biometric.views :as biometric] + (:require ["react-native" :refer (BackHandler)] [quo.design-system.colors :as colors] - [status-im.ui.screens.keycard.views :as keycard.views] - [status-im.ui.screens.keycard.frozen-card.view :as frozen-card] + [re-frame.core :as re-frame] + [reagent.core :as reagent] + [status-im.ui.components.animation :as anim] + [status-im.ui.components.react :as react] + [status-im.ui.screens.biometric.views :as biometric] [status-im.ui.screens.chat.message.pinned-message :as pinned-message] + [status-im.ui.screens.communities.views :as communities] + [status-im.ui.screens.keycard.frozen-card.view :as frozen-card] + [status-im.ui.screens.keycard.views :as keycard.views] + [status-im.ui.screens.multiaccounts.key-storage.views :as multiaccounts.key-storage] + [status-im.ui.screens.multiaccounts.recover.views :as multiaccounts.recover] + [status-im.ui.screens.profile.user.views :as profile.user] + [status-im.ui.screens.reset-password.views :as reset-password.views] [status-im.ui.screens.signing.sheets :as signing-sheets] + [status-im.ui.screens.signing.views :as signing] + [status-im.ui.screens.wallet.request.views :as request] + [status-im.ui.screens.wallet.signing-phrase.views :as signing-phrase] + [status-im.utils.platform :as platform] [status-im2.contexts.activity-center.view :as activity-center])) (defn hide-panel-anim [bottom-anim-value alpha-value window-height] (anim/start (anim/parallel - [(anim/spring bottom-anim-value {:toValue (- window-height) - :useNativeDriver true}) - (anim/timing alpha-value {:toValue 0 - :duration 500 - :useNativeDriver true})]))) + [(anim/spring bottom-anim-value + {:toValue (- window-height) + :useNativeDriver true}) + (anim/timing alpha-value + {:toValue 0 + :duration 500 + :useNativeDriver true})]))) (defn show-panel-anim [bottom-anim-value alpha-value] (anim/start (anim/parallel - [(anim/spring bottom-anim-value {:toValue 0 - :useNativeDriver true}) - (anim/timing alpha-value {:toValue 0.4 - :duration 500 - :useNativeDriver true})]))) + [(anim/spring bottom-anim-value + {:toValue 0 + :useNativeDriver true}) + (anim/timing alpha-value + {:toValue 0.4 + :duration 500 + :useNativeDriver true})]))) -(defn popover-view [_ window-height] +(defn popover-view + [_ window-height] (let [bottom-anim-value (anim/create-value window-height) alpha-value (anim/create-value 0) clear-timeout (atom nil) @@ -51,11 +56,14 @@ request-close (fn [] (when-not (:prevent-closing? @current-popover) (reset! clear-timeout - (js/setTimeout - #(do (reset! current-popover nil) - (re-frame/dispatch [:hide-popover])) 200)) + (js/setTimeout + #(do (reset! current-popover nil) + (re-frame/dispatch [:hide-popover])) + 200)) (hide-panel-anim - bottom-anim-value alpha-value (- window-height))) + bottom-anim-value + alpha-value + (- window-height))) true) on-show (fn [] (show-panel-anim bottom-anim-value alpha-value) @@ -92,34 +100,38 @@ :else (do (reset! current-popover nil) (on-hide)))) - :component-will-unmount on-hide + :component-will-unmount on-hide :reagent-render (fn [] (when @current-popover - (let [{:keys [view style disable-touchable-overlay? blur-view? blur-view-props]} @current-popover + (let [{:keys [view style disable-touchable-overlay? blur-view? blur-view-props]} + @current-popover component (if blur-view? react/blur-view react/view) overlay-component (if disable-touchable-overlay? react/view react/touchable-highlight)] - [component (merge {:style {:position :absolute :top 0 :bottom 0 :left 0 :right 0}} blur-view-props) + [component + (merge {:style {:position :absolute :top 0 :bottom 0 :left 0 :right 0}} blur-view-props) (when platform/ios? [react/animated-view {:style {:flex 1 :background-color colors/black-persist :opacity alpha-value}}]) - [react/animated-view {:style - {:position :absolute - :height window-height - :left 0 - :right 0 - :transform [{:translateY bottom-anim-value}]}} + [react/animated-view + {:style + {:position :absolute + :height window-height + :left 0 + :right 0 + :transform [{:translateY bottom-anim-value}]}} [overlay-component {:style {:flex 1 :align-items :center :justify-content :center} :on-press request-close} - [react/view (merge {:background-color (if blur-view? :transparent colors/white) - :border-radius 16 - :margin 32 - :shadow-offset {:width 0 :height 2} - :shadow-radius 8 - :shadow-opacity 1 - :shadow-color "rgba(0, 9, 26, 0.12)"} - style) + [react/view + (merge {:background-color (if blur-view? :transparent colors/white) + :border-radius 16 + :margin 32 + :shadow-offset {:width 0 :height 2} + :shadow-radius 8 + :shadow-opacity 1 + :shadow-color "rgba(0, 9, 26, 0.12)"} + style) (cond (vector? view) view @@ -181,7 +193,8 @@ :else [view])]]]])))}))) -(views/defview popover [] - (views/letsubs [popover [:popover/popover] +(views/defview popover + [] + (views/letsubs [popover [:popover/popover] {window-height :height} [:dimensions/window]] [popover-view popover window-height])) diff --git a/src/status_im/ui/screens/privacy_and_security_settings/delete_profile.cljs b/src/status_im/ui/screens/privacy_and_security_settings/delete_profile.cljs index aef7fdeb4b..70e1cf2cd4 100644 --- a/src/status_im/ui/screens/privacy_and_security_settings/delete_profile.cljs +++ b/src/status_im/ui/screens/privacy_and_security_settings/delete_profile.cljs @@ -1,71 +1,81 @@ (ns status-im.ui.screens.privacy-and-security-settings.delete-profile - (:require [status-im.ui.components.react :as react] - [quo.core :as quo] + (:require [quo.core :as quo] + [re-frame.core :as re-frame] + [reagent.core :as reagent] + [status-im.i18n.i18n :as i18n] + status-im.keycard.delete-key [status-im.multiaccounts.core :as multiaccounts] [status-im.ui.components.chat-icon.screen :as chat-icon.screen] - [re-frame.core :as re-frame] - [status-im.i18n.i18n :as i18n] - [reagent.core :as reagent] - [utils.security.core :as security] + [status-im.ui.components.react :as react] [status-im.ui.screens.privacy-and-security-settings.events :as delete-profile] - status-im.keycard.delete-key)) + [utils.security.core :as security])) -(defn valid-password? [password] +(defn valid-password? + [password] (>= (count password) 6)) -(defn on-delete-profile [password] +(defn on-delete-profile + [password] #(do (re-frame/dispatch [::delete-profile/delete-profile @password]) (reset! password nil))) -(defn on-delete-keycard-profile [keep-keys-on-keycard?] +(defn on-delete-keycard-profile + [keep-keys-on-keycard?] #(re-frame/dispatch [:keycard/proceed-to-reset-card keep-keys-on-keycard?])) -(defn delete-profile [] +(defn delete-profile + [] (let [password (reagent/atom nil) text-input-ref (atom nil)] (fn [] - (let [keycard? @(re-frame/subscribe [:keycard-multiaccount?]) - multiaccount @(re-frame/subscribe [:multiaccount]) - error @(re-frame/subscribe [:delete-profile/error]) + (let [keycard? @(re-frame/subscribe [:keycard-multiaccount?]) + multiaccount @(re-frame/subscribe [:multiaccount]) + error @(re-frame/subscribe [:delete-profile/error]) keep-keys-on-keycard? @(re-frame/subscribe [:delete-profile/keep-keys-on-keycard?])] (when (and @text-input-ref error (not @password)) (.clear ^js @text-input-ref)) [react/keyboard-avoiding-view {:style {:flex 1}} [react/scroll-view {:style {:flex 1}} [react/view {:style {:align-items :center}} - [quo/text {:weight :bold - :size :x-large} + [quo/text + {:weight :bold + :size :x-large} (i18n/label :t/delete-profile)]] [quo/list-item - {:title (multiaccounts/displayed-name multiaccount) - :icon [chat-icon.screen/contact-icon-contacts-tab - (multiaccounts/displayed-photo multiaccount)]}] + {:title (multiaccounts/displayed-name multiaccount) + :icon [chat-icon.screen/contact-icon-contacts-tab + (multiaccounts/displayed-photo multiaccount)]}] (when keycard? [react/view [quo/list-header (i18n/label :t/actions)] - [quo/list-item {:title (i18n/label :t/delete-keys-keycard) - :accessory :checkbox - :active (not keep-keys-on-keycard?) - :on-press #(re-frame/dispatch [::delete-profile/keep-keys-on-keycard (not keep-keys-on-keycard?)])}] - [quo/list-item {:title (i18n/label :t/unpair-keycard) - :subtitle (i18n/label :t/unpair-keycard-warning) - :subtitle-max-lines 4 - :disabled true - :active true - :accessory :checkbox}] - [quo/list-item {:title (i18n/label :t/reset-database) - :subtitle (i18n/label :t/reset-database-warning-keycard) - :subtitle-max-lines 4 - :disabled true - :active true - :accessory :checkbox}]]) + [quo/list-item + {:title (i18n/label :t/delete-keys-keycard) + :accessory :checkbox + :active (not keep-keys-on-keycard?) + :on-press #(re-frame/dispatch [::delete-profile/keep-keys-on-keycard + (not keep-keys-on-keycard?)])}] + [quo/list-item + {:title (i18n/label :t/unpair-keycard) + :subtitle (i18n/label :t/unpair-keycard-warning) + :subtitle-max-lines 4 + :disabled true + :active true + :accessory :checkbox}] + [quo/list-item + {:title (i18n/label :t/reset-database) + :subtitle (i18n/label :t/reset-database-warning-keycard) + :subtitle-max-lines 4 + :disabled true + :active true + :accessory :checkbox}]]) (when-not keycard? - [quo/text {:style {:margin-horizontal 24} - :align :center - :color :negative} + [quo/text + {:style {:margin-horizontal 24} + :align :center + :color :negative} (i18n/label :t/delete-profile-warning)]) (when-not keycard? [quo/text-input @@ -87,14 +97,18 @@ (when-not keycard? [quo/separator]) (when (and keycard? (not keep-keys-on-keycard?)) - [quo/text {:style {:margin-horizontal 24 :margin-bottom 16} - :align :center - :color :negative} + [quo/text + {:style {:margin-horizontal 24 :margin-bottom 16} + :align :center + :color :negative} (i18n/label :t/delete-profile-warning)]) [react/view {:style {:margin-vertical 8}} - [quo/button {:on-press (if keycard? (on-delete-keycard-profile keep-keys-on-keycard?) (on-delete-profile password)) - :theme :negative - :accessibility-label :delete-profile-confirm - :disabled (and (not keycard?) ((complement valid-password?) @password))} + [quo/button + {:on-press (if keycard? + (on-delete-keycard-profile keep-keys-on-keycard?) + (on-delete-profile password)) + :theme :negative + :accessibility-label :delete-profile-confirm + :disabled (and (not keycard?) ((complement valid-password?) @password))} (i18n/label :t/delete-profile)]]]])))) diff --git a/src/status_im/ui/screens/privacy_and_security_settings/events.cljs b/src/status_im/ui/screens/privacy_and_security_settings/events.cljs index 43c6dfa36a..79c0e4cc5b 100644 --- a/src/status_im/ui/screens/privacy_and_security_settings/events.cljs +++ b/src/status_im/ui/screens/privacy_and_security_settings/events.cljs @@ -1,15 +1,16 @@ (ns status-im.ui.screens.privacy-and-security-settings.events - (:require [status-im.utils.fx :as fx] + (:require [clojure.string :as string] [re-frame.core :as re-frame] - [utils.security.core :as security] - [status-im.native-module.core :as status] [status-im.ethereum.core :as ethereum] + [status-im.i18n.i18n :as i18n] + [status-im.native-module.core :as status] + [status-im.utils.fx :as fx] [status-im.utils.types :as types] [taoensso.timbre :as log] - [clojure.string :as string] - [status-im.i18n.i18n :as i18n])) + [utils.security.core :as security])) -(defn safe-blank? [s] +(defn safe-blank? + [s] (or (not s) (string/blank? s))) @@ -39,7 +40,7 @@ [{:keys [db] :as cofx} masked-password] (log/info "[delete-profile] delete") (let [{:keys [key-uid wallet-root-address]} (:multiaccount db)] - {:db (dissoc db :delete-profile/error) + {:db (dissoc db :delete-profile/error) ::delete-profile {:masked-password masked-password :key-uid key-uid diff --git a/src/status_im/ui/screens/privacy_and_security_settings/messages_from_contacts_only.cljs b/src/status_im/ui/screens/privacy_and_security_settings/messages_from_contacts_only.cljs index 7429b39aa0..e65d2f4878 100644 --- a/src/status_im/ui/screens/privacy_and_security_settings/messages_from_contacts_only.cljs +++ b/src/status_im/ui/screens/privacy_and_security_settings/messages_from_contacts_only.cljs @@ -1,19 +1,22 @@ (ns status-im.ui.screens.privacy-and-security-settings.messages-from-contacts-only (:require-macros [status-im.utils.views :as views]) - (:require [re-frame.core :as re-frame] - [status-im.utils.fx :as fx] - [status-im.ui.components.react :as react] - [status-im.multiaccounts.update.core :as multiaccounts.update] + (:require [quo.core :as quo] + [re-frame.core :as re-frame] [status-im.i18n.i18n :as i18n] - [quo.core :as quo])) + [status-im.multiaccounts.update.core :as multiaccounts.update] + [status-im.ui.components.react :as react] + [status-im.utils.fx :as fx])) (fx/defn handle-messages-from-contacts-only-switched {:events [::messages-from-contacts-only-switched]} [cofx value] (multiaccounts.update/multiaccount-update cofx - :messages-from-contacts-only value {})) + :messages-from-contacts-only + value + {})) -(views/defview messages-from-contacts-only [] +(views/defview messages-from-contacts-only + [] (views/letsubs [{:keys [messages-from-contacts-only]} [:multiaccount]] [react/view {:margin-top 8} [quo/list-item @@ -22,9 +25,9 @@ :title (i18n/label :t/anyone) :on-press #(re-frame/dispatch [::messages-from-contacts-only-switched false])}] [quo/list-item - {:active messages-from-contacts-only - :accessory :radio - :title (i18n/label :t/contacts) - :subtitle (i18n/label :t/messages-from-contacts-only-subtitle) + {:active messages-from-contacts-only + :accessory :radio + :title (i18n/label :t/contacts) + :subtitle (i18n/label :t/messages-from-contacts-only-subtitle) :subtitle-max-lines 4 - :on-press #(re-frame/dispatch [::messages-from-contacts-only-switched true])}]])) + :on-press #(re-frame/dispatch [::messages-from-contacts-only-switched true])}]])) diff --git a/src/status_im/ui/screens/privacy_and_security_settings/views.cljs b/src/status_im/ui/screens/privacy_and_security_settings/views.cljs index 2dfa1a6207..2c5bcacfe3 100644 --- a/src/status_im/ui/screens/privacy_and_security_settings/views.cljs +++ b/src/status_im/ui/screens/privacy_and_security_settings/views.cljs @@ -1,121 +1,139 @@ (ns status-im.ui.screens.privacy-and-security-settings.views - (:require [re-frame.core :as re-frame] + (:require [quo.core :as quo] + [re-frame.core :as re-frame] + [status-im.constants :as constants] [status-im.i18n.i18n :as i18n] - [quo.core :as quo] - [status-im.multiaccounts.reset-password.core :as reset-password] + [status-im.multiaccounts.biometric.core :as biometric] [status-im.multiaccounts.key-storage.core :as key-storage] + [status-im.multiaccounts.reset-password.core :as reset-password] + [status-im.multiaccounts.update.core :as multiaccounts.update] [status-im.ui.components.common.common :as components.common] [status-im.ui.components.react :as react] [status-im.utils.config :as config] - [status-im.multiaccounts.update.core :as multiaccounts.update] - [status-im.multiaccounts.biometric.core :as biometric] - [status-im.utils.platform :as platform] - [status-im.constants :as constants]) + [status-im.utils.platform :as platform]) (:require-macros [status-im.utils.views :as views])) -(defn separator [] - [quo/separator {:style {:margin-vertical 8}}]) +(defn separator + [] + [quo/separator {:style {:margin-vertical 8}}]) -(def titles {constants/profile-pictures-visibility-contacts-only (i18n/label :t/recent-recipients) - constants/profile-pictures-visibility-everyone (i18n/label :t/everyone) - constants/profile-pictures-visibility-none (i18n/label :t/none) - constants/profile-pictures-show-to-contacts-only (i18n/label :t/recent-recipients) - constants/profile-pictures-show-to-everyone (i18n/label :t/everyone) - constants/profile-pictures-show-to-none (i18n/label :t/none)}) +(def titles + {constants/profile-pictures-visibility-contacts-only (i18n/label :t/recent-recipients) + constants/profile-pictures-visibility-everyone (i18n/label :t/everyone) + constants/profile-pictures-visibility-none (i18n/label :t/none) + constants/profile-pictures-show-to-contacts-only (i18n/label :t/recent-recipients) + constants/profile-pictures-show-to-everyone (i18n/label :t/everyone) + constants/profile-pictures-show-to-none (i18n/label :t/none)}) -(views/defview privacy-and-security [] +(views/defview privacy-and-security + [] (views/letsubs [{:keys [mnemonic preview-privacy? messages-from-contacts-only webview-allow-permission-requests? opensea-enabled? - profile-pictures-visibility]} [:multiaccount] - has-picture [:profile/has-picture] + profile-pictures-visibility]} + [:multiaccount] + has-picture [:profile/has-picture] supported-biometric-auth [:supported-biometric-auth] - keycard? [:keycard-multiaccount?] - auth-method [:auth-method] + keycard? [:keycard-multiaccount?] + auth-method [:auth-method] profile-pictures-show-to [:multiaccount/profile-pictures-show-to]] [react/scroll-view {:padding-vertical 8} [quo/list-header (i18n/label :t/security)] - [quo/list-item {:size :small - :title (i18n/label :t/back-up-seed-phrase) - :accessibility-label :back-up-recovery-phrase-button - :disabled (not mnemonic) - :chevron (boolean mnemonic) - :accessory (when mnemonic [components.common/counter {:size 22} 1]) - :on-press #(re-frame/dispatch [:navigate-to :backup-seed])}] + [quo/list-item + {:size :small + :title (i18n/label :t/back-up-seed-phrase) + :accessibility-label :back-up-recovery-phrase-button + :disabled (not mnemonic) + :chevron (boolean mnemonic) + :accessory (when mnemonic [components.common/counter {:size 22} 1]) + :on-press #(re-frame/dispatch [:navigate-to :backup-seed])}] (when supported-biometric-auth [quo/list-item {:size :small - :title (str (i18n/label :t/lock-app-with) " " (biometric/get-label supported-biometric-auth)) + :title (str (i18n/label :t/lock-app-with) + " " + (biometric/get-label supported-biometric-auth)) :active (= auth-method "biometric") :accessibility-label :biometric-auth-settings-switch :accessory :switch :on-press #(re-frame/dispatch [:multiaccounts.ui/biometric-auth-switched - ((complement boolean) (= auth-method "biometric"))])}]) + ((complement boolean) + (= auth-method "biometric"))])}]) [separator] [quo/list-header (i18n/label :t/privacy)] - [quo/list-item {:size :small - :title (i18n/label :t/set-dapp-access-permissions) - :on-press #(re-frame/dispatch [:navigate-to :dapps-permissions]) - :accessibility-label :dapps-permissions-button - :chevron true}] - [quo/list-item {:size :small - :title (if platform/android? - (i18n/label :t/hide-content-when-switching-apps) - (i18n/label :t/hide-content-when-switching-apps-ios)) - :container-margin-bottom 8 - :active preview-privacy? - :accessory :switch - :on-press #(re-frame/dispatch - [:multiaccounts.ui/preview-privacy-mode-switched - ((complement boolean) preview-privacy?)])}] + [quo/list-item + {:size :small + :title (i18n/label :t/set-dapp-access-permissions) + :on-press #(re-frame/dispatch [:navigate-to :dapps-permissions]) + :accessibility-label :dapps-permissions-button + :chevron true}] + [quo/list-item + {:size :small + :title (if platform/android? + (i18n/label :t/hide-content-when-switching-apps) + (i18n/label :t/hide-content-when-switching-apps-ios)) + :container-margin-bottom 8 + :active preview-privacy? + :accessory :switch + :on-press #(re-frame/dispatch + [:multiaccounts.ui/preview-privacy-mode-switched + ((complement boolean) preview-privacy?)])}] (when config/collectibles-enabled? - [quo/list-item {:size :small - :title (i18n/label :t/display-collectibles) - :container-margin-bottom 8 - :active opensea-enabled? - :accessory :switch - :on-press #(re-frame/dispatch [::multiaccounts.update/toggle-opensea-nfts-visiblity (not opensea-enabled?)])}]) - [quo/list-item {:size :small - :title (i18n/label :t/chat-link-previews) - :chevron true - :on-press #(re-frame/dispatch [:navigate-to :link-previews-settings]) - :accessibility-label :chat-link-previews}] - [quo/list-item {:size :small - :title (i18n/label :t/accept-new-chats-from) - :chevron true - :accessory :text - :accessory-text (i18n/label (if messages-from-contacts-only - :t/contacts - :t/anyone)) - :on-press #(re-frame/dispatch [:navigate-to :messages-from-contacts-only]) - :accessibility-label :accept-new-chats-from}] + [quo/list-item + {:size :small + :title (i18n/label :t/display-collectibles) + :container-margin-bottom 8 + :active opensea-enabled? + :accessory :switch + :on-press #(re-frame/dispatch + [::multiaccounts.update/toggle-opensea-nfts-visiblity + (not opensea-enabled?)])}]) + [quo/list-item + {:size :small + :title (i18n/label :t/chat-link-previews) + :chevron true + :on-press #(re-frame/dispatch [:navigate-to :link-previews-settings]) + :accessibility-label :chat-link-previews}] + [quo/list-item + {:size :small + :title (i18n/label :t/accept-new-chats-from) + :chevron true + :accessory :text + :accessory-text (i18n/label (if messages-from-contacts-only + :t/contacts + :t/anyone)) + :on-press #(re-frame/dispatch [:navigate-to :messages-from-contacts-only]) + :accessibility-label :accept-new-chats-from}] (when (not keycard?) - [quo/list-item {:size :small - :title (i18n/label :t/reset-password) - :chevron true - :accessory :text - :on-press #(do - (re-frame/dispatch [::reset-password/clear-form-vals]) - (re-frame/dispatch [:navigate-to :reset-password])) - :accessibility-label :reset-password}]) + [quo/list-item + {:size :small + :title (i18n/label :t/reset-password) + :chevron true + :accessory :text + :on-press #(do + (re-frame/dispatch [::reset-password/clear-form-vals]) + (re-frame/dispatch [:navigate-to :reset-password])) + :accessibility-label :reset-password}]) (when platform/android? - [quo/list-item {:size :small - :title (i18n/label :t/webview-camera-permission-requests) - :active webview-allow-permission-requests? - :accessory :switch - :subtitle (i18n/label :t/webview-camera-permission-requests-subtitle) - :subtitle-max-lines 2 - :on-press #(re-frame/dispatch - [:multiaccounts.ui/webview-permission-requests-switched - ((complement boolean) webview-allow-permission-requests?)])}]) + [quo/list-item + {:size :small + :title (i18n/label :t/webview-camera-permission-requests) + :active webview-allow-permission-requests? + :accessory :switch + :subtitle (i18n/label :t/webview-camera-permission-requests-subtitle) + :subtitle-max-lines 2 + :on-press #(re-frame/dispatch + [:multiaccounts.ui/webview-permission-requests-switched + ((complement boolean) webview-allow-permission-requests?)])}]) (when (not keycard?) - [quo/list-item {:size :small - :title (i18n/label :t/manage-keys-and-storage) - :chevron true - :on-press #(re-frame/dispatch [::key-storage/logout-and-goto-key-storage]) - :accessibility-label :key-managment}]) + [quo/list-item + {:size :small + :title (i18n/label :t/manage-keys-and-storage) + :chevron true + :on-press #(re-frame/dispatch [::key-storage/logout-and-goto-key-storage]) + :accessibility-label :key-managment}]) [separator] [quo/list-header (i18n/label :t/privacy-photos)] @@ -146,14 +164,16 @@ :accessibility-label :dapps-permissions-button :chevron true}]])) -(defn ppst-radio-item [id value] +(defn ppst-radio-item + [id value] [quo/list-item {:active (= value id) :accessory :radio :title (get titles id) :on-press #(re-frame/dispatch [:multiaccounts.ui/profile-picture-show-to-switched id])}]) -(views/defview profile-pic-show-to [] +(views/defview profile-pic-show-to + [] (views/letsubs [{:keys [profile-pictures-show-to]} [:multiaccount]] [react/view {:margin-top 8} [ppst-radio-item constants/profile-pictures-show-to-everyone profile-pictures-show-to] @@ -164,14 +184,16 @@ [quo/text {:color :secondary} (i18n/label :t/privacy-show-to-warning)]]])) -(defn ppvf-radio-item [id value] +(defn ppvf-radio-item + [id value] [quo/list-item {:active (= value id) :accessory :radio :title (get titles id) :on-press #(re-frame/dispatch [:multiaccounts.ui/appearance-profile-switched id])}]) -(views/defview profile-pic [] +(views/defview profile-pic + [] (views/letsubs [{:keys [profile-pictures-visibility]} [:multiaccount]] [react/view {:margin-top 8} [ppvf-radio-item constants/profile-pictures-visibility-everyone profile-pictures-visibility] diff --git a/src/status_im/ui/screens/profile/components/sheets.cljs b/src/status_im/ui/screens/profile/components/sheets.cljs index 2be57aaf25..7ff26b7458 100644 --- a/src/status_im/ui/screens/profile/components/sheets.cljs +++ b/src/status_im/ui/screens/profile/components/sheets.cljs @@ -1,28 +1,31 @@ (ns status-im.ui.screens.profile.components.sheets - (:require [re-frame.core :as re-frame] - [status-im.ui.components.react :as react] + (:require [quo.core :as quo] + [re-frame.core :as re-frame] + [reagent.core :as reagent] [status-im.i18n.i18n :as i18n] - [status-im.ui.screens.profile.components.styles :as styles] - [quo.core :as quo] - [reagent.core :as reagent]) + [status-im.ui.components.react :as react] + [status-im.ui.screens.profile.components.styles :as styles]) (:require-macros [status-im.utils.views :as views])) -(views/defview block-contact [] +(views/defview block-contact + [] (views/letsubs [{:keys [public-key]} [:popover/popover] - in-progress? (reagent/atom false)] + in-progress? (reagent/atom false)] [react/view {:style {:padding-top 16 :padding-horizontal 24 :padding-bottom 8}} [react/text {:style styles/sheet-text} (i18n/label :t/block-contact-details)] [react/view {:align-items :center :margin-top 16} - [quo/button {:theme :negative - :disabled @in-progress? - :loading @in-progress? - :accessibility-label :block-contact-confirm - :on-press #(do (reset! in-progress? true) - (re-frame/dispatch [:contact.ui/block-contact-confirmed public-key]))} + [quo/button + {:theme :negative + :disabled @in-progress? + :loading @in-progress? + :accessibility-label :block-contact-confirm + :on-press #(do (reset! in-progress? true) + (re-frame/dispatch [:contact.ui/block-contact-confirmed public-key]))} (i18n/label :t/block)] [react/view {:height 8}] - [quo/button {:type :secondary - :disabled @in-progress? - :on-press #(re-frame/dispatch [:hide-popover])} + [quo/button + {:type :secondary + :disabled @in-progress? + :on-press #(re-frame/dispatch [:hide-popover])} (i18n/label :t/close)]]])) diff --git a/src/status_im/ui/screens/profile/components/styles.cljs b/src/status_im/ui/screens/profile/components/styles.cljs index 3b6a0dae05..2ca5fd4efb 100644 --- a/src/status_im/ui/screens/profile/components/styles.cljs +++ b/src/status_im/ui/screens/profile/components/styles.cljs @@ -55,6 +55,6 @@ ;; sheets (def sheet-text - {:text-align :center + {:text-align :center :line-height 22 :font-size 15}) diff --git a/src/status_im/ui/screens/profile/components/views.cljs b/src/status_im/ui/screens/profile/components/views.cljs index e4097fde5f..0eb61eb6b9 100644 --- a/src/status_im/ui/screens/profile/components/views.cljs +++ b/src/status_im/ui/screens/profile/components/views.cljs @@ -1,7 +1,7 @@ (ns status-im.ui.screens.profile.components.views (:require [clojure.string :as string] - [status-im.i18n.i18n :as i18n] [quo.design-system.colors :as colors] + [status-im.i18n.i18n :as i18n] [status-im.ui.components.icons.icons :as icons] [status-im.ui.components.react :as react] [status-im.ui.screens.profile.components.styles :as styles])) @@ -22,18 +22,20 @@ [react/view styles/settings-item-icon [icons/icon icon {:color colors/blue}]]) [react/view styles/settings-item-text-wrapper - [react/text {:style (merge styles/settings-item-text - (when destructive? - styles/settings-item-destructive) - (when-not active? - styles/settings-item-disabled) - (when icon - {:font-size 17})) - :number-of-lines 1} + [react/text + {:style (merge styles/settings-item-text + (when destructive? + styles/settings-item-destructive) + (when-not active? + styles/settings-item-disabled) + (when icon + {:font-size 17})) + :number-of-lines 1} (or item-text (i18n/label label-kw))] (when-not (string/blank? value) - [react/text {:style styles/settings-item-value - :number-of-lines 1} + [react/text + {:style styles/settings-item-value + :number-of-lines 1} value])] (if icon-content icon-content @@ -42,11 +44,12 @@ (defn settings-switch-item [{:keys [label-kw value action-fn active?] - :or {active? true}}] + :or {active? true}}] [react/view styles/settings-item [react/view styles/settings-item-text-wrapper [react/i18n-text {:style styles/settings-item-text :key label-kw}]] - [react/switch {:track-color #js {:true colors/blue :false colors/gray-lighter} - :value (boolean value) - :on-value-change action-fn - :disabled (not active?)}]]) + [react/switch + {:track-color #js {:true colors/blue :false colors/gray-lighter} + :value (boolean value) + :on-value-change action-fn + :disabled (not active?)}]]) diff --git a/src/status_im/ui/screens/profile/contact/styles.cljs b/src/status_im/ui/screens/profile/contact/styles.cljs index db6acc0ec2..242092b35a 100644 --- a/src/status_im/ui/screens/profile/contact/styles.cljs +++ b/src/status_im/ui/screens/profile/contact/styles.cljs @@ -13,7 +13,8 @@ (def contact-profile-detail-share-icon {:color colors/gray-transparent-40}) -(defn updates-descr-cont [] +(defn updates-descr-cont + [] {:border-width 1 :border-color colors/gray-lighter :border-top-right-radius 16 diff --git a/src/status_im/ui/screens/profile/contact/views.cljs b/src/status_im/ui/screens/profile/contact/views.cljs index c182d3cc8b..cc014bdb88 100644 --- a/src/status_im/ui/screens/profile/contact/views.cljs +++ b/src/status_im/ui/screens/profile/contact/views.cljs @@ -1,33 +1,34 @@ (ns status-im.ui.screens.profile.contact.views - (:require [quo.core :as quo] + (:require [clojure.string :as string] + [quo.components.list.item :as list-item] + [quo.core :as quo] + [quo.design-system.colors :as colors] [re-frame.core :as re-frame] - [status-im.i18n.i18n :as i18n] + [reagent.core :as reagent] [status-im.chat.models :as chat.models] + [status-im.i18n.i18n :as i18n] [status-im.multiaccounts.core :as multiaccounts] [status-im.ui.components.chat-icon.screen :as chat-icon] [status-im.ui.components.icons.icons :as icons] + [status-im.ui.components.keyboard-avoid-presentation :as kb-presentation] + [status-im.ui.components.list.views :as list] [status-im.ui.components.profile-header.view :as profile-header] [status-im.ui.components.react :as react] + [status-im.ui.components.toolbar :as toolbar] + [status-im.ui.components.topbar :as topbar] [status-im.ui.screens.profile.components.sheets :as sheets] [status-im.ui.screens.profile.contact.styles :as styles] - [status-im.utils.utils :as utils] - [status-im.ui.components.topbar :as topbar] - [quo.design-system.colors :as colors] - [status-im.ui.components.toolbar :as toolbar] - [status-im.ui.components.keyboard-avoid-presentation :as kb-presentation] - [reagent.core :as reagent] - [clojure.string :as string] - [quo.components.list.item :as list-item] - [status-im.ui.components.list.views :as list] - [status-im.ui.screens.status.views :as status.views]) + [status-im.ui.screens.status.views :as status.views] + [status-im.utils.utils :as utils]) (:require-macros [status-im.utils.views :as views])) (defn actions [{:keys [public-key added? blocked? ens-name] :as contact} muted?] (concat [{:label (i18n/label :t/chat) :icon :main-icons/message - :action #(re-frame/dispatch [:contact.ui/send-message-pressed {:public-key public-key - :ens-name ens-name}]) + :action #(re-frame/dispatch [:contact.ui/send-message-pressed + {:public-key public-key + :ens-name ens-name}]) :accessibility-label :start-conversation-button}] (if added? [{:label (i18n/label :t/remove-from-contacts) @@ -40,14 +41,16 @@ :disabled blocked? :accessibility-label :add-to-contacts-button :action (when-not blocked? - #(re-frame/dispatch [:contact.ui/add-to-contact-pressed public-key nil ens-name]))}]) + #(re-frame/dispatch [:contact.ui/add-to-contact-pressed public-key + nil ens-name]))}]) [{:label (i18n/label (if (or muted? blocked?) :t/unmute :t/mute)) :icon :main-icons/notification :accessibility-label :mute-chat :selected muted? :disabled blocked? :action (when-not blocked? - #(re-frame/dispatch [::chat.models/mute-chat-toggled public-key (not muted?)]))}] + #(re-frame/dispatch [::chat.models/mute-chat-toggled public-key + (not muted?)]))}] [{:label (i18n/label (if blocked? :t/unblock :t/block)) :negative true :selected blocked? @@ -62,31 +65,37 @@ :unblock-contact :block-contact)}])) -(defn render-detail [{:keys [public-key names name] :as detail}] +(defn render-detail + [{:keys [public-key names name] :as detail}] [quo/list-item {:title (:three-words-name names) - :subtitle [quo/text {:monospace true - :color :secondary} + :subtitle [quo/text + {:monospace true + :color :secondary} (utils/get-shortened-address public-key)] :icon [chat-icon/contact-icon-contacts-tab (multiaccounts/displayed-photo detail)] :accessibility-label :profile-public-key - :on-press #(re-frame/dispatch [:show-popover (merge {:view :share-chat-key - :address public-key} - (when (and (:ens-name names) name) - {:ens-name name}))]) + :on-press #(re-frame/dispatch [:show-popover + (merge {:view :share-chat-key + :address public-key} + (when (and (:ens-name names) name) + {:ens-name name}))]) :accessory [icons/icon :main-icons/share styles/contact-profile-detail-share-icon]}]) -(defn profile-details [contact] +(defn profile-details + [contact] (when contact [react/view [quo/list-header - [quo/text {:accessibility-label :profile-details - :color :inherit} + [quo/text + {:accessibility-label :profile-details + :color :inherit} (i18n/label :t/profile-details)]] [render-detail contact]])) -(defn pin-settings [public-key pin-count] +(defn pin-settings + [public-key pin-count] [quo/list-item {:title (i18n/label :t/pinned-messages) :size :small @@ -97,7 +106,8 @@ :on-press #(re-frame/dispatch [:contact.ui/pinned-messages-pressed public-key]) :chevron true}]) -(defn nickname-settings [{:keys [names]}] +(defn nickname-settings + [{:keys [names]}] [quo/list-item {:title (i18n/label :t/nickname) :size :small @@ -107,13 +117,16 @@ :on-press #(re-frame/dispatch [:open-modal :nickname]) :chevron true}]) -(defn save-nickname [public-key nickname] +(defn save-nickname + [public-key nickname] (re-frame/dispatch [:contacts/update-nickname public-key nickname])) -(defn valid-nickname? [nickname] +(defn valid-nickname? + [nickname] (not (string/blank? nickname))) -(defn- nickname-input [nickname entered-nickname public-key] +(defn- nickname-input + [nickname entered-nickname public-key] [quo/text-input {:on-change-text #(reset! entered-nickname %) :on-submit-editing #(when (valid-nickname? @entered-nickname) @@ -127,97 +140,129 @@ :return-key-type :done :auto-correct false}]) -(defn nickname-view [public-key {:keys [nickname ens-name three-words-name]}] +(defn nickname-view + [public-key {:keys [nickname ens-name three-words-name]}] (let [entered-nickname (reagent/atom nickname)] (fn [] - [kb-presentation/keyboard-avoiding-view {:style {:flex 1} - :ignore-offset true} - [topbar/topbar {:title (i18n/label :t/nickname) - :subtitle (or ens-name three-words-name) - :modal? true}] + [kb-presentation/keyboard-avoiding-view + {:style {:flex 1} + :ignore-offset true} + [topbar/topbar + {:title (i18n/label :t/nickname) + :subtitle (or ens-name three-words-name) + :modal? true}] [react/view {:flex 1 :padding 16} [react/text {:style {:color colors/gray :margin-bottom 16}} (i18n/label :t/nickname-description)] [nickname-input nickname entered-nickname public-key] - [react/text {:style {:align-self :flex-end :margin-top 16 - :color colors/gray}} + [react/text + {:style {:align-self :flex-end + :margin-top 16 + :color colors/gray}} (str (count @entered-nickname) " / 32")]] - [toolbar/toolbar {:show-border? true - :center - [quo/button - {:type :secondary - :on-press #(save-nickname public-key @entered-nickname)} - (i18n/label :t/done)]}]]))) + [toolbar/toolbar + {:show-border? true + :center + [quo/button + {:type :secondary + :on-press #(save-nickname public-key @entered-nickname)} + (i18n/label :t/done)]}]]))) -(views/defview nickname [] +(views/defview nickname + [] (views/letsubs [{:keys [public-key names]} [:contacts/current-contact]] [nickname-view public-key names])) -(defn button-item [{:keys [icon label action selected disabled negative]}] - [react/touchable-highlight {:on-press action :style {:flex 1} - :accessibility-label (str label "-item-button")} +(defn button-item + [{:keys [icon label action selected disabled negative]}] + [react/touchable-highlight + {:on-press action + :style {:flex 1} + :accessibility-label (str label "-item-button")} [react/view {:flex 1 :align-items :center} - [list-item/icon-column {:icon icon - :size :small - :icon-bg-color (if disabled - colors/gray-lighter - (if negative - (if selected colors/red colors/red-light) - (if selected colors/blue colors/blue-light))) - :icon-color (if disabled - colors/gray - (if negative - (if selected colors/white colors/red) - (if selected colors/white colors/blue)))}] - [react/text {:style {:text-align :center :color (if disabled - colors/gray - (if negative colors/red colors/blue)) - :font-size 12 :line-height 16 :margin-top 6} - :number-of-lines 2} + [list-item/icon-column + {:icon icon + :size :small + :icon-bg-color (if disabled + colors/gray-lighter + (if negative + (if selected colors/red colors/red-light) + (if selected colors/blue colors/blue-light))) + :icon-color (if disabled + colors/gray + (if negative + (if selected colors/white colors/red) + (if selected colors/white colors/blue)))}] + [react/text + {:style {:text-align :center + :color (if disabled + colors/gray + (if negative colors/red colors/blue)) + :font-size 12 + :line-height 16 + :margin-top 6} + :number-of-lines 2} label]]]) -(defn profile [] - (let [{:keys [public-key name ens-verified] :as contact} @(re-frame/subscribe [:contacts/current-contact]) - current-chat-id @(re-frame/subscribe [:chats/current-profile-chat]) - messages @(re-frame/subscribe [:chats/profile-messages-stream current-chat-id]) - no-messages? @(re-frame/subscribe [:chats/chat-no-messages? current-chat-id]) - muted? @(re-frame/subscribe [:chats/muted public-key]) - pinned-messages @(re-frame/subscribe [:chats/pinned public-key]) - [first-name second-name] (multiaccounts/contact-two-names contact true) - on-share #(re-frame/dispatch [:show-popover (merge - {:view :share-chat-key - :address public-key} - (when (and ens-verified name) - {:ens-name name}))])] +(defn profile + [] + (let [{:keys [public-key name ens-verified] :as contact} @(re-frame/subscribe + [:contacts/current-contact]) + current-chat-id @(re-frame/subscribe + [:chats/current-profile-chat]) + messages @(re-frame/subscribe + [:chats/profile-messages-stream + current-chat-id]) + no-messages? @(re-frame/subscribe [:chats/chat-no-messages? + current-chat-id]) + muted? @(re-frame/subscribe [:chats/muted + public-key]) + pinned-messages @(re-frame/subscribe [:chats/pinned + public-key]) + [first-name second-name] (multiaccounts/contact-two-names contact true) + on-share #(re-frame/dispatch + [:show-popover + (merge + {:view :share-chat-key + :address public-key} + (when (and ens-verified name) + {:ens-name name}))])] (when contact [:<> - [quo/header {:right-accessories [{:icon :main-icons/share - :accessibility-label :share-button - :on-press on-share}] - :left-accessories [{:icon :main-icons/close - :accessibility-label :back-button - :on-press #(re-frame/dispatch [:navigate-back])}]}] + [quo/header + {:right-accessories [{:icon :main-icons/share + :accessibility-label :share-button + :on-press on-share}] + :left-accessories [{:icon :main-icons/close + :accessibility-label :back-button + :on-press #(re-frame/dispatch [:navigate-back])}]}] [list/flat-list {:key-fn #(or (:message-id %) (:value %)) :header [:<> [(profile-header/extended-header - {:on-press on-share + {:on-press on-share :bottom-separator false - :title first-name - :photo (multiaccounts/displayed-photo contact) - :monospace (not ens-verified) - :subtitle second-name - :public-key public-key})] - [react/view {:height 1 :background-color colors/gray-lighter :margin-top 8}] + :title first-name + :photo (multiaccounts/displayed-photo contact) + :monospace (not ens-verified) + :subtitle second-name + :public-key public-key})] + [react/view + {:height 1 :background-color colors/gray-lighter :margin-top 8}] [nickname-settings contact] [pin-settings public-key (count pinned-messages)] [react/view {:height 1 :background-color colors/gray-lighter}] - [react/view {:padding-top 17 :flex-direction :row :align-items :stretch :flex 1} + [react/view + {:padding-top 17 + :flex-direction :row + :align-items :stretch + :flex 1} (for [{:keys [label] :as action} (actions contact muted?) - :when label] + :when label] ^{:key label} [button-item action])] - [react/view {:height 1 :background-color colors/gray-lighter :margin-top 16}] + [react/view + {:height 1 :background-color colors/gray-lighter :margin-top 16}] (when no-messages? [react/view {:padding-horizontal 32 :margin-top 32} [react/view (styles/updates-descr-cont) diff --git a/src/status_im/ui/screens/profile/group_chat/views.cljs b/src/status_im/ui/screens/profile/group_chat/views.cljs index 358e9ba39c..3b08e7454f 100644 --- a/src/status_im/ui/screens/profile/group_chat/views.cljs +++ b/src/status_im/ui/screens/profile/group_chat/views.cljs @@ -6,20 +6,21 @@ [status-im.i18n.i18n :as i18n] [status-im.multiaccounts.core :as multiaccounts] [status-im.ui.components.chat-icon.screen :as chat-icon] + [status-im.ui.components.common.common :as components.common] [status-im.ui.components.list.views :as list] [status-im.ui.components.profile-header.view :as profile-header] [status-im.ui.components.react :as react] - [status-im.ui.screens.chat.sheets :as chat.sheets] - [status-im.ui.screens.profile.components.styles :as profile.components.styles] [status-im.ui.components.topbar :as topbar] - [status-im.ui.components.common.common :as components.common] [status-im.ui.screens.chat.message.message :as message] [status-im.ui.screens.chat.photos :as photos] + [status-im.ui.screens.chat.sheets :as chat.sheets] [status-im.ui.screens.chat.utils :as chat.utils] + [status-im.ui.screens.profile.components.styles :as profile.components.styles] [utils.debounce :as debounce]) (:require-macros [status-im.utils.views :refer [defview letsubs]])) -(defn member-sheet [chat-id member us-admin?] +(defn member-sheet + [chat-id member us-admin?] (let [[first-name _] (multiaccounts/contact-two-names member false)] [react/view [quo/list-item @@ -40,16 +41,20 @@ :title (i18n/label :t/make-admin) :accessibility-label :make-admin :icon :main-icons/make-admin - :on-press #(chat.sheets/hide-sheet-and-dispatch [:group-chats.ui/make-admin-pressed chat-id (:public-key member)])}]) + :on-press #(chat.sheets/hide-sheet-and-dispatch [:group-chats.ui/make-admin-pressed + chat-id (:public-key member)])}]) (when-not (:admin? member) [quo/list-item {:theme :accent :title (i18n/label :t/remove-from-chat) :accessibility-label :remove-from-chat :icon :main-icons/remove-contact - :on-press #(chat.sheets/hide-sheet-and-dispatch [:group-chats.ui/remove-member-pressed chat-id (:public-key member)])}])])) + :on-press #(chat.sheets/hide-sheet-and-dispatch + [:group-chats.ui/remove-member-pressed chat-id + (:public-key member)])}])])) -(defn render-member [{:keys [public-key] :as member} _ _ {:keys [chat-id admin? current-user-identity]}] +(defn render-member + [{:keys [public-key] :as member} _ _ {:keys [chat-id admin? current-user-identity]}] (let [[first-name second-name] (multiaccounts/contact-two-names member false)] [quo/list-item (merge @@ -66,25 +71,30 @@ (when (and admin? (not (:admin? member)) (not= public-key current-user-identity)) - {:accessory [quo/button {:on-press #(re-frame/dispatch [:bottom-sheet/show-sheet - {:content (fn [] - [member-sheet chat-id member admin?])}]) - :type :icon - :theme :icon - :accessibility-label :menu-option} + {:accessory [quo/button + {:on-press #(re-frame/dispatch + [:bottom-sheet/show-sheet + {:content (fn [] + [member-sheet chat-id member admin?])}]) + :type :icon + :theme :icon + :accessibility-label :menu-option} :main-icons/more]}))])) -(defview chat-group-members-view [chat-id admin? current-user-identity] +(defview chat-group-members-view + [chat-id admin? current-user-identity] (letsubs [members [:contacts/current-chat-contacts]] (when (seq members) - [list/flat-list {:data members - :key-fn :address - :render-data {:chat-id chat-id - :admin? admin? - :current-user-identity current-user-identity} - :render-fn render-member}]))) + [list/flat-list + {:data members + :key-fn :address + :render-data {:chat-id chat-id + :admin? admin? + :current-user-identity current-user-identity} + :render-fn render-member}]))) -(defn members-list [{:keys [chat-id admin? current-pk allow-adding-members?]}] +(defn members-list + [{:keys [chat-id admin? current-pk allow-adding-members?]}] [react/view [quo/list-header (i18n/label :t/members-title)] (when allow-adding-members? @@ -95,18 +105,20 @@ :on-press #(re-frame/dispatch [:open-modal :add-participants-toggle-list])}]) [chat-group-members-view chat-id admin? current-pk]]) -(defn hide-sheet-and-dispatch [event] +(defn hide-sheet-and-dispatch + [event] (re-frame/dispatch [:bottom-sheet/hide]) (debounce/dispatch-and-chill event 2000)) -(defn invitation-sheet [{:keys [introduction-message id]} contact] - (let [members @(re-frame/subscribe [:contacts/current-chat-contacts]) +(defn invitation-sheet + [{:keys [introduction-message id]} contact] + (let [members @(re-frame/subscribe [:contacts/current-chat-contacts]) allow-adding-members? (< (count members) constants/max-group-chat-participants)] [react/view - (let [message {:content {:parsed-text - [{:type "paragraph" - :children [{:literal introduction-message}]}]} - :content-type constants/content-type-text}] + (let [message {:content {:parsed-text + [{:type "paragraph" + :children [{:literal introduction-message}]}]} + :content-type constants/content-type-text}] [react/view {:margin-bottom 8 :margin-right 16} [react/view {:padding-left 72} (chat.utils/format-author-old contact)] @@ -118,7 +130,7 @@ {:theme :accent :disabled (not allow-adding-members?) :title (i18n/label :t/accept) - :subtitle (when-not allow-adding-members? (i18n/label :t/members-limit-reached)) + :subtitle (when-not allow-adding-members? (i18n/label :t/members-limit-reached)) :accessibility-label :accept-invitation-button :icon :main-icons/checkmark-circle :on-press #(hide-sheet-and-dispatch @@ -130,7 +142,8 @@ :icon :main-icons/cancel :on-press #(hide-sheet-and-dispatch [:send-group-chat-membership-rejection id])}]])) -(defn contacts-list-item [{:keys [from] :as invitation}] +(defn contacts-list-item + [{:keys [from] :as invitation}] (let [contact (or @(re-frame/subscribe [:contacts/contact-by-identity from]) {:public-key from})] [quo/list-item {:title (multiaccounts/displayed-name contact) @@ -140,30 +153,37 @@ {:content (fn [] [invitation-sheet invitation contact])}])}])) -(defview group-chat-invite [] +(defview group-chat-invite + [] (letsubs [{:keys [chat-id]} [:chats/current-chat]] - (let [invitations @(re-frame/subscribe [:group-chat/pending-invitations-by-chat-id chat-id])] + (let [invitations @(re-frame/subscribe [:group-chat/pending-invitations-by-chat-id chat-id])] [react/view {:flex 1} [topbar/topbar {:title (i18n/label :t/group-invite)}] [react/scroll-view {:flex 1} [react/view {:margin-top 26} (if (seq invitations) [list/flat-list - {:data invitations - :key-fn :id - :render-fn contacts-list-item}] - [react/text {:style {:color colors/gray :margin-top 28 :text-align :center - :padding-horizontal 16}} + {:data invitations + :key-fn :id + :render-fn contacts-list-item}] + [react/text + {:style {:color colors/gray + :margin-top 28 + :text-align :center + :padding-horizontal 16}} (i18n/label :t/empty-pending-invitations-descr)])]]]))) -(defview group-chat-profile [] - (letsubs [{:keys [admins chat-id member? chat-name color contacts] :as current-chat} [:chats/current-chat] - members [:contacts/current-chat-contacts] - current-pk [:multiaccount/public-key] +(defview group-chat-profile + [] + (letsubs [{:keys [admins chat-id member? chat-name color contacts] :as current-chat} + [:chats/current-chat] + members [:contacts/current-chat-contacts] + current-pk [:multiaccount/public-key] pinned-messages [:chats/pinned chat-id]] (when current-chat (let [admin? (get admins current-pk) - allow-adding-members? (and admin? member? + allow-adding-members? (and admin? + member? (< (count members) constants/max-group-chat-participants))] [react/view profile.components.styles/profile [quo/animated-header @@ -174,7 +194,8 @@ :right-accessories (when (and admin? member?) [{:icon :icons/edit :accessibility-label :edit-button - :on-press #(re-frame/dispatch [:open-modal :edit-group-chat-name])}]) + :on-press #(re-frame/dispatch [:open-modal + :edit-group-chat-name])}]) :extended-header (profile-header/extended-header {:title chat-name :color color @@ -189,17 +210,18 @@ :icon :main-icons/share :accessory (let [invitations (count @(re-frame/subscribe - [:group-chat/pending-invitations-by-chat-id chat-id]))] + [:group-chat/pending-invitations-by-chat-id + chat-id]))] (when (pos? invitations) [components.common/counter {:size 22} invitations])) :on-press #(re-frame/dispatch [:navigate-to :group-chat-invite])}]) [quo/list-item - {:title (i18n/label :t/pinned-messages) - :icon :main-icons/pin - :accessory :text - :accessory-text (count pinned-messages) - :chevron true - :on-press #(re-frame/dispatch [:contact.ui/pinned-messages-pressed chat-id])}] + {:title (i18n/label :t/pinned-messages) + :icon :main-icons/pin + :accessory :text + :accessory-text (count pinned-messages) + :chevron true + :on-press #(re-frame/dispatch [:contact.ui/pinned-messages-pressed chat-id])}] (when member? [quo/list-item {:theme :negative @@ -207,7 +229,8 @@ :accessibility-label :leave-chat-button :icon :main-icons/arrow-left :on-press #(re-frame/dispatch [:group-chats.ui/leave-chat-pressed chat-id])}]) - [members-list {:chat-id chat-id - :admin? admin? - :current-pk current-pk - :allow-adding-members? allow-adding-members?}]]]])))) + [members-list + {:chat-id chat-id + :admin? admin? + :current-pk current-pk + :allow-adding-members? allow-adding-members?}]]]])))) diff --git a/src/status_im/ui/screens/profile/seed/styles.cljs b/src/status_im/ui/screens/profile/seed/styles.cljs index 41ce3f1fb8..9298e864bb 100644 --- a/src/status_im/ui/screens/profile/seed/styles.cljs +++ b/src/status_im/ui/screens/profile/seed/styles.cljs @@ -13,13 +13,13 @@ :margin-vertical 16}) (def intro-description - {:margin-top 8 - :text-align :center - :color colors/gray}) + {:margin-top 8 + :text-align :center + :color colors/gray}) (def intro-button - {:flex-direction :row - :margin-vertical 16}) + {:flex-direction :row + :margin-vertical 16}) (def six-words-container {:flex 1 @@ -53,12 +53,12 @@ {:flex 1}) (def twelve-words-columns - {:margin-top 8 - :margin-bottom 16 - :flex-direction :row - :border-radius 8 - :border-width 1 - :border-color colors/gray-lighter}) + {:margin-top 8 + :margin-bottom 16 + :flex-direction :row + :border-radius 8 + :border-width 1 + :border-color colors/gray-lighter}) (def twelve-words-columns-separator {:width 1 diff --git a/src/status_im/ui/screens/profile/seed/views.cljs b/src/status_im/ui/screens/profile/seed/views.cljs index 3cd55e7272..cdbb090fc6 100644 --- a/src/status_im/ui/screens/profile/seed/views.cljs +++ b/src/status_im/ui/screens/profile/seed/views.cljs @@ -1,18 +1,18 @@ (ns status-im.ui.screens.profile.seed.views (:require-macros [status-im.utils.views :refer [defview letsubs]]) - (:require [status-im.ui.components.react :as react] - [status-im.ui.components.topbar :as topbar] - [status-im.react-native.resources :as resources] + (:require [clojure.string :as string] + [quo.core :as quo] [re-frame.core :as re-frame] [reagent.core :as reagent] - [status-im.ui.components.icons.icons :as icons] - [status-im.ui.components.common.styles :as components.common.styles] - [status-im.ui.components.toolbar :as toolbar] - [clojure.string :as string] - [status-im.utils.utils :as utils] - [status-im.ui.screens.profile.seed.styles :as styles] [status-im.i18n.i18n :as i18n] - [quo.core :as quo])) + [status-im.react-native.resources :as resources] + [status-im.ui.components.common.styles :as components.common.styles] + [status-im.ui.components.icons.icons :as icons] + [status-im.ui.components.react :as react] + [status-im.ui.components.toolbar :as toolbar] + [status-im.ui.components.topbar :as topbar] + [status-im.ui.screens.profile.seed.styles :as styles] + [status-im.utils.utils :as utils])) (def steps-numbers {:intro 1 @@ -21,28 +21,35 @@ :second-word 3 :finish 3}) -(defn step-back [step] +(defn step-back + [step] (case step (:intro :12-words) (re-frame/dispatch [:navigate-back]) - :first-word (re-frame/dispatch [:my-profile/set-step :12-words]) - :second-word (re-frame/dispatch [:my-profile/set-step :first-word]))) + :first-word (re-frame/dispatch [:my-profile/set-step :12-words]) + :second-word (re-frame/dispatch [:my-profile/set-step :first-word]))) -(defn intro [] - [react/scroll-view {:style {:padding-horizontal 16} - :content-container-style {:align-items :center - :justify-content :center}} - [react/image {:source (resources/get-image :lock) - :style styles/intro-image}] - [react/i18n-text {:style styles/intro-text - :key :your-data-belongs-to-you}] - [react/i18n-text {:style styles/intro-description - :key :your-data-belongs-to-you-description}] +(defn intro + [] + [react/scroll-view + {:style {:padding-horizontal 16} + :content-container-style {:align-items :center + :justify-content :center}} + [react/image + {:source (resources/get-image :lock) + :style styles/intro-image}] + [react/i18n-text + {:style styles/intro-text + :key :your-data-belongs-to-you}] + [react/i18n-text + {:style styles/intro-description + :key :your-data-belongs-to-you-description}] [react/view {:style styles/intro-button} [quo/button {:on-press #(re-frame/dispatch [:set-in [:my-profile/seed :step] :12-words])} (i18n/label :t/ok-continue)]]]) -(defn six-words [words] +(defn six-words + [words] [react/view {:style styles/six-words-container} (for [[i word] words] ^{:key (str "word" i)} @@ -55,15 +62,17 @@ (when (not= i (first (last words))) [react/view {:style styles/six-words-separator}])])]) -(defview twelve-words [{:keys [mnemonic]}] +(defview twelve-words + [{:keys [mnemonic]}] (letsubs [mnemonic-vec (vec (map-indexed vector (clojure.string/split mnemonic #" "))) - ref (reagent/atom nil)] + ref (reagent/atom nil)] [react/view {:flex 1} [react/view {:style styles/twelve-words-container} [react/text {:style styles/twelve-words-label} (i18n/label :t/your-recovery-phrase)] - [react/view {:style styles/twelve-words-columns - :ref (partial reset! ref)} + [react/view + {:style styles/twelve-words-columns + :ref (partial reset! ref)} [six-words (subvec mnemonic-vec 0 6)] [react/view {:style styles/twelve-words-columns-separator}] [six-words (subvec mnemonic-vec 6 12)]] @@ -72,26 +81,30 @@ [react/view styles/twelve-words-spacer]] [toolbar/toolbar {:right - [quo/button {:type :secondary - :after :main-icon/next - :on-press #(re-frame/dispatch [:my-profile/enter-two-random-words])} + [quo/button + {:type :secondary + :after :main-icon/next + :on-press #(re-frame/dispatch [:my-profile/enter-two-random-words])} (i18n/label :t/next)]}]])) -(defn next-handler [word entered-word step] +(defn next-handler + [word entered-word step] (fn [_] - (cond (not= word entered-word) - (re-frame/dispatch [:set-in [:my-profile/seed :error] (i18n/label :t/wrong-word)]) + (cond + (not= word entered-word) + (re-frame/dispatch [:set-in [:my-profile/seed :error] (i18n/label :t/wrong-word)]) - (= :first-word step) - (re-frame/dispatch [:my-profile/set-step :second-word]) + (= :first-word step) + (re-frame/dispatch [:my-profile/set-step :second-word]) - :else - (utils/show-question - (i18n/label :t/are-you-sure?) - (i18n/label :t/are-you-sure-description) - #(re-frame/dispatch [:my-profile/finish]))))) + :else + (utils/show-question + (i18n/label :t/are-you-sure?) + (i18n/label :t/are-you-sure-description) + #(re-frame/dispatch [:my-profile/finish]))))) -(defn enter-word [step [idx word] error entered-word] +(defn enter-word + [step [idx word] error entered-word] ^{:key word} [:<> [react/scroll-view {:style styles/enter-word-container} @@ -114,16 +127,18 @@ [react/view styles/twelve-words-spacer]] [toolbar/toolbar {:right - [quo/button (merge {:type :secondary - :disabled (string/blank? entered-word) - :on-press (next-handler word entered-word step)} - (when-not (= :second-word step) - {:after :main-icon/next})) + [quo/button + (merge {:type :secondary + :disabled (string/blank? entered-word) + :on-press (next-handler word entered-word step)} + (when-not (= :second-word step) + {:after :main-icon/next})) (if (= :second-word step) (i18n/label :t/done) (i18n/label :t/next))]}]]) -(defn finish [] +(defn finish + [] [react/view {:style styles/finish-container} [react/view {:style styles/finish-logo-container} [react/view {:style (components.common.styles/logo-container 80)} @@ -136,16 +151,19 @@ [quo/button {:on-press #(re-frame/dispatch [:navigate-back])} (i18n/label :t/ok-got-it)]]]) -(defview backup-seed [] - (letsubs [current-multiaccount [:multiaccount] +(defview backup-seed + [] + (letsubs [current-multiaccount [:multiaccount] {:keys [step first-word second-word error word]} [:my-profile/recovery]] - [react/keyboard-avoiding-view {:style {:flex 1} - :ignore-offset true} - [topbar/topbar {:title (i18n/label :t/backup-recovery-phrase) - :subtitle (i18n/label :t/step-i-of-n {:step (steps-numbers step) :number 3}) - :navigation (if (= :finish step) - :none - {:on-press #(step-back step)})}] + [react/keyboard-avoiding-view + {:style {:flex 1} + :ignore-offset true} + [topbar/topbar + {:title (i18n/label :t/backup-recovery-phrase) + :subtitle (i18n/label :t/step-i-of-n {:step (steps-numbers step) :number 3}) + :navigation (if (= :finish step) + :none + {:on-press #(step-back step)})}] (case step :intro [intro] :12-words [twelve-words current-multiaccount] diff --git a/src/status_im/ui/screens/profile/user/edit_picture.cljs b/src/status_im/ui/screens/profile/user/edit_picture.cljs index c389b3a13f..9143430caa 100644 --- a/src/status_im/ui/screens/profile/user/edit_picture.cljs +++ b/src/status_im/ui/screens/profile/user/edit_picture.cljs @@ -2,45 +2,52 @@ (:require [quo.core :as quo] [re-frame.core :as re-frame] [status-im.i18n.i18n :as i18n] - [status-im.utils.config :as config] [status-im.multiaccounts.core :as multiaccounts] - [status-im.ui.components.react :as react])) + [status-im.ui.components.react :as react] + [status-im.utils.config :as config])) (def crop-size 1000) -(def crop-opts {:cropping true - :cropperCircleOverlay true - :width crop-size - :height crop-size}) +(def crop-opts + {:cropping true + :cropperCircleOverlay true + :width crop-size + :height crop-size}) -(defn pick-pic [] +(defn pick-pic + [] (re-frame/dispatch [:bottom-sheet/hide]) (react/show-image-picker #(re-frame/dispatch [::multiaccounts/save-profile-picture (.-path ^js %) 0 0 crop-size crop-size]) crop-opts)) -(defn take-pic [] +(defn take-pic + [] (re-frame/dispatch [:bottom-sheet/hide]) (react/show-image-picker-camera #(re-frame/dispatch [::multiaccounts/save-profile-picture (.-path ^js %) 0 0 crop-size crop-size]) crop-opts)) -(defn bottom-sheet [has-picture] +(defn bottom-sheet + [has-picture] (fn [] [:<> - [quo/list-item {:accessibility-label :take-photo - :theme :accent - :icon :main-icons/camera - :title (i18n/label :t/profile-pic-take) - :on-press take-pic}] - [quo/list-item {:accessibility-label :pick-photo - :icon :main-icons/gallery - :theme :accent - :title (i18n/label :t/profile-pic-pick) - :on-press pick-pic}] + [quo/list-item + {:accessibility-label :take-photo + :theme :accent + :icon :main-icons/camera + :title (i18n/label :t/profile-pic-take) + :on-press take-pic}] + [quo/list-item + {:accessibility-label :pick-photo + :icon :main-icons/gallery + :theme :accent + :title (i18n/label :t/profile-pic-pick) + :on-press pick-pic}] (when (and config/enable-remove-profile-picture? has-picture) - [quo/list-item {:accessibility-label :remove-photo - :icon :main-icons/delete - :theme :accent - :title (i18n/label :t/profile-pic-remove) - :on-press #(re-frame/dispatch [::multiaccounts/delete-profile-picture nil])}])])) + [quo/list-item + {:accessibility-label :remove-photo + :icon :main-icons/delete + :theme :accent + :title (i18n/label :t/profile-pic-remove) + :on-press #(re-frame/dispatch [::multiaccounts/delete-profile-picture nil])}])])) diff --git a/src/status_im/ui/screens/profile/user/views.cljs b/src/status_im/ui/screens/profile/user/views.cljs index b31f83e5f2..5ff21ab870 100644 --- a/src/status_im/ui/screens/profile/user/views.cljs +++ b/src/status_im/ui/screens/profile/user/views.cljs @@ -1,30 +1,31 @@ (ns status-im.ui.screens.profile.user.views - (:require [re-frame.core :as re-frame] - [reagent.core :as reagent] - [status-im.i18n.i18n :as i18n] - [quo.core :as quo] + (:require [quo.core :as quo] [quo.design-system.colors :as colors] + [quo.design-system.spacing :as spacing] + [re-frame.core :as re-frame] + [reagent.core :as reagent] + [status-im.ethereum.stateofus :as stateofus] + [status-im.i18n.i18n :as i18n] [status-im.multiaccounts.core :as multiaccounts] [status-im.ui.components.common.common :as components.common] [status-im.ui.components.copyable-text :as copyable-text] [status-im.ui.components.list-selection :as list-selection] + [status-im.ui.components.profile-header.view :as profile-header] [status-im.ui.components.qr-code-viewer.views :as qr-code-viewer] [status-im.ui.components.react :as react] + [status-im.ui.screens.profile.user.edit-picture :as edit] [status-im.ui.screens.profile.user.styles :as styles] - [status-im2.setup.config :as config] + [status-im.ui.screens.profile.visibility-status.views :as visibility-status] [status-im.utils.gfycat.core :as gfy] [status-im.utils.universal-links.utils :as universal-links] - [status-im.ui.components.profile-header.view :as profile-header] - [status-im.ui.screens.profile.user.edit-picture :as edit] [status-im.utils.utils :as utils] - [status-im.ethereum.stateofus :as stateofus] - [quo.design-system.spacing :as spacing] - [status-im.ui.screens.profile.visibility-status.views :as visibility-status]) + [status-im2.setup.config :as config]) (:require-macros [status-im.utils.views :as views])) -(views/defview share-chat-key [] +(views/defview share-chat-key + [] (views/letsubs [{:keys [address ens-name]} [:popover/popover] - width (reagent/atom nil)] + width (reagent/atom nil)] (let [link (universal-links/generate-link :user :external (or ens-name address))] [react/view {:on-layout #(reset! width (-> ^js % .-nativeEvent .-layout .-width))} [react/view {:style {:padding-top 16 :padding-horizontal 16}} @@ -40,27 +41,33 @@ {:monospace true :accessibility-label :ens-username} ens-name]] - [react/view {:height 1 :margin-top 12 :margin-horizontal -16 - :background-color colors/gray-lighter}]]) + [react/view + {:height 1 + :margin-top 12 + :margin-horizontal -16 + :background-color colors/gray-lighter}]]) [copyable-text/copyable-text-view {:label :t/chat-key :container-style {:margin-top 12 :margin-bottom 4} :copied-text address} - [quo/text {:number-of-lines 1 - :ellipsize-mode :middle - :accessibility-label :chat-key - :monospace true} + [quo/text + {:number-of-lines 1 + :ellipsize-mode :middle + :accessibility-label :chat-key + :monospace true} address]]] [react/view styles/share-link-button [quo/button {:on-press (fn [] (re-frame/dispatch [:hide-popover]) (js/setTimeout - #(list-selection/open-share {:message link}) 250)) + #(list-selection/open-share {:message link}) + 250)) :accessibility-label :share-my-contact-code-button} (i18n/label :t/share-link)]]]))) -(defn content [] +(defn content + [] (let [{:keys [preferred-name mnemonic keycard-pairing]} @@ -72,7 +79,7 @@ [:<> [visibility-status/visibility-status-button visibility-status/calculate-button-height-and-dispatch-popover] - [quo/separator {:style {:margin-top (:tiny spacing/spacing)}}] + [quo/separator {:style {:margin-top (:tiny spacing/spacing)}}] [quo/list-item (cond-> {:title (or (when registrar preferred-name) (i18n/label :t/ens-usernames)) @@ -177,15 +184,17 @@ :on-press #(re-frame/dispatch [:multiaccounts.logout.ui/logout-pressed])}]]])) -(defn my-profile [] +(defn my-profile + [] (fn [] (let [{:keys [public-key ens-verified preferred-name] - :as account} @(re-frame/subscribe [:profile/multiaccount]) - on-share #(re-frame/dispatch [:show-popover - {:view :share-chat-key - :address public-key - :ens-name preferred-name}]) - has-picture @(re-frame/subscribe [:profile/has-picture])] + :as account} + @(re-frame/subscribe [:profile/multiaccount]) + on-share #(re-frame/dispatch [:show-popover + {:view :share-chat-key + :address public-key + :ens-name preferred-name}]) + has-picture @(re-frame/subscribe [:profile/has-picture])] [react/view {:flex 1} [quo/animated-header {:right-accessories [{:accessibility-label :share-header-button @@ -195,7 +204,8 @@ :extended-header (profile-header/extended-header {:on-press on-share :on-edit #(re-frame/dispatch [:bottom-sheet/show-sheet - {:content (edit/bottom-sheet has-picture)}]) + {:content (edit/bottom-sheet + has-picture)}]) :title (multiaccounts/displayed-name account) :photo (multiaccounts/displayed-photo account) :monospace (not ens-verified) diff --git a/src/status_im/ui/screens/profile/visibility_status/styles.cljs b/src/status_im/ui/screens/profile/visibility_status/styles.cljs index bda70e7cdf..334da036d2 100644 --- a/src/status_im/ui/screens/profile/visibility_status/styles.cljs +++ b/src/status_im/ui/screens/profile/visibility_status/styles.cljs @@ -2,7 +2,8 @@ (:require [quo.design-system.colors :as colors] [quo2.foundations.colors :as quo2.colors])) -(defn visibility-status-button-container [] +(defn visibility-status-button-container + [] {:background-color (:interactive-02 @colors/theme) :margin-left 16 :border-radius 16 @@ -39,22 +40,26 @@ :margin-left margin-left :border-width border-width})) -(defn visibility-status-text [] +(defn visibility-status-text + [] {:color (:text-01 @colors/theme) :font-size 16 :text-align :center}) -(defn visibility-status-subtitle [] +(defn visibility-status-subtitle + [] {:color (:text-02 @colors/theme) :font-size 16 :margin-left 22 :margin-right 6}) -(defn visibility-status-option [] +(defn visibility-status-option + [] {:flex-direction :row :align-items :center}) -(defn visibility-status-options [scale position] +(defn visibility-status-options + [scale position] {:background-color (if (colors/dark?) (:interactive-02 @colors/theme) colors/white) @@ -62,27 +67,30 @@ :border-top-left-radius 4 :justify-content :center :align-self :flex-start - :left 16 + :left 16 :top -76 - :padding-bottom 6 - :padding-top 8 - :transform [{:scaleY scale} - {:translateY position}]}) + :padding-bottom 6 + :padding-top 8 + :transform [{:scaleY scale} + {:translateY position}]}) -(defn visibility-status-popover-container [] +(defn visibility-status-popover-container + [] {:position :absolute :top 0 :bottom 0 :left 0 :right 0}) -(defn visibility-status-popover-ios-backdrop [alpha-value] +(defn visibility-status-popover-ios-backdrop + [alpha-value] {:flex 1 :background-color colors/black-persist :opacity alpha-value}) -(defn visibility-status-popover-child-container [window-height] - {:position :absolute - :height window-height - :left 0 - :right 0}) +(defn visibility-status-popover-child-container + [window-height] + {:position :absolute + :height window-height + :left 0 + :right 0}) diff --git a/src/status_im/ui/screens/profile/visibility_status/utils.cljs b/src/status_im/ui/screens/profile/visibility_status/utils.cljs index 106f59e08a..07e13d4d98 100644 --- a/src/status_im/ui/screens/profile/visibility_status/utils.cljs +++ b/src/status_im/ui/screens/profile/visibility_status/utils.cljs @@ -21,8 +21,8 @@ ;; Note: Only send pings if the user interacted with the app in the last x minutes. (def visibility-status-type-data {constants/visibility-status-unknown - {:color colors/red - :title (i18n/label :t/error)} + {:color colors/red + :title (i18n/label :t/error)} constants/visibility-status-automatic {:color quo2.colors/success-50 :title (i18n/label :t/status-automatic) @@ -32,8 +32,8 @@ :title (i18n/label :t/status-dnd) :subtitle (i18n/label :t/status-dnd-subtitle)} constants/visibility-status-always-online - {:color quo2.colors/success-50 - :title (i18n/label :t/status-always-online)} + {:color quo2.colors/success-50 + :title (i18n/label :t/status-always-online)} constants/visibility-status-inactive {:color colors/color-inactive :title (i18n/label :t/status-inactive) @@ -41,8 +41,8 @@ (def visibility-status-type-data-old {constants/visibility-status-unknown - {:color colors/red - :title (i18n/label :t/error)} + {:color colors/red + :title (i18n/label :t/error)} constants/visibility-status-automatic {:color colors/color-online :title (i18n/label :t/status-automatic) @@ -52,8 +52,8 @@ :title (i18n/label :t/status-dnd) :subtitle (i18n/label :t/status-dnd-subtitle)} constants/visibility-status-always-online - {:color colors/color-online - :title (i18n/label :t/status-always-online)} + {:color colors/color-online + :title (i18n/label :t/status-always-online)} constants/visibility-status-inactive {:color colors/color-inactive :title (i18n/label :t/status-inactive) @@ -73,10 +73,12 @@ constants/visibility-status-inactive status-type))) -(defn icon-dot-color [{:keys [status-type] :or {status-type constants/visibility-status-inactive}}] +(defn icon-dot-color + [{:keys [status-type] :or {status-type constants/visibility-status-inactive}}] (:color (get visibility-status-type-data status-type))) -(defn my-icon? [public-key] +(defn my-icon? + [public-key] (or (string/blank? public-key) (= public-key ( (when automatic? - [rn/view {:style (styles/visibility-status-profile-dot - {:color colors/color-inactive - :size size - :border-width border-width - :margin-left 6 - :new-ui? new-ui?})}]) - [rn/view {:style (styles/visibility-status-profile-dot - {:color color - :size size - :border-width border-width - :margin-left margin-left - :new-ui? new-ui?})}]])) + [rn/view + {:style (styles/visibility-status-profile-dot + {:color colors/color-inactive + :size size + :border-width border-width + :margin-left 6 + :new-ui? new-ui?})}]) + [rn/view + {:style (styles/visibility-status-profile-dot + {:color color + :size size + :border-width border-width + :margin-left margin-left + :new-ui? new-ui?})}]])) -(defn visibility-status-button [on-press props] +(defn visibility-status-button + [on-press props] (let [logged-in? ( event .-nativeEvent .-codeStringValue)] (string/trim data))) -(defn- topbar [_ {:keys [title] :as opts}] +(defn- topbar + [_ {:keys [title] :as opts}] [topbar/topbar {:background colors/black-persist :use-insets true :border-bottom false :navigation :none - :left-component [quo/button {:type :secondary - :on-press #(re-frame/dispatch [:qr-scanner.callback/scan-qr-code-cancel opts])} + :left-component [quo/button + {:type :secondary + :on-press #(re-frame/dispatch [:qr-scanner.callback/scan-qr-code-cancel opts])} [quo/text {:style {:color colors/white-persist}} (i18n/label :t/cancel)]] - :title-component [quo/text {:style {:color colors/white-persist} - :weight :bold - :number-of-lines 1 - :align :center - :size :large} + :title-component [quo/text + {:style {:color colors/white-persist} + :weight :bold + :number-of-lines 1 + :align :center + :size :large} (or title (i18n/label :t/scan-qr))]}]) -(defn qr-test-view [opts] +(defn qr-test-view + [opts] (let [text-value (atom "")] - [react/view {:flex 1 - :align-items :center - :justify-content :center} - [react/text-input {:multiline true - :style {:color colors/white-persist} - :on-change-text #(reset! text-value %)}] + [react/view + {:flex 1 + :align-items :center + :justify-content :center} + [react/text-input + {:multiline true + :style {:color colors/white-persist} + :on-change-text #(reset! text-value %)}] [react/view {:flex-direction :row} [quo/button {:on-press #(re-frame/dispatch [:qr-scanner.callback/scan-qr-code-cancel opts])} "Cancel"] [quo/button - {:on-press #(re-frame/dispatch [:qr-scanner.callback/scan-qr-code-success opts (when-let [text @text-value] (-> text string/trim (string/replace #"^Ethereum:" "ethereum:")))])} + {:on-press #(re-frame/dispatch + [:qr-scanner.callback/scan-qr-code-success opts + (when-let [text @text-value] + (-> text string/trim (string/replace #"^Ethereum:" "ethereum:")))])} "Ok"]]])) -(defn corner [border1 border2 corner] - [react/view (assoc {:border-color colors/white-persist :width 60 :height 60} border1 5 border2 5 corner 32)]) +(defn corner + [border1 border2 corner] + [react/view + (assoc {:border-color colors/white-persist :width 60 :height 60} border1 5 border2 5 corner 32)]) -(defn- viewfinder [size] +(defn- viewfinder + [size] [react/view {:style styles/viewfinder-port} [react/view {:width size :height size :justify-content :space-between} [react/view {:flex-direction :row :justify-content :space-between} @@ -64,17 +77,20 @@ [corner :border-bottom-width :border-left-width :border-bottom-left-radius] [corner :border-bottom-width :border-right-width :border-bottom-right-radius]]]]) -(defn on-barcode-read [opts data] +(defn on-barcode-read + [opts data] (re-frame/dispatch [:qr-scanner.callback/scan-qr-code-success opts (get-qr-code-data data)])) -(defview qr-scanner [] - (letsubs [read-once? (atom false) +(defview qr-scanner + [] + (letsubs [read-once? (atom false) {:keys [height width]} [:dimensions/window] - camera-flashlight [:wallet.send/camera-flashlight] - opts [:get-screen-params] - camera-ref (atom nil)] - [react/view {:flex 1 - :background-color colors/black-persist} + camera-flashlight [:wallet.send/camera-flashlight] + opts [:get-screen-params] + camera-ref (atom nil)] + [react/view + {:flex 1 + :background-color colors/black-persist} [topbar camera-flashlight opts] (if config/qr-test-menu-enabled? [qr-test-view opts] diff --git a/src/status_im/ui/screens/reset_password/views.cljs b/src/status_im/ui/screens/reset_password/views.cljs index 09a978ab55..c2511cc8ac 100644 --- a/src/status_im/ui/screens/reset_password/views.cljs +++ b/src/status_im/ui/screens/reset_password/views.cljs @@ -1,13 +1,13 @@ (ns status-im.ui.screens.reset-password.views - (:require [re-frame.core :as re-frame] - [status-im.i18n.i18n :as i18n] - [quo.core :as quo] - [status-im.ui.components.react :as react] + (:require [quo.core :as quo] [quo.design-system.colors :as colors] - [status-im.ui.components.icons.icons :as icons] + [re-frame.core :as re-frame] + [status-im.i18n.i18n :as i18n] [status-im.multiaccounts.reset-password.core :as reset-password] - [utils.security.core :as security] - [status-im.ui.components.toolbar :as toolbar]) + [status-im.ui.components.icons.icons :as icons] + [status-im.ui.components.react :as react] + [status-im.ui.components.toolbar :as toolbar] + [utils.security.core :as security]) (:require-macros [status-im.utils.views :refer [defview letsubs]])) (defn input-field @@ -32,33 +32,43 @@ (i18n/label error) error))}]]) -(defview reset-password [] +(defview reset-password + [] (letsubs [{:keys [form-vals errors next-enabled? resetting?]} [:multiaccount/reset-password-form-vals-and-errors]] (let [{:keys [current-password - new-password]} form-vals - on-submit #(re-frame/dispatch [::reset-password/reset form-vals])] + new-password]} + form-vals + on-submit #(re-frame/dispatch [::reset-password/reset form-vals])] [react/keyboard-avoiding-view {:flex 1} - [react/view {:style {:flex 1 - :justify-content :space-between}} - [react/view {:style {:margin-top 32 - :padding-horizontal 16 - :padding-vertical 16}} - [input-field {:id :current-password - :errors errors - :on-sumbit on-submit - :disabled? false - :focus? true}] - [input-field {:id :new-password - :errors errors - :on-sumbit on-submit - :disabled? (zero? (count current-password))}] - [input-field {:id :confirm-new-password - :errors errors - :on-sumbit on-submit - :disabled? (zero? (count new-password))}]] - [quo/text {:color :secondary :align :center :size :small - :style {:padding-horizontal 16}} + [react/view + {:style {:flex 1 + :justify-content :space-between}} + [react/view + {:style {:margin-top 32 + :padding-horizontal 16 + :padding-vertical 16}} + [input-field + {:id :current-password + :errors errors + :on-sumbit on-submit + :disabled? false + :focus? true}] + [input-field + {:id :new-password + :errors errors + :on-sumbit on-submit + :disabled? (zero? (count current-password))}] + [input-field + {:id :confirm-new-password + :errors errors + :on-sumbit on-submit + :disabled? (zero? (count new-password))}]] + [quo/text + {:color :secondary + :align :center + :size :small + :style {:padding-horizontal 16}} (i18n/label :t/password-description)] [toolbar/toolbar {:show-border? true @@ -67,45 +77,53 @@ {:on-press on-submit :accessibility-label :next-button :disabled (or (not next-enabled?) - ;; disable on resetting? so the user cannot press the next button recklessly + ;; disable on resetting? so the user cannot press the next button + ;; recklessly ;; https://github.com/status-im/status-mobile/pull/12245#issuecomment-874827573 resetting?) :type :secondary :after :main-icons/next} (i18n/label :t/next)]}]]]))) -(defview reset-password-popover [] +(defview reset-password-popover + [] (letsubs [{:keys [resetting?]} [:multiaccount/reset-password-form-vals-and-errors]] - [react/view {:padding-vertical 24 - :padding-horizontal 48 - :align-items :center} - [react/view {:width 32 - :height 32 - :background-color (if resetting? - colors/gray-lighter - colors/green-transparent-10) - :border-radius 32 - :align-items :center - :justify-content :center} + [react/view + {:padding-vertical 24 + :padding-horizontal 48 + :align-items :center} + [react/view + {:width 32 + :height 32 + :background-color (if resetting? + colors/gray-lighter + colors/green-transparent-10) + :border-radius 32 + :align-items :center + :justify-content :center} (if resetting? - [react/activity-indicator {:size :small - :animating true}] + [react/activity-indicator + {:size :small + :animating true}] [icons/icon :main-icons/check {:color colors/green}])] - [quo/text {:size :x-large - :weight :bold - :align :center - :style {:typography :title-bold - :margin-top 16 - :margin-bottom 24}} + [quo/text + {:size :x-large + :weight :bold + :align :center + :style {:typography :title-bold + :margin-top 16 + :margin-bottom 24}} (i18n/label (if resetting? :t/password-reset-in-progress :t/password-reset-success))] (when-not resetting? - [quo/text {:align :center - :color :secondary - :style {:margin-bottom 24}} + [quo/text + {:align :center + :color :secondary + :style {:margin-bottom 24}} (i18n/label :t/password-reset-success-message)]) [react/view {:align-items :center} - [quo/button {:on-press #(re-frame/dispatch [:logout]) - :disabled resetting?} + [quo/button + {:on-press #(re-frame/dispatch [:logout]) + :disabled resetting?} (i18n/label :t/okay)]]])) diff --git a/src/status_im/ui/screens/rpc_usage_info.cljs b/src/status_im/ui/screens/rpc_usage_info.cljs index 6a28167820..fbd4c33490 100644 --- a/src/status_im/ui/screens/rpc_usage_info.cljs +++ b/src/status_im/ui/screens/rpc_usage_info.cljs @@ -83,13 +83,14 @@ [{:keys [db]} method-filter] {:db (assoc db :rpc-usage/filter method-filter)}) -(defn stats-table [{:keys [total filtered-total stats]}] +(defn stats-table + [{:keys [total filtered-total stats]}] [quo.react-native/scroll-view {:style {:padding-horizontal 8}} [quo.react-native/view {:style {:flex-direction :row :justify-content :space-between - :margin-bottom 2}} + :margin-bottom 2}} [quo.core/text {:style typography/font-semi-bold} (i18n/label :t/rpc-usage-total)] [quo.core/text {:style typography/font-semi-bold} @@ -108,18 +109,21 @@ v]] [quo.core/separator]]))]) -(defn prepare-stats [{:keys [stats]}] +(defn prepare-stats + [{:keys [stats]}] (string/join "\n" (map (fn [[k v]] (str k " " v)) stats))) -(defn usage-info-render [] +(defn usage-info-render + [] (let [stats @(re-frame/subscribe [:rpc-usage/data]) methods-filter @(re-frame/subscribe [:rpc-usage/filter])] - [react/view {:flex 1 - :margin-horizontal 8} + [react/view + {:flex 1 + :margin-horizontal 8} [quo.react-native/view {:style {:flex-direction :row :margin-vertical 8 @@ -146,12 +150,13 @@ :auto-focus false}] [stats-table stats]])) -(defn usage-info [] +(defn usage-info + [] (reagent/create-class {:component-did-mount (fn [] (reset! rpc-refresh-interval - (utils/set-interval #(re-frame/dispatch [::get-stats]) rpc-usage-refresh-interval-ms))) + (utils/set-interval #(re-frame/dispatch [::get-stats]) rpc-usage-refresh-interval-ms))) :component-will-unmount (fn [] (utils/clear-interval @rpc-refresh-interval) diff --git a/src/status_im/ui/screens/screens.cljs b/src/status_im/ui/screens/screens.cljs index 9c202c4db1..e8edea8a29 100644 --- a/src/status_im/ui/screens/screens.cljs +++ b/src/status_im/ui/screens/screens.cljs @@ -1,127 +1,122 @@ (ns status-im.ui.screens.screens - (:require [quo.design-system.colors :as colors] - [status-im.add-new.core :as new-chat.events] - [status-im.i18n.i18n :as i18n] - [status-im.ui.components.icons.icons :as icons] - [status-im.ui.screens.about-app.views :as about-app] - [status-im.ui.screens.add-new.new-chat.views :as new-chat] - [status-im.ui.screens.add-new.new-public-chat.view :as new-public-chat] - [status-im.ui.screens.advanced-settings.views :as advanced-settings] - [status-im.ui.screens.appearance.views :as appearance] - [status-im.ui.screens.bootnodes-settings.edit-bootnode.views - :as - edit-bootnode] - [status-im.ui.screens.bootnodes-settings.views :as bootnodes-settings] - [status-im.ui.screens.browser.bookmarks.views :as bookmarks] - [status-im.ui.screens.browser.empty-tab.views :as empty-tab] - [status-im.ui.screens.browser.tabs.views :as browser.tabs] - [status-im.ui.screens.browser.views :as browser] - [status-im.ui.screens.bug-report :as bug-report] - [status-im.ui.screens.chat.pinned-messages :as pin-messages] - [status-im.ui.screens.communities.channel-details :as communities.channel-details] - [status-im.ui.screens.communities.community :as community] - [status-im.ui.screens.communities.community-emoji-thumbnail-picker :as community-emoji-thumbnail-picker] - [status-im.ui.screens.communities.create :as communities.create] - [status-im.ui.screens.communities.create-category :as create-category] - [status-im.ui.screens.communities.create-channel :as create-channel] - [status-im.ui.screens.communities.edit :as community.edit] - [status-im.ui.screens.communities.edit-channel :as edit-channel] - [status-im.ui.screens.communities.import :as communities.import] - [status-im.ui.screens.communities.invite :as communities.invite] - [status-im.ui.screens.communities.members :as members] - [status-im.ui.screens.communities.membership :as membership] - [status-im.ui.screens.communities.profile :as community.profile] - [status-im.ui.screens.communities.reorder-categories :as reorder-categories] - [status-im.ui.screens.communities.requests-to-join :as requests-to-join] - [status-im.ui.screens.communities.select-category :as select-category] - [status-im.ui.screens.communities.views :as communities] - [status-im.ui.screens.contacts-list.views :as contacts-list] - [status-im.ui.screens.currency-settings.views :as currency-settings] - [status-im.ui.screens.dapps-permissions.views :as dapps-permissions] - [status-im.ui.screens.default-sync-period-settings.view :as default-sync-period-settings] - [status-im.ui.screens.wallet.settings.views :as wallet-settings] - [status-im.ui.screens.wallet.transactions.views :as wallet-transactions] - [status-im.ui.screens.wallet.custom-tokens.views :as custom-tokens] - [status-im.ui.screens.wallet.accounts.views :as wallet.accounts] - [status-im.ui.screens.wallet.collectibles.views :as wallet.collectibles] - [status-im.ui.screens.wallet.account.views :as wallet.account] - [status-im.ui.screens.wallet.add-new.views :as add-account] - [status-im.ui.screens.wallet.account-settings.views :as account-settings] - [status-im.ui.screens.wallet.swap.views :as wallet.swap] - [status-im.ui.screens.status.views :as status.views] - [status-im.ui.screens.profile.user.views :as profile.user] - [status-im.ui.screens.ens.views :as ens] - [status-im.ui.screens.fleet-settings.views :as fleet-settings] - [status-im.ui.screens.glossary.view :as glossary] - [status-im.ui.screens.group.views :as group-chat] - [status-im.ui.screens.help-center.views :as help-center] - [status-im.ui.screens.keycard.authentication-method.views :as keycard.authentication] - [status-im.ui.screens.keycard.onboarding.views :as keycard.onboarding] - [status-im.ui.screens.keycard.pairing.views :as keycard.pairing] - [status-im.ui.screens.keycard.pin.views :as keycard.pin] - [status-im.ui.screens.keycard.recovery.views :as keycard.recovery] - [status-im.ui.screens.keycard.settings.views :as keycard.settings] - [status-im.ui.screens.keycard.views :as keycard] - [status-im.ui.screens.link-previews-settings.views :as link-previews-settings] - [status-im.ui.screens.log-level-settings.views :as log-level-settings] - [status-im.ui.screens.mobile-network-settings.view - :as - mobile-network-settings] - [status-im.ui.screens.multiaccounts.key-storage.views :as key-storage.views] - [status-im.ui.screens.multiaccounts.login.views :as login] - [status-im.ui.screens.multiaccounts.views :as multiaccounts] - [status-im.ui.screens.network-info.views :as network-info] - [status-im.ui.screens.network.edit-network.views :as edit-network] - [status-im.ui.screens.network.network-details.views :as network-details] - [status-im.ui.screens.network.views :as network] - [status-im.ui.screens.notifications-settings.views :as notifications-settings] - [status-im.ui.screens.offline-messaging-settings.edit-mailserver.views - :as - edit-mailserver] - [status-im.ui.screens.offline-messaging-settings.views - :as - offline-messaging-settings] - [status-im.ui.screens.onboarding.intro.views :as onboarding.intro] - [status-im.ui.screens.onboarding.keys.views :as onboarding.keys] - [status-im.ui.screens.onboarding.notifications.views :as onboarding.notifications] - [status-im.ui.screens.onboarding.password.views :as onboarding.password] - [status-im.ui.screens.onboarding.phrase.view :as onboarding.phrase] - [status-im.ui.screens.onboarding.storage.views :as onboarding.storage] - [status-im.ui.screens.onboarding.welcome.views :as onboarding.welcome] - [status-im.ui.screens.pairing.views :as pairing] - [status-im.ui.screens.privacy-and-security-settings.delete-profile :as delete-profile] - [status-im.ui.screens.privacy-and-security-settings.messages-from-contacts-only :as messages-from-contacts-only] - [status-im.ui.screens.privacy-and-security-settings.views :as privacy-and-security] - [status-im.ui.screens.profile.contact.views :as contact] - [status-im.ui.screens.profile.group-chat.views :as profile.group-chat] - [status-im.ui.screens.profile.seed.views :as profile.seed] - [status-im.ui.screens.progress.views :as progress] - [status-im.ui.screens.qr-scanner.views :as qr-scanner] - [status-im.ui.screens.backup-settings.view :as backup-settings] - [status-im.ui.screens.reset-password.views :as reset-password] - [status-im.ui.screens.rpc-usage-info :as rpc-usage-info] - [status-im.ui.screens.peers-stats :as peers-stats] - [status-im.ui.screens.status.new.views :as status.new] - [status-im.ui.screens.stickers.views :as stickers] - [status-im.ui.screens.sync-settings.views :as sync-settings] - [status-im.ui.screens.terms-of-service.views :as terms-of-service] - [status-im.ui.screens.wakuv2-settings.edit-node.views - :as - edit-wakuv2-node] - [status-im.ui.screens.wakuv2-settings.views :as wakuv2-settings] - [status-im.ui.screens.wallet.accounts-manage.views :as accounts-manage] - [status-im.ui.screens.wallet.buy-crypto.views :as wallet.buy-crypto] - [status-im.ui.screens.wallet.recipient.views :as recipient] - [status-im.ui.screens.wallet.send.views :as wallet.send] - [status-im.ui.screens.wallet.manage-connections.views :as manage-all-connections] - [status-im.ui2.screens.chat.group-details.view :as group-details] - [status-im.ui2.screens.chat.photo-selector.view :as photo-selector])) + (:require + [quo.design-system.colors :as colors] + [status-im.add-new.core :as new-chat.events] + [status-im.i18n.i18n :as i18n] + [status-im.ui.components.icons.icons :as icons] + [status-im.ui.screens.about-app.views :as about-app] + [status-im.ui.screens.add-new.new-chat.views :as new-chat] + [status-im.ui.screens.add-new.new-public-chat.view :as new-public-chat] + [status-im.ui.screens.advanced-settings.views :as advanced-settings] + [status-im.ui.screens.appearance.views :as appearance] + [status-im.ui.screens.backup-settings.view :as backup-settings] + [status-im.ui.screens.bootnodes-settings.edit-bootnode.views :as edit-bootnode] + [status-im.ui.screens.bootnodes-settings.views :as bootnodes-settings] + [status-im.ui.screens.browser.bookmarks.views :as bookmarks] + [status-im.ui.screens.browser.empty-tab.views :as empty-tab] + [status-im.ui.screens.browser.tabs.views :as browser.tabs] + [status-im.ui.screens.browser.views :as browser] + [status-im.ui.screens.bug-report :as bug-report] + [status-im.ui.screens.chat.pinned-messages :as pin-messages] + [status-im.ui.screens.communities.channel-details :as communities.channel-details] + [status-im.ui.screens.communities.community :as community] + [status-im.ui.screens.communities.community-emoji-thumbnail-picker :as + community-emoji-thumbnail-picker] + [status-im.ui.screens.communities.create :as communities.create] + [status-im.ui.screens.communities.create-category :as create-category] + [status-im.ui.screens.communities.create-channel :as create-channel] + [status-im.ui.screens.communities.edit :as community.edit] + [status-im.ui.screens.communities.edit-channel :as edit-channel] + [status-im.ui.screens.communities.import :as communities.import] + [status-im.ui.screens.communities.invite :as communities.invite] + [status-im.ui.screens.communities.members :as members] + [status-im.ui.screens.communities.membership :as membership] + [status-im.ui.screens.communities.profile :as community.profile] + [status-im.ui.screens.communities.reorder-categories :as reorder-categories] + [status-im.ui.screens.communities.requests-to-join :as requests-to-join] + [status-im.ui.screens.communities.select-category :as select-category] + [status-im.ui.screens.communities.views :as communities] + [status-im.ui.screens.contacts-list.views :as contacts-list] + [status-im.ui.screens.currency-settings.views :as currency-settings] + [status-im.ui.screens.dapps-permissions.views :as dapps-permissions] + [status-im.ui.screens.default-sync-period-settings.view :as default-sync-period-settings] + [status-im.ui.screens.ens.views :as ens] + [status-im.ui.screens.fleet-settings.views :as fleet-settings] + [status-im.ui.screens.glossary.view :as glossary] + [status-im.ui.screens.group.views :as group-chat] + [status-im.ui.screens.help-center.views :as help-center] + [status-im.ui.screens.keycard.authentication-method.views :as keycard.authentication] + [status-im.ui.screens.keycard.onboarding.views :as keycard.onboarding] + [status-im.ui.screens.keycard.pairing.views :as keycard.pairing] + [status-im.ui.screens.keycard.pin.views :as keycard.pin] + [status-im.ui.screens.keycard.recovery.views :as keycard.recovery] + [status-im.ui.screens.keycard.settings.views :as keycard.settings] + [status-im.ui.screens.keycard.views :as keycard] + [status-im.ui.screens.link-previews-settings.views :as link-previews-settings] + [status-im.ui.screens.log-level-settings.views :as log-level-settings] + [status-im.ui.screens.mobile-network-settings.view :as mobile-network-settings] + [status-im.ui.screens.multiaccounts.key-storage.views :as key-storage.views] + [status-im.ui.screens.multiaccounts.login.views :as login] + [status-im.ui.screens.multiaccounts.views :as multiaccounts] + [status-im.ui.screens.network-info.views :as network-info] + [status-im.ui.screens.network.edit-network.views :as edit-network] + [status-im.ui.screens.network.network-details.views :as network-details] + [status-im.ui.screens.network.views :as network] + [status-im.ui.screens.notifications-settings.views :as notifications-settings] + [status-im.ui.screens.offline-messaging-settings.edit-mailserver.views :as edit-mailserver] + [status-im.ui.screens.offline-messaging-settings.views :as offline-messaging-settings] + [status-im.ui.screens.onboarding.intro.views :as onboarding.intro] + [status-im.ui.screens.onboarding.keys.views :as onboarding.keys] + [status-im.ui.screens.onboarding.notifications.views :as onboarding.notifications] + [status-im.ui.screens.onboarding.password.views :as onboarding.password] + [status-im.ui.screens.onboarding.phrase.view :as onboarding.phrase] + [status-im.ui.screens.onboarding.storage.views :as onboarding.storage] + [status-im.ui.screens.onboarding.welcome.views :as onboarding.welcome] + [status-im.ui.screens.pairing.views :as pairing] + [status-im.ui.screens.peers-stats :as peers-stats] + [status-im.ui.screens.privacy-and-security-settings.delete-profile :as delete-profile] + [status-im.ui.screens.privacy-and-security-settings.messages-from-contacts-only :as + messages-from-contacts-only] + [status-im.ui.screens.privacy-and-security-settings.views :as privacy-and-security] + [status-im.ui.screens.profile.contact.views :as contact] + [status-im.ui.screens.profile.group-chat.views :as profile.group-chat] + [status-im.ui.screens.profile.seed.views :as profile.seed] + [status-im.ui.screens.profile.user.views :as profile.user] + [status-im.ui.screens.progress.views :as progress] + [status-im.ui.screens.qr-scanner.views :as qr-scanner] + [status-im.ui.screens.reset-password.views :as reset-password] + [status-im.ui.screens.rpc-usage-info :as rpc-usage-info] + [status-im.ui.screens.status.new.views :as status.new] + [status-im.ui.screens.status.views :as status.views] + [status-im.ui.screens.stickers.views :as stickers] + [status-im.ui.screens.sync-settings.views :as sync-settings] + [status-im.ui.screens.terms-of-service.views :as terms-of-service] + [status-im.ui.screens.wakuv2-settings.edit-node.views :as edit-wakuv2-node] + [status-im.ui.screens.wakuv2-settings.views :as wakuv2-settings] + [status-im.ui.screens.wallet.account-settings.views :as account-settings] + [status-im.ui.screens.wallet.account.views :as wallet.account] + [status-im.ui.screens.wallet.accounts-manage.views :as accounts-manage] + [status-im.ui.screens.wallet.accounts.views :as wallet.accounts] + [status-im.ui.screens.wallet.add-new.views :as add-account] + [status-im.ui.screens.wallet.buy-crypto.views :as wallet.buy-crypto] + [status-im.ui.screens.wallet.collectibles.views :as wallet.collectibles] + [status-im.ui.screens.wallet.custom-tokens.views :as custom-tokens] + [status-im.ui.screens.wallet.manage-connections.views :as manage-all-connections] + [status-im.ui.screens.wallet.recipient.views :as recipient] + [status-im.ui.screens.wallet.send.views :as wallet.send] + [status-im.ui.screens.wallet.settings.views :as wallet-settings] + [status-im.ui.screens.wallet.swap.views :as wallet.swap] + [status-im.ui.screens.wallet.transactions.views :as wallet-transactions] + [status-im.ui2.screens.chat.group-details.view :as group-details] + [status-im.ui2.screens.chat.photo-selector.view :as photo-selector])) -(defn right-button-options [id icon] +(defn right-button-options + [id icon] {:id id :icon (icons/icon-source icon)}) -(defn screens [] +(defn screens + [] [;;INTRO, ONBOARDING, LOGIN ;Multiaccounts @@ -132,7 +127,7 @@ :right-handler multiaccounts/topbar-button :component multiaccounts/multiaccounts} - ;Login + ;Login {:name :login :insets {:bottom true} :options {:topBar {:rightButtons (right-button-options :login :more)}} @@ -142,17 +137,17 @@ {:name :progress :component progress/progress} - ;[Onboarding] + ;[Onboarding] {:name :intro :insets {:bottom true} :component onboarding.intro/intro} - ;[Onboarding] + ;[Onboarding] {:name :get-your-keys :insets {:bottom true} :component onboarding.keys/get-your-keys} - ;[Onboarding] + ;[Onboarding] {:name :choose-name :options {:topBar {:visible false} :popGesture false @@ -161,7 +156,7 @@ :insets {:bottom true} :component onboarding.keys/choose-a-chat-name} - ;[Onboarding] + ;[Onboarding] {:name :select-key-storage :insets {:bottom true} :options {:popGesture false @@ -169,7 +164,7 @@ :popStackOnPress false}} :component onboarding.storage/select-key-storage} - ;[Onboarding] Create Password + ;[Onboarding] Create Password {:name :create-password :options {:popGesture false :hardwareBackButton {:dismissModalOnPress false @@ -177,7 +172,7 @@ :insets {:bottom true} :component onboarding.password/screen} - ;[Onboarding] Welcome + ;[Onboarding] Welcome {:name :welcome :options {:popGesture false :hardwareBackButton {:dismissModalOnPress false @@ -185,7 +180,7 @@ :insets {:bottom true} :component onboarding.welcome/welcome} - ;[Onboarding] Notification + ;[Onboarding] Notification {:name :onboarding-notification :options {:popGesture false :hardwareBackButton {:dismissModalOnPress false @@ -193,7 +188,7 @@ :insets {:bottom true} :component onboarding.notifications/notifications-onboarding} - ;[Onboarding] Recovery + ;[Onboarding] Recovery {:name :recover-multiaccount-enter-phrase :insets {:bottom true} :component onboarding.phrase/enter-phrase} @@ -205,9 +200,9 @@ :component onboarding.phrase/wizard-recovery-success} ;;CHAT - ;Pinned messages + ;Pinned messages {:name :chat-pinned-messages - ;TODO custom subtitle + ;TODO custom subtitle :options {:topBar {:visible false}} :component pin-messages/pinned-messages} @@ -567,38 +562,38 @@ ;;MODALS - ;[Chat] New Chat + ;[Chat] New Chat {:name :new-chat :on-focus [::new-chat.events/new-chat-focus] ;;TODO accessories :options {:topBar {:visible false}} :component new-chat/new-chat} - ;[Chat] New Public chat + ;[Chat] New Public chat {:name :new-public-chat :insets {:bottom true} :options {:topBar {:title {:text (i18n/label :t/new-public-group-chat)}}} :component new-public-chat/new-public-chat} - ;[Chat] Link preview settings + ;[Chat] Link preview settings {:name :link-preview-settings :options {:topBar {:title {:text (i18n/label :t/chat-link-previews)}}} :component link-previews-settings/link-previews-settings} - ;[Chat] Edit nickname + ;[Chat] Edit nickname {:name :nickname :insets {:bottom true} ;;TODO dyn subtitle :options {:topBar {:visible false}} :component contact/nickname} - ;[Group chat] Edit group chat name + ;[Group chat] Edit group chat name {:name :edit-group-chat-name :insets {:bottom true} :options {:topBar {:title {:text (i18n/label :t/edit-group)}}} :component group-chat/edit-group-chat-name} - ;[Group chat] Add participants + ;[Group chat] Add participants {:name :add-participants-toggle-list :on-focus [:group/add-participants-toggle-list] :insets {:bottom true} @@ -606,34 +601,34 @@ :options {:topBar {:visible false}} :component group-chat/add-participants-toggle-list} - ;[Communities] Invite people + ;[Communities] Invite people {:name :invite-people-community ;;TODO dyn title :options {:topBar {:visible false}} :component communities.invite/invite :insets {:bottom true}} - ;New Contact + ;New Contact {:name :new-contact :on-focus [::new-chat.events/new-chat-focus] ;;TODO accessories :options {:topBar {:visible false}} :component new-chat/new-contact} - ;[Wallet] Recipient + ;[Wallet] Recipient {:name :recipient :insets {:bottom true} ;;TODO accessories :options {:topBar {:visible false}} :component recipient/recipient} - ;[Wallet] New favourite + ;[Wallet] New favourite {:name :new-favourite :insets {:bottom true} :options {:topBar {:title {:text (i18n/label :t/new-favourite)}}} :component recipient/new-favourite} - ;QR Scanner + ;QR Scanner {:name :qr-scanner :insets {:top false :bottom false} ;;TODO custom topbar @@ -644,7 +639,7 @@ :component qr-scanner/qr-scanner} ;;TODO WHY MODAL? - ;[Profile] Notifications settings + ;[Profile] Notifications settings {:name :notifications-settings :options {:topBar {:title {:text (i18n/label :t/notification-settings)}} :popGesture false @@ -654,7 +649,7 @@ :component notifications-settings/notifications-settings} ;;TODO WHY MODAL? - ;[Profile] Notifications Advanced settings + ;[Profile] Notifications Advanced settings {:name :notifications-advanced-settings :options {:topBar {:title {:text (i18n/label :t/notification-settings)}} :popGesture false @@ -663,7 +658,7 @@ :insets {:bottom true} :component notifications-settings/notifications-advanced-settings} - ;[Wallet] Prepare Transaction + ;[Wallet] Prepare Transaction {:name :prepare-send-transaction :insets {:bottom true} :on-dissmiss [:wallet/cancel-transaction-command] @@ -672,7 +667,7 @@ :hardwareBackButton {:dismissModalOnPress false}} :component wallet.send/prepare-send-transaction} - ;[Wallet] Request Transaction + ;[Wallet] Request Transaction {:name :request-transaction :insets {:bottom true} :on-dissmiss [:wallet/cancel-transaction-command] @@ -681,12 +676,12 @@ :hardwareBackButton {:dismissModalOnPress false}} :component wallet.send/request-transaction} - ;[Wallet] Buy crypto + ;[Wallet] Buy crypto {:name :buy-crypto :insets {:bottom true} :component wallet.buy-crypto/container} - ;[Wallet] Buy crypto website + ;[Wallet] Buy crypto website {:name :buy-crypto-website :insets {:bottom true} ;;TODO subtitle @@ -699,34 +694,34 @@ :options {:topBar {:visible false}} :component wallet.collectibles/nft-details-modal} - ;My Status + ;My Status {:name :my-status :insets {:bottom true} :options {:topBar {:title {:text (i18n/label :t/my-status)}}} :component status.new/my-status} - ;[Browser] New bookmark + ;[Browser] New bookmark {:name :new-bookmark :insets {:bottom true} ;;TODO dynamic title :options {:topBar {:visible false}} :component bookmarks/new-bookmark} - ;Profile + ;Profile {:name :profile :insets {:bottom true} ;;TODO custom toolbar :options {:topBar {:visible false}} :component contact/profile} - ;KEYCARD - {:name :keycard-onboarding-intro - :insets {:bottom true} - :options {:topBar {:visible false} - :popGesture false - :hardwareBackButton {:dismissModalOnPress false - :popStackOnPress false}} - :component keycard.onboarding/intro} + ;KEYCARD + {:name :keycard-onboarding-intro + :insets {:bottom true} + :options {:topBar {:visible false} + :popGesture false + :hardwareBackButton {:dismissModalOnPress false + :popStackOnPress false}} + :component keycard.onboarding/intro} {:name :keycard-onboarding-puk-code :insets {:bottom true} :options {:topBar {:visible false} diff --git a/src/status_im/ui/screens/signing/sheets.cljs b/src/status_im/ui/screens/signing/sheets.cljs index 0c90e1b0dc..0ffea155c5 100644 --- a/src/status_im/ui/screens/signing/sheets.cljs +++ b/src/status_im/ui/screens/signing/sheets.cljs @@ -1,24 +1,26 @@ (ns status-im.ui.screens.signing.sheets (:require-macros [status-im.utils.views :as views]) - (:require [status-im.ui.components.react :as react] - [re-frame.core :as re-frame] - [status-im.i18n.i18n :as i18n] + (:require [clojure.string :as string] [quo.core :as quo] [quo.design-system.colors :as colors] - [status-im.utils.money :as money] - [status-im.ui.components.icons.icons :as icons] - [clojure.string :as string] + [re-frame.core :as re-frame] + [reagent.core :as reagent] + [status-im.i18n.i18n :as i18n] [status-im.signing.gas :as gas] - [reagent.core :as reagent])) + [status-im.ui.components.icons.icons :as icons] + [status-im.ui.components.react :as react] + [status-im.utils.money :as money])) -(views/defview fee-bottom-sheet [fee-display-symbol] +(views/defview fee-bottom-sheet + [fee-display-symbol] (views/letsubs [{gas-edit :gas gas-price-edit :gasPrice max-fee :max-fee} [:signing/edit-fee]] [react/view [react/view {:style {:margin-horizontal 16 :margin-top 8}} [react/text {:style {:typography :title-bold}} (i18n/label :t/network-fee)] - [react/view {:style {:flex-direction :row - :margin-top 8 - :align-items :flex-end}} + [react/view + {:style {:flex-direction :row + :margin-top 8 + :align-items :flex-end}} [react/view {:flex 1} [quo/text-input {:on-change-text #(re-frame/dispatch [:signing.edit-fee.ui/edit-value :gas %]) @@ -31,8 +33,9 @@ :placeholder "0" :show-cancel false :auto-focus false}]] - [react/view {:flex 1 - :padding-left 16} + [react/view + {:flex 1 + :padding-left 16} [quo/text-input {:label (i18n/label :t/gas-price) :on-change-text #(re-frame/dispatch [:signing.edit-fee.ui/edit-value :gasPrice %]) @@ -44,8 +47,9 @@ :placeholder "0.000" :show-cancel false :auto-focus false}]] - [react/view {:padding-left 8 - :padding-bottom 12} + [react/view + {:padding-left 8 + :padding-bottom 12} [react/text (i18n/label :t/gwei)]]] [react/view {:margin-vertical 16 :align-items :center} @@ -55,27 +59,34 @@ max-fee " " [{:style {:color colors/gray}} fee-display-symbol]]]] [react/view {:height 1 :background-color colors/gray-lighter}] - [react/view {:margin-horizontal 16 :align-items :center :justify-content :space-between :flex-direction :row :margin-top 6} + [react/view + {:margin-horizontal 16 + :align-items :center + :justify-content :space-between + :flex-direction :row + :margin-top 6} [quo/button {:type :secondary :on-press #(re-frame/dispatch [:bottom-sheet/hide])} (i18n/label :t/cancel)] [quo/button - {:type :secondary - :on-press #(re-frame/dispatch [:signing.edit-fee.ui/submit]) - :disabled (or (:error gas-edit) (:error gas-price-edit))} + {:type :secondary + :on-press #(re-frame/dispatch [:signing.edit-fee.ui/submit]) + :disabled (or (:error gas-edit) (:error gas-price-edit))} (i18n/label :t/update)]]])) (declare fee-bottom-sheet-eip1559) -(defn fee-bottom-sheet-eip1559-custom [_ #_fee-display-symbol] +(defn fee-bottom-sheet-eip1559-custom + [_ #_fee-display-symbol] (let [{gas-edit :gas max-fee-per-gas-edit :maxFeePerGas max-priority-fee-per-gas-edit :maxPriorityFeePerGas} @(re-frame/subscribe [:signing/edit-fee]) - error? (some :error [gas-edit - max-fee-per-gas-edit - max-priority-fee-per-gas-edit]) + error? (some :error + [gas-edit + max-fee-per-gas-edit + max-priority-fee-per-gas-edit]) base-fee @(re-frame/subscribe [:wallet/current-base-fee]) [fee-currency fiat-currency price] @(re-frame/subscribe [:signing/currencies]) @@ -90,8 +101,9 @@ [:<> [react/view {:style {:margin-horizontal 16 :margin-top 8}} [react/text {:style {:typography :title-bold}} (i18n/label :t/max-priority-fee)] - [react/text {:style {:color (colors/get-color :text-02) - :margin-top 12}} + [react/text + {:style {:color (colors/get-color :text-02) + :margin-top 12}} (i18n/label :t/miners-higher-fee)] [react/view {:style {:margin-top 12 @@ -103,10 +115,11 @@ (money/to-fixed (money/wei-> :gwei base-fee)) " " (i18n/label :t/gwei)]]] - [react/view {:margin-vertical 12 - :margin-horizontal 16 - :height 1 - :background-color colors/gray-lighter}] + [react/view + {:margin-vertical 12 + :margin-horizontal 16 + :height 1 + :background-color colors/gray-lighter}] [react/view {:margin-horizontal 16 :margin-top 4 @@ -128,7 +141,8 @@ :error (or (:error max-priority-fee-per-gas-edit) (get-in max-priority-fee-per-gas-edit [:fee-error :label])) :default-value (str (:value max-priority-fee-per-gas-edit)) - :on-change-text #(re-frame/dispatch [:signing.edit-fee.ui/edit-value :maxPriorityFeePerGas %]) + :on-change-text #(re-frame/dispatch [:signing.edit-fee.ui/edit-value :maxPriorityFeePerGas + %]) :show-cancel false :after {:component [quo/text {:style {:color (colors/get-color :text-02)}} @@ -148,9 +162,10 @@ :after {:component [quo/text {:style {:color (colors/get-color :text-02)}} (i18n/label :t/gwei)]}}]] - [react/view {:margin-vertical 12 - :height 1 - :background-color colors/gray-lighter}] + [react/view + {:margin-vertical 12 + :height 1 + :background-color colors/gray-lighter}] [react/view {:style {:margin-top 4 :margin-horizontal 16 @@ -170,8 +185,9 @@ (money/to-fixed (money/mul fee-eth (money/bignumber (or price 0))) 2) " " fiat-currency]]] - [react/text {:style {:color (colors/get-color :text-02) - :margin 16}} + [react/text + {:style {:color (colors/get-color :text-02) + :margin 16}} (i18n/label :t/fee-explanation)] [react/view {:style {:margin-left 12 @@ -181,7 +197,7 @@ :align-items :center :justify-content :space-between}} [quo/button - {:type :secondary + {:type :secondary :accessibility-label :see-fee-suggestions ;;:on-press #_(re-frame/dispatch @@ -198,19 +214,21 @@ :theme :accent} (i18n/label :t/save)]]])) -(defn fee-bottom-sheet-eip1559 [fee-display-symbol] +(defn fee-bottom-sheet-eip1559 + [fee-display-symbol] (let [{priority-fee-edit :maxPriorityFeePerGas - option :selected-fee-option - fee-edit :maxFeePerGas} + option :selected-fee-option + fee-edit :maxFeePerGas} @(re-frame/subscribe [:signing/edit-fee]) {:keys [normal fast slow]} @(re-frame/subscribe [:signing/priority-fee-suggestions-range])] [react/view [react/view {:style {:margin-horizontal 16 :margin-top 8}} [react/text {:style {:typography :title-bold}} (i18n/label :t/fee-options)] - [react/text {:style {:margin-top 12} - :accessibility-label :slow-fee - :on-press #(re-frame/dispatch [:signing.edit-fee.ui/set-option :slow])} + [react/text + {:style {:margin-top 12} + :accessibility-label :slow-fee + :on-press #(re-frame/dispatch [:signing.edit-fee.ui/set-option :slow])} (string/join " " [(str (i18n/label :t/slow) ":") @@ -218,9 +236,10 @@ (str (:tip slow) " gwei") (when (= :slow option) "<- selected")])] - [react/text {:style {:margin-top 12} - :accessibility-label :normal-fee - :on-press #(re-frame/dispatch [:signing.edit-fee.ui/set-option :normal])} + [react/text + {:style {:margin-top 12} + :accessibility-label :normal-fee + :on-press #(re-frame/dispatch [:signing.edit-fee.ui/set-option :normal])} (string/join " " [(str (i18n/label :t/normal) ":") @@ -229,9 +248,10 @@ (when (or (nil? option) (= :normal option)) "<- selected")])] - [react/text {:style {:margin-top 12} - :accessibility-label :fast-fee - :on-press #(re-frame/dispatch [:signing.edit-fee.ui/set-option :fast])} + [react/text + {:style {:margin-top 12} + :accessibility-label :fast-fee + :on-press #(re-frame/dispatch [:signing.edit-fee.ui/set-option :fast])} (string/join " " [(str (i18n/label :t/fast) ":") @@ -246,40 +266,44 @@ [(str (i18n/label :t/custom) ":") (str (-> fee-edit :value-number - (money/to-fixed 2)) " gwei") + (money/to-fixed 2)) + " gwei") (str (-> priority-fee-edit :value-number - (money/to-fixed 2)) " gwei") + (money/to-fixed 2)) + " gwei") (when (= :custom option) "<- selected")])])] [react/view - {:style {:margin-left 12 - :margin-right 16 - :margin-top 38} - :flex-direction :row - :align-items :center - :justify-content :space-between} + {:style {:margin-left 12 + :margin-right 16 + :margin-top 38} + :flex-direction :row + :align-items :center + :justify-content :space-between} [quo/button - {:type :secondary + {:type :secondary :accessibility-label :set-custom-fee - :on-press #(re-frame/dispatch - [:bottom-sheet/show-sheet - {:content (fn [] - [fee-bottom-sheet-eip1559-custom fee-display-symbol]) - :content-height 270}])} + :on-press #(re-frame/dispatch + [:bottom-sheet/show-sheet + {:content (fn [] + [fee-bottom-sheet-eip1559-custom fee-display-symbol]) + :content-height 270}])} (i18n/label :t/set-custom-fee)] [quo/button - {:type :primary + {:type :primary :accessibility-label :save-custom-fee - :theme :accent - :on-press #(re-frame/dispatch [:signing.edit-fee.ui/submit])} + :theme :accent + :on-press #(re-frame/dispatch [:signing.edit-fee.ui/submit])} (i18n/label :t/save)]]])) -(defn gwei [val] +(defn gwei + [val] (str (money/to-fixed val 2) " " (i18n/label :t/gwei))) -(defn fees-warning [] - (let [base-fee @(re-frame/subscribe [:wallet/current-base-fee]) +(defn fees-warning + [] + (let [base-fee @(re-frame/subscribe [:wallet/current-base-fee]) base-fee-gwei (money/wei-> :gwei (money/bignumber base-fee)) priority-fee @(re-frame/subscribe [:wallet/current-priority-fee]) priority-fee-gwei (money/wei-> :gwei (money/bignumber priority-fee)) @@ -287,24 +311,28 @@ fee-edit :maxFeePerGas} @(re-frame/subscribe [:signing/edit-fee])] [react/view - [react/view {:margin-top 24 - :margin-horizontal 24 - :margin-bottom 32 - :align-items :center} - [react/view {:background-color colors/blue-light - :width 32 - :height 32 - :border-radius 16 - :align-items :center - :justify-content :center} + [react/view + {:margin-top 24 + :margin-horizontal 24 + :margin-bottom 32 + :align-items :center} + [react/view + {:background-color colors/blue-light + :width 32 + :height 32 + :border-radius 16 + :align-items :center + :justify-content :center} [icons/icon :main-icons/warning {:color colors/black}]] - [react/text {:style {:typography :title-bold - :margin-top 16 - :margin-bottom 8}} + [react/text + {:style {:typography :title-bold + :margin-top 16 + :margin-bottom 8}} (i18n/label :t/are-you-sure)] - [react/text {:style {:color colors/gray - :text-align :center - :margin-horizontal 24}} + [react/text + {:style {:color colors/gray + :text-align :center + :margin-horizontal 24}} (i18n/label :t/bad-fees-description)]] [react/view {:style {:flex-direction :row @@ -326,9 +354,10 @@ :margin-horizontal 32}} [react/text (i18n/label :t/current-average-tip)] [react/text (gwei (money/to-fixed priority-fee-gwei 2))]] - [react/view {:margin-vertical 16 - :height 1 - :background-color colors/gray-lighter}] + [react/view + {:margin-vertical 16 + :height 1 + :background-color colors/gray-lighter}] [react/view {:style {:flex-direction :row :justify-content :space-between @@ -346,11 +375,12 @@ (i18n/label :t/your-price-limit)] [react/text {:style {:color (colors/get-color :negative-01)}} (gwei (:value-number fee-edit))]] - [react/view {:style - {:background-color colors/gray-lighter - :padding-horizontal 32 - :padding-vertical 16 - :margin-vertical 16}} + [react/view + {:style + {:background-color colors/gray-lighter + :padding-horizontal 32 + :padding-vertical 16 + :margin-vertical 16}} [react/view {:style {:flex-direction :row :justify-content :space-between}} @@ -366,7 +396,7 @@ :justify-content :center :margin-top 8}} [quo/button - {:type :primary + {:type :primary :on-press #(re-frame/dispatch [:hide-popover])} (i18n/label :t/change-tip)]] [react/view @@ -375,13 +405,14 @@ :margin-top 8 :margin-bottom 16}} [quo/button - {:type :secondary + {:type :secondary :on-press #(do (re-frame/dispatch [:hide-popover]) (re-frame/dispatch [:signing.edit-fee.ui/submit true]))} (i18n/label :t/continue-anyway)]]])) -(defn advanced [] - (let [nonce (reagent/atom nil) +(defn advanced + [] + (let [nonce (reagent/atom nil) default-nonce (:nonce @(re-frame/subscribe [:signing/tx]))] (fn [] [react/view {:padding 20} @@ -393,7 +424,7 @@ :on-change-text #(reset! nonce %) :show-cancel false :auto-focus true - :container-style {:margin-bottom 20}}] + :container-style {:margin-bottom 20}}] [react/view {:align-items :flex-end} [quo/button {:type :primary diff --git a/src/status_im/ui/screens/signing/styles.cljs b/src/status_im/ui/screens/signing/styles.cljs index dc4642bc56..368f95b3d7 100644 --- a/src/status_im/ui/screens/signing/styles.cljs +++ b/src/status_im/ui/screens/signing/styles.cljs @@ -18,7 +18,8 @@ :padding-right 24 :margin-bottom 19}) -(defn message [] +(defn message + [] {:background-color colors/white :border-top-right-radius 16 :border-top-left-radius 16 @@ -34,13 +35,15 @@ :border-width 1 :padding 8}) -(defn sheet [] +(defn sheet + [] {:background-color colors/white :border-top-right-radius 16 :border-top-left-radius 16 :padding-bottom 40}) -(defn sign-with-keycard-button [disabled?] +(defn sign-with-keycard-button + [disabled?] {:background-color (if (colors/dark?) "#27D8B9" colors/black-light) :padding-top 2 :border-radius 8 @@ -52,7 +55,8 @@ :opacity (if disabled? 0.1 1) :padding-horizontal 12}) -(defn sign-with-keycard-button-text [disabled?] +(defn sign-with-keycard-button-text + [disabled?] {:padding-right 2 :padding-left 16 :color (if disabled? colors/black colors/white-persist) diff --git a/src/status_im/ui/screens/signing/views.cljs b/src/status_im/ui/screens/signing/views.cljs index df81166b3b..532f76fd10 100644 --- a/src/status_im/ui/screens/signing/views.cljs +++ b/src/status_im/ui/screens/signing/views.cljs @@ -23,20 +23,23 @@ [status-im.ui.screens.signing.styles :as styles] [status-im.ui.screens.wallet.components.views :as wallet.components] [status-im.utils.platform :as platform] - [utils.security.core :as security] [status-im.utils.types :as types] [status-im.utils.utils :as utils] - [status-im.wallet.utils :as wallet.utils])) + [status-im.wallet.utils :as wallet.utils] + [utils.security.core :as security])) -(defn separator [] +(defn separator + [] [react/view {:height 1 :background-color colors/gray-lighter}]) -(defn displayed-name [contact] +(defn displayed-name + [contact] (if (or (:preferred-name contact) (:name contact)) (multiaccounts/displayed-name contact) (utils/get-shortened-checksum-address (:address contact)))) -(defn contact-item [title contact] +(defn contact-item + [title contact] [copyable-text/copyable-text-view {:copied-text (:address contact)} [quo/list-item @@ -50,21 +53,23 @@ :monospace true} (displayed-name contact)]}]]) -(defn token-item [{:keys [icon color] :as token} display-symbol] +(defn token-item + [{:keys [icon color] :as token} display-symbol] (when token [react/view [quo/list-item {:size :small :title (i18n/label :t/wallet-asset) :accessory [react/view {:flex-direction :row} - [quo/text {:color :secondary - :style {:margin-right 8}} + [quo/text + {:color :secondary + :style {:margin-right 8}} display-symbol] (if icon [wallet.components/token-icon (assoc icon - :style {:background-color colors/gray-lighter - :border-radius 16} + :style {:background-color colors/gray-lighter + :border-radius 16} :image-style {:width 24 :height 24})] [chat-icon/custom-icon-view-list (:name token) color 32])]}] [separator]])) @@ -75,41 +80,46 @@ display-symbol fee fee-display-symbol] [react/view styles/header (when sign - [react/touchable-highlight (when-not in-progress? {:on-press #(re-frame/dispatch [:set :signing/sign nil])}) + [react/touchable-highlight + (when-not in-progress? {:on-press #(re-frame/dispatch [:set :signing/sign nil])}) [react/view {:padding-right 16} [icons/icon :main-icons/back]]]) [react/view {:flex 1} (if amount - [react/text {:style {:typography :title-bold}} (str (cond approve? - (i18n/label :t/authorize) - cancel? - (i18n/label :t/cancelling) - :else - (i18n/label :t/sending)) - (if cancel? - (str " " (utils/get-shortened-address hash)) - (str " " amount " " display-symbol)))] + [react/text {:style {:typography :title-bold}} + (str (cond approve? + (i18n/label :t/authorize) + cancel? + (i18n/label :t/cancelling) + :else + (i18n/label :t/sending)) + (if cancel? + (str " " (utils/get-shortened-address hash)) + (str " " amount " " display-symbol)))] [react/text {:style {:typography :title-bold}} (i18n/label :t/contract-interaction)]) (if sign - [react/nested-text {:style {:color colors/gray} - :ellipsize-mode :middle - :number-of-lines 1} (i18n/label :t/to) " " + [react/nested-text + {:style {:color colors/gray} + :ellipsize-mode :middle + :number-of-lines 1} (i18n/label :t/to) " " [{:style {:color colors/black}} (displayed-name contact)]] [react/text {:style {:margin-top 6 :color colors/gray}} (str fee " " fee-display-symbol " " (string/lower-case (i18n/label :t/network-fee)))])] [react/view {:padding-horizontal 24} - [quo/button (merge {:type :secondary} - (when-not in-progress? {:on-press #(re-frame/dispatch [:signing.ui/cancel-is-pressed])})) + [quo/button + (merge {:type :secondary} + (when-not in-progress? {:on-press #(re-frame/dispatch [:signing.ui/cancel-is-pressed])})) (i18n/label :t/cancel)]]]) -(views/defview keycard-pin-view [] - (views/letsubs [pin [:keycard/pin] +(views/defview keycard-pin-view + [] + (views/letsubs [pin [:keycard/pin] small-screen? [:dimensions/small-screen?] - error-label [:keycard/pin-error-label] - enter-step [:keycard/pin-enter-step] - status [:keycard/pin-status] + error-label [:keycard/pin-error-label] + enter-step [:keycard/pin-enter-step] + status [:keycard/pin-status] retry-counter [:keycard/retry-counter]] - (let [enter-step (or enter-step :sign) + (let [enter-step (or enter-step :sign) margin-bottom (if platform/ios? 40 0)] [react/view {:margin-bottom margin-bottom} [pin.views/pin-view @@ -123,20 +133,23 @@ (defn sign-with-keycard-button [amount-error gas-error] (let [disabled? (or amount-error gas-error)] - [react/touchable-highlight {:on-press #(when-not disabled? - (re-frame/dispatch [:signing.ui/sign-with-keycard-pressed]))} + [react/touchable-highlight + {:on-press #(when-not disabled? + (re-frame/dispatch [:signing.ui/sign-with-keycard-pressed]))} [react/view (styles/sign-with-keycard-button disabled?) [react/text {:style (styles/sign-with-keycard-button-text disabled?)} (i18n/label :t/sign-with)] [react/view {:padding-right 16} - [react/image {:source (resources/get-image :keycard-logo) - :style (merge {:width 64 - :margin-bottom 7 - :height 26} - (when (colors/dark?) - {:tint-color colors/white-persist}))}]]]])) + [react/image + {:source (resources/get-image :keycard-logo) + :style (merge {:width 64 + :margin-bottom 7 + :height 26} + (when (colors/dark?) + {:tint-color colors/white-persist}))}]]]])) -(defn- signing-phrase-view [phrase] +(defn- signing-phrase-view + [phrase] [react/view {:align-items :center} [react/text {:style {:color colors/gray :padding-bottom 8}} (i18n/label :t/signing-phrase)] [react/text phrase]]) @@ -146,73 +159,88 @@ [react/view [signing-phrase-view phrase] (case keycard-step - :pin [keycard-pin-view] + :pin [keycard-pin-view] [react/view {:align-items :center :margin-top 16 :margin-bottom 40} [sign-with-keycard-button nil nil]])]) -(defn redeem-tx-header [account receiver small-screen?] +(defn redeem-tx-header + [account receiver small-screen?] (fn [] [react/view {:style {:align-self :stretch :margin-top 30}} [separator] - [react/view {:style {:flex-direction :row - :justify-content :space-between - :align-items :center - :padding-left 16 :margin-vertical 8}} + [react/view + {:style {:flex-direction :row + :justify-content :space-between + :align-items :center + :padding-left 16 + :margin-vertical 8}} [react/text {:style {:flex 2 :margin-right 16}} (i18n/label :t/keycard-redeem-title)] - [react/text {:number-of-lines 1 - :ellipsize-mode :middle - :style {:padding-left 16 - :color colors/gray - :flex 3}} + [react/text + {:number-of-lines 1 + :ellipsize-mode :middle + :style {:padding-left 16 + :color colors/gray + :flex 3}} (if account (:name account) receiver)] (when account [react/view {:style {:flex 1 :padding-left 8}} [chat-icon/custom-icon-view-list (:name account) (:color account) (if small-screen? 20 32)]])] [separator]])) -(defn signature-request-header [amount currency small-screen? fiat-amount fiat-currency] +(defn signature-request-header + [amount currency small-screen? fiat-amount fiat-currency] (fn [] [react/view {:style {:align-self :stretch :margin-vertical 30}} - [react/nested-text {:style {:font-weight "500" :font-size (if small-screen? 34 44) - :text-align :center}} + [react/nested-text + {:style {:font-weight "500" + :font-size (if small-screen? 34 44) + :text-align :center}} (str amount " ") [{:style {:color colors/gray}} currency]] - [react/text {:style {:font-size 19 :text-align :center - :margin-bottom 16}} + [react/text + {:style {:font-size 19 + :text-align :center + :margin-bottom 16}} (str fiat-amount " " fiat-currency)] [separator]])) -(defn terminal-button [{:keys [on-press theme disabled? height]} label] - [react/touchable-opacity {:disabled disabled? - :on-press on-press - :style {:height height - :border-radius 16 - :flex 1 - :justify-content :center - :align-items :center - :background-color (if (= theme :negative) - colors/red-transparent-10 - colors/blue-light)}} - [quo/text {:size :large - :number-of-lines 1 - :color (if (= theme :negative) - :negative - :link) - :weight :medium} +(defn terminal-button + [{:keys [on-press theme disabled? height]} label] + [react/touchable-opacity + {:disabled disabled? + :on-press on-press + :style {:height height + :border-radius 16 + :flex 1 + :justify-content :center + :align-items :center + :background-color (if (= theme :negative) + colors/red-transparent-10 + colors/blue-light)}} + [quo/text + {:size :large + :number-of-lines 1 + :color (if (= theme :negative) + :negative + :link) + :weight :medium} label]]) -(defn signature-request-footer [keycard-step small-screen?] +(defn signature-request-footer + [keycard-step small-screen?] (fn [] [react/view {:style {:padding 16 :align-items :center}} [react/view {:style {:flex-direction :row}} - [terminal-button {:disabled? (= keycard-step :success) - :height (if small-screen? 52 64) - :on-press #(re-frame/dispatch [:show-popover {:view :transaction-data}])} + [terminal-button + {:disabled? (= keycard-step :success) + :height (if small-screen? 52 64) + :on-press #(re-frame/dispatch [:show-popover {:view :transaction-data}])} (i18n/label :t/show-transaction-data)]]])) -(defn signature-request [{:keys [formatted-data account fiat-amount fiat-currency keycard-step]} - connected? - small-screen?] +(defn signature-request + [{:keys [formatted-data account fiat-amount fiat-currency keycard-step]} + connected? + small-screen?] (let [message (:message formatted-data)] [react/view (assoc (styles/message) :padding-vertical 16) [keycard-sheet/connect-keycard @@ -222,67 +250,83 @@ :on-cancel #(re-frame/dispatch [:signing.ui/cancel-is-pressed]) :params (cond - (:receiver message) {:title (i18n/label :t/confirmation-request) - :header (redeem-tx-header account (:receiver message) small-screen?) - :footer (signature-request-footer keycard-step small-screen?) - :small-screen? small-screen? - :state-translations {:init {:title :t/keycard-redeem-tx + (:receiver message) {:title (i18n/label :t/confirmation-request) + :header (redeem-tx-header account + (:receiver message) + small-screen?) + :footer (signature-request-footer keycard-step small-screen?) + :small-screen? small-screen? + :state-translations {:init {:title :t/keycard-redeem-tx :description :t/keycard-redeem-tx-desc}}} - (:currency message) {:title (i18n/label :t/confirmation-request) - :header (signature-request-header (:formatted-amount message) - (:formatted-currency message) - small-screen? fiat-amount fiat-currency) - :footer (signature-request-footer keycard-step small-screen?) + (:currency message) {:title (i18n/label :t/confirmation-request) + :header (signature-request-header (:formatted-amount message) + (:formatted-currency message) + small-screen? + fiat-amount + fiat-currency) + :footer (signature-request-footer keycard-step small-screen?) :small-screen? small-screen?} - :else {:title (i18n/label :t/confirmation-request) - :header (signature-request-header (:formatted-amount message) - (:formatted-currency message) + :else {:title (i18n/label :t/confirmation-request) + :header (signature-request-header (:formatted-amount message) + (:formatted-currency message) - small-screen? fiat-amount fiat-currency) - :footer (signature-request-footer keycard-step small-screen?) - :small-screen? small-screen?})}]])) + small-screen? + fiat-amount + fiat-currency) + :footer (signature-request-footer keycard-step small-screen?) + :small-screen? small-screen?})}]])) -(defn- transaction-data-item [data] +(defn- transaction-data-item + [data] (let [text (types/clj->pretty-json data 2)] [react/view - [react/text {:style {:font-size 17 - :line-height 20 - :margin-bottom 24}} + [react/text + {:style {:font-size 17 + :line-height 20 + :margin-bottom 24}} text]])) -(views/defview transaction-data [] +(views/defview transaction-data + [] (views/letsubs [{:keys [formatted-data]} [:signing/sign]] [react/view {:style {:flex 1}} - [react/view {:style {:margin-horizontal 24 - :margin-top 24}} - [react/text {:style {:font-size 17 - :font-weight "700"}} + [react/view + {:style {:margin-horizontal 24 + :margin-top 24}} + [react/text + {:style {:font-size 17 + :font-weight "700"}} (i18n/label :t/transaction-data)]] - [react/scroll-view {:style {:flex 1 - :margin-horizontal 8 - :padding-horizontal 16 - :padding-vertical 10 - :margin-vertical 14}} + [react/scroll-view + {:style {:flex 1 + :margin-horizontal 8 + :padding-horizontal 16 + :padding-vertical 10 + :margin-vertical 14}} [transaction-data-item formatted-data]] [separator] - [react/view {:style {:margin-horizontal 8 - :margin-vertical 16}} - [quo/button {:on-press #(re-frame/dispatch [:hide-popover])} + [react/view + {:style {:margin-horizontal 8 + :margin-vertical 16}} + [quo/button {:on-press #(re-frame/dispatch [:hide-popover])} (i18n/label :t/close)]]])) -(views/defview password-view [{:keys [type error in-progress? enabled?] :as sign}] +(views/defview password-view + [{:keys [type error in-progress? enabled?] :as sign}] (views/letsubs [phrase [:signing/phrase]] (case type :password [react/view {:padding-top 8 :padding-bottom 8} [signing-phrase-view phrase] - [react/view {:padding-horizontal 16 - :padding-vertical 12} + [react/view + {:padding-horizontal 16 + :padding-vertical 12} [quo/text-input {:secure-text-entry true :placeholder (i18n/label :t/enter-password) - :on-change-text #(re-frame/dispatch [:signing.ui/password-is-changed (security/mask-data %)]) + :on-change-text #(re-frame/dispatch [:signing.ui/password-is-changed + (security/mask-data %)]) :accessibility-label :enter-password-input :auto-capitalize :none :editable (not in-progress?) @@ -290,19 +334,22 @@ :show-cancel false}]] [react/view {:align-items :center :height 60} (if in-progress? - [react/activity-indicator {:animating true - :size :large}] - [quo/button {:on-press #(re-frame/dispatch [:signing.ui/sign-is-pressed]) - :disabled (not enabled?)} + [react/activity-indicator + {:animating true + :size :large}] + [quo/button + {:on-press #(re-frame/dispatch [:signing.ui/sign-is-pressed]) + :disabled (not enabled?)} (i18n/label :t/transactions-sign)])]] :keycard [keycard-view sign phrase] [react/view]))) -(views/defview message-sheet [] +(views/defview message-sheet + [] (views/letsubs [{:keys [formatted-data type] :as sign} [:signing/sign-message] - small-screen? [:dimensions/small-screen?] - keycard [:keycard]] + small-screen? [:dimensions/small-screen?] + keycard [:keycard]] (if (= type :pinless) [signature-request sign (:card-connected? keycard) small-screen?] [react/view (styles/message) @@ -318,19 +365,25 @@ [react/text (or formatted-data "")]]] [password-view sign]]]))) -(defn error-item [] +(defn error-item + [] (fn [title show-error] [gh/touchable-highlight {:on-press #(swap! show-error not)} - [react/view {:style {:align-items :center - :flex-direction :row}} + [react/view + {:style {:align-items :center + :flex-direction :row}} [react/text {:style {:color colors/red :margin-right 8}} (i18n/label title)] [icons/icon :warning {:color colors/red}]]])) -(defn amount-item [] +(defn amount-item + [] (let [show-error (reagent/atom false)] (fn [prices wallet-currency amount amount-error display-symbol fee-display-symbol prices-loading?] - (let [converted-value (* amount (get-in prices [(keyword display-symbol) (keyword (:code wallet-currency)) :price]))] + (let [converted-value (* amount + (get-in prices + [(keyword display-symbol) (keyword (:code wallet-currency)) + :price]))] [quo/list-item {:size :small :title (if amount-error @@ -350,11 +403,15 @@ (i18n/format-currency converted-value (:code wallet-currency))]) [react/text {:style {:color colors/gray}} (str " " (:code wallet-currency))]]}])))) -(views/defview fee-item [prices wallet-currency fee-display-symbol fee - insufficient-balance? gas-error gas-error-state prices-loading?] +(views/defview fee-item + [prices wallet-currency fee-display-symbol fee + insufficient-balance? gas-error gas-error-state prices-loading?] (views/letsubs [{:keys [gas-price-loading? gas-loading?]} [:signing/edit-fee] - show-error (reagent/atom false)] - (let [converted-fee-value (* fee (get-in prices [(keyword fee-display-symbol) (keyword (:code wallet-currency)) :price]))] + show-error (reagent/atom false)] + (let [converted-fee-value (* fee + (get-in prices + [(keyword fee-display-symbol) (keyword (:code wallet-currency)) + :price]))] [quo/list-item {:size :small :title (if (and (not (or gas-price-loading? gas-loading?)) gas-error) @@ -369,11 +426,13 @@ (if (or gas-price-loading? gas-loading?) [react/small-loading-indicator] (if (= :gas-isnt-set gas-error-state) - [react/text {:style {:color colors/blue} - :accessibility-label :custom-gas-fee} + [react/text + {:style {:color colors/blue} + :accessibility-label :custom-gas-fee} (i18n/label :t/set-custom-fee)] - [react/view {:style {:flex-direction :row} - :accessibility-label :custom-gas-fee} + [react/view + {:style {:flex-direction :row} + :accessibility-label :custom-gas-fee} [react/nested-text {:style {:color colors/gray}} [{:style {:color colors/black}} (utils/format-decimals fee 6)] " " @@ -383,15 +442,17 @@ [react/small-loading-indicator] [react/text {:style {:color colors/black}} (i18n/format-currency converted-fee-value (:code wallet-currency))]) - [react/text {:style {:color colors/gray}} (str " " (:code wallet-currency))]]))) - :on-press #(re-frame/dispatch - [:signing.ui/open-fee-sheet - {:content (fn [] - (if (eip1559/enabled?) - [sheets/fee-bottom-sheet-eip1559-custom fee-display-symbol] - [sheets/fee-bottom-sheet fee-display-symbol]))}])}]))) + [react/text {:style {:color colors/gray}} + (str " " (:code wallet-currency))]]))) + :on-press #(re-frame/dispatch + [:signing.ui/open-fee-sheet + {:content (fn [] + (if (eip1559/enabled?) + [sheets/fee-bottom-sheet-eip1559-custom fee-display-symbol] + [sheets/fee-bottom-sheet fee-display-symbol]))}])}]))) -(views/defview network-item [] +(views/defview network-item + [] (views/letsubs [network-name [:network-name]] [quo/list-item {:title (i18n/label :t/network) @@ -399,28 +460,29 @@ :accessory :text :accessory-text network-name}])) -(defn advanced-item [] +(defn advanced-item + [] [:<> [separator] [quo/list-item - {:size :small - :title (i18n/label :t/advanced) - :chevron true + {:size :small + :title (i18n/label :t/advanced) + :chevron true :on-press #(re-frame/dispatch [:bottom-sheet/show-sheet {:content sheets/advanced}])}]]) (views/defview sheet [{:keys [from contact amount token cancel?] :as tx}] - (views/letsubs [fee [:signing/fee] - sign [:signing/sign] - chain [:current-network] + (views/letsubs [fee [:signing/fee] + sign [:signing/sign] + chain [:current-network] {:keys [amount-error gas-error gas-error-state insufficient-balalce?]} [:signing/amount-errors (:address from)] keycard-multiaccount? [:keycard-multiaccount?] - prices [:prices] - wallet-currency [:wallet/currency] - mainnet? [:mainnet?] - prices-loading? [:prices-loading?] - management-enabled? [:wallet/transactions-management-enabled?]] + prices [:prices] + wallet-currency [:wallet/currency] + mainnet? [:mainnet?] + prices-loading? [:prices-loading?] + management-enabled? [:wallet/transactions-management-enabled?]] (let [display-symbol (wallet.utils/display-symbol token) fee-display-symbol (wallet.utils/display-symbol (tokens/native-currency chain))] [react/view (styles/sheet) @@ -442,9 +504,11 @@ (when-not cancel? [token-item token display-symbol]) (when-not cancel? - [amount-item prices wallet-currency amount amount-error display-symbol fee-display-symbol prices-loading?]) + [amount-item prices wallet-currency amount amount-error display-symbol fee-display-symbol + prices-loading?]) [separator] - [fee-item prices wallet-currency fee-display-symbol fee insufficient-balalce? gas-error gas-error-state prices-loading?] + [fee-item prices wallet-currency fee-display-symbol fee insufficient-balalce? gas-error + gas-error-state prices-loading?] (when (and management-enabled? (not keycard-multiaccount?)) [advanced-item]) (when (= :gas-is-set gas-error-state) @@ -456,14 +520,16 @@ (if (= :gas-isnt-set gas-error-state) [react/text {:style {:color colors/gray :margin-horizontal 32 :text-align :center}} (i18n/label :t/tx-fail-description2)] - [quo/button {:on-press #(re-frame/dispatch [:set :signing/sign {:type :password}]) - :disabled (or amount-error gas-error) - :theme (if gas-error-state :negative :main)} + [quo/button + {:on-press #(re-frame/dispatch [:set :signing/sign {:type :password}]) + :disabled (or amount-error gas-error) + :theme (if gas-error-state :negative :main)} (i18n/label (if gas-error-state :t/sign-anyway :t/sign-with-password))]))]])]))) -(views/defview signing [] +(views/defview signing + [] (views/letsubs [tx [:signing/tx]] [bottom-panel/animated-bottom-panel ;;we use select-keys here because we don't want to update view if other keys in map are changed diff --git a/src/status_im/ui/screens/status/new/styles.cljs b/src/status_im/ui/screens/status/new/styles.cljs index 19b63e7c6b..1ae16cef62 100644 --- a/src/status_im/ui/screens/status/new/styles.cljs +++ b/src/status_im/ui/screens/status/new/styles.cljs @@ -15,7 +15,8 @@ :margin-right 4 :border-radius 4}) -(defn photos-buttons [] +(defn photos-buttons + [] {:height 88 :border-top-width 1 :border-top-color colors/gray-lighter diff --git a/src/status_im/ui/screens/status/new/views.cljs b/src/status_im/ui/screens/status/new/views.cljs index 2934b51aae..a82597737c 100644 --- a/src/status_im/ui/screens/status/new/views.cljs +++ b/src/status_im/ui/screens/status/new/views.cljs @@ -1,43 +1,50 @@ (ns status-im.ui.screens.status.new.views (:require-macros [status-im.utils.views :refer [defview letsubs]]) - (:require [status-im.ui.components.keyboard-avoid-presentation :as kb-presentation] - [status-im.ui.components.react :as react] - [status-im.i18n.i18n :as i18n] - [re-frame.core :as re-frame] - [status-im.ui.components.toolbar :as toolbar] + (:require [clojure.string :as string] + [quo.components.animated.pressable :as pressable] [quo.core :as quo] [quo.design-system.colors :as colors] + [re-frame.core :as re-frame] [reagent.core :as reagent] - [clojure.string :as string] + [status-im.i18n.i18n :as i18n] [status-im.ui.components.icons.icons :as icons] - [quo.components.animated.pressable :as pressable] - [status-im.ui.screens.status.views :as status.views] + [status-im.ui.components.keyboard-avoid-presentation :as kb-presentation] + [status-im.ui.components.react :as react] + [status-im.ui.components.toolbar :as toolbar] [status-im.ui.screens.status.new.styles :as styles] + [status-im.ui.screens.status.views :as status.views] [status-im.utils.platform :as platform])) -(defn buttons [] +(defn buttons + [] [react/view styles/buttons - [pressable/pressable {:type :scale - :accessibility-label :take-picture - :on-press #(re-frame/dispatch [:chat.ui/show-image-picker-camera-timeline])} + [pressable/pressable + {:type :scale + :accessibility-label :take-picture + :on-press #(re-frame/dispatch [:chat.ui/show-image-picker-camera-timeline])} [icons/icon :main-icons/camera]] [react/view {:style {:padding-top 8}} - [pressable/pressable {:on-press #(re-frame/dispatch [:chat.ui/open-image-picker-timeline]) - :accessibility-label :open-gallery - :type :scale} + [pressable/pressable + {:on-press #(re-frame/dispatch [:chat.ui/open-image-picker-timeline]) + :accessibility-label :open-gallery + :type :scale} [icons/icon :main-icons/gallery]]]]) -(defn image-preview [uri] +(defn image-preview + [uri] [react/touchable-highlight {:on-press #(re-frame/dispatch [:chat.ui/camera-roll-pick-timeline uri])} - [react/image {:style styles/image - :source {:uri uri}}]]) + [react/image + {:style styles/image + :source {:uri uri}}]]) -(defview photos [] +(defview photos + [] (letsubs [camera-roll-photos [:camera-roll/photos]] {:component-did-mount #(re-frame/dispatch [:chat.ui/camera-roll-get-photos 20])} - [react/scroll-view {:horizontal true - :style {:max-height 88} - :keyboard-should-persist-taps :handled} + [react/scroll-view + {:horizontal true + :style {:max-height 88} + :keyboard-should-persist-taps :handled} [react/view (styles/photos-buttons) [buttons] (for [img camera-roll-photos] @@ -46,23 +53,25 @@ (def message-max-length 600) -(defn my-status [] +(defn my-status + [] (let [images-opened (reagent/atom false) - scroll (reagent/atom nil) - autoscroll? (reagent/atom false) + scroll (reagent/atom nil) + autoscroll? (reagent/atom false) scroll-height (reagent/atom nil) - input-text (re-frame/subscribe [:chats/timeline-chat-input-text]) + input-text (re-frame/subscribe [:chats/timeline-chat-input-text]) sending-image (re-frame/subscribe [:chats/timeline-sending-image])] (fn [] (let [{:keys [uri]} (first (vals @sending-image)) - text-length (count @input-text)] + text-length (count @input-text)] [kb-presentation/keyboard-avoiding-view {:style {:flex 1}} [:<> - [react/scroll-view {:style {:flex 1} - :ref #(reset! scroll %) - :on-layout #(reset! scroll-height - (.-nativeEvent.layout.height ^js %)) - :keyboard-should-persist-taps :handled} + [react/scroll-view + {:style {:flex 1} + :ref #(reset! scroll %) + :on-layout #(reset! scroll-height + (.-nativeEvent.layout.height ^js %)) + :keyboard-should-persist-taps :handled} [react/text-input {:style {:margin 16} :scroll-enabled false @@ -75,7 +84,7 @@ :multiline true :on-selection-change (fn [args] (let [selection (.-selection ^js (.-nativeEvent ^js args)) - end (.-end ^js selection)] + end (.-end ^js selection)] (reset! autoscroll? (< (- (count @input-text) end) 10)))) :on-content-size-change #(when (and @autoscroll? @scroll @scroll-height) (when-let [height (- (.-nativeEvent.contentSize.height ^js %) @@ -112,7 +121,8 @@ (re-frame/dispatch [:navigate-back]))} (i18n/label :t/wallet-send)]}] [react/view styles/count-container - [react/text {:style {:color (if (> text-length message-max-length) - colors/red - colors/gray)}} + [react/text + {:style {:color (if (> text-length message-max-length) + colors/red + colors/gray)}} (str text-length " / " message-max-length)]]]]]])))) diff --git a/src/status_im/ui/screens/status/styles.cljs b/src/status_im/ui/screens/status/styles.cljs index 6b1a734aca..1b02481a76 100644 --- a/src/status_im/ui/screens/status/styles.cljs +++ b/src/status_im/ui/screens/status/styles.cljs @@ -1,7 +1,8 @@ (ns status-im.ui.screens.status.styles (:require [quo.design-system.colors :as colors])) -(defn descr-container [] +(defn descr-container + [] {:border-width 1 :border-color colors/gray-lighter :border-top-right-radius 16 diff --git a/src/status_im/ui/screens/status/views.cljs b/src/status_im/ui/screens/status/views.cljs index 6faa7624f0..9b4e13c12e 100644 --- a/src/status_im/ui/screens/status/views.cljs +++ b/src/status_im/ui/screens/status/views.cljs @@ -1,72 +1,81 @@ (ns status-im.ui.screens.status.views - (:require [status-im.ui.screens.chat.message.message :as message] - [status-im.ui.components.react :as react] - [quo.design-system.colors :as colors] - [status-im.utils.datetime :as datetime] - [status-im.ui.screens.chat.message.gap :as gap] - [status-im.constants :as constants] + (:require [quo.design-system.colors :as colors] [re-frame.core :as re-frame] [reagent.core :as reagent] + [status-im.chat.models :as chat] + [status-im.chat.models.reactions :as models.reactions] + [status-im.constants :as constants] + [status-im.i18n.i18n :as i18n] + [status-im.ui.components.fast-image :as fast-image] [status-im.ui.components.icons.icons :as icons] [status-im.ui.components.list.views :as list] - [status-im.i18n.i18n :as i18n] - [status-im.ui.screens.status.styles :as styles] [status-im.ui.components.plus-button :as components.plus-button] - [status-im.ui.screens.chat.image.preview.views :as preview] - [status-im.ui.screens.chat.photos :as photos] + [status-im.ui.components.react :as react] [status-im.ui.components.tabs :as tabs] - [status-im.ui.screens.chat.message.reactions-old :as reactions] - [status-im.chat.models.reactions :as models.reactions] [status-im.ui.screens.chat.components.reply :as components.reply] + [status-im.ui.screens.chat.image.preview.views :as preview] + [status-im.ui.screens.chat.message.gap :as gap] [status-im.ui.screens.chat.message.link-preview :as link-preview] - [status-im.chat.models :as chat] - [status-im.ui.components.fast-image :as fast-image])) + [status-im.ui.screens.chat.message.message :as message] + [status-im.ui.screens.chat.message.reactions-old :as reactions] + [status-im.ui.screens.chat.photos :as photos] + [status-im.ui.screens.status.styles :as styles] + [status-im.utils.datetime :as datetime])) (defonce messages-list-ref (atom nil)) (def image-max-dimension 192) -(defn image-set-size [width] +(defn image-set-size + [width] (fn [evt] - (reset! width (/ (.-width (.-nativeEvent evt)) (/ (.-height (.-nativeEvent evt)) image-max-dimension))))) + (reset! width (/ (.-width (.-nativeEvent evt)) + (/ (.-height (.-nativeEvent evt)) image-max-dimension))))) -(defn message-content-image [_ _] +(defn message-content-image + [_ _] (let [width (reagent/atom nil)] (fn [uri show-close?] - [react/view {:style {:width @width - :align-items :center - :height image-max-dimension - :max-width :100% - :overflow :hidden - :opacity (if @width 1 0) - :border-radius 16 - :margin-top 8} - :accessibility-label :image-message} - [fast-image/fast-image {:style {:width @width - :height image-max-dimension} - :on-load (image-set-size width) - :source {:uri uri}}] - [react/view {:border-width 1 - :top 0 - :left 0 - :width @width - :height image-max-dimension - :border-radius 16 - :position :absolute - :background-color :transparent - :border-color colors/black-transparent}] + [react/view + {:style {:width @width + :align-items :center + :height image-max-dimension + :max-width :100% + :overflow :hidden + :opacity (if @width 1 0) + :border-radius 16 + :margin-top 8} + :accessibility-label :image-message} + [fast-image/fast-image + {:style {:width @width + :height image-max-dimension} + :on-load (image-set-size width) + :source {:uri uri}}] + [react/view + {:border-width 1 + :top 0 + :left 0 + :width @width + :height image-max-dimension + :border-radius 16 + :position :absolute + :background-color :transparent + :border-color colors/black-transparent}] (when show-close? - [react/touchable-highlight {:on-press #(re-frame/dispatch [:chat.ui/cancel-sending-image-timeline]) - :accessibility-label :cancel-send-image - :style {:right 4 :top 12 :position :absolute}} - [react/view {:width 24 - :height 24 - :background-color colors/black-persist - :border-radius 12 - :align-items :center - :justify-content :center} + [react/touchable-highlight + {:on-press #(re-frame/dispatch [:chat.ui/cancel-sending-image-timeline]) + :accessibility-label :cancel-send-image + :style {:right 4 :top 12 :position :absolute}} + [react/view + {:width 24 + :height 24 + :background-color colors/black-persist + :border-radius 12 + :align-items :center + :justify-content :center} [icons/icon :main-icons/close-circle {:color colors/white-persist}]]])]))) -(defn on-long-press-fn [on-long-press content image] +(defn on-long-press-fn + [on-long-press content image] (on-long-press (when-not image [{:id :copy @@ -75,31 +84,36 @@ (get content :parsed-text))) :label (i18n/label :t/sharing-copy-to-clipboard)}]))) -(defn image-message [] +(defn image-message + [] (let [visible (reagent/atom false)] (fn [{:keys [content] :as message} on-long-press] [:<> - [preview/preview-image {:message (assoc message :cant-be-replied true) - :visible @visible - :can-reply false - :on-close #(do (reset! visible false) - (reagent/flush))}] - [react/touchable-highlight {:on-press (fn [_] - (reset! visible true) - (react/dismiss-keyboard!)) - :on-long-press #(on-long-press-fn on-long-press content true)} + [preview/preview-image + {:message (assoc message :cant-be-replied true) + :visible @visible + :can-reply false + :on-close #(do (reset! visible false) + (reagent/flush))}] + [react/touchable-highlight + {:on-press (fn [_] + (reset! visible true) + (react/dismiss-keyboard!)) + :on-long-press #(on-long-press-fn on-long-press content true)} [message-content-image (:image content) false]]]))) -(defn message-item [account profile] +(defn message-item + [account profile] (fn [{:keys [content-type content from timestamp outgoing] :as message} {:keys [modal on-long-press close-modal]}] - [react/view (merge {:padding-vertical 8 - :flex-direction :row - :background-color (when modal colors/white) - :padding-horizontal 16} - (when modal - {:border-radius 16})) + [react/view + (merge {:padding-vertical 8 + :flex-direction :row + :background-color (when modal colors/white) + :padding-horizontal 16} + (when modal + {:border-radius 16})) [react/touchable-highlight {:on-press #(do (when modal (close-modal)) (when profile (re-frame/dispatch [:navigate-back])) @@ -109,29 +123,34 @@ [photos/account-photo account] [photos/member-photo from])]] [react/view {:flex 1} - [react/view {:flex-direction :row - :justify-content :space-between - :width :98% - :word-break :break-all} + [react/view + {:flex-direction :row + :justify-content :space-between + :width :98% + :word-break :break-all} [react/touchable-highlight {:on-press #(do (when modal (close-modal)) (when profile (re-frame/dispatch [:navigate-back])) (re-frame/dispatch [:chat.ui/show-profile from]))} (let [message-author-width (* @(re-frame/subscribe [:dimensions/window-width]) 0.75)] (if outgoing - [react/view {:style {:width message-author-width}} [message/message-my-name {:profile? true :you? false}]] - [react/view {:style {:width message-author-width}} [message/message-author-name from {:profile? true}]]))] + [react/view {:style {:width message-author-width}} + [message/message-my-name {:profile? true :you? false}]] + [react/view {:style {:width message-author-width}} + [message/message-author-name from {:profile? true}]]))] [react/text {:style {:font-size 10 :color colors/gray :margin-left :auto}} (datetime/time-ago (datetime/to-date timestamp))]] [react/view (if (= content-type constants/content-type-image) [image-message message on-long-press] - [react/touchable-highlight (when-not modal - {:on-long-press #(on-long-press-fn on-long-press content false)}) + [react/touchable-highlight + (when-not modal + {:on-long-press #(on-long-press-fn on-long-press content false)}) [message/render-parsed-text (assoc message :outgoing false) (:parsed-text content)]]) [link-preview/link-preview-wrapper (:links content) outgoing true]]]])) -(defn render-message [{:keys [type] :as message} idx _ {:keys [timeline account chat-id profile]}] +(defn render-message + [{:keys [type] :as message} idx _ {:keys [timeline account chat-id profile]}] (if (= type :datemark) nil (if (= type :gap) @@ -140,16 +159,18 @@ [gap/gap message idx messages-list-ref true chat-id]) ;; for timeline for reactions we need to use :from as chat-id (let [chat-id (chat/profile-chat-topic (:from message))] - [react/view (merge {:accessibility-label :chat-item} - (when (:last-in-group? message) - {:padding-bottom 8 - :margin-bottom 8 - :border-bottom-width 1 - :border-bottom-color colors/gray-lighter})) + [react/view + (merge {:accessibility-label :chat-item} + (when (:last-in-group? message) + {:padding-bottom 8 + :margin-bottom 8 + :border-bottom-width 1 + :border-bottom-color colors/gray-lighter})) [reactions/with-reaction-picker {:message message :timeline true - :reactions @(re-frame/subscribe [:chats/message-reactions (:message-id message) constants/timeline-chat-id]) + :reactions @(re-frame/subscribe [:chats/message-reactions (:message-id message) + constants/timeline-chat-id]) :picker-on-open (fn []) :picker-on-close (fn []) :send-emoji (fn [{:keys [emoji-id]}] @@ -167,35 +188,44 @@ (def state (reagent/atom {:tab :timeline})) -(defn tabs [] +(defn tabs + [] (let [{:keys [tab]} @state] - [react/view {:flex-direction :row - :padding-horizontal 4 - :margin-top 8} + [react/view + {:flex-direction :row + :padding-horizontal 4 + :margin-top 8} [tabs/tab-title state :timeline (i18n/label :t/timeline) (= tab :timeline)] [tabs/tab-title state :status (i18n/label :t/my-status) (= tab :status)]])) -(defn timeline [] - (let [messages @(re-frame/subscribe [:chats/timeline-messages-stream]) +(defn timeline + [] + (let [messages @(re-frame/subscribe [:chats/timeline-messages-stream]) loading-messages? @(re-frame/subscribe [:chats/loading-messages? constants/timeline-chat-id]) - no-messages? @(re-frame/subscribe [:chats/chat-no-messages? constants/timeline-chat-id]) - account @(re-frame/subscribe [:multiaccount])] + no-messages? @(re-frame/subscribe [:chats/chat-no-messages? constants/timeline-chat-id]) + account @(re-frame/subscribe [:multiaccount])] [react/view {:flex 1} - [react/view {:height 1 - :background-color colors/gray-lighter}] + [react/view + {:height 1 + :background-color colors/gray-lighter}] (if (and no-messages? loading-messages?) [react/view {:flex 1 :align-items :center :justify-content :center} [react/activity-indicator {:animating true}]] (if no-messages? - [react/view {:padding-horizontal 32 - :margin-top 64} - [fast-image/fast-image {:style {:width 140 - :height 140 - :align-self :center} - :source {:uri "https://bafybeieayj76s4vjlw5uwdvnakosy46rqyioqsp2ygl6sedivemhkxrbwi.ipfs.cf-ipfs.com"}}] + [react/view + {:padding-horizontal 32 + :margin-top 64} + [fast-image/fast-image + {:style {:width 140 + :height 140 + :align-self :center} + :source + {:uri + "https://bafybeieayj76s4vjlw5uwdvnakosy46rqyioqsp2ygl6sedivemhkxrbwi.ipfs.cf-ipfs.com"}}] [react/view (styles/descr-container) - [react/text {:style {:color colors/gray - :line-height 22}} + [react/text + {:style {:color colors/gray + :line-height 22}} (if (= :timeline (:tab @state)) (i18n/label :t/statuses-descr) (i18n/label :t/statuses-my-status-descr))]]] @@ -205,7 +235,8 @@ :account account} :render-fn render-message :data messages - :on-end-reached #(re-frame/dispatch [:chat.ui/load-more-messages constants/timeline-chat-id]) + :on-end-reached #(re-frame/dispatch [:chat.ui/load-more-messages + constants/timeline-chat-id]) ;;don't remove :on-scroll-to-index-failed :on-scroll-to-index-failed #() :header [react/view {:height 8}] diff --git a/src/status_im/ui/screens/stickers/styles.cljs b/src/status_im/ui/screens/stickers/styles.cljs index b76d365090..444406557b 100644 --- a/src/status_im/ui/screens/stickers/styles.cljs +++ b/src/status_im/ui/screens/stickers/styles.cljs @@ -2,15 +2,17 @@ (:require [quo.design-system.colors :as colors])) (def screen - {:flex 1}) + {:flex 1}) -(defn sticker-image [sticker-icon-size] +(defn sticker-image + [sticker-icon-size] {:margin 16 :width sticker-icon-size :height sticker-icon-size :border-radius (/ sticker-icon-size 2)}) -(defn price-badge [not-enough-snt?] +(defn price-badge + [not-enough-snt?] {:background-color (if not-enough-snt? colors/gray colors/blue) :border-radius 14 :flex-direction :row diff --git a/src/status_im/ui/screens/stickers/views.cljs b/src/status_im/ui/screens/stickers/views.cljs index 172d461ff0..b9954665ee 100644 --- a/src/status_im/ui/screens/stickers/views.cljs +++ b/src/status_im/ui/screens/stickers/views.cljs @@ -1,51 +1,60 @@ (ns status-im.ui.screens.stickers.views - (:require [re-frame.core :as re-frame] + (:require [quo.design-system.colors :as colors] + [re-frame.core :as re-frame] [status-im.i18n.i18n :as i18n] - [quo.design-system.colors :as colors] + [status-im.ui.components.fast-image :as fast-image] [status-im.ui.components.icons.icons :as icons] [status-im.ui.components.react :as react] [status-im.ui.screens.stickers.styles :as styles] - [status-im.utils.money :as money] [status-im.utils.handlers :refer [ :eth price)))])]]))))) -(defn pack-badge [{:keys [name author thumbnail preview id] :as pack}] +(defn pack-badge + [{:keys [name author thumbnail preview id] :as pack}] [react/touchable-highlight {:on-press #(re-frame/dispatch [:navigate-to :stickers-pack {:id id}])} [react/view {:margin-bottom 27} [fast-image/fast-image {:style {:height 200 :border-radius 20} :source {:uri (cache preview)}}] @@ -53,11 +62,13 @@ [thumbnail-icon thumbnail 40] [react/view {:padding-horizontal 16 :flex 1} [react/text {:accessibility-label :sticker-pack-name} name] - [react/text {:style {:color colors/gray :margin-top 6} - :accessibility-label :sticker-pack-author} author]] + [react/text + {:style {:color colors/gray :margin-top 6} + :accessibility-label :sticker-pack-author} author]] [price-badge pack]]]]) -(defview packs [] +(defview packs + [] (letsubs [packs [:stickers/all-packs]] [react/view styles/screen [react/keyboard-avoiding-view {:flex 1} @@ -72,7 +83,8 @@ (def sticker-icon-size 60) -(defview pack-main [] +(defview pack-main + [] (letsubs [{:keys [name author thumbnail stickers] :as pack} [:stickers/get-current-pack]] @@ -90,10 +102,12 @@ [react/view {:flex-direction :row :flex-wrap :wrap} (for [{:keys [url]} stickers] ^{:key url} - [fast-image/fast-image {:style (styles/sticker-image sticker-icon-size) - :source {:uri (cache url)}}])]]]] + [fast-image/fast-image + {:style (styles/sticker-image sticker-icon-size) + :source {:uri (cache url)}}])]]]] [react/view {:flex 1 :align-items :center :justify-content :center} [react/activity-indicator {:animating true}]])])) -(defview pack [] +(defview pack + [] [pack-main]) diff --git a/src/status_im/ui/screens/sync_settings/views.cljs b/src/status_im/ui/screens/sync_settings/views.cljs index 2fb0970925..b31908507c 100644 --- a/src/status_im/ui/screens/sync_settings/views.cljs +++ b/src/status_im/ui/screens/sync_settings/views.cljs @@ -1,71 +1,79 @@ (ns status-im.ui.screens.sync-settings.views (:require-macros [status-im.utils.views :as views]) - (:require [re-frame.core :as re-frame] - [quo.core :as quo] - [status-im.i18n.i18n :as i18n] - [status-im.constants :as constants] + (:require [quo.core :as quo] [quo.design-system.colors :as colors] + [re-frame.core :as re-frame] + [status-im.constants :as constants] + [status-im.i18n.i18n :as i18n] [status-im.ui.components.react :as react])) -(views/defview sync-settings [] +(views/defview sync-settings + [] (views/letsubs [{:keys [syncing-on-mobile-network? backup-enabled? default-sync-period - use-mailservers?]} [:multiaccount] - current-mailserver-name [:mailserver/current-name]] + use-mailservers?]} + [:multiaccount] + current-mailserver-name [:mailserver/current-name]] [react/scroll-view [quo/list-header (i18n/label :t/data-syncing)] - [quo/list-item {:size :small - :title (i18n/label :t/mobile-network-settings) - :accessibility-label :notifications-button - :on-press #(re-frame/dispatch [:navigate-to :mobile-network-settings]) - :chevron true - :accessory :text - :accessory-text (if syncing-on-mobile-network? - (i18n/label :t/mobile-network-use-mobile) - (i18n/label :t/mobile-network-use-wifi))}] - [quo/list-item {:size :small - :title (i18n/label :t/backup-settings) - :accessibility-label :backup-settings-button - :on-press #(re-frame/dispatch [:navigate-to :backup-settings]) - :chevron true - :accessory :text - :accessory-text (if backup-enabled? - (i18n/label :t/backup-enabled) - (i18n/label :t/backup-disabled))}] - [quo/list-item {:size :small - :title (i18n/label :t/default-sync-period) - :accessibility-label :default-sync-period-button - :on-press #(re-frame/dispatch [:navigate-to :default-sync-period-settings]) - :chevron true - :accessory :text - :accessory-text (cond - (= default-sync-period constants/two-mins) - (i18n/label :t/two-minutes) - (or - (nil? default-sync-period) - (= default-sync-period constants/one-day)) - (i18n/label :t/one-day) - (= default-sync-period constants/three-days) - (i18n/label :t/three-days) - (= default-sync-period constants/one-week) - (i18n/label :t/one-week) - (= default-sync-period constants/one-month) - (i18n/label :t/one-month))}] - [quo/list-item {:size :small - :accessibility-label :offline-messages-settings-button - :title (i18n/label :t/history-nodes) - :on-press #(re-frame/dispatch [:navigate-to :offline-messaging-settings]) - :accessory :text - :accessory-text (when use-mailservers? current-mailserver-name) - :chevron true}] + [quo/list-item + {:size :small + :title (i18n/label :t/mobile-network-settings) + :accessibility-label :notifications-button + :on-press #(re-frame/dispatch [:navigate-to :mobile-network-settings]) + :chevron true + :accessory :text + :accessory-text (if syncing-on-mobile-network? + (i18n/label :t/mobile-network-use-mobile) + (i18n/label :t/mobile-network-use-wifi))}] + [quo/list-item + {:size :small + :title (i18n/label :t/backup-settings) + :accessibility-label :backup-settings-button + :on-press #(re-frame/dispatch [:navigate-to :backup-settings]) + :chevron true + :accessory :text + :accessory-text (if backup-enabled? + (i18n/label :t/backup-enabled) + (i18n/label :t/backup-disabled))}] + [quo/list-item + {:size :small + :title (i18n/label :t/default-sync-period) + :accessibility-label :default-sync-period-button + :on-press #(re-frame/dispatch [:navigate-to :default-sync-period-settings]) + :chevron true + :accessory :text + :accessory-text (cond + (= default-sync-period constants/two-mins) + (i18n/label :t/two-minutes) + (or + (nil? default-sync-period) + (= default-sync-period constants/one-day)) + (i18n/label :t/one-day) + (= default-sync-period constants/three-days) + (i18n/label :t/three-days) + (= default-sync-period constants/one-week) + (i18n/label :t/one-week) + (= default-sync-period constants/one-month) + (i18n/label :t/one-month))}] + [quo/list-item + {:size :small + :accessibility-label :offline-messages-settings-button + :title (i18n/label :t/history-nodes) + :on-press #(re-frame/dispatch [:navigate-to :offline-messaging-settings]) + :accessory :text + :accessory-text (when use-mailservers? current-mailserver-name) + :chevron true}] ;; TODO(Ferossgp): Devider componemt - [react/view {:height 1 - :background-color colors/gray-lighter - :margin-top 8}] + [react/view + {:height 1 + :background-color colors/gray-lighter + :margin-top 8}] [quo/list-header (i18n/label :t/device-syncing)] - [quo/list-item {:size :small - :title (i18n/label :t/devices) - :accessibility-label :pairing-settings-button - :on-press #(re-frame/dispatch [:navigate-to :installations]) - :chevron true}]])) + [quo/list-item + {:size :small + :title (i18n/label :t/devices) + :accessibility-label :pairing-settings-button + :on-press #(re-frame/dispatch [:navigate-to :installations]) + :chevron true}]])) diff --git a/src/status_im/ui/screens/terms_of_service/views.cljs b/src/status_im/ui/screens/terms_of_service/views.cljs index fb8f0581df..8f1b8ac523 100644 --- a/src/status_im/ui/screens/terms_of_service/views.cljs +++ b/src/status_im/ui/screens/terms_of_service/views.cljs @@ -1,18 +1,19 @@ (ns status-im.ui.screens.terms-of-service.views - (:require [status-im.ui.components.react :as react] - [status-im.i18n.i18n :as i18n] - [quo.core :as quo] + (:require [quo.core :as quo] + [quo.design-system.colors :as colors] [quo.design-system.spacing :as spacing] [quo.design-system.typography :as typography] + [re-frame.core :as re-frame] + [status-im.constants :refer [docs-link]] + [status-im.i18n.i18n :as i18n] [status-im.react-native.resources :as resources] [status-im.ui.components.icons.icons :as icons] - [status-im.ui.components.toolbar :as toolbar] - [status-im.constants :refer [docs-link]] - [re-frame.core :as re-frame] - [quo.design-system.colors :as colors]) + [status-im.ui.components.react :as react] + [status-im.ui.components.toolbar :as toolbar]) (:require-macros [status-im.utils.views :refer [defview letsubs]])) -(defn principles-item [] +(defn principles-item + [] [react/nested-text {} (i18n/label :t/wc-new-tos-based-on-principles-prefix) [{:style (merge {:color colors/blue} @@ -21,18 +22,20 @@ " " (i18n/label :t/principles)]]) -(def changes [[principles-item] - :wc-how-to-use-status-app - :wc-brand-guide - :wc-disclaimer - :wc-dispute]) +(def changes + [[principles-item] + :wc-how-to-use-status-app + :wc-brand-guide + :wc-disclaimer + :wc-dispute]) (defn change-list-item [label] - [react/view {:flex-direction :row - :align-items :center - :margin-horizontal (:base spacing/spacing) - :margin-vertical (:tiny spacing/spacing)} + [react/view + {:flex-direction :row + :align-items :center + :margin-horizontal (:base spacing/spacing) + :margin-vertical (:tiny spacing/spacing)} [icons/icon :main-icons/checkmark-circle {:color colors/blue :container-style {:margin-top 1.2 @@ -42,28 +45,34 @@ [react/text (i18n/label label)] label)]]) -(defview force-accept-tos [] +(defview force-accept-tos + [] (letsubs [next-root [:tos-accept-next-root]] [react/scroll-view - [react/view {:style (merge {:align-items :center} - (:x-large spacing/padding-horizontal))} - [react/image {:source (resources/get-image :status-logo) - :style {:margin-vertical (:base spacing/spacing) - :width 32 - :height 32}}] - [quo/text {:size :x-large - :align :center - :weight :bold - :style {:margin-bottom (:base spacing/spacing)}} + [react/view + {:style (merge {:align-items :center} + (:x-large spacing/padding-horizontal))} + [react/image + {:source (resources/get-image :status-logo) + :style {:margin-vertical (:base spacing/spacing) + :width 32 + :height 32}}] + [quo/text + {:size :x-large + :align :center + :weight :bold + :style {:margin-bottom (:base spacing/spacing)}} (i18n/label :t/updates-to-tos)] - [quo/text {:color :secondary - :align :center} + [quo/text + {:color :secondary + :align :center} (i18n/label :t/updates-to-tos-desc)]] [quo/separator {:style {:margin-top (:base spacing/spacing)}}] [quo/list-item - {:title [quo/text {:color :link - :weight :medium} + {:title [quo/text + {:color :link + :weight :medium} (i18n/label :t/terms-of-service)] :accessibility-label :tos :chevron true @@ -80,21 +89,23 @@ [react/view {:style (:base spacing/padding-horizontal)} [quo/text {:weight :medium} (i18n/label :t/status-is-open-source)] [quo/text {:color :secondary} (i18n/label :t/build-yourself)] - [quo/text {:color :link - :weight :medium - :on-press #(.openURL ^js react/linking docs-link)} + [quo/text + {:color :link + :weight :medium + :on-press #(.openURL ^js react/linking docs-link)} docs-link]] [quo/separator {:style {:margin-vertical (:base spacing/spacing)}}] [toolbar/toolbar - {:size :large + {:size :large :center [react/view {:padding-horizontal 8} - [quo/button {:type :primary - :on-press #(do - (re-frame/dispatch [:hide-terms-of-services-opt-in-screen]) - (re-frame/dispatch [:init-root next-root]))} + [quo/button + {:type :primary + :on-press #(do + (re-frame/dispatch [:hide-terms-of-services-opt-in-screen]) + (re-frame/dispatch [:init-root next-root]))} (i18n/label :t/accept-and-continue)]]}]])) (comment diff --git a/src/status_im/ui/screens/wakuv2_settings/edit_node/styles.cljs b/src/status_im/ui/screens/wakuv2_settings/edit_node/styles.cljs index 9dfab20820..5e6eb22145 100644 --- a/src/status_im/ui/screens/wakuv2_settings/edit_node/styles.cljs +++ b/src/status_im/ui/screens/wakuv2_settings/edit_node/styles.cljs @@ -13,11 +13,11 @@ :margin-horizontal 16}) (styles/def button - {:height 52 - :align-items :center - :justify-content :center - :border-radius 8 - :ios {:opacity 0.9}}) + {:height 52 + :align-items :center + :justify-content :center + :border-radius 8 + :ios {:opacity 0.9}}) (def button-label {:color colors/white-persist @@ -25,4 +25,5 @@ (def delete-button (assoc button - :background-color colors/red)) + :background-color + colors/red)) diff --git a/src/status_im/ui/screens/wakuv2_settings/edit_node/views.cljs b/src/status_im/ui/screens/wakuv2_settings/edit_node/views.cljs index 2f829629ad..f09fe541ef 100644 --- a/src/status_im/ui/screens/wakuv2_settings/edit_node/views.cljs +++ b/src/status_im/ui/screens/wakuv2_settings/edit_node/views.cljs @@ -1,35 +1,37 @@ (ns status-im.ui.screens.wakuv2-settings.edit-node.views (:require [clojure.string :as string] + [quo.core :as quo] [re-frame.core :as re-frame] [status-im.i18n.i18n :as i18n] [status-im.ui.components.react :as react] [status-im.ui.components.toolbar :as toolbar] - [quo.core :as quo] [status-im.ui.components.topbar :as topbar] - [status-im.ui.screens.wakuv2-settings.edit-node.styles - :as - styles]) + [status-im.ui.screens.wakuv2-settings.edit-node.styles :as styles]) (:require-macros [status-im.utils.views :as views])) -(defn delete-button [id] +(defn delete-button + [id] [react/touchable-highlight {:on-press #(re-frame/dispatch [:wakuv2.ui/delete-pressed id])} [react/view styles/button-container - [react/view {:style styles/delete-button - :accessibility-label :wakuv2-delete-button} + [react/view + {:style styles/delete-button + :accessibility-label :wakuv2-delete-button} [react/text {:style styles/button-label} (i18n/label :t/delete)]]]]) -(views/defview edit-node [] - (views/letsubs [manage-node [:wakuv2-nodes/manage] +(views/defview edit-node + [] + (views/letsubs [manage-node [:wakuv2-nodes/manage] validation-errors [:wakuv2-nodes/validation-errors]] - (let [address (get-in manage-node [:address :value]) - id (:id manage-node) - name (get-in manage-node [:name :value]) - new-node? (:new? manage-node) - is-valid? (empty? validation-errors) + (let [address (get-in manage-node [:address :value]) + id (:id manage-node) + name (get-in manage-node [:name :value]) + new-node? (:new? manage-node) + is-valid? (empty? validation-errors) invalid-address? (contains? validation-errors :address)] - [react/keyboard-avoiding-view {:style {:flex 1} - :ignore-offset true} + [react/keyboard-avoiding-view + {:style {:flex 1} + :ignore-offset true} [topbar/topbar {:title (i18n/label (if name :t/node-details :t/add-node))}] [react/scroll-view {:keyboard-should-persist-taps :handled} [react/view styles/edit-node-view @@ -61,8 +63,8 @@ [toolbar/toolbar {:right [quo/button - {:type :secondary - :after :main-icon/next - :disabled (not is-valid?) - :on-press #(re-frame/dispatch [:wakuv2.ui/save-node-pressed])} + {:type :secondary + :after :main-icon/next + :disabled (not is-valid?) + :on-press #(re-frame/dispatch [:wakuv2.ui/save-node-pressed])} (i18n/label :t/save)]}]]))) diff --git a/src/status_im/ui/screens/wakuv2_settings/styles.cljs b/src/status_im/ui/screens/wakuv2_settings/styles.cljs index c351b17b2a..15ee5039e6 100644 --- a/src/status_im/ui/screens/wakuv2_settings/styles.cljs +++ b/src/status_im/ui/screens/wakuv2_settings/styles.cljs @@ -2,7 +2,7 @@ (:require [status-im.utils.styles :as styles])) (def wrapper - {:flex 1}) + {:flex 1}) (def node-item-inner {:padding-horizontal 16}) @@ -15,8 +15,8 @@ :android {:height 56}}) (def node-item-name-text - {:font-size 17}) + {:font-size 17}) (def switch-container - {:height 50 - :padding-left 15}) + {:height 50 + :padding-left 15}) diff --git a/src/status_im/ui/screens/wakuv2_settings/views.cljs b/src/status_im/ui/screens/wakuv2_settings/views.cljs index 3bd52970a1..3855e9309d 100644 --- a/src/status_im/ui/screens/wakuv2_settings/views.cljs +++ b/src/status_im/ui/screens/wakuv2_settings/views.cljs @@ -1,18 +1,20 @@ (ns status-im.ui.screens.wakuv2-settings.views (:require-macros [status-im.utils.views :as views]) - (:require [re-frame.core :as re-frame] + (:require [quo.core :as quo] + [re-frame.core :as re-frame] [status-im.i18n.i18n :as i18n] [status-im.ui.components.list.views :as list] [status-im.ui.components.react :as react] - [status-im.ui.screens.wakuv2-settings.styles :as styles] - [quo.core :as quo] [status-im.ui.components.toolbar :as toolbar] - [status-im.ui.components.topbar :as topbar])) + [status-im.ui.components.topbar :as topbar] + [status-im.ui.screens.wakuv2-settings.styles :as styles])) -(defn navigate-to-add-node [id] +(defn navigate-to-add-node + [id] (re-frame/dispatch [:wakuv2.ui/add-node-pressed id])) -(defn render-row [[id {:keys [name]}]] +(defn render-row + [[id {:keys [name]}]] [react/touchable-highlight {:on-press #(navigate-to-add-node id) :accessibility-label :wakuv2-node-item} @@ -21,30 +23,33 @@ [react/text {:style styles/node-item-name-text} name]]]]) -(views/defview wakuv2-settings [] +(views/defview wakuv2-settings + [] (views/letsubs [nodes [:wakuv2-nodes/list]] [:<> - [topbar/topbar {:title (i18n/label :t/wakuv2-settings) - :navigation :none - :right-accessories - [{:icon :main-icons/add - :accessibility-label :add-wakuv2-node - :on-press #(navigate-to-add-node nil)}]}] + [topbar/topbar + {:title (i18n/label :t/wakuv2-settings) + :navigation :none + :right-accessories + [{:icon :main-icons/add + :accessibility-label :add-wakuv2-node + :on-press #(navigate-to-add-node nil)}]}] [react/view styles/wrapper - [list/flat-list {:data nodes - :default-separator? false - :key-fn :id - :render-fn render-row}]] + [list/flat-list + {:data nodes + :default-separator? false + :key-fn :id + :render-fn render-row}]] [toolbar/toolbar {:left [quo/button - {:type :secondary - :after :main-icon/close - :on-press #(re-frame/dispatch [:wakuv2.ui/discard-all-pressed])} + {:type :secondary + :after :main-icon/close + :on-press #(re-frame/dispatch [:wakuv2.ui/discard-all-pressed])} (i18n/label :t/cancel)] :right [quo/button - {:type :secondary - :after :main-icon/next - :on-press #(re-frame/dispatch [:wakuv2.ui/save-all-pressed])} + {:type :secondary + :after :main-icon/next + :on-press #(re-frame/dispatch [:wakuv2.ui/save-all-pressed])} (i18n/label :t/save)]}]])) diff --git a/src/status_im/ui/screens/wallet/account/styles.cljs b/src/status_im/ui/screens/wallet/account/styles.cljs index e1a4a00ece..758d155e66 100644 --- a/src/status_im/ui/screens/wallet/account/styles.cljs +++ b/src/status_im/ui/screens/wallet/account/styles.cljs @@ -2,7 +2,8 @@ (:require [quo.design-system.colors :as colors] [status-im.ui.components.animation :as animation])) -(defn card [window-width color] +(defn card + [window-width color] {:width (- window-width 30) :height 161 :background-color color @@ -16,7 +17,8 @@ :border-radius 8 :justify-content :space-between}) -(defn divider [] +(defn divider + [] {:height 52 :width 1 :background-color colors/black-transparent-20 @@ -27,20 +29,22 @@ "rgba(0, 0, 0, 0.75)" "rgba(0, 9, 26, 0.12)")}) -(defn bottom-send-recv-buttons-raise [anim-y] +(defn bottom-send-recv-buttons-raise + [anim-y] (animation/timing anim-y - {:toValue 0 - :duration 200 - :easing (.-ease ^js animation/easing) + {:toValue 0 + :duration 200 + :easing (.-ease ^js animation/easing) :useNativeDriver true})) -(defn bottom-send-recv-buttons-lower [anim-y y] +(defn bottom-send-recv-buttons-lower + [anim-y y] (animation/timing anim-y - {:toValue y - :duration 200 - :easing (.-ease ^js animation/easing) + {:toValue y + :duration 200 + :easing (.-ease ^js animation/easing) :useNativeDriver true})) (def round-action-button diff --git a/src/status_im/ui/screens/wallet/account/views.cljs b/src/status_im/ui/screens/wallet/account/views.cljs index d3253f51f5..299ef2e788 100644 --- a/src/status_im/ui/screens/wallet/account/views.cljs +++ b/src/status_im/ui/screens/wallet/account/views.cljs @@ -1,34 +1,35 @@ (ns status-im.ui.screens.wallet.account.views - (:require [re-frame.core :as re-frame] + (:require [quo.core :as quo] + [quo.design-system.colors :as colors] + [quo.design-system.spacing :as spacing] + [quo2.components.buttons.button :as quo2.button] + [quo2.components.markdown.text :as quo2.text] + [quo2.components.tabs.tabs :as quo2.tabs] + [quo2.foundations.colors :as quo2.colors] + [re-frame.core :as re-frame] [reagent.core :as reagent] [status-im.ethereum.core :as ethereum] [status-im.i18n.i18n :as i18n] [status-im.ui.components.animation :as animation] - [quo.design-system.colors :as colors] [status-im.ui.components.icons.icons :as icons] - [quo.core :as quo] - [quo.design-system.spacing :as spacing] [status-im.ui.components.react :as react] - [status-im.ui.components.topbar :as topbar] - [status-im.utils.config :as config] - [status-im.ui.screens.wallet.account.styles :as styles] - [status-im.ui.screens.wallet.accounts.sheets :as sheets] - [status-im.ui.screens.wallet.accounts.common :as common] - [status-im.ui.screens.wallet.transactions.views :as history] [status-im.ui.components.tabs :as tabs] - [status-im.ui.screens.wallet.collectibles.views :as collectibles.views] + [status-im.ui.components.topbar :as topbar] + [status-im.ui.screens.wallet.account.styles :as styles] + [status-im.ui.screens.wallet.accounts.common :as common] + [status-im.ui.screens.wallet.accounts.sheets :as sheets] [status-im.ui.screens.wallet.buy-crypto.views :as buy-crypto] - [quo2.foundations.colors :as quo2.colors] - [status-im.utils.handlers :refer [ [topbar/topbar - {:title name + {:title name :right-accessories [{:icon :main-icons/more :on-press #(re-frame/dispatch [:bottom-sheet/show-sheet @@ -303,9 +329,10 @@ @common/updates-counter @(re-frame/subscribe [:wallet/refreshing-history?])))} (when fetching-error - [react/view {:style {:flex 1 - :align-items :center - :margin 8}} + [react/view + {:style {:flex 1 + :align-items :center + :margin 8}} [icons/icon :main-icons/warning {:color :red diff --git a/src/status_im/ui/screens/wallet/account_settings/views.cljs b/src/status_im/ui/screens/wallet/account_settings/views.cljs index 7eb600a23f..3d05c73f81 100644 --- a/src/status_im/ui/screens/wallet/account_settings/views.cljs +++ b/src/status_im/ui/screens/wallet/account_settings/views.cljs @@ -1,24 +1,26 @@ (ns status-im.ui.screens.wallet.account-settings.views (:require-macros [status-im.utils.views :refer [defview letsubs]]) - (:require [status-im.ui.components.react :as react] - [re-frame.core :as re-frame] - [status-im.i18n.i18n :as i18n] - [status-im.ui.components.icons.icons :as icons] + (:require [quo.core :as quo] [quo.design-system.colors :as colors] - [status-im.ui.components.toolbar :as toolbar] - [status-im.ui.components.copyable-text :as copyable-text] + [re-frame.core :as re-frame] [reagent.core :as reagent] - [quo.core :as quo] + [status-im.i18n.i18n :as i18n] + [status-im.ui.components.copyable-text :as copyable-text] + [status-im.ui.components.icons.icons :as icons] + [status-im.ui.components.react :as react] + [status-im.ui.components.toolbar :as toolbar] [status-im.ui.components.topbar :as topbar] [utils.security.core :as security])) -(defn not-valid-password? [password] +(defn not-valid-password? + [password] (< (count (security/safe-unmask-data password)) 6)) -(defn delete-account [_] - (let [password (reagent/atom nil) +(defn delete-account + [_] + (let [password (reagent/atom nil) text-input-ref (atom nil) - error (reagent/atom nil)] + error (reagent/atom nil)] (fn [account] (when (and @text-input-ref error (not @password)) (.clear ^js @text-input-ref)) @@ -37,18 +39,20 @@ (if (= :wrong-password @error) (i18n/label :t/wrong-password) (str @error)))}] - [quo/button {:on-press (fn [] - (re-frame/dispatch [:wallet.accounts/delete-key - account - @password - #(reset! error :wrong-password)]) - (reset! password nil)) - :theme :negative - :accessibility-label :delete-account-confirm - :disabled (not-valid-password? @password)} + [quo/button + {:on-press (fn [] + (re-frame/dispatch [:wallet.accounts/delete-key + account + @password + #(reset! error :wrong-password)]) + (reset! password nil)) + :theme :negative + :accessibility-label :delete-account-confirm + :disabled (not-valid-password? @password)} (i18n/label :t/delete)]]))) -(defview colors-popover [selected-color on-press] +(defview colors-popover + [selected-color on-press] (letsubs [width [:dimensions/window-width]] [react/view {:flex 1 :padding-bottom 16} [react/scroll-view {:style {:margin 16}} @@ -56,43 +60,60 @@ (for [color colors/account-colors] ^{:key color} [react/touchable-highlight {:on-press #(on-press color)} - [react/view {:height 52 :background-color color :border-radius 8 :width (* 0.7 width) - :justify-content :center :padding-left 12 :margin-bottom 16} - [react/view {:height 32 :width 32 :border-radius 20 :align-items :center :justify-content :center - :background-color colors/black-transparent} + [react/view + {:height 52 + :background-color color + :border-radius 8 + :width (* 0.7 width) + :justify-content :center + :padding-left 12 + :margin-bottom 16} + [react/view + {:height 32 + :width 32 + :border-radius 20 + :align-items :center + :justify-content :center + :background-color colors/black-transparent} (when (= selected-color color) [icons/icon :main-icons/check {:color colors/white}])]]]))] [toolbar/toolbar {:center - [quo/button {:on-press #(re-frame/dispatch [:hide-popover]) - :type :secondary} + [quo/button + {:on-press #(re-frame/dispatch [:hide-popover]) + :type :secondary} (i18n/label :t/cancel)]}]])) -(defn property [label value] +(defn property + [label value] [react/view {:margin-top 28} [react/text {:style {:color colors/gray}} label] (if (string? value) [react/text {:style {:margin-top 6}} value] value)]) -(defview account-settings [] +(defview account-settings + [] (letsubs [{:keys [address color path type] :as account} [:multiaccount/current-account] - new-account (reagent/atom nil) - keycard? [:keycard-multiaccount?]] - [react/keyboard-avoiding-view {:style {:flex 1} - :ignore-offset true} + new-account (reagent/atom nil) + keycard? [:keycard-multiaccount?]] + [react/keyboard-avoiding-view + {:style {:flex 1} + :ignore-offset true} [topbar/topbar (cond-> {:title (i18n/label :t/account-settings)} (and @new-account (not= "" (:name @new-account))) - (assoc :right-accessories [{:label (i18n/label :t/apply) - :on-press - #(do - (re-frame/dispatch [:wallet.accounts/save-account - account - @new-account]) - (reset! new-account nil))}]))] - [react/scroll-view {:keyboard-should-persist-taps :handled - :style {:flex 1}} + (assoc :right-accessories + [{:label (i18n/label :t/apply) + :on-press + #(do + (re-frame/dispatch [:wallet.accounts/save-account + account + @new-account]) + (reset! new-account nil))}]))] + [react/scroll-view + {:keyboard-should-persist-taps :handled + :style {:flex 1}} [react/view {:padding-bottom 28 :padding-top 10} [react/view {:margin-horizontal 16} [quo/text-input @@ -109,27 +130,34 @@ (swap! new-account assoc :color new-color) (re-frame/dispatch [:hide-popover]))] :style {:max-height "60%"}}])} - [react/view {:height 52 :margin-top 12 :background-color (or (:color @new-account) color) - :border-radius 8 - :align-items :flex-end :justify-content :center :padding-right 12} + [react/view + {:height 52 + :margin-top 12 + :background-color (or (:color @new-account) color) + :border-radius 8 + :align-items :flex-end + :justify-content :center + :padding-right 12} [icons/icon :main-icons/dropdown {:color colors/white}]]] [property (i18n/label :t/type) (case type - :watch (i18n/label :t/watch-only) + :watch (i18n/label :t/watch-only) (:key :seed) (i18n/label :t/off-status-tree) (i18n/label :t/on-status-tree))] [property (i18n/label :t/wallet-address) [copyable-text/copyable-text-view {:copied-text address} - [quo/text {:style {:margin-top 6} - :monospace true} + [quo/text + {:style {:margin-top 6} + :monospace true} address]]] (when-not (= type :watch) [property (i18n/label :t/derivation-path) [copyable-text/copyable-text-view {:copied-text path} - [quo/text {:style {:margin-top 6} - :monospace true} path]]]) + [quo/text + {:style {:margin-top 6} + :monospace true} path]]]) (when-not (= type :watch) [property (i18n/label :t/storage) (i18n/label (if keycard? diff --git a/src/status_im/ui/screens/wallet/accounts/common.cljs b/src/status_im/ui/screens/wallet/accounts/common.cljs index bebd27dd90..46794f3e62 100644 --- a/src/status_im/ui/screens/wallet/accounts/common.cljs +++ b/src/status_im/ui/screens/wallet/accounts/common.cljs @@ -1,21 +1,22 @@ (ns status-im.ui.screens.wallet.accounts.common (:require [quo.core :as quo] - [status-im.wallet.utils :as wallet.utils] - [status-im.ui.screens.wallet.components.views :as wallet.components] - [status-im.ui.components.chat-icon.screen :as chat-icon] - [reagent.core :as reagent] [quo.react-native :as rn] - [status-im.utils.utils :as utils.utils] - [re-frame.core :as re-frame] + [quo2.components.markdown.text :as quo2.text] [quo2.foundations.colors :as quo2.colors] - [quo2.components.markdown.text :as quo2.text])) + [re-frame.core :as re-frame] + [reagent.core :as reagent] + [status-im.ui.components.chat-icon.screen :as chat-icon] + [status-im.ui.screens.wallet.components.views :as wallet.components] + [status-im.utils.utils :as utils.utils] + [status-im.wallet.utils :as wallet.utils])) ;; Note(rasom): sometimes `refreshing` might get stuck on iOS if action happened ;; too fast. By updating this atom in 1s we ensure that `refreshing?` property ;; is updated properly in this case. (def updates-counter (reagent/atom 0)) -(defn schedule-counter-reset [] +(defn schedule-counter-reset + [] (utils.utils/set-timeout (fn [] (swap! updates-counter inc) @@ -23,17 +24,20 @@ (schedule-counter-reset))) 1000)) -(defn refresh-action [] +(defn refresh-action + [] (schedule-counter-reset) (re-frame/dispatch [:wallet.ui/pull-to-refresh-history])) -(defn refresh-control [refreshing?] +(defn refresh-control + [refreshing?] (reagent/as-element [rn/refresh-control {:refreshing (boolean refreshing?) :onRefresh refresh-action}])) -(defn render-asset [{:keys [icon decimals amount color value] :as token} _ _ currency] +(defn render-asset + [{:keys [icon decimals amount color value] :as token} _ _ currency] [quo/list-item {:title [quo/text {:weight :medium} [quo/text {:weight :inherit} @@ -41,8 +45,9 @@ (wallet.utils/format-amount amount decimals) "...") " ")] - [quo/text {:color :secondary - :weight :inherit} + [quo/text + {:color :secondary + :weight :inherit} (wallet.utils/display-symbol token)]] :subtitle (str (if value value "...") " " currency) :accessibility-label (str (:symbol token) "-asset-value") @@ -50,8 +55,9 @@ [wallet.components/token-icon icon] [chat-icon/custom-icon-view-list (:name token) color])}]) -(defn render-asset-new [{:keys [icon decimals amount color value name] :as token} _ _ currency] - [rn/view {:height 56 :margin-horizontal 8 :margin-top 4} +(defn render-asset-new + [{:keys [icon decimals amount color value name] :as token} _ _ currency] + [rn/view {:height 56 :margin-horizontal 8 :margin-top 4} [rn/view {:position :absolute :left 12 :top 12} (if icon [wallet.components/token-icon (merge icon {:width 32 :height 32})] @@ -62,9 +68,10 @@ name] [quo2.text/text {:size :paragraph-2 :weight :medium} (str (if value value "...") " " currency)]] - [quo2.text/text {:size :paragraph-2 - :weight :medium - :style {:color (quo2.colors/theme-colors quo2.colors/neutral-50 quo2.colors/neutral-40)}} + [quo2.text/text + {:size :paragraph-2 + :weight :medium + :style {:color (quo2.colors/theme-colors quo2.colors/neutral-50 quo2.colors/neutral-40)}} (str (if amount (wallet.utils/format-amount amount decimals) "...") @@ -77,8 +84,9 @@ (wallet.utils/format-amount amount decimals) "...") " ")] - [quo/text {:color :secondary - :weight :inherit} + [quo/text + {:color :secondary + :weight :inherit} (wallet.utils/display-symbol token)]] :subtitle (str (if value value "...") " " currency) :accessibility-label (str (:symbol token) "-asset-value") diff --git a/src/status_im/ui/screens/wallet/accounts/sheets.cljs b/src/status_im/ui/screens/wallet/accounts/sheets.cljs index 6b310a527d..11293d92c6 100644 --- a/src/status_im/ui/screens/wallet/accounts/sheets.cljs +++ b/src/status_im/ui/screens/wallet/accounts/sheets.cljs @@ -1,14 +1,16 @@ (ns status-im.ui.screens.wallet.accounts.sheets - (:require [re-frame.core :as re-frame] + (:require [quo.core :as quo] + [re-frame.core :as re-frame] [status-im.i18n.i18n :as i18n] - [status-im.ui.components.react :as react] - [quo.core :as quo])) + [status-im.ui.components.react :as react])) -(defn hide-sheet-and-dispatch [event] +(defn hide-sheet-and-dispatch + [event] (re-frame/dispatch [:bottom-sheet/hide]) (re-frame/dispatch event)) -(defn accounts-options [mnemonic] +(defn accounts-options + [mnemonic] (fn [] [:<> [quo/list-item @@ -61,7 +63,8 @@ :on-press #(hide-sheet-and-dispatch [:navigate-to :backup-seed])}])])) -(defn account-card-actions [account type wallet] +(defn account-card-actions + [account type wallet] [react/view (when-not (= type :watch) [quo/list-item @@ -87,7 +90,8 @@ :on-press #(hide-sheet-and-dispatch [:wallet.accounts/save-account account {:hidden true}])}])]) -(defn add-account [] +(defn add-account + [] (let [keycard? @(re-frame/subscribe [:keycard-multiaccount?])] [react/view [quo/list-item @@ -125,7 +129,8 @@ [:wallet.accounts/start-adding-new-account {:type :key}])}])])) -(defn account-settings [] +(defn account-settings + [] [react/view [quo/list-item {:theme :accent diff --git a/src/status_im/ui/screens/wallet/accounts/styles.cljs b/src/status_im/ui/screens/wallet/accounts/styles.cljs index a996e41601..2c0372a606 100644 --- a/src/status_im/ui/screens/wallet/accounts/styles.cljs +++ b/src/status_im/ui/screens/wallet/accounts/styles.cljs @@ -10,12 +10,14 @@ :flex-wrap :wrap :padding-horizontal 8}) -(defn dot-selector [] +(defn dot-selector + [] {:flex-direction :row :justify-content :space-between :align-items :center}) -(defn dot-style [selected] +(defn dot-style + [selected] {:background-color (if selected colors/blue colors/blue-light) :overflow :hidden :opacity 1 @@ -24,21 +26,25 @@ :height dot-size :border-radius 3}) -(defn container [{:keys [minimized]}] +(defn container + [{:keys [minimized]}] (when-not minimized {:padding-bottom 8 :padding-horizontal 16})) -(defn value-container [{:keys [minimized animation]}] +(defn value-container + [{:keys [minimized animation]}] (when minimized {:opacity animation})) -(defn value-text [{:keys [minimized]}] +(defn value-text + [{:keys [minimized]}] {:font-size (if minimized 20 32) :line-height 40 :color colors/black}) -(defn accounts-mnemonic [{:keys [animation]}] +(defn accounts-mnemonic + [{:keys [animation]}] {:opacity (animated/mix animation 1 0) :flex 1 :justify-content :center @@ -48,16 +54,19 @@ :left 0}) (def card-margin 8) -(defn page-width [card-width] +(defn page-width + [card-width] (+ card-width (* card-margin 2))) -(defn card-common [card-width] - {:margin card-margin - :width card-width - :height 82 - :border-radius 16}) +(defn card-common + [card-width] + {:margin card-margin + :width card-width + :height 82 + :border-radius 16}) -(defn card [color card-width] +(defn card + [color card-width] (merge (card-common card-width) {:background-color color :justify-content :space-between @@ -65,64 +74,65 @@ :padding-top 12 :padding-bottom 10})) -(defn add-card [card-width] +(defn add-card + [card-width] (merge (card-common card-width) {:background-color colors/white - :flex-direction :row + :flex-direction :row :border-width 1 :border-color colors/gray-lighter :justify-content :center :align-items :center})) (def add-text - {:color colors/blue + {:color colors/blue :margin-left 8 :font-weight "500" - :font-size 15 + :font-size 15 :line-height 22}) (def card-name - {:color colors/white-persist + {:color colors/white-persist :font-weight "500" - :font-size 15 + :font-size 15 :line-height 22}) (def card-address {:number-of-lines 1 - :ellipsize-mode :middle - :size :small - :monospace true - :style {:width 110 - :font-size 15 - :line-height 22 - :color colors/white-transparent-70-persist}}) + :ellipsize-mode :middle + :size :small + :monospace true + :style {:width 110 + :font-size 15 + :line-height 22 + :color colors/white-transparent-70-persist}}) (def card-value - {:color colors/white-persist - :font-size 22 + {:color colors/white-persist + :font-size 22 :font-weight "500"}) (def card-value-currency - {:color colors/white-persist - :font-size 22 + {:color colors/white-persist + :font-size 22 :font-weight "500"}) (def card-icon-more - {:border-radius 32 - :width 36 - :height 36 - :justify-content :center - :align-items :center + {:border-radius 32 + :width 36 + :height 36 + :justify-content :center + :align-items :center :background-color colors/black-transparent}) (def card-icon-type - {:border-radius 32 - :width 36 - :height 36 - :justify-content :center - :align-items :center - :margin-left :auto - :margin-right 12 + {:border-radius 32 + :width 36 + :height 36 + :justify-content :center + :align-items :center + :margin-left :auto + :margin-right 12 :background-color colors/white-persist}) (def send-button-container @@ -135,7 +145,8 @@ :bottom 16 :height 40}) -(defn send-button [] +(defn send-button + [] {:width 40 :height 40 :background-color colors/blue diff --git a/src/status_im/ui/screens/wallet/accounts/views.cljs b/src/status_im/ui/screens/wallet/accounts/views.cljs index d3f4702355..868bca7754 100644 --- a/src/status_im/ui/screens/wallet/accounts/views.cljs +++ b/src/status_im/ui/screens/wallet/accounts/views.cljs @@ -1,27 +1,28 @@ (ns status-im.ui.screens.wallet.accounts.views (:require [quo.animated :as reanimated] [quo.core :as quo] - [re-frame.core :as re-frame] - [reagent.core :as reagent] - [status-im.i18n.i18n :as i18n] - [status-im.ui.screens.wallet.buy-crypto.views :as buy-crypto] [quo.design-system.colors :as colors] - [status-im.ui.components.icons.icons :as icons] - [status-im.ui.components.react :as react] - [status-im.ui.screens.wallet.accounts.sheets :as sheets] - [status-im.ui.screens.wallet.accounts.styles :as styles] - [status-im.qr-scanner.core :as qr-scanner] - [status-im.keycard.login :as keycard.login] - [quo2.foundations.colors :as quo2.colors] [quo2.components.buttons.button :as quo2.button] [quo2.components.markdown.text :as quo2.text] [quo2.components.tabs.tabs :as quo2.tabs] + [quo2.foundations.colors :as quo2.colors] + [re-frame.core :as re-frame] + [reagent.core :as reagent] + [status-im.i18n.i18n :as i18n] + [status-im.keycard.login :as keycard.login] + [status-im.qr-scanner.core :as qr-scanner] + [status-im.ui.components.icons.icons :as icons] + [status-im.ui.components.react :as react] + [status-im.ui.screens.wallet.account.views :as account.views] [status-im.ui.screens.wallet.accounts.common :as common] - [status-im.ui.screens.wallet.account.views :as account.views]) + [status-im.ui.screens.wallet.accounts.sheets :as sheets] + [status-im.ui.screens.wallet.accounts.styles :as styles] + [status-im.ui.screens.wallet.buy-crypto.views :as buy-crypto]) (:require-macros [status-im.utils.views :as views])) -(views/defview account-card [{:keys [name color address type wallet] :as account} keycard? card-width] - (views/letsubs [currency [:wallet/currency] +(views/defview account-card + [{:keys [name color address type wallet] :as account} keycard? card-width] + (views/letsubs [currency [:wallet/currency] portfolio-value [:account-portfolio-value address] prices-loading? [:prices-loading?]] [react/touchable-highlight @@ -36,8 +37,9 @@ [react/view {:style {:flex-direction :row}} (if prices-loading? [react/small-loading-indicator :colors/white-persist] - [react/text {:style styles/card-value - :accessibility-label "account-total-value"} portfolio-value]) + [react/text + {:style styles/card-value + :accessibility-label "account-total-value"} portfolio-value]) [react/text {:style styles/card-value-currency} (str " " (:code currency))]] (let [icon (cond (= type :watch) @@ -46,33 +48,39 @@ (and (not= type :watch) keycard?) :main-icons/keycard-account)] (when icon - [icons/icon icon {:container-style styles/card-icon-type - :color color}])) + [icons/icon icon + {:container-style styles/card-icon-type + :color color}])) [react/touchable-highlight {:style styles/card-icon-more - :on-press #(re-frame/dispatch [:bottom-sheet/show-sheet - {:content (fn [] [sheets/account-card-actions account type wallet]) - :content-height 130}])} + :on-press #(re-frame/dispatch + [:bottom-sheet/show-sheet + {:content (fn [] [sheets/account-card-actions account type wallet]) + :content-height 130}])} [icons/icon :main-icons/more {:color colors/white-persist}]]]]])) -(defn add-card [card-width] - [react/touchable-highlight {:on-press #(re-frame/dispatch [:bottom-sheet/show-sheet - {:content sheets/add-account - :content-height 260}]) - :accessibility-label "add-new-account"} +(defn add-card + [card-width] + [react/touchable-highlight + {:on-press #(re-frame/dispatch [:bottom-sheet/show-sheet + {:content sheets/add-account + :content-height 260}]) + :accessibility-label "add-new-account"} [react/view {:style (styles/add-card card-width)} [icons/icon :main-icons/add-circle {:color colors/blue}] [react/text {:style styles/add-text} (i18n/label :t/add-account)]]]) -(views/defview assets [] +(views/defview assets + [] (views/letsubs [{:keys [tokens]} [:wallet/all-visible-assets-with-values] - currency [:wallet/currency]] + currency [:wallet/currency]] [:<> (for [item tokens] ^{:key (:name item)} [common/render-asset item nil nil (:code currency)])])) -(views/defview send-button [] +(views/defview send-button + [] (views/letsubs [account [:multiaccount/default-account]] [react/view styles/send-button-container [quo/button @@ -82,54 +90,62 @@ [react/view (styles/send-button) [icons/icon :main-icons/send {:color colors/white-persist}]]]])) -(defn dot [] +(defn dot + [] (fn [{:keys [selected]}] [react/view {:style (styles/dot-style selected)}])) -(defn dots-selector [{:keys [n selected]}] +(defn dots-selector + [{:keys [n selected]}] [react/view {:style (styles/dot-selector)} (for [i (range n)] ^{:key i} [dot {:selected (= selected i)}])]) ;;ACCOUNTS OLD -(views/defview accounts-old [] - (views/letsubs [accounts [:multiaccount/visible-accounts] - keycard? [:keycard-multiaccount?] +(views/defview accounts-old + [] + (views/letsubs [accounts [:multiaccount/visible-accounts] + keycard? [:keycard-multiaccount?] window-width [:dimensions/window-width] - index (reagent/atom 0)] + index (reagent/atom 0)] (let [card-width (quot window-width 1.1) page-width (styles/page-width card-width)] - [react/view {:style {:align-items :center - :flex 1 - :justify-content :flex-end}} - [react/scroll-view {:horizontal true - :deceleration-rate "fast" - :snap-to-interval page-width - :snap-to-alignment "left" - :shows-horizontal-scroll-indicator false - :scroll-event-throttle 64 - :on-scroll #(let [x (.-nativeEvent.contentOffset.x ^js %)] - (reset! index (Math/max (Math/round (/ x page-width)) 0)))} + [react/view + {:style {:align-items :center + :flex 1 + :justify-content :flex-end}} + [react/scroll-view + {:horizontal true + :deceleration-rate "fast" + :snap-to-interval page-width + :snap-to-alignment "left" + :shows-horizontal-scroll-indicator false + :scroll-event-throttle 64 + :on-scroll #(let [x (.-nativeEvent.contentOffset.x ^js %)] + (reset! index (Math/max (Math/round (/ x page-width)) + 0)))} [react/view styles/dot-container (doall (for [account accounts] ^{:key account} [account-card account keycard? card-width])) [add-card card-width]]] - (let [columns (Math/ceil (/ (inc (count accounts)) 2)) + (let [columns (Math/ceil (/ (inc (count accounts)) 2)) totalwidth (* (styles/page-width card-width) columns) - n (Math/ceil (/ totalwidth window-width))] + n (Math/ceil (/ totalwidth window-width))] (when (> n 1) - [dots-selector {:selected @index - :n n}]))]))) + [dots-selector + {:selected @index + :n n}]))]))) ;;TOTAL VALUE OLD -(views/defview total-value-old [{:keys [animation minimized]}] - (views/letsubs [currency [:wallet/currency] - portfolio-value [:portfolio-value] - empty-balances? [:empty-balances?] - frozen-card? [:keycard/frozen-card?] +(views/defview total-value-old + [{:keys [animation minimized]}] + (views/letsubs [currency [:wallet/currency] + portfolio-value [:portfolio-value] + empty-balances? [:empty-balances?] + frozen-card? [:keycard/frozen-card?] {:keys [mnemonic]} [:multiaccount]] [reanimated/view {:style (styles/container {:minimized minimized})} (when (or @@ -141,49 +157,57 @@ (if frozen-card? [::keycard.login/reset-pin] [:navigate-to :backup-seed]))} - [react/view {:flex-direction :row - :align-items :center} - [react/view {:width 14 - :height 14 - :background-color colors/gray - :border-radius 7 - :align-items :center - :justify-content :center - :margin-right 9} - [react/text {:style {:color colors/white - :font-size 13 - :font-weight "700"}} + [react/view + {:flex-direction :row + :align-items :center} + [react/view + {:width 14 + :height 14 + :background-color colors/gray + :border-radius 7 + :align-items :center + :justify-content :center + :margin-right 9} + [react/text + {:style {:color colors/white + :font-size 13 + :font-weight "700"}} "!"]] - [react/text {:style {:color colors/gray} - :accessibility-label :back-up-your-seed-phrase-warning} + [react/text + {:style {:color colors/gray} + :accessibility-label :back-up-your-seed-phrase-warning} (if frozen-card? (i18n/label :t/your-card-is-frozen) (i18n/label :t/back-up-your-seed-phrase))]]]]) - [reanimated/view {:style (styles/value-container {:minimized minimized - :animation animation}) - :pointer-events :none} + [reanimated/view + {:style (styles/value-container {:minimized minimized + :animation animation}) + :pointer-events :none} [reanimated/view {:style {:justify-content :center}} - [quo/text {:animated? true - :weight :semi-bold - :style (styles/value-text {:minimized minimized})} + [quo/text + {:animated? true + :weight :semi-bold + :style (styles/value-text {:minimized minimized})} portfolio-value - [quo/text {:animated? true - :size :inherit - :weight :inherit - :color :secondary} + [quo/text + {:animated? true + :size :inherit + :weight :inherit + :color :secondary} (str " " (:code currency))]]]] (when-not minimized [reanimated/view [quo/text {:color :secondary} (i18n/label :t/wallet-total-value)]])])) -(views/defview total-value [] - (views/letsubs [currency [:wallet/currency] +(views/defview total-value + [] + (views/letsubs [currency [:wallet/currency] portfolio-value [:portfolio-value]] - ;empty-balances? [:empty-balances?] - ;frozen-card? [:keycard/frozen-card?] - ;{:keys [mnemonic]} [:multiaccount]] + ;empty-balances? [:empty-balances?] + ;frozen-card? [:keycard/frozen-card?] + ;{:keys [mnemonic]} [:multiaccount]] [react/view {:padding-vertical 12} [quo2.text/text (i18n/label :t/wallet-total-value)] [quo2.text/text {:size :heading-1 :weight :semi-bold} @@ -199,97 +223,114 @@ (if frozen-card? [::keycard.login/reset-pin] [:navigate-to :backup-seed]))} - [react/view {:flex-direction :row - :align-items :center} - [react/view {:width 14 - :height 14 - :background-color colors/gray - :border-radius 7 - :align-items :center - :justify-content :center - :margin-right 9} - [react/text {:style {:color colors/white - :font-size 13 - :font-weight "700"}} + [react/view + {:flex-direction :row + :align-items :center} + [react/view + {:width 14 + :height 14 + :background-color colors/gray + :border-radius 7 + :align-items :center + :justify-content :center + :margin-right 9} + [react/text + {:style {:color colors/white + :font-size 13 + :font-weight "700"}} "!"]] - [react/text {:style {:color colors/gray} - :accessibility-label :back-up-your-seed-phrase-warning} + [react/text + {:style {:color colors/gray} + :accessibility-label :back-up-your-seed-phrase-warning} (if frozen-card? (i18n/label :t/your-card-is-frozen) (i18n/label :t/back-up-your-seed-phrase))]]]]) - [reanimated/view {:style (styles/value-container {:minimized minimized - :animation animation}) - :pointer-events :none} + [reanimated/view + {:style (styles/value-container {:minimized minimized + :animation animation}) + :pointer-events :none} [reanimated/view {:style {:justify-content :center}} - [quo/text {:animated? true - :weight :semi-bold - :style (styles/value-text {:minimized minimized})} + [quo/text + {:animated? true + :weight :semi-bold + :style (styles/value-text {:minimized minimized})} portfolio-value - [quo/text {:animated? true - :size :inherit - :weight :inherit - :color :secondary} + [quo/text + {:animated? true + :size :inherit + :weight :inherit + :color :secondary} (str " " (:code currency))]]]] (when-not minimized [reanimated/view [quo/text {:color :secondary} (i18n/label :t/wallet-total-value)]])])) -(views/defview accounts [selected-account-atom] +(views/defview accounts + [selected-account-atom] (views/letsubs [accounts [:multiaccount/visible-accounts]] - ;keycard? [:keycard-multiaccount?]] + ;keycard? [:keycard-multiaccount?]] (do (reset! selected-account-atom (:address (first accounts))) (let [accounts-data (for [account accounts] {:label (:name account) :id (:address account)})] - [react/scroll-view {:horizontal true - :shows-horizontal-scroll-indicator false - :scroll-event-throttle 64 - :margin-top 12 - :margin-bottom 20} + [react/scroll-view + {:horizontal true + :shows-horizontal-scroll-indicator false + :scroll-event-throttle 64 + :margin-top 12 + :margin-bottom 20} [react/view {:flex-direction :row} - [quo2.tabs/tabs {:default-active (:address (first accounts)) - :on-change #(reset! selected-account-atom %) - :data accounts-data}] - [quo2.button/button {:type :grey - :size 32 - :on-press #(re-frame/dispatch [:bottom-sheet/show-sheet - {:content sheets/add-account - :content-height 260}])} + [quo2.tabs/tabs + {:default-active (:address (first accounts)) + :on-change #(reset! selected-account-atom %) + :data accounts-data}] + [quo2.button/button + {:type :grey + :size 32 + :on-press #(re-frame/dispatch [:bottom-sheet/show-sheet + {:content sheets/add-account + :content-height 260}])} "Add account"]]])))) -(defn accounts-overview [] - (let [mnemonic @(re-frame/subscribe [:mnemonic]) +(defn accounts-overview + [] + (let [mnemonic @(re-frame/subscribe [:mnemonic]) ;mainnet? @(re-frame/subscribe [:mainnet?]) selected-account-atom (reagent/atom nil)] (fn [] - [react/view {:style {:flex 1 - :background-color (quo2.colors/theme-colors quo2.colors/neutral-5 quo2.colors/neutral-95)}} + [react/view + {:style {:flex 1 + :background-color (quo2.colors/theme-colors quo2.colors/neutral-5 + quo2.colors/neutral-95)}} [react/view {:padding-horizontal 20} [react/view {:flex-direction :row :height 56 :align-items :center :justify-content :flex-end} - [quo2.button/button {:icon true - :size 32 - :type :grey - :accessibility-label :accounts-qr-code - :on-press #(re-frame/dispatch - [::qr-scanner/scan-code - {:handler :wallet.send/qr-scanner-result}])} + [quo2.button/button + {:icon true + :size 32 + :type :grey + :accessibility-label :accounts-qr-code + :on-press #(re-frame/dispatch + [::qr-scanner/scan-code + {:handler :wallet.send/qr-scanner-result}])} :i/placeholder] [react/view {:width 12}] - [quo2.button/button {:icon true - :size 32 - :type :grey - :on-press #(re-frame/dispatch [:bottom-sheet/show-sheet - {:content (sheets/accounts-options mnemonic)}]) - :accessibility-label :accounts-more-options} + [quo2.button/button + {:icon true + :size 32 + :type :grey + :on-press #(re-frame/dispatch [:bottom-sheet/show-sheet + {:content (sheets/accounts-options mnemonic)}]) + :accessibility-label :accounts-more-options} :i/placeholder]] [total-value] [accounts selected-account-atom]] [account.views/account-new @selected-account-atom]]))) -(defn accounts-overview-old [] +(defn accounts-overview-old + [] (let [mnemonic @(re-frame/subscribe [:mnemonic]) mainnet? @(re-frame/subscribe [:mainnet?])] [react/view @@ -306,7 +347,8 @@ :icon :main-icons/qr :accessibility-label :accounts-qr-code} {:on-press #(re-frame/dispatch [:bottom-sheet/show-sheet - {:content (sheets/accounts-options mnemonic)}]) + {:content (sheets/accounts-options + mnemonic)}]) :icon :main-icons/more :accessibility-label :accounts-more-options}]} [accounts-old] diff --git a/src/status_im/ui/screens/wallet/accounts_manage/views.cljs b/src/status_im/ui/screens/wallet/accounts_manage/views.cljs index 1f6248f7ba..37a5e1d697 100644 --- a/src/status_im/ui/screens/wallet/accounts_manage/views.cljs +++ b/src/status_im/ui/screens/wallet/accounts_manage/views.cljs @@ -1,13 +1,14 @@ (ns status-im.ui.screens.wallet.accounts-manage.views - (:require [status-im.utils.handlers :refer [>evt evt]] + [status-im.utils.utils :as utils])) -(defn render-account [_] +(defn render-account + [_] (reagent/create-class {:should-component-update (fn [_ [_ old-item] [_ new-item]] @@ -26,7 +27,8 @@ :subtitle (utils/get-shortened-checksum-address address) :on-press #(>evt [:wallet.accounts/save-account account {:hidden (not hidden)}])}])})) -(defn manage [] +(defn manage + [] (let [accounts (