From f542b19c9233db6a33423c531d9f80201108d806 Mon Sep 17 00:00:00 2001 From: chrisnojima Date: Fri, 13 Feb 2026 10:22:33 -0500 Subject: [PATCH 001/112] version 670 --- go/libkb/version.go | 2 +- shared/android/app/build.gradle | 2 +- shared/app/global-errors.tsx | 332 ++ shared/app/global-errors/hook.tsx | 103 - shared/app/global-errors/index.d.ts | 3 - shared/app/global-errors/index.desktop.tsx | 185 -- shared/app/global-errors/index.native.tsx | 105 - shared/app/index.native.tsx | 18 +- shared/app/main.desktop.tsx | 2 +- shared/app/out-of-date.tsx | 4 +- shared/app/runtime-stats.tsx | 2 +- shared/chat/audio/audio-recorder.native.tsx | 2 +- shared/chat/blocking/block-modal.tsx | 6 +- shared/chat/blocking/invitation-to-block.tsx | 6 +- shared/chat/chat-button.tsx | 4 +- .../attachment-fullscreen/hooks.tsx | 6 +- .../conversation/attachment-get-titles.tsx | 2 +- shared/chat/conversation/bot/confirm.tsx | 2 +- shared/chat/conversation/bot/install.tsx | 6 +- shared/chat/conversation/bot/search.tsx | 4 +- shared/chat/conversation/bot/team-picker.tsx | 2 +- shared/chat/conversation/bottom-banner.tsx | 9 +- shared/chat/conversation/command-markdown.tsx | 2 +- shared/chat/conversation/command-status.tsx | 6 +- shared/chat/conversation/container.tsx | 2 +- shared/chat/conversation/error.tsx | 2 +- shared/chat/conversation/fwd-msg.tsx | 2 +- shared/chat/conversation/giphy/hooks.tsx | 2 +- .../conversation/header-area/index.native.tsx | 8 +- .../conversation/info-panel/add-people.tsx | 4 +- .../info-panel/add-to-channel.tsx | 4 +- .../conversation/info-panel/attachments.tsx | 6 +- shared/chat/conversation/info-panel/bot.tsx | 8 +- .../chat/conversation/info-panel/common.tsx | 4 +- .../chat/conversation/info-panel/header.tsx | 4 +- shared/chat/conversation/info-panel/index.tsx | 4 +- .../chat/conversation/info-panel/members.tsx | 8 +- shared/chat/conversation/info-panel/menu.tsx | 8 +- .../info-panel/settings/index.tsx | 6 +- .../info-panel/settings/min-writer-role.tsx | 4 +- .../info-panel/settings/notifications.tsx | 2 +- .../conversation/input-area/container.tsx | 2 +- .../input-area/location-popup.d.ts | 3 + .../input-area/location-popup.desktop.tsx | 2 + ...on-popup.tsx => location-popup.native.tsx} | 62 +- .../conversation/input-area/normal2/index.tsx | 4 +- ...nu-popup.tsx => moremenu-popup.native.tsx} | 2 +- .../normal2/platform-input.desktop.tsx | 2 +- .../normal2/platform-input.native.tsx | 6 +- .../normal2/set-explode-popup/hooks.tsx | 9 +- .../set-explode-popup/index.desktop.tsx | 4 +- .../input-area/normal2/typing.tsx | 2 +- .../chat/conversation/input-area/preview.tsx | 2 +- .../input-area/suggestors/channels.tsx | 4 +- .../input-area/suggestors/commands.tsx | 2 +- .../input-area/suggestors/emoji.tsx | 2 +- .../input-area/suggestors/index.tsx | 2 +- .../input-area/suggestors/users.tsx | 6 +- shared/chat/conversation/list-area/hooks.tsx | 2 +- .../conversation/list-area/index.desktop.tsx | 6 +- .../conversation/list-area/index.native.tsx | 2 +- shared/chat/conversation/load-status.tsx | 2 +- .../messages/account-payment/container.tsx | 4 +- .../messages/account-payment/wrapper.tsx | 2 +- .../messages/attachment/audio.tsx | 6 +- .../conversation/messages/attachment/file.tsx | 8 +- .../messages/attachment/image2/use-state.tsx | 2 +- .../messages/attachment/shared.tsx | 6 +- .../messages/attachment/video/use-state.tsx | 2 +- .../conversation/messages/cards/make-team.tsx | 2 +- .../messages/cards/team-journey/container.tsx | 4 +- .../chat/conversation/messages/emoji-row.tsx | 2 +- .../messages/message-popup/attachment.tsx | 6 +- .../message-popup/exploding-header.tsx | 2 +- .../messages/message-popup/header.tsx | 2 +- .../messages/message-popup/hooks.tsx | 12 +- .../messages/message-popup/index.tsx | 2 +- .../messages/message-popup/journeycard.tsx | 2 +- .../messages/message-popup/reactionitem.tsx | 2 +- .../messages/message-popup/text.tsx | 8 +- .../chat/conversation/messages/pin/index.tsx | 2 +- .../conversation/messages/pin/wrapper.tsx | 2 +- .../messages/placeholder/wrapper.tsx | 2 +- .../conversation/messages/react-button.tsx | 4 +- .../messages/reaction-tooltip.tsx | 4 +- .../conversation/messages/reactions-rows.tsx | 2 +- .../chat/conversation/messages/reset-user.tsx | 4 +- .../messages/retention-notice.tsx | 4 +- .../chat/conversation/messages/separator.tsx | 10 +- .../messages/set-channelname/wrapper.tsx | 2 +- .../messages/set-description/wrapper.tsx | 2 +- .../messages/special-bottom-message.tsx | 2 +- .../messages/special-top-message.tsx | 6 +- .../system-added-to-team/container.tsx | 6 +- .../messages/system-added-to-team/wrapper.tsx | 2 +- .../messages/system-change-avatar/index.tsx | 2 +- .../messages/system-change-avatar/wrapper.tsx | 2 +- .../system-change-retention/container.tsx | 6 +- .../system-change-retention/wrapper.tsx | 2 +- .../messages/system-create-team/container.tsx | 6 +- .../messages/system-create-team/wrapper.tsx | 2 +- .../messages/system-git-push/container.tsx | 8 +- .../messages/system-git-push/wrapper.tsx | 2 +- .../system-invite-accepted/container.tsx | 6 +- .../system-invite-accepted/wrapper.tsx | 2 +- .../messages/system-joined/container.tsx | 2 +- .../messages/system-joined/wrapper.tsx | 2 +- .../messages/system-left/container.tsx | 2 +- .../messages/system-left/wrapper.tsx | 2 +- .../messages/system-new-channel/container.tsx | 4 +- .../messages/system-new-channel/wrapper.tsx | 2 +- .../container.tsx | 2 +- .../messages/system-profile-reset-notice.tsx | 2 +- .../messages/system-sbs-resolve/container.tsx | 2 +- .../messages/system-sbs-resolve/wrapper.tsx | 4 +- .../system-simple-to-complex/container.tsx | 6 +- .../system-simple-to-complex/wrapper.tsx | 2 +- .../messages/system-text/wrapper.tsx | 2 +- .../system-users-added-to-conv/container.tsx | 4 +- .../system-users-added-to-conv/wrapper.tsx | 2 +- .../conversation/messages/text/bottom.tsx | 2 +- .../messages/text/coinflip/index.tsx | 2 +- .../chat/conversation/messages/text/reply.tsx | 2 +- .../text/unfurl/prompt-list/container.tsx | 2 +- .../text/unfurl/unfurl-list/generic.tsx | 2 +- .../text/unfurl/unfurl-list/giphy.tsx | 2 +- .../text/unfurl/unfurl-list/image/index.tsx | 2 +- .../text/unfurl/unfurl-list/index.tsx | 2 +- .../text/unfurl/unfurl-list/map-popup.tsx | 4 +- .../messages/text/unfurl/unfurl-list/map.tsx | 2 +- .../text/unfurl/unfurl-list/use-state.tsx | 4 +- .../conversation/messages/text/wrapper.tsx | 2 +- .../conversation/messages/wrapper/edited.tsx | 2 +- .../exploding-height-retainer/container.tsx | 2 +- .../messages/wrapper/exploding-meta.tsx | 2 +- .../wrapper/long-pressable/index.native.tsx | 2 +- .../messages/wrapper/send-indicator.tsx | 2 +- .../conversation/messages/wrapper/wrapper.tsx | 4 +- shared/chat/conversation/normal/container.tsx | 7 +- .../conversation/normal/index.desktop.tsx | 2 +- .../chat/conversation/normal/index.native.tsx | 2 +- shared/chat/conversation/pinned-message.tsx | 6 +- shared/chat/conversation/rekey/container.tsx | 6 +- shared/chat/conversation/reply-preview.tsx | 2 +- shared/chat/conversation/search.tsx | 2 +- shared/chat/create-channel/hooks.tsx | 2 +- shared/chat/delete-history-warning.tsx | 2 +- shared/chat/emoji-picker/container.tsx | 4 +- shared/chat/inbox-and-conversation-2.tsx | 2 +- shared/chat/inbox-and-conversation-header.tsx | 8 +- shared/chat/inbox-search/index.tsx | 4 +- shared/chat/inbox/container.tsx | 39 +- shared/chat/inbox/filter-row.tsx | 2 +- shared/chat/inbox/index.d.ts | 3 +- shared/chat/inbox/index.desktop.tsx | 5 +- shared/chat/inbox/index.native.tsx | 5 +- shared/chat/inbox/new-chat-button.tsx | 2 +- shared/chat/inbox/row/big-team-channel.tsx | 2 +- shared/chat/inbox/row/big-team-header.tsx | 4 +- shared/chat/inbox/row/big-teams-divider.tsx | 2 +- shared/chat/inbox/row/build-team.tsx | 2 +- shared/chat/inbox/row/index.tsx | 4 +- shared/chat/inbox/row/opened-row-state.tsx | 2 +- shared/chat/inbox/row/sizes.tsx | 6 +- .../chat/inbox/row/small-team/bottom-line.tsx | 4 +- shared/chat/inbox/row/small-team/index.tsx | 4 +- .../swipe-conv-actions/index.native.tsx | 2 +- shared/chat/inbox/row/small-team/top-line.tsx | 2 +- shared/chat/inbox/row/teams-divider.tsx | 5 +- .../types/chat2 => chat/inbox}/rowitem.tsx | 0 shared/chat/inbox/search-row.tsx | 2 +- shared/chat/new-team-dialog-container.tsx | 4 +- shared/chat/payments/status/index.tsx | 4 +- shared/chat/pdf/index.desktop.tsx | 6 +- shared/chat/pdf/index.native.tsx | 6 +- shared/chat/routes.tsx | 2 +- .../selectable-big-team-channel-container.tsx | 2 +- .../chat/selectable-small-team-container.tsx | 4 +- shared/chat/send-to-chat/index.tsx | 8 +- shared/common-adapters/avatar/hooks.tsx | 8 +- shared/common-adapters/avatar/store.tsx | 1 + shared/common-adapters/copy-text.tsx | 6 +- shared/common-adapters/markdown/channel.tsx | 2 +- .../markdown/maybe-mention/index.tsx | 2 +- .../markdown/maybe-mention/team.tsx | 4 +- .../markdown/service-decoration.tsx | 5 +- shared/common-adapters/mention-container.tsx | 10 +- shared/common-adapters/mention.tsx | 2 +- shared/common-adapters/name-with-icon.tsx | 6 +- shared/common-adapters/profile-card.tsx | 10 +- .../common-adapters/proof-broken-banner.tsx | 4 +- shared/common-adapters/reload.tsx | 4 +- shared/common-adapters/team-with-popup.tsx | 2 +- shared/common-adapters/usernames.tsx | 10 +- shared/common-adapters/wave-button.tsx | 2 +- shared/constants/active/index.tsx | 27 - shared/constants/archive/util.tsx | 15 - shared/constants/autoreset/util.tsx | 13 - shared/constants/bots/util.tsx | 30 - shared/constants/chat2/common.tsx | 11 +- shared/constants/chat2/debug.tsx | 2 +- shared/constants/chat2/index.tsx | 2001 ------------ shared/constants/chat2/message.tsx | 10 +- shared/constants/chat2/meta.tsx | 19 +- shared/constants/chat2/util.tsx | 61 - .../constants/{config/util.tsx => config.tsx} | 2 +- .../constants/{crypto/util.tsx => crypto.tsx} | 2 +- shared/constants/deeplinks.tsx | 265 ++ shared/constants/deeplinks/index.tsx | 343 -- shared/constants/deeplinks/util.tsx | 13 - shared/constants/devices/util.tsx | 21 - shared/constants/engine/index.tsx | 84 - shared/constants/fs.tsx | 772 +++++ shared/constants/fs/common.native.tsx | 69 - .../fs/platform-specific.android.tsx | 68 - shared/constants/fs/platform-specific.d.ts | 2 - .../fs/platform-specific.desktop.tsx | 310 -- shared/constants/fs/platform-specific.ios.tsx | 2 - shared/constants/fs/util.tsx | 24 - shared/constants/git/util.tsx | 20 - shared/constants/index.tsx | 9 +- shared/constants/init/index.d.ts | 7 + shared/constants/init/index.desktop.tsx | 583 ++++ .../index.native.tsx | 500 ++- .../push-listener.native.tsx} | 38 +- shared/constants/init/shared.tsx | 923 ++++++ shared/constants/notifications/util.tsx | 69 - shared/constants/people/util.tsx | 28 - shared/constants/pinentry/util.tsx | 28 - .../platform-specific/index.desktop.tsx | 290 -- shared/constants/recover-password/utils.tsx | 1 - .../{router2/util.tsx => router2.tsx} | 11 +- shared/constants/router2/index.tsx | 182 -- shared/constants/rpc-utils.tsx | 9 +- .../{settings/util.tsx => settings.tsx} | 17 - shared/constants/signup/util.tsx | 13 - shared/constants/store-registry.tsx | 353 --- shared/constants/strings.tsx | 9 + .../utils.tsx => team-building.tsx} | 4 +- .../constants/{teams/util.tsx => teams.tsx} | 25 +- shared/constants/tracker2/util.tsx | 36 - shared/constants/types/chat2/index.tsx | 11 - shared/constants/types/chat2/message.tsx | 5 - shared/constants/types/config.tsx | 5 +- shared/constants/types/crypto.tsx | 2 - shared/constants/types/git.tsx | 7 - shared/constants/types/push.tsx | 2 - shared/constants/types/wallets.tsx | 5 - shared/constants/unlock-folders/util.tsx | 30 - shared/constants/users/util.tsx | 14 - shared/constants/values.tsx | 9 + shared/constants/wallets/utils.tsx | 2 - shared/constants/whats-new/index.tsx | 55 - shared/constants/whats-new/utils.tsx | 69 - shared/crypto/input.tsx | 2 +- shared/crypto/operations/decrypt.tsx | 2 +- shared/crypto/operations/encrypt.tsx | 2 +- shared/crypto/operations/sign.tsx | 2 +- shared/crypto/operations/verify.tsx | 2 +- shared/crypto/output.tsx | 14 +- shared/crypto/recipients.tsx | 2 +- shared/crypto/routes.tsx | 2 +- shared/crypto/sub-nav/index.desktop.tsx | 2 +- shared/crypto/sub-nav/index.native.tsx | 2 +- shared/crypto/sub-nav/left-nav.desktop.tsx | 2 +- shared/deeplinks/error.tsx | 11 +- shared/desktop/app/installer.desktop.tsx | 19 +- shared/desktop/app/menu-bar.desktop.tsx | 2 +- shared/desktop/app/node2.desktop.tsx | 3 +- shared/desktop/electron-sums.tsx | 12 +- shared/desktop/package.desktop.tsx | 13 +- .../remote/use-serialize-props.desktop.tsx | 2 +- shared/desktop/renderer/main.desktop.tsx | 2 +- shared/desktop/renderer/main2.desktop.tsx | 229 +- shared/desktop/webpack.config.babel.js | 22 +- shared/devices/add-device.tsx | 4 +- shared/devices/device-icon.tsx | 4 +- shared/devices/device-page.tsx | 2 +- shared/devices/device-revoke.tsx | 12 +- shared/devices/index.tsx | 2 +- shared/devices/nav-header.tsx | 4 +- shared/devices/row.tsx | 2 +- shared/engine/index-impl.tsx | 20 +- shared/engine/index.d.ts | 2 +- shared/fs/banner/conflict-banner.tsx | 6 +- shared/fs/banner/public-reminder.tsx | 4 +- shared/fs/banner/reset-banner.tsx | 10 +- .../container.tsx | 8 +- .../kext-permission-popup.tsx | 4 +- shared/fs/browser/destination-picker.tsx | 4 +- shared/fs/browser/index.tsx | 6 +- shared/fs/browser/offline.tsx | 4 +- shared/fs/browser/root.tsx | 6 +- shared/fs/browser/rows/editing.tsx | 4 +- shared/fs/browser/rows/rows-container.tsx | 6 +- shared/fs/browser/rows/still.tsx | 4 +- shared/fs/browser/rows/tlf-type.tsx | 2 +- shared/fs/browser/rows/tlf.tsx | 6 +- shared/fs/common/errs-container.tsx | 2 +- shared/fs/common/folder-view-filter-icon.tsx | 4 +- shared/fs/common/folder-view-filter.tsx | 4 +- shared/fs/common/hooks.tsx | 8 +- shared/fs/common/item-icon.tsx | 4 +- shared/fs/common/kbfs-path.tsx | 4 +- shared/fs/common/last-modified-line.tsx | 4 +- .../fs/common/open-in-system-file-manager.tsx | 4 +- shared/fs/common/path-info.tsx | 4 +- .../common/path-item-action/choose-view.tsx | 2 +- .../path-item-action/confirm-delete.tsx | 4 +- shared/fs/common/path-item-action/confirm.tsx | 4 +- shared/fs/common/path-item-action/index.tsx | 4 +- shared/fs/common/path-item-action/layout.tsx | 2 +- .../path-item-action/menu-container.tsx | 10 +- shared/fs/common/path-item-info.tsx | 4 +- .../fs/common/path-status-icon-container.tsx | 4 +- .../common/refresh-driver-status-on-mount.tsx | 4 +- shared/fs/common/sfmi-popup.tsx | 2 +- shared/fs/common/tlf-info-line-container.tsx | 6 +- shared/fs/common/upload-button.tsx | 8 +- shared/fs/common/use-open.tsx | 4 +- shared/fs/filepreview/bare-preview.tsx | 2 +- shared/fs/filepreview/default-view.tsx | 6 +- shared/fs/filepreview/view.tsx | 6 +- shared/fs/footer/download.tsx | 6 +- shared/fs/footer/downloads.tsx | 4 +- shared/fs/footer/proof-broken.tsx | 4 +- shared/fs/footer/upload-container.tsx | 4 +- shared/fs/index.tsx | 4 +- shared/fs/nav-header/actions.tsx | 4 +- shared/fs/nav-header/main-banner.tsx | 6 +- shared/fs/nav-header/mobile-header.tsx | 4 +- shared/fs/nav-header/title.tsx | 2 +- shared/fs/routes.tsx | 2 +- shared/fs/top-bar/loading.tsx | 4 +- shared/fs/top-bar/sort.tsx | 4 +- shared/fs/top-bar/sync-toggle.tsx | 4 +- shared/git/delete-repo.tsx | 2 +- shared/git/index.tsx | 2 +- shared/git/nav-header.tsx | 2 +- shared/git/new-repo.tsx | 4 +- shared/git/row.tsx | 12 +- shared/git/select-channel.tsx | 4 +- shared/incoming-share/index.tsx | 17 +- shared/ios/Keybase/Info.plist | 2 +- shared/ios/KeybaseShare/Info.plist | 2 +- shared/ios/Podfile.lock | 60 +- shared/login/index.tsx | 4 +- shared/login/join-or-login.tsx | 6 +- shared/login/loading.tsx | 2 +- .../recover-password/device-selector.tsx | 2 +- shared/login/recover-password/error-modal.tsx | 4 +- shared/login/recover-password/error.tsx | 4 +- .../login/recover-password/explain-device.tsx | 2 +- shared/login/recover-password/paper-key.tsx | 2 +- shared/login/recover-password/password.tsx | 2 +- .../recover-password/prompt-reset-shared.tsx | 4 +- shared/login/relogin/container.tsx | 8 +- shared/login/reset/confirm.tsx | 4 +- shared/login/reset/modal.tsx | 2 +- shared/login/reset/password-enter.tsx | 2 +- shared/login/reset/password-known.tsx | 2 +- shared/login/reset/waiting.tsx | 2 +- shared/login/routes.tsx | 2 +- shared/login/signup/error.tsx | 2 +- shared/menubar/chat-container.desktop.tsx | 2 +- shared/menubar/files-container.desktop.tsx | 4 +- shared/menubar/remote-container.desktop.tsx | 14 +- shared/menubar/remote-proxy.desktop.tsx | 22 +- shared/override-d.ts/misc/index.d.ts | 13 - shared/package.json | 62 +- shared/patches/qrcode-generator+1.4.4.patch | 29 - shared/people/announcement.tsx | 6 +- shared/people/container.tsx | 8 +- shared/people/index.shared.tsx | 4 +- shared/people/routes.tsx | 2 +- shared/people/todo.tsx | 16 +- shared/pinentry/remote-container.desktop.tsx | 2 +- shared/pinentry/remote-proxy.desktop.tsx | 2 +- shared/pinentry/remote-serializer.desktop.tsx | 2 +- shared/profile/add-to-team.tsx | 2 +- shared/profile/confirm-or-pending.tsx | 2 +- shared/profile/edit-avatar/hooks.tsx | 4 +- shared/profile/edit-profile.tsx | 6 +- shared/profile/generic/enter-username.tsx | 2 +- shared/profile/generic/proofs-list.tsx | 4 +- shared/profile/generic/result.tsx | 2 +- shared/profile/pgp/finished/index.desktop.tsx | 2 +- shared/profile/pgp/generate/index.desktop.tsx | 2 +- shared/profile/pgp/info/index.desktop.tsx | 2 +- shared/profile/post-proof.tsx | 6 +- shared/profile/prove-enter-username.tsx | 2 +- shared/profile/prove-website-choice.tsx | 2 +- shared/profile/revoke.tsx | 2 +- shared/profile/showcase-team-offer.tsx | 6 +- shared/profile/user/actions/index.tsx | 14 +- shared/profile/user/friend.tsx | 4 +- shared/profile/user/hooks.tsx | 8 +- shared/profile/user/teams/index.tsx | 6 +- shared/provision/code-page/container.tsx | 6 +- shared/provision/code-page/qr-image.tsx | 9 +- shared/provision/code-page/qr-scan/hooks.tsx | 2 +- .../code-page/qr-scan/not-authorized.tsx | 4 +- shared/provision/error.tsx | 4 +- shared/provision/forgot-username.tsx | 4 +- shared/provision/paper-key.tsx | 2 +- shared/provision/password.tsx | 4 +- .../select-other-device-connected.tsx | 4 +- shared/provision/select-other-device.tsx | 2 +- shared/provision/set-public-name.tsx | 4 +- shared/provision/troubleshooting.tsx | 4 +- shared/provision/username-or-email.tsx | 15 +- shared/router-v2/account-switcher/index.tsx | 14 +- shared/router-v2/common.native.tsx | 2 +- shared/router-v2/header/index.desktop.tsx | 2 +- shared/router-v2/header/syncing-folders.tsx | 4 +- shared/router-v2/hooks.native.tsx | 10 +- .../router-v2/left-tab-navigator.desktop.tsx | 5 +- shared/router-v2/router.desktop.tsx | 6 +- shared/router-v2/router.native.tsx | 10 +- shared/router-v2/router.shared.tsx | 4 +- shared/router-v2/tab-bar.desktop.tsx | 16 +- shared/router-v2/tab-bar.native.tsx | 4 +- shared/settings/account/add-modals.tsx | 4 +- shared/settings/account/confirm-delete.tsx | 4 +- shared/settings/account/email-phone-row.tsx | 4 +- shared/settings/account/index.tsx | 8 +- shared/settings/advanced.tsx | 8 +- shared/settings/archive/index.tsx | 18 +- shared/settings/archive/modal.tsx | 27 +- shared/settings/chat.tsx | 10 +- shared/settings/contacts-joined.tsx | 6 +- shared/settings/db-nuke.confirm.tsx | 2 +- .../check-passphrase.native.tsx | 2 +- shared/settings/delete-confirm/index.tsx | 6 +- .../settings/disable-cert-pinning-modal.tsx | 2 +- shared/settings/display.tsx | 4 +- .../settings/feedback/container.desktop.tsx | 2 +- shared/settings/feedback/container.native.tsx | 6 +- shared/settings/files/hooks.tsx | 2 +- shared/settings/files/index.desktop.tsx | 6 +- shared/settings/files/index.native.tsx | 4 +- shared/settings/files/refresh-settings.tsx | 2 +- shared/settings/group.tsx | 2 +- shared/settings/invites/index.desktop.tsx | 6 +- shared/settings/logout.tsx | 6 +- shared/settings/manage-contacts.tsx | 8 +- shared/settings/notifications/hooks.tsx | 6 +- .../settings/notifications/index.desktop.tsx | 4 +- .../settings/notifications/index.native.tsx | 6 +- shared/settings/notifications/push-prompt.tsx | 2 +- shared/settings/notifications/render.tsx | 2 +- shared/settings/password.tsx | 2 +- shared/settings/proxy.tsx | 2 +- shared/settings/root-desktop-tablet.tsx | 2 +- shared/settings/root-phone.tsx | 12 +- shared/settings/routes.tsx | 2 +- shared/settings/sub-nav/left-nav.tsx | 8 +- shared/signup/common.tsx | 2 +- shared/signup/device-name.tsx | 4 +- shared/signup/email.tsx | 6 +- shared/signup/feedback.tsx | 2 +- shared/signup/phone-number/index.tsx | 2 +- shared/signup/phone-number/verify.tsx | 2 +- shared/signup/username.tsx | 4 +- .../archive/index.tsx => stores/archive.tsx} | 29 +- .../index.tsx => stores/autoreset.tsx} | 32 +- .../bots/index.tsx => stores/bots.tsx} | 21 +- shared/stores/chat2.tsx | 2047 ++++++++++++ .../config/index.tsx => stores/config.tsx} | 391 +-- .../chat2 => stores}/convostate.tsx | 209 +- .../crypto/index.tsx => stores/crypto.tsx} | 22 +- .../index.tsx => stores/current-user.tsx} | 3 +- .../daemon/index.tsx => stores/daemon.tsx} | 88 +- .../index.tsx => stores/darkmode.tsx} | 5 +- .../devices/index.tsx => stores/devices.tsx} | 6 +- .../index.tsx => stores/followers.tsx} | 2 +- .../{constants/fs/index.tsx => stores/fs.tsx} | 1150 ++----- .../git/index.tsx => stores/git.tsx} | 25 +- .../logout/index.tsx => stores/logout.tsx} | 11 +- .../index.tsx => stores/notifications.tsx} | 31 +- .../people/index.tsx => stores/people.tsx} | 15 +- .../index.tsx => stores/pinentry.tsx} | 7 +- .../profile/index.tsx => stores/profile.tsx} | 84 +- .../index.tsx => stores/provision.tsx} | 94 +- shared/{constants => stores}/push.d.ts | 11 +- shared/{constants => stores}/push.desktop.tsx | 9 +- shared/{constants => stores}/push.native.tsx | 58 +- .../index.tsx => stores/recover-password.tsx} | 51 +- shared/stores/router2.tsx | 90 + .../index.tsx => stores/settings-chat.tsx} | 16 +- .../settings-contacts.d.ts | 2 +- .../settings-contacts.desktop.tsx | 0 .../settings-contacts.native.tsx | 30 +- .../index.tsx => stores/settings-email.tsx} | 6 +- .../index.tsx => stores/settings-invites.tsx} | 8 +- .../settings-notifications.tsx} | 8 +- .../settings-password.tsx} | 12 +- .../index.tsx => stores/settings-phone.tsx} | 34 +- .../index.tsx => stores/settings.tsx} | 95 +- .../signup/index.tsx => stores/signup.tsx} | 32 +- shared/stores/store-registry.tsx | 157 + .../index.tsx => stores/team-building.tsx} | 78 +- .../teams/index.tsx => stores/teams.tsx} | 118 +- .../index.tsx => stores/tracker2.tsx} | 55 +- .../index.tsx => stores/unlock-folders.tsx} | 9 +- .../users/index.tsx => stores/users.tsx} | 8 +- .../waiting/index.tsx => stores/waiting.tsx} | 3 +- .../wallets/index.tsx => stores/wallets.tsx} | 12 +- shared/stores/whats-new.tsx | 118 + shared/styles/colors.tsx | 2 +- shared/styles/index.native.tsx | 2 +- shared/styles/style-sheet-proxy.tsx | 2 +- shared/team-building/contacts.tsx | 4 +- shared/team-building/email-search.tsx | 5 +- .../filtered-service-tab-bar.tsx | 2 +- shared/team-building/index.tsx | 4 +- shared/team-building/list-body.tsx | 10 +- shared/team-building/page.tsx | 2 +- shared/team-building/phone-search.tsx | 9 +- .../search-result/hellobot-result.tsx | 4 +- .../search-result/people-result.tsx | 12 +- .../search-result/you-result.tsx | 4 +- shared/team-building/shared.tsx | 2 +- .../add-contacts.native.tsx | 2 +- shared/teams/add-members-wizard/add-email.tsx | 2 +- .../add-members-wizard/add-from-where.tsx | 2 +- shared/teams/add-members-wizard/add-phone.tsx | 4 +- shared/teams/add-members-wizard/confirm.tsx | 6 +- shared/teams/channel/create-channels.tsx | 2 +- shared/teams/channel/header.tsx | 6 +- shared/teams/channel/index.tsx | 8 +- shared/teams/channel/rows.tsx | 10 +- shared/teams/channel/tabs.tsx | 2 +- shared/teams/common/activity.tsx | 4 +- shared/teams/common/channel-hooks.tsx | 4 +- shared/teams/common/enable-contacts.tsx | 4 +- shared/teams/common/selection-popup.tsx | 6 +- shared/teams/common/use-contacts.native.tsx | 4 +- .../teams/confirm-modals/confirm-kick-out.tsx | 4 +- .../confirm-remove-from-channel.tsx | 4 +- .../teams/confirm-modals/delete-channel.tsx | 2 +- .../really-leave-team/index.tsx | 4 +- shared/teams/container.tsx | 8 +- shared/teams/delete-team.tsx | 4 +- shared/teams/edit-team-description.tsx | 2 +- shared/teams/emojis/add-alias.tsx | 2 +- shared/teams/emojis/add-emoji.tsx | 2 +- shared/teams/external-team.tsx | 6 +- shared/teams/get-options.tsx | 2 +- .../team-invite-by-contacts.native.tsx | 4 +- shared/teams/invite-by-email.tsx | 2 +- shared/teams/join-team/container.tsx | 2 +- shared/teams/join-team/join-from-invite.tsx | 2 +- shared/teams/main/index.tsx | 2 +- shared/teams/main/team-row.tsx | 6 +- shared/teams/new-team/index.tsx | 2 +- .../new-team/wizard/add-subteam-members.tsx | 4 +- .../teams/new-team/wizard/create-channels.tsx | 2 +- .../teams/new-team/wizard/create-subteams.tsx | 2 +- .../teams/new-team/wizard/make-big-team.tsx | 2 +- .../teams/new-team/wizard/new-team-info.tsx | 4 +- shared/teams/new-team/wizard/team-purpose.tsx | 2 +- shared/teams/rename-team.tsx | 2 +- shared/teams/routes.tsx | 2 +- shared/teams/subscriber.tsx | 2 +- shared/teams/team/index.tsx | 4 +- shared/teams/team/member/add-to-channels.tsx | 6 +- shared/teams/team/member/edit-channel.tsx | 2 +- shared/teams/team/member/index.new.tsx | 8 +- shared/teams/team/menu-container.tsx | 6 +- shared/teams/team/new-header.tsx | 8 +- shared/teams/team/rows/bot-row/bot.tsx | 8 +- .../teams/team/rows/channel-row/channel.tsx | 4 +- shared/teams/team/rows/emoji-row/add.tsx | 4 +- shared/teams/team/rows/emoji-row/item.tsx | 4 +- shared/teams/team/rows/empty-row.tsx | 6 +- shared/teams/team/rows/index.tsx | 6 +- shared/teams/team/rows/invite-row/invite.tsx | 4 +- shared/teams/team/rows/invite-row/request.tsx | 6 +- shared/teams/team/rows/member-row.tsx | 12 +- shared/teams/team/rows/subteam-row/add.tsx | 2 +- .../team/settings-tab/default-channels.tsx | 4 +- shared/teams/team/settings-tab/index.tsx | 4 +- .../team/settings-tab/retention/index.tsx | 6 +- shared/teams/team/tabs.tsx | 4 +- shared/teams/team/team-info.tsx | 2 +- shared/todo.txt | 6 +- shared/tracker2/assertion.tsx | 12 +- shared/tracker2/bio.tsx | 4 +- shared/tracker2/remote-container.desktop.tsx | 12 +- shared/tracker2/remote-proxy.desktop.tsx | 10 +- shared/unlock-folders/device-list.desktop.tsx | 2 +- shared/unlock-folders/index.desktop.tsx | 4 +- .../remote-container.desktop.tsx | 4 +- .../unlock-folders/remote-proxy.desktop.tsx | 2 +- .../remote-serializer.desktop.tsx | 2 +- shared/util/crop.tsx | 2 +- shared/util/phone-numbers/index.tsx | 20 +- .../platform-specific/index.d.ts | 3 +- .../util/platform-specific/index.desktop.tsx | 13 + .../util/platform-specific/index.native.tsx | 111 + .../input-monitor.desktop.tsx | 0 .../platform-specific/kbfs-notifications.tsx | 4 +- shared/util/qr-code.tsx | 643 ++++ shared/util/zustand.tsx | 11 +- shared/wallets/index.tsx | 4 +- shared/wallets/really-remove-account.tsx | 8 +- shared/wallets/remove-account.tsx | 2 +- shared/whats-new/container.tsx | 6 +- shared/whats-new/icon/index.tsx | 4 +- shared/whats-new/index.tsx | 2 +- shared/whats-new/versions.tsx | 6 +- shared/yarn.lock | 2804 +++++++++-------- 613 files changed, 10359 insertions(+), 9448 deletions(-) create mode 100644 shared/app/global-errors.tsx delete mode 100644 shared/app/global-errors/hook.tsx delete mode 100644 shared/app/global-errors/index.d.ts delete mode 100644 shared/app/global-errors/index.desktop.tsx delete mode 100644 shared/app/global-errors/index.native.tsx create mode 100644 shared/chat/conversation/input-area/location-popup.d.ts create mode 100644 shared/chat/conversation/input-area/location-popup.desktop.tsx rename shared/chat/conversation/input-area/{location-popup.tsx => location-popup.native.tsx} (70%) rename shared/chat/conversation/input-area/normal2/{moremenu-popup.tsx => moremenu-popup.native.tsx} (97%) rename shared/{constants/types/chat2 => chat/inbox}/rowitem.tsx (100%) delete mode 100644 shared/constants/active/index.tsx delete mode 100644 shared/constants/archive/util.tsx delete mode 100644 shared/constants/autoreset/util.tsx delete mode 100644 shared/constants/bots/util.tsx delete mode 100644 shared/constants/chat2/util.tsx rename shared/constants/{config/util.tsx => config.tsx} (96%) rename shared/constants/{crypto/util.tsx => crypto.tsx} (98%) create mode 100644 shared/constants/deeplinks.tsx delete mode 100644 shared/constants/deeplinks/index.tsx delete mode 100644 shared/constants/deeplinks/util.tsx delete mode 100644 shared/constants/devices/util.tsx delete mode 100644 shared/constants/engine/index.tsx create mode 100644 shared/constants/fs.tsx delete mode 100644 shared/constants/fs/common.native.tsx delete mode 100644 shared/constants/fs/platform-specific.android.tsx delete mode 100644 shared/constants/fs/platform-specific.d.ts delete mode 100644 shared/constants/fs/platform-specific.desktop.tsx delete mode 100644 shared/constants/fs/platform-specific.ios.tsx delete mode 100644 shared/constants/fs/util.tsx delete mode 100644 shared/constants/git/util.tsx create mode 100644 shared/constants/init/index.d.ts create mode 100644 shared/constants/init/index.desktop.tsx rename shared/constants/{platform-specific => init}/index.native.tsx (58%) rename shared/constants/{platform-specific/push.native.tsx => init/push-listener.native.tsx} (87%) create mode 100644 shared/constants/init/shared.tsx delete mode 100644 shared/constants/notifications/util.tsx delete mode 100644 shared/constants/people/util.tsx delete mode 100644 shared/constants/pinentry/util.tsx delete mode 100644 shared/constants/platform-specific/index.desktop.tsx delete mode 100644 shared/constants/recover-password/utils.tsx rename shared/constants/{router2/util.tsx => router2.tsx} (98%) delete mode 100644 shared/constants/router2/index.tsx rename shared/constants/{settings/util.tsx => settings.tsx} (78%) delete mode 100644 shared/constants/signup/util.tsx delete mode 100644 shared/constants/store-registry.tsx rename shared/constants/{team-building/utils.tsx => team-building.tsx} (82%) rename shared/constants/{teams/util.tsx => teams.tsx} (82%) delete mode 100644 shared/constants/tracker2/util.tsx delete mode 100644 shared/constants/unlock-folders/util.tsx delete mode 100644 shared/constants/users/util.tsx delete mode 100644 shared/constants/wallets/utils.tsx delete mode 100644 shared/constants/whats-new/index.tsx delete mode 100644 shared/constants/whats-new/utils.tsx delete mode 100644 shared/patches/qrcode-generator+1.4.4.patch rename shared/{constants/archive/index.tsx => stores/archive.tsx} (93%) rename shared/{constants/autoreset/index.tsx => stores/autoreset.tsx} (85%) rename shared/{constants/bots/index.tsx => stores/bots.tsx} (91%) create mode 100644 shared/stores/chat2.tsx rename shared/{constants/config/index.tsx => stores/config.tsx} (67%) rename shared/{constants/chat2 => stores}/convostate.tsx (94%) rename shared/{constants/crypto/index.tsx => stores/crypto.tsx} (97%) rename shared/{constants/current-user/index.tsx => stores/current-user.tsx} (87%) rename shared/{constants/daemon/index.tsx => stores/daemon.tsx} (70%) rename shared/{constants/darkmode/index.tsx => stores/darkmode.tsx} (95%) rename shared/{constants/devices/index.tsx => stores/devices.tsx} (96%) rename shared/{constants/followers/index.tsx => stores/followers.tsx} (92%) rename shared/{constants/fs/index.tsx => stores/fs.tsx} (68%) rename shared/{constants/git/index.tsx => stores/git.tsx} (91%) rename shared/{constants/logout/index.tsx => stores/logout.tsx} (88%) rename shared/{constants/notifications/index.tsx => stores/notifications.tsx} (89%) rename shared/{constants/people/index.tsx => stores/people.tsx} (97%) rename shared/{constants/pinentry/index.tsx => stores/pinentry.tsx} (92%) rename shared/{constants/profile/index.tsx => stores/profile.tsx} (89%) rename shared/{constants/provision/index.tsx => stores/provision.tsx} (86%) rename shared/{constants => stores}/push.d.ts (70%) rename shared/{constants => stores}/push.desktop.tsx (75%) rename shared/{constants => stores}/push.native.tsx (84%) rename shared/{constants/recover-password/index.tsx => stores/recover-password.tsx} (87%) create mode 100644 shared/stores/router2.tsx rename shared/{constants/settings-chat/index.tsx => stores/settings-chat.tsx} (91%) rename shared/{constants => stores}/settings-contacts.d.ts (95%) rename shared/{constants => stores}/settings-contacts.desktop.tsx (100%) rename shared/{constants => stores}/settings-contacts.native.tsx (90%) rename shared/{constants/settings-email/index.tsx => stores/settings-email.tsx} (97%) rename shared/{constants/settings-invites/index.tsx => stores/settings-invites.tsx} (95%) rename shared/{constants/settings-notifications/index.tsx => stores/settings-notifications.tsx} (97%) rename shared/{constants/settings-password/index.tsx => stores/settings-password.tsx} (93%) rename shared/{constants/settings-phone/index.tsx => stores/settings-phone.tsx} (88%) rename shared/{constants/settings/index.tsx => stores/settings.tsx} (70%) rename shared/{constants/signup/index.tsx => stores/signup.tsx} (90%) create mode 100644 shared/stores/store-registry.tsx rename shared/{constants/team-building/index.tsx => stores/team-building.tsx} (82%) rename shared/{constants/teams/index.tsx => stores/teams.tsx} (96%) rename shared/{constants/tracker2/index.tsx => stores/tracker2.tsx} (93%) rename shared/{constants/unlock-folders/index.tsx => stores/unlock-folders.tsx} (86%) rename shared/{constants/users/index.tsx => stores/users.tsx} (96%) rename shared/{constants/waiting/index.tsx => stores/waiting.tsx} (94%) rename shared/{constants/wallets/index.tsx => stores/wallets.tsx} (84%) create mode 100644 shared/stores/whats-new.tsx rename shared/{constants => util}/platform-specific/index.d.ts (86%) create mode 100644 shared/util/platform-specific/index.desktop.tsx create mode 100644 shared/util/platform-specific/index.native.tsx rename shared/{constants => util}/platform-specific/input-monitor.desktop.tsx (100%) rename shared/{constants => util}/platform-specific/kbfs-notifications.tsx (98%) create mode 100644 shared/util/qr-code.tsx diff --git a/go/libkb/version.go b/go/libkb/version.go index d8b23ce1ad0e..e9c38269738b 100644 --- a/go/libkb/version.go +++ b/go/libkb/version.go @@ -4,4 +4,4 @@ package libkb // Version is the current version (should be MAJOR.MINOR.PATCH) -const Version = "6.6.0" +const Version = "6.7.0" diff --git a/shared/android/app/build.gradle b/shared/android/app/build.gradle index 43cfc1c7c8ad..29e9bfaf1434 100644 --- a/shared/android/app/build.gradle +++ b/shared/android/app/build.gradle @@ -4,7 +4,7 @@ apply plugin: "com.facebook.react" apply plugin: 'com.github.triplet.play' // KB: app version -def VERSION_NAME = "6.6.0" +def VERSION_NAME = "6.7.0" // KB: Number of commits, like ios Integer getVersionCode() { diff --git a/shared/app/global-errors.tsx b/shared/app/global-errors.tsx new file mode 100644 index 000000000000..23e6106654b6 --- /dev/null +++ b/shared/app/global-errors.tsx @@ -0,0 +1,332 @@ +import * as C from '@/constants' +import * as Kb from '@/common-adapters' +import * as React from 'react' +import logger from '@/logger' +import {ignoreDisconnectOverlay} from '@/local-debug' +import {useConfigState} from '@/stores/config' +import type {RPCError} from '@/util/errors' +import {settingsFeedbackTab} from '@/constants/settings' +import {useDaemonState} from '@/stores/daemon' + +type Size = 'Closed' | 'Small' | 'Big' + +const summaryForError = (err?: Error | RPCError) => err?.message ?? '' +const detailsForError = (err?: Error | RPCError) => err?.stack ?? '' + +const maxHeightForSize = (size: Size) => { + return { + Big: 900, + Closed: 0, + Small: 35, + }[size] +} + +const useData = () => { + const loggedIn = useConfigState(s => s.loggedIn) + const daemonError = useDaemonState(s => s.error) + const error = useConfigState(s => s.globalError) + const setGlobalError = useConfigState(s => s.dispatch.setGlobalError) + const clearModals = C.useRouterState(s => s.dispatch.clearModals) + const navigateAppend = C.useRouterState(s => s.dispatch.navigateAppend) + const onFeedback = React.useCallback(() => { + setGlobalError() + if (loggedIn) { + clearModals() + navigateAppend(settingsFeedbackTab) + } else { + navigateAppend('feedback') + } + }, [navigateAppend, clearModals, loggedIn, setGlobalError]) + const onDismiss = React.useCallback(() => { + setGlobalError() + }, [setGlobalError]) + + const [cachedSummary, setSummary] = React.useState(summaryForError(error)) + const [cachedDetails, setDetails] = React.useState(detailsForError(error)) + const [size, setSize] = React.useState('Closed') + const countdownTimerRef = React.useRef>(undefined) + + const clearCountdown = React.useCallback(() => { + countdownTimerRef.current && clearTimeout(countdownTimerRef.current) + countdownTimerRef.current = undefined + }, [countdownTimerRef]) + + const onExpandClick = React.useCallback(() => { + setSize('Big') + if (!C.isMobile) { + clearCountdown() + } + }, [clearCountdown]) + + const resetError = React.useCallback( + (newError: boolean) => { + setSize(newError ? 'Small' : 'Closed') + if (!C.isMobile) { + clearCountdown() + if (newError) { + countdownTimerRef.current = setTimeout(() => { + onDismiss() + }, 10000) + } + } + }, + [clearCountdown, onDismiss] + ) + + C.useOnUnMountOnce(() => { + clearCountdown() + }) + + C.useOnMountOnce(() => { + resetError(!!error) + }) + + React.useEffect(() => { + const id = setTimeout( + () => { + setDetails(detailsForError(error)) + if (!C.isMobile) { + setSummary(summaryForError(error)) + } + }, + error ? 0 : 7000 + ) // if it's set, do it immediately, if it's cleared set it in a bit + resetError(!!error) + return () => { + clearTimeout(id) + } + }, [error, resetError]) + + return { + cachedDetails, + cachedSummary, + daemonError, + error, + onDismiss, + onExpandClick, + onFeedback, + size, + } +} + +const GlobalError = () => { + const d = useData() + const {daemonError, error, onDismiss, onFeedback} = d + const {cachedDetails, cachedSummary, size, onExpandClick} = d + + if (size === 'Closed') { + return null + } + + if (!daemonError && !error) { + return null + } + + if (daemonError) { + if (C.isMobile) { + return null + } + if (ignoreDisconnectOverlay) { + logger.warn('Ignoring disconnect overlay') + return null + } + + const message = daemonError.message || 'Keybase is currently unreachable. Trying to reconnect you…' + return ( + + + + {message} + + + + + + + ) + } + + if (C.isMobile) { + return ( + + + + + + {size !== 'Big' && ( + + )} + {' '} + An error occurred. + + + + + + + + {size === 'Big' && ( + + + {error?.message} + {'\n\n'} + {cachedDetails} + + + )} + + ) + } + + const summary = cachedSummary + const details = cachedDetails + + let stylesContainer: Kb.Styles.StylesCrossPlatform + switch (size) { + case 'Big': + stylesContainer = styles.containerBig + break + case 'Small': + stylesContainer = styles.containerSmall + break + } + + return ( + + + + {summary} + + + {summary && ( + + )} + + + + {details} + + + + ) +} + +const styles = Kb.Styles.styleSheetCreate(() => { + const containerBase = { + left: 0, + overflow: 'hidden' as const, + position: 'absolute' as const, + right: 0, + top: 40, + zIndex: 1000, + ...Kb.Styles.transition('max-height'), + } + + return { + containerBig: Kb.Styles.platformStyles({ + isElectron: {...containerBase, maxHeight: maxHeightForSize('Big')}, + }), + containerOverlay: { + ...Kb.Styles.globalStyles.fillAbsolute, + zIndex: 1000, + }, + containerSmall: Kb.Styles.platformStyles({ + isElectron: {...containerBase, maxHeight: maxHeightForSize('Small')}, + }), + details: { + backgroundColor: Kb.Styles.globalColors.black, + color: Kb.Styles.globalColors.white_75, + ...Kb.Styles.padding(8, Kb.Styles.globalMargins.xlarge), + }, + innerContainer: { + ...Kb.Styles.globalStyles.flexBoxCenter, + backgroundColor: Kb.Styles.globalColors.black, + flex: 1, + gap: Kb.Styles.globalMargins.small, + minHeight: maxHeightForSize('Small'), + ...Kb.Styles.padding(Kb.Styles.globalMargins.xtiny, Kb.Styles.globalMargins.small), + }, + message: { + color: Kb.Styles.globalColors.white, + }, + mobileContainer: { + backgroundColor: Kb.Styles.globalColors.black, + position: 'absolute', + top: 0, + }, + mobileDetails: { + color: Kb.Styles.globalColors.white_75, + fontSize: 14, + lineHeight: 19, + ...Kb.Styles.padding(Kb.Styles.globalMargins.tiny, Kb.Styles.globalMargins.xtiny, Kb.Styles.globalMargins.xtiny), + }, + mobileErrorText: { + color: Kb.Styles.globalColors.white, + flex: 1, + }, + mobileErrorTextContainer: { + paddingBottom: Kb.Styles.globalMargins.xtiny, + position: 'relative', + }, + mobileSafeAreaView: { + backgroundColor: Kb.Styles.globalColors.transparent, + flexGrow: 0, + }, + mobileSummaryRow: { + alignItems: 'center', + flexShrink: 0, + justifyContent: 'center', + ...Kb.Styles.padding(Kb.Styles.globalMargins.tiny, Kb.Styles.globalMargins.xsmall), + }, + overlayFill: { + ...Kb.Styles.globalStyles.flexBoxCenter, + backgroundColor: Kb.Styles.globalColors.white, + flex: 1, + }, + overlayRow: { + ...Kb.Styles.globalStyles.flexBoxCenter, + backgroundColor: Kb.Styles.globalColors.blue, + padding: 8, + }, + summary: { + color: Kb.Styles.globalColors.white, + flex: 1, + }, + } as const +}) + +export default GlobalError diff --git a/shared/app/global-errors/hook.tsx b/shared/app/global-errors/hook.tsx deleted file mode 100644 index 646d872c8f90..000000000000 --- a/shared/app/global-errors/hook.tsx +++ /dev/null @@ -1,103 +0,0 @@ -import * as C from '@/constants' -import * as React from 'react' -import {useConfigState} from '@/constants/config' -import type {RPCError} from '@/util/errors' -import {settingsFeedbackTab} from '@/constants/settings/util' -import {useDaemonState} from '@/constants/daemon' - -export type Size = 'Closed' | 'Small' | 'Big' - -const summaryForError = (err?: Error | RPCError) => err?.message ?? '' -const detailsForError = (err?: Error | RPCError) => err?.stack ?? '' - -const useData = () => { - const loggedIn = useConfigState(s => s.loggedIn) - const daemonError = useDaemonState(s => s.error) - const error = useConfigState(s => s.globalError) - const setGlobalError = useConfigState(s => s.dispatch.setGlobalError) - const clearModals = C.useRouterState(s => s.dispatch.clearModals) - const navigateAppend = C.useRouterState(s => s.dispatch.navigateAppend) - const onFeedback = React.useCallback(() => { - setGlobalError() - if (loggedIn) { - clearModals() - navigateAppend(settingsFeedbackTab) - } else { - navigateAppend('feedback') - } - }, [navigateAppend, clearModals, loggedIn, setGlobalError]) - const copyToClipboard = useConfigState(s => s.dispatch.dynamic.copyToClipboard) - const onDismiss = React.useCallback(() => { - setGlobalError() - }, [setGlobalError]) - - const [cachedSummary, setSummary] = React.useState(summaryForError(error)) - const [cachedDetails, setDetails] = React.useState(detailsForError(error)) - const [size, setSize] = React.useState('Closed') - const countdownTimerRef = React.useRef>(undefined) - - const clearCountdown = React.useCallback(() => { - countdownTimerRef.current && clearTimeout(countdownTimerRef.current) - countdownTimerRef.current = undefined - }, [countdownTimerRef]) - - const onExpandClick = React.useCallback(() => { - setSize('Big') - if (!C.isMobile) { - clearCountdown() - } - }, [clearCountdown]) - - const resetError = React.useCallback( - (newError: boolean) => { - setSize(newError ? 'Small' : 'Closed') - if (!C.isMobile) { - clearCountdown() - if (newError) { - countdownTimerRef.current = setTimeout(() => { - onDismiss() - }, 10000) - } - } - }, - [clearCountdown, onDismiss] - ) - - C.useOnUnMountOnce(() => { - clearCountdown() - }) - - C.useOnMountOnce(() => { - resetError(!!error) - }) - - React.useEffect(() => { - const id = setTimeout( - () => { - setDetails(detailsForError(error)) - if (!C.isMobile) { - setSummary(summaryForError(error)) - } - }, - error ? 0 : 7000 - ) // if it's set, do it immediately, if it's cleared set it in a bit - resetError(!!error) - return () => { - clearTimeout(id) - } - }, [error, resetError]) - - return { - cachedDetails, - cachedSummary, - copyToClipboard, - daemonError, - error, - onDismiss, - onExpandClick, - onFeedback, - size, - } -} - -export default useData diff --git a/shared/app/global-errors/index.d.ts b/shared/app/global-errors/index.d.ts deleted file mode 100644 index 695dd42f1e55..000000000000 --- a/shared/app/global-errors/index.d.ts +++ /dev/null @@ -1,3 +0,0 @@ -import type * as React from 'react' -declare const GlobalError: () => React.ReactNode -export default GlobalError diff --git a/shared/app/global-errors/index.desktop.tsx b/shared/app/global-errors/index.desktop.tsx deleted file mode 100644 index 815396255845..000000000000 --- a/shared/app/global-errors/index.desktop.tsx +++ /dev/null @@ -1,185 +0,0 @@ -import logger from '@/logger' -import * as Kb from '@/common-adapters' -import {ignoreDisconnectOverlay} from '@/local-debug.desktop' -import useData, {type Size} from './hook' - -const maxHeightForSize = (size: Size) => { - return { - Big: 900, - Closed: 0, - Small: 35, - }[size] -} - -const GlobalError = () => { - const d = useData() - const {daemonError, error, onDismiss, onFeedback} = d - const {cachedDetails, cachedSummary, size, onExpandClick} = d - - if (size === 'Closed') { - return null - } - - if (!daemonError && !error) { - return null - } - - if (daemonError) { - if (ignoreDisconnectOverlay) { - logger.warn('Ignoring disconnect overlay') - return null - } - - const message = daemonError.message || 'Keybase is currently unreachable. Trying to reconnect you…' - return ( - - - - {message} - - - - - - - ) - } else { - const summary = cachedSummary - const details = cachedDetails - - let stylesContainer: Kb.Styles.StylesCrossPlatform - switch (size) { - case 'Big': - stylesContainer = styles.containerBig - break - case 'Small': - stylesContainer = styles.containerSmall - break - } - - return ( - - - - {summary} - - - {summary && ( - - )} - - - - {details} - - - - ) - } -} - -const styles = Kb.Styles.styleSheetCreate(() => { - const containerBase = { - ...Kb.Styles.globalStyles.flexBoxColumn, - left: 0, - overflow: 'hidden', - position: 'absolute', - right: 0, - top: 40, - zIndex: 1000, - ...Kb.Styles.transition('max-height'), - } as const - - return { - closeIcon: { - position: 'absolute', - right: Kb.Styles.globalMargins.xsmall, - top: 10, - }, - containerBig: Kb.Styles.platformStyles({ - isElectron: {...containerBase, maxHeight: maxHeightForSize('Big')}, - }), - containerClosed: Kb.Styles.platformStyles({ - isElectron: {...containerBase, maxHeight: maxHeightForSize('Closed')}, - }), - containerOverlay: { - ...Kb.Styles.globalStyles.flexBoxColumn, - bottom: 0, - left: 0, - position: 'absolute', - right: 0, - top: 0, - zIndex: 1000, - }, - containerSmall: Kb.Styles.platformStyles({ - isElectron: {...containerBase, maxHeight: maxHeightForSize('Small')}, - }), - details: { - backgroundColor: Kb.Styles.globalColors.black, - color: Kb.Styles.globalColors.white_75, - padding: 8, - paddingLeft: Kb.Styles.globalMargins.xlarge, - paddingRight: Kb.Styles.globalMargins.xlarge, - }, - feedbackButton: { - marginRight: Kb.Styles.globalMargins.large, - }, - innerContainer: { - ...Kb.Styles.globalStyles.flexBoxRow, - alignItems: 'center', - backgroundColor: Kb.Styles.globalColors.black, - flex: 1, - justifyContent: 'center', - minHeight: maxHeightForSize('Small'), - padding: Kb.Styles.globalMargins.xtiny, - position: 'relative', - }, - message: { - color: Kb.Styles.globalColors.white, - }, - overlayFill: { - ...Kb.Styles.globalStyles.flexBoxColumn, - alignItems: 'center', - backgroundColor: Kb.Styles.globalColors.white, - flex: 1, - justifyContent: 'center', - }, - overlayRow: { - ...Kb.Styles.globalStyles.flexBoxRow, - alignItems: 'center', - backgroundColor: Kb.Styles.globalColors.blue, - justifyContent: 'center', - padding: 8, - }, - summary: { - color: Kb.Styles.globalColors.white, - flex: 1, - }, - summaryRow: { - ...Kb.Styles.globalStyles.flexBoxRow, - alignItems: 'center', - flex: 1, - justifyContent: 'center', - padding: Kb.Styles.globalMargins.xtiny, - position: 'relative', - }, - summaryRowError: { - backgroundColor: Kb.Styles.globalColors.black, - minHeight: maxHeightForSize('Small'), - }, - } as const -}) - -export default GlobalError diff --git a/shared/app/global-errors/index.native.tsx b/shared/app/global-errors/index.native.tsx deleted file mode 100644 index 7555c1ef588c..000000000000 --- a/shared/app/global-errors/index.native.tsx +++ /dev/null @@ -1,105 +0,0 @@ -import * as Kb from '@/common-adapters' -import NativeScrollView from '@/common-adapters/scroll-view.native' -import useData from './hook' - -const GlobalError = () => { - const d = useData() - const {daemonError, error, onDismiss, onFeedback} = d - const {cachedDetails, size, onExpandClick} = d - - if (size === 'Closed') { - return null - } - - if (!daemonError && !error) { - return null - } - - return ( - - - - - - {size !== 'Big' && ( - - )} - {' '} - An error occurred. - - - - - - - - {size === 'Big' && ( - - - {error?.message} - {'\n\n'} - {cachedDetails} - - - )} - - ) -} - -const styles = Kb.Styles.styleSheetCreate( - () => - ({ - container: { - backgroundColor: Kb.Styles.globalColors.black, - position: 'absolute', - top: 0, - }, - details: { - color: Kb.Styles.globalColors.white_75, - fontSize: 14, - lineHeight: 19, - padding: Kb.Styles.globalMargins.xtiny, - paddingTop: Kb.Styles.globalMargins.tiny, - }, - errorText: { - color: Kb.Styles.globalColors.white, - flex: 1, - }, - errorTextContainer: { - paddingBottom: Kb.Styles.globalMargins.xtiny, - position: 'relative', - }, - itemText: { - color: Kb.Styles.globalColors.white, - fontSize: 8, - lineHeight: 8, - }, - safeAreaView: { - backgroundColor: Kb.Styles.globalColors.transparent, - flexGrow: 0, - }, - summaryRow: { - ...Kb.Styles.globalStyles.flexBoxRow, - alignItems: 'center', - flexShrink: 0, - justifyContent: 'center', - paddingBottom: Kb.Styles.globalMargins.tiny, - paddingLeft: Kb.Styles.globalMargins.xsmall, - paddingRight: Kb.Styles.globalMargins.xsmall, - paddingTop: Kb.Styles.globalMargins.tiny, - }, - }) as const -) - -export default GlobalError diff --git a/shared/app/index.native.tsx b/shared/app/index.native.tsx index 1152498d3a5f..d9f009250dad 100644 --- a/shared/app/index.native.tsx +++ b/shared/app/index.native.tsx @@ -1,8 +1,8 @@ import * as C from '@/constants' -import {useConfigState} from '@/constants/config' +import {useConfigState} from '@/stores/config' import * as Kb from '@/common-adapters' import * as React from 'react' -import {useDeepLinksState} from '@/constants/deeplinks' +import {handleAppLink} from '@/constants/deeplinks' import Main from './main.native' import {KeyboardProvider} from 'react-native-keyboard-controller' import Animated, {ReducedMotionConfig, ReduceMotion} from 'react-native-reanimated' @@ -18,9 +18,8 @@ import ServiceDecoration from '@/common-adapters/markdown/service-decoration' import {useUnmountAll} from '@/util/debug-react' import {darkModeSupported, guiConfig} from 'react-native-kb' import {install} from 'react-native-kb' -import {useEngineState} from '@/constants/engine' -import * as DarkMode from '@/constants/darkmode' -import {initPlatformListener} from '@/constants/platform-specific' +import * as DarkMode from '@/stores/darkmode' +import {initPlatformListener, onEngineConnected, onEngineDisconnected, onEngineIncoming} from '@/constants/init/index.native' import logger from '@/logger' logger.info('INIT App index module load') @@ -110,7 +109,6 @@ const StoreHelper = (p: {children: React.ReactNode}): React.ReactNode => { const {children} = p useDarkHookup() useKeyboardHookup() - const handleAppLink = useDeepLinksState(s => s.dispatch.handleAppLink) React.useEffect(() => { const linkingSub = Linking.addEventListener('url', ({url}: {url: string}) => { @@ -119,7 +117,7 @@ const StoreHelper = (p: {children: React.ReactNode}): React.ReactNode => { return () => { linkingSub.remove() } - }, [handleAppLink]) + }, []) return children } @@ -139,11 +137,11 @@ const useInit = () => { const {batch} = C.useWaitingState.getState().dispatch const eng = makeEngine(batch, c => { if (c) { - useEngineState.getState().dispatch.onEngineConnected() + onEngineConnected() } else { - useEngineState.getState().dispatch.onEngineDisconnected() + onEngineDisconnected() } - }) + }, onEngineIncoming) initPlatformListener() eng.listenersAreReady() diff --git a/shared/app/main.desktop.tsx b/shared/app/main.desktop.tsx index 16ef0670516d..12bd8e36da36 100644 --- a/shared/app/main.desktop.tsx +++ b/shared/app/main.desktop.tsx @@ -1,6 +1,6 @@ import * as React from 'react' import Router from '@/router-v2/router' -import {useDarkModeState} from '@/constants/darkmode' +import {useDarkModeState} from '@/stores/darkmode' import ResetModal from '../login/reset/modal' import GlobalError from './global-errors' import OutOfDate from './out-of-date' diff --git a/shared/app/out-of-date.tsx b/shared/app/out-of-date.tsx index 3287a86e87d1..70b33bb01693 100644 --- a/shared/app/out-of-date.tsx +++ b/shared/app/out-of-date.tsx @@ -3,7 +3,7 @@ import * as Kb from '@/common-adapters' import * as React from 'react' import * as T from '@/constants/types' import logger from '@/logger' -import {useConfigState} from '@/constants/config' +import {useConfigState} from '@/stores/config' const styles = Kb.Styles.styleSheetCreate(() => ({ container: { @@ -62,7 +62,7 @@ const OutOfDate = () => { C.ignorePromise(f()) }) - const onOpenAppStore = useConfigState(s => s.dispatch.dynamic.openAppStore) + const onOpenAppStore = useConfigState(s => s.dispatch.defer.openAppStore) return status !== 'critical' ? null : ( diff --git a/shared/app/runtime-stats.tsx b/shared/app/runtime-stats.tsx index 55e94eee4a96..2ea975b665cc 100644 --- a/shared/app/runtime-stats.tsx +++ b/shared/app/runtime-stats.tsx @@ -1,7 +1,7 @@ import * as React from 'react' import * as Kb from '@/common-adapters' import * as T from '@/constants/types' -import {useConfigState} from '@/constants/config' +import {useConfigState} from '@/stores/config' const isIPhoneX = false as boolean // import lagRadar from 'lag-radar' diff --git a/shared/chat/audio/audio-recorder.native.tsx b/shared/chat/audio/audio-recorder.native.tsx index b68e7f68ade6..8c3a2d197c98 100644 --- a/shared/chat/audio/audio-recorder.native.tsx +++ b/shared/chat/audio/audio-recorder.native.tsx @@ -1,4 +1,4 @@ -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as T from '@/constants/types' import * as Kb from '@/common-adapters' import {Portal} from '@/common-adapters/portal.native' diff --git a/shared/chat/blocking/block-modal.tsx b/shared/chat/blocking/block-modal.tsx index e0c695937385..0a9dfdd64d03 100644 --- a/shared/chat/blocking/block-modal.tsx +++ b/shared/chat/blocking/block-modal.tsx @@ -1,9 +1,9 @@ import * as C from '@/constants' import * as React from 'react' import * as Kb from '@/common-adapters' -import * as Chat from '@/constants/chat2' -import {useTeamsState} from '@/constants/teams' -import {useUsersState} from '@/constants/users' +import * as Chat from '@/stores/chat2' +import {useTeamsState} from '@/stores/teams' +import {useUsersState} from '@/stores/users' // Type for extra RouteProp passed to block modal sometimes when launching the // modal from specific places from the app. diff --git a/shared/chat/blocking/invitation-to-block.tsx b/shared/chat/blocking/invitation-to-block.tsx index e7ba0b2f0950..9e124e37d688 100644 --- a/shared/chat/blocking/invitation-to-block.tsx +++ b/shared/chat/blocking/invitation-to-block.tsx @@ -1,8 +1,8 @@ -import * as Chat from '@/constants/chat2' -import {useProfileState} from '@/constants/profile' +import * as Chat from '@/stores/chat2' +import {useProfileState} from '@/stores/profile' import * as Kb from '@/common-adapters' import {useSafeNavigation} from '@/util/safe-navigation' -import {useCurrentUserState} from '@/constants/current-user' +import {useCurrentUserState} from '@/stores/current-user' const BlockButtons = () => { const nav = useSafeNavigation() diff --git a/shared/chat/chat-button.tsx b/shared/chat/chat-button.tsx index 1524bf4e37a2..d565cffd15b4 100644 --- a/shared/chat/chat-button.tsx +++ b/shared/chat/chat-button.tsx @@ -1,7 +1,7 @@ import * as C from '@/constants' -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as Styles from '@/styles' -import {useConfigState} from '@/constants/config' +import {useConfigState} from '@/stores/config' import WaitingButton from '@/common-adapters/waiting-button' import Icon from '@/common-adapters/icon' diff --git a/shared/chat/conversation/attachment-fullscreen/hooks.tsx b/shared/chat/conversation/attachment-fullscreen/hooks.tsx index 584d3cdeacc9..3c6c21497450 100644 --- a/shared/chat/conversation/attachment-fullscreen/hooks.tsx +++ b/shared/chat/conversation/attachment-fullscreen/hooks.tsx @@ -1,9 +1,9 @@ import * as React from 'react' import * as C from '@/constants' -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import type * as T from '@/constants/types' import {maxWidth, maxHeight} from '../messages/attachment/shared' -import {useFSState} from '@/constants/fs' +import {useFSState} from '@/stores/fs' const blankMessage = Chat.makeMessageAttachment({}) export const useData = (initialOrdinal: T.Chat.Ordinal) => { @@ -37,7 +37,7 @@ export const useData = (initialOrdinal: T.Chat.Ordinal) => { }, [onSwitchAttachment]) const openLocalPathInSystemFileManagerDesktop = useFSState( - s => s.dispatch.dynamic.openLocalPathInSystemFileManagerDesktop + s => s.dispatch.defer.openLocalPathInSystemFileManagerDesktop ) const navigateUp = C.useRouterState(s => s.dispatch.navigateUp) const showInfoPanel = Chat.useChatContext(s => s.dispatch.showInfoPanel) diff --git a/shared/chat/conversation/attachment-get-titles.tsx b/shared/chat/conversation/attachment-get-titles.tsx index f5658e3078a8..609cbee8610b 100644 --- a/shared/chat/conversation/attachment-get-titles.tsx +++ b/shared/chat/conversation/attachment-get-titles.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as T from '@/constants/types' import * as React from 'react' import * as Kb from '@/common-adapters' diff --git a/shared/chat/conversation/bot/confirm.tsx b/shared/chat/conversation/bot/confirm.tsx index 4f0c8230ebbc..6d7c001fb176 100644 --- a/shared/chat/conversation/bot/confirm.tsx +++ b/shared/chat/conversation/bot/confirm.tsx @@ -1,7 +1,7 @@ import * as React from 'react' import * as Kb from '@/common-adapters' import * as C from '@/constants' -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import type * as T from '@/constants/types' import {useBotConversationIDKey} from './install' diff --git a/shared/chat/conversation/bot/install.tsx b/shared/chat/conversation/bot/install.tsx index c1e24c343beb..88c1f3a90369 100644 --- a/shared/chat/conversation/bot/install.tsx +++ b/shared/chat/conversation/bot/install.tsx @@ -1,12 +1,12 @@ import * as C from '@/constants' -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as Kb from '@/common-adapters' -import * as Teams from '@/constants/teams' +import * as Teams from '@/stores/teams' import * as React from 'react' import ChannelPicker from './channel-picker' import openURL from '@/util/open-url' import * as T from '@/constants/types' -import {useBotsState} from '@/constants/bots' +import {useBotsState} from '@/stores/bots' import {useAllChannelMetas} from '@/teams/common/channel-hooks' const RestrictedItem = '---RESTRICTED---' diff --git a/shared/chat/conversation/bot/search.tsx b/shared/chat/conversation/bot/search.tsx index 3a3dd2ab36f3..ec1309ad25f6 100644 --- a/shared/chat/conversation/bot/search.tsx +++ b/shared/chat/conversation/bot/search.tsx @@ -1,11 +1,11 @@ import * as C from '@/constants' -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as Kb from '@/common-adapters' import * as React from 'react' import debounce from 'lodash/debounce' import type * as T from '@/constants/types' import {Bot} from '../info-panel/bot' -import {getFeaturedSorted, useBotsState} from '@/constants/bots' +import {getFeaturedSorted, useBotsState} from '@/stores/bots' type Props = {teamID?: T.Teams.TeamID} diff --git a/shared/chat/conversation/bot/team-picker.tsx b/shared/chat/conversation/bot/team-picker.tsx index 23c869eeff54..b62b38be91c9 100644 --- a/shared/chat/conversation/bot/team-picker.tsx +++ b/shared/chat/conversation/bot/team-picker.tsx @@ -5,7 +5,7 @@ import * as T from '@/constants/types' import {Avatars, TeamAvatar} from '@/chat/avatars' import debounce from 'lodash/debounce' import logger from '@/logger' -import {useBotsState} from '@/constants/bots' +import {useBotsState} from '@/stores/bots' type Props = {botUsername: string} diff --git a/shared/chat/conversation/bottom-banner.tsx b/shared/chat/conversation/bottom-banner.tsx index 3b845cd09e92..878d44341538 100644 --- a/shared/chat/conversation/bottom-banner.tsx +++ b/shared/chat/conversation/bottom-banner.tsx @@ -1,12 +1,13 @@ import * as C from '@/constants' -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as Kb from '@/common-adapters' import * as React from 'react' import _openSMS from '@/util/sms' import {assertionToDisplay} from '@/common-adapters/usernames' import type {Props as TextProps} from '@/common-adapters/text' -import {useUsersState} from '@/constants/users' -import {useFollowerState} from '@/constants/followers' +import {useUsersState} from '@/stores/users' +import {useFollowerState} from '@/stores/followers' +import {showShareActionSheet} from '@/util/platform-specific' const installMessage = `I sent you encrypted messages on Keybase. You can install it here: https://keybase.io/phone-app` @@ -16,7 +17,7 @@ const Invite = () => { const users = participantInfoAll.filter(p => p.includes('@')) const openShareSheet = () => { - C.PlatformSpecific.showShareActionSheet({ + showShareActionSheet({ message: installMessage, mimeType: 'text/plain', }) diff --git a/shared/chat/conversation/command-markdown.tsx b/shared/chat/conversation/command-markdown.tsx index 1f434bc4d280..10c20f1c5509 100644 --- a/shared/chat/conversation/command-markdown.tsx +++ b/shared/chat/conversation/command-markdown.tsx @@ -1,4 +1,4 @@ -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as Kb from '@/common-adapters' const CommandMarkdown = () => { diff --git a/shared/chat/conversation/command-status.tsx b/shared/chat/conversation/command-status.tsx index 9a005c52a13c..dc67888d461c 100644 --- a/shared/chat/conversation/command-status.tsx +++ b/shared/chat/conversation/command-status.tsx @@ -1,7 +1,7 @@ -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as T from '@/constants/types' import * as Kb from '@/common-adapters' -import {useConfigState} from '@/constants/config' +import {useConfigState} from '@/stores/config' const empty = { actions: [], @@ -13,7 +13,7 @@ const Container = () => { const info = Chat.useChatContext(s => s.commandStatus) const _info = info || empty - const onOpenAppSettings = useConfigState(s => s.dispatch.dynamic.openAppSettings) + const onOpenAppSettings = useConfigState(s => s.dispatch.defer.openAppSettings) const setCommandStatusInfo = Chat.useChatContext(s => s.dispatch.setCommandStatusInfo) const onCancel = () => { setCommandStatusInfo() diff --git a/shared/chat/conversation/container.tsx b/shared/chat/conversation/container.tsx index 0cb2f2d21908..d892d3f39949 100644 --- a/shared/chat/conversation/container.tsx +++ b/shared/chat/conversation/container.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import Normal from './normal/container' import NoConversation from './no-conversation' import Error from './error' diff --git a/shared/chat/conversation/error.tsx b/shared/chat/conversation/error.tsx index c4762dc4f87a..0c9266bb235c 100644 --- a/shared/chat/conversation/error.tsx +++ b/shared/chat/conversation/error.tsx @@ -1,4 +1,4 @@ -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as Kb from '@/common-adapters' const ConversationError = () => { diff --git a/shared/chat/conversation/fwd-msg.tsx b/shared/chat/conversation/fwd-msg.tsx index 53ec16deb001..265d9e69c756 100644 --- a/shared/chat/conversation/fwd-msg.tsx +++ b/shared/chat/conversation/fwd-msg.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as React from 'react' import * as Kb from '@/common-adapters' import * as T from '@/constants/types' diff --git a/shared/chat/conversation/giphy/hooks.tsx b/shared/chat/conversation/giphy/hooks.tsx index bad968dd56aa..b2aea6c79b09 100644 --- a/shared/chat/conversation/giphy/hooks.tsx +++ b/shared/chat/conversation/giphy/hooks.tsx @@ -1,4 +1,4 @@ -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' export const useHooks = () => { const giphy = Chat.useChatContext(s => s.giphyResult) diff --git a/shared/chat/conversation/header-area/index.native.tsx b/shared/chat/conversation/header-area/index.native.tsx index c2b82732a4aa..ceaaff316fdd 100644 --- a/shared/chat/conversation/header-area/index.native.tsx +++ b/shared/chat/conversation/header-area/index.native.tsx @@ -1,6 +1,6 @@ import * as C from '@/constants' -import * as Chat from '@/constants/chat2' -import {useProfileState} from '@/constants/profile' +import * as Chat from '@/stores/chat2' +import {useProfileState} from '@/stores/profile' import * as Kb from '@/common-adapters' import * as React from 'react' import type {HeaderBackButtonProps} from '@react-navigation/elements' @@ -9,8 +9,8 @@ import {Keyboard} from 'react-native' // import {DebugChatDumpContext} from '@/constants/chat2/debug' import {assertionToDisplay} from '@/common-adapters/usernames' import {useSafeAreaFrame} from 'react-native-safe-area-context' -import {useUsersState} from '@/constants/users' -import {useCurrentUserState} from '@/constants/current-user' +import {useUsersState} from '@/stores/users' +import {useCurrentUserState} from '@/stores/current-user' export const HeaderAreaRight = () => { const conversationIDKey = Chat.useChatContext(s => s.id) diff --git a/shared/chat/conversation/info-panel/add-people.tsx b/shared/chat/conversation/info-panel/add-people.tsx index 9e7492a91f92..724cd29ec1c1 100644 --- a/shared/chat/conversation/info-panel/add-people.tsx +++ b/shared/chat/conversation/info-panel/add-people.tsx @@ -1,5 +1,5 @@ -import * as Chat from '@/constants/chat2' -import {useTeamsState} from '@/constants/teams' +import * as Chat from '@/stores/chat2' +import {useTeamsState} from '@/stores/teams' import * as React from 'react' import * as Kb from '@/common-adapters' diff --git a/shared/chat/conversation/info-panel/add-to-channel.tsx b/shared/chat/conversation/info-panel/add-to-channel.tsx index 59bfd1d8e9ca..2109aaae2717 100644 --- a/shared/chat/conversation/info-panel/add-to-channel.tsx +++ b/shared/chat/conversation/info-panel/add-to-channel.tsx @@ -1,7 +1,7 @@ import * as C from '@/constants' -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as React from 'react' -import * as Teams from '@/constants/teams' +import * as Teams from '@/stores/teams' import * as Kb from '@/common-adapters' import {useSafeNavigation} from '@/util/safe-navigation' import * as T from '@/constants/types' diff --git a/shared/chat/conversation/info-panel/attachments.tsx b/shared/chat/conversation/info-panel/attachments.tsx index 451fa0a9bf98..61b7fb7608a9 100644 --- a/shared/chat/conversation/info-panel/attachments.tsx +++ b/shared/chat/conversation/info-panel/attachments.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as Kb from '@/common-adapters' import type {StylesTextCrossPlatform} from '@/common-adapters/text' import * as T from '@/constants/types' @@ -8,7 +8,7 @@ import chunk from 'lodash/chunk' import {formatAudioRecordDuration, formatTimeForMessages} from '@/util/timestamp' import {infoPanelWidth} from './common' import {useMessagePopup} from '../messages/message-popup' -import {useFSState} from '@/constants/fs' +import {useFSState} from '@/stores/fs' type Props = { commonSections: ReadonlyArray
@@ -499,7 +499,7 @@ export const useAttachmentSections = ( } const openLocalPathInSystemFileManagerDesktop = useFSState( - s => s.dispatch.dynamic.openLocalPathInSystemFileManagerDesktop + s => s.dispatch.defer.openLocalPathInSystemFileManagerDesktop ) const onShowInFinder = (message: T.Chat.MessageAttachment) => message.downloadPath && openLocalPathInSystemFileManagerDesktop?.(message.downloadPath) diff --git a/shared/chat/conversation/info-panel/bot.tsx b/shared/chat/conversation/info-panel/bot.tsx index 9acfef03a51a..1152170c6d5d 100644 --- a/shared/chat/conversation/info-panel/bot.tsx +++ b/shared/chat/conversation/info-panel/bot.tsx @@ -1,11 +1,11 @@ import * as C from '@/constants' -import * as Chat from '@/constants/chat2' -import * as Teams from '@/constants/teams' +import * as Chat from '@/stores/chat2' +import * as Teams from '@/stores/teams' import * as Kb from '@/common-adapters' import * as React from 'react' import type * as T from '@/constants/types' -import {getFeaturedSorted, useBotsState} from '@/constants/bots' -import {useUsersState} from '@/constants/users' +import {getFeaturedSorted, useBotsState} from '@/stores/bots' +import {useUsersState} from '@/stores/users' type AddToChannelProps = { conversationIDKey: T.Chat.ConversationIDKey diff --git a/shared/chat/conversation/info-panel/common.tsx b/shared/chat/conversation/info-panel/common.tsx index 1de1bb893716..d01c196b8579 100644 --- a/shared/chat/conversation/info-panel/common.tsx +++ b/shared/chat/conversation/info-panel/common.tsx @@ -1,5 +1,5 @@ -import type * as Chat from '@/constants/chat2' -import {useTeamsState} from '@/constants/teams' +import type * as Chat from '@/stores/chat2' +import {useTeamsState} from '@/stores/teams' import * as React from 'react' import * as Styles from '@/styles' import type * as T from '@/constants/types' diff --git a/shared/chat/conversation/info-panel/header.tsx b/shared/chat/conversation/info-panel/header.tsx index 121f86d02404..6d45cc0ca533 100644 --- a/shared/chat/conversation/info-panel/header.tsx +++ b/shared/chat/conversation/info-panel/header.tsx @@ -1,6 +1,6 @@ -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as React from 'react' -import * as Teams from '@/constants/teams' +import * as Teams from '@/stores/teams' import * as Kb from '@/common-adapters' import InfoPanelMenu from './menu' import * as InfoPanelCommon from './common' diff --git a/shared/chat/conversation/info-panel/index.tsx b/shared/chat/conversation/info-panel/index.tsx index f2bcd79a3346..0bf1adf3fad2 100644 --- a/shared/chat/conversation/info-panel/index.tsx +++ b/shared/chat/conversation/info-panel/index.tsx @@ -1,6 +1,6 @@ -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as Kb from '@/common-adapters' -import * as Teams from '@/constants/teams' +import * as Teams from '@/stores/teams' import * as React from 'react' import {AdhocHeader, TeamHeader} from './header' import SettingsList from './settings' diff --git a/shared/chat/conversation/info-panel/members.tsx b/shared/chat/conversation/info-panel/members.tsx index 5c1242ee02d7..72b3a4405985 100644 --- a/shared/chat/conversation/info-panel/members.tsx +++ b/shared/chat/conversation/info-panel/members.tsx @@ -1,12 +1,12 @@ import * as C from '@/constants' -import * as Chat from '@/constants/chat2' -import {useProfileState} from '@/constants/profile' -import * as Teams from '@/constants/teams' +import * as Chat from '@/stores/chat2' +import {useProfileState} from '@/stores/profile' +import * as Teams from '@/stores/teams' import * as React from 'react' import * as Kb from '@/common-adapters' import * as T from '@/constants/types' import Participant from './participant' -import {useUsersState} from '@/constants/users' +import {useUsersState} from '@/stores/users' type Props = { commonSections: ReadonlyArray
diff --git a/shared/chat/conversation/info-panel/menu.tsx b/shared/chat/conversation/info-panel/menu.tsx index eac5285c62f0..0d1bd8e20c50 100644 --- a/shared/chat/conversation/info-panel/menu.tsx +++ b/shared/chat/conversation/info-panel/menu.tsx @@ -1,14 +1,14 @@ import * as C from '@/constants' -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as Kb from '@/common-adapters' -import * as Teams from '@/constants/teams' +import * as Teams from '@/stores/teams' import * as React from 'react' import * as T from '@/constants/types' import * as InfoPanelCommon from './common' import {Avatars, TeamAvatar} from '@/chat/avatars' import {TeamsSubscriberMountOnly} from '@/teams/subscriber' -import {useUsersState} from '@/constants/users' -import {useCurrentUserState} from '@/constants/current-user' +import {useUsersState} from '@/stores/users' +import {useCurrentUserState} from '@/stores/current-user' export type OwnProps = { attachTo?: React.RefObject diff --git a/shared/chat/conversation/info-panel/settings/index.tsx b/shared/chat/conversation/info-panel/settings/index.tsx index 6535768ef672..7656d2a941d9 100644 --- a/shared/chat/conversation/info-panel/settings/index.tsx +++ b/shared/chat/conversation/info-panel/settings/index.tsx @@ -1,13 +1,13 @@ import * as C from '@/constants' -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as Kb from '@/common-adapters' -import * as Teams from '@/constants/teams' +import * as Teams from '@/stores/teams' import * as T from '@/constants/types' import * as React from 'react' import MinWriterRole from './min-writer-role' import Notifications from './notifications' import RetentionPicker from '@/teams/team/settings-tab/retention' -import {useCurrentUserState} from '@/constants/current-user' +import {useCurrentUserState} from '@/stores/current-user' type EntityType = 'adhoc' | 'small team' | 'channel' type SettingsPanelProps = {isPreview: boolean} diff --git a/shared/chat/conversation/info-panel/settings/min-writer-role.tsx b/shared/chat/conversation/info-panel/settings/min-writer-role.tsx index a25934607682..56c8a09e4566 100644 --- a/shared/chat/conversation/info-panel/settings/min-writer-role.tsx +++ b/shared/chat/conversation/info-panel/settings/min-writer-role.tsx @@ -1,6 +1,6 @@ -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as Kb from '@/common-adapters' -import * as Teams from '@/constants/teams' +import * as Teams from '@/stores/teams' import * as React from 'react' import * as Style from '@/styles' import type * as T from '@/constants/types' diff --git a/shared/chat/conversation/info-panel/settings/notifications.tsx b/shared/chat/conversation/info-panel/settings/notifications.tsx index 53bbf518206f..fdfcb3a0bc94 100644 --- a/shared/chat/conversation/info-panel/settings/notifications.tsx +++ b/shared/chat/conversation/info-panel/settings/notifications.tsx @@ -1,4 +1,4 @@ -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as React from 'react' import * as Kb from '@/common-adapters' import type * as T from '@/constants/types' diff --git a/shared/chat/conversation/input-area/container.tsx b/shared/chat/conversation/input-area/container.tsx index 7ce09e75c96b..e318566ef103 100644 --- a/shared/chat/conversation/input-area/container.tsx +++ b/shared/chat/conversation/input-area/container.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import Normal from './normal2' import Preview from './preview' import ThreadSearch from '../search' diff --git a/shared/chat/conversation/input-area/location-popup.d.ts b/shared/chat/conversation/input-area/location-popup.d.ts new file mode 100644 index 000000000000..251868e41272 --- /dev/null +++ b/shared/chat/conversation/input-area/location-popup.d.ts @@ -0,0 +1,3 @@ +import type * as React from 'react' +declare const LocationPopup: () => React.ReactNode +export default LocationPopup diff --git a/shared/chat/conversation/input-area/location-popup.desktop.tsx b/shared/chat/conversation/input-area/location-popup.desktop.tsx new file mode 100644 index 000000000000..c07558b9f77f --- /dev/null +++ b/shared/chat/conversation/input-area/location-popup.desktop.tsx @@ -0,0 +1,2 @@ +const LocationPopup = () => null +export default LocationPopup diff --git a/shared/chat/conversation/input-area/location-popup.tsx b/shared/chat/conversation/input-area/location-popup.native.tsx similarity index 70% rename from shared/chat/conversation/input-area/location-popup.tsx rename to shared/chat/conversation/input-area/location-popup.native.tsx index fff570f758c7..ec5fef04455f 100644 --- a/shared/chat/conversation/input-area/location-popup.tsx +++ b/shared/chat/conversation/input-area/location-popup.native.tsx @@ -1,11 +1,53 @@ import * as C from '@/constants' -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as React from 'react' -import {useConfigState} from '@/constants/config' +import {useConfigState} from '@/stores/config' +import logger from '@/logger' import * as Kb from '@/common-adapters' import * as T from '@/constants/types' import LocationMap from '@/chat/location-map' -import {useCurrentUserState} from '@/constants/current-user' +import {useCurrentUserState} from '@/stores/current-user' +import {requestLocationPermission} from '@/util/platform-specific' +import * as ExpoLocation from 'expo-location' + +const useWatchPosition = (conversationIDKey: T.Chat.ConversationIDKey) => { + const updateLastCoord = Chat.useChatState(s => s.dispatch.updateLastCoord) + const setCommandStatusInfo = Chat.useChatContext(s => s.dispatch.setCommandStatusInfo) + React.useEffect(() => { + let unsub = () => {} + logger.info('[location] perms check due to map') + const f = async () => { + try { + await requestLocationPermission(T.RPCChat.UIWatchPositionPerm.base) + const sub = await ExpoLocation.watchPositionAsync( + {accuracy: ExpoLocation.LocationAccuracy.Highest}, + (location: ExpoLocation.LocationObject) => { + const coord = { + accuracy: Math.floor(location.coords.accuracy ?? 0), + lat: location.coords.latitude, + lon: location.coords.longitude, + } + updateLastCoord(coord) + } + ) + unsub = () => sub.remove() + } catch (_error) { + const error = _error as {message?: string} + logger.info('failed to get location: ' + error.message) + setCommandStatusInfo({ + actions: [T.RPCChat.UICommandStatusActionTyp.appsettings], + displayText: `Failed to access location. ${error.message}`, + displayType: T.RPCChat.UICommandStatusDisplayTyp.error, + }) + } + } + + C.ignorePromise(f()) + return () => { + unsub() + } + }, [conversationIDKey, updateLastCoord, setCommandStatusInfo]) +} const LocationPopup = () => { const conversationIDKey = Chat.useChatContext(s => s.id) @@ -20,24 +62,14 @@ const LocationPopup = () => { const onClose = () => { clearModals() } - const onSettings = useConfigState(s => s.dispatch.dynamic.openAppSettings) + const onSettings = useConfigState(s => s.dispatch.defer.openAppSettings) const sendMessage = Chat.useChatContext(s => s.dispatch.sendMessage) const onLocationShare = (duration: string) => { onClose() sendMessage(duration ? `/location live ${duration}` : '/location') } - React.useEffect(() => { - let unwatch: undefined | (() => void) - C.PlatformSpecific.watchPositionForMap(conversationIDKey) - .then(unsub => { - unwatch = unsub - }) - .catch(() => {}) - return () => { - unwatch?.() - } - }, [conversationIDKey]) + useWatchPosition(conversationIDKey) const width = Math.ceil(Kb.Styles.dimensionWidth) const height = Math.ceil(Kb.Styles.dimensionHeight - 320) diff --git a/shared/chat/conversation/input-area/normal2/index.tsx b/shared/chat/conversation/input-area/normal2/index.tsx index 8ceea809bb21..248c3e9db0bd 100644 --- a/shared/chat/conversation/input-area/normal2/index.tsx +++ b/shared/chat/conversation/input-area/normal2/index.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as Kb from '@/common-adapters' import * as React from 'react' import CommandMarkdown from '../../command-markdown' @@ -13,7 +13,7 @@ import {infoPanelWidthTablet} from '../../info-panel/common' import {assertionToDisplay} from '@/common-adapters/usernames' import {FocusContext, ScrollContext} from '@/chat/conversation/normal/context' import type {RefType as Input2Ref} from '@/common-adapters/input2' -import {useCurrentUserState} from '@/constants/current-user' +import {useCurrentUserState} from '@/stores/current-user' const useHintText = (p: { isExploding: boolean diff --git a/shared/chat/conversation/input-area/normal2/moremenu-popup.tsx b/shared/chat/conversation/input-area/normal2/moremenu-popup.native.tsx similarity index 97% rename from shared/chat/conversation/input-area/normal2/moremenu-popup.tsx rename to shared/chat/conversation/input-area/normal2/moremenu-popup.native.tsx index fc790cc73f5a..e75a2301b57f 100644 --- a/shared/chat/conversation/input-area/normal2/moremenu-popup.tsx +++ b/shared/chat/conversation/input-area/normal2/moremenu-popup.native.tsx @@ -1,4 +1,4 @@ -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as Kb from '@/common-adapters' type Props = { diff --git a/shared/chat/conversation/input-area/normal2/platform-input.desktop.tsx b/shared/chat/conversation/input-area/normal2/platform-input.desktop.tsx index d56a9ef8bba8..5533d8515bec 100644 --- a/shared/chat/conversation/input-area/normal2/platform-input.desktop.tsx +++ b/shared/chat/conversation/input-area/normal2/platform-input.desktop.tsx @@ -1,4 +1,4 @@ -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as T from '@/constants/types' import * as Kb from '@/common-adapters' import * as React from 'react' diff --git a/shared/chat/conversation/input-area/normal2/platform-input.native.tsx b/shared/chat/conversation/input-area/normal2/platform-input.native.tsx index 4194566d7d99..496f7ebb8c5a 100644 --- a/shared/chat/conversation/input-area/normal2/platform-input.native.tsx +++ b/shared/chat/conversation/input-area/normal2/platform-input.native.tsx @@ -1,11 +1,11 @@ import * as C from '@/constants' -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as Kb from '@/common-adapters' import * as React from 'react' import AudioRecorder from '@/chat/audio/audio-recorder.native' import FilePickerPopup from '../filepicker-popup' import {onHWKeyPressed, removeOnHWKeyPressed} from 'react-native-kb' -import MoreMenuPopup from './moremenu-popup' +import MoreMenuPopup from './moremenu-popup.native' import SetExplodingMessagePicker from './set-explode-popup' import Typing from './typing' import type * as ImagePicker from 'expo-image-picker' @@ -28,7 +28,7 @@ import logger from '@/logger' import {AudioSendWrapper} from '@/chat/audio/audio-send.native' import {usePickerState} from '@/chat/emoji-picker/use-picker' import type {RefType as Input2Ref, Props as Input2Props} from '@/common-adapters/input2' -import {useConfigState} from '@/constants/config' +import {useConfigState} from '@/stores/config' const singleLineHeight = 36 const threeLineHeight = 78 diff --git a/shared/chat/conversation/input-area/normal2/set-explode-popup/hooks.tsx b/shared/chat/conversation/input-area/normal2/set-explode-popup/hooks.tsx index 1552f0f23d43..781e2a289d60 100644 --- a/shared/chat/conversation/input-area/normal2/set-explode-popup/hooks.tsx +++ b/shared/chat/conversation/input-area/normal2/set-explode-popup/hooks.tsx @@ -1,9 +1,14 @@ -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as React from 'react' import type * as T from '@/constants/types' import type {Props} from '.' -const messageExplodeDescriptions: T.Chat.MessageExplodeDescription[] = [ +export type MessageExplodeDescription = { + text: string + seconds: number +} + +const messageExplodeDescriptions: MessageExplodeDescription[] = [ {seconds: 30, text: '30 seconds'}, {seconds: 300, text: '5 minutes'}, {seconds: 3600, text: '60 minutes'}, diff --git a/shared/chat/conversation/input-area/normal2/set-explode-popup/index.desktop.tsx b/shared/chat/conversation/input-area/normal2/set-explode-popup/index.desktop.tsx index ed46e8c6e1f2..255ad7b0c55e 100644 --- a/shared/chat/conversation/input-area/normal2/set-explode-popup/index.desktop.tsx +++ b/shared/chat/conversation/input-area/normal2/set-explode-popup/index.desktop.tsx @@ -1,7 +1,7 @@ -import type * as T from '@/constants/types' import * as Kb from '@/common-adapters' import type {Props} from '.' import useHooks from './hooks' +import type {MessageExplodeDescription} from './hooks' const quantityTextStyle = Kb.Styles.platformStyles({ common: { @@ -13,7 +13,7 @@ const quantityTextStyle = Kb.Styles.platformStyles({ }) type ItemProps = { - desc: T.Chat.MessageExplodeDescription + desc: MessageExplodeDescription selected: boolean } diff --git a/shared/chat/conversation/input-area/normal2/typing.tsx b/shared/chat/conversation/input-area/normal2/typing.tsx index b9a42ecff2ee..c274f4396aae 100644 --- a/shared/chat/conversation/input-area/normal2/typing.tsx +++ b/shared/chat/conversation/input-area/normal2/typing.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as React from 'react' import * as Kb from '@/common-adapters' diff --git a/shared/chat/conversation/input-area/preview.tsx b/shared/chat/conversation/input-area/preview.tsx index 3f118f4469ac..64e25419c5f8 100644 --- a/shared/chat/conversation/input-area/preview.tsx +++ b/shared/chat/conversation/input-area/preview.tsx @@ -1,4 +1,4 @@ -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as React from 'react' import * as Kb from '@/common-adapters' diff --git a/shared/chat/conversation/input-area/suggestors/channels.tsx b/shared/chat/conversation/input-area/suggestors/channels.tsx index 0fcf0b2bb3ba..4e0bc22ec650 100644 --- a/shared/chat/conversation/input-area/suggestors/channels.tsx +++ b/shared/chat/conversation/input-area/suggestors/channels.tsx @@ -1,7 +1,7 @@ import * as C from '@/constants' -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as T from '@/constants/types' -import * as Teams from '@/constants/teams' +import * as Teams from '@/stores/teams' import * as Common from './common' import * as Kb from '@/common-adapters' diff --git a/shared/chat/conversation/input-area/suggestors/commands.tsx b/shared/chat/conversation/input-area/suggestors/commands.tsx index 80f9f7753c81..4cfa2041c692 100644 --- a/shared/chat/conversation/input-area/suggestors/commands.tsx +++ b/shared/chat/conversation/input-area/suggestors/commands.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as T from '@/constants/types' import * as Common from './common' import * as Kb from '@/common-adapters' diff --git a/shared/chat/conversation/input-area/suggestors/emoji.tsx b/shared/chat/conversation/input-area/suggestors/emoji.tsx index 2af8b5366e0e..df285c3eb4d2 100644 --- a/shared/chat/conversation/input-area/suggestors/emoji.tsx +++ b/shared/chat/conversation/input-area/suggestors/emoji.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as Common from './common' import * as Kb from '@/common-adapters' import * as React from 'react' diff --git a/shared/chat/conversation/input-area/suggestors/index.tsx b/shared/chat/conversation/input-area/suggestors/index.tsx index d82cd92e2019..a075d4262c9c 100644 --- a/shared/chat/conversation/input-area/suggestors/index.tsx +++ b/shared/chat/conversation/input-area/suggestors/index.tsx @@ -1,5 +1,5 @@ import * as Channels from './channels' -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as Commands from './commands' import * as Emoji from './emoji' import * as Kb from '@/common-adapters' diff --git a/shared/chat/conversation/input-area/suggestors/users.tsx b/shared/chat/conversation/input-area/suggestors/users.tsx index 841ea76922fd..f951b0e3536a 100644 --- a/shared/chat/conversation/input-area/suggestors/users.tsx +++ b/shared/chat/conversation/input-area/suggestors/users.tsx @@ -1,11 +1,11 @@ import * as C from '@/constants' -import * as Chat from '@/constants/chat2' -import {useTeamsState} from '@/constants/teams' +import * as Chat from '@/stores/chat2' +import {useTeamsState} from '@/stores/teams' import * as T from '@/constants/types' import * as Common from './common' import * as Kb from '@/common-adapters' import * as React from 'react' -import {useUsersState} from '@/constants/users' +import {useUsersState} from '@/stores/users' export const transformer = ( input: { diff --git a/shared/chat/conversation/list-area/hooks.tsx b/shared/chat/conversation/list-area/hooks.tsx index 6208ee8417e4..0570ca7ed8fd 100644 --- a/shared/chat/conversation/list-area/hooks.tsx +++ b/shared/chat/conversation/list-area/hooks.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as React from 'react' import JumpToRecent from './jump-to-recent' import type * as T from '@/constants/types' diff --git a/shared/chat/conversation/list-area/index.desktop.tsx b/shared/chat/conversation/list-area/index.desktop.tsx index 10742edc1a83..a7558654025f 100644 --- a/shared/chat/conversation/list-area/index.desktop.tsx +++ b/shared/chat/conversation/list-area/index.desktop.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as Kb from '@/common-adapters' import * as Hooks from './hooks' import * as React from 'react' @@ -17,7 +17,7 @@ import logger from '@/logger' import shallowEqual from 'shallowequal' import useResizeObserver from '@/util/use-resize-observer.desktop' import useIntersectionObserver from '@/util/use-intersection-observer' -import {useConfigState} from '@/constants/config' +import {useConfigState} from '@/stores/config' // Infinite scrolling list. // We group messages into a series of Waypoints. When the waypoint exits the screen we replace it with a single div instead @@ -506,7 +506,7 @@ const ThreadWrapper = React.memo(function ThreadWrapper() { ) const {conversationIDKey, editingOrdinal, centeredOrdinal} = data const {containsLatestMessage, messageOrdinals, loaded, messageTypeMap} = data - const copyToClipboard = useConfigState(s => s.dispatch.dynamic.copyToClipboard) + const copyToClipboard = useConfigState(s => s.dispatch.defer.copyToClipboard) const listRef = React.useRef(null) const _setListRef = React.useCallback((r: HTMLDivElement | null) => { listRef.current = r diff --git a/shared/chat/conversation/list-area/index.native.tsx b/shared/chat/conversation/list-area/index.native.tsx index 129406200774..4a4ea8308087 100644 --- a/shared/chat/conversation/list-area/index.native.tsx +++ b/shared/chat/conversation/list-area/index.native.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as T from '@/constants/types' import * as Hooks from './hooks' import * as Kb from '@/common-adapters' diff --git a/shared/chat/conversation/load-status.tsx b/shared/chat/conversation/load-status.tsx index e992dd920145..786061c50ab9 100644 --- a/shared/chat/conversation/load-status.tsx +++ b/shared/chat/conversation/load-status.tsx @@ -1,4 +1,4 @@ -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as React from 'react' import * as Kb from '@/common-adapters' import * as T from '@/constants/types' diff --git a/shared/chat/conversation/messages/account-payment/container.tsx b/shared/chat/conversation/messages/account-payment/container.tsx index 7aec89fb2655..e728ab2606eb 100644 --- a/shared/chat/conversation/messages/account-payment/container.tsx +++ b/shared/chat/conversation/messages/account-payment/container.tsx @@ -1,8 +1,8 @@ -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as Kb from '@/common-adapters' import type * as T from '@/constants/types' import MarkdownMemo from '@/wallets/markdown-memo' -import {useCurrentUserState} from '@/constants/current-user' +import {useCurrentUserState} from '@/stores/current-user' // Props for rendering the loading indicator const loadingProps = { diff --git a/shared/chat/conversation/messages/account-payment/wrapper.tsx b/shared/chat/conversation/messages/account-payment/wrapper.tsx index 7e2bd8bf7b79..619ed8222849 100644 --- a/shared/chat/conversation/messages/account-payment/wrapper.tsx +++ b/shared/chat/conversation/messages/account-payment/wrapper.tsx @@ -1,4 +1,4 @@ -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as React from 'react' import {WrapperMessage, useCommon, type Props} from '../wrapper/wrapper' import type PaymentMessageType from './container' diff --git a/shared/chat/conversation/messages/attachment/audio.tsx b/shared/chat/conversation/messages/attachment/audio.tsx index eb299725bc45..cd32586199c4 100644 --- a/shared/chat/conversation/messages/attachment/audio.tsx +++ b/shared/chat/conversation/messages/attachment/audio.tsx @@ -1,9 +1,9 @@ -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import type * as T from '@/constants/types' import * as Kb from '@/common-adapters' import {useOrdinal} from '../ids-context' import AudioPlayer from '@/chat/audio/audio-player' -import {useFSState} from '@/constants/fs' +import {useFSState} from '@/stores/fs' const missingMessage = Chat.makeMessageAttachment() @@ -25,7 +25,7 @@ const AudioAttachment = () => { const progressLabel = Chat.messageAttachmentTransferStateToProgressLabel(message.transferState) const hasProgress = messageAttachmentHasProgress(message) const openLocalPathInSystemFileManagerDesktop = useFSState( - s => s.dispatch.dynamic.openLocalPathInSystemFileManagerDesktop + s => s.dispatch.defer.openLocalPathInSystemFileManagerDesktop ) const onShowInFinder = () => { message.downloadPath && openLocalPathInSystemFileManagerDesktop?.(message.downloadPath) diff --git a/shared/chat/conversation/messages/attachment/file.tsx b/shared/chat/conversation/messages/attachment/file.tsx index 4d1dc8419ee6..bfcbe3bbc11f 100644 --- a/shared/chat/conversation/messages/attachment/file.tsx +++ b/shared/chat/conversation/messages/attachment/file.tsx @@ -1,6 +1,6 @@ import * as C from '@/constants' -import * as Chat from '@/constants/chat2' -import * as Crypto from '@/constants/crypto' +import * as Chat from '@/stores/chat2' +import * as Crypto from '@/stores/crypto' import * as React from 'react' import {isPathSaltpack, isPathSaltpackEncrypted, isPathSaltpackSigned} from '@/util/path' import type * as T from '@/constants/types' @@ -9,7 +9,7 @@ import captialize from 'lodash/capitalize' import * as Kb from '@/common-adapters' import type {StyleOverride} from '@/common-adapters/markdown' import {getEditStyle, ShowToastAfterSaving} from './shared' -import {useFSState} from '@/constants/fs' +import {useFSState} from '@/stores/fs' type OwnProps = {showPopup: () => void} @@ -57,7 +57,7 @@ const FileContainer = React.memo(function FileContainer(p: OwnProps) { [switchTab, saltpackOpenFile] ) const openLocalPathInSystemFileManagerDesktop = useFSState( - s => s.dispatch.dynamic.openLocalPathInSystemFileManagerDesktop + s => s.dispatch.defer.openLocalPathInSystemFileManagerDesktop ) const _onShowInFinder = React.useCallback(() => { downloadPath && openLocalPathInSystemFileManagerDesktop?.(downloadPath) diff --git a/shared/chat/conversation/messages/attachment/image2/use-state.tsx b/shared/chat/conversation/messages/attachment/image2/use-state.tsx index da4c4093b538..2399086be164 100644 --- a/shared/chat/conversation/messages/attachment/image2/use-state.tsx +++ b/shared/chat/conversation/messages/attachment/image2/use-state.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import {useOrdinal} from '@/chat/conversation/messages/ids-context' import {maxWidth, maxHeight} from '../shared' diff --git a/shared/chat/conversation/messages/attachment/shared.tsx b/shared/chat/conversation/messages/attachment/shared.tsx index 379a85ae8374..3f0efebc0588 100644 --- a/shared/chat/conversation/messages/attachment/shared.tsx +++ b/shared/chat/conversation/messages/attachment/shared.tsx @@ -1,12 +1,12 @@ import * as C from '@/constants' -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as Kb from '@/common-adapters' import * as React from 'react' import * as T from '@/constants/types' import {useOrdinal} from '../ids-context' import {sharedStyles} from '../shared-styles' import {Keyboard} from 'react-native' -import {useFSState} from '@/constants/fs' +import {useFSState} from '@/stores/fs' type Props = { transferState: T.Chat.MessageAttachmentTransferState @@ -103,7 +103,7 @@ export const TransferIcon = (p: {style: Kb.Styles.StylesCrossPlatform}) => { download(ordinal) }, [ordinal, download]) - const openFinder = useFSState(s => s.dispatch.dynamic.openLocalPathInSystemFileManagerDesktop) + const openFinder = useFSState(s => s.dispatch.defer.openLocalPathInSystemFileManagerDesktop) const onFinder = React.useCallback(() => { downloadPath && openFinder?.(downloadPath) }, [openFinder, downloadPath]) diff --git a/shared/chat/conversation/messages/attachment/video/use-state.tsx b/shared/chat/conversation/messages/attachment/video/use-state.tsx index b02adc6d8a21..5fc3c9eaa684 100644 --- a/shared/chat/conversation/messages/attachment/video/use-state.tsx +++ b/shared/chat/conversation/messages/attachment/video/use-state.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import {useOrdinal} from '@/chat/conversation/messages/ids-context' import {missingMessage, maxWidth, maxHeight} from '../shared' diff --git a/shared/chat/conversation/messages/cards/make-team.tsx b/shared/chat/conversation/messages/cards/make-team.tsx index ce3ca6ea16b3..d01d46e8e954 100644 --- a/shared/chat/conversation/messages/cards/make-team.tsx +++ b/shared/chat/conversation/messages/cards/make-team.tsx @@ -1,4 +1,4 @@ -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as Kb from '@/common-adapters' const MakeTeam = () => { diff --git a/shared/chat/conversation/messages/cards/team-journey/container.tsx b/shared/chat/conversation/messages/cards/team-journey/container.tsx index 249383bf41a7..19d47a2be434 100644 --- a/shared/chat/conversation/messages/cards/team-journey/container.tsx +++ b/shared/chat/conversation/messages/cards/team-journey/container.tsx @@ -1,7 +1,7 @@ import * as C from '@/constants' -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as T from '@/constants/types' -import * as Teams from '@/constants/teams' +import * as Teams from '@/stores/teams' import * as Kb from '@/common-adapters' import {renderWelcomeMessage} from './util' import {useAllChannelMetas} from '@/teams/common/channel-hooks' diff --git a/shared/chat/conversation/messages/emoji-row.tsx b/shared/chat/conversation/messages/emoji-row.tsx index 43e13a7f324c..ffbb461d8082 100644 --- a/shared/chat/conversation/messages/emoji-row.tsx +++ b/shared/chat/conversation/messages/emoji-row.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as React from 'react' import {useOrdinal} from './ids-context' import * as Kb from '@/common-adapters' diff --git a/shared/chat/conversation/messages/message-popup/attachment.tsx b/shared/chat/conversation/messages/message-popup/attachment.tsx index 0cf6fdb9efb9..b6da989e6deb 100644 --- a/shared/chat/conversation/messages/message-popup/attachment.tsx +++ b/shared/chat/conversation/messages/message-popup/attachment.tsx @@ -1,11 +1,11 @@ import * as C from '@/constants' -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as React from 'react' import type * as T from '@/constants/types' import {type Position, fileUIName, type StylesCrossPlatform} from '@/styles' import {useItems, useHeader} from './hooks' import * as Kb from '@/common-adapters' -import {useFSState} from '@/constants/fs' +import {useFSState} from '@/stores/fs' type OwnProps = { attachTo?: React.RefObject @@ -84,7 +84,7 @@ const PopAttach = (ownProps: OwnProps) => { const onShareAttachment = C.isMobile ? _onShareAttachment : undefined const openLocalPathInSystemFileManagerDesktop = useFSState( - s => s.dispatch.dynamic.openLocalPathInSystemFileManagerDesktop + s => s.dispatch.defer.openLocalPathInSystemFileManagerDesktop ) const _onShowInFinder = React.useCallback(() => { downloadPath && openLocalPathInSystemFileManagerDesktop?.(downloadPath) diff --git a/shared/chat/conversation/messages/message-popup/exploding-header.tsx b/shared/chat/conversation/messages/message-popup/exploding-header.tsx index e390b96f7879..7d691f4b30b8 100644 --- a/shared/chat/conversation/messages/message-popup/exploding-header.tsx +++ b/shared/chat/conversation/messages/message-popup/exploding-header.tsx @@ -1,5 +1,5 @@ import * as React from 'react' -import {useProfileState} from '@/constants/profile' +import {useProfileState} from '@/stores/profile' import * as Kb from '@/common-adapters' import {formatTimeForPopup, formatTimeForRevoked, msToDHMS} from '@/util/timestamp' import {addTicker, removeTicker} from '@/util/second-timer' diff --git a/shared/chat/conversation/messages/message-popup/header.tsx b/shared/chat/conversation/messages/message-popup/header.tsx index d0221144273a..d57e76ad84f5 100644 --- a/shared/chat/conversation/messages/message-popup/header.tsx +++ b/shared/chat/conversation/messages/message-popup/header.tsx @@ -1,6 +1,6 @@ import * as Kb from '@/common-adapters' import * as React from 'react' -import {useProfileState} from '@/constants/profile' +import {useProfileState} from '@/stores/profile' import {formatTimeForPopup, formatTimeForRevoked} from '@/util/timestamp' import type * as T from '@/constants/types' diff --git a/shared/chat/conversation/messages/message-popup/hooks.tsx b/shared/chat/conversation/messages/message-popup/hooks.tsx index 6a8a35db164b..48b40ada8b7f 100644 --- a/shared/chat/conversation/messages/message-popup/hooks.tsx +++ b/shared/chat/conversation/messages/message-popup/hooks.tsx @@ -1,11 +1,11 @@ import * as React from 'react' import type * as T from '@/constants/types' import * as C from '@/constants' -import * as Chat from '@/constants/chat2' -import * as Teams from '@/constants/teams' -import {useConfigState} from '@/constants/config' -import {useProfileState} from '@/constants/profile' -import {useCurrentUserState} from '@/constants/current-user' +import * as Chat from '@/stores/chat2' +import * as Teams from '@/stores/teams' +import {useConfigState} from '@/stores/config' +import {useProfileState} from '@/stores/profile' +import {useCurrentUserState} from '@/stores/current-user' import {linkFromConvAndMessage} from '@/constants/deeplinks' import ReactionItem from './reactionitem' import MessagePopupHeader from './header' @@ -104,7 +104,7 @@ export const useItems = (ordinal: T.Chat.Ordinal, onHidden: () => void) => { : [] const convLabel = getConversationLabel(participantInfo, meta, true) - const copyToClipboard = useConfigState(s => s.dispatch.dynamic.copyToClipboard) + const copyToClipboard = useConfigState(s => s.dispatch.defer.copyToClipboard) const onCopyLink = React.useCallback(() => { copyToClipboard(linkFromConvAndMessage(convLabel, id)) }, [copyToClipboard, id, convLabel]) diff --git a/shared/chat/conversation/messages/message-popup/index.tsx b/shared/chat/conversation/messages/message-popup/index.tsx index 111c936d3678..3736bfdc3587 100644 --- a/shared/chat/conversation/messages/message-popup/index.tsx +++ b/shared/chat/conversation/messages/message-popup/index.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as Kb from '@/common-adapters' import * as React from 'react' import AttachmentMessage from './attachment' diff --git a/shared/chat/conversation/messages/message-popup/journeycard.tsx b/shared/chat/conversation/messages/message-popup/journeycard.tsx index 560590fd8ce3..f631f84246f6 100644 --- a/shared/chat/conversation/messages/message-popup/journeycard.tsx +++ b/shared/chat/conversation/messages/message-popup/journeycard.tsx @@ -1,4 +1,4 @@ -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as Kb from '@/common-adapters' import * as T from '@/constants/types' import * as React from 'react' diff --git a/shared/chat/conversation/messages/message-popup/reactionitem.tsx b/shared/chat/conversation/messages/message-popup/reactionitem.tsx index 051dce87b10c..9c734e3f529c 100644 --- a/shared/chat/conversation/messages/message-popup/reactionitem.tsx +++ b/shared/chat/conversation/messages/message-popup/reactionitem.tsx @@ -1,4 +1,4 @@ -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as Kb from '@/common-adapters' type Props = { diff --git a/shared/chat/conversation/messages/message-popup/text.tsx b/shared/chat/conversation/messages/message-popup/text.tsx index 3a8c7f28077d..66425418691d 100644 --- a/shared/chat/conversation/messages/message-popup/text.tsx +++ b/shared/chat/conversation/messages/message-popup/text.tsx @@ -1,13 +1,13 @@ import * as C from '@/constants' -import * as Chat from '@/constants/chat2' -import {useConfigState} from '@/constants/config' +import * as Chat from '@/stores/chat2' +import {useConfigState} from '@/stores/config' import * as React from 'react' import * as Kb from '@/common-adapters' import * as T from '@/constants/types' import type {Position, StylesCrossPlatform} from '@/styles' import {useItems, useHeader} from './hooks' import openURL from '@/util/open-url' -import {useCurrentUserState} from '@/constants/current-user' +import {useCurrentUserState} from '@/stores/current-user' type OwnProps = { attachTo?: React.RefObject @@ -75,7 +75,7 @@ const PopText = (ownProps: OwnProps) => { // you can reply privately *if* text message, someone else's message, and not in a 1-on-1 chat const canReplyPrivately = ['small', 'big'].includes(teamType) || numPart > 2 const navigateAppend = C.useRouterState(s => s.dispatch.navigateAppend) - const copyToClipboard = useConfigState(s => s.dispatch.dynamic.copyToClipboard) + const copyToClipboard = useConfigState(s => s.dispatch.defer.copyToClipboard) const onCopy = React.useCallback(() => { text && copyToClipboard(text) }, [copyToClipboard, text]) diff --git a/shared/chat/conversation/messages/pin/index.tsx b/shared/chat/conversation/messages/pin/index.tsx index 937d4cb6fb2c..7618510b7557 100644 --- a/shared/chat/conversation/messages/pin/index.tsx +++ b/shared/chat/conversation/messages/pin/index.tsx @@ -1,4 +1,4 @@ -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as Kb from '@/common-adapters' import type * as T from '@/constants/types' diff --git a/shared/chat/conversation/messages/pin/wrapper.tsx b/shared/chat/conversation/messages/pin/wrapper.tsx index 5cfd3cb7c7f2..67125e1e8575 100644 --- a/shared/chat/conversation/messages/pin/wrapper.tsx +++ b/shared/chat/conversation/messages/pin/wrapper.tsx @@ -1,4 +1,4 @@ -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as React from 'react' import {WrapperMessage, useCommon, type Props} from '../wrapper/wrapper' import type PinType from '.' diff --git a/shared/chat/conversation/messages/placeholder/wrapper.tsx b/shared/chat/conversation/messages/placeholder/wrapper.tsx index 5cc050f9d681..45c196e3be7a 100644 --- a/shared/chat/conversation/messages/placeholder/wrapper.tsx +++ b/shared/chat/conversation/messages/placeholder/wrapper.tsx @@ -1,4 +1,4 @@ -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as Kb from '@/common-adapters' import * as React from 'react' import * as T from '@/constants/types' diff --git a/shared/chat/conversation/messages/react-button.tsx b/shared/chat/conversation/messages/react-button.tsx index 2486bd7a1129..4ce79b009d4b 100644 --- a/shared/chat/conversation/messages/react-button.tsx +++ b/shared/chat/conversation/messages/react-button.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as React from 'react' import type {StylesCrossPlatform} from '@/styles' import {useOrdinal} from './ids-context' @@ -7,7 +7,7 @@ import * as Kb from '@/common-adapters' import type {StyleOverride} from '@/common-adapters/markdown' import {colors, darkColors} from '@/styles/colors' import {useColorScheme} from 'react-native' -import {useCurrentUserState} from '@/constants/current-user' +import {useCurrentUserState} from '@/stores/current-user' export type OwnProps = { className?: string diff --git a/shared/chat/conversation/messages/reaction-tooltip.tsx b/shared/chat/conversation/messages/reaction-tooltip.tsx index 985f3edacb4f..d196fa1180f1 100644 --- a/shared/chat/conversation/messages/reaction-tooltip.tsx +++ b/shared/chat/conversation/messages/reaction-tooltip.tsx @@ -1,11 +1,11 @@ import * as C from '@/constants' -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as Kb from '@/common-adapters' import * as React from 'react' import ReactButton from './react-button' import type * as T from '@/constants/types' import {MessageContext} from './ids-context' -import {useUsersState} from '@/constants/users' +import {useUsersState} from '@/stores/users' const positionFallbacks = ['bottom center', 'left center'] as const diff --git a/shared/chat/conversation/messages/reactions-rows.tsx b/shared/chat/conversation/messages/reactions-rows.tsx index cde5d850f470..21d1eef42325 100644 --- a/shared/chat/conversation/messages/reactions-rows.tsx +++ b/shared/chat/conversation/messages/reactions-rows.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as Kb from '@/common-adapters' import * as React from 'react' import EmojiRow from './emoji-row' diff --git a/shared/chat/conversation/messages/reset-user.tsx b/shared/chat/conversation/messages/reset-user.tsx index 08ae473cf8ff..5ef6bde4ba85 100644 --- a/shared/chat/conversation/messages/reset-user.tsx +++ b/shared/chat/conversation/messages/reset-user.tsx @@ -1,5 +1,5 @@ -import * as Chat from '@/constants/chat2' -import {useProfileState} from '@/constants/profile' +import * as Chat from '@/stores/chat2' +import {useProfileState} from '@/stores/profile' import * as Kb from '@/common-adapters' const ResetUser = () => { diff --git a/shared/chat/conversation/messages/retention-notice.tsx b/shared/chat/conversation/messages/retention-notice.tsx index add6faf305ea..77327c4f58ed 100644 --- a/shared/chat/conversation/messages/retention-notice.tsx +++ b/shared/chat/conversation/messages/retention-notice.tsx @@ -1,6 +1,6 @@ import type * as T from '@/constants/types' -import * as Chat from '@/constants/chat2' -import * as Teams from '@/constants/teams' +import * as Chat from '@/stores/chat2' +import * as Teams from '@/stores/teams' import * as Kb from '@/common-adapters' import * as React from 'react' diff --git a/shared/chat/conversation/messages/separator.tsx b/shared/chat/conversation/messages/separator.tsx index d2a640007553..877beff3ad3a 100644 --- a/shared/chat/conversation/messages/separator.tsx +++ b/shared/chat/conversation/messages/separator.tsx @@ -1,15 +1,15 @@ import * as C from '@/constants' -import * as Chat from '@/constants/chat2' -import {useTeamsState} from '@/constants/teams' +import * as Chat from '@/stores/chat2' +import {useTeamsState} from '@/stores/teams' import * as Kb from '@/common-adapters' import * as React from 'react' import * as T from '@/constants/types' import {formatTimeForConversationList, formatTimeForChat} from '@/util/timestamp' import {OrangeLineContext} from '../orange-line-context' import logger from '@/logger' -import {useTrackerState} from '@/constants/tracker2' -import {useProfileState} from '@/constants/profile' -import {useCurrentUserState} from '@/constants/current-user' +import {useTrackerState} from '@/stores/tracker2' +import {useProfileState} from '@/stores/profile' +import {useCurrentUserState} from '@/stores/current-user' // import {useChatDebugDump} from '@/constants/chat2/debug' const enoughTimeBetweenMessages = (mtimestamp?: number, ptimestamp?: number): boolean => diff --git a/shared/chat/conversation/messages/set-channelname/wrapper.tsx b/shared/chat/conversation/messages/set-channelname/wrapper.tsx index e3a0c0164c7d..c507ae29e940 100644 --- a/shared/chat/conversation/messages/set-channelname/wrapper.tsx +++ b/shared/chat/conversation/messages/set-channelname/wrapper.tsx @@ -1,4 +1,4 @@ -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as React from 'react' import {WrapperMessage, useCommon, type Props} from '../wrapper/wrapper' import type SetChannelnameType from './container' diff --git a/shared/chat/conversation/messages/set-description/wrapper.tsx b/shared/chat/conversation/messages/set-description/wrapper.tsx index 9f8400a9e58d..0383a7edc43e 100644 --- a/shared/chat/conversation/messages/set-description/wrapper.tsx +++ b/shared/chat/conversation/messages/set-description/wrapper.tsx @@ -1,4 +1,4 @@ -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as React from 'react' import {WrapperMessage, useCommon, type Props} from '../wrapper/wrapper' import type SetDescriptionType from './container' diff --git a/shared/chat/conversation/messages/special-bottom-message.tsx b/shared/chat/conversation/messages/special-bottom-message.tsx index e6e45bf35bee..386a56528d86 100644 --- a/shared/chat/conversation/messages/special-bottom-message.tsx +++ b/shared/chat/conversation/messages/special-bottom-message.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as React from 'react' import OldProfileReset from './system-old-profile-reset-notice/container' import ResetUser from './reset-user' diff --git a/shared/chat/conversation/messages/special-top-message.tsx b/shared/chat/conversation/messages/special-top-message.tsx index 40205b7dc910..e726d608a19c 100644 --- a/shared/chat/conversation/messages/special-top-message.tsx +++ b/shared/chat/conversation/messages/special-top-message.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as T from '@/constants/types' import * as Kb from '@/common-adapters' import * as React from 'react' @@ -11,7 +11,7 @@ import ProfileResetNotice from './system-profile-reset-notice' import RetentionNotice from './retention-notice' import {usingFlashList} from '../list-area/flashlist-config' import * as FS from '@/constants/fs' -import {useCurrentUserState} from '@/constants/current-user' +import {useCurrentUserState} from '@/stores/current-user' const ErrorMessage = () => { const createConversationError = Chat.useChatState(s => s.createConversationError) @@ -178,7 +178,7 @@ const SpecialTopMessage = React.memo(function SpecialTopMessage() { }, []) const openPrivateFolder = React.useCallback(() => { - FS.makeActionForOpenPathInFilesTab(T.FS.stringToPath(`/keybase/private/${username}`)) + FS.navToPath(T.FS.stringToPath(`/keybase/private/${username}`)) }, [username]) return ( diff --git a/shared/chat/conversation/messages/system-added-to-team/container.tsx b/shared/chat/conversation/messages/system-added-to-team/container.tsx index 58c6e70e030f..10999c12dc9f 100644 --- a/shared/chat/conversation/messages/system-added-to-team/container.tsx +++ b/shared/chat/conversation/messages/system-added-to-team/container.tsx @@ -1,12 +1,12 @@ -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as React from 'react' -import * as Teams from '@/constants/teams' +import * as Teams from '@/stores/teams' import type * as T from '@/constants/types' import * as Kb from '@/common-adapters' import UserNotice from '../user-notice' import {getAddedUsernames} from '../system-users-added-to-conv/container' import {indefiniteArticle} from '@/util/string' -import {useCurrentUserState} from '@/constants/current-user' +import {useCurrentUserState} from '@/stores/current-user' type OwnProps = {message: T.Chat.MessageSystemAddedToTeam} diff --git a/shared/chat/conversation/messages/system-added-to-team/wrapper.tsx b/shared/chat/conversation/messages/system-added-to-team/wrapper.tsx index f90914a28f97..dcefb1626e38 100644 --- a/shared/chat/conversation/messages/system-added-to-team/wrapper.tsx +++ b/shared/chat/conversation/messages/system-added-to-team/wrapper.tsx @@ -1,5 +1,5 @@ import * as React from 'react' -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import {WrapperMessage, useCommon, type Props} from '../wrapper/wrapper' import type SystemAddedToTeamType from './container' diff --git a/shared/chat/conversation/messages/system-change-avatar/index.tsx b/shared/chat/conversation/messages/system-change-avatar/index.tsx index bdf0fffdfe4b..8aa32c6b3098 100644 --- a/shared/chat/conversation/messages/system-change-avatar/index.tsx +++ b/shared/chat/conversation/messages/system-change-avatar/index.tsx @@ -1,7 +1,7 @@ import * as Kb from '@/common-adapters' import type * as T from '@/constants/types' import UserNotice from '../user-notice' -import {useCurrentUserState} from '@/constants/current-user' +import {useCurrentUserState} from '@/stores/current-user' type Props = { message: T.Chat.MessageSystemChangeAvatar diff --git a/shared/chat/conversation/messages/system-change-avatar/wrapper.tsx b/shared/chat/conversation/messages/system-change-avatar/wrapper.tsx index 172ac0051c55..ed14bda12cb3 100644 --- a/shared/chat/conversation/messages/system-change-avatar/wrapper.tsx +++ b/shared/chat/conversation/messages/system-change-avatar/wrapper.tsx @@ -1,4 +1,4 @@ -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as React from 'react' import {WrapperMessage, useCommon, type Props} from '../wrapper/wrapper' import type SystemChangeAvatarType from '.' diff --git a/shared/chat/conversation/messages/system-change-retention/container.tsx b/shared/chat/conversation/messages/system-change-retention/container.tsx index c0ad42530fa3..d192ebd93f88 100644 --- a/shared/chat/conversation/messages/system-change-retention/container.tsx +++ b/shared/chat/conversation/messages/system-change-retention/container.tsx @@ -1,11 +1,11 @@ -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as React from 'react' -import * as Teams from '@/constants/teams' +import * as Teams from '@/stores/teams' import * as Kb from '@/common-adapters' import UserNotice from '../user-notice' import * as T from '@/constants/types' import * as dateFns from 'date-fns' -import {useCurrentUserState} from '@/constants/current-user' +import {useCurrentUserState} from '@/stores/current-user' type OwnProps = {message: T.Chat.MessageSystemChangeRetention} diff --git a/shared/chat/conversation/messages/system-change-retention/wrapper.tsx b/shared/chat/conversation/messages/system-change-retention/wrapper.tsx index 85490ad934a3..8d6d231f78ed 100644 --- a/shared/chat/conversation/messages/system-change-retention/wrapper.tsx +++ b/shared/chat/conversation/messages/system-change-retention/wrapper.tsx @@ -1,4 +1,4 @@ -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as React from 'react' import {WrapperMessage, useCommon, type Props} from '../wrapper/wrapper' import type SystemChangeRetentionType from './container' diff --git a/shared/chat/conversation/messages/system-create-team/container.tsx b/shared/chat/conversation/messages/system-create-team/container.tsx index f248de32f0f6..b3c654e66d2f 100644 --- a/shared/chat/conversation/messages/system-create-team/container.tsx +++ b/shared/chat/conversation/messages/system-create-team/container.tsx @@ -1,11 +1,11 @@ import * as C from '@/constants' -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as React from 'react' -import * as Teams from '@/constants/teams' +import * as Teams from '@/stores/teams' import * as Kb from '@/common-adapters' import UserNotice from '../user-notice' import type * as T from '@/constants/types' -import {useCurrentUserState} from '@/constants/current-user' +import {useCurrentUserState} from '@/stores/current-user' type OwnProps = {message: T.Chat.MessageSystemCreateTeam} diff --git a/shared/chat/conversation/messages/system-create-team/wrapper.tsx b/shared/chat/conversation/messages/system-create-team/wrapper.tsx index 4d4cd246197a..2a9216ababec 100644 --- a/shared/chat/conversation/messages/system-create-team/wrapper.tsx +++ b/shared/chat/conversation/messages/system-create-team/wrapper.tsx @@ -1,4 +1,4 @@ -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as React from 'react' import {WrapperMessage, useCommon, type Props} from '../wrapper/wrapper' import type SystemCreateTeamType from './container' diff --git a/shared/chat/conversation/messages/system-git-push/container.tsx b/shared/chat/conversation/messages/system-git-push/container.tsx index 3d8d2c346a98..725af44edf1a 100644 --- a/shared/chat/conversation/messages/system-git-push/container.tsx +++ b/shared/chat/conversation/messages/system-git-push/container.tsx @@ -1,11 +1,11 @@ -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as React from 'react' import * as T from '@/constants/types' import * as Kb from '@/common-adapters' -import {useGitState} from '@/constants/git' +import {useGitState} from '@/stores/git' import UserNotice from '../user-notice' import * as FS from '@/constants/fs' -import {useCurrentUserState} from '@/constants/current-user' +import {useCurrentUserState} from '@/stores/current-user' type OwnProps = {message: T.Chat.MessageSystemGitPush} @@ -21,7 +21,7 @@ const GitContainer = React.memo(function GitContainer(p: OwnProps) { '/.kbfs_autogit_commit_' + commitHash ) - FS.makeActionForOpenPathInFilesTab(path) + FS.navToPath(path) }, [message] ) diff --git a/shared/chat/conversation/messages/system-git-push/wrapper.tsx b/shared/chat/conversation/messages/system-git-push/wrapper.tsx index aaac3ac464e6..89e05a0090f8 100644 --- a/shared/chat/conversation/messages/system-git-push/wrapper.tsx +++ b/shared/chat/conversation/messages/system-git-push/wrapper.tsx @@ -1,4 +1,4 @@ -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as React from 'react' import {WrapperMessage, useCommon, type Props} from '../wrapper/wrapper' import type SystemGitPushType from './container' diff --git a/shared/chat/conversation/messages/system-invite-accepted/container.tsx b/shared/chat/conversation/messages/system-invite-accepted/container.tsx index b3b76a752bba..b2d1357a85fd 100644 --- a/shared/chat/conversation/messages/system-invite-accepted/container.tsx +++ b/shared/chat/conversation/messages/system-invite-accepted/container.tsx @@ -1,11 +1,11 @@ import * as C from '@/constants' -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as React from 'react' -import * as Teams from '@/constants/teams' +import * as Teams from '@/stores/teams' import type * as T from '@/constants/types' import * as Kb from '@/common-adapters' import UserNotice from '../user-notice' -import {useCurrentUserState} from '@/constants/current-user' +import {useCurrentUserState} from '@/stores/current-user' type OwnProps = {message: T.Chat.MessageSystemInviteAccepted} diff --git a/shared/chat/conversation/messages/system-invite-accepted/wrapper.tsx b/shared/chat/conversation/messages/system-invite-accepted/wrapper.tsx index 118224348648..aeed739b80d4 100644 --- a/shared/chat/conversation/messages/system-invite-accepted/wrapper.tsx +++ b/shared/chat/conversation/messages/system-invite-accepted/wrapper.tsx @@ -1,4 +1,4 @@ -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as React from 'react' import {WrapperMessage, useCommon, type Props} from '../wrapper/wrapper' import type SystemInviteAcceptedType from './container' diff --git a/shared/chat/conversation/messages/system-joined/container.tsx b/shared/chat/conversation/messages/system-joined/container.tsx index 7ca9df41d333..a9dc7563c9d2 100644 --- a/shared/chat/conversation/messages/system-joined/container.tsx +++ b/shared/chat/conversation/messages/system-joined/container.tsx @@ -1,4 +1,4 @@ -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as React from 'react' import type * as T from '@/constants/types' import * as Kb from '@/common-adapters' diff --git a/shared/chat/conversation/messages/system-joined/wrapper.tsx b/shared/chat/conversation/messages/system-joined/wrapper.tsx index 996f2dc95968..7fe46e21979f 100644 --- a/shared/chat/conversation/messages/system-joined/wrapper.tsx +++ b/shared/chat/conversation/messages/system-joined/wrapper.tsx @@ -1,4 +1,4 @@ -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as React from 'react' import {WrapperMessage, useCommon, type Props} from '../wrapper/wrapper' import type SystemJoinedType from './container' diff --git a/shared/chat/conversation/messages/system-left/container.tsx b/shared/chat/conversation/messages/system-left/container.tsx index 70ed9a1b3d74..65d0cdf61516 100644 --- a/shared/chat/conversation/messages/system-left/container.tsx +++ b/shared/chat/conversation/messages/system-left/container.tsx @@ -1,4 +1,4 @@ -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as React from 'react' import * as Kb from '@/common-adapters' import UserNotice from '../user-notice' diff --git a/shared/chat/conversation/messages/system-left/wrapper.tsx b/shared/chat/conversation/messages/system-left/wrapper.tsx index cf6f97dea1dd..53f8df6c82d2 100644 --- a/shared/chat/conversation/messages/system-left/wrapper.tsx +++ b/shared/chat/conversation/messages/system-left/wrapper.tsx @@ -1,4 +1,4 @@ -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as React from 'react' import {WrapperMessage, useCommon, type Props} from '../wrapper/wrapper' import type SystemLeftType from './container' diff --git a/shared/chat/conversation/messages/system-new-channel/container.tsx b/shared/chat/conversation/messages/system-new-channel/container.tsx index fb791f2b70b4..f8e1e201837d 100644 --- a/shared/chat/conversation/messages/system-new-channel/container.tsx +++ b/shared/chat/conversation/messages/system-new-channel/container.tsx @@ -1,6 +1,6 @@ -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as Kb from '@/common-adapters' -import {useTeamsState} from '@/constants/teams' +import {useTeamsState} from '@/stores/teams' import * as React from 'react' import type * as T from '@/constants/types' import UserNotice from '../user-notice' diff --git a/shared/chat/conversation/messages/system-new-channel/wrapper.tsx b/shared/chat/conversation/messages/system-new-channel/wrapper.tsx index 27da6aaba425..be12f9f0554f 100644 --- a/shared/chat/conversation/messages/system-new-channel/wrapper.tsx +++ b/shared/chat/conversation/messages/system-new-channel/wrapper.tsx @@ -1,4 +1,4 @@ -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as React from 'react' import {WrapperMessage, useCommon, type Props} from '../wrapper/wrapper' import type SystemNewChannelType from './container' diff --git a/shared/chat/conversation/messages/system-old-profile-reset-notice/container.tsx b/shared/chat/conversation/messages/system-old-profile-reset-notice/container.tsx index 4044fd8a281b..e306630f87ad 100644 --- a/shared/chat/conversation/messages/system-old-profile-reset-notice/container.tsx +++ b/shared/chat/conversation/messages/system-old-profile-reset-notice/container.tsx @@ -1,4 +1,4 @@ -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import type * as T from '@/constants/types' import {Text} from '@/common-adapters' import UserNotice from '../user-notice' diff --git a/shared/chat/conversation/messages/system-profile-reset-notice.tsx b/shared/chat/conversation/messages/system-profile-reset-notice.tsx index 2d7deca69d72..0a2677a6140d 100644 --- a/shared/chat/conversation/messages/system-profile-reset-notice.tsx +++ b/shared/chat/conversation/messages/system-profile-reset-notice.tsx @@ -1,4 +1,4 @@ -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import type * as T from '@/constants/types' import * as Kb from '@/common-adapters' import UserNotice from './user-notice' diff --git a/shared/chat/conversation/messages/system-sbs-resolve/container.tsx b/shared/chat/conversation/messages/system-sbs-resolve/container.tsx index 51da16607892..8346cd4d5a80 100644 --- a/shared/chat/conversation/messages/system-sbs-resolve/container.tsx +++ b/shared/chat/conversation/messages/system-sbs-resolve/container.tsx @@ -2,7 +2,7 @@ import type * as T from '@/constants/types' import * as Kb from '@/common-adapters' import {e164ToDisplay} from '@/util/phone-numbers' import UserNotice from '../user-notice' -import {useCurrentUserState} from '@/constants/current-user' +import {useCurrentUserState} from '@/stores/current-user' type OwnProps = {message: T.Chat.MessageSystemSBSResolved} diff --git a/shared/chat/conversation/messages/system-sbs-resolve/wrapper.tsx b/shared/chat/conversation/messages/system-sbs-resolve/wrapper.tsx index 32db70666c32..bd9d9626c95a 100644 --- a/shared/chat/conversation/messages/system-sbs-resolve/wrapper.tsx +++ b/shared/chat/conversation/messages/system-sbs-resolve/wrapper.tsx @@ -1,9 +1,9 @@ -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as React from 'react' import {WrapperMessage, useCommon, type Props} from '../wrapper/wrapper' import type SystemSBSResolvedType from './container' import type SystemJoinedType from '../system-joined/container' -import {useCurrentUserState} from '@/constants/current-user' +import {useCurrentUserState} from '@/stores/current-user' const WrapperSystemInvite = React.memo(function WrapperSystemInvite(p: Props) { const {ordinal} = p diff --git a/shared/chat/conversation/messages/system-simple-to-complex/container.tsx b/shared/chat/conversation/messages/system-simple-to-complex/container.tsx index d0066afafefb..baa73a2f6af5 100644 --- a/shared/chat/conversation/messages/system-simple-to-complex/container.tsx +++ b/shared/chat/conversation/messages/system-simple-to-complex/container.tsx @@ -1,10 +1,10 @@ -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as React from 'react' -import {useTeamsState} from '@/constants/teams' +import {useTeamsState} from '@/stores/teams' import type * as T from '@/constants/types' import * as Kb from '@/common-adapters' import UserNotice from '../user-notice' -import {useCurrentUserState} from '@/constants/current-user' +import {useCurrentUserState} from '@/stores/current-user' type OwnProps = {message: T.Chat.MessageSystemSimpleToComplex} diff --git a/shared/chat/conversation/messages/system-simple-to-complex/wrapper.tsx b/shared/chat/conversation/messages/system-simple-to-complex/wrapper.tsx index 93d830470578..d894327e7599 100644 --- a/shared/chat/conversation/messages/system-simple-to-complex/wrapper.tsx +++ b/shared/chat/conversation/messages/system-simple-to-complex/wrapper.tsx @@ -1,4 +1,4 @@ -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as React from 'react' import {WrapperMessage, useCommon, type Props} from '../wrapper/wrapper' import type SystemSimpleToComplexType from './container' diff --git a/shared/chat/conversation/messages/system-text/wrapper.tsx b/shared/chat/conversation/messages/system-text/wrapper.tsx index 9b99bc02b180..4f2efa4555f6 100644 --- a/shared/chat/conversation/messages/system-text/wrapper.tsx +++ b/shared/chat/conversation/messages/system-text/wrapper.tsx @@ -1,4 +1,4 @@ -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as React from 'react' import {WrapperMessage, useCommon, type Props} from '../wrapper/wrapper' import type SystemTextType from './container' diff --git a/shared/chat/conversation/messages/system-users-added-to-conv/container.tsx b/shared/chat/conversation/messages/system-users-added-to-conv/container.tsx index 3d67beba56bb..1a9d48e4233d 100644 --- a/shared/chat/conversation/messages/system-users-added-to-conv/container.tsx +++ b/shared/chat/conversation/messages/system-users-added-to-conv/container.tsx @@ -1,9 +1,9 @@ -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as React from 'react' import type * as T from '@/constants/types' import * as Kb from '@/common-adapters' import UserNotice from '../user-notice' -import {useCurrentUserState} from '@/constants/current-user' +import {useCurrentUserState} from '@/stores/current-user' type OwnProps = {message: T.Chat.MessageSystemUsersAddedToConversation} diff --git a/shared/chat/conversation/messages/system-users-added-to-conv/wrapper.tsx b/shared/chat/conversation/messages/system-users-added-to-conv/wrapper.tsx index 5941d8eebbcd..1fe3e0bf1c8b 100644 --- a/shared/chat/conversation/messages/system-users-added-to-conv/wrapper.tsx +++ b/shared/chat/conversation/messages/system-users-added-to-conv/wrapper.tsx @@ -1,4 +1,4 @@ -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as React from 'react' import {WrapperMessage, useCommon, type Props} from '../wrapper/wrapper' import type SystemUsersAddedToConvType from './container' diff --git a/shared/chat/conversation/messages/text/bottom.tsx b/shared/chat/conversation/messages/text/bottom.tsx index 128cc464ef16..e1a26112b6ab 100644 --- a/shared/chat/conversation/messages/text/bottom.tsx +++ b/shared/chat/conversation/messages/text/bottom.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as React from 'react' import type * as T from '@/constants/types' import type CoinFlipType from './coinflip' diff --git a/shared/chat/conversation/messages/text/coinflip/index.tsx b/shared/chat/conversation/messages/text/coinflip/index.tsx index 223547f15130..45f53d2d088d 100644 --- a/shared/chat/conversation/messages/text/coinflip/index.tsx +++ b/shared/chat/conversation/messages/text/coinflip/index.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as Kb from '@/common-adapters' import * as React from 'react' import * as T from '@/constants/types' diff --git a/shared/chat/conversation/messages/text/reply.tsx b/shared/chat/conversation/messages/text/reply.tsx index 7ad7cfacd4f9..bc0d9c745128 100644 --- a/shared/chat/conversation/messages/text/reply.tsx +++ b/shared/chat/conversation/messages/text/reply.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as Kb from '@/common-adapters' import * as React from 'react' import {useOrdinal, useIsHighlighted} from '../ids-context' diff --git a/shared/chat/conversation/messages/text/unfurl/prompt-list/container.tsx b/shared/chat/conversation/messages/text/unfurl/prompt-list/container.tsx index 240d090eab40..ea0da57b5081 100644 --- a/shared/chat/conversation/messages/text/unfurl/prompt-list/container.tsx +++ b/shared/chat/conversation/messages/text/unfurl/prompt-list/container.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as React from 'react' import {useOrdinal} from '@/chat/conversation/messages/ids-context' import * as T from '@/constants/types' diff --git a/shared/chat/conversation/messages/text/unfurl/unfurl-list/generic.tsx b/shared/chat/conversation/messages/text/unfurl/unfurl-list/generic.tsx index 444f2452a61e..51105368b6e1 100644 --- a/shared/chat/conversation/messages/text/unfurl/unfurl-list/generic.tsx +++ b/shared/chat/conversation/messages/text/unfurl/unfurl-list/generic.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as Kb from '@/common-adapters/index' import * as T from '@/constants/types' import * as React from 'react' diff --git a/shared/chat/conversation/messages/text/unfurl/unfurl-list/giphy.tsx b/shared/chat/conversation/messages/text/unfurl/unfurl-list/giphy.tsx index 3e97b16ef8fd..238515cda7c3 100644 --- a/shared/chat/conversation/messages/text/unfurl/unfurl-list/giphy.tsx +++ b/shared/chat/conversation/messages/text/unfurl/unfurl-list/giphy.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as Kb from '@/common-adapters/index' import * as React from 'react' import UnfurlImage from './image' diff --git a/shared/chat/conversation/messages/text/unfurl/unfurl-list/image/index.tsx b/shared/chat/conversation/messages/text/unfurl/unfurl-list/image/index.tsx index a826e6dc5e04..e2a3ba2f2c5d 100644 --- a/shared/chat/conversation/messages/text/unfurl/unfurl-list/image/index.tsx +++ b/shared/chat/conversation/messages/text/unfurl/unfurl-list/image/index.tsx @@ -1,6 +1,6 @@ import * as React from 'react' import * as Kb from '@/common-adapters/index' -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import {maxWidth} from '@/chat/conversation/messages/attachment/shared' import {Video} from './video' import openURL from '@/util/open-url' diff --git a/shared/chat/conversation/messages/text/unfurl/unfurl-list/index.tsx b/shared/chat/conversation/messages/text/unfurl/unfurl-list/index.tsx index 1aa89030311a..65c37cb40387 100644 --- a/shared/chat/conversation/messages/text/unfurl/unfurl-list/index.tsx +++ b/shared/chat/conversation/messages/text/unfurl/unfurl-list/index.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as T from '@/constants/types' import * as React from 'react' import UnfurlGeneric from './generic' diff --git a/shared/chat/conversation/messages/text/unfurl/unfurl-list/map-popup.tsx b/shared/chat/conversation/messages/text/unfurl/unfurl-list/map-popup.tsx index 243034849db6..59e03f3db1c5 100644 --- a/shared/chat/conversation/messages/text/unfurl/unfurl-list/map-popup.tsx +++ b/shared/chat/conversation/messages/text/unfurl/unfurl-list/map-popup.tsx @@ -1,10 +1,10 @@ import * as C from '@/constants' -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as Kb from '@/common-adapters/index' import type * as T from '@/constants/types' import openURL from '@/util/open-url' import LocationMap from '@/chat/location-map' -import {useConfigState} from '@/constants/config' +import {useConfigState} from '@/stores/config' type Props = { coord: T.Chat.Coordinate diff --git a/shared/chat/conversation/messages/text/unfurl/unfurl-list/map.tsx b/shared/chat/conversation/messages/text/unfurl/unfurl-list/map.tsx index 2cf754f6891e..68ec76e60397 100644 --- a/shared/chat/conversation/messages/text/unfurl/unfurl-list/map.tsx +++ b/shared/chat/conversation/messages/text/unfurl/unfurl-list/map.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as Kb from '@/common-adapters/index' import * as T from '@/constants/types' import * as React from 'react' diff --git a/shared/chat/conversation/messages/text/unfurl/unfurl-list/use-state.tsx b/shared/chat/conversation/messages/text/unfurl/unfurl-list/use-state.tsx index 4a7fddb9d9dd..1092daf24b90 100644 --- a/shared/chat/conversation/messages/text/unfurl/unfurl-list/use-state.tsx +++ b/shared/chat/conversation/messages/text/unfurl/unfurl-list/use-state.tsx @@ -1,7 +1,7 @@ -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as React from 'react' import type * as T from '@/constants/types' -import {useCurrentUserState} from '@/constants/current-user' +import {useCurrentUserState} from '@/stores/current-user' export const useActions = (youAreAuthor: boolean, messageID: T.Chat.MessageID, ordinal: T.Chat.Ordinal) => { const unfurlRemove = Chat.useChatContext(s => s.dispatch.unfurlRemove) diff --git a/shared/chat/conversation/messages/text/wrapper.tsx b/shared/chat/conversation/messages/text/wrapper.tsx index d3a6ed554bae..c1b077922b4b 100644 --- a/shared/chat/conversation/messages/text/wrapper.tsx +++ b/shared/chat/conversation/messages/text/wrapper.tsx @@ -1,4 +1,4 @@ -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as Kb from '@/common-adapters' import * as React from 'react' import {useReply} from './reply' diff --git a/shared/chat/conversation/messages/wrapper/edited.tsx b/shared/chat/conversation/messages/wrapper/edited.tsx index bc39cbae643b..a3e2d0df2fbe 100644 --- a/shared/chat/conversation/messages/wrapper/edited.tsx +++ b/shared/chat/conversation/messages/wrapper/edited.tsx @@ -1,4 +1,4 @@ -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as React from 'react' import * as Kb from '@/common-adapters' import {useIsHighlighted, useOrdinal} from '../ids-context' diff --git a/shared/chat/conversation/messages/wrapper/exploding-height-retainer/container.tsx b/shared/chat/conversation/messages/wrapper/exploding-height-retainer/container.tsx index fae3926d1218..0fc71930593c 100644 --- a/shared/chat/conversation/messages/wrapper/exploding-height-retainer/container.tsx +++ b/shared/chat/conversation/messages/wrapper/exploding-height-retainer/container.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as React from 'react' import ExplodingHeightRetainer from '.' import {useOrdinal} from '../../ids-context' diff --git a/shared/chat/conversation/messages/wrapper/exploding-meta.tsx b/shared/chat/conversation/messages/wrapper/exploding-meta.tsx index c6a3a6c1bdde..fa796f6b0354 100644 --- a/shared/chat/conversation/messages/wrapper/exploding-meta.tsx +++ b/shared/chat/conversation/messages/wrapper/exploding-meta.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as React from 'react' import {useIsHighlighted, useOrdinal} from '../ids-context' import * as Kb from '@/common-adapters' diff --git a/shared/chat/conversation/messages/wrapper/long-pressable/index.native.tsx b/shared/chat/conversation/messages/wrapper/long-pressable/index.native.tsx index f5d163fce1c0..72f39ea87942 100644 --- a/shared/chat/conversation/messages/wrapper/long-pressable/index.native.tsx +++ b/shared/chat/conversation/messages/wrapper/long-pressable/index.native.tsx @@ -1,4 +1,4 @@ -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as Kb from '@/common-adapters' import * as React from 'react' import type {Props} from '.' diff --git a/shared/chat/conversation/messages/wrapper/send-indicator.tsx b/shared/chat/conversation/messages/wrapper/send-indicator.tsx index 4f5df715d762..6f197da1ed5a 100644 --- a/shared/chat/conversation/messages/wrapper/send-indicator.tsx +++ b/shared/chat/conversation/messages/wrapper/send-indicator.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import {useOrdinal} from '../ids-context' import * as React from 'react' import * as Kb from '@/common-adapters' diff --git a/shared/chat/conversation/messages/wrapper/wrapper.tsx b/shared/chat/conversation/messages/wrapper/wrapper.tsx index d1c4186278d0..e4f0a9609e36 100644 --- a/shared/chat/conversation/messages/wrapper/wrapper.tsx +++ b/shared/chat/conversation/messages/wrapper/wrapper.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as Kb from '@/common-adapters' import * as React from 'react' import {MessageContext, useOrdinal} from '../ids-context' @@ -13,7 +13,7 @@ import SendIndicator from './send-indicator' import * as T from '@/constants/types' import capitalize from 'lodash/capitalize' import {useEdited} from './edited' -import {useCurrentUserState} from '@/constants/current-user' +import {useCurrentUserState} from '@/stores/current-user' // import {useDebugLayout} from '@/util/debug-react' export type Props = { diff --git a/shared/chat/conversation/normal/container.tsx b/shared/chat/conversation/normal/container.tsx index fd38892d4b89..dc1ee30496aa 100644 --- a/shared/chat/conversation/normal/container.tsx +++ b/shared/chat/conversation/normal/container.tsx @@ -1,10 +1,9 @@ import * as C from '@/constants' -import * as Chat from '@/constants/chat2' -import {useConfigState} from '@/constants/config' +import * as Chat from '@/stores/chat2' +import {useConfigState} from '@/stores/config' import * as React from 'react' import Normal from '.' import * as T from '@/constants/types' -import {useActiveState} from '@/constants/active' import {FocusProvider, ScrollProvider} from './context' import {OrangeLineContext} from '../orange-line-context' @@ -51,7 +50,7 @@ const useOrangeLine = () => { // just use the rpc for orange line if we're not active // if we are active we want to keep whatever state we had so it is maintained - const active = useActiveState(s => s.active) + const active = useConfigState(s => s.active) React.useEffect(() => { if (!active) { loadOrangeLine() diff --git a/shared/chat/conversation/normal/index.desktop.tsx b/shared/chat/conversation/normal/index.desktop.tsx index 645314cf0f43..2e5dde5320c0 100644 --- a/shared/chat/conversation/normal/index.desktop.tsx +++ b/shared/chat/conversation/normal/index.desktop.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as Kb from '@/common-adapters' import * as React from 'react' import Banner from '../bottom-banner' diff --git a/shared/chat/conversation/normal/index.native.tsx b/shared/chat/conversation/normal/index.native.tsx index b51457ece826..bfb4263d9019 100644 --- a/shared/chat/conversation/normal/index.native.tsx +++ b/shared/chat/conversation/normal/index.native.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import {PortalHost} from '@/common-adapters/portal.native' import * as Kb from '@/common-adapters' import * as React from 'react' diff --git a/shared/chat/conversation/pinned-message.tsx b/shared/chat/conversation/pinned-message.tsx index c1bee40e5890..9e18af274b21 100644 --- a/shared/chat/conversation/pinned-message.tsx +++ b/shared/chat/conversation/pinned-message.tsx @@ -1,10 +1,10 @@ import * as C from '@/constants' -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as React from 'react' -import * as Teams from '@/constants/teams' +import * as Teams from '@/stores/teams' import type * as T from '@/constants/types' import * as Kb from '@/common-adapters' -import {useCurrentUserState} from '@/constants/current-user' +import {useCurrentUserState} from '@/stores/current-user' const PinnedMessage = React.memo(function PinnedMessage() { const {conversationIDKey, teamname, pinnedMsg, replyJump, onIgnore, pinMessage} = Chat.useChatContext( diff --git a/shared/chat/conversation/rekey/container.tsx b/shared/chat/conversation/rekey/container.tsx index d4397fb0346a..8cc2428b7f7b 100644 --- a/shared/chat/conversation/rekey/container.tsx +++ b/shared/chat/conversation/rekey/container.tsx @@ -1,7 +1,7 @@ import * as C from '@/constants' -import * as Chat from '@/constants/chat2' -import {useProfileState} from '@/constants/profile' -import {useCurrentUserState} from '@/constants/current-user' +import * as Chat from '@/stores/chat2' +import {useProfileState} from '@/stores/profile' +import {useCurrentUserState} from '@/stores/current-user' import * as T from '@/constants/types' import ParticipantRekey from './participant-rekey' import YouRekey from './you-rekey' diff --git a/shared/chat/conversation/reply-preview.tsx b/shared/chat/conversation/reply-preview.tsx index 4997bad338bd..b194fb2091cb 100644 --- a/shared/chat/conversation/reply-preview.tsx +++ b/shared/chat/conversation/reply-preview.tsx @@ -1,4 +1,4 @@ -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as React from 'react' import * as Kb from '@/common-adapters' import * as T from '@/constants/types' diff --git a/shared/chat/conversation/search.tsx b/shared/chat/conversation/search.tsx index 20d9d78fa081..455b96ee6743 100644 --- a/shared/chat/conversation/search.tsx +++ b/shared/chat/conversation/search.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import type * as Styles from '@/styles' import * as React from 'react' import * as Kb from '@/common-adapters' diff --git a/shared/chat/create-channel/hooks.tsx b/shared/chat/create-channel/hooks.tsx index ec52505df5e5..ae69f3d75443 100644 --- a/shared/chat/create-channel/hooks.tsx +++ b/shared/chat/create-channel/hooks.tsx @@ -1,6 +1,6 @@ import * as C from '@/constants' import * as React from 'react' -import * as Teams from '@/constants/teams' +import * as Teams from '@/stores/teams' import upperFirst from 'lodash/upperFirst' import type {Props} from '.' diff --git a/shared/chat/delete-history-warning.tsx b/shared/chat/delete-history-warning.tsx index 1e748facdc92..47ffafd71431 100644 --- a/shared/chat/delete-history-warning.tsx +++ b/shared/chat/delete-history-warning.tsx @@ -1,6 +1,6 @@ import * as Kb from '@/common-adapters' import * as C from '@/constants' -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import MaybePopup from './maybe-popup' const DeleteHistoryWarning = () => { diff --git a/shared/chat/emoji-picker/container.tsx b/shared/chat/emoji-picker/container.tsx index abe1b741f551..34376fe70b98 100644 --- a/shared/chat/emoji-picker/container.tsx +++ b/shared/chat/emoji-picker/container.tsx @@ -1,6 +1,6 @@ import * as C from '@/constants' -import * as Chat from '@/constants/chat2' -import * as Teams from '@/constants/teams' +import * as Chat from '@/stores/chat2' +import * as Teams from '@/stores/teams' import * as React from 'react' import * as Kb from '@/common-adapters' import * as T from '@/constants/types' diff --git a/shared/chat/inbox-and-conversation-2.tsx b/shared/chat/inbox-and-conversation-2.tsx index 730766b62d5b..f0a13e01420a 100644 --- a/shared/chat/inbox-and-conversation-2.tsx +++ b/shared/chat/inbox-and-conversation-2.tsx @@ -1,6 +1,6 @@ // Just for desktop and tablet, we show inbox and conversation side by side import * as C from '@/constants' -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as Kb from '@/common-adapters' import * as React from 'react' import type * as T from '@/constants/types' diff --git a/shared/chat/inbox-and-conversation-header.tsx b/shared/chat/inbox-and-conversation-header.tsx index 157b61f73d3b..46fba91a82b5 100644 --- a/shared/chat/inbox-and-conversation-header.tsx +++ b/shared/chat/inbox-and-conversation-header.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as React from 'react' import * as Kb from '@/common-adapters' import type {StyleOverride} from '@/common-adapters/markdown' @@ -7,9 +7,9 @@ import SearchRow from './inbox/search-row' import NewChatButton from './inbox/new-chat-button' import {useRoute} from '@react-navigation/native' import type {RootRouteProps} from '@/router-v2/route-params' -import {useUsersState} from '@/constants/users' -import {useCurrentUserState} from '@/constants/current-user' -import * as Teams from '@/constants/teams' +import {useUsersState} from '@/stores/users' +import {useCurrentUserState} from '@/stores/current-user' +import * as Teams from '@/stores/teams' const Header = () => { const {params} = useRoute>() diff --git a/shared/chat/inbox-search/index.tsx b/shared/chat/inbox-search/index.tsx index f85ab2edb2d3..823e2b970a33 100644 --- a/shared/chat/inbox-search/index.tsx +++ b/shared/chat/inbox-search/index.tsx @@ -1,6 +1,6 @@ import * as C from '@/constants' -import * as Chat from '@/constants/chat2' -import {useTeamsState} from '@/constants/teams' +import * as Chat from '@/stores/chat2' +import {useTeamsState} from '@/stores/teams' import * as Kb from '@/common-adapters' import * as React from 'react' import Rover from './background' diff --git a/shared/chat/inbox/container.tsx b/shared/chat/inbox/container.tsx index a8f303cc416c..1bd795aef1d2 100644 --- a/shared/chat/inbox/container.tsx +++ b/shared/chat/inbox/container.tsx @@ -1,9 +1,17 @@ import * as C from '@/constants' -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as React from 'react' import * as T from '@/constants/types' import Inbox, {type Props} from '.' import {useIsFocused} from '@react-navigation/core' +import type { + ChatInboxRowItemBig, + ChatInboxRowItemBigHeader, + ChatInboxRowItemTeamBuilder, + ChatInboxRowItemSmall, + ChatInboxRowItemDivider, + ChatInboxRowItem, +} from './rowitem' type OwnProps = { navKey: string @@ -12,9 +20,7 @@ type OwnProps = { const makeBigRows = ( bigTeams: ReadonlyArray -): Array< - T.Chat.ChatInboxRowItemBig | T.Chat.ChatInboxRowItemBigHeader | T.Chat.ChatInboxRowItemTeamBuilder -> => { +): Array => { return bigTeams.map(t => { switch (t.state) { case T.RPCChat.UIInboxBigTeamRowTyp.channel: { @@ -43,7 +49,7 @@ const makeBigRows = ( const makeSmallRows = ( smallTeams: ReadonlyArray -): Array => { +): Array => { return smallTeams.map(t => { const conversationIDKey = T.Chat.stringToConversationIDKey(t.convID) return { @@ -180,18 +186,17 @@ const Connected = (ownProps: OwnProps) => { const hasAllSmallTeamConvs = (_inboxLayout?.smallTeams?.length ?? 0) === (_inboxLayout?.totalSmallTeams ?? 0) - const divider: Array = - React.useMemo(() => { - return bigRows.length !== 0 || !hasAllSmallTeamConvs - ? [{showButton: !hasAllSmallTeamConvs || smallTeamsBelowTheFold, type: 'divider'}] - : [] - }, [bigRows.length, hasAllSmallTeamConvs, smallTeamsBelowTheFold]) - - const rows: Array = React.useMemo(() => { - const builderAfterSmall = new Array() - const builderAfterDivider = new Array() - const builderAfterBig = new Array() - const teamBuilder: T.Chat.ChatInboxRowItemTeamBuilder = {type: 'teamBuilder'} + const divider: Array = React.useMemo(() => { + return bigRows.length !== 0 || !hasAllSmallTeamConvs + ? [{showButton: !hasAllSmallTeamConvs || smallTeamsBelowTheFold, type: 'divider'}] + : [] + }, [bigRows.length, hasAllSmallTeamConvs, smallTeamsBelowTheFold]) + + const rows: Array = React.useMemo(() => { + const builderAfterSmall = new Array() + const builderAfterDivider = new Array() + const builderAfterBig = new Array() + const teamBuilder: ChatInboxRowItemTeamBuilder = {type: 'teamBuilder'} if (smallRows.length !== 0) { if (bigRows.length === 0) { if (divider.length !== 0) { diff --git a/shared/chat/inbox/filter-row.tsx b/shared/chat/inbox/filter-row.tsx index 8a5651be6445..1d7214f8b4c1 100644 --- a/shared/chat/inbox/filter-row.tsx +++ b/shared/chat/inbox/filter-row.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as React from 'react' import * as Kb from '@/common-adapters' diff --git a/shared/chat/inbox/index.d.ts b/shared/chat/inbox/index.d.ts index 71bf8d45eba2..24737a0fdf77 100644 --- a/shared/chat/inbox/index.d.ts +++ b/shared/chat/inbox/index.d.ts @@ -1,5 +1,6 @@ import type * as React from 'react' import type * as T from '@/constants/types' +import type {ChatInboxRowItem} from './rowitem' export type Props = { allowShowFloatingButton: boolean @@ -10,7 +11,7 @@ export type Props = { neverLoaded: boolean onNewChat: () => void onUntrustedInboxVisible: (conversationIDKeys: Array) => void - rows: Array + rows: Array setInboxNumSmallRows: (rows: number) => void smallTeamsExpanded: boolean toggleSmallTeamsExpanded: () => void diff --git a/shared/chat/inbox/index.desktop.tsx b/shared/chat/inbox/index.desktop.tsx index 98eac06219af..c40b4875c1ea 100644 --- a/shared/chat/inbox/index.desktop.tsx +++ b/shared/chat/inbox/index.desktop.tsx @@ -4,6 +4,7 @@ import * as C from '@/constants' import * as React from 'react' import type * as TInbox from './index.d' import type * as T from '@/constants/types' +import type {ChatInboxRowItem} from './rowitem' import BigTeamsDivider from './row/big-teams-divider' import BuildTeam from './row/build-team' import TeamsDivider from './row/teams-divider' @@ -47,7 +48,7 @@ const DragLine = (p: { toggleSmallTeamsExpanded: () => void setInboxNumSmallRows: (n: number) => void style: object - rows: T.Chat.ChatInboxRowItem[] + rows: ChatInboxRowItem[] }) => { const {inboxNumSmallRows, showButton, style, scrollDiv} = p const {smallTeamsExpanded, toggleSmallTeamsExpanded, rows, setInboxNumSmallRows} = p @@ -185,7 +186,7 @@ const DragLine = (p: { type InboxRowData = { inboxNumSmallRows: number navKey: string - rows: T.Chat.ChatInboxRowItem[] + rows: ChatInboxRowItem[] scrollDiv: React.RefObject selectedConversationIDKey: string setInboxNumSmallRows: (rows: number) => void diff --git a/shared/chat/inbox/index.native.tsx b/shared/chat/inbox/index.native.tsx index 3a33bbcbe653..e1aa6aaf9ceb 100644 --- a/shared/chat/inbox/index.native.tsx +++ b/shared/chat/inbox/index.native.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as Kb from '@/common-adapters' import * as React from 'react' import * as RowSizes from './row/sizes' @@ -16,8 +16,9 @@ import {FlatList} from 'react-native-gesture-handler' // import {FlashList, type ListRenderItemInfo} from '@shopify/flash-list' import {makeRow} from './row' import {useOpenedRowState} from './row/opened-row-state' +import type {ChatInboxRowItem} from './rowitem' -type RowItem = T.Chat.ChatInboxRowItem +type RowItem = ChatInboxRowItem const usingFlashList = false as boolean const List = /*usingFlashList ? FlashList :*/ FlatList diff --git a/shared/chat/inbox/new-chat-button.tsx b/shared/chat/inbox/new-chat-button.tsx index e6987c1ddc92..bb086547ac38 100644 --- a/shared/chat/inbox/new-chat-button.tsx +++ b/shared/chat/inbox/new-chat-button.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as React from 'react' import * as Kb from '@/common-adapters' diff --git a/shared/chat/inbox/row/big-team-channel.tsx b/shared/chat/inbox/row/big-team-channel.tsx index 79a4e46f06f3..e4089507b3b3 100644 --- a/shared/chat/inbox/row/big-team-channel.tsx +++ b/shared/chat/inbox/row/big-team-channel.tsx @@ -1,4 +1,4 @@ -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as Kb from '@/common-adapters' import * as React from 'react' import * as RowSizes from './sizes' diff --git a/shared/chat/inbox/row/big-team-header.tsx b/shared/chat/inbox/row/big-team-header.tsx index b4cf9085a9b0..0fc4172f6497 100644 --- a/shared/chat/inbox/row/big-team-header.tsx +++ b/shared/chat/inbox/row/big-team-header.tsx @@ -1,7 +1,7 @@ import * as C from '@/constants' -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as Kb from '@/common-adapters' -import * as Teams from '@/constants/teams' +import * as Teams from '@/stores/teams' import * as React from 'react' import * as RowSizes from './sizes' import type * as T from '@/constants/types' diff --git a/shared/chat/inbox/row/big-teams-divider.tsx b/shared/chat/inbox/row/big-teams-divider.tsx index 35fc6ab594b1..547f2e267670 100644 --- a/shared/chat/inbox/row/big-teams-divider.tsx +++ b/shared/chat/inbox/row/big-teams-divider.tsx @@ -1,4 +1,4 @@ -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as React from 'react' import * as Kb from '@/common-adapters' import * as RowSizes from './sizes' diff --git a/shared/chat/inbox/row/build-team.tsx b/shared/chat/inbox/row/build-team.tsx index 7f6415928fed..822260af2b68 100644 --- a/shared/chat/inbox/row/build-team.tsx +++ b/shared/chat/inbox/row/build-team.tsx @@ -1,6 +1,6 @@ import * as C from '@/constants' import * as React from 'react' -import {useTeamsState} from '@/constants/teams' +import {useTeamsState} from '@/stores/teams' import * as Kb from '@/common-adapters' import {useSafeNavigation} from '@/util/safe-navigation' diff --git a/shared/chat/inbox/row/index.tsx b/shared/chat/inbox/row/index.tsx index 0cd35a8c9631..c47b53e3576c 100644 --- a/shared/chat/inbox/row/index.tsx +++ b/shared/chat/inbox/row/index.tsx @@ -3,9 +3,9 @@ import BigTeamHeader from './big-team-header' import BigTeamChannel from './big-team-channel' import {SmallTeam} from './small-team' import {BigTeamsLabel} from './big-teams-label' -import type * as T from '@/constants/types' +import type {ChatInboxRowItem} from '../rowitem' -const makeRow = (item: T.Chat.ChatInboxRowItem, navKey: string, selected: boolean) => { +const makeRow = (item: ChatInboxRowItem, navKey: string, selected: boolean) => { if (item.type === 'bigTeamsLabel') { return } diff --git a/shared/chat/inbox/row/opened-row-state.tsx b/shared/chat/inbox/row/opened-row-state.tsx index f32a8b91ce9a..29c1d4c415ff 100644 --- a/shared/chat/inbox/row/opened-row-state.tsx +++ b/shared/chat/inbox/row/opened-row-state.tsx @@ -1,4 +1,4 @@ -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as Z from '@/util/zustand' import type * as T from '@/constants/types' diff --git a/shared/chat/inbox/row/sizes.tsx b/shared/chat/inbox/row/sizes.tsx index 584b245ccfed..027ee2a5c384 100644 --- a/shared/chat/inbox/row/sizes.tsx +++ b/shared/chat/inbox/row/sizes.tsx @@ -1,7 +1,7 @@ // In order for the inbox rows to be calculated quickly we use fixed sizes for each type so // in order for the list and the rows to ensure they're the same size we keep the sizes here import * as Kb from '@/common-adapters' -import type * as T from '@/constants/types' +import type {ChatInboxRowType} from '../rowitem' export const smallRowHeight = Kb.Styles.isMobile ? 64 : 56 export const bigRowHeight = Kb.Styles.isMobile ? 40 : 24 @@ -17,8 +17,8 @@ export const dividerHeight = (showingButton: boolean) => { } } -export const getRowHeight = (type: T.Chat.ChatInboxRowType, showingDividerButton: boolean) => { - const exhaustive = (type: T.Chat.ChatInboxRowType, showingDividerButton: boolean) => { +export const getRowHeight = (type: ChatInboxRowType, showingDividerButton: boolean) => { + const exhaustive = (type: ChatInboxRowType, showingDividerButton: boolean) => { switch (type) { case 'bigTeamsLabel': return bigHeaderHeight diff --git a/shared/chat/inbox/row/small-team/bottom-line.tsx b/shared/chat/inbox/row/small-team/bottom-line.tsx index eccb3d94d2bb..1dfbc213d35e 100644 --- a/shared/chat/inbox/row/small-team/bottom-line.tsx +++ b/shared/chat/inbox/row/small-team/bottom-line.tsx @@ -1,10 +1,10 @@ import * as C from '@/constants' -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as React from 'react' import * as Kb from '@/common-adapters' import * as T from '@/constants/types' import {SnippetContext, SnippetDecorationContext} from './contexts' -import {useCurrentUserState} from '@/constants/current-user' +import {useCurrentUserState} from '@/stores/current-user' type Props = { layoutSnippet?: string diff --git a/shared/chat/inbox/row/small-team/index.tsx b/shared/chat/inbox/row/small-team/index.tsx index 869aaa3a9957..f0289b126b34 100644 --- a/shared/chat/inbox/row/small-team/index.tsx +++ b/shared/chat/inbox/row/small-team/index.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as React from 'react' import * as Kb from '@/common-adapters' import {SimpleTopLine} from './top-line' @@ -9,7 +9,7 @@ import * as RowSizes from '../sizes' import * as T from '@/constants/types' import SwipeConvActions from './swipe-conv-actions' import './small-team.css' -import {useCurrentUserState} from '@/constants/current-user' +import {useCurrentUserState} from '@/stores/current-user' import { IsTeamContext, ParticipantsContext, diff --git a/shared/chat/inbox/row/small-team/swipe-conv-actions/index.native.tsx b/shared/chat/inbox/row/small-team/swipe-conv-actions/index.native.tsx index 5ec1de0c2ee2..94ff76ff16f9 100644 --- a/shared/chat/inbox/row/small-team/swipe-conv-actions/index.native.tsx +++ b/shared/chat/inbox/row/small-team/swipe-conv-actions/index.native.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as Kb from '@/common-adapters' import * as React from 'react' import * as Reanimated from 'react-native-reanimated' diff --git a/shared/chat/inbox/row/small-team/top-line.tsx b/shared/chat/inbox/row/small-team/top-line.tsx index 86825d823584..3e45c439e679 100644 --- a/shared/chat/inbox/row/small-team/top-line.tsx +++ b/shared/chat/inbox/row/small-team/top-line.tsx @@ -1,4 +1,4 @@ -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as React from 'react' import * as Kb from '@/common-adapters' import TeamMenu from '@/chat/conversation/info-panel/menu' diff --git a/shared/chat/inbox/row/teams-divider.tsx b/shared/chat/inbox/row/teams-divider.tsx index 282156d7da76..5c90d6eab859 100644 --- a/shared/chat/inbox/row/teams-divider.tsx +++ b/shared/chat/inbox/row/teams-divider.tsx @@ -1,14 +1,15 @@ import * as C from '@/constants' -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as Kb from '@/common-adapters' import * as T from '@/constants/types' import * as React from 'react' import * as RowSizes from './sizes' +import type {ChatInboxRowItem} from '../rowitem' type Props = { hiddenCountDelta?: number smallTeamsExpanded: boolean - rows: Array + rows: Array showButton: boolean toggle: () => void style?: Kb.Styles.StylesCrossPlatform diff --git a/shared/constants/types/chat2/rowitem.tsx b/shared/chat/inbox/rowitem.tsx similarity index 100% rename from shared/constants/types/chat2/rowitem.tsx rename to shared/chat/inbox/rowitem.tsx diff --git a/shared/chat/inbox/search-row.tsx b/shared/chat/inbox/search-row.tsx index c97a2cc786bd..ceedb67615a9 100644 --- a/shared/chat/inbox/search-row.tsx +++ b/shared/chat/inbox/search-row.tsx @@ -1,6 +1,6 @@ import * as React from 'react' import * as C from '@/constants' -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import ChatFilterRow from './filter-row' import StartNewChat from './row/start-new-chat' diff --git a/shared/chat/new-team-dialog-container.tsx b/shared/chat/new-team-dialog-container.tsx index 0421c30d6fef..4c4d1864c28e 100644 --- a/shared/chat/new-team-dialog-container.tsx +++ b/shared/chat/new-team-dialog-container.tsx @@ -1,7 +1,7 @@ import * as C from '@/constants' -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import {CreateNewTeam} from '../teams/new-team' -import {useTeamsState} from '@/constants/teams' +import {useTeamsState} from '@/stores/teams' import upperFirst from 'lodash/upperFirst' const NewTeamDialog = () => { diff --git a/shared/chat/payments/status/index.tsx b/shared/chat/payments/status/index.tsx index 3fe3ec65668b..05236a073498 100644 --- a/shared/chat/payments/status/index.tsx +++ b/shared/chat/payments/status/index.tsx @@ -1,6 +1,6 @@ import * as React from 'react' import * as Styles from '@/styles' -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import PaymentStatusError from './error' import Text from '@/common-adapters/text' import {Box2} from '@/common-adapters/box' @@ -9,7 +9,7 @@ import type * as T from '@/constants/types' import type {MeasureRef} from '@/common-adapters/measure-ref' import type * as WalletTypes from '@/constants/types/wallets' import {useOrdinal} from '@/chat/conversation/messages/ids-context' -import {useCurrentUserState} from '@/constants/current-user' +import {useCurrentUserState} from '@/stores/current-user' // This is actually a dependency of common-adapters/markdown so we have to treat it like a common-adapter, no * import allowed const Kb = { diff --git a/shared/chat/pdf/index.desktop.tsx b/shared/chat/pdf/index.desktop.tsx index d0c53c8853c4..8866e8ec8316 100644 --- a/shared/chat/pdf/index.desktop.tsx +++ b/shared/chat/pdf/index.desktop.tsx @@ -1,9 +1,9 @@ import * as C from '@/constants' -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as React from 'react' import * as Kb from '@/common-adapters' import type {Props} from '.' -import {useFSState} from '@/constants/fs' +import {useFSState} from '@/stores/fs' const ChatPDF = (props: Props) => { const {ordinal} = props @@ -11,7 +11,7 @@ const ChatPDF = (props: Props) => { const title = message?.title || message?.fileName || 'PDF' const url = message?.fileURL const openLocalPathInSystemFileManagerDesktop = useFSState( - s => s.dispatch.dynamic.openLocalPathInSystemFileManagerDesktop + s => s.dispatch.defer.openLocalPathInSystemFileManagerDesktop ) const attachmentDownload = Chat.useChatContext(s => s.dispatch.attachmentDownload) diff --git a/shared/chat/pdf/index.native.tsx b/shared/chat/pdf/index.native.tsx index 044e8691e00b..d0900794f567 100644 --- a/shared/chat/pdf/index.native.tsx +++ b/shared/chat/pdf/index.native.tsx @@ -1,9 +1,9 @@ import * as C from '@/constants' -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as React from 'react' import * as Kb from '@/common-adapters' import type {Props} from '.' -import {useConfigState} from '@/constants/config' +import {useConfigState} from '@/stores/config' const ChatPDF = (props: Props) => { const {ordinal, url} = props @@ -12,7 +12,7 @@ const ChatPDF = (props: Props) => { const [error, setError] = React.useState('') const navigateUp = C.useRouterState(s => s.dispatch.navigateUp) const onBack = () => navigateUp() - const showShareActionSheet = useConfigState(s => s.dispatch.dynamic.showShareActionSheet) + const showShareActionSheet = useConfigState(s => s.dispatch.defer.showShareActionSheet) const onShare = () => { showShareActionSheet?.(url ?? '', '', 'application/pdf') } diff --git a/shared/chat/routes.tsx b/shared/chat/routes.tsx index b34bfe654bdd..d9dcd49db022 100644 --- a/shared/chat/routes.tsx +++ b/shared/chat/routes.tsx @@ -1,6 +1,6 @@ import * as React from 'react' import * as C from '@/constants' -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import chatNewChat from '../team-building/page' import {headerNavigationOptions} from './conversation/header-area' import inboxGetOptions from './inbox/get-options' diff --git a/shared/chat/selectable-big-team-channel-container.tsx b/shared/chat/selectable-big-team-channel-container.tsx index 19af38e9941a..35e0b58f9614 100644 --- a/shared/chat/selectable-big-team-channel-container.tsx +++ b/shared/chat/selectable-big-team-channel-container.tsx @@ -1,4 +1,4 @@ -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import SelectableBigTeamChannel from './selectable-big-team-channel' type OwnProps = { diff --git a/shared/chat/selectable-small-team-container.tsx b/shared/chat/selectable-small-team-container.tsx index 2248c71d3a05..0a1f5f44b24a 100644 --- a/shared/chat/selectable-small-team-container.tsx +++ b/shared/chat/selectable-small-team-container.tsx @@ -1,8 +1,8 @@ -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as Kb from '@/common-adapters' import type {AllowedColors} from '@/common-adapters/text' import SelectableSmallTeam from './selectable-small-team' -import {useCurrentUserState} from '@/constants/current-user' +import {useCurrentUserState} from '@/stores/current-user' type OwnProps = { filter?: string diff --git a/shared/chat/send-to-chat/index.tsx b/shared/chat/send-to-chat/index.tsx index 521f840c96bf..532f8a5a55fd 100644 --- a/shared/chat/send-to-chat/index.tsx +++ b/shared/chat/send-to-chat/index.tsx @@ -1,14 +1,14 @@ import * as C from '@/constants' -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as T from '@/constants/types' import * as React from 'react' import * as Kb from '@/common-adapters' import * as Kbfs from '@/fs/common' import ConversationList from './conversation-list/conversation-list' import ChooseConversation from './conversation-list/choose-conversation' -import {useFSState} from '@/constants/fs' -import * as FS from '@/constants/fs' -import {useCurrentUserState} from '@/constants/current-user' +import {useFSState} from '@/stores/fs' +import * as FS from '@/stores/fs' +import {useCurrentUserState} from '@/stores/current-user' type Props = { canBack?: boolean diff --git a/shared/common-adapters/avatar/hooks.tsx b/shared/common-adapters/avatar/hooks.tsx index 0307b138030d..e1177a28b72f 100644 --- a/shared/common-adapters/avatar/hooks.tsx +++ b/shared/common-adapters/avatar/hooks.tsx @@ -1,5 +1,5 @@ // High level avatar class. Handdles converting from usernames to urls. Deals with testing mode. -import {useConfigState} from '@/constants/config' +import {useConfigState} from '@/stores/config' import * as React from 'react' import {iconTypeToImgSet, urlsToImgSet, urlsToSrcSet, urlsToBaseSrc, type IconType} from '../icon' import * as Styles from '@/styles' @@ -7,9 +7,9 @@ import * as AvatarZus from './store' import './avatar.css' import type {Props} from '.' import {useColorScheme} from 'react-native' -import {useUsersState} from '@/constants/users' -import {useFollowerState} from '@/constants/followers' -import {navToProfile} from '@/constants/router2' +import {useUsersState} from '@/stores/users' +import {useFollowerState} from '@/stores/followers' +import {navToProfile} from '@/stores/router2' export const avatarSizes = [128, 96, 64, 48, 32, 24, 16] as const export type AvatarSize = (typeof avatarSizes)[number] diff --git a/shared/common-adapters/avatar/store.tsx b/shared/common-adapters/avatar/store.tsx index da6964f9fc9a..c73d64efeff5 100644 --- a/shared/common-adapters/avatar/store.tsx +++ b/shared/common-adapters/avatar/store.tsx @@ -1,6 +1,7 @@ import type * as T from '@/constants/types' import * as Z from '@/util/zustand' +// This store has no dependencies on other stores and is safe to import directly from other stores. type Store = T.Immutable<{ counts: Map }> diff --git a/shared/common-adapters/copy-text.tsx b/shared/common-adapters/copy-text.tsx index 7942082307fb..43a797333631 100644 --- a/shared/common-adapters/copy-text.tsx +++ b/shared/common-adapters/copy-text.tsx @@ -8,7 +8,7 @@ import {useTimeout} from './use-timers' import * as Styles from '@/styles' import logger from '@/logger' import type {MeasureRef} from './measure-ref' -import {useConfigState} from '@/constants/config' +import {useConfigState} from '@/stores/config' const Kb = { Box2Measure, @@ -61,8 +61,8 @@ const CopyText = (props: Props) => { const popupAnchor = React.useRef(null) const textRef = React.useRef(null) - const copyToClipboard = useConfigState(s => s.dispatch.dynamic.copyToClipboard) - const showShareActionSheet = useConfigState(s => s.dispatch.dynamic.showShareActionSheet) + const copyToClipboard = useConfigState(s => s.dispatch.defer.copyToClipboard) + const showShareActionSheet = useConfigState(s => s.dispatch.defer.showShareActionSheet) const copy = React.useCallback(() => { if (!text) { if (!loadText) { diff --git a/shared/common-adapters/markdown/channel.tsx b/shared/common-adapters/markdown/channel.tsx index 55a2dc3d237e..a80546942696 100644 --- a/shared/common-adapters/markdown/channel.tsx +++ b/shared/common-adapters/markdown/channel.tsx @@ -1,4 +1,4 @@ -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import type * as T from '@/constants/types' import Text, {type StylesTextCrossPlatform} from '../text' import * as React from 'react' diff --git a/shared/common-adapters/markdown/maybe-mention/index.tsx b/shared/common-adapters/markdown/maybe-mention/index.tsx index 33431631f674..86bbe6a0a396 100644 --- a/shared/common-adapters/markdown/maybe-mention/index.tsx +++ b/shared/common-adapters/markdown/maybe-mention/index.tsx @@ -1,4 +1,4 @@ -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as T from '@/constants/types' import Text, {type StylesTextCrossPlatform} from '@/common-adapters/text' import Mention from '../../mention-container' diff --git a/shared/common-adapters/markdown/maybe-mention/team.tsx b/shared/common-adapters/markdown/maybe-mention/team.tsx index 90bd396a1ee5..e9b7f234f06b 100644 --- a/shared/common-adapters/markdown/maybe-mention/team.tsx +++ b/shared/common-adapters/markdown/maybe-mention/team.tsx @@ -1,7 +1,7 @@ import * as C from '@/constants' -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as T from '@/constants/types' -import {useTeamsState} from '@/constants/teams' +import {useTeamsState} from '@/stores/teams' import * as React from 'react' import Text, {type StylesTextCrossPlatform} from '@/common-adapters/text' import {Box2} from '@/common-adapters/box' diff --git a/shared/common-adapters/markdown/service-decoration.tsx b/shared/common-adapters/markdown/service-decoration.tsx index 9951adafed94..90879a58e0b2 100644 --- a/shared/common-adapters/markdown/service-decoration.tsx +++ b/shared/common-adapters/markdown/service-decoration.tsx @@ -1,7 +1,7 @@ import * as T from '@/constants/types' import * as React from 'react' import * as C from '@/constants' -import {useDeepLinksState} from '@/constants/deeplinks' +import {handleAppLink} from '@/constants/deeplinks' import * as Styles from '@/styles' import Channel from './channel' import KbfsPath from '@/fs/common/kbfs-path' @@ -29,10 +29,9 @@ type KeybaseLinkProps = { } const KeybaseLink = (props: KeybaseLinkProps) => { - const handleAppLink = useDeepLinksState(s => s.dispatch.handleAppLink) const onClick = React.useCallback(() => { handleAppLink(props.link) - }, [handleAppLink, props.link]) + }, [props.link]) return ( { let {username} = ownProps diff --git a/shared/common-adapters/mention.tsx b/shared/common-adapters/mention.tsx index 468c66b4297e..973ec7212085 100644 --- a/shared/common-adapters/mention.tsx +++ b/shared/common-adapters/mention.tsx @@ -1,4 +1,4 @@ -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as Styles from '@/styles' import {WithProfileCardPopup} from './profile-card' import Text from './text' diff --git a/shared/common-adapters/name-with-icon.tsx b/shared/common-adapters/name-with-icon.tsx index fdadf76d3ba6..07c7f2c4f3c7 100644 --- a/shared/common-adapters/name-with-icon.tsx +++ b/shared/common-adapters/name-with-icon.tsx @@ -1,7 +1,7 @@ import * as React from 'react' import * as Styles from '@/styles' import * as C from '@/constants' -import {useTeamsState} from '@/constants/teams' +import {useTeamsState} from '@/stores/teams' import Avatar, {type AvatarSize} from './avatar' import {Box} from './box' import ClickableBox from './clickable-box' @@ -13,8 +13,8 @@ import Text, { type TextTypeBold, } from './text' import ConnectedUsernames from './usernames' -import {useTrackerState} from '@/constants/tracker2' -import {useProfileState} from '@/constants/profile' +import {useTrackerState} from '@/stores/tracker2' +import {useProfileState} from '@/stores/profile' type Size = 'smaller' | 'small' | 'default' | 'big' | 'huge' diff --git a/shared/common-adapters/profile-card.tsx b/shared/common-adapters/profile-card.tsx index 933bfa706f49..a053acccd437 100644 --- a/shared/common-adapters/profile-card.tsx +++ b/shared/common-adapters/profile-card.tsx @@ -2,7 +2,7 @@ import * as C from '@/constants' import * as React from 'react' import * as Styles from '@/styles' import * as Platforms from '@/util/platforms' -import * as Tracker from '@/constants/tracker2' +import * as Tracker from '@/stores/tracker2' import type * as T from '@/constants/types' import capitalize from 'lodash/capitalize' import Box, {Box2, Box2Measure} from './box' @@ -12,16 +12,16 @@ import {_setWithProfileCardPopup} from './usernames' import FloatingMenu from './floating-menu' import Icon from './icon' import Meta from './meta' -import {useProfileState} from '@/constants/profile' -import {useFollowerState} from '@/constants/followers' -import {useCurrentUserState} from '@/constants/current-user' +import {useProfileState} from '@/stores/profile' +import {useFollowerState} from '@/stores/followers' +import {useCurrentUserState} from '@/stores/current-user' import ProgressIndicator from './progress-indicator' import Text from './text' import WithTooltip from './with-tooltip' import DelayedMounting from './delayed-mounting' import {type default as FollowButtonType} from '../profile/user/actions/follow-button' import type ChatButtonType from '../chat/chat-button' -import {useTrackerState} from '@/constants/tracker2' +import {useTrackerState} from '@/stores/tracker2' import type {MeasureRef} from './measure-ref' const positionFallbacks = ['top center', 'bottom center'] as const diff --git a/shared/common-adapters/proof-broken-banner.tsx b/shared/common-adapters/proof-broken-banner.tsx index 861ead336641..e1ecfd0cb296 100644 --- a/shared/common-adapters/proof-broken-banner.tsx +++ b/shared/common-adapters/proof-broken-banner.tsx @@ -1,8 +1,8 @@ import * as C from '@/constants' import * as React from 'react' import {Banner, BannerParagraph} from './banner' -import {useTrackerState} from '@/constants/tracker2' -import {useProfileState} from '@/constants/profile' +import {useTrackerState} from '@/stores/tracker2' +import {useProfileState} from '@/stores/profile' const Kb = {Banner} type Props = {users?: Array} diff --git a/shared/common-adapters/reload.tsx b/shared/common-adapters/reload.tsx index d9b92a06514e..e93d4d91c339 100644 --- a/shared/common-adapters/reload.tsx +++ b/shared/common-adapters/reload.tsx @@ -9,8 +9,8 @@ import Text from './text' import Button from './button' import Icon from './icon' import type {RPCError} from '@/util/errors' -import {settingsFeedbackTab} from '@/constants/settings/util' -import {useConfigState} from '@/constants/config' +import {settingsFeedbackTab} from '@/constants/settings' +import {useConfigState} from '@/stores/config' const Kb = { Box2, diff --git a/shared/common-adapters/team-with-popup.tsx b/shared/common-adapters/team-with-popup.tsx index 4059c9608948..f907e5e1ca80 100644 --- a/shared/common-adapters/team-with-popup.tsx +++ b/shared/common-adapters/team-with-popup.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Teams from '@/constants/teams' +import * as Teams from '@/stores/teams' import * as React from 'react' import {Box2} from './box' import * as Styles from '@/styles' diff --git a/shared/common-adapters/usernames.tsx b/shared/common-adapters/usernames.tsx index e86fcb564666..570f91b6abab 100644 --- a/shared/common-adapters/usernames.tsx +++ b/shared/common-adapters/usernames.tsx @@ -12,11 +12,11 @@ import Text, { import {backgroundModeIsNegative} from './text.shared' import isArray from 'lodash/isArray' import type {e164ToDisplay as e164ToDisplayType} from '@/util/phone-numbers' -import {useTrackerState} from '@/constants/tracker2' -import {useUsersState} from '@/constants/users' -import {useProfileState} from '@/constants/profile' -import {useFollowerState} from '@/constants/followers' -import {useCurrentUserState} from '@/constants/current-user' +import {useTrackerState} from '@/stores/tracker2' +import {useUsersState} from '@/stores/users' +import {useProfileState} from '@/stores/profile' +import {useFollowerState} from '@/stores/followers' +import {useCurrentUserState} from '@/stores/current-user' export type User = { username: string diff --git a/shared/common-adapters/wave-button.tsx b/shared/common-adapters/wave-button.tsx index 40a33f359a66..63a8ab05699a 100644 --- a/shared/common-adapters/wave-button.tsx +++ b/shared/common-adapters/wave-button.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as React from 'react' import {Box2, Box} from './box' import Icon from './icon' diff --git a/shared/constants/active/index.tsx b/shared/constants/active/index.tsx deleted file mode 100644 index cf53cc33b318..000000000000 --- a/shared/constants/active/index.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import * as Chat from '../chat2' -import type * as T from '../types' -import * as Z from '@/util/zustand' -import {storeRegistry} from '../store-registry' - -type Store = T.Immutable<{active: boolean}> -const initialStore: Store = {active: true} - -export interface State extends Store { - dispatch: { - resetState: 'default' - setActive: (a: boolean) => void - } -} -export const useActiveState = Z.createZustand(set => { - const dispatch: State['dispatch'] = { - resetState: 'default', - setActive: a => { - set(s => { - s.active = a - }) - const cs = storeRegistry.getConvoState(Chat.getSelectedConversation()) - cs.dispatch.markThreadAsRead() - }, - } - return {...initialStore, dispatch} -}) diff --git a/shared/constants/archive/util.tsx b/shared/constants/archive/util.tsx deleted file mode 100644 index e44657ebf42e..000000000000 --- a/shared/constants/archive/util.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import * as EngineGen from '@/actions/engine-gen-gen' -import {storeRegistry} from '../store-registry' - -export const onEngineIncoming = (action: EngineGen.Actions) => { - switch (action.type) { - case EngineGen.keybase1NotifySimpleFSSimpleFSArchiveStatusChanged: - case EngineGen.chat1NotifyChatChatArchiveComplete: - case EngineGen.chat1NotifyChatChatArchiveProgress: - { - storeRegistry.getState('archive').dispatch.onEngineIncomingImpl(action) - } - break - default: - } -} diff --git a/shared/constants/autoreset/util.tsx b/shared/constants/autoreset/util.tsx deleted file mode 100644 index ccaa494f61a0..000000000000 --- a/shared/constants/autoreset/util.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import * as EngineGen from '@/actions/engine-gen-gen' -import {storeRegistry} from '../store-registry' - -export const onEngineIncoming = (action: EngineGen.Actions) => { - switch (action.type) { - case EngineGen.keybase1NotifyBadgesBadgeState: - { - storeRegistry.getState('autoreset').dispatch.onEngineIncomingImpl(action) - } - break - default: - } -} diff --git a/shared/constants/bots/util.tsx b/shared/constants/bots/util.tsx deleted file mode 100644 index 1af60048ec8d..000000000000 --- a/shared/constants/bots/util.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import * as EngineGen from '@/actions/engine-gen-gen' -import type * as T from '../types' -import {storeRegistry} from '../store-registry' - -export const onEngineIncoming = (action: EngineGen.Actions) => { - switch (action.type) { - case EngineGen.keybase1NotifyFeaturedBotsFeaturedBotsUpdate: - { - storeRegistry.getState('bots').dispatch.onEngineIncomingImpl(action) - } - break - default: - } -} - - -export const getFeaturedSorted = ( - featuredBotsMap: ReadonlyMap -): Array => { - const featured = [...featuredBotsMap.values()] - featured.sort((a: T.RPCGen.FeaturedBot, b: T.RPCGen.FeaturedBot) => { - if (a.rank < b.rank) { - return 1 - } else if (a.rank > b.rank) { - return -1 - } - return 0 - }) - return featured -} diff --git a/shared/constants/chat2/common.tsx b/shared/constants/chat2/common.tsx index e69d40c23dc3..45ae0acc1c03 100644 --- a/shared/constants/chat2/common.tsx +++ b/shared/constants/chat2/common.tsx @@ -1,12 +1,12 @@ import * as T from '../types' import {isMobile, isTablet} from '../platform' -import * as Router2 from '../router2' -import {storeRegistry} from '../store-registry' +import {getVisibleScreen} from '@/constants/router2' +import {useConfigState} from '@/stores/config' export const explodingModeGregorKeyPrefix = 'exploding:' export const getSelectedConversation = (allowUnderModal: boolean = false): T.Chat.ConversationIDKey => { - const maybeVisibleScreen = Router2.getVisibleScreen(undefined, allowUnderModal) + const maybeVisibleScreen = getVisibleScreen(undefined, allowUnderModal) if (maybeVisibleScreen?.name === threadRouteName) { const mParams = maybeVisibleScreen.params as undefined | {conversationIDKey?: T.Chat.ConversationIDKey} return mParams?.conversationIDKey ?? T.Chat.noConversationIDKey @@ -25,13 +25,12 @@ export const isUserActivelyLookingAtThisThread = (conversationIDKey: T.Chat.Conv if (!isSplit) { chatThreadSelected = true // conversationIDKey === selectedConversationIDKey is the only thing that matters in the new router } else { - const maybeVisibleScreen = Router2.getVisibleScreen() + const maybeVisibleScreen = getVisibleScreen() chatThreadSelected = (maybeVisibleScreen === undefined ? undefined : maybeVisibleScreen.name) === threadRouteName } - const {appFocused} = storeRegistry.getState('config') - const {active: userActive} = storeRegistry.getState('active') + const {appFocused, active: userActive} = useConfigState.getState() return ( appFocused && // app focused? diff --git a/shared/constants/chat2/debug.tsx b/shared/constants/chat2/debug.tsx index ba94fb89a7f8..e91c336a2aca 100644 --- a/shared/constants/chat2/debug.tsx +++ b/shared/constants/chat2/debug.tsx @@ -1,5 +1,5 @@ // Debug utilities for chat -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as React from 'react' import type * as T from '@/constants/types' import logger from '@/logger' diff --git a/shared/constants/chat2/index.tsx b/shared/constants/chat2/index.tsx index 818915ca1b15..e69de29bb2d1 100644 --- a/shared/constants/chat2/index.tsx +++ b/shared/constants/chat2/index.tsx @@ -1,2001 +0,0 @@ -import * as T from '../types' -import {ignorePromise, timeoutPromise, type ViewPropsToPageProps} from '../utils' -import * as Tabs from '../tabs' -import * as EngineGen from '@/actions/engine-gen-gen' -import type * as ConfigConstants from '../config' -import * as Message from './message' -import * as Router2 from '../router2' -import * as TeamConstants from '../teams/util' -import logger from '@/logger' -import {RPCError} from '@/util/errors' -import * as Meta from './meta' -import {isMobile, isPhone} from '../platform' -import * as Z from '@/util/zustand' -import * as Common from './common' -import {clearChatStores, chatStores} from './convostate' -import {uint8ArrayToString} from 'uint8array-extras' -import isEqual from 'lodash/isEqual' -import {bodyToJSON} from '../rpc-utils' -import {navigateAppend, navUpToScreen, switchTab} from '../router2/util' -import {storeRegistry} from '../store-registry' -import * as S from '../strings' - -const defaultTopReacjis = [ - {name: ':+1:'}, - {name: ':-1:'}, - {name: ':tada:'}, - {name: ':joy:'}, - {name: ':sunglasses:'}, -] -const defaultSkinTone = 1 -const defaultUserReacjis = {skinTone: defaultSkinTone, topReacjis: defaultTopReacjis} - -// while we're debugging chat issues -export const DEBUG_CHAT_DUMP = true - -const blockButtonsGregorPrefix = 'blockButtons.' - -export const inboxSearchMaxTextMessages = 25 -export const inboxSearchMaxTextResults = 50 -export const inboxSearchMaxNameResults = 7 -export const inboxSearchMaxUnreadNameResults = isMobile ? 5 : 10 - -export const makeInboxSearchInfo = (): T.Chat.InboxSearchInfo => ({ - botsResults: [], - botsResultsSuggested: false, - botsStatus: 'initial', - indexPercent: 0, - nameResults: [], - nameResultsUnread: false, - nameStatus: 'initial', - openTeamsResults: [], - openTeamsResultsSuggested: false, - openTeamsStatus: 'initial', - query: '', - selectedIndex: 0, - textResults: [], - textStatus: 'initial', -}) - -const getInboxSearchSelected = ( - inboxSearch: T.Immutable -): - | undefined - | { - conversationIDKey: T.Chat.ConversationIDKey - query?: string - } => { - const {selectedIndex, nameResults, botsResults, openTeamsResults, textResults} = inboxSearch - const firstTextResultIdx = botsResults.length + openTeamsResults.length + nameResults.length - const firstOpenTeamResultIdx = nameResults.length - - if (selectedIndex < firstOpenTeamResultIdx) { - const maybeNameResults = nameResults[selectedIndex] - const conversationIDKey = maybeNameResults === undefined ? undefined : maybeNameResults.conversationIDKey - if (conversationIDKey) { - return { - conversationIDKey, - query: undefined, - } - } - } else if (selectedIndex < firstTextResultIdx) { - return - } else if (selectedIndex >= firstTextResultIdx) { - const result = textResults[selectedIndex - firstTextResultIdx] - if (result) { - return { - conversationIDKey: result.conversationIDKey, - query: result.query, - } - } - } - return -} - -export const getMessageKey = (message: T.Chat.Message) => - `${message.conversationIDKey}:${T.Chat.ordinalToNumber(message.ordinal)}` - -export const getBotsAndParticipants = ( - meta: T.Immutable, - participantInfo: T.Immutable, - sort?: boolean -) => { - const isAdhocTeam = meta.teamType === 'adhoc' - const teamMembers = - storeRegistry.getState('teams').teamIDToMembers.get(meta.teamID) ?? new Map() - let bots: Array = [] - if (isAdhocTeam) { - bots = participantInfo.all.filter(p => !participantInfo.name.includes(p)) - } else { - bots = [...teamMembers.values()] - .filter( - p => - TeamConstants.userIsRoleInTeamWithInfo(teamMembers, p.username, 'restrictedbot') || - TeamConstants.userIsRoleInTeamWithInfo(teamMembers, p.username, 'bot') - ) - .map(p => p.username) - .sort((l, r) => l.localeCompare(r)) - } - let participants: ReadonlyArray = participantInfo.all - if (meta.channelname === 'general') { - participants = [...teamMembers.values()].reduce>((l, mi) => { - l.push(mi.username) - return l - }, []) - } - participants = participants.filter(p => !bots.includes(p)) - participants = sort - ? participants - .map(p => ({ - isAdmin: !isAdhocTeam ? TeamConstants.userIsRoleInTeamWithInfo(teamMembers, p, 'admin') : false, - isOwner: !isAdhocTeam ? TeamConstants.userIsRoleInTeamWithInfo(teamMembers, p, 'owner') : false, - username: p, - })) - .sort((l, r) => { - const leftIsAdmin = l.isAdmin || l.isOwner - const rightIsAdmin = r.isAdmin || r.isOwner - if (leftIsAdmin && !rightIsAdmin) { - return -1 - } else if (!leftIsAdmin && rightIsAdmin) { - return 1 - } - return l.username.localeCompare(r.username) - }) - .map(p => p.username) - : participants - return {bots, participants} -} - -export const getTeamMentionName = (name: string, channel: string) => { - return name + (channel ? `#${channel}` : '') -} - -export const isAssertion = (username: string) => username.includes('@') - -export const clampImageSize = (width: number, height: number, maxWidth: number, maxHeight: number) => { - const aspectRatio = width / height - - let newWidth = width - let newHeight = height - - if (newWidth > maxWidth) { - newWidth = maxWidth - newHeight = newWidth / aspectRatio - } - - if (newHeight > maxHeight) { - newHeight = maxHeight - newWidth = newHeight * aspectRatio - } - - return { - height: Math.ceil(newHeight), - width: Math.ceil(newWidth), - } -} - -export const zoomImage = (width: number, height: number, maxThumbSize: number) => { - const dims = - height > width - ? {height: (maxThumbSize * height) / width, width: maxThumbSize} - : {height: maxThumbSize, width: (maxThumbSize * width) / height} - const marginHeight = dims.height > maxThumbSize ? (dims.height - maxThumbSize) / 2 : 0 - const marginWidth = dims.width > maxThumbSize ? (dims.width - maxThumbSize) / 2 : 0 - return { - dims, - margins: { - marginBottom: -marginHeight, - marginLeft: -marginWidth, - marginRight: -marginWidth, - marginTop: -marginHeight, - }, - } -} - -const uiParticipantsToParticipantInfo = ( - uiParticipants: ReadonlyArray -): T.Chat.ParticipantInfo => { - const participantInfo = {all: new Array(), contactName: new Map(), name: new Array()} - uiParticipants.forEach(part => { - const {assertion, contactName, inConvName} = part - participantInfo.all.push(assertion) - if (inConvName) { - participantInfo.name.push(assertion) - } - if (contactName) { - participantInfo.contactName.set(assertion, contactName) - } - }) - return participantInfo -} - -/** - * Returns true if the team is big and you're a member - */ -export const isBigTeam = (state: State, teamID: string): boolean => { - const bigTeams = state.inboxLayout?.bigTeams - return (bigTeams || []).some(v => v.state === T.RPCChat.UIInboxBigTeamRowTyp.label && v.label.id === teamID) -} - -// prettier-ignore -type PreviewReason = - | 'appLink' | 'channelHeader' | 'convertAdHoc' | 'files' | 'forward' | 'fromAReset' - | 'journeyCardPopular' | 'manageView' | 'memberView' | 'messageLink' | 'newChannel' - | 'profile' | 'requestedPayment' | 'resetChatWithoutThem' | 'search' | 'sentPayment' - | 'teamHeader' | 'teamInvite' | 'teamMember' | 'teamMention' | 'teamRow' | 'tracker' | 'transaction' - -type Store = T.Immutable<{ - botPublicCommands: Map - createConversationError?: T.Chat.CreateConversationError - smallTeamBadgeCount: number - bigTeamBadgeCount: number - smallTeamsExpanded: boolean // if we're showing all small teams, - lastCoord?: T.Chat.Coordinate - paymentStatusMap: Map - staticConfig?: T.Chat.StaticConfig // static config stuff from the service. only needs to be loaded once. if null, it hasn't been loaded, - trustedInboxHasLoaded: boolean // if we've done initial trusted inbox load, - userReacjis: T.Chat.UserReacjis - userEmojis?: Array - userEmojisForAutocomplete?: Array - infoPanelShowing: boolean - infoPanelSelectedTab?: 'settings' | 'members' | 'attachments' | 'bots' - inboxNumSmallRows?: number - inboxHasLoaded: boolean // if we've ever loaded, - inboxLayout?: T.RPCChat.UIInboxLayout // layout of the inbox - inboxSearch?: T.Chat.InboxSearchInfo - teamIDToGeneralConvID: Map - flipStatusMap: Map - maybeMentionMap: Map - blockButtonsMap: Map // Should we show block buttons for this team ID? -}> - -const initialStore: Store = { - bigTeamBadgeCount: 0, - blockButtonsMap: new Map(), - botPublicCommands: new Map(), - createConversationError: undefined, - flipStatusMap: new Map(), - inboxHasLoaded: false, - inboxLayout: undefined, - inboxNumSmallRows: 5, - inboxSearch: undefined, - infoPanelSelectedTab: undefined, - infoPanelShowing: false, - lastCoord: undefined, - maybeMentionMap: new Map(), - paymentStatusMap: new Map(), - smallTeamBadgeCount: 0, - smallTeamsExpanded: false, - staticConfig: undefined, - teamIDToGeneralConvID: new Map(), - trustedInboxHasLoaded: false, - userEmojis: undefined, - userEmojisForAutocomplete: undefined, - userReacjis: defaultUserReacjis, -} - -export interface State extends Store { - dispatch: { - badgesUpdated: (badgeState?: T.RPCGen.BadgeState) => void - clearMetas: () => void - conversationErrored: ( - allowedUsers: ReadonlyArray, - disallowedUsers: ReadonlyArray, - code: number, - message: string - ) => void - createConversation: (participants: ReadonlyArray, highlightMessageID?: T.Chat.MessageID) => void - ensureWidgetMetas: () => void - findGeneralConvIDFromTeamID: (teamID: T.Teams.TeamID) => void - fetchUserEmoji: (conversationIDKey?: T.Chat.ConversationIDKey, onlyInTeam?: boolean) => void - inboxRefresh: ( - reason: - | 'bootstrap' - | 'componentNeverLoaded' - | 'inboxStale' - | 'inboxSyncedClear' - | 'inboxSyncedUnknown' - | 'joinedAConversation' - | 'leftAConversation' - | 'teamTypeChanged' - | 'maybeKickedFromTeam' - | 'widgetRefresh' - | 'shareConfigSearch' - ) => void - inboxSearch: (query: string) => void - inboxSearchMoveSelectedIndex: (increment: boolean) => void - inboxSearchSelect: ( - conversationIDKey?: T.Chat.ConversationIDKey, - query?: string, - selectedIndex?: number - ) => void - loadStaticConfig: () => void - loadedUserEmoji: (results: T.RPCChat.UserEmojiRes) => void - maybeChangeSelectedConv: () => void - messageSendByUsername: (username: string, text: string, waitingKey?: string) => void - metasReceived: ( - metas: ReadonlyArray, - removals?: ReadonlyArray // convs to remove - ) => void - navigateToInbox: (allowSwitchTab?: boolean) => void - onChatThreadStale: (action: EngineGen.Chat1NotifyChatChatThreadsStalePayload) => void - onEngineIncomingImpl: (action: EngineGen.Actions) => void - onChatInboxSynced: (action: EngineGen.Chat1NotifyChatChatInboxSyncedPayload) => void - onGetInboxConvsUnboxed: (action: EngineGen.Chat1ChatUiChatInboxConversationPayload) => void - onGetInboxUnverifiedConvs: (action: EngineGen.Chat1ChatUiChatInboxUnverifiedPayload) => void - onIncomingInboxUIItem: (inboxUIItem?: T.RPCChat.InboxUIItem) => void - onRouteChanged: (prev: T.Immutable, next: T.Immutable) => void - onTeamBuildingFinished: (users: ReadonlySet) => void - paymentInfoReceived: (paymentInfo: T.Chat.ChatPaymentInfo) => void - previewConversation: (p: { - participants?: ReadonlyArray - teamname?: string - channelname?: string - conversationIDKey?: T.Chat.ConversationIDKey // we only use this when we click on channel mentions. we could maybe change that plumbing but keeping it for now - highlightMessageID?: T.Chat.MessageID - reason: PreviewReason - }) => void - queueMetaToRequest: (ids: ReadonlyArray) => void - queueMetaHandle: () => void - refreshBotPublicCommands: (username: string) => void - resetConversationErrored: () => void - resetState: () => void - setMaybeMentionInfo: (name: string, info: T.RPCChat.UIMaybeMentionInfo) => void - setTrustedInboxHasLoaded: () => void - setInfoPanelTab: (tab: 'settings' | 'members' | 'attachments' | 'bots' | undefined) => void - setInboxNumSmallRows: (rows: number, ignoreWrite?: boolean) => void - toggleInboxSearch: (enabled: boolean) => void - toggleSmallTeamsExpanded: () => void - unboxRows: (ids: Array, force?: boolean) => void - updateCoinFlipStatus: (statuses: ReadonlyArray) => void - updateInboxLayout: (layout: string) => void - updateLastCoord: (coord: T.Chat.Coordinate) => void - updateUserReacjis: (userReacjis: T.RPCGen.UserReacjis) => void - updatedGregor: (items: ConfigConstants.State['gregorPushState']) => void - updateInfoPanel: (show: boolean, tab: 'settings' | 'members' | 'attachments' | 'bots' | undefined) => void - } - getBackCount: (conversationIDKey: T.Chat.ConversationIDKey) => number - getBadgeHiddenCount: (ids: Set) => {badgeCount: number; hiddenCount: number} - getUnreadIndicies: (ids: Array) => Map -} - -// Only get the untrusted conversations out -const untrustedConversationIDKeys = (ids: ReadonlyArray) => - ids.filter(id => storeRegistry.getConvoState(id).meta.trustedState === 'untrusted') - -// generic chat store -export const useChatState = Z.createZustand((set, get) => { - // We keep a set of conversations to unbox - let metaQueue = new Set() - - const dispatch: State['dispatch'] = { - badgesUpdated: b => { - if (!b) return - // clear all first - for (const [, cs] of chatStores) { - cs.getState().dispatch.badgesUpdated(0) - } - b.conversations?.forEach(c => { - const id = T.Chat.conversationIDToKey(c.convID) - storeRegistry.getConvoState(id).dispatch.badgesUpdated(c.badgeCount) - storeRegistry.getConvoState(id).dispatch.unreadUpdated(c.unreadMessages) - }) - const {bigTeamBadgeCount, smallTeamBadgeCount} = b - set(s => { - s.smallTeamBadgeCount = smallTeamBadgeCount - s.bigTeamBadgeCount = bigTeamBadgeCount - }) - }, - clearMetas: () => { - for (const [, cs] of chatStores) { - cs.getState().dispatch.setMeta() - } - }, - conversationErrored: (allowedUsers, disallowedUsers, code, message) => { - set(s => { - s.createConversationError = T.castDraft({ - allowedUsers, - code, - disallowedUsers, - message, - }) - }) - }, - createConversation: (participants, highlightMessageID) => { - // TODO This will break if you try to make 2 new conversations at the same time because there is - // only one pending conversation state. - // The fix involves being able to make multiple pending conversations - const f = async () => { - const username = storeRegistry.getState('current-user').username - if (!username) { - logger.error('Making a convo while logged out?') - return - } - try { - const result = await T.RPCChat.localNewConversationLocalRpcPromise( - { - identifyBehavior: T.RPCGen.TLFIdentifyBehavior.chatGui, - membersType: T.RPCChat.ConversationMembersType.impteamnative, - tlfName: [...new Set([username, ...participants])].join(','), - tlfVisibility: T.RPCGen.TLFVisibility.private, - topicType: T.RPCChat.TopicType.chat, - }, - S.waitingKeyChatCreating - ) - const {conv, uiConv} = result - const conversationIDKey = T.Chat.conversationIDToKey(conv.info.id) - if (!conversationIDKey) { - logger.warn("Couldn't make a new conversation?") - } else { - const meta = Meta.inboxUIItemToConversationMeta(uiConv) - if (meta) { - get().dispatch.metasReceived([meta]) - } - - const participantInfo: T.Chat.ParticipantInfo = uiParticipantsToParticipantInfo( - uiConv.participants ?? [] - ) - if (participantInfo.all.length > 0) { - storeRegistry - .getConvoState(T.Chat.stringToConversationIDKey(uiConv.convID)) - .dispatch.setParticipants(participantInfo) - } - storeRegistry - .getConvoState(conversationIDKey) - .dispatch.navigateToThread('justCreated', highlightMessageID) - } - } catch (error) { - if (error instanceof RPCError) { - const f = error.fields as Array<{key?: string}> | undefined - const errUsernames = f?.filter(elem => elem.key === 'usernames') as - | undefined - | Array<{key: string; value: string}> - let disallowedUsers: Array = [] - if (errUsernames?.length) { - const {value} = errUsernames[0] ?? {value: ''} - disallowedUsers = value.split(',') - } - const allowedUsers = participants.filter(x => !disallowedUsers.includes(x)) - get().dispatch.conversationErrored(allowedUsers, disallowedUsers, error.code, error.desc) - storeRegistry - .getConvoState(T.Chat.pendingErrorConversationIDKey) - .dispatch.navigateToThread('justCreated', highlightMessageID) - } - } - } - ignorePromise(f()) - }, - ensureWidgetMetas: () => { - const {inboxLayout} = get() - if (!inboxLayout?.widgetList) { - return - } - const missing = inboxLayout.widgetList.reduce>((l, v) => { - if (!storeRegistry.getConvoState(v.convID).isMetaGood()) { - l.push(v.convID) - } - return l - }, []) - if (missing.length === 0) { - return - } - get().dispatch.unboxRows(missing, true) - }, - fetchUserEmoji: (conversationIDKey, onlyInTeam) => { - const f = async () => { - const results = await T.RPCChat.localUserEmojisRpcPromise( - { - convID: - conversationIDKey && conversationIDKey !== T.Chat.noConversationIDKey - ? T.Chat.keyToConversationID(conversationIDKey) - : null, - opts: { - getAliases: true, - getCreationInfo: false, - onlyInTeam: onlyInTeam ?? false, - }, - }, - S.waitingKeyChatLoadingEmoji - ) - get().dispatch.loadedUserEmoji(results) - } - ignorePromise(f()) - }, - findGeneralConvIDFromTeamID: teamID => { - const f = async () => { - try { - const conv = await T.RPCChat.localFindGeneralConvFromTeamIDRpcPromise({teamID}) - const meta = Meta.inboxUIItemToConversationMeta(conv) - if (!meta) { - logger.info(`findGeneralConvIDFromTeamID: failed to convert to meta`) - return - } - get().dispatch.metasReceived([meta]) - set(s => { - s.teamIDToGeneralConvID.set(teamID, T.Chat.stringToConversationIDKey(conv.convID)) - }) - } catch (error) { - if (error instanceof RPCError) { - logger.info(`findGeneralConvIDFromTeamID: failed to get general conv: ${error.message}`) - } - } - } - ignorePromise(f()) - }, - inboxRefresh: reason => { - const f = async () => { - const {username} = storeRegistry.getState('current-user') - const {loggedIn} = storeRegistry.getState('config') - if (!loggedIn || !username) { - return - } - const clearExistingMetas = reason === 'inboxSyncedClear' - const clearExistingMessages = reason === 'inboxSyncedClear' - - logger.info(`Inbox refresh due to ${reason}`) - const reselectMode = - get().inboxHasLoaded || isPhone - ? T.RPCChat.InboxLayoutReselectMode.default - : T.RPCChat.InboxLayoutReselectMode.force - await T.RPCChat.localRequestInboxLayoutRpcPromise({reselectMode}) - if (clearExistingMetas) { - get().dispatch.clearMetas() - } - if (clearExistingMessages) { - for (const [, cs] of chatStores) { - cs.getState().dispatch.messagesClear() - } - } - } - ignorePromise(f()) - }, - inboxSearch: query => { - set(s => { - const {inboxSearch} = s - if (inboxSearch) { - inboxSearch.query = query - } - }) - const f = async () => { - const teamType = (t: T.RPCChat.TeamType) => (t === T.RPCChat.TeamType.complex ? 'big' : 'small') - - const onConvHits = (resp: T.RPCChat.MessageTypes['chat.1.chatUi.chatSearchConvHits']['inParam']) => { - const results = (resp.hits.hits || []).reduce>((arr, h) => { - arr.push({ - conversationIDKey: T.Chat.stringToConversationIDKey(h.convID), - name: h.name, - teamType: teamType(h.teamType), - }) - return arr - }, []) - - set(s => { - const unread = resp.hits.unreadMatches - const {inboxSearch} = s - if (inboxSearch?.nameStatus === 'inprogress') { - inboxSearch.nameResults = results - inboxSearch.nameResultsUnread = unread - inboxSearch.nameStatus = 'success' - } - }) - - const missingMetas = results.reduce>((arr, r) => { - if (!storeRegistry.getConvoState(r.conversationIDKey).isMetaGood()) { - arr.push(r.conversationIDKey) - } - return arr - }, []) - if (missingMetas.length > 0) { - get().dispatch.unboxRows(missingMetas, true) - } - } - - const onOpenTeamHits = ( - resp: T.RPCChat.MessageTypes['chat.1.chatUi.chatSearchTeamHits']['inParam'] - ) => { - const results = (resp.hits.hits || []).reduce>((arr, h) => { - const {description, name, memberCount, inTeam} = h - arr.push({ - description: description ?? '', - inTeam, - memberCount, - name, - publicAdmins: [], - }) - return arr - }, []) - const suggested = resp.hits.suggestedMatches - set(s => { - const {inboxSearch} = s - if (inboxSearch?.openTeamsStatus === 'inprogress') { - inboxSearch.openTeamsResultsSuggested = suggested - inboxSearch.openTeamsResults = T.castDraft(results) - inboxSearch.openTeamsStatus = 'success' - } - }) - } - - const onBotsHits = (resp: T.RPCChat.MessageTypes['chat.1.chatUi.chatSearchBotHits']['inParam']) => { - const results = resp.hits.hits || [] - const suggested = resp.hits.suggestedMatches - set(s => { - const {inboxSearch} = s - if (inboxSearch?.botsStatus === 'inprogress') { - inboxSearch.botsResultsSuggested = suggested - inboxSearch.botsResults = T.castDraft(results) - inboxSearch.botsStatus = 'success' - } - }) - } - - const onTextHit = (resp: T.RPCChat.MessageTypes['chat.1.chatUi.chatSearchInboxHit']['inParam']) => { - const {convID, convName, hits, query, teamType: tt, time} = resp.searchHit - - const result = { - conversationIDKey: T.Chat.conversationIDToKey(convID), - name: convName, - numHits: hits?.length ?? 0, - query, - teamType: teamType(tt), - time, - } as const - set(s => { - const {inboxSearch} = s - if (inboxSearch?.textStatus === 'inprogress') { - const {conversationIDKey} = result - const textResults = inboxSearch.textResults.filter( - r => r.conversationIDKey !== conversationIDKey - ) - textResults.push(result) - inboxSearch.textResults = textResults.sort((l, r) => r.time - l.time) - } - }) - - if ( - storeRegistry.getConvoState(result.conversationIDKey).meta.conversationIDKey === - T.Chat.noConversationIDKey - ) { - get().dispatch.unboxRows([result.conversationIDKey], true) - } - } - const onStart = () => { - set(s => { - const {inboxSearch} = s - if (inboxSearch) { - inboxSearch.nameStatus = 'inprogress' - inboxSearch.selectedIndex = 0 - inboxSearch.textResults = [] - inboxSearch.textStatus = 'inprogress' - inboxSearch.openTeamsStatus = 'inprogress' - inboxSearch.botsStatus = 'inprogress' - } - }) - } - const onDone = () => { - set(s => { - const status = 'success' - const inboxSearch = s.inboxSearch ?? makeInboxSearchInfo() - s.inboxSearch = T.castDraft(inboxSearch) - inboxSearch.textStatus = status - }) - } - - const onIndexStatus = ( - resp: T.RPCChat.MessageTypes['chat.1.chatUi.chatSearchIndexStatus']['inParam'] - ) => { - const percent = resp.status.percentIndexed - set(s => { - const {inboxSearch} = s - if (inboxSearch?.textStatus === 'inprogress') { - inboxSearch.indexPercent = percent - } - }) - } - - try { - await T.RPCChat.localSearchInboxRpcListener({ - incomingCallMap: { - 'chat.1.chatUi.chatSearchBotHits': onBotsHits, - 'chat.1.chatUi.chatSearchConvHits': onConvHits, - 'chat.1.chatUi.chatSearchInboxDone': onDone, - 'chat.1.chatUi.chatSearchInboxHit': onTextHit, - 'chat.1.chatUi.chatSearchInboxStart': onStart, - 'chat.1.chatUi.chatSearchIndexStatus': onIndexStatus, - 'chat.1.chatUi.chatSearchTeamHits': onOpenTeamHits, - }, - params: { - identifyBehavior: T.RPCGen.TLFIdentifyBehavior.chatGui, - namesOnly: false, - opts: { - afterContext: 0, - beforeContext: 0, - isRegex: false, - matchMentions: false, - maxBots: 10, - maxConvsHit: inboxSearchMaxTextResults, - maxConvsSearched: 0, - maxHits: inboxSearchMaxTextMessages, - maxMessages: -1, - maxNameConvs: query.length > 0 ? inboxSearchMaxNameResults : inboxSearchMaxUnreadNameResults, - maxTeams: 10, - reindexMode: T.RPCChat.ReIndexingMode.postsearchSync, - sentAfter: 0, - sentBefore: 0, - sentBy: '', - sentTo: '', - skipBotCache: false, - }, - query, - }, - }) - } catch (error) { - if (error instanceof RPCError) { - if (!(error.code === T.RPCGen.StatusCode.sccanceled)) { - logger.error('search failed: ' + error.message) - set(s => { - const status = 'error' - const inboxSearch = s.inboxSearch ?? makeInboxSearchInfo() - s.inboxSearch = T.castDraft(inboxSearch) - inboxSearch.textStatus = status - }) - } - } - } - } - ignorePromise(f()) - }, - inboxSearchMoveSelectedIndex: increment => { - set(s => { - const {inboxSearch} = s - if (inboxSearch) { - const {selectedIndex} = inboxSearch - const totalResults = inboxSearch.nameResults.length + inboxSearch.textResults.length - if (increment && selectedIndex < totalResults - 1) { - inboxSearch.selectedIndex = selectedIndex + 1 - } else if (!increment && selectedIndex > 0) { - inboxSearch.selectedIndex = selectedIndex - 1 - } - } - }) - }, - inboxSearchSelect: (_conversationIDKey, q, selectedIndex) => { - let conversationIDKey = _conversationIDKey - let query = q - set(s => { - const {inboxSearch} = s - if (inboxSearch && selectedIndex !== undefined) { - inboxSearch.selectedIndex = selectedIndex - } - }) - - const {inboxSearch} = get() - if (!inboxSearch) { - return - } - const selected = getInboxSearchSelected(inboxSearch) - if (!conversationIDKey) { - conversationIDKey = selected?.conversationIDKey - } - - if (!conversationIDKey) { - return - } - if (!query) { - query = selected?.query - } - - storeRegistry.getConvoState(conversationIDKey).dispatch.navigateToThread('inboxSearch') - if (query) { - const cs = storeRegistry.getConvoState(conversationIDKey) - cs.dispatch.setThreadSearchQuery(query) - cs.dispatch.toggleThreadSearch(false) - cs.dispatch.threadSearch(query) - } else { - get().dispatch.toggleInboxSearch(false) - } - }, - loadStaticConfig: () => { - if (get().staticConfig) { - return - } - const {handshakeVersion, dispatch} = storeRegistry.getState('daemon') - const f = async () => { - const name = 'chat.loadStatic' - dispatch.wait(name, handshakeVersion, true) - try { - const res = await T.RPCChat.localGetStaticConfigRpcPromise() - if (!res.deletableByDeleteHistory) { - logger.error('chat.loadStaticConfig: got no deletableByDeleteHistory in static config') - return - } - const deletableByDeleteHistory = res.deletableByDeleteHistory.reduce>( - (res, type) => { - const ourTypes = Message.serviceMessageTypeToMessageTypes(type) - res.push(...ourTypes) - return res - }, - [] - ) - set(s => { - s.staticConfig = { - builtinCommands: (res.builtinCommands || []).reduce( - (map, c) => { - map[c.typ] = T.castDraft(c.commands) || [] - return map - }, - { - [T.RPCChat.ConversationBuiltinCommandTyp.none]: [], - [T.RPCChat.ConversationBuiltinCommandTyp.adhoc]: [], - [T.RPCChat.ConversationBuiltinCommandTyp.smallteam]: [], - [T.RPCChat.ConversationBuiltinCommandTyp.bigteam]: [], - [T.RPCChat.ConversationBuiltinCommandTyp.bigteamgeneral]: [], - } - ), - deletableByDeleteHistory: new Set(deletableByDeleteHistory), - } - }) - } finally { - dispatch.wait(name, handshakeVersion, false) - } - } - ignorePromise(f()) - }, - loadedUserEmoji: results => { - set(s => { - const newEmojis: Array = [] - results.emojis.emojis?.forEach(group => { - group.emojis?.forEach(e => newEmojis.push(e)) - }) - s.userEmojisForAutocomplete = newEmojis - s.userEmojis = T.castDraft(results.emojis.emojis) ?? [] - }) - }, - maybeChangeSelectedConv: () => { - const {inboxLayout} = get() - const newConvID = inboxLayout?.reselectInfo?.newConvID - const oldConvID = inboxLayout?.reselectInfo?.oldConvID - - const selectedConversation = Common.getSelectedConversation() - - if (!newConvID && !oldConvID) { - return - } - - const existingValid = T.Chat.isValidConversationIDKey(selectedConversation) - // no new id, just take the opportunity to resolve - if (!newConvID) { - if (!existingValid && isPhone) { - logger.info(`maybeChangeSelectedConv: no new and no valid, so go to inbox`) - get().dispatch.navigateToInbox(false) - } - return - } - // not matching? - if (selectedConversation !== oldConvID) { - if (!existingValid && isPhone) { - logger.info(`maybeChangeSelectedConv: no new and no valid, so go to inbox`) - get().dispatch.navigateToInbox(false) - } - return - } - // matching - if (isPhone) { - // on mobile just head back to the inbox if we have something selected - if (T.Chat.isValidConversationIDKey(selectedConversation)) { - logger.info(`maybeChangeSelectedConv: mobile: navigating up on conv change`) - get().dispatch.navigateToInbox(false) - return - } - logger.info(`maybeChangeSelectedConv: mobile: ignoring conv change, no conv selected`) - return - } else { - logger.info( - `maybeChangeSelectedConv: selecting new conv: new:${newConvID} old:${oldConvID} prevselected ${selectedConversation}` - ) - storeRegistry.getConvoState(newConvID).dispatch.navigateToThread('findNewestConversation') - } - }, - messageSendByUsername: (username, text, waitingKey) => { - const f = async () => { - const tlfName = `${storeRegistry.getState('current-user').username},${username}` - try { - const result = await T.RPCChat.localNewConversationLocalRpcPromise( - { - identifyBehavior: T.RPCGen.TLFIdentifyBehavior.chatGui, - membersType: T.RPCChat.ConversationMembersType.impteamnative, - tlfName, - tlfVisibility: T.RPCGen.TLFVisibility.private, - topicType: T.RPCChat.TopicType.chat, - }, - waitingKey - ) - storeRegistry - .getConvoState(T.Chat.conversationIDToKey(result.conv.info.id)) - .dispatch.sendMessage(text) - } catch (error) { - if (error instanceof RPCError) { - logger.warn('Could not send in messageSendByUsernames', error.message) - } - } - } - ignorePromise(f()) - }, - metasReceived: (metas, removals) => { - removals?.forEach(r => { - storeRegistry.getConvoState(r).dispatch.setMeta() - }) - metas.forEach(m => { - const {meta: oldMeta, dispatch, isMetaGood} = storeRegistry.getConvoState(m.conversationIDKey) - if (isMetaGood()) { - dispatch.updateMeta(Meta.updateMeta(oldMeta, m)) - } else { - dispatch.setMeta(m) - } - }) - - const selectedConversation = Common.getSelectedConversation() - const {isMetaGood, meta} = storeRegistry.getConvoState(selectedConversation) - if (isMetaGood()) { - const {teamID} = meta - if (!storeRegistry.getState('teams').teamIDToMembers.get(teamID) && meta.teamname) { - storeRegistry.getState('teams').dispatch.getMembers(teamID) - } - } - }, - navigateToInbox: (allowSwitchTab = true) => { - // components can call us during render sometimes so always defer - setTimeout(() => { - navUpToScreen('chatRoot') - if (allowSwitchTab) { - switchTab(Tabs.chatTab) - } - }, 1) - }, - onChatInboxSynced: action => { - const {syncRes} = action.payload.params - const {clear} = storeRegistry.getState('waiting').dispatch - const {inboxRefresh} = get().dispatch - clear(S.waitingKeyChatInboxSyncStarted) - - switch (syncRes.syncType) { - // Just clear it all - case T.RPCChat.SyncInboxResType.clear: - inboxRefresh('inboxSyncedClear') - break - // We're up to date - case T.RPCChat.SyncInboxResType.current: - break - // We got some new messages appended - case T.RPCChat.SyncInboxResType.incremental: { - const items = syncRes.incremental.items || [] - const selectedConversation = Common.getSelectedConversation() - let loadMore = false as boolean - const metas = items.reduce>((arr, i) => { - const meta = Meta.unverifiedInboxUIItemToConversationMeta(i.conv) - if (meta) { - arr.push(meta) - if (meta.conversationIDKey === selectedConversation) { - loadMore = true - } - } - return arr - }, []) - if (loadMore) { - storeRegistry.getConvoState(selectedConversation).dispatch.loadMoreMessages({reason: 'got stale'}) - } - const removals = syncRes.incremental.removals?.map(T.Chat.stringToConversationIDKey) - // Update new untrusted - if (metas.length || removals?.length) { - get().dispatch.metasReceived(metas, removals) - } - - get().dispatch.unboxRows( - items.filter(i => i.shouldUnbox).map(i => T.Chat.stringToConversationIDKey(i.conv.convID)), - true - ) - break - } - default: - inboxRefresh('inboxSyncedUnknown') - } - }, - onChatThreadStale: (action: EngineGen.Chat1NotifyChatChatThreadsStalePayload) => { - const {updates} = action.payload.params - const keys = ['clear', 'newactivity'] as const - if (__DEV__) { - if (keys.length * 2 !== Object.keys(T.RPCChat.StaleUpdateType).length) { - throw new Error('onChatThreadStale invalid enum') - } - } - let loadMore = false as boolean - const selectedConversation = Common.getSelectedConversation() - keys.forEach(key => { - const conversationIDKeys = (updates || []).reduce>((arr, u) => { - const cid = T.Chat.conversationIDToKey(u.convID) - if (u.updateType === T.RPCChat.StaleUpdateType[key]) { - arr.push(cid) - } - // mentioned? - if (cid === selectedConversation) { - loadMore = true - } - return arr - }, []) - // load the inbox instead - if (conversationIDKeys.length > 0) { - logger.info( - `onChatThreadStale: dispatching thread reload actions for ${conversationIDKeys.length} convs of type ${key}` - ) - get().dispatch.unboxRows(conversationIDKeys, true) - if (T.RPCChat.StaleUpdateType[key] === T.RPCChat.StaleUpdateType.clear) { - conversationIDKeys.forEach(convID => storeRegistry.getConvoState(convID).dispatch.messagesClear()) - } - } - }) - if (loadMore) { - storeRegistry.getConvoState(selectedConversation).dispatch.loadMoreMessages({reason: 'got stale'}) - } - }, - onEngineIncomingImpl: action => { - switch (action.type) { - case EngineGen.chat1ChatUiChatInboxFailed: // fallthrough - case EngineGen.chat1NotifyChatChatSetConvSettings: // fallthrough - case EngineGen.chat1NotifyChatChatAttachmentUploadStart: // fallthrough - case EngineGen.chat1NotifyChatChatPromptUnfurl: // fallthrough - case EngineGen.chat1NotifyChatChatPaymentInfo: // fallthrough - case EngineGen.chat1NotifyChatChatRequestInfo: // fallthrough - case EngineGen.chat1NotifyChatChatAttachmentDownloadProgress: //fallthrough - case EngineGen.chat1NotifyChatChatAttachmentDownloadComplete: //fallthrough - case EngineGen.chat1NotifyChatChatAttachmentUploadProgress: { - const {convID} = action.payload.params - const conversationIDKey = T.Chat.conversationIDToKey(convID) - storeRegistry.getConvoState(conversationIDKey).dispatch.onEngineIncoming(action) - break - } - case EngineGen.chat1ChatUiChatCommandMarkdown: //fallthrough - case EngineGen.chat1ChatUiChatGiphyToggleResultWindow: // fallthrough - case EngineGen.chat1ChatUiChatCommandStatus: // fallthrough - case EngineGen.chat1ChatUiChatBotCommandsUpdateStatus: //fallthrough - case EngineGen.chat1ChatUiChatGiphySearchResults: { - const {convID} = action.payload.params - const conversationIDKey = T.Chat.stringToConversationIDKey(convID) - storeRegistry.getConvoState(conversationIDKey).dispatch.onEngineIncoming(action) - break - } - case EngineGen.chat1NotifyChatChatParticipantsInfo: { - const {participants: participantMap} = action.payload.params - Object.keys(participantMap ?? {}).forEach(convIDStr => { - const participants = participantMap?.[convIDStr] - const conversationIDKey = T.Chat.stringToConversationIDKey(convIDStr) - if (participants) { - storeRegistry - .getConvoState(conversationIDKey) - .dispatch.setParticipants(uiParticipantsToParticipantInfo(participants)) - } - }) - break - } - case EngineGen.chat1ChatUiChatMaybeMentionUpdate: { - const {teamName, channel, info} = action.payload.params - get().dispatch.setMaybeMentionInfo(getTeamMentionName(teamName, channel), info) - break - } - case EngineGen.chat1NotifyChatChatConvUpdate: { - const {conv} = action.payload.params - if (conv) { - const meta = Meta.inboxUIItemToConversationMeta(conv) - meta && get().dispatch.metasReceived([meta]) - } - break - } - case EngineGen.chat1ChatUiChatCoinFlipStatus: { - const {statuses} = action.payload.params - get().dispatch.updateCoinFlipStatus(statuses || []) - break - } - case EngineGen.chat1NotifyChatChatThreadsStale: - get().dispatch.onChatThreadStale(action) - break - case EngineGen.chat1NotifyChatChatSubteamRename: { - const {convs} = action.payload.params - const conversationIDKeys = (convs ?? []).map(c => T.Chat.stringToConversationIDKey(c.convID)) - get().dispatch.unboxRows(conversationIDKeys, true) - break - } - case EngineGen.chat1NotifyChatChatTLFFinalize: - get().dispatch.unboxRows([T.Chat.conversationIDToKey(action.payload.params.convID)]) - break - case EngineGen.chat1NotifyChatChatIdentifyUpdate: { - // Some participants are broken/fixed now - const {update} = action.payload.params - const usernames = update.CanonicalName.split(',') - const broken = (update.breaks.breaks || []).map(b => b.user.username) - const updates = usernames.map(name => ({info: {broken: broken.includes(name)}, name})) - storeRegistry.getState('users').dispatch.updates(updates) - break - } - case EngineGen.chat1ChatUiChatInboxUnverified: - get().dispatch.onGetInboxUnverifiedConvs(action) - break - case EngineGen.chat1NotifyChatChatInboxSyncStarted: - storeRegistry.getState('waiting').dispatch.increment(S.waitingKeyChatInboxSyncStarted) - break - - case EngineGen.chat1NotifyChatChatInboxSynced: - get().dispatch.onChatInboxSynced(action) - break - case EngineGen.chat1ChatUiChatInboxLayout: - get().dispatch.updateInboxLayout(action.payload.params.layout) - get().dispatch.maybeChangeSelectedConv() - get().dispatch.ensureWidgetMetas() - break - case EngineGen.chat1NotifyChatChatInboxStale: - get().dispatch.inboxRefresh('inboxStale') - break - case EngineGen.chat1ChatUiChatInboxConversation: - get().dispatch.onGetInboxConvsUnboxed(action) - break - case EngineGen.chat1NotifyChatNewChatActivity: { - const {activity} = action.payload.params - switch (activity.activityType) { - case T.RPCChat.ChatActivityType.incomingMessage: { - const {incomingMessage} = activity - const conversationIDKey = T.Chat.conversationIDToKey(incomingMessage.convID) - storeRegistry.getConvoState(conversationIDKey).dispatch.onIncomingMessage(incomingMessage) - get().dispatch.onIncomingInboxUIItem(incomingMessage.conv ?? undefined) - break - } - case T.RPCChat.ChatActivityType.setStatus: - get().dispatch.onIncomingInboxUIItem(activity.setStatus.conv ?? undefined) - break - case T.RPCChat.ChatActivityType.readMessage: - get().dispatch.onIncomingInboxUIItem(activity.readMessage.conv ?? undefined) - break - case T.RPCChat.ChatActivityType.newConversation: - get().dispatch.onIncomingInboxUIItem(activity.newConversation.conv ?? undefined) - break - case T.RPCChat.ChatActivityType.failedMessage: { - const {failedMessage} = activity - get().dispatch.onIncomingInboxUIItem(failedMessage.conv ?? undefined) - const {outboxRecords} = failedMessage - if (!outboxRecords) return - for (const outboxRecord of outboxRecords) { - const s = outboxRecord.state - if (s.state !== T.RPCChat.OutboxStateType.error) return - const {error} = s - const conversationIDKey = T.Chat.conversationIDToKey(outboxRecord.convID) - const outboxID = T.Chat.rpcOutboxIDToOutboxID(outboxRecord.outboxID) - // This is temp until fixed by CORE-7112. We get this error but not the call to let us show the red banner - const reason = Message.rpcErrorToString(error) - storeRegistry - .getConvoState(conversationIDKey) - .dispatch.onMessageErrored(outboxID, reason, error.typ) - - if (error.typ === T.RPCChat.OutboxErrorType.identify) { - // Find out the user who failed identify - const match = error.message.match(/"(.*)"/) - const tempForceRedBox = match?.[1] - if (tempForceRedBox) { - storeRegistry - .getState('users') - .dispatch.updates([{info: {broken: true}, name: tempForceRedBox}]) - } - } - } - break - } - case T.RPCChat.ChatActivityType.membersUpdate: - get().dispatch.unboxRows([T.Chat.conversationIDToKey(activity.membersUpdate.convID)], true) - break - case T.RPCChat.ChatActivityType.setAppNotificationSettings: { - const {setAppNotificationSettings} = activity - const conversationIDKey = T.Chat.conversationIDToKey(setAppNotificationSettings.convID) - const settings = setAppNotificationSettings.settings - const cs = storeRegistry.getConvoState(conversationIDKey) - if (cs.isMetaGood()) { - cs.dispatch.updateMeta(Meta.parseNotificationSettings(settings)) - } - break - } - case T.RPCChat.ChatActivityType.expunge: { - // Get actions to update messagemap / metamap when retention policy expunge happens - const {expunge} = activity - const conversationIDKey = T.Chat.conversationIDToKey(expunge.convID) - const staticConfig = get().staticConfig - // The types here are askew. It confuses frontend MessageType with protocol MessageType. - // Placeholder is an example where it doesn't make sense. - const deletableMessageTypes = staticConfig?.deletableByDeleteHistory || Common.allMessageTypes - storeRegistry.getConvoState(conversationIDKey).dispatch.messagesWereDeleted({ - deletableMessageTypes, - upToMessageID: T.Chat.numberToMessageID(expunge.expunge.upto), - }) - break - } - case T.RPCChat.ChatActivityType.ephemeralPurge: { - const {ephemeralPurge} = activity - // Get actions to update messagemap / metamap when ephemeral messages expire - const conversationIDKey = T.Chat.conversationIDToKey(ephemeralPurge.convID) - const messageIDs = ephemeralPurge.msgs?.reduce>((arr, msg) => { - const msgID = Message.getMessageID(msg) - if (msgID) { - arr.push(msgID) - } - return arr - }, []) - - !!messageIDs && - storeRegistry.getConvoState(conversationIDKey).dispatch.messagesExploded(messageIDs) - break - } - case T.RPCChat.ChatActivityType.reactionUpdate: { - // Get actions to update the messagemap when reactions are updated - const {reactionUpdate} = activity - const conversationIDKey = T.Chat.conversationIDToKey(reactionUpdate.convID) - if (!reactionUpdate.reactionUpdates || reactionUpdate.reactionUpdates.length === 0) { - logger.warn(`Got ReactionUpdateNotif with no reactionUpdates for convID=${conversationIDKey}`) - break - } - const updates = reactionUpdate.reactionUpdates.map(ru => ({ - reactions: Message.reactionMapToReactions(ru.reactions), - targetMsgID: T.Chat.numberToMessageID(ru.targetMsgID), - })) - logger.info(`Got ${updates.length} reaction updates for convID=${conversationIDKey}`) - storeRegistry.getConvoState(conversationIDKey).dispatch.updateReactions(updates) - get().dispatch.updateUserReacjis(reactionUpdate.userReacjis) - break - } - case T.RPCChat.ChatActivityType.messagesUpdated: { - const {messagesUpdated} = activity - const conversationIDKey = T.Chat.conversationIDToKey(messagesUpdated.convID) - storeRegistry.getConvoState(conversationIDKey).dispatch.onMessagesUpdated(messagesUpdated) - break - } - default: - } - break - } - case EngineGen.chat1NotifyChatChatTypingUpdate: { - const {typingUpdates} = action.payload.params - typingUpdates?.forEach(u => { - storeRegistry - .getConvoState(T.Chat.conversationIDToKey(u.convID)) - .dispatch.setTyping(new Set(u.typers?.map(t => t.username))) - }) - break - } - case EngineGen.chat1NotifyChatChatSetConvRetention: { - const {conv, convID} = action.payload.params - if (!conv) { - logger.warn('onChatSetConvRetention: no conv given') - return - } - const meta = Meta.inboxUIItemToConversationMeta(conv) - if (!meta) { - logger.warn(`onChatSetConvRetention: no meta found for ${convID.toString()}`) - return - } - const cs = storeRegistry.getConvoState(meta.conversationIDKey) - // only insert if the convo is already in the inbox - if (cs.isMetaGood()) { - cs.dispatch.setMeta(meta) - } - break - } - case EngineGen.chat1NotifyChatChatSetTeamRetention: { - const {convs} = action.payload.params - const metas = (convs ?? []).reduce>((l, c) => { - const meta = Meta.inboxUIItemToConversationMeta(c) - if (meta) { - l.push(meta) - } - return l - }, []) - if (metas.length) { - metas.forEach(meta => { - const cs = storeRegistry.getConvoState(meta.conversationIDKey) - // only insert if the convo is already in the inbox - if (cs.isMetaGood()) { - cs.dispatch.setMeta(meta) - } - }) - storeRegistry.getState('teams').dispatch.updateTeamRetentionPolicy(metas) - } - // this is a more serious problem, but we don't need to bug the user about it - logger.error( - 'got NotifyChat.ChatSetTeamRetention with no attached InboxUIItems. The local version may be out of date' - ) - break - } - case EngineGen.keybase1NotifyBadgesBadgeState: { - const {badgeState} = action.payload.params - get().dispatch.badgesUpdated(badgeState) - break - } - case EngineGen.keybase1GregorUIPushState: { - const {state} = action.payload.params - const items = state.items || [] - const goodState = items.reduce>( - (arr, {md, item}) => { - md && item && arr.push({item, md}) - return arr - }, - [] - ) - if (goodState.length !== items.length) { - logger.warn('Lost some messages in filtering out nonNull gregor items') - } - get().dispatch.updatedGregor(goodState) - break - } - default: - } - }, - onGetInboxConvsUnboxed: (action: EngineGen.Chat1ChatUiChatInboxConversationPayload) => { - // TODO not reactive - const {infoMap} = storeRegistry.getState('users') - const {convs} = action.payload.params - const inboxUIItems = JSON.parse(convs) as Array - const metas: Array = [] - let added = false as boolean - const usernameToFullname: {[username: string]: string} = {} - inboxUIItems.forEach(inboxUIItem => { - const meta = Meta.inboxUIItemToConversationMeta(inboxUIItem) - if (meta) { - metas.push(meta) - } - const participantInfo: T.Chat.ParticipantInfo = uiParticipantsToParticipantInfo( - inboxUIItem.participants ?? [] - ) - if (participantInfo.all.length > 0) { - storeRegistry - .getConvoState(T.Chat.stringToConversationIDKey(inboxUIItem.convID)) - .dispatch.setParticipants(participantInfo) - } - inboxUIItem.participants?.forEach((part: T.RPCChat.UIParticipant) => { - const {assertion, fullName} = part - if (!infoMap.get(assertion) && fullName) { - added = true - usernameToFullname[assertion] = fullName - } - }) - }) - if (added) { - storeRegistry.getState('users').dispatch.updates( - Object.keys(usernameToFullname).map(name => ({ - info: {fullname: usernameToFullname[name]}, - name, - })) - ) - } - if (metas.length > 0) { - get().dispatch.metasReceived(metas) - } - }, - onGetInboxUnverifiedConvs: (action: EngineGen.Chat1ChatUiChatInboxUnverifiedPayload) => { - const {inbox} = action.payload.params - const result = JSON.parse(inbox) as T.RPCChat.UnverifiedInboxUIItems - const items: ReadonlyArray = result.items ?? [] - // We get a subset of meta information from the cache even in the untrusted payload - const metas = items.reduce>((arr, item) => { - const m = Meta.unverifiedInboxUIItemToConversationMeta(item) - m && arr.push(m) - return arr - }, []) - get().dispatch.setTrustedInboxHasLoaded() - // Check if some of our existing stored metas might no longer be valid - get().dispatch.metasReceived(metas) - }, - onIncomingInboxUIItem: conv => { - if (!conv) return - const meta = Meta.inboxUIItemToConversationMeta(conv) - const usernameToFullname = (conv.participants ?? []).reduce<{[key: string]: string}>((map, part) => { - if (part.fullName) { - map[part.assertion] = part.fullName - } - return map - }, {}) - - storeRegistry.getState('users').dispatch.updates( - Object.keys(usernameToFullname).map(name => ({ - info: {fullname: usernameToFullname[name]}, - name, - })) - ) - - if (meta) { - get().dispatch.metasReceived([meta]) - } - }, - onRouteChanged: (prev, next) => { - const maybeChangeChatSelection = () => { - const wasModal = prev && Router2.getModalStack(prev).length > 0 - const isModal = next && Router2.getModalStack(next).length > 0 - // ignore if changes involve a modal - if (wasModal || isModal) { - return - } - const p = Router2.getVisibleScreen(prev) - const n = Router2.getVisibleScreen(next) - const wasChat = p?.name === Common.threadRouteName - const isChat = n?.name === Common.threadRouteName - // nothing to do with chat - if (!wasChat && !isChat) { - return - } - const pParams = p?.params as undefined | {conversationIDKey?: T.Chat.ConversationIDKey} - const nParams = n?.params as undefined | {conversationIDKey?: T.Chat.ConversationIDKey} - const wasID = pParams?.conversationIDKey - const isID = nParams?.conversationIDKey - - logger.info('maybeChangeChatSelection ', {isChat, isID, wasChat, wasID}) - - // same? ignore - if (wasChat && isChat && wasID === isID) { - // if we've never loaded anything, keep going so we load it - if (!isID || storeRegistry.getConvoState(isID).loaded) { - return - } - } - - // deselect if there was one - const deselectAction = () => { - if (wasChat && wasID && T.Chat.isValidConversationIDKey(wasID)) { - get().dispatch.unboxRows([wasID], true) - // needed? - // storeRegistry.getConvoState(wasID).dispatch.clearOrangeLine('deselected') - } - } - - // still chatting? just select new one - if (wasChat && isChat && isID && T.Chat.isValidConversationIDKey(isID)) { - deselectAction() - storeRegistry.getConvoState(isID).dispatch.selectedConversation() - return - } - - // leaving a chat - if (wasChat && !isChat) { - deselectAction() - return - } - - // going into a chat - if (isChat && isID && T.Chat.isValidConversationIDKey(isID)) { - deselectAction() - storeRegistry.getConvoState(isID).dispatch.selectedConversation() - return - } - } - - const maybeChatTabSelected = () => { - if (Router2.getTab(prev) !== Tabs.chatTab && Router2.getTab(next) === Tabs.chatTab) { - const n = Router2.getVisibleScreen(next) - const nParams = n?.params as undefined | {conversationIDKey?: T.Chat.ConversationIDKey} - const isID = nParams?.conversationIDKey - isID && storeRegistry.getConvoState(isID).dispatch.tabSelected() - } - } - maybeChangeChatSelection() - maybeChatTabSelected() - }, - onTeamBuildingFinished: users => { - const f = async () => { - // need to let the mdoal hide first else its thrashy - await timeoutPromise(500) - storeRegistry - .getConvoState(T.Chat.pendingWaitingConversationIDKey) - .dispatch.navigateToThread('justCreated') - get().dispatch.createConversation([...users].map(u => u.id)) - } - ignorePromise(f()) - }, - paymentInfoReceived: paymentInfo => { - set(s => { - s.paymentStatusMap.set(paymentInfo.paymentID, paymentInfo) - }) - }, - previewConversation: p => { - // We always make adhoc convos and never preview it - const previewConversationPersonMakesAConversation = () => { - const {participants, teamname, highlightMessageID} = p - if (teamname) return - if (!participants) return - const toFind = [...participants].sort().join(',') - const toFindN = participants.length - for (const cs of chatStores.values()) { - const names = cs.getState().participants.name - if (names.length !== toFindN) continue - const p = [...names].sort().join(',') - if (p === toFind) { - storeRegistry - .getConvoState(cs.getState().id) - .dispatch.navigateToThread('justCreated', highlightMessageID) - return - } - } - - storeRegistry - .getConvoState(T.Chat.pendingWaitingConversationIDKey) - .dispatch.navigateToThread('justCreated') - get().dispatch.createConversation(participants, highlightMessageID) - } - - // We preview channels - const previewConversationTeam = async () => { - const {conversationIDKey, highlightMessageID, teamname, reason} = p - if (conversationIDKey) { - if ( - reason === 'messageLink' || - reason === 'teamMention' || - reason === 'channelHeader' || - reason === 'manageView' - ) { - // Add preview channel to inbox - await T.RPCChat.localPreviewConversationByIDLocalRpcPromise({ - convID: T.Chat.keyToConversationID(conversationIDKey), - }) - } - - storeRegistry - .getConvoState(conversationIDKey) - .dispatch.navigateToThread('previewResolved', highlightMessageID) - return - } - - if (!teamname) { - return - } - - const channelname = p.channelname || 'general' - try { - const results = await T.RPCChat.localFindConversationsLocalRpcPromise({ - identifyBehavior: T.RPCGen.TLFIdentifyBehavior.chatGui, - membersType: T.RPCChat.ConversationMembersType.team, - oneChatPerTLF: true, - tlfName: teamname, - topicName: channelname, - topicType: T.RPCChat.TopicType.chat, - visibility: T.RPCGen.TLFVisibility.private, - }) - const resultMetas = (results.uiConversations || []) - .map(row => Meta.inboxUIItemToConversationMeta(row)) - .filter(Boolean) - - const first = resultMetas[0] - if (!first) { - if (p.reason === 'appLink') { - storeRegistry - .getState('deeplinks') - .dispatch.setLinkError( - "We couldn't find this team chat channel. Please check that you're a member of the team and the channel exists." - ) - navigateAppend('keybaseLinkError') - return - } else { - return - } - } - - const results2 = await T.RPCChat.localPreviewConversationByIDLocalRpcPromise({ - convID: T.Chat.keyToConversationID(first.conversationIDKey), - }) - const meta = Meta.inboxUIItemToConversationMeta(results2.conv) - if (meta) { - storeRegistry.getState('chat').dispatch.metasReceived([meta]) - } - - storeRegistry - .getConvoState(first.conversationIDKey) - .dispatch.navigateToThread('previewResolved', highlightMessageID) - } catch (error) { - if ( - error instanceof RPCError && - error.code === T.RPCGen.StatusCode.scteamnotfound && - reason === 'appLink' - ) { - storeRegistry - .getState('deeplinks') - .dispatch.setLinkError( - "We couldn't find this team. Please check that you're a member of the team and the channel exists." - ) - navigateAppend('keybaseLinkError') - return - } else { - throw error - } - } - } - previewConversationPersonMakesAConversation() - ignorePromise(previewConversationTeam()) - }, - queueMetaHandle: () => { - // Watch the meta queue and take up to 10 items. Choose the last items first since they're likely still visible - const f = async () => { - const maxToUnboxAtATime = 10 - const ar = [...metaQueue] - const maybeUnbox = ar.slice(0, maxToUnboxAtATime) - metaQueue = new Set(ar.slice(maxToUnboxAtATime)) - const conversationIDKeys = untrustedConversationIDKeys(maybeUnbox) - if (conversationIDKeys.length) { - get().dispatch.unboxRows(conversationIDKeys) - } - if (metaQueue.size && conversationIDKeys.length) { - await timeoutPromise(100) - } - if (metaQueue.size) { - get().dispatch.queueMetaHandle() - } - } - ignorePromise(f()) - }, - queueMetaToRequest: ids => { - let added = false as boolean - untrustedConversationIDKeys(ids).forEach(k => { - if (!metaQueue.has(k)) { - added = true - metaQueue.add(k) - } - }) - if (added) { - // only unboxMore if something changed - get().dispatch.queueMetaHandle() - } else { - logger.info('skipping meta queue run, queue unchanged') - } - }, - refreshBotPublicCommands: username => { - set(s => { - s.botPublicCommands.delete(username) - }) - const f = async () => { - let res: T.RPCChat.ListBotCommandsLocalRes | undefined - try { - res = await T.RPCChat.localListPublicBotCommandsLocalRpcPromise({ - username, - }) - } catch (error) { - if (error instanceof RPCError) { - logger.info('refreshBotPublicCommands: failed to get public commands: ' + error.message) - set(s => { - s.botPublicCommands.set(username, {commands: [], loadError: true}) - }) - } - } - const commands = (res?.commands ?? []).reduce>((l, c) => { - l.push(c.name) - return l - }, []) - - set(s => { - s.botPublicCommands.set(username, {commands, loadError: false}) - }) - } - ignorePromise(f()) - }, - resetConversationErrored: () => { - set(s => { - s.createConversationError = undefined - }) - }, - resetState: () => { - set(s => ({ - ...s, - ...initialStore, - dispatch: s.dispatch, - staticConfig: s.staticConfig, - })) - // also blow away convoState - clearChatStores() - }, - setInboxNumSmallRows: (rows, ignoreWrite) => { - set(s => { - if (rows > 0) { - s.inboxNumSmallRows = rows - } - }) - if (ignoreWrite) { - return - } - const {inboxNumSmallRows} = get() - if (inboxNumSmallRows === undefined || inboxNumSmallRows <= 0) { - return - } - const f = async () => { - try { - await T.RPCGen.configGuiSetValueRpcPromise({ - path: 'ui.inboxSmallRows', - value: {i: inboxNumSmallRows, isNull: false}, - }) - } catch {} - } - ignorePromise(f()) - }, - setInfoPanelTab: tab => { - set(s => { - s.infoPanelSelectedTab = tab - }) - }, - setMaybeMentionInfo: (name, info) => { - set(s => { - const {maybeMentionMap} = s - maybeMentionMap.set(name, T.castDraft(info)) - }) - }, - setTrustedInboxHasLoaded: () => { - set(s => { - s.trustedInboxHasLoaded = true - }) - }, - toggleInboxSearch: enabled => { - set(s => { - const {inboxSearch} = s - if (enabled && !inboxSearch) { - s.inboxSearch = T.castDraft(makeInboxSearchInfo()) - } else if (!enabled && inboxSearch) { - s.inboxSearch = undefined - } - }) - const f = async () => { - const {inboxSearch} = get() - if (!inboxSearch) { - await T.RPCChat.localCancelActiveInboxSearchRpcPromise() - return - } - if (inboxSearch.nameStatus === 'initial') { - get().dispatch.inboxSearch('') - } - } - ignorePromise(f()) - }, - toggleSmallTeamsExpanded: () => { - set(s => { - s.smallTeamsExpanded = !s.smallTeamsExpanded - }) - }, - unboxRows: (ids, force) => { - // We want to unbox rows that have scroll into view - const f = async () => { - if (!storeRegistry.getState('config').loggedIn) { - return - } - - // Get valid keys that we aren't already loading or have loaded - const conversationIDKeys = ids.reduce((arr: Array, id) => { - if (id && T.Chat.isValidConversationIDKey(id)) { - const cs = storeRegistry.getConvoState(id) - const trustedState = cs.meta.trustedState - if (force || (trustedState !== 'requesting' && trustedState !== 'trusted')) { - arr.push(id) - cs.dispatch.updateMeta({trustedState: 'requesting'}) - } - } - return arr - }, []) - - if (!conversationIDKeys.length) { - return - } - logger.info( - `unboxRows: unboxing len: ${conversationIDKeys.length} convs: ${conversationIDKeys.join(',')}` - ) - try { - await T.RPCChat.localRequestInboxUnboxRpcPromise({ - convIDs: conversationIDKeys.map(k => T.Chat.keyToConversationID(k)), - }) - } catch (error) { - if (error instanceof RPCError) { - logger.info(`unboxRows: failed ${error.desc}`) - } - } - } - ignorePromise(f()) - }, - updateCoinFlipStatus: statuses => { - set(s => { - const {flipStatusMap} = s - statuses.forEach(status => { - flipStatusMap.set(status.gameID, T.castDraft(status)) - }) - }) - }, - updateInboxLayout: str => { - set(s => { - try { - const {inboxHasLoaded} = s - const _layout = JSON.parse(str) as unknown - if (!_layout || typeof _layout !== 'object') { - console.log('Invalid layout?') - return - } - const layout = _layout as T.RPCChat.UIInboxLayout - - if (!isEqual(s.inboxLayout, layout)) { - s.inboxLayout = T.castDraft(layout) - } - s.inboxHasLoaded = !!layout - if (!inboxHasLoaded) { - // on first layout, initialize any drafts and muted status - // After the first layout, any other updates will come in the form of meta updates. - layout.smallTeams?.forEach(t => { - const cs = storeRegistry.getConvoState(t.convID) - cs.dispatch.updateFromUIInboxLayout(t) - }) - layout.bigTeams?.forEach(t => { - if (t.state === T.RPCChat.UIInboxBigTeamRowTyp.channel) { - const cs = storeRegistry.getConvoState(t.channel.convID) - cs.dispatch.updateFromUIInboxLayout(t.channel) - } - }) - } - } catch (e) { - logger.info('failed to JSON parse inbox layout: ' + e) - } - }) - }, - updateInfoPanel: (show, tab) => { - set(s => { - s.infoPanelShowing = show - s.infoPanelSelectedTab = tab - }) - }, - updateLastCoord: coord => { - set(s => { - s.lastCoord = coord - }) - const f = async () => { - const {accuracy, lat, lon} = coord - await T.RPCChat.localLocationUpdateRpcPromise({coord: {accuracy, lat, lon}}) - } - ignorePromise(f()) - }, - updateUserReacjis: userReacjis => { - set(s => { - const {skinTone, topReacjis} = userReacjis - s.userReacjis.skinTone = skinTone - // filter out non-simple emojis - s.userReacjis.topReacjis = - T.castDraft(topReacjis)?.filter(r => /^:[^:]+:$/.test(r.name)) ?? defaultTopReacjis - }) - }, - updatedGregor: items => { - const explodingItems = items.filter(i => - i.item.category.startsWith(Common.explodingModeGregorKeyPrefix) - ) - if (!explodingItems.length) { - // No conversations have exploding modes, clear out what is set - for (const s of chatStores.values()) { - s.getState().dispatch.setExplodingMode(0, true) - } - } else { - // logger.info('Got push state with some exploding modes') - explodingItems.forEach(i => { - try { - const {category, body} = i.item - const secondsString = uint8ArrayToString(body) - const seconds = parseInt(secondsString, 10) - if (isNaN(seconds)) { - logger.warn(`Got dirty exploding mode ${secondsString} for category ${category}`) - return - } - const _conversationIDKey = category.substring(Common.explodingModeGregorKeyPrefix.length) - const conversationIDKey = T.Chat.stringToConversationIDKey(_conversationIDKey) - storeRegistry.getConvoState(conversationIDKey).dispatch.setExplodingMode(seconds, true) - } catch (e) { - logger.info('Error parsing exploding' + e) - } - }) - } - - set(s => { - const blockButtons = items.some(i => i.item.category.startsWith(blockButtonsGregorPrefix)) - if (blockButtons || s.blockButtonsMap.size > 0) { - const shouldKeepExistingBlockButtons = new Map() - s.blockButtonsMap.forEach((_, teamID: string) => shouldKeepExistingBlockButtons.set(teamID, false)) - items - .filter(i => i.item.category.startsWith(blockButtonsGregorPrefix)) - .forEach(i => { - try { - const teamID = i.item.category.substring(blockButtonsGregorPrefix.length) - if (!s.blockButtonsMap.get(teamID)) { - const body = bodyToJSON(i.item.body) as {adder: string} - const adder = body.adder - s.blockButtonsMap.set(teamID, {adder}) - } else { - shouldKeepExistingBlockButtons.set(teamID, true) - } - } catch (e) { - logger.info('block buttons parse fail', e) - } - }) - shouldKeepExistingBlockButtons.forEach((keep, teamID) => { - if (!keep) { - s.blockButtonsMap.delete(teamID) - } - }) - } - }) - }, - } - return { - ...initialStore, - dispatch, - getBackCount: conversationIDKey => { - let count = 0 - chatStores.forEach(s => { - const {id, badge} = s.getState() - // only show sum of badges that aren't for the current conversation - if (id !== conversationIDKey) { - count += badge - } - }) - return count - }, - getBadgeHiddenCount: ids => { - let badgeCount = 0 - let hiddenCount = 0 - - chatStores.forEach(s => { - const {id, badge} = s.getState() - if (ids.has(id)) { - badgeCount -= badge - hiddenCount -= 1 - } - }) - - return {badgeCount, hiddenCount} - }, - getUnreadIndicies: ids => { - const unreadIndices: Map = new Map() - ids.forEach((cur, idx) => { - Array.from(chatStores.values()).some(s => { - const {id, badge} = s.getState() - if (id === cur && badge > 0) { - unreadIndices.set(idx, badge) - return true - } - return false - }) - }) - return unreadIndices - }, - } -}) - -import {type ChatProviderProps, ProviderScreen} from './convostate' -import type {GetOptionsRet} from '@/constants/types/router2' - -export function makeChatScreen>( - Component: COM, - options?: { - getOptions?: GetOptionsRet | ((props: ChatProviderProps>) => GetOptionsRet) - skipProvider?: boolean - canBeNullConvoID?: boolean - } -) { - return { - ...options, - screen: function Screen(p: ChatProviderProps>) { - const Comp = Component as any - return options?.skipProvider ? ( - - ) : ( - - - - ) - }, - } -} - -export * from './convostate' -export * from './common' -export * from './meta' -export * from './message' - -export { - noConversationIDKey, - pendingWaitingConversationIDKey, - pendingErrorConversationIDKey, - isValidConversationIDKey, - dummyConversationIDKey, -} from '../types/chat2/common' diff --git a/shared/constants/chat2/message.tsx b/shared/constants/chat2/message.tsx index c35c576d07e2..b0cdf7684d5b 100644 --- a/shared/constants/chat2/message.tsx +++ b/shared/constants/chat2/message.tsx @@ -1,14 +1,14 @@ // Message related constants import * as T from '../types' -import * as TeamsUtil from '../teams/util' -import type * as ConvoConstants from './convostate' +import * as TeamsUtil from '@/constants/teams' +import type * as ConvoConstants from '@/stores/convostate' import HiddenString from '@/util/hidden-string' import logger from '@/logger' -import type * as MessageTypes from '../types/chat2/message' +import type * as MessageTypes from '@/constants/types/chat2/message' import type {ServiceId} from 'util/platforms' -import {noConversationIDKey} from '../types/chat2/common' +import {noConversationIDKey} from '@/constants/types/chat2/common' import invert from 'lodash/invert' -import {isIOS, isMobile} from '../platform' +import {isIOS, isMobile} from '@/constants/platform' const noString = new HiddenString('') diff --git a/shared/constants/chat2/meta.tsx b/shared/constants/chat2/meta.tsx index 9adc5eda010f..1540e8f9879f 100644 --- a/shared/constants/chat2/meta.tsx +++ b/shared/constants/chat2/meta.tsx @@ -1,10 +1,11 @@ // Meta manages the metadata about a conversation. Participants, isMuted, reset people, etc. Things that drive the inbox import {shallowEqual} from '../utils' -import * as T from '../types' -import * as Teams from '../teams/util' +import * as T from '@/constants/types' +import * as Teams from '@/constants/teams' import * as Message from './message' import {base64ToUint8Array, uint8ArrayToHex} from 'uint8array-extras' -import {storeRegistry} from '../store-registry' +import {storeRegistry} from '@/stores/store-registry' +import {useCurrentUserState} from '@/stores/current-user' const conversationMemberStatusToMembershipType = (m: T.RPCChat.ConversationMemberStatus) => { switch (m) { @@ -40,9 +41,9 @@ export const unverifiedInboxUIItemToConversationMeta = ( // We only treat implicit adhoc teams as having resetParticipants const resetParticipants: Set = new Set( i.localMetadata && - (i.membersType === T.RPCChat.ConversationMembersType.impteamnative || - i.membersType === T.RPCChat.ConversationMembersType.impteamupgrade) && - i.localMetadata.resetParticipants + (i.membersType === T.RPCChat.ConversationMembersType.impteamnative || + i.membersType === T.RPCChat.ConversationMembersType.impteamupgrade) && + i.localMetadata.resetParticipants ? i.localMetadata.resetParticipants : [] ) @@ -236,7 +237,7 @@ export const inboxUIItemToConversationMeta = ( const resetParticipants = new Set( (i.membersType === T.RPCChat.ConversationMembersType.impteamnative || i.membersType === T.RPCChat.ConversationMembersType.impteamupgrade) && - i.resetParticipants + i.resetParticipants ? i.resetParticipants : [] ) @@ -263,8 +264,8 @@ export const inboxUIItemToConversationMeta = ( const conversationIDKey = T.Chat.stringToConversationIDKey(i.convID) let pinnedMsg: T.Chat.PinnedMessageInfo | undefined if (i.pinnedMsg) { - const username = storeRegistry.getState('current-user').username - const devicename = storeRegistry.getState('current-user').deviceName + const username = useCurrentUserState.getState().username + const devicename = useCurrentUserState.getState().deviceName const getLastOrdinal = () => storeRegistry.getConvoState(conversationIDKey).messageOrdinals?.at(-1) ?? T.Chat.numberToOrdinal(0) const message = Message.uiMessageToMessage( diff --git a/shared/constants/chat2/util.tsx b/shared/constants/chat2/util.tsx deleted file mode 100644 index a1e5b2c74fc6..000000000000 --- a/shared/constants/chat2/util.tsx +++ /dev/null @@ -1,61 +0,0 @@ -import * as T from '../types' -import {ignorePromise} from '../utils' -import * as EngineGen from '@/actions/engine-gen-gen' -import {storeRegistry} from '../store-registry' - -export const onEngineConnected = () => { - const f = async () => { - try { - await T.RPCGen.delegateUiCtlRegisterChatUIRpcPromise() - await T.RPCGen.delegateUiCtlRegisterLogUIRpcPromise() - console.log('Registered Chat UI') - } catch (error) { - console.warn('Error in registering Chat UI:', error) - } - } - ignorePromise(f()) -} - -export const onEngineIncoming = (action: EngineGen.Actions) => { - switch (action.type) { - case EngineGen.chat1ChatUiChatInboxFailed: - case EngineGen.chat1NotifyChatChatSetConvSettings: - case EngineGen.chat1NotifyChatChatAttachmentUploadStart: - case EngineGen.chat1NotifyChatChatPromptUnfurl: - case EngineGen.chat1NotifyChatChatPaymentInfo: - case EngineGen.chat1NotifyChatChatRequestInfo: - case EngineGen.chat1NotifyChatChatAttachmentDownloadProgress: - case EngineGen.chat1NotifyChatChatAttachmentDownloadComplete: - case EngineGen.chat1NotifyChatChatAttachmentUploadProgress: - case EngineGen.chat1ChatUiChatCommandMarkdown: - case EngineGen.chat1ChatUiChatGiphyToggleResultWindow: - case EngineGen.chat1ChatUiChatCommandStatus: - case EngineGen.chat1ChatUiChatBotCommandsUpdateStatus: - case EngineGen.chat1ChatUiChatGiphySearchResults: - case EngineGen.chat1NotifyChatChatParticipantsInfo: - case EngineGen.chat1ChatUiChatMaybeMentionUpdate: - case EngineGen.chat1NotifyChatChatConvUpdate: - case EngineGen.chat1ChatUiChatCoinFlipStatus: - case EngineGen.chat1NotifyChatChatThreadsStale: - case EngineGen.chat1NotifyChatChatSubteamRename: - case EngineGen.chat1NotifyChatChatTLFFinalize: - case EngineGen.chat1NotifyChatChatIdentifyUpdate: - case EngineGen.chat1ChatUiChatInboxUnverified: - case EngineGen.chat1NotifyChatChatInboxSyncStarted: - case EngineGen.chat1NotifyChatChatInboxSynced: - case EngineGen.chat1ChatUiChatInboxLayout: - case EngineGen.chat1NotifyChatChatInboxStale: - case EngineGen.chat1ChatUiChatInboxConversation: - case EngineGen.chat1NotifyChatNewChatActivity: - case EngineGen.chat1NotifyChatChatTypingUpdate: - case EngineGen.chat1NotifyChatChatSetConvRetention: - case EngineGen.chat1NotifyChatChatSetTeamRetention: - case EngineGen.keybase1NotifyBadgesBadgeState: - case EngineGen.keybase1GregorUIPushState: - { - storeRegistry.getState('chat').dispatch.onEngineIncomingImpl(action) - } - break - default: - } -} diff --git a/shared/constants/config/util.tsx b/shared/constants/config.tsx similarity index 96% rename from shared/constants/config/util.tsx rename to shared/constants/config.tsx index b44de040a866..954a6daddde8 100644 --- a/shared/constants/config/util.tsx +++ b/shared/constants/config.tsx @@ -1,5 +1,5 @@ import uniq from 'lodash/uniq' -import {runMode} from '../platform' +import {runMode} from './platform' // An ugly error message from the service that we'd like to rewrite ourselves. export const invalidPasswordErrorString = 'Bad password: Invalid password. Server rejected login attempt..' diff --git a/shared/constants/crypto/util.tsx b/shared/constants/crypto.tsx similarity index 98% rename from shared/constants/crypto/util.tsx rename to shared/constants/crypto.tsx index 6f88e37d8e57..459215e60c1f 100644 --- a/shared/constants/crypto/util.tsx +++ b/shared/constants/crypto.tsx @@ -1,4 +1,4 @@ -import {isMobile} from '../platform' +import {isMobile} from './platform' export const saltpackDocumentation = 'https://saltpack.org' export const inputDesktopMaxHeight = {maxHeight: '30%'} as const diff --git a/shared/constants/deeplinks.tsx b/shared/constants/deeplinks.tsx new file mode 100644 index 000000000000..1b207eff3219 --- /dev/null +++ b/shared/constants/deeplinks.tsx @@ -0,0 +1,265 @@ +import * as Tabs from './tabs' +import URL from 'url-parse' +import logger from '@/logger' +import * as T from '@/constants/types' +import {navigateAppend, switchTab} from './router2' +import {storeRegistry} from '@/stores/store-registry' +import {useChatState} from '@/stores/chat2' +import {useProfileState} from '@/stores/profile' +import {useSettingsPhoneState} from '@/stores/settings-phone' +import {useTeamsState} from '@/stores/teams' + +const prefix = 'keybase://' +export const linkFromConvAndMessage = (conv: string, messageID: number) => + `${prefix}chat/${conv}/${messageID}` + +const isTeamPageAction = (a?: string): a is TeamPageAction => { + switch (a) { + case 'add_or_invite': + case 'manage_settings': + case 'join': + return true + default: + return false + } +} + +type TeamPageAction = 'add_or_invite' | 'manage_settings' | 'join' + +// This logic is copied from go/protocol/keybase1/extras.go. +const validTeamnamePart = (s: string): boolean => { + if (s.length < 2 || s.length > 16) { + return false + } + + return /^([a-zA-Z0-9][a-zA-Z0-9_]?)+$/.test(s) +} + +const validTeamname = (s: string) => s.split('.').every(validTeamnamePart) +const handleShowUserProfileLink = (username: string) => { + switchTab(Tabs.peopleTab) + useProfileState.getState().dispatch.showUserProfile(username) +} + +const isKeybaseIoUrl = (url: URL) => { + const {protocol} = url + if (protocol !== 'http:' && protocol !== 'https:') return false + if (url.username || url.password) return false + const {hostname} = url + if (hostname !== 'keybase.io' && hostname !== 'www.keybase.io') return false + const {port} = url + if (port) { + if (protocol === 'http:' && port !== '80') return false + if (protocol === 'https:' && port !== '443') return false + } + return true +} + +const urlToUsername = (url: URL) => { + if (!isKeybaseIoUrl(url)) { + return null + } + // Adapted username regexp (see libkb/checkers.go) with a leading /, an + // optional trailing / and a dash for custom links. + const match = url.pathname.match(/^\/((?:[a-zA-Z0-9][a-zA-Z0-9_-]?)+)\/?$/) + if (!match) { + return null + } + const usernameMatch = match[1] + if (!usernameMatch || usernameMatch.length < 2 || usernameMatch.length > 16) { + return null + } + // Ignore query string and hash parameters. + return usernameMatch.toLowerCase() +} + +const urlToTeamDeepLink = (url: URL) => { + if (!isKeybaseIoUrl(url)) { + return null + } + // Similar regexp to username but allow `.` for subteams + const match = url.pathname.match(/^\/team\/((?:[a-zA-Z0-9][a-zA-Z0-9_.-]?)+)\/?$/) + if (!match) { + return null + } + const teamName = match[1] + if (!teamName || teamName.length < 2 || teamName.length > 255) { + return null + } + // `url.query` has a wrong type in @types/url-parse. It's a `string` in the + // code, but @types claim it's a {[k: string]: string | undefined}. + const queryString = url.query as unknown as string + // URLSearchParams is not available in react-native. See if any of recognized + // query parameters is passed using regular expressions. + const action = (['add_or_invite', 'manage_settings'] satisfies readonly TeamPageAction[]).find( + x => queryString.search(`[?&]applink=${x}([?&].+)?$`) !== -1 + ) + return {action, teamName} +} + +const handleTeamPageLink = (teamname: string, action?: TeamPageAction) => { + useTeamsState + .getState() + .dispatch.showTeamByName( + teamname, + action === 'manage_settings' ? 'settings' : undefined, + action === 'join' ? true : undefined, + action === 'add_or_invite' ? true : undefined + ) +} + +export const handleAppLink = (link: string) => { + if (link.startsWith('keybase://')) { + handleKeybaseLink(link.replace('keybase://', '')) + return + } else { + // Normal deeplink + const url = new URL(link) + const username = urlToUsername(url) + if (username === 'phone-app') { + const phoneState = useSettingsPhoneState.getState() + const phones = (phoneState as {phones?: Map}).phones + if (!phones || phones.size > 0) { + return + } + switchTab(Tabs.settingsTab) + navigateAppend('settingsAddPhone') + } else if (username && username !== 'app') { + handleShowUserProfileLink(username) + return + } + const teamLink = urlToTeamDeepLink(url) + if (teamLink) { + handleTeamPageLink(teamLink.teamName, teamLink.action) + return + } + } +} + +export const handleKeybaseLink = (link: string) => { + if (!link) return + const error = + "We couldn't read this link. The link might be bad, or your Keybase app might be out of date and needs to be updated." + const parts = link.split('/') + // List guaranteed to contain at least one elem. + switch (parts[0]) { + case 'profile': + if (parts[1] === 'new-proof' && (parts.length === 3 || parts.length === 4)) { + parts.length === 4 && parts[3] && useProfileState.getState().dispatch.showUserProfile(parts[3]) + useProfileState.getState().dispatch.addProof(parts[2]!, 'appLink') + return + } else if (parts[1] === 'show' && parts.length === 3) { + // Username is basically a team name part, we can use the same logic to + // validate deep link. + const username = parts[2]! + if (username.length && validTeamnamePart(username)) { + return handleShowUserProfileLink(username) + } + } + break + // Fall-through + case 'private': + case 'public': + case 'team': + try { + const decoded = decodeURIComponent(link) + switchTab(Tabs.fsTab) + navigateAppend({props: {path: `/keybase/${decoded}`}, selected: 'fsRoot'}) + return + } catch { + logger.warn("Coudn't decode KBFS URI") + return + } + case 'convid': + if (parts.length === 2) { + const conversationIDKey = parts[1] + if (conversationIDKey) { + storeRegistry.getConvoState(conversationIDKey).dispatch.navigateToThread('navChanged') + } + return + } + break + case 'chat': + if (parts.length === 2 || parts.length === 3) { + if (parts[1]!.includes('#')) { + const teamChat = parts[1]!.split('#') + if (teamChat.length !== 2) { + navigateAppend({props: {error}, selected: 'keybaseLinkError'}) + return + } + const [teamname, channelname] = teamChat + const _highlightMessageID = parseInt(parts[2]!, 10) + if (_highlightMessageID < 0) { + logger.warn(`invalid chat message id: ${_highlightMessageID}`) + return + } + + const highlightMessageID = T.Chat.numberToMessageID(_highlightMessageID) + const {previewConversation} = useChatState.getState().dispatch + previewConversation({ + channelname, + highlightMessageID, + reason: 'appLink', + teamname, + }) + return + } else { + const highlightMessageID = parseInt(parts[2]!, 10) + if (highlightMessageID < 0) { + logger.warn(`invalid chat message id: ${highlightMessageID}`) + return + } + const {previewConversation} = useChatState.getState().dispatch + previewConversation({ + highlightMessageID: T.Chat.numberToMessageID(highlightMessageID), + participants: parts[1]!.split(','), + reason: 'appLink', + }) + return + } + } + break + case 'team-page': // keybase://team-page/{team_name}/{manage_settings,add_or_invite}? + if (parts.length >= 2) { + const teamName = parts[1]! + if (teamName.length && validTeamname(teamName)) { + const actionPart = parts[2] + const action = isTeamPageAction(actionPart) ? actionPart : undefined + handleTeamPageLink(teamName, action) + return + } + } + break + case 'incoming-share': + // android needs to render first when coming back + setTimeout(() => { + navigateAppend('incomingShareNew') + }, 500) + return + case 'team-invite-link': + useTeamsState.getState().dispatch.openInviteLink(parts[1] ?? '', parts[2] || '') + return + case 'settingsPushPrompt': + navigateAppend('settingsPushPrompt') + return + case Tabs.teamsTab: + switchTab(Tabs.teamsTab) + return + case Tabs.fsTab: + switchTab(Tabs.fsTab) + return + case Tabs.chatTab: + switchTab(Tabs.chatTab) + return + case Tabs.peopleTab: + switchTab(Tabs.peopleTab) + return + case Tabs.settingsTab: + switchTab(Tabs.settingsTab) + return + default: + // Fall through to the error return below. + } + navigateAppend({props: {error}, selected: 'keybaseLinkError'}) +} + diff --git a/shared/constants/deeplinks/index.tsx b/shared/constants/deeplinks/index.tsx deleted file mode 100644 index 9dfdf5e6e749..000000000000 --- a/shared/constants/deeplinks/index.tsx +++ /dev/null @@ -1,343 +0,0 @@ -import * as Crypto from '../crypto/util' -import * as Tabs from '../tabs' -import {isPathSaltpackEncrypted, isPathSaltpackSigned} from '@/util/path' -import * as Z from '@/util/zustand' -import * as EngineGen from '@/actions/engine-gen-gen' -import type HiddenString from '@/util/hidden-string' -import URL from 'url-parse' -import logger from '@/logger' -import * as T from '@/constants/types' -import {navigateAppend, switchTab} from '../router2/util' -import {storeRegistry} from '../store-registry' - -const prefix = 'keybase://' -type Store = T.Immutable<{ - keybaseLinkError: string -}> -export const linkFromConvAndMessage = (conv: string, messageID: number) => - `${prefix}chat/${conv}/${messageID}` - -const isTeamPageAction = (a?: string): a is TeamPageAction => { - switch (a) { - case 'add_or_invite': - case 'manage_settings': - case 'join': - return true - default: - return false - } -} - -type TeamPageAction = 'add_or_invite' | 'manage_settings' | 'join' - -// This logic is copied from go/protocol/keybase1/extras.go. -const validTeamnamePart = (s: string): boolean => { - if (s.length < 2 || s.length > 16) { - return false - } - - return /^([a-zA-Z0-9][a-zA-Z0-9_]?)+$/.test(s) -} - -const validTeamname = (s: string) => s.split('.').every(validTeamnamePart) - -const initialStore: Store = { - keybaseLinkError: '', -} - -export interface State extends Store { - dispatch: { - handleAppLink: (link: string) => void - handleKeybaseLink: (link: string) => void - handleSaltPackOpen: (_path: string | HiddenString) => void - onEngineIncomingImpl: (action: EngineGen.Actions) => void - resetState: 'default' - setLinkError: (e: string) => void - } -} - -export const useDeepLinksState = Z.createZustand((set, get) => { - const handleShowUserProfileLink = (username: string) => { - switchTab(Tabs.peopleTab) - storeRegistry.getState('profile').dispatch.showUserProfile(username) - } - - const isKeybaseIoUrl = (url: URL) => { - const {protocol} = url - if (protocol !== 'http:' && protocol !== 'https:') return false - if (url.username || url.password) return false - const {hostname} = url - if (hostname !== 'keybase.io' && hostname !== 'www.keybase.io') return false - const {port} = url - if (port) { - if (protocol === 'http:' && port !== '80') return false - if (protocol === 'https:' && port !== '443') return false - } - return true - } - - const urlToUsername = (url: URL) => { - if (!isKeybaseIoUrl(url)) { - return null - } - // Adapted username regexp (see libkb/checkers.go) with a leading /, an - // optional trailing / and a dash for custom links. - const match = url.pathname.match(/^\/((?:[a-zA-Z0-9][a-zA-Z0-9_-]?)+)\/?$/) - if (!match) { - return null - } - const usernameMatch = match[1] - if (!usernameMatch || usernameMatch.length < 2 || usernameMatch.length > 16) { - return null - } - // Ignore query string and hash parameters. - return usernameMatch.toLowerCase() - } - - const urlToTeamDeepLink = (url: URL) => { - if (!isKeybaseIoUrl(url)) { - return null - } - // Similar regexp to username but allow `.` for subteams - const match = url.pathname.match(/^\/team\/((?:[a-zA-Z0-9][a-zA-Z0-9_.-]?)+)\/?$/) - if (!match) { - return null - } - const teamName = match[1] - if (!teamName || teamName.length < 2 || teamName.length > 255) { - return null - } - // `url.query` has a wrong type in @types/url-parse. It's a `string` in the - // code, but @types claim it's a {[k: string]: string | undefined}. - const queryString = url.query as unknown as string - // URLSearchParams is not available in react-native. See if any of recognized - // query parameters is passed using regular expressions. - const action = (['add_or_invite', 'manage_settings'] satisfies readonly TeamPageAction[]).find( - x => queryString.search(`[?&]applink=${x}([?&].+)?$`) !== -1 - ) - return {action, teamName} - } - - const handleTeamPageLink = (teamname: string, action?: TeamPageAction) => { - storeRegistry - .getState('teams') - .dispatch.showTeamByName( - teamname, - action === 'manage_settings' ? 'settings' : undefined, - action === 'join' ? true : undefined, - action === 'add_or_invite' ? true : undefined - ) - } - - const dispatch: State['dispatch'] = { - handleAppLink: link => { - if (link.startsWith('keybase://')) { - get().dispatch.handleKeybaseLink(link.replace('keybase://', '')) - return - } else { - // Normal deeplink - const url = new URL(link) - const username = urlToUsername(url) - if (username === 'phone-app') { - const phones = storeRegistry.getState('settings-phone').phones - if (!phones || phones.size > 0) { - return - } - switchTab(Tabs.settingsTab) - navigateAppend('settingsAddPhone') - } else if (username && username !== 'app') { - handleShowUserProfileLink(username) - return - } - const teamLink = urlToTeamDeepLink(url) - if (teamLink) { - handleTeamPageLink(teamLink.teamName, teamLink.action) - return - } - } - }, - handleKeybaseLink: link => { - if (!link) return - const error = - "We couldn't read this link. The link might be bad, or your Keybase app might be out of date and needs to be updated." - const parts = link.split('/') - // List guaranteed to contain at least one elem. - switch (parts[0]) { - case 'profile': - if (parts[1] === 'new-proof' && (parts.length === 3 || parts.length === 4)) { - parts.length === 4 && - parts[3] && - storeRegistry.getState('profile').dispatch.showUserProfile(parts[3]) - storeRegistry.getState('profile').dispatch.addProof(parts[2]!, 'appLink') - return - } else if (parts[1] === 'show' && parts.length === 3) { - // Username is basically a team name part, we can use the same logic to - // validate deep link. - const username = parts[2]! - if (username.length && validTeamnamePart(username)) { - return handleShowUserProfileLink(username) - } - } - break - // Fall-through - case 'private': - case 'public': - case 'team': - try { - const decoded = decodeURIComponent(link) - switchTab(Tabs.fsTab) - storeRegistry - .getState('router') - .dispatch.navigateAppend({props: {path: `/keybase/${decoded}`}, selected: 'fsRoot'}) - return - } catch { - logger.warn("Coudn't decode KBFS URI") - return - } - case 'convid': - if (parts.length === 2) { - const conversationIDKey = parts[1] - if (conversationIDKey) { - storeRegistry.getConvoState(conversationIDKey).dispatch.navigateToThread('navChanged') - } - return - } - break - case 'chat': - if (parts.length === 2 || parts.length === 3) { - if (parts[1]!.includes('#')) { - const teamChat = parts[1]!.split('#') - if (teamChat.length !== 2) { - get().dispatch.setLinkError(error) - navigateAppend('keybaseLinkError') - return - } - const [teamname, channelname] = teamChat - const _highlightMessageID = parseInt(parts[2]!, 10) - if (_highlightMessageID < 0) { - logger.warn(`invalid chat message id: ${_highlightMessageID}`) - return - } - - const highlightMessageID = T.Chat.numberToMessageID(_highlightMessageID) - const {previewConversation} = storeRegistry.getState('chat').dispatch - previewConversation({ - channelname, - highlightMessageID, - reason: 'appLink', - teamname, - }) - return - } else { - const highlightMessageID = parseInt(parts[2]!, 10) - if (highlightMessageID < 0) { - logger.warn(`invalid chat message id: ${highlightMessageID}`) - return - } - const {previewConversation} = storeRegistry.getState('chat').dispatch - previewConversation({ - highlightMessageID: T.Chat.numberToMessageID(highlightMessageID), - participants: parts[1]!.split(','), - reason: 'appLink', - }) - return - } - } - break - case 'team-page': // keybase://team-page/{team_name}/{manage_settings,add_or_invite}? - if (parts.length >= 2) { - const teamName = parts[1]! - if (teamName.length && validTeamname(teamName)) { - const actionPart = parts[2] - const action = isTeamPageAction(actionPart) ? actionPart : undefined - handleTeamPageLink(teamName, action) - return - } - } - break - case 'incoming-share': - // android needs to render first when coming back - setTimeout(() => { - const selectedConversationIDKey = parts[1] - ? T.Chat.stringToConversationIDKey(parts[1]) - : undefined - navigateAppend({ - props: selectedConversationIDKey ? {selectedConversationIDKey} : {}, - selected: 'incomingShareNew', - }) - }, 500) - return - case 'team-invite-link': - storeRegistry.getState('teams').dispatch.openInviteLink(parts[1] ?? '', parts[2] || '') - return - case 'settingsPushPrompt': - navigateAppend('settingsPushPrompt') - return - case Tabs.teamsTab: - switchTab(Tabs.teamsTab) - return - case Tabs.fsTab: - switchTab(Tabs.fsTab) - return - case Tabs.chatTab: - switchTab(Tabs.chatTab) - return - case Tabs.peopleTab: - switchTab(Tabs.peopleTab) - return - case Tabs.settingsTab: - switchTab(Tabs.settingsTab) - return - default: - // Fall through to the error return below. - } - get().dispatch.setLinkError(error) - navigateAppend('keybaseLinkError') - }, - handleSaltPackOpen: _path => { - const path = typeof _path === 'string' ? _path : _path.stringValue() - - if (!storeRegistry.getState('config').loggedIn) { - console.warn('Tried to open a saltpack file before being logged in') - return - } - let operation: T.Crypto.Operations | undefined - if (isPathSaltpackEncrypted(path)) { - operation = Crypto.Operations.Decrypt - } else if (isPathSaltpackSigned(path)) { - operation = Crypto.Operations.Verify - } else { - logger.warn( - 'Deeplink received saltpack file path not ending in ".encrypted.saltpack" or ".signed.saltpack"' - ) - return - } - storeRegistry.getState('crypto').dispatch.onSaltpackOpenFile(operation, path) - switchTab(Tabs.cryptoTab) - }, - - onEngineIncomingImpl: action => { - switch (action.type) { - case EngineGen.keybase1NotifyServiceHandleKeybaseLink: { - const {link, deferred} = action.payload.params - if (deferred && !link.startsWith('keybase://team-invite-link/')) { - return - } - get().dispatch.handleKeybaseLink(link) - break - } - default: - } - }, - resetState: 'default', - setLinkError: e => { - set(s => { - s.keybaseLinkError = e - }) - }, - } - return { - ...initialStore, - dispatch, - } -}) diff --git a/shared/constants/deeplinks/util.tsx b/shared/constants/deeplinks/util.tsx deleted file mode 100644 index 7d1d3078ac54..000000000000 --- a/shared/constants/deeplinks/util.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import * as EngineGen from '@/actions/engine-gen-gen' -import {storeRegistry} from '../store-registry' - -export const onEngineIncoming = (action: EngineGen.Actions) => { - switch (action.type) { - case EngineGen.keybase1NotifyServiceHandleKeybaseLink: - { - storeRegistry.getState('deeplinks').dispatch.onEngineIncomingImpl(action) - } - break - default: - } -} diff --git a/shared/constants/devices/util.tsx b/shared/constants/devices/util.tsx deleted file mode 100644 index 23b3694282b6..000000000000 --- a/shared/constants/devices/util.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import * as EngineGen from '@/actions/engine-gen-gen' -import {storeRegistry} from '../store-registry' - -let loaded = false - -export const onEngineIncoming = (action: EngineGen.Actions) => { - switch (action.type) { - case EngineGen.keybase1NotifyBadgesBadgeState: - { - const {badgeState} = action.payload.params - const {newDevices, revokedDevices} = badgeState - const hasValue = (newDevices?.length ?? 0) + (revokedDevices?.length ?? 0) > 0 - if (loaded || hasValue) { - loaded = true - storeRegistry.getState('devices').dispatch.onEngineIncomingImpl(action) - } - } - break - default: - } -} diff --git a/shared/constants/engine/index.tsx b/shared/constants/engine/index.tsx deleted file mode 100644 index e2d651083c6b..000000000000 --- a/shared/constants/engine/index.tsx +++ /dev/null @@ -1,84 +0,0 @@ -import * as Z from '@/util/zustand' -import {storeRegistry} from '../store-registry' -import type * as EngineGen from '@/actions/engine-gen-gen' -import * as ArchiveUtil from '../archive/util' -import * as AutoResetUtil from '../autoreset/util' -import * as BotsUtil from '../bots/util' -import * as ChatUtil from '../chat2/util' -import * as DeepLinksUtil from '../deeplinks/util' -import * as DevicesUtil from '../devices/util' -import * as FSUtil from '../fs/util' -import * as GitUtil from '../git/util' -import * as NotifUtil from '../notifications/util' -import * as PeopleUtil from '../people/util' -import * as PinentryUtil from '../pinentry/util' -import * as SettingsUtil from '../settings/util' -import * as SignupUtil from '../signup/util' -import * as TeamsUtil from '../teams/util' -import * as TrackerUtil from '../tracker2/util' -import * as UnlockFoldersUtil from '../unlock-folders/util' -import * as UsersUtil from '../users/util' - -type Store = object -const initialStore: Store = {} - -export interface State extends Store { - dispatch: { - onEngineConnected: () => void - onEngineDisconnected: () => void - onEngineIncoming: (action: EngineGen.Actions) => void - resetState: () => void - } -} - -export const useEngineState = Z.createZustand(set => { - let incomingTimeout: NodeJS.Timeout - const dispatch: State['dispatch'] = { - onEngineConnected: () => { - ChatUtil.onEngineConnected() - storeRegistry.getState('config').dispatch.onEngineConnected() - NotifUtil.onEngineConnected() - PeopleUtil.onEngineConnected() - PinentryUtil.onEngineConnected() - TrackerUtil.onEngineConnected() - UnlockFoldersUtil.onEngineConnected() - }, - onEngineDisconnected: () => { - storeRegistry.getState('config').dispatch.onEngineDisonnected() - }, - onEngineIncoming: action => { - // defer a frame so its more like before - incomingTimeout = setTimeout(() => { - // we delegate to these utils so we don't need to load stores that we don't need yet - ArchiveUtil.onEngineIncoming(action) - AutoResetUtil.onEngineIncoming(action) - BotsUtil.onEngineIncoming(action) - ChatUtil.onEngineIncoming(action) - storeRegistry.getState('config').dispatch.dynamic.onEngineIncomingDesktop?.(action) - storeRegistry.getState('config').dispatch.dynamic.onEngineIncomingNative?.(action) - storeRegistry.getState('config').dispatch.onEngineIncoming(action) - DeepLinksUtil.onEngineIncoming(action) - DevicesUtil.onEngineIncoming(action) - FSUtil.onEngineIncoming(action) - GitUtil.onEngineIncoming(action) - NotifUtil.onEngineIncoming(action) - PeopleUtil.onEngineIncoming(action) - PinentryUtil.onEngineIncoming(action) - SettingsUtil.onEngineIncoming(action) - SignupUtil.onEngineIncoming(action) - TeamsUtil.onEngineIncoming(action) - TrackerUtil.onEngineIncoming(action) - UnlockFoldersUtil.onEngineIncoming(action) - UsersUtil.onEngineIncoming(action) - }, 0) - }, - resetState: () => { - set(s => ({...s, ...initialStore, dispatch: s.dispatch})) - clearTimeout(incomingTimeout) - }, - } - return { - ...initialStore, - dispatch, - } -}) diff --git a/shared/constants/fs.tsx b/shared/constants/fs.tsx new file mode 100644 index 000000000000..13b1a8580584 --- /dev/null +++ b/shared/constants/fs.tsx @@ -0,0 +1,772 @@ +import * as T from '@/constants/types' +import {isLinux, isMobile} from '@/constants/platform' +import {navigateAppend} from '@/constants/router2' + +// Prefetch Constants +const prefetchNotStarted: T.FS.PrefetchNotStarted = { + state: T.FS.PrefetchState.NotStarted, +} + +const prefetchComplete: T.FS.PrefetchComplete = { + state: T.FS.PrefetchState.Complete, +} + +export {prefetchNotStarted, prefetchComplete} + +export const navToPath = ( + // TODO: remove the second arg when we are done with migrating to nav2 + path: T.FS.Path +) => { + navigateAppend({props: {path}, selected: 'fsRoot'}) +} + +// Path Constants +export const defaultPath = T.FS.stringToPath('/keybase') + +// PathItem Constants +const pathItemMetadataDefault = { + lastModifiedTimestamp: 0, + lastWriter: '', + name: 'unknown', + prefetchStatus: prefetchNotStarted, + size: 0, + writable: false, +} + +export const emptyFolder: T.FS.FolderPathItem = { + ...pathItemMetadataDefault, + children: new Set(), + progress: T.FS.ProgressType.Pending, + type: T.FS.PathType.Folder, +} + +export const emptyFile: T.FS.FilePathItem = { + ...pathItemMetadataDefault, + type: T.FS.PathType.File, +} + +export const emptySymlink: T.FS.SymlinkPathItem = { + ...pathItemMetadataDefault, + linkTarget: '', + type: T.FS.PathType.Symlink, +} + +export const unknownPathItem: T.FS.UnknownPathItem = { + ...pathItemMetadataDefault, + type: T.FS.PathType.Unknown, +} + +// Factory Functions +export const unknownTlf = (() => { + const tlfSyncDisabled: T.FS.TlfSyncDisabled = { + mode: T.FS.TlfSyncMode.Disabled, + } + const makeConflictStateNormalView = ({ + localViewTlfPaths, + resolvingConflict, + stuckInConflict, + }: Partial): T.FS.ConflictStateNormalView => ({ + localViewTlfPaths: [...(localViewTlfPaths || [])], + resolvingConflict: resolvingConflict || false, + stuckInConflict: stuckInConflict || false, + type: T.FS.ConflictStateType.NormalView, + }) + const tlfNormalViewWithNoConflict = makeConflictStateNormalView({}) + const makeTlf = (p: Partial): T.FS.Tlf => { + const { + conflictState, + isFavorite, + isIgnored, + isNew, + name, + resetParticipants, + syncConfig, + teamId, + tlfMtime, + } = p + return { + conflictState: conflictState || tlfNormalViewWithNoConflict, + isFavorite: isFavorite || false, + isIgnored: isIgnored || false, + isNew: isNew || false, + name: name || '', + resetParticipants: [...(resetParticipants || [])], + syncConfig: syncConfig || tlfSyncDisabled, + teamId: teamId || '', + tlfMtime: tlfMtime || 0, + /* See comment in constants/types/fs.js + needsRekey: false, + waitingForParticipantUnlock: I.List(), + youCanUnlock: I.List(), + */ + } + } + return makeTlf({}) +})() + +// Empty/Default Objects +export const emptyNewFolder: T.FS.Edit = { + error: undefined, + name: 'New Folder', + originalName: 'New Folder', + parentPath: T.FS.stringToPath('/keybase'), + type: T.FS.EditType.NewFolder, +} + +export const emptySyncingFoldersProgress: T.FS.SyncingFoldersProgress = { + bytesFetched: 0, + bytesTotal: 0, + endEstimate: 0, + start: 0, +} + +export const emptyOverallSyncStatus: T.FS.OverallSyncStatus = { + diskSpaceStatus: T.FS.DiskSpaceStatus.Ok, + showingBanner: false, + syncingFoldersProgress: emptySyncingFoldersProgress, +} + +export const defaultPathUserSetting: T.FS.PathUserSetting = { + sort: T.FS.SortSetting.NameAsc, +} + +export const defaultTlfListPathUserSetting: T.FS.PathUserSetting = { + sort: T.FS.SortSetting.TimeAsc, +} + +export const emptyDownloadState: T.FS.DownloadState = { + canceled: false, + done: false, + endEstimate: 0, + error: '', + localPath: '', + progress: 0, +} + +export const emptyDownloadInfo: T.FS.DownloadInfo = { + filename: '', + isRegularDownload: false, + path: defaultPath, + startTime: 0, +} + +export const emptyPathItemActionMenu: T.FS.PathItemActionMenu = { + downloadID: undefined, + downloadIntent: undefined, + previousView: T.FS.PathItemActionMenuView.Root, + view: T.FS.PathItemActionMenuView.Root, +} + +export const emptySettings: T.FS.Settings = { + isLoading: false, + loaded: false, + sfmiBannerDismissed: false, + spaceAvailableNotificationThreshold: 0, + syncOnCellular: false, +} + +export const emptyPathInfo: T.FS.PathInfo = { + deeplinkPath: '', + platformAfterMountPath: '', +} + +export const emptyFileContext: T.FS.FileContext = { + contentType: '', + url: '', + viewType: T.RPCGen.GUIViewType.default, +} + +// Driver Status Constants +export const driverStatusUnknown: T.FS.DriverStatusUnknown = { + type: T.FS.DriverStatusType.Unknown, +} as const + +export const emptyDriverStatusEnabled: T.FS.DriverStatusEnabled = { + dokanOutdated: false, + dokanUninstallExecPath: undefined, + isDisabling: false, + type: T.FS.DriverStatusType.Enabled, +} as const + +export const emptyDriverStatusDisabled: T.FS.DriverStatusDisabled = { + isEnabling: false, + kextPermissionError: false, + type: T.FS.DriverStatusType.Disabled, +} as const + +export const defaultDriverStatus: T.FS.DriverStatus = isLinux ? emptyDriverStatusEnabled : driverStatusUnknown + +export const unknownKbfsDaemonStatus: T.FS.KbfsDaemonStatus = { + onlineStatus: T.FS.KbfsDaemonOnlineStatus.Unknown, + rpcStatus: T.FS.KbfsDaemonRpcStatus.Waiting, +} + +// Parsed Path Constants +const parsedPathRoot: T.FS.ParsedPathRoot = {kind: T.FS.PathKind.Root} + +const parsedPathPrivateList: T.FS.ParsedPathTlfList = { + kind: T.FS.PathKind.TlfList, + tlfType: T.FS.TlfType.Private, +} + +const parsedPathPublicList: T.FS.ParsedPathTlfList = { + kind: T.FS.PathKind.TlfList, + tlfType: T.FS.TlfType.Public, +} + +const parsedPathTeamList: T.FS.ParsedPathTlfList = { + kind: T.FS.PathKind.TlfList, + tlfType: T.FS.TlfType.Team, +} + +// Conversion Functions +export const pathToRPCPath = ( + path: T.FS.Path +): {PathType: T.RPCGen.PathType.kbfs; kbfs: T.RPCGen.KBFSPath} => ({ + PathType: T.RPCGen.PathType.kbfs, + kbfs: { + identifyBehavior: T.RPCGen.TLFIdentifyBehavior.fsGui, + path: T.FS.pathToString(path).substring('/keybase'.length) || '/', + }, +}) + +// Path/PathItem Utilities +export const pathTypeToTextType = (type: T.FS.PathType) => + type === T.FS.PathType.Folder ? 'BodySemibold' : 'Body' + +export const getPathItem = ( + pathItems: T.Immutable>, + path: T.Immutable +): T.Immutable => pathItems.get(path) || (unknownPathItem as T.FS.PathItem) + +export const getTlfPath = (path: T.FS.Path): T.FS.Path => { + const elems = T.FS.getPathElements(path) + return elems.length > 2 ? T.FS.pathConcat(T.FS.pathConcat(defaultPath, elems[1]!), elems[2]!) : undefined +} + +export const getTlfListFromType = ( + tlfs: T.Immutable, + tlfType: T.Immutable +): T.Immutable => { + switch (tlfType) { + case T.FS.TlfType.Private: + return tlfs.private + case T.FS.TlfType.Public: + return tlfs.public + case T.FS.TlfType.Team: + return tlfs.team + default: + return new Map() + } +} + +export const getTlfListAndTypeFromPath = ( + tlfs: T.Immutable, + path: T.Immutable +): T.Immutable<{ + tlfList: T.FS.TlfList + tlfType: T.FS.TlfType +}> => { + const visibility = T.FS.getPathVisibility(path) + switch (visibility) { + case T.FS.TlfType.Private: + case T.FS.TlfType.Public: + case T.FS.TlfType.Team: { + const tlfType: T.FS.TlfType = visibility + return {tlfList: getTlfListFromType(tlfs, tlfType), tlfType} + } + default: + return {tlfList: new Map(), tlfType: T.FS.TlfType.Private} + } +} + +export const getTlfFromPathInFavoritesOnly = (tlfs: T.Immutable, path: T.FS.Path): T.FS.Tlf => { + const elems = T.FS.getPathElements(path) + if (elems.length < 3) { + return unknownTlf + } + const {tlfList} = getTlfListAndTypeFromPath(tlfs, path) + return tlfList.get(elems[2]!) || unknownTlf +} + +export const getTlfFromPath = (tlfs: T.Immutable, path: T.FS.Path): T.FS.Tlf => { + const fromFavorites = getTlfFromPathInFavoritesOnly(tlfs, path) + return fromFavorites !== unknownTlf + ? fromFavorites + : tlfs.additionalTlfs.get(getTlfPath(path)) || unknownTlf +} + +export const getTlfFromTlfs = (tlfs: T.FS.Tlfs, tlfType: T.FS.TlfType, name: string): T.FS.Tlf => { + switch (tlfType) { + case T.FS.TlfType.Private: + return tlfs.private.get(name) || unknownTlf + case T.FS.TlfType.Public: + return tlfs.public.get(name) || unknownTlf + case T.FS.TlfType.Team: + return tlfs.team.get(name) || unknownTlf + default: + return unknownTlf + } +} + +export const tlfTypeAndNameToPath = (tlfType: T.FS.TlfType, name: string): T.FS.Path => + T.FS.stringToPath(`/keybase/${tlfType}/${name}`) + +export const getUploadedPath = (parentPath: T.FS.Path, localPath: string) => + T.FS.pathConcat(parentPath, T.FS.getLocalPathName(localPath)) + +export const pathsInSameTlf = (a: T.FS.Path, b: T.FS.Path): boolean => { + const elemsA = T.FS.getPathElements(a) + const elemsB = T.FS.getPathElements(b) + return elemsA.length >= 3 && elemsB.length >= 3 && elemsA[1] === elemsB[1] && elemsA[2] === elemsB[2] +} + +const slashKeybaseSlashLength = '/keybase/'.length +export const escapePath = (path: T.FS.Path): string => + 'keybase://' + + encodeURIComponent(T.FS.pathToString(path).slice(slashKeybaseSlashLength)).replace( + // We need to do this because otherwise encodeURIComponent would encode + // "/"s. + /%2F/g, + '/' + ) + +export const rebasePathToDifferentTlf = (path: T.FS.Path, newTlfPath: T.FS.Path) => + T.FS.pathConcat(newTlfPath, T.FS.getPathElements(path).slice(3).join('/')) + +export const isFolder = (path: T.FS.Path, pathItem: T.FS.PathItem) => + T.FS.getPathLevel(path) <= 3 || pathItem.type === T.FS.PathType.Folder + +export const isInTlf = (path: T.FS.Path) => T.FS.getPathLevel(path) > 2 + +export const hasPublicTag = (path: T.FS.Path): boolean => { + const publicPrefix = '/keybase/public/' + // The slash after public in `publicPrefix` prevents /keybase/public from counting. + return T.FS.pathToString(path).startsWith(publicPrefix) +} + +export const hasSpecialFileElement = (path: T.FS.Path): boolean => + T.FS.getPathElements(path).some(elem => elem.startsWith('.kbfs')) + +// Username/User Utilities +export const splitTlfIntoUsernames = (tlf: string): ReadonlyArray => + tlf.split(' ')[0]?.replace(/#/g, ',').split(',') ?? [] + +export const getUsernamesFromPath = (path: T.FS.Path): ReadonlyArray => { + const elems = T.FS.getPathElements(path) + return elems.length < 3 ? [] : splitTlfIntoUsernames(elems[2]!) +} + +export const usernameInPath = (username: string, path: T.FS.Path) => { + const elems = T.FS.getPathElements(path) + return elems.length >= 3 && elems[2]!.split(',').includes(username) +} + +const splitTlfIntoReadersAndWriters = ( + tlf: string +): { + readers?: Array + writers: Array +} => { + const [w, r] = tlf.split('#') + return { + readers: r ? r.split(',').filter(i => !!i) : undefined, + writers: w?.split(',').filter(i => !!i) ?? [], + } +} + +export const getUsernamesFromTlfName = (tlfName: string): Array => { + const split = splitTlfIntoReadersAndWriters(tlfName) + return split.writers.concat(split.readers || []) +} + +// TLF/List Utilities +export const computeBadgeNumberForTlfList = (tlfList: T.Immutable): number => + [...tlfList.values()].reduce((accumulator, tlf) => (tlfIsBadged(tlf) ? accumulator + 1 : accumulator), 0) + +export const computeBadgeNumberForAll = (tlfs: T.Immutable): number => + [T.FS.TlfType.Private, T.FS.TlfType.Public, T.FS.TlfType.Team] + .map(tlfType => computeBadgeNumberForTlfList(getTlfListFromType(tlfs, tlfType))) + .reduce((sum, count) => sum + count, 0) + +export const tlfIsBadged = (tlf: T.FS.Tlf) => !tlf.isIgnored && tlf.isNew + +export const tlfIsStuckInConflict = (tlf: T.FS.Tlf) => + tlf.conflictState.type === T.FS.ConflictStateType.NormalView && tlf.conflictState.stuckInConflict + +// Path Parsing +export const parsePath = (path: T.FS.Path): T.FS.ParsedPath => { + const elems = T.FS.getPathElements(path) + if (elems.length <= 1) { + return parsedPathRoot + } + switch (elems[1]) { + case 'private': + switch (elems.length) { + case 2: + return parsedPathPrivateList + case 3: + return { + kind: T.FS.PathKind.GroupTlf, + tlfName: elems[2]!, + tlfType: T.FS.TlfType.Private, + ...splitTlfIntoReadersAndWriters(elems[2]!), + } + default: + return { + kind: T.FS.PathKind.InGroupTlf, + rest: elems.slice(3), + tlfName: elems[2] ?? '', + tlfType: T.FS.TlfType.Private, + ...splitTlfIntoReadersAndWriters(elems[2] ?? ''), + } + } + case 'public': + switch (elems.length) { + case 2: + return parsedPathPublicList + case 3: + return { + kind: T.FS.PathKind.GroupTlf, + tlfName: elems[2]!, + tlfType: T.FS.TlfType.Public, + ...splitTlfIntoReadersAndWriters(elems[2]!), + } + default: + return { + kind: T.FS.PathKind.InGroupTlf, + rest: elems.slice(3), + tlfName: elems[2] ?? '', + tlfType: T.FS.TlfType.Public, + ...splitTlfIntoReadersAndWriters(elems[2] ?? ''), + } + } + case 'team': + switch (elems.length) { + case 2: + return parsedPathTeamList + case 3: + return { + kind: T.FS.PathKind.TeamTlf, + team: elems[2]!, + tlfName: elems[2]!, + tlfType: T.FS.TlfType.Team, + } + default: + return { + kind: T.FS.PathKind.InTeamTlf, + rest: elems.slice(3), + team: elems[2] ?? '', + tlfName: elems[2] ?? '', + tlfType: T.FS.TlfType.Team, + } + } + default: + return parsedPathRoot + } +} + +// Chat/Share Utilities +export const canChat = (path: T.FS.Path) => { + const parsedPath = parsePath(path) + switch (parsedPath.kind) { + case T.FS.PathKind.Root: + case T.FS.PathKind.TlfList: + return false + case T.FS.PathKind.GroupTlf: + case T.FS.PathKind.TeamTlf: + return true + case T.FS.PathKind.InGroupTlf: + case T.FS.PathKind.InTeamTlf: + return true + default: + return false + } +} + +export const isTeamPath = (path: T.FS.Path): boolean => { + const parsedPath = parsePath(path) + return parsedPath.kind !== T.FS.PathKind.Root && parsedPath.tlfType === T.FS.TlfType.Team +} + +export const getChatTarget = (path: T.FS.Path, me: string): string => { + const parsedPath = parsePath(path) + if (parsedPath.kind !== T.FS.PathKind.Root && parsedPath.tlfType === T.FS.TlfType.Team) { + return 'team conversation' + } + if (parsedPath.kind === T.FS.PathKind.GroupTlf || parsedPath.kind === T.FS.PathKind.InGroupTlf) { + if (parsedPath.writers.length === 1 && !parsedPath.readers && parsedPath.writers[0] === me) { + return 'yourself' + } + if (parsedPath.writers.length + (parsedPath.readers ? parsedPath.readers.length : 0) === 2) { + const notMe = parsedPath.writers.concat(parsedPath.readers || []).filter(u => u !== me) + if (notMe.length === 1) { + return notMe[0]! + } + } + return 'group conversation' + } + return 'conversation' +} + +export const getSharePathArrayDescription = (paths: ReadonlyArray): string => { + return !paths.length ? '' : paths.length === 1 ? T.FS.getPathName(paths[0]) : `${paths.length} items` +} + +export const getDestinationPickerPathName = (picker: T.FS.DestinationPicker): string => + picker.source.type === T.FS.DestinationPickerSource.MoveOrCopy + ? T.FS.getPathName(picker.source.path) + : picker.source.type === T.FS.DestinationPickerSource.IncomingShare + ? getSharePathArrayDescription( + picker.source.source + .map(({originalPath}) => (originalPath ? T.FS.getLocalPathName(originalPath) : '')) + .filter(Boolean) + ) + : '' + +// File/Download Utilities +export const humanReadableFileSize = (size: number) => { + const kib = 1024 + const mib = kib * kib + const gib = mib * kib + const tib = gib * kib + + if (!size) return '' + if (size >= tib) return `${Math.round(size / tib)} TB` + if (size >= gib) return `${Math.round(size / gib)} GB` + if (size >= mib) return `${Math.round(size / mib)} MB` + if (size >= kib) return `${Math.round(size / kib)} KB` + return `${size} B` +} + +export const humanizeBytes = (n: number, numDecimals: number): string => { + const kb = 1024 + const mb = kb * 1024 + const gb = mb * 1024 + + if (n < kb) { + return `${n} bytes` + } else if (n < mb) { + return `${(n / kb).toFixed(numDecimals)} KB` + } else if (n < gb) { + return `${(n / mb).toFixed(numDecimals)} MB` + } + return `${(n / gb).toFixed(numDecimals)} GB` +} + +export const humanizeBytesOfTotal = (n: number, d: number): string => { + const kb = 1024 + const mb = kb * 1024 + const gb = mb * 1024 + + if (d < kb) { + return `${n} of ${d} bytes` + } else if (d < mb) { + return `${(n / kb).toFixed(2)} of ${(d / kb).toFixed(2)} KB` + } else if (d < gb) { + return `${(n / mb).toFixed(2)} of ${(d / mb).toFixed(2)} MB` + } + return `${(n / gb).toFixed(2)} of ${(d / gb).toFixed(2)} GB` +} + +export const downloadIsOngoing = (dlState: T.FS.DownloadState) => + dlState !== emptyDownloadState && !dlState.error && !dlState.done && !dlState.canceled + +export const getDownloadIntent = ( + path: T.FS.Path, + downloads: T.FS.Downloads, + pathItemActionMenu: T.FS.PathItemActionMenu +): T.FS.DownloadIntent | undefined => { + const found = [...downloads.info].find(([_, info]) => info.path === path) + if (!found) { + return undefined + } + const [downloadID] = found + const dlState = downloads.state.get(downloadID) || emptyDownloadState + if (!downloadIsOngoing(dlState)) { + return undefined + } + if (pathItemActionMenu.downloadID === downloadID) { + return pathItemActionMenu.downloadIntent + } + return T.FS.DownloadIntent.None +} + +export const canSaveMedia = (pathItem: T.FS.PathItem, fileContext: T.FS.FileContext): boolean => { + if (pathItem.type !== T.FS.PathType.File || fileContext === emptyFileContext) { + return false + } + return ( + fileContext.viewType === T.RPCGen.GUIViewType.image || fileContext.viewType === T.RPCGen.GUIViewType.video + ) +} + +export const isOfflineUnsynced = ( + daemonStatus: T.FS.KbfsDaemonStatus, + pathItem: T.FS.PathItem, + path: T.FS.Path +) => + daemonStatus.onlineStatus === T.FS.KbfsDaemonOnlineStatus.Offline && + T.FS.getPathLevel(path) > 2 && + pathItem.prefetchStatus !== prefetchComplete + +// Status/Icon Utilities +export const getUploadIconForTlfType = ( + kbfsDaemonStatus: T.FS.KbfsDaemonStatus, + uploads: T.FS.Uploads, + tlfList: T.FS.TlfList, + tlfType: T.FS.TlfType +): T.FS.UploadIcon | undefined => { + if ( + [...tlfList].some( + ([_, tlf]) => + tlf.conflictState.type === T.FS.ConflictStateType.NormalView && tlf.conflictState.stuckInConflict + ) + ) { + return T.FS.UploadIcon.UploadingStuck + } + + const prefix = T.FS.pathToString(T.FS.getTlfTypePathFromTlfType(tlfType)) + if ( + [...uploads.syncingPaths].some(p => T.FS.pathToString(p).startsWith(prefix)) || + [...uploads.writingToJournal.keys()].some(p => T.FS.pathToString(p).startsWith(prefix)) + ) { + return kbfsDaemonStatus.onlineStatus === T.FS.KbfsDaemonOnlineStatus.Offline + ? T.FS.UploadIcon.AwaitingToUpload + : T.FS.UploadIcon.Uploading + } + + return undefined +} + +export const isPathEnabledForSync = (syncConfig: T.FS.TlfSyncConfig, path: T.FS.Path): boolean => { + switch (syncConfig.mode) { + case T.FS.TlfSyncMode.Disabled: + return false + case T.FS.TlfSyncMode.Enabled: + return true + case T.FS.TlfSyncMode.Partial: + // TODO: when we enable partial sync lookup, remember to deal with + // potential ".." traversal as well. + return syncConfig.enabledPaths.includes(path) + default: + return false + } +} + +export const getPathStatusIconInMergeProps = ( + kbfsDaemonStatus: T.FS.KbfsDaemonStatus, + tlf: T.Immutable, + pathItem: T.Immutable, + uploadingPaths: T.Immutable>, + path: T.Immutable +): T.FS.PathStatusIcon => { + // There's no upload or sync for local conflict view. + if (tlf.conflictState.type === T.FS.ConflictStateType.ManualResolvingLocalView) { + return T.FS.LocalConflictStatus + } + + // uploading state has higher priority + if (uploadingPaths.has(path)) { + // eslint-disable-next-line + return tlf.conflictState.type === T.FS.ConflictStateType.NormalView && tlf.conflictState.stuckInConflict + ? T.FS.UploadIcon.UploadingStuck + : kbfsDaemonStatus.onlineStatus === T.FS.KbfsDaemonOnlineStatus.Offline + ? T.FS.UploadIcon.AwaitingToUpload + : T.FS.UploadIcon.Uploading + } + if (!isPathEnabledForSync(tlf.syncConfig, path)) { + return T.FS.NonUploadStaticSyncStatus.OnlineOnly + } + + if (pathItem === unknownPathItem && tlf.syncConfig.mode !== T.FS.TlfSyncMode.Disabled) { + return T.FS.NonUploadStaticSyncStatus.Unknown + } + + // TODO: what about 'sync-error'? + + // We don't have an upload state, and sync is enabled for this path. + switch (pathItem.prefetchStatus.state) { + case T.FS.PrefetchState.NotStarted: + return T.FS.NonUploadStaticSyncStatus.AwaitingToSync + case T.FS.PrefetchState.Complete: + return T.FS.NonUploadStaticSyncStatus.Synced + case T.FS.PrefetchState.InProgress: { + if (kbfsDaemonStatus.onlineStatus === T.FS.KbfsDaemonOnlineStatus.Offline) { + return T.FS.NonUploadStaticSyncStatus.AwaitingToSync + } + const inProgress: T.FS.PrefetchInProgress = pathItem.prefetchStatus + if (inProgress.bytesTotal === 0) { + return T.FS.NonUploadStaticSyncStatus.AwaitingToSync + } + return inProgress.bytesFetched / inProgress.bytesTotal + } + default: + return T.FS.NonUploadStaticSyncStatus.Unknown + } +} + +export const getMainBannerType = ( + kbfsDaemonStatus: T.FS.KbfsDaemonStatus, + overallSyncStatus: T.FS.OverallSyncStatus +): T.FS.MainBannerType => { + if (kbfsDaemonStatus.onlineStatus === T.FS.KbfsDaemonOnlineStatus.Offline) { + return T.FS.MainBannerType.Offline + } else if (kbfsDaemonStatus.onlineStatus === T.FS.KbfsDaemonOnlineStatus.Trying) { + return T.FS.MainBannerType.TryingToConnect + } else if (overallSyncStatus.diskSpaceStatus === T.FS.DiskSpaceStatus.Error) { + return T.FS.MainBannerType.OutOfSpace + } else { + return T.FS.MainBannerType.None + } +} + +// Settings/Configuration Utilities +export const getPathUserSetting = ( + pathUserSettings: T.Immutable>, + path: T.Immutable +): T.FS.PathUserSetting => + pathUserSettings.get(path) || + (T.FS.getPathLevel(path) < 3 ? defaultTlfListPathUserSetting : defaultPathUserSetting) + +export const showSortSetting = ( + path: T.FS.Path, + pathItem: T.FS.PathItem, + kbfsDaemonStatus: T.FS.KbfsDaemonStatus +) => + !isMobile && + path !== defaultPath && + (T.FS.getPathLevel(path) === 2 || (pathItem.type === T.FS.PathType.Folder && !!pathItem.children.size)) && + !isOfflineUnsynced(kbfsDaemonStatus, pathItem, path) + +export const getSoftError = (softErrors: T.FS.SoftErrors, path: T.FS.Path): T.FS.SoftError | undefined => { + const pathError = softErrors.pathErrors.get(path) + if (pathError) { + return pathError + } + if (!softErrors.tlfErrors.size) { + return undefined + } + const tlfPath = getTlfPath(path) + return (tlfPath && softErrors.tlfErrors.get(tlfPath)) || undefined +} + +export const sfmiInfoLoaded = (settings: T.FS.Settings, driverStatus: T.FS.DriverStatus): boolean => + settings.loaded && driverStatus !== driverStatusUnknown + +export const hideOrDisableInDestinationPicker = ( + tlfType: T.FS.TlfType, + name: string, + username: string, + destinationPickerIndex?: number +) => typeof destinationPickerIndex === 'number' && tlfType === T.FS.TlfType.Public && name !== username + +// Other Utilities + +export const showIgnoreFolder = (path: T.FS.Path, username?: string): boolean => { + const elems = T.FS.getPathElements(path) + if (elems.length !== 3) { + return false + } + return ['public', 'private'].includes(elems[1]!) && elems[2]! !== username +} diff --git a/shared/constants/fs/common.native.tsx b/shared/constants/fs/common.native.tsx deleted file mode 100644 index c75025efc827..000000000000 --- a/shared/constants/fs/common.native.tsx +++ /dev/null @@ -1,69 +0,0 @@ -import logger from '@/logger' -import {ignorePromise} from '../utils' -import {wrapErrors} from '@/util/debug' -import * as T from '../types' -import * as Styles from '@/styles' -import * as FS from '@/constants/fs' -import {launchImageLibraryAsync} from '@/util/expo-image-picker.native' -import {saveAttachmentToCameraRoll, showShareActionSheet} from '../platform-specific' -import {useFSState} from '.' - -export default function initNative() { - useFSState.setState(s => { - s.dispatch.dynamic.pickAndUploadMobile = wrapErrors( - (type: T.FS.MobilePickType, parentPath: T.FS.Path) => { - const f = async () => { - try { - const result = await launchImageLibraryAsync(type, true, true) - if (result.canceled) return - result.assets.map(r => - useFSState.getState().dispatch.upload(parentPath, Styles.unnormalizePath(r.uri)) - ) - } catch (e) { - FS.errorToActionOrThrow(e) - } - } - ignorePromise(f()) - } - ) - - s.dispatch.dynamic.finishedDownloadWithIntentMobile = wrapErrors( - (downloadID: string, downloadIntent: T.FS.DownloadIntent, mimeType: string) => { - const f = async () => { - const {downloads, dispatch} = useFSState.getState() - const downloadState = downloads.state.get(downloadID) || FS.emptyDownloadState - if (downloadState === FS.emptyDownloadState) { - logger.warn('missing download', downloadID) - return - } - const dismissDownload = dispatch.dismissDownload - if (downloadState.error) { - dispatch.redbar(downloadState.error) - dismissDownload(downloadID) - return - } - const {localPath} = downloadState - try { - switch (downloadIntent) { - case T.FS.DownloadIntent.CameraRoll: - await saveAttachmentToCameraRoll(localPath, mimeType) - dismissDownload(downloadID) - return - case T.FS.DownloadIntent.Share: - await showShareActionSheet({filePath: localPath, mimeType}) - dismissDownload(downloadID) - return - case T.FS.DownloadIntent.None: - return - default: - return - } - } catch (err) { - FS.errorToActionOrThrow(err) - } - } - ignorePromise(f()) - } - ) - }) -} diff --git a/shared/constants/fs/platform-specific.android.tsx b/shared/constants/fs/platform-specific.android.tsx deleted file mode 100644 index 8e7f3ff2a04b..000000000000 --- a/shared/constants/fs/platform-specific.android.tsx +++ /dev/null @@ -1,68 +0,0 @@ -import * as T from '../types' -import {ignorePromise, wrapErrors} from '../utils' -import * as FS from '@/constants/fs' -import logger from '@/logger' -import nativeInit from './common.native' -import {useFSState} from '.' -import {androidAddCompleteDownload, fsCacheDir, fsDownloadDir} from 'react-native-kb' - -const finishedRegularDownloadIDs = new Set() - -export default function initPlatformSpecific() { - nativeInit() - - useFSState.setState(s => { - s.dispatch.dynamic.afterKbfsDaemonRpcStatusChanged = wrapErrors(() => { - const f = async () => { - await T.RPCGen.SimpleFSSimpleFSConfigureDownloadRpcPromise({ - // Android's cache dir is (when I tried) [app]/cache but Go side uses - // [app]/.cache by default, which can't be used for sharing to other apps. - cacheDirOverride: fsCacheDir, - downloadDirOverride: fsDownloadDir, - }) - } - ignorePromise(f()) - }) - // needs to be called, TODO could make this better - s.dispatch.dynamic.afterKbfsDaemonRpcStatusChanged() - - s.dispatch.dynamic.finishedRegularDownloadMobile = wrapErrors( - (downloadID: string, mimeType: string) => { - const f = async () => { - // This is fired from a hook and can happen more than once per downloadID. - // So just deduplicate them here. This is small enough and won't happen - // constantly, so don't worry about clearing them. - if (finishedRegularDownloadIDs.has(downloadID)) { - return - } - finishedRegularDownloadIDs.add(downloadID) - - const {downloads} = useFSState.getState() - - const downloadState = downloads.state.get(downloadID) || FS.emptyDownloadState - const downloadInfo = downloads.info.get(downloadID) || FS.emptyDownloadInfo - if (downloadState === FS.emptyDownloadState || downloadInfo === FS.emptyDownloadInfo) { - logger.warn('missing download', downloadID) - return - } - if (downloadState.error) { - return - } - try { - await androidAddCompleteDownload({ - description: `Keybase downloaded ${downloadInfo.filename}`, - mime: mimeType, - path: downloadState.localPath, - showNotification: true, - title: downloadInfo.filename, - }) - } catch { - logger.warn('Failed to addCompleteDownload') - } - // No need to dismiss here as the download wrapper does it for Android. - } - ignorePromise(f()) - } - ) - }) -} diff --git a/shared/constants/fs/platform-specific.d.ts b/shared/constants/fs/platform-specific.d.ts deleted file mode 100644 index e5bdb364c59a..000000000000 --- a/shared/constants/fs/platform-specific.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare function initPlatformSpecific(): void -export default initPlatformSpecific diff --git a/shared/constants/fs/platform-specific.desktop.tsx b/shared/constants/fs/platform-specific.desktop.tsx deleted file mode 100644 index b9e372eded7c..000000000000 --- a/shared/constants/fs/platform-specific.desktop.tsx +++ /dev/null @@ -1,310 +0,0 @@ -import * as T from '@/constants/types' -import {ignorePromise, wrapErrors} from '../utils' -import * as Constants from '../fs' -import * as Tabs from '../tabs' -import {isWindows, isLinux, pathSep, isDarwin} from '../platform.desktop' -import logger from '@/logger' -import * as Path from '@/util/path' -import KB2 from '@/util/electron.desktop' -import {uint8ArrayToHex} from 'uint8array-extras' -import {useFSState} from '.' -import {navigateAppend} from '../router2/util' -import {storeRegistry} from '../store-registry' - -const {openPathInFinder, openURL, getPathType, selectFilesToUploadDialog} = KB2.functions -const {darwinCopyToKBFSTempUploadFile, relaunchApp, uninstallKBFSDialog, uninstallDokanDialog} = KB2.functions -const {exitApp, windowsCheckMountFromOtherDokanInstall, installCachedDokan, uninstallDokan} = KB2.functions - -// _openPathInSystemFileManagerPromise opens `openPath` in system file manager. -// If isFolder is true, it just opens it. Otherwise, it shows it in its parent -// folder. This function does not check if the file exists, or try to convert -// KBFS paths. Caller should take care of those. -const _openPathInSystemFileManagerPromise = async (openPath: string, isFolder: boolean): Promise => - openPathInFinder?.(openPath, isFolder) - -const escapeBackslash = isWindows - ? (pathElem: string): string => - pathElem - .replace(/‰/g, '‰2030') - .replace(/([<>:"/\\|?*])/g, (_, c: Uint8Array) => '‰' + uint8ArrayToHex(c)) - : (pathElem: string): string => pathElem - -const _rebaseKbfsPathToMountLocation = (kbfsPath: T.FS.Path, mountLocation: string) => - Path.join(mountLocation, T.FS.getPathElements(kbfsPath).slice(1).map(escapeBackslash).join(pathSep)) - -const fuseStatusToUninstallExecPath = isWindows - ? (status: T.RPCGen.FuseStatus) => { - const field = status.status.fields?.find(({key}) => key === 'uninstallString') - return field?.value - } - : () => undefined - -const fuseStatusToActions = - (previousStatusType: T.FS.DriverStatusType) => (status: T.RPCGen.FuseStatus | undefined) => { - if (!status) { - useFSState.getState().dispatch.setDriverStatus(Constants.defaultDriverStatus) - return - } - - if (status.kextStarted) { - useFSState.getState().dispatch.setDriverStatus({ - ...Constants.emptyDriverStatusEnabled, - dokanOutdated: status.installAction === T.RPCGen.InstallAction.upgrade, - dokanUninstallExecPath: fuseStatusToUninstallExecPath(status), - }) - } else { - useFSState.getState().dispatch.setDriverStatus(Constants.emptyDriverStatusDisabled) - } - - if (status.kextStarted && previousStatusType === T.FS.DriverStatusType.Disabled) { - useFSState - .getState() - .dispatch.dynamic.openPathInSystemFileManagerDesktop?.(T.FS.stringToPath('/keybase')) - } - } - -const fuseInstallResultIsKextPermissionError = (result: T.RPCGen.InstallResult): boolean => - result.componentResults?.findIndex( - c => c.name === 'fuse' && c.exitCode === Constants.ExitCodeFuseKextPermissionError - ) !== -1 - -const driverEnableFuse = async (isRetry: boolean) => { - const result = await T.RPCGen.installInstallFuseRpcPromise() - if (fuseInstallResultIsKextPermissionError(result)) { - useFSState.getState().dispatch.driverKextPermissionError() - if (!isRetry) { - navigateAppend('kextPermission') - } - } else { - await T.RPCGen.installInstallKBFSRpcPromise() // restarts kbfsfuse - await T.RPCGen.kbfsMountWaitForMountsRpcPromise() - useFSState.getState().dispatch.dynamic.refreshDriverStatusDesktop?.() - } -} - -const uninstallKBFSConfirm = async () => { - const remove = await (uninstallKBFSDialog?.() ?? Promise.resolve(false)) - if (remove) { - useFSState.getState().dispatch.driverDisabling() - } -} - -const uninstallKBFS = async () => - T.RPCGen.installUninstallKBFSRpcPromise().then(() => { - // Restart since we had to uninstall KBFS and it's needed by the service (for chat) - relaunchApp?.() - exitApp?.(0) - }) - -const uninstallDokanConfirm = async () => { - const driverStatus = useFSState.getState().sfmi.driverStatus - if (driverStatus.type !== T.FS.DriverStatusType.Enabled) { - return - } - if (!driverStatus.dokanUninstallExecPath) { - await uninstallDokanDialog?.() - useFSState.getState().dispatch.dynamic.refreshDriverStatusDesktop?.() - return - } - useFSState.getState().dispatch.driverDisabling() -} - -const onUninstallDokan = async () => { - const driverStatus = useFSState.getState().sfmi.driverStatus - if (driverStatus.type !== T.FS.DriverStatusType.Enabled) return - const execPath: string = driverStatus.dokanUninstallExecPath || '' - logger.info('Invoking dokan uninstaller', execPath) - try { - await uninstallDokan?.(execPath) - } catch {} - useFSState.getState().dispatch.dynamic.refreshDriverStatusDesktop?.() -} - -// Invoking the cached installer package has to happen from the topmost process -// or it won't be visible to the user. The service also does this to support command line -// operations. -const onInstallCachedDokan = async () => { - try { - await installCachedDokan?.() - useFSState.getState().dispatch.dynamic.refreshDriverStatusDesktop?.() - } catch (e) { - Constants.errorToActionOrThrow(e) - } -} - -const initPlatformSpecific = () => { - storeRegistry.getStore('config').subscribe((s, old) => { - if (s.appFocused === old.appFocused) return - useFSState.getState().dispatch.onChangedFocus(s.appFocused) - }) - - useFSState.setState(s => { - s.dispatch.dynamic.uploadFromDragAndDropDesktop = wrapErrors( - (parentPath: T.FS.Path, localPaths: string[]) => { - const {upload} = useFSState.getState().dispatch - const f = async () => { - if (isDarwin && darwinCopyToKBFSTempUploadFile) { - const dir = await T.RPCGen.SimpleFSSimpleFSMakeTempDirForUploadRpcPromise() - const lp = await Promise.all( - localPaths.map(async localPath => darwinCopyToKBFSTempUploadFile(dir, localPath)) - ) - lp.forEach(localPath => upload(parentPath, localPath)) - } else { - localPaths.forEach(localPath => upload(parentPath, localPath)) - } - } - ignorePromise(f()) - } - ) - - s.dispatch.dynamic.openLocalPathInSystemFileManagerDesktop = wrapErrors((localPath: string) => { - const f = async () => { - try { - if (getPathType) { - const pathType = await getPathType(localPath) - await _openPathInSystemFileManagerPromise(localPath, pathType === 'directory') - } - } catch (e) { - Constants.errorToActionOrThrow(e) - } - } - ignorePromise(f()) - }) - - s.dispatch.dynamic.openPathInSystemFileManagerDesktop = wrapErrors((path: T.FS.Path) => { - const f = async () => { - const {sfmi, pathItems} = useFSState.getState() - return sfmi.driverStatus.type === T.FS.DriverStatusType.Enabled && sfmi.directMountDir - ? _openPathInSystemFileManagerPromise( - _rebaseKbfsPathToMountLocation(path, sfmi.directMountDir), - ![T.FS.PathKind.InGroupTlf, T.FS.PathKind.InTeamTlf].includes(Constants.parsePath(path).kind) || - Constants.getPathItem(pathItems, path).type === T.FS.PathType.Folder - ).catch((e: unknown) => Constants.errorToActionOrThrow(e, path)) - : new Promise((resolve, reject) => { - if (sfmi.driverStatus.type !== T.FS.DriverStatusType.Enabled) { - // This usually indicates a developer error as - // openPathInSystemFileManager shouldn't be used when FUSE integration - // is not enabled. So just blackbar to encourage a log send. - reject(new Error('FUSE integration is not enabled')) - } else { - logger.warn('empty directMountDir') // if this happens it might be a race? - resolve() - } - }) - } - ignorePromise(f()) - }) - - s.dispatch.dynamic.refreshDriverStatusDesktop = wrapErrors(() => { - const f = async () => { - let status = await T.RPCGen.installFuseStatusRpcPromise({ - bundleVersion: '', - }) - if (isWindows && status.installStatus !== T.RPCGen.InstallStatus.installed) { - const m = await T.RPCGen.kbfsMountGetCurrentMountDirRpcPromise() - status = await (windowsCheckMountFromOtherDokanInstall?.(m, status) ?? Promise.resolve(status)) - } - fuseStatusToActions(useFSState.getState().sfmi.driverStatus.type)(status) - } - ignorePromise(f()) - }) - - s.dispatch.dynamic.refreshMountDirsDesktop = wrapErrors(() => { - const f = async () => { - const {sfmi, dispatch} = useFSState.getState() - const driverStatus = sfmi.driverStatus - if (driverStatus.type !== T.FS.DriverStatusType.Enabled) { - return - } - const directMountDir = await T.RPCGen.kbfsMountGetCurrentMountDirRpcPromise() - const preferredMountDirs = await T.RPCGen.kbfsMountGetPreferredMountDirsRpcPromise() - dispatch.setDirectMountDir(directMountDir) - dispatch.setPreferredMountDirs(preferredMountDirs || []) - } - ignorePromise(f()) - }) - - s.dispatch.dynamic.setSfmiBannerDismissedDesktop = wrapErrors((dismissed: boolean) => { - const f = async () => { - await T.RPCGen.SimpleFSSimpleFSSetSfmiBannerDismissedRpcPromise({dismissed}) - } - ignorePromise(f()) - }) - - s.dispatch.dynamic.afterDriverEnabled = wrapErrors((isRetry: boolean) => { - const f = async () => { - useFSState.getState().dispatch.dynamic.setSfmiBannerDismissedDesktop?.(false) - if (isWindows) { - await onInstallCachedDokan() - } else { - await driverEnableFuse(isRetry) - } - } - ignorePromise(f()) - }) - - s.dispatch.dynamic.afterDriverDisable = wrapErrors(() => { - const f = async () => { - useFSState.getState().dispatch.dynamic.setSfmiBannerDismissedDesktop?.(false) - if (isWindows) { - await uninstallDokanConfirm() - } else { - await uninstallKBFSConfirm() - } - } - ignorePromise(f()) - }) - - s.dispatch.dynamic.afterDriverDisabling = wrapErrors(() => { - const f = async () => { - if (isWindows) { - await onUninstallDokan() - } else { - await uninstallKBFS() - } - } - ignorePromise(f()) - }) - - s.dispatch.dynamic.openSecurityPreferencesDesktop = wrapErrors(() => { - const f = async () => { - await openURL?.('x-apple.systempreferences:com.apple.preference.security?General', {activate: true}) - } - ignorePromise(f()) - }) - - s.dispatch.dynamic.openFilesFromWidgetDesktop = wrapErrors((path: T.FS.Path) => { - storeRegistry.getState('config').dispatch.showMain() - if (path) { - Constants.makeActionForOpenPathInFilesTab(path) - } else { - navigateAppend(Tabs.fsTab) - } - }) - - s.dispatch.dynamic.openAndUploadDesktop = wrapErrors( - (type: T.FS.OpenDialogType, parentPath: T.FS.Path) => { - const f = async () => { - const localPaths = await (selectFilesToUploadDialog?.(type, parentPath ?? undefined) ?? - Promise.resolve([])) - localPaths.forEach(localPath => useFSState.getState().dispatch.upload(parentPath, localPath)) - } - ignorePromise(f()) - } - ) - - if (!isLinux) { - s.dispatch.dynamic.afterKbfsDaemonRpcStatusChanged = wrapErrors(() => { - const {kbfsDaemonStatus, dispatch} = useFSState.getState() - if (kbfsDaemonStatus.rpcStatus === T.FS.KbfsDaemonRpcStatus.Connected) { - dispatch.dynamic.refreshDriverStatusDesktop?.() - } - dispatch.dynamic.refreshMountDirsDesktop?.() - }) - // force call as it could have happened already - s.dispatch.dynamic.afterKbfsDaemonRpcStatusChanged() - } - }) -} - -export default initPlatformSpecific diff --git a/shared/constants/fs/platform-specific.ios.tsx b/shared/constants/fs/platform-specific.ios.tsx deleted file mode 100644 index abfbc2d4b997..000000000000 --- a/shared/constants/fs/platform-specific.ios.tsx +++ /dev/null @@ -1,2 +0,0 @@ -import nativeInit from './common.native' -export default nativeInit diff --git a/shared/constants/fs/util.tsx b/shared/constants/fs/util.tsx deleted file mode 100644 index 0ecea95b7a97..000000000000 --- a/shared/constants/fs/util.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import * as EngineGen from '@/actions/engine-gen-gen' -import type * as T from '../types' -import {navigateAppend} from '../router2/util' -import {storeRegistry} from '../store-registry' - -export const onEngineIncoming = (action: EngineGen.Actions) => { - switch (action.type) { - case EngineGen.keybase1NotifyFSFSOverallSyncStatusChanged: - case EngineGen.keybase1NotifyFSFSSubscriptionNotifyPath: - case EngineGen.keybase1NotifyFSFSSubscriptionNotify: - { - storeRegistry.getState('fs').dispatch.onEngineIncomingImpl(action) - } - break - default: - } -} - -export const makeActionForOpenPathInFilesTab = ( - // TODO: remove the second arg when we are done with migrating to nav2 - path: T.FS.Path -) => { - navigateAppend({props: {path}, selected: 'fsRoot'}) -} diff --git a/shared/constants/git/util.tsx b/shared/constants/git/util.tsx deleted file mode 100644 index c72418bcf735..000000000000 --- a/shared/constants/git/util.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import * as EngineGen from '@/actions/engine-gen-gen' -import {storeRegistry} from '../store-registry' - -let loadedStore = false -export const onEngineIncoming = (action: EngineGen.Actions) => { - switch (action.type) { - case EngineGen.keybase1NotifyBadgesBadgeState: - { - const {badgeState} = action.payload.params - const badges = new Set(badgeState.newGitRepoGlobalUniqueIDs) - // don't bother loading the store if no badges - if (loadedStore || badges.size) { - loadedStore = true - storeRegistry.getState('git').dispatch.onEngineIncomingImpl(action) - } - } - break - default: - } -} diff --git a/shared/constants/index.tsx b/shared/constants/index.tsx index b3359e15383d..7065a91ae687 100644 --- a/shared/constants/index.tsx +++ b/shared/constants/index.tsx @@ -1,10 +1,9 @@ export * from './platform' export * from './values' export * from './strings' -export {useRouterState, makeScreen} from './router2' -export * as Router2 from './router2' +export {useRouterState, makeScreen} from '@/stores/router2' +export * as Router2 from '@/stores/router2' export * as Tabs from './tabs' -export {useWaitingState} from './waiting' -export * as Waiting from './waiting' -export * as PlatformSpecific from './platform-specific' +export {useWaitingState} from '@/stores/waiting' +export * as Waiting from '@/stores/waiting' export * from './utils' diff --git a/shared/constants/init/index.d.ts b/shared/constants/init/index.d.ts new file mode 100644 index 000000000000..f08146c34c87 --- /dev/null +++ b/shared/constants/init/index.d.ts @@ -0,0 +1,7 @@ +// links all the stores together, stores never import this +import type * as EngineGen from '@/actions/engine-gen-gen' + +export declare function initPlatformListener(): void +export declare function onEngineConnected(): void +export declare function onEngineDisconnected(): void +export declare function onEngineIncoming(action: EngineGen.Actions): void diff --git a/shared/constants/init/index.desktop.tsx b/shared/constants/init/index.desktop.tsx new file mode 100644 index 000000000000..08d952d63514 --- /dev/null +++ b/shared/constants/init/index.desktop.tsx @@ -0,0 +1,583 @@ +// links all the stores together, stores never import this +import * as Chat from '@/stores/chat2' +import {ignorePromise} from '@/constants/utils' +import {useConfigState} from '@/stores/config' +import * as ConfigConstants from '@/stores/config' +import {useDaemonState} from '@/stores/daemon' +import {useFSState} from '@/stores/fs' +import {useProfileState} from '@/stores/profile' +import {useRouterState} from '@/stores/router2' +import * as EngineGen from '@/actions/engine-gen-gen' +import * as T from '@/constants/types' +import InputMonitor from '@/util/platform-specific/input-monitor.desktop' +import KB2 from '@/util/electron.desktop' +import logger from '@/logger' +import type {RPCError} from '@/util/errors' +import {getEngine} from '@/engine' +import {isLinux, isWindows, isDarwin, pathSep} from '@/constants/platform.desktop' +import {kbfsNotification} from '@/util/platform-specific/kbfs-notifications' +import {skipAppFocusActions} from '@/local-debug.desktop' +import NotifyPopup from '@/util/notify-popup' +import {noKBFSFailReason} from '@/constants/config' +import {initSharedSubscriptions, _onEngineIncoming} from './shared' +import {wrapErrors} from '@/util/debug' +import * as Constants from '@/constants/fs' +import * as Tabs from '@/constants/tabs' +import * as Path from '@/util/path' +import {uint8ArrayToHex} from 'uint8array-extras' +import {navigateAppend} from '@/constants/router2' +import {errorToActionOrThrow} from '@/stores/fs' +import {ExitCodeFuseKextPermissionError} from '@/constants/values' + +const {showMainWindow, activeChanged, requestWindowsStartService, ctlQuit, dumpNodeLogger} = KB2.functions +const {quitApp, exitApp, setOpenAtLogin, copyToClipboard} = KB2.functions +const {openPathInFinder, openURL, getPathType, selectFilesToUploadDialog} = KB2.functions +const {darwinCopyToKBFSTempUploadFile, relaunchApp, uninstallKBFSDialog, uninstallDokanDialog} = KB2.functions +const {windowsCheckMountFromOtherDokanInstall, installCachedDokan, uninstallDokan} = KB2.functions + +const dumpLogs = async (reason?: string) => { + await logger.dump() + await (dumpNodeLogger?.() ?? Promise.resolve([])) + // quit as soon as possible + if (reason === 'quitting through menu') { + ctlQuit?.() + } +} + +const maybePauseVideos = () => { + const {appFocused} = useConfigState.getState() + const videos = document.querySelectorAll('video') + const allVideos = Array.from(videos) + + allVideos.forEach(v => { + if (appFocused) { + if (v.hasAttribute('data-focus-paused')) { + if (v.paused) { + v.play() + .then(() => {}) + .catch(() => {}) + } + } + } else { + // only pause looping videos + if (!v.paused && v.hasAttribute('loop') && v.hasAttribute('autoplay')) { + v.setAttribute('data-focus-paused', 'true') + v.pause() + } + } + }) +} + +export const onEngineIncoming = (action: EngineGen.Actions) => { + _onEngineIncoming(action) + switch (action.type) { + case EngineGen.keybase1LogsendPrepareLogsend: { + const f = async () => { + const response = action.payload.response + try { + await dumpLogs() + } finally { + response.result() + } + } + ignorePromise(f()) + break + } + case EngineGen.keybase1NotifyAppExit: + console.log('App exit requested') + exitApp?.(0) + break + case EngineGen.keybase1NotifyFSFSActivity: + kbfsNotification(action.payload.params.notification, NotifyPopup) + break + case EngineGen.keybase1NotifyPGPPgpKeyInSecretStoreFile: { + const f = async () => { + try { + await T.RPCGen.pgpPgpStorageDismissRpcPromise() + } catch (err) { + console.warn('Error in sending pgpPgpStorageDismissRpc:', err) + } + } + ignorePromise(f()) + break + } + case EngineGen.keybase1NotifyServiceShutdown: { + const {code} = action.payload.params + if (isWindows && code !== (T.RPCGen.ExitCode.restart as number)) { + console.log('Quitting due to service shutdown with code: ', code) + // Quit just the app, not the service + quitApp?.() + } + break + } + + case EngineGen.keybase1LogUiLog: { + const {params} = action.payload + const {level, text} = params + logger.info('keybase.1.logUi.log:', params.text.data) + if (level >= T.RPCGen.LogLevel.error) { + NotifyPopup(text.data) + } + break + } + + case EngineGen.keybase1NotifySessionClientOutOfDate: { + const {upgradeTo, upgradeURI, upgradeMsg} = action.payload.params + const body = upgradeMsg || `Please update to ${upgradeTo} by going to ${upgradeURI}` + NotifyPopup('Client out of date!', {body}, 60 * 60) + // This is from the API server. Consider notifications from server always critical. + useConfigState + .getState() + .dispatch.setOutOfDate({critical: true, message: upgradeMsg, outOfDate: true, updating: false}) + break + } + default: + } +} + +// _openPathInSystemFileManagerPromise opens `openPath` in system file manager. +// If isFolder is true, it just opens it. Otherwise, it shows it in its parent +// folder. This function does not check if the file exists, or try to convert +// KBFS paths. Caller should take care of those. +const _openPathInSystemFileManagerPromise = async (openPath: string, isFolder: boolean): Promise => + openPathInFinder?.(openPath, isFolder) + +const escapeBackslash = isWindows + ? (pathElem: string): string => + pathElem + .replace(/‰/g, '‰2030') + .replace(/([<>:"/\\|?*])/g, (_, c: Uint8Array) => '‰' + uint8ArrayToHex(c)) + : (pathElem: string): string => pathElem + +const _rebaseKbfsPathToMountLocation = (kbfsPath: T.FS.Path, mountLocation: string) => + Path.join(mountLocation, T.FS.getPathElements(kbfsPath).slice(1).map(escapeBackslash).join(pathSep)) + +const fuseStatusToUninstallExecPath = isWindows + ? (status: T.RPCGen.FuseStatus) => { + const field = status.status.fields?.find(({key}) => key === 'uninstallString') + return field?.value + } + : () => undefined + +const fuseStatusToActions = + (previousStatusType: T.FS.DriverStatusType) => (status: T.RPCGen.FuseStatus | undefined) => { + if (!status) { + useFSState.getState().dispatch.setDriverStatus(Constants.defaultDriverStatus) + return + } + + if (status.kextStarted) { + useFSState.getState().dispatch.setDriverStatus({ + ...Constants.emptyDriverStatusEnabled, + dokanOutdated: status.installAction === T.RPCGen.InstallAction.upgrade, + dokanUninstallExecPath: fuseStatusToUninstallExecPath(status), + }) + } else { + useFSState.getState().dispatch.setDriverStatus(Constants.emptyDriverStatusDisabled) + } + + if (status.kextStarted && previousStatusType === T.FS.DriverStatusType.Disabled) { + useFSState + .getState() + .dispatch.defer.openPathInSystemFileManagerDesktop?.(T.FS.stringToPath('/keybase')) + } + } + +const fuseInstallResultIsKextPermissionError = (result: T.RPCGen.InstallResult): boolean => + result.componentResults?.findIndex( + c => c.name === 'fuse' && c.exitCode === ExitCodeFuseKextPermissionError + ) !== -1 + +const driverEnableFuse = async (isRetry: boolean) => { + const result = await T.RPCGen.installInstallFuseRpcPromise() + if (fuseInstallResultIsKextPermissionError(result)) { + useFSState.getState().dispatch.driverKextPermissionError() + if (!isRetry) { + navigateAppend('kextPermission') + } + } else { + await T.RPCGen.installInstallKBFSRpcPromise() // restarts kbfsfuse + await T.RPCGen.kbfsMountWaitForMountsRpcPromise() + useFSState.getState().dispatch.defer.refreshDriverStatusDesktop?.() + } +} + +const uninstallKBFSConfirm = async () => { + const remove = await (uninstallKBFSDialog?.() ?? Promise.resolve(false)) + if (remove) { + useFSState.getState().dispatch.driverDisabling() + } +} + +const uninstallKBFS = async () => + T.RPCGen.installUninstallKBFSRpcPromise().then(() => { + // Restart since we had to uninstall KBFS and it's needed by the service (for chat) + relaunchApp?.() + exitApp?.(0) + }) + +const uninstallDokanConfirm = async () => { + const driverStatus = useFSState.getState().sfmi.driverStatus + if (driverStatus.type !== T.FS.DriverStatusType.Enabled) { + return + } + if (!driverStatus.dokanUninstallExecPath) { + await uninstallDokanDialog?.() + useFSState.getState().dispatch.defer.refreshDriverStatusDesktop?.() + return + } + useFSState.getState().dispatch.driverDisabling() +} + +const onUninstallDokan = async () => { + const driverStatus = useFSState.getState().sfmi.driverStatus + if (driverStatus.type !== T.FS.DriverStatusType.Enabled) return + const execPath: string = driverStatus.dokanUninstallExecPath || '' + logger.info('Invoking dokan uninstaller', execPath) + try { + await uninstallDokan?.(execPath) + } catch {} + useFSState.getState().dispatch.defer.refreshDriverStatusDesktop?.() +} + +// Invoking the cached installer package has to happen from the topmost process +// or it won't be visible to the user. The service also does this to support command line +// operations. +const onInstallCachedDokan = async () => { + try { + await installCachedDokan?.() + useFSState.getState().dispatch.defer.refreshDriverStatusDesktop?.() + } catch (e) { + errorToActionOrThrow(e) + } +} + +export const initPlatformListener = () => { + useConfigState.setState(s => { + s.dispatch.defer.dumpLogsNative = dumpLogs + s.dispatch.defer.showMainNative = wrapErrors(() => showMainWindow?.()) + s.dispatch.defer.copyToClipboard = wrapErrors((s: string) => copyToClipboard?.(s)) + s.dispatch.defer.onEngineConnectedDesktop = wrapErrors(() => { + // Introduce ourselves to the service + const f = async () => { + await T.RPCGen.configHelloIAmRpcPromise({details: KB2.constants.helloDetails}) + } + ignorePromise(f()) + }) + }) + + useConfigState.subscribe((s, old) => { + if (s.appFocused === old.appFocused) return + useFSState.getState().dispatch.onChangedFocus(s.appFocused) + }) + + useConfigState.subscribe((s, old) => { + if (s.loggedIn !== old.loggedIn) { + s.dispatch.osNetworkStatusChanged(navigator.onLine, 'notavailable', true) + } + + if (s.appFocused !== old.appFocused) { + maybePauseVideos() + } + + if (s.openAtLogin !== old.openAtLogin) { + const {openAtLogin} = s + const f = async () => { + if (__DEV__) { + console.log('onSetOpenAtLogin disabled for dev mode') + return + } else { + await T.RPCGen.configGuiSetValueRpcPromise({ + path: ConfigConstants.openAtLoginKey, + value: {b: openAtLogin, isNull: false}, + }) + } + if (isLinux || isWindows) { + const enabled = + (await T.RPCGen.ctlGetOnLoginStartupRpcPromise()) === T.RPCGen.OnLoginStartupStatus.enabled + if (enabled !== openAtLogin) { + try { + await T.RPCGen.ctlSetOnLoginStartupRpcPromise({enabled: openAtLogin}) + } catch (error_) { + const error = error_ as RPCError + logger.warn(`Error in sending ctlSetOnLoginStartup: ${error.message}`) + } + } + } else { + logger.info(`Login item settings changed! now ${openAtLogin ? 'on' : 'off'}`) + await setOpenAtLogin?.(openAtLogin) + } + } + ignorePromise(f()) + } + }) + + const handleWindowFocusEvents = () => { + const handle = (appFocused: boolean) => { + if (skipAppFocusActions) { + console.log('Skipping app focus actions!') + } else { + useConfigState.getState().dispatch.changedFocus(appFocused) + } + } + window.addEventListener('focus', () => handle(true)) + window.addEventListener('blur', () => handle(false)) + } + handleWindowFocusEvents() + + const setupReachabilityWatcher = () => { + window.addEventListener('online', () => + useConfigState.getState().dispatch.osNetworkStatusChanged(true, 'notavailable') + ) + window.addEventListener('offline', () => + useConfigState.getState().dispatch.osNetworkStatusChanged(false, 'notavailable') + ) + } + setupReachabilityWatcher() + + useDaemonState.subscribe((s, old) => { + if (s.handshakeVersion !== old.handshakeVersion) { + if (!isWindows) return + + const f = async () => { + const waitKey = 'pipeCheckFail' + const version = s.handshakeVersion + const {wait} = s.dispatch + wait(waitKey, version, true) + try { + logger.info('Checking RPC ownership') + if (KB2.functions.winCheckRPCOwnership) { + await KB2.functions.winCheckRPCOwnership() + } + wait(waitKey, version, false) + } catch (error_) { + // error will be logged in bootstrap check + getEngine().reset() + const error = error_ as RPCError + wait(waitKey, version, false, error.message || 'windows pipe owner fail', true) + } + } + ignorePromise(f()) + } + + if (s.handshakeState !== old.handshakeState && s.handshakeState === 'done') { + useConfigState.getState().dispatch.setStartupDetails({ + conversation: Chat.noConversationIDKey, + followUser: '', + link: '', + tab: undefined, + }) + } + }) + + if (isLinux) { + useConfigState.getState().dispatch.initUseNativeFrame() + } + useConfigState.getState().dispatch.initNotifySound() + useConfigState.getState().dispatch.initForceSmallNav() + useConfigState.getState().dispatch.initOpenAtLogin() + useConfigState.getState().dispatch.initAppUpdateLoop() + + useProfileState.setState(s => { + s.dispatch.editAvatar = () => { + useRouterState + .getState() + .dispatch.navigateAppend({props: {image: undefined}, selected: 'profileEditAvatar'}) + } + }) + + const initializeInputMonitor = () => { + const inputMonitor = new InputMonitor() + inputMonitor.notifyActive = (userActive: boolean) => { + if (skipAppFocusActions) { + console.log('Skipping app focus actions!') + } else { + useConfigState.getState().dispatch.setActive(userActive) + // let node thread save file + activeChanged?.(Date.now(), userActive) + } + } + } + initializeInputMonitor() + + useDaemonState.setState(s => { + s.dispatch.onRestartHandshakeNative = () => { + const {handshakeFailedReason} = useDaemonState.getState() + if (isWindows && handshakeFailedReason === noKBFSFailReason) { + requestWindowsStartService?.() + } + } + }) + + useFSState.setState(s => { + s.dispatch.defer.uploadFromDragAndDropDesktop = wrapErrors( + (parentPath: T.FS.Path, localPaths: string[]) => { + const {upload} = useFSState.getState().dispatch + const f = async () => { + if (isDarwin && darwinCopyToKBFSTempUploadFile) { + const dir = await T.RPCGen.SimpleFSSimpleFSMakeTempDirForUploadRpcPromise() + const lp = await Promise.all( + localPaths.map(async localPath => darwinCopyToKBFSTempUploadFile(dir, localPath)) + ) + lp.forEach(localPath => upload(parentPath, localPath)) + } else { + localPaths.forEach(localPath => upload(parentPath, localPath)) + } + } + ignorePromise(f()) + } + ) + + s.dispatch.defer.openLocalPathInSystemFileManagerDesktop = wrapErrors((localPath: string) => { + const f = async () => { + try { + if (getPathType) { + const pathType = await getPathType(localPath) + await _openPathInSystemFileManagerPromise(localPath, pathType === 'directory') + } + } catch (e) { + errorToActionOrThrow(e) + } + } + ignorePromise(f()) + }) + + s.dispatch.defer.openPathInSystemFileManagerDesktop = wrapErrors((path: T.FS.Path) => { + const f = async () => { + const {sfmi, pathItems} = useFSState.getState() + return sfmi.driverStatus.type === T.FS.DriverStatusType.Enabled && sfmi.directMountDir + ? _openPathInSystemFileManagerPromise( + _rebaseKbfsPathToMountLocation(path, sfmi.directMountDir), + ![T.FS.PathKind.InGroupTlf, T.FS.PathKind.InTeamTlf].includes(Constants.parsePath(path).kind) || + Constants.getPathItem(pathItems, path).type === T.FS.PathType.Folder + ).catch((e: unknown) => errorToActionOrThrow(e, path)) + : new Promise((resolve, reject) => { + if (sfmi.driverStatus.type !== T.FS.DriverStatusType.Enabled) { + // This usually indicates a developer error as + // openPathInSystemFileManager shouldn't be used when FUSE integration + // is not enabled. So just blackbar to encourage a log send. + reject(new Error('FUSE integration is not enabled')) + } else { + logger.warn('empty directMountDir') // if this happens it might be a race? + resolve() + } + }) + } + ignorePromise(f()) + }) + + s.dispatch.defer.refreshDriverStatusDesktop = wrapErrors(() => { + const f = async () => { + let status = await T.RPCGen.installFuseStatusRpcPromise({ + bundleVersion: '', + }) + if (isWindows && status.installStatus !== T.RPCGen.InstallStatus.installed) { + const m = await T.RPCGen.kbfsMountGetCurrentMountDirRpcPromise() + status = await (windowsCheckMountFromOtherDokanInstall?.(m, status) ?? Promise.resolve(status)) + } + fuseStatusToActions(useFSState.getState().sfmi.driverStatus.type)(status) + } + ignorePromise(f()) + }) + + s.dispatch.defer.refreshMountDirsDesktop = wrapErrors(() => { + const f = async () => { + const {sfmi, dispatch} = useFSState.getState() + const driverStatus = sfmi.driverStatus + if (driverStatus.type !== T.FS.DriverStatusType.Enabled) { + return + } + const directMountDir = await T.RPCGen.kbfsMountGetCurrentMountDirRpcPromise() + const preferredMountDirs = await T.RPCGen.kbfsMountGetPreferredMountDirsRpcPromise() + dispatch.setDirectMountDir(directMountDir) + dispatch.setPreferredMountDirs(preferredMountDirs || []) + } + ignorePromise(f()) + }) + + s.dispatch.defer.setSfmiBannerDismissedDesktop = wrapErrors((dismissed: boolean) => { + const f = async () => { + await T.RPCGen.SimpleFSSimpleFSSetSfmiBannerDismissedRpcPromise({dismissed}) + } + ignorePromise(f()) + }) + + s.dispatch.defer.afterDriverEnabled = wrapErrors((isRetry: boolean) => { + const f = async () => { + useFSState.getState().dispatch.defer.setSfmiBannerDismissedDesktop?.(false) + if (isWindows) { + await onInstallCachedDokan() + } else { + await driverEnableFuse(isRetry) + } + } + ignorePromise(f()) + }) + + s.dispatch.defer.afterDriverDisable = wrapErrors(() => { + const f = async () => { + useFSState.getState().dispatch.defer.setSfmiBannerDismissedDesktop?.(false) + if (isWindows) { + await uninstallDokanConfirm() + } else { + await uninstallKBFSConfirm() + } + } + ignorePromise(f()) + }) + + s.dispatch.defer.afterDriverDisabling = wrapErrors(() => { + const f = async () => { + if (isWindows) { + await onUninstallDokan() + } else { + await uninstallKBFS() + } + } + ignorePromise(f()) + }) + + s.dispatch.defer.openSecurityPreferencesDesktop = wrapErrors(() => { + const f = async () => { + await openURL?.('x-apple.systempreferences:com.apple.preference.security?General', {activate: true}) + } + ignorePromise(f()) + }) + + s.dispatch.defer.openFilesFromWidgetDesktop = wrapErrors((path: T.FS.Path) => { + useConfigState.getState().dispatch.showMain() + if (path) { + Constants.navToPath(path) + } else { + navigateAppend(Tabs.fsTab) + } + }) + + s.dispatch.defer.openAndUploadDesktop = wrapErrors( + (type: T.FS.OpenDialogType, parentPath: T.FS.Path) => { + const f = async () => { + const localPaths = await (selectFilesToUploadDialog?.(type, parentPath ?? undefined) ?? + Promise.resolve([])) + localPaths.forEach(localPath => useFSState.getState().dispatch.upload(parentPath, localPath)) + } + ignorePromise(f()) + } + ) + + if (!isLinux) { + s.dispatch.defer.afterKbfsDaemonRpcStatusChanged = wrapErrors(() => { + const {kbfsDaemonStatus, dispatch} = useFSState.getState() + if (kbfsDaemonStatus.rpcStatus === T.FS.KbfsDaemonRpcStatus.Connected) { + dispatch.defer.refreshDriverStatusDesktop?.() + } + dispatch.defer.refreshMountDirsDesktop?.() + }) + // force call as it could have happened already + s.dispatch.defer.afterKbfsDaemonRpcStatusChanged() + } + }) + + initSharedSubscriptions() +} + +export {onEngineConnected, onEngineDisconnected} from './shared' diff --git a/shared/constants/platform-specific/index.native.tsx b/shared/constants/init/index.native.tsx similarity index 58% rename from shared/constants/platform-specific/index.native.tsx rename to shared/constants/init/index.native.tsx index 82d9536f9600..31e1b322cdd1 100644 --- a/shared/constants/platform-specific/index.native.tsx +++ b/shared/constants/init/index.native.tsx @@ -1,139 +1,54 @@ +// links all the stores together, stores never import this import {ignorePromise, neverThrowPromiseFunc, timeoutPromise} from '../utils' -import {storeRegistry} from '../store-registry' -import * as T from '../types' +import {useChatState} from '@/stores/chat2' +import {useConfigState} from '@/stores/config' +import {useCurrentUserState} from '@/stores/current-user' +import {useDaemonState} from '@/stores/daemon' +import {useDarkModeState} from '@/stores/darkmode' +import {useFSState} from '@/stores/fs' +import {useProfileState} from '@/stores/profile' +import {useRouterState} from '@/stores/router2' +import {useSettingsContactsState} from '@/stores/settings-contacts' +import * as T from '@/constants/types' import * as Clipboard from 'expo-clipboard' import * as EngineGen from '@/actions/engine-gen-gen' import * as ExpoLocation from 'expo-location' import * as ExpoTaskManager from 'expo-task-manager' -import * as MediaLibrary from 'expo-media-library' -import * as Tabs from '../tabs' +import * as Tabs from '@/constants/tabs' import * as NetInfo from '@react-native-community/netinfo' import NotifyPopup from '@/util/notify-popup' -import {addNotificationRequest} from 'react-native-kb' import logger from '@/logger' -import {Alert, Linking, ActionSheetIOS} from 'react-native' -import {isIOS, isAndroid} from '../platform.native' +import {Alert, Linking} from 'react-native' +import {isAndroid} from '@/constants/platform.native' import {wrapErrors} from '@/util/debug' -import {getTab, getVisiblePath, logState} from '../router2' +import {getTab, getVisiblePath, logState} from '@/constants/router2' import {launchImageLibraryAsync} from '@/util/expo-image-picker.native' import {setupAudioMode} from '@/util/audio.native' import { + androidAddCompleteDownload, androidOpenSettings, - androidShare, - androidShareText, - androidUnlink, fsCacheDir, fsDownloadDir, androidAppColorSchemeChanged, guiConfig, shareListenersRegistered, } from 'react-native-kb' -import {initPushListener, getStartupDetailsFromInitialPush} from './push.native' +import {initPushListener, getStartupDetailsFromInitialPush} from './push-listener.native' +import {initSharedSubscriptions, _onEngineIncoming} from './shared' import type {ImageInfo} from '@/util/expo-image-picker.native' -import {noConversationIDKey} from '@/constants/types/chat2/common' - -export const requestPermissionsToWrite = async () => { - if (isAndroid) { - const p = await MediaLibrary.requestPermissionsAsync(false) - return p.granted ? Promise.resolve() : Promise.reject(new Error('Unable to acquire storage permissions')) - } - return Promise.resolve() -} - -export const requestLocationPermission = async (mode: T.RPCChat.UIWatchPositionPerm) => { - if (isIOS) { - logger.info('[location] Requesting location perms', mode) - switch (mode) { - case T.RPCChat.UIWatchPositionPerm.base: - { - const iosFGPerms = await ExpoLocation.requestForegroundPermissionsAsync() - if (iosFGPerms.ios?.scope === 'none') { - throw new Error('Please allow Keybase to access your location in the phone settings.') - } - } - break - case T.RPCChat.UIWatchPositionPerm.always: { - const iosBGPerms = await ExpoLocation.requestBackgroundPermissionsAsync() - if (iosBGPerms.status !== ExpoLocation.PermissionStatus.GRANTED) { - throw new Error( - 'Please allow Keybase to access your location even if the app is not running for live location.' - ) - } - break - } - } - } else if (isAndroid) { - const androidBGPerms = await ExpoLocation.requestForegroundPermissionsAsync() - if (androidBGPerms.status !== ExpoLocation.PermissionStatus.GRANTED) { - throw new Error('Unable to acquire location permissions') - } - } -} - -export async function saveAttachmentToCameraRoll(filePath: string, mimeType: string): Promise { - const fileURL = 'file://' + filePath - const saveType: 'video' | 'photo' = mimeType.startsWith('video') ? 'video' : 'photo' - const logPrefix = '[saveAttachmentToCameraRoll] ' - try { - try { - // see it we can keep going anyways, android perms are needed sometimes and sometimes not w/ 33 - await requestPermissionsToWrite() - } catch {} - logger.info(logPrefix + `Attempting to save as ${saveType}`) - await MediaLibrary.saveToLibraryAsync(fileURL) - logger.info(logPrefix + 'Success') - } catch (e) { - // This can fail if the user backgrounds too quickly, so throw up a local notification - // just in case to get their attention. - addNotificationRequest({ - body: `Failed to save ${saveType} to camera roll`, - id: Math.floor(Math.random() * 2 ** 32).toString(), - }).catch(() => {}) - logger.debug(logPrefix + 'failed to save: ' + e) - throw e - } finally { - try { - await androidUnlink(filePath) - } catch { - logger.warn('failed to unlink') - } - } -} - -export const showShareActionSheet = async (options: { - filePath?: string - message?: string - mimeType: string -}) => { - if (isIOS) { - return new Promise((resolve, reject) => { - ActionSheetIOS.showShareActionSheetWithOptions( - { - message: options.message, - url: options.filePath, - }, - reject, - resolve - ) - }) - } else { - if (!options.filePath && options.message) { - try { - await androidShareText(options.message, options.mimeType) - return {completed: true, method: ''} - } catch (e) { - throw new Error('Failed to share: ' + String(e)) - } - } +import {noConversationIDKey} from '../types/chat2/common' +import {getSelectedConversation} from '../chat2/common' +import {getConvoState} from '@/stores/convostate' +import { + requestLocationPermission, + saveAttachmentToCameraRoll, + showShareActionSheet, +} from '@/util/platform-specific/index.native' +import * as FS from '@/constants/fs' +import {errorToActionOrThrow} from '@/stores/fs' +import * as Styles from '@/styles' - try { - await androidShare(options.filePath ?? '', options.mimeType) - return {completed: true, method: ''} - } catch (e) { - throw new Error('Failed to share: ' + String(e)) - } - } -} +const finishedRegularDownloadIDs = new Set() const loadStartupDetails = async () => { const [routeState, initialUrl, push] = await Promise.all([ @@ -191,7 +106,7 @@ const loadStartupDetails = async () => { tab = '' } - storeRegistry.getState('config').dispatch.setStartupDetails({ + useConfigState.getState().dispatch.setStartupDetails({ conversation: conversation ?? noConversationIDKey, followUser, link, @@ -207,8 +122,42 @@ const loadStartupDetails = async () => { ) } +const locationTaskName = 'background-location-task' +let locationRefs = 0 +let madeBackgroundTask = false + +const ensureBackgroundTask = () => { + if (madeBackgroundTask) return + madeBackgroundTask = true + + ExpoTaskManager.defineTask(locationTaskName, async ({data, error}) => { + if (error) { + // check `error.message` for more details. + return Promise.resolve() + } + + if (!data) { + return Promise.resolve() + } + const d = data as {locations?: Array} + const locations = d.locations + if (!locations?.length) { + return Promise.resolve() + } + const pos = locations.at(-1) + const coord = { + accuracy: Math.floor(pos?.coords.accuracy ?? 0), + lat: pos?.coords.latitude ?? 0, + lon: pos?.coords.longitude ?? 0, + } + + useChatState.getState().dispatch.updateLastCoord(coord) + return Promise.resolve() + }) +} + const setPermissionDeniedCommandStatus = (conversationIDKey: T.Chat.ConversationIDKey, text: string) => { - storeRegistry.getConvoState(conversationIDKey).dispatch.setCommandStatusInfo({ + getConvoState(conversationIDKey).dispatch.setCommandStatusInfo({ actions: [T.RPCChat.UICommandStatusActionTyp.appsettings], displayText: text, displayType: T.RPCChat.UICommandStatusDisplayTyp.error, @@ -265,76 +214,35 @@ const onChatClearWatch = async () => { } } -const locationTaskName = 'background-location-task' -let locationRefs = 0 -let madeBackgroundTask = false - -const ensureBackgroundTask = () => { - if (madeBackgroundTask) return - madeBackgroundTask = true - - ExpoTaskManager.defineTask(locationTaskName, async ({data, error}) => { - if (error) { - // check `error.message` for more details. - return Promise.resolve() - } - - if (!data) { - return Promise.resolve() - } - const d = data as {locations?: Array} - const locations = d.locations - if (!locations?.length) { - return Promise.resolve() - } - const pos = locations.at(-1) - const coord = { - accuracy: Math.floor(pos?.coords.accuracy ?? 0), - lat: pos?.coords.latitude ?? 0, - lon: pos?.coords.longitude ?? 0, - } - - storeRegistry.getState('chat').dispatch.updateLastCoord(coord) - return Promise.resolve() - }) -} - -export const watchPositionForMap = async (conversationIDKey: T.Chat.ConversationIDKey) => { - try { - logger.info('[location] perms check due to map') - await requestLocationPermission(T.RPCChat.UIWatchPositionPerm.base) - } catch (_error) { - const error = _error as {message?: string} - logger.info('failed to get location perms: ' + error.message) - setPermissionDeniedCommandStatus(conversationIDKey, `Failed to access location. ${error.message}`) - return () => {} - } - - try { - const sub = await ExpoLocation.watchPositionAsync( - {accuracy: ExpoLocation.LocationAccuracy.Highest}, - location => { - const coord = { - accuracy: Math.floor(location.coords.accuracy ?? 0), - lat: location.coords.latitude, - lon: location.coords.longitude, - } - storeRegistry.getState('chat').dispatch.updateLastCoord(coord) +export const onEngineIncoming = (action: EngineGen.Actions) => { + _onEngineIncoming(action) + switch (action.type) { + case EngineGen.chat1ChatUiTriggerContactSync: + useSettingsContactsState.getState().dispatch.manageContactsCache() + break + case EngineGen.keybase1LogUiLog: { + const {params} = action.payload + const {level, text} = params + logger.info('keybase.1.logUi.log:', params.text.data) + if (level >= T.RPCGen.LogLevel.error) { + NotifyPopup(text.data) } - ) - return () => sub.remove() - } catch (_error) { - const error = _error as {message?: string} - logger.info('failed to get location: ' + error.message) - setPermissionDeniedCommandStatus(conversationIDKey, `Failed to access location. ${error.message}`) - return () => {} + break + } + case EngineGen.chat1ChatUiChatWatchPosition: + ignorePromise(onChatWatchPosition(action)) + break + case EngineGen.chat1ChatUiChatClearWatch: + ignorePromise(onChatClearWatch()) + break + default: } } export const initPlatformListener = () => { let _lastPersist = '' - storeRegistry.getStore('config').setState(s => { - s.dispatch.dynamic.persistRoute = wrapErrors((clear: boolean, immediate: boolean) => { + useConfigState.setState(s => { + s.dispatch.defer.persistRoute = wrapErrors((clear: boolean, immediate: boolean) => { const doClear = async () => { try { await T.RPCGen.configGuiSetValueRpcPromise({ @@ -344,7 +252,7 @@ export const initPlatformListener = () => { } catch {} } const doPersist = async () => { - if (!storeRegistry.getState('config').startup.loaded) { + if (!useConfigState.getState().startup.loaded) { return } let param = {} @@ -387,15 +295,9 @@ export const initPlatformListener = () => { ignorePromise(f()) } }) - - s.dispatch.dynamic.onEngineIncomingNative = wrapErrors((action: EngineGen.Actions) => { - switch (action.type) { - default: - } - }) }) - storeRegistry.getStore('config').subscribe((s, old) => { + useConfigState.subscribe((s, old) => { if (s.mobileAppState === old.mobileAppState) return let appFocused: boolean let logState: T.RPCGen.MobileAppState @@ -407,7 +309,7 @@ export const initPlatformListener = () => { case 'background': appFocused = false logState = T.RPCGen.MobileAppState.background - storeRegistry.getState('config').dispatch.dynamic.persistRoute?.(false, true) + useConfigState.getState().dispatch.defer.persistRoute?.(false, true) break case 'inactive': appFocused = false @@ -419,11 +321,17 @@ export const initPlatformListener = () => { } logger.info(`setting app state on service to: ${logState}`) - storeRegistry.getState('config').dispatch.changedFocus(appFocused) + s.dispatch.changedFocus(appFocused) + + if (appFocused && old.mobileAppState !== 'active') { + const {dispatch} = getConvoState(getSelectedConversation()) + dispatch.loadMoreMessages({reason: 'foregrounding'}) + dispatch.markThreadAsRead() + } }) - storeRegistry.getStore('config').setState(s => { - s.dispatch.dynamic.copyToClipboard = wrapErrors((s: string) => { + useConfigState.setState(s => { + s.dispatch.defer.copyToClipboard = wrapErrors((s: string) => { Clipboard.setStringAsync(s) .then(() => {}) .catch(() => {}) @@ -451,7 +359,7 @@ export const initPlatformListener = () => { } } - storeRegistry.getStore('daemon').subscribe((s, old) => { + useDaemonState.subscribe((s, old) => { const versionChanged = s.handshakeVersion !== old.handshakeVersion const stateChanged = s.handshakeState !== old.handshakeState const justBecameReady = stateChanged && s.handshakeState === 'done' && old.handshakeState !== 'done' @@ -461,11 +369,11 @@ export const initPlatformListener = () => { } }) - storeRegistry.getStore('config').setState(s => { - s.dispatch.dynamic.onFilePickerError = wrapErrors((error: Error) => { + useConfigState.setState(s => { + s.dispatch.defer.onFilePickerError = wrapErrors((error: Error) => { Alert.alert('Error', String(error)) }) - s.dispatch.dynamic.openAppStore = wrapErrors(() => { + s.dispatch.defer.openAppStore = wrapErrors(() => { Linking.openURL( isAndroid ? 'http://play.google.com/store/apps/details?id=io.keybase.ossifrage' @@ -474,7 +382,7 @@ export const initPlatformListener = () => { }) }) - storeRegistry.getStore('profile').setState(s => { + useProfileState.setState(s => { s.dispatch.editAvatar = () => { const f = async () => { try { @@ -486,30 +394,28 @@ export const initPlatformListener = () => { return acc }, undefined) if (!result.canceled && first) { - storeRegistry - .getState('router') + useRouterState + .getState() .dispatch.navigateAppend({props: {image: first}, selected: 'profileEditAvatar'}) } } catch (error) { - storeRegistry.getState('config').dispatch.filePickerError(new Error(String(error))) + useConfigState.getState().dispatch.filePickerError(new Error(String(error))) } } ignorePromise(f()) } }) - storeRegistry.getStore('config').subscribe((s, old) => { + useConfigState.subscribe((s, old) => { if (s.loggedIn === old.loggedIn) return const f = async () => { const {type} = await NetInfo.fetch() - storeRegistry - .getState('config') - .dispatch.osNetworkStatusChanged(type !== NetInfo.NetInfoStateType.none, type, true) + s.dispatch.osNetworkStatusChanged(type !== NetInfo.NetInfoStateType.none, type, true) } ignorePromise(f()) }) - storeRegistry.getStore('config').subscribe((s, old) => { + useConfigState.subscribe((s, old) => { if (s.networkStatus === old.networkStatus) return const type = s.networkStatus?.type if (!type) return @@ -523,8 +429,8 @@ export const initPlatformListener = () => { ignorePromise(f()) }) - storeRegistry.getStore('config').setState(s => { - s.dispatch.dynamic.showShareActionSheet = wrapErrors( + useConfigState.setState(s => { + s.dispatch.defer.showShareActionSheet = wrapErrors( (filePath: string, message: string, mimeType: string) => { const f = async () => { await showShareActionSheet({filePath, message, mimeType}) @@ -534,31 +440,30 @@ export const initPlatformListener = () => { ) }) - storeRegistry.getStore('config').subscribe((s, old) => { + useConfigState.subscribe((s, old) => { if (s.mobileAppState === old.mobileAppState) return if (s.mobileAppState === 'active') { // only reload on foreground - storeRegistry.getState('settings-contacts').dispatch.loadContactPermissions() + useSettingsContactsState.getState().dispatch.loadContactPermissions() } }) // Location if (isAndroid) { - storeRegistry.getStore('dark-mode').subscribe((s, old) => { + useDarkModeState.subscribe((s, old) => { if (s.darkModePreference === old.darkModePreference) return - const {darkModePreference} = storeRegistry.getState('dark-mode') - androidAppColorSchemeChanged(darkModePreference) + androidAppColorSchemeChanged(s.darkModePreference) }) } // we call this when we're logged in. let calledShareListenersRegistered = false - storeRegistry.getStore('router').subscribe((s, old) => { + useRouterState.subscribe((s, old) => { const next = s.navState const prev = old.navState if (next === prev) return - storeRegistry.getState('config').dispatch.dynamic.persistRoute?.(false, false) + useConfigState.getState().dispatch.defer.persistRoute?.(false, false) if (!calledShareListenersRegistered && logState().loggedIn) { calledShareListenersRegistered = true @@ -571,9 +476,7 @@ export const initPlatformListener = () => { initPushListener() NetInfo.addEventListener(({type}) => { - storeRegistry - .getState('config') - .dispatch.osNetworkStatusChanged(type !== NetInfo.NetInfoStateType.none, type) + useConfigState.getState().dispatch.osNetworkStatusChanged(type !== NetInfo.NetInfoStateType.none, type) }) const initAudioModes = () => { @@ -582,14 +485,14 @@ export const initPlatformListener = () => { initAudioModes() if (isAndroid) { - const daemonState = storeRegistry.getState('daemon') + const daemonState = useDaemonState.getState() if (daemonState.handshakeState === 'done' || daemonState.handshakeVersion > 0) { configureAndroidCacheDir() } } - storeRegistry.getStore('config').setState(s => { - s.dispatch.dynamic.openAppSettings = wrapErrors(() => { + useConfigState.setState(s => { + s.dispatch.defer.openAppSettings = wrapErrors(() => { const f = async () => { if (isAndroid) { androidOpenSettings() @@ -605,44 +508,137 @@ export const initPlatformListener = () => { } ignorePromise(f()) }) - - s.dispatch.dynamic.onEngineIncomingNative = wrapErrors((action: EngineGen.Actions) => { - switch (action.type) { - case EngineGen.chat1ChatUiTriggerContactSync: - storeRegistry.getState('settings-contacts').dispatch.manageContactsCache() - break - case EngineGen.keybase1LogUiLog: { - const {params} = action.payload - const {level, text} = params - logger.info('keybase.1.logUi.log:', params.text.data) - if (level >= T.RPCGen.LogLevel.error) { - NotifyPopup(text.data) - } - break - } - case EngineGen.chat1ChatUiChatWatchPosition: - ignorePromise(onChatWatchPosition(action)) - break - case EngineGen.chat1ChatUiChatClearWatch: - ignorePromise(onChatClearWatch()) - break - default: - } - }) }) - storeRegistry.getStore('router').setState(s => { - s.dispatch.dynamic.tabLongPress = wrapErrors((tab: string) => { + useRouterState.setState(s => { + s.dispatch.defer.tabLongPress = wrapErrors((tab: string) => { if (tab !== Tabs.peopleTab) return - const accountRows = storeRegistry.getState('config').configuredAccounts - const current = storeRegistry.getState('current-user').username + const accountRows = useConfigState.getState().configuredAccounts + const current = useCurrentUserState.getState().username const row = accountRows.find(a => a.username !== current && a.hasStoredSecret) if (row) { - storeRegistry.getState('config').dispatch.setUserSwitching(true) - storeRegistry.getState('config').dispatch.login(row.username, '') + useConfigState.getState().dispatch.setUserSwitching(true) + useConfigState.getState().dispatch.login(row.username, '') } }) }) - ignorePromise(storeRegistry.getState('fs').dispatch.setupSubscriptions()) + useFSState.setState(s => { + s.dispatch.defer.pickAndUploadMobile = wrapErrors( + (type: T.FS.MobilePickType, parentPath: T.FS.Path) => { + const f = async () => { + try { + const result = await launchImageLibraryAsync(type, true, true) + if (result.canceled) return + result.assets.map(r => + useFSState.getState().dispatch.upload(parentPath, Styles.unnormalizePath(r.uri)) + ) + } catch (e) { + errorToActionOrThrow(e) + } + } + ignorePromise(f()) + } + ) + + s.dispatch.defer.finishedDownloadWithIntentMobile = wrapErrors( + (downloadID: string, downloadIntent: T.FS.DownloadIntent, mimeType: string) => { + const f = async () => { + const {downloads, dispatch} = useFSState.getState() + const downloadState = downloads.state.get(downloadID) || FS.emptyDownloadState + if (downloadState === FS.emptyDownloadState) { + logger.warn('missing download', downloadID) + return + } + const dismissDownload = dispatch.dismissDownload + if (downloadState.error) { + dispatch.redbar(downloadState.error) + dismissDownload(downloadID) + return + } + const {localPath} = downloadState + try { + switch (downloadIntent) { + case T.FS.DownloadIntent.CameraRoll: + await saveAttachmentToCameraRoll(localPath, mimeType) + dismissDownload(downloadID) + return + case T.FS.DownloadIntent.Share: + await showShareActionSheet({filePath: localPath, mimeType}) + dismissDownload(downloadID) + return + case T.FS.DownloadIntent.None: + return + default: + return + } + } catch (err) { + errorToActionOrThrow(err) + } + } + ignorePromise(f()) + } + ) + }) + + if (isAndroid) { + useFSState.setState(s => { + s.dispatch.defer.afterKbfsDaemonRpcStatusChanged = wrapErrors(() => { + const f = async () => { + await T.RPCGen.SimpleFSSimpleFSConfigureDownloadRpcPromise({ + // Android's cache dir is (when I tried) [app]/cache but Go side uses + // [app]/.cache by default, which can't be used for sharing to other apps. + cacheDirOverride: fsCacheDir, + downloadDirOverride: fsDownloadDir, + }) + } + ignorePromise(f()) + }) + // needs to be called, TODO could make this better + s.dispatch.defer.afterKbfsDaemonRpcStatusChanged() + + s.dispatch.defer.finishedRegularDownloadMobile = wrapErrors( + (downloadID: string, mimeType: string) => { + const f = async () => { + // This is fired from a hook and can happen more than once per downloadID. + // So just deduplicate them here. This is small enough and won't happen + // constantly, so don't worry about clearing them. + if (finishedRegularDownloadIDs.has(downloadID)) { + return + } + finishedRegularDownloadIDs.add(downloadID) + + const {downloads} = useFSState.getState() + + const downloadState = downloads.state.get(downloadID) || FS.emptyDownloadState + const downloadInfo = downloads.info.get(downloadID) || FS.emptyDownloadInfo + if (downloadState === FS.emptyDownloadState || downloadInfo === FS.emptyDownloadInfo) { + logger.warn('missing download', downloadID) + return + } + if (downloadState.error) { + return + } + try { + await androidAddCompleteDownload({ + description: `Keybase downloaded ${downloadInfo.filename}`, + mime: mimeType, + path: downloadState.localPath, + showNotification: true, + title: downloadInfo.filename, + }) + } catch { + logger.warn('Failed to addCompleteDownload') + } + // No need to dismiss here as the download wrapper does it for Android. + } + ignorePromise(f()) + } + ) + }) + } + + initSharedSubscriptions() } + +export {onEngineConnected, onEngineDisconnected} from './shared' diff --git a/shared/constants/platform-specific/push.native.tsx b/shared/constants/init/push-listener.native.tsx similarity index 87% rename from shared/constants/platform-specific/push.native.tsx rename to shared/constants/init/push-listener.native.tsx index 5da8dcdb800c..a30a3e2bfe0b 100644 --- a/shared/constants/platform-specific/push.native.tsx +++ b/shared/constants/init/push-listener.native.tsx @@ -1,7 +1,8 @@ -import * as T from '../types' -import {ignorePromise, timeoutPromise} from '../utils' +import * as T from '@/constants/types' +import {ignorePromise, timeoutPromise} from '@/constants/utils' import logger from '@/logger' -import {isAndroid, isIOS} from '../platform' +import {handleAppLink} from '@/constants/deeplinks' +import {isAndroid, isIOS} from '@/constants/platform' import { getRegistrationToken, setApplicationIconBadgeNumber, @@ -9,8 +10,9 @@ import { getInitialNotification, removeAllPendingNotificationRequests, } from 'react-native-kb' -import {storeRegistry} from '../store-registry' -import {DeviceEventEmitter} from 'react-native' +import {useConfigState} from '@/stores/config' +import {useLogoutState} from '@/stores/logout' +import {usePushState} from '@/stores/push' type DataCommon = { userInteraction: boolean @@ -162,7 +164,7 @@ const getStartupDetailsFromInitialPush = async () => { export const initPushListener = () => { // Permissions - storeRegistry.getStore('config').subscribe((s, old) => { + useConfigState.subscribe((s, old) => { if (s.mobileAppState === old.mobileAppState) return // Only recheck on foreground, not background if (s.mobileAppState !== 'active') { @@ -170,21 +172,21 @@ export const initPushListener = () => { return } logger.debug(`[PushCheck] checking on foreground`) - storeRegistry - .getState('push') + usePushState + .getState() .dispatch.checkPermissions() .then(() => {}) .catch(() => {}) }) // Token handling - storeRegistry.getStore('logout').subscribe((s, old) => { + useLogoutState.subscribe((s, old) => { if (s.version === old.version) return - storeRegistry.getState('push').dispatch.deleteToken(s.version) + usePushState.getState().dispatch.deleteToken(s.version) }) let lastCount = -1 - storeRegistry.getStore('config').subscribe((s, old) => { + useConfigState.subscribe((s, old) => { if (s.badgeState === old.badgeState) return if (!s.badgeState) return const count = s.badgeState.bigTeamBadgeCount + s.badgeState.smallTeamBadgeCount @@ -196,7 +198,7 @@ export const initPushListener = () => { lastCount = count }) - storeRegistry.getState('push').dispatch.initialPermissionsCheck() + usePushState.getState().dispatch.initialPermissionsCheck() const listenNative = async () => { const RNEmitter = getNativeEmitter() @@ -210,7 +212,7 @@ export const initPushListener = () => { logger.warn('[onNotification]: normalized notification is null/undefined') return } - storeRegistry.getState('push').dispatch.handlePush(notification) + usePushState.getState().dispatch.handlePush(notification) } try { @@ -224,14 +226,14 @@ export const initPushListener = () => { const token = payload?.token if (token) { logger.debug('[PushToken] received token via onPushToken event: ', token) - storeRegistry.getState('push').dispatch.setPushToken(token) + usePushState.getState().dispatch.setPushToken(token) } }) } if (isAndroid) { - DeviceEventEmitter.addListener('onShareData', (evt: {text?: string; localPaths?: Array}) => { - const {setAndroidShare} = storeRegistry.getState('config').dispatch + RNEmitter.addListener('onShareData', (evt: {text?: string; localPaths?: Array}) => { + const {setAndroidShare} = useConfigState.getState().dispatch const text = evt.text const urls = evt.localPaths @@ -244,7 +246,7 @@ export const initPushListener = () => { return } try { - storeRegistry.getState('deeplinks').dispatch.handleAppLink('keybase://incoming-share') + handleAppLink('keybase://incoming-share') } catch {} }) } @@ -256,7 +258,7 @@ export const initPushListener = () => { try { const pushToken = await getRegistrationToken() logger.debug('[PushToken] received new token: ', pushToken) - storeRegistry.getState('push').dispatch.setPushToken(pushToken) + usePushState.getState().dispatch.setPushToken(pushToken) } catch (e) { logger.warn('[PushToken] failed to get token (will retry later): ', e) // Token will be retrieved later when permissions are checked diff --git a/shared/constants/init/shared.tsx b/shared/constants/init/shared.tsx new file mode 100644 index 000000000000..830d47dd9019 --- /dev/null +++ b/shared/constants/init/shared.tsx @@ -0,0 +1,923 @@ +import * as EngineGen from '@/actions/engine-gen-gen' +import * as T from '../types' +import isEqual from 'lodash/isEqual' +import logger from '@/logger' +import * as Tabs from '@/constants/tabs' +import type * as UseArchiveStateType from '@/stores/archive' +import type * as UseAutoResetStateType from '@/stores/autoreset' +import type * as UseBotsStateType from '@/stores/bots' +import type * as UseChatStateType from '@/stores/chat2' +import type * as UseDevicesStateType from '@/stores/devices' +import type * as UseFSStateType from '@/stores/fs' +import type * as UseGitStateType from '@/stores/git' +import type * as UseNotificationsStateType from '@/stores/notifications' +import type * as UsePeopleStateType from '@/stores/people' +import type * as UsePinentryStateType from '@/stores/pinentry' +import type * as UseSettingsPasswordStateType from '@/stores/settings-password' +import type * as UseSignupStateType from '@/stores/signup' +import type * as UseTeamsStateType from '@/stores/teams' +import type * as UseTracker2StateType from '@/stores/tracker2' +import type * as UseUnlockFoldersStateType from '@/stores/unlock-folders' +import type * as UseUsersStateType from '@/stores/users' +import {createTBStore, getTBStore} from '@/stores/team-building' +import {getSelectedConversation} from '@/constants/chat2/common' +import {handleKeybaseLink} from '@/constants/deeplinks' +import {ignorePromise} from '../utils' +import {isMobile, serverConfigFileName} from '../platform' +import {storeRegistry} from '@/stores/store-registry' +import {useAutoResetState} from '@/stores/autoreset' +import {useAvatarState} from '@/common-adapters/avatar/store' +import {useChatState} from '@/stores/chat2' +import {useConfigState} from '@/stores/config' +import {useCryptoState} from '@/stores/crypto' +import {useCurrentUserState} from '@/stores/current-user' +import {useDaemonState} from '@/stores/daemon' +import {useDarkModeState} from '@/stores/darkmode' +import {useFSState} from '@/stores/fs' +import {useFollowerState} from '@/stores/followers' +import {useNotifState} from '@/stores/notifications' +import {useProfileState} from '@/stores/profile' +import {useProvisionState} from '@/stores/provision' +import {usePushState} from '@/stores/push' +import {useSettingsContactsState} from '@/stores/settings-contacts' +import {useSettingsEmailState} from '@/stores/settings-email' +import {useSettingsPhoneState} from '@/stores/settings-phone' +import {useSettingsState} from '@/stores/settings' +import {useSignupState} from '@/stores/signup' +import {useState as useRecoverPasswordState} from '@/stores/recover-password' +import {useTeamsState} from '@/stores/teams' +import {useTrackerState} from '@/stores/tracker2' +import {useUsersState} from '@/stores/users' +import {useWhatsNewState} from '@/stores/whats-new' +import {useRouterState} from '@/stores/router2' +import * as Util from '@/constants/router2' +import {setConvoDefer} from '@/stores/convostate' + +let _emitStartupOnLoadDaemonConnectedOnce = false +let _devicesLoaded = false +let _gitLoaded = false + +export const onEngineConnected = () => { + { + const registerUIs = async () => { + try { + await T.RPCGen.delegateUiCtlRegisterChatUIRpcPromise() + await T.RPCGen.delegateUiCtlRegisterLogUIRpcPromise() + logger.info('Registered Chat UI') + await T.RPCGen.delegateUiCtlRegisterHomeUIRpcPromise() + logger.info('Registered home UI') + await T.RPCGen.delegateUiCtlRegisterSecretUIRpcPromise() + logger.info('Registered secret ui') + await T.RPCGen.delegateUiCtlRegisterIdentify3UIRpcPromise() + logger.info('Registered identify ui') + await T.RPCGen.delegateUiCtlRegisterRekeyUIRpcPromise() + logger.info('Registered rekey ui') + } catch (error) { + logger.error('Error in registering UIs:', error) + } + } + ignorePromise(registerUIs()) + } + useConfigState.getState().dispatch.onEngineConnected() + storeRegistry.getState('daemon').dispatch.startHandshake() + { + const notifyCtl = async () => { + try { + // prettier-ignore + await T.RPCGen.notifyCtlSetNotificationsRpcPromise({ + channels: { + allowChatNotifySkips: true, app: true, audit: true, badges: true, chat: true, chatarchive: true, + chatattachments: true, chatdev: false, chatemoji: false, chatemojicross: false, chatkbfsedits: false, + deviceclone: false, ephemeral: false, favorites: false, featuredBots: true, kbfs: true, kbfsdesktop: !isMobile, + kbfslegacy: false, kbfsrequest: false, kbfssubscription: true, keyfamily: false, notifysimplefs: true, + paperkeys: false, pgp: true, reachability: true, runtimestats: true, saltpack: true, service: true, session: true, + team: true, teambot: false, tracking: true, users: true, wallet: false, + }, + }) + } catch (error) { + if (error) { + logger.warn('error in toggling notifications: ', error) + } + } + } + ignorePromise(notifyCtl()) + } +} + +export const onEngineDisconnected = () => { + const f = async () => { + await logger.dump() + } + ignorePromise(f()) + storeRegistry.getState('daemon').dispatch.setError(new Error('Disconnected')) +} + +// Initialize team building callbacks. Not ideal but keeping all the existing logic for now. +export const initTeamBuildingCallbacks = () => { + const commonCallbacks = { + onAddMembersWizardPushMembers: (members: Array) => { + useTeamsState.getState().dispatch.addMembersWizardPushMembers(members) + }, + onGetSettingsContactsImportEnabled: () => { + return useSettingsContactsState.getState().importEnabled + }, + onGetSettingsContactsUserCountryCode: () => { + return useSettingsContactsState.getState().userCountryCode + }, + onShowUserProfile: (username: string) => { + useProfileState.getState().dispatch.showUserProfile(username) + }, + onUsersGetBlockState: (usernames: ReadonlyArray) => { + useUsersState.getState().dispatch.getBlockState(usernames) + }, + onUsersUpdates: (infos: ReadonlyArray<{name: string; info: Partial}>) => { + useUsersState.getState().dispatch.updates(infos) + }, + } + + const namespaces: Array = ['chat2', 'crypto', 'teams', 'people'] + for (const namespace of namespaces) { + const store = createTBStore(namespace) + const currentState = store.getState() + store.setState({ + dispatch: { + ...currentState.dispatch, + defer: { + ...currentState.dispatch.defer, + ...commonCallbacks, + ...(namespace === 'chat2' + ? { + onFinishedTeamBuildingChat: users => { + storeRegistry.getState('chat').dispatch.onTeamBuildingFinished(users) + }, + } + : {}), + ...(namespace === 'crypto' + ? { + onFinishedTeamBuildingCrypto: users => { + useCryptoState.getState().dispatch.onTeamBuildingFinished(users) + }, + } + : {}), + }, + }, + }) + } +} + +export const initAutoResetCallbacks = () => { + const currentState = useAutoResetState.getState() + useAutoResetState.setState({ + dispatch: { + ...currentState.dispatch, + defer: { + onGetRecoverPasswordUsername: () => { + return storeRegistry.getState('recover-password').username + }, + onStartProvision: (username: string, fromReset: boolean) => { + storeRegistry.getState('provision').dispatch.startProvision(username, fromReset) + }, + }, + }, + }) +} + +export const initChat2Callbacks = () => { + const currentState = useChatState.getState() + useChatState.setState({ + dispatch: { + ...currentState.dispatch, + defer: { + onGetDaemonState: () => { + const daemonState = storeRegistry.getState('daemon') + return {dispatch: daemonState.dispatch, handshakeVersion: daemonState.handshakeVersion} + }, + onGetTeamsTeamIDToMembers: (teamID: T.Teams.TeamID) => { + return storeRegistry.getState('teams').teamIDToMembers.get(teamID) + }, + onGetUsersInfoMap: () => { + return storeRegistry.getState('users').infoMap + }, + onTeamsGetMembers: (teamID: T.Teams.TeamID) => { + storeRegistry.getState('teams').dispatch.getMembers(teamID) + }, + onTeamsUpdateTeamRetentionPolicy: (metas: ReadonlyArray) => { + storeRegistry.getState('teams').dispatch.updateTeamRetentionPolicy(metas) + }, + onUsersUpdates: (updates: ReadonlyArray<{name: string; info: Partial}>) => { + storeRegistry.getState('users').dispatch.updates(updates) + }, + }, + }, + }) +} + +export const initTeamsCallbacks = () => { + const currentState = useTeamsState.getState() + useTeamsState.setState({ + dispatch: { + ...currentState.dispatch, + defer: { + ...currentState.dispatch.defer, + onChatNavigateToInbox: (allowSwitchTab?: boolean) => { + storeRegistry.getState('chat').dispatch.navigateToInbox(allowSwitchTab) + }, + onChatPreviewConversation: ( + p: Parameters['dispatch']['previewConversation']>[0] + ) => { + storeRegistry.getState('chat').dispatch.previewConversation(p) + }, + onUsersUpdates: (updates: ReadonlyArray<{name: string; info: Partial}>) => { + storeRegistry.getState('users').dispatch.updates(updates) + }, + }, + }, + }) +} + +export const initFSCallbacks = () => { + const currentState = useFSState.getState() + useFSState.setState({ + dispatch: { + ...currentState.dispatch, + defer: { + ...currentState.dispatch.defer, + onBadgeApp: (key: 'kbfsUploading' | 'outOfSpace', on: boolean) => { + useNotifState.getState().dispatch.badgeApp(key, on) + }, + onSetBadgeCounts: (counts: Map) => { + useNotifState.getState().dispatch.setBadgeCounts(counts) + }, + }, + }, + }) +} + +export const initNotificationsCallbacks = () => { + const currentState = useNotifState.getState() + useNotifState.setState({ + dispatch: { + ...currentState.dispatch, + defer: { + ...currentState.dispatch.defer, + onFavoritesLoad: () => { + useFSState.getState().dispatch.favoritesLoad() + }, + }, + }, + }) +} + +export const initProfileCallbacks = () => { + const currentState = useProfileState.getState() + useProfileState.setState({ + dispatch: { + ...currentState.dispatch, + defer: { + ...currentState.dispatch.defer, + onTracker2GetDetails: (username: string) => { + return useTrackerState.getState().getDetails(username) + }, + onTracker2Load: ( + params: Parameters['dispatch']['load']>[0] + ) => { + useTrackerState.getState().dispatch.load(params) + }, + onTracker2ShowUser: (username: string, asTracker: boolean, skipNav?: boolean) => { + useTrackerState.getState().dispatch.showUser(username, asTracker, skipNav) + }, + onTracker2UpdateResult: (guiID: string, result: T.Tracker.DetailsState, reason?: string) => { + useTrackerState.getState().dispatch.updateResult(guiID, result, reason) + }, + }, + }, + }) +} + +export const initPushCallbacks = () => { + const currentState = usePushState.getState() + usePushState.setState({ + dispatch: { + ...currentState.dispatch, + defer: { + ...currentState.dispatch.defer, + onGetDaemonHandshakeState: () => { + return useDaemonState.getState().handshakeState + }, + onNavigateToThread: ( + conversationIDKey: T.Chat.ConversationIDKey, + reason: 'push' | 'extension', + pushBody?: string + ) => { + storeRegistry + .getConvoState(conversationIDKey) + .dispatch.navigateToThread(reason, undefined, pushBody) + }, + onShowUserProfile: (username: string) => { + useProfileState.getState().dispatch.showUserProfile(username) + }, + }, + }, + }) +} + +export const initRecoverPasswordCallbacks = () => { + const currentState = useRecoverPasswordState.getState() + useRecoverPasswordState.setState({ + dispatch: { + ...currentState.dispatch, + defer: { + ...currentState.dispatch.defer, + onProvisionCancel: (ignoreWarning?: boolean) => { + useProvisionState.getState().dispatch.dynamic.cancel?.(ignoreWarning) + }, + onStartAccountReset: (skipPassword: boolean, username: string) => { + useAutoResetState.getState().dispatch.startAccountReset(skipPassword, username) + }, + }, + }, + }) +} + +export const initSignupCallbacks = () => { + const currentState = useSignupState.getState() + useSignupState.setState({ + dispatch: { + ...currentState.dispatch, + defer: { + ...currentState.dispatch.defer, + onEditEmail: (p: {email: string; makeSearchable: boolean}) => { + useSettingsEmailState.getState().dispatch.editEmail(p) + }, + onShowPermissionsPrompt: (p: {justSignedUp?: boolean}) => { + usePushState.getState().dispatch.showPermissionsPrompt(p) + }, + }, + }, + }) +} + +export const initTracker2Callbacks = () => { + const currentState = useTrackerState.getState() + useTrackerState.setState({ + dispatch: { + ...currentState.dispatch, + defer: { + ...currentState.dispatch.defer, + onShowUserProfile: (username: string) => { + useProfileState.getState().dispatch.showUserProfile(username) + }, + onUsersUpdates: (updates: ReadonlyArray<{name: string; info: Partial}>) => { + useUsersState.getState().dispatch.updates(updates) + }, + }, + }, + }) +} + +export const initSettingsCallbacks = () => { + const currentState = useSettingsState.getState() + useSettingsState.setState({ + dispatch: { + ...currentState.dispatch, + defer: { + ...currentState.dispatch.defer, + getSettingsPhonePhones: () => { + return useSettingsPhoneState.getState().phones + }, + onSettingsEmailNotifyEmailsChanged: (emails: ReadonlyArray) => { + useSettingsEmailState.getState().dispatch.notifyEmailAddressEmailsChanged(emails) + }, + onSettingsPhoneSetNumbers: (phoneNumbers?: ReadonlyArray) => { + useSettingsPhoneState.getState().dispatch.setNumbers(phoneNumbers) + }, + }, + }, + }) +} + +export const initSharedSubscriptions = () => { + setConvoDefer({ + chatBlockButtonsMapHas: teamID => + storeRegistry.getState('chat').blockButtonsMap.has(teamID), + chatInboxLayoutSmallTeamsFirstConvID: () => + storeRegistry.getState('chat').inboxLayout?.smallTeams?.[0]?.convID, + chatInboxRefresh: reason => + storeRegistry.getState('chat').dispatch.inboxRefresh(reason), + chatMetasReceived: metas => + storeRegistry.getState('chat').dispatch.metasReceived(metas), + chatNavigateToInbox: () => + storeRegistry.getState('chat').dispatch.navigateToInbox(), + chatPaymentInfoReceived: (_messageID, paymentInfo) => + storeRegistry.getState('chat').dispatch.paymentInfoReceived(paymentInfo), + chatPreviewConversation: p => + storeRegistry.getState('chat').dispatch.previewConversation(p), + chatResetConversationErrored: () => + storeRegistry.getState('chat').dispatch.resetConversationErrored(), + chatUnboxRows: (convIDs, force) => + storeRegistry.getState('chat').dispatch.unboxRows(convIDs, force), + chatUpdateInfoPanel: (show, tab) => + storeRegistry.getState('chat').dispatch.updateInfoPanel(show, tab), + teamsGetMembers: teamID => + storeRegistry.getState('teams').dispatch.getMembers(teamID), + usersGetBio: username => + storeRegistry.getState('users').dispatch.getBio(username), + }) + useConfigState.subscribe((s, old) => { + if (s.loadOnStartPhase !== old.loadOnStartPhase) { + if (s.loadOnStartPhase === 'startupOrReloginButNotInARush') { + const getFollowerInfo = () => { + const {uid} = useCurrentUserState.getState() + logger.info(`getFollowerInfo: init; uid=${uid}`) + if (uid) { + // request follower info in the background + T.RPCGen.configRequestFollowingAndUnverifiedFollowersRpcPromise() + .then(() => {}) + .catch(() => {}) + } + } + + const updateServerConfig = async () => { + if (s.loggedIn) { + try { + await T.RPCGen.configUpdateLastLoggedInAndServerConfigRpcPromise({ + serverConfigPath: serverConfigFileName, + }) + } catch {} + } + } + + const updateTeams = () => { + useTeamsState.getState().dispatch.getTeams() + useTeamsState.getState().dispatch.refreshTeamRoleMap() + } + + const updateSettings = () => { + useSettingsContactsState.getState().dispatch.loadContactImportEnabled() + } + + const updateChat = async () => { + // On login lets load the untrusted inbox. This helps make some flows easier + if (useCurrentUserState.getState().username) { + const {inboxRefresh} = useChatState.getState().dispatch + inboxRefresh('bootstrap') + } + try { + const rows = await T.RPCGen.configGuiGetValueRpcPromise({path: 'ui.inboxSmallRows'}) + const ri = rows.i ?? -1 + if (ri > 0) { + useChatState.getState().dispatch.setInboxNumSmallRows(ri, true) + } + } catch {} + } + + getFollowerInfo() + ignorePromise(updateServerConfig()) + updateTeams() + updateSettings() + ignorePromise(updateChat()) + } + } + + if (s.gregorReachable !== old.gregorReachable) { + // Re-get info about our account if you log in/we're done handshaking/became reachable + if (s.gregorReachable === T.RPCGen.Reachable.yes) { + // not in waiting state + if (storeRegistry.getState('daemon').handshakeWaiters.size === 0) { + ignorePromise(storeRegistry.getState('daemon').dispatch.loadDaemonBootstrapStatus()) + } + storeRegistry.getState('teams').dispatch.eagerLoadTeams() + } + } + + if (s.installerRanCount !== old.installerRanCount) { + storeRegistry.getState('fs').dispatch.checkKbfsDaemonRpcStatus() + } + + if (s.loggedIn !== old.loggedIn) { + if (s.loggedIn) { + ignorePromise(storeRegistry.getState('daemon').dispatch.loadDaemonBootstrapStatus()) + storeRegistry.getState('fs').dispatch.checkKbfsDaemonRpcStatus() + } + storeRegistry + .getState('daemon') + .dispatch.loadDaemonAccounts( + s.configuredAccounts.length, + s.loggedIn, + useConfigState.getState().dispatch.refreshAccounts + ) + if (!s.loggedInCausedbyStartup) { + ignorePromise(useConfigState.getState().dispatch.refreshAccounts()) + } + } + + if (s.mobileAppState !== old.mobileAppState) { + if (s.mobileAppState === 'background' && storeRegistry.getState('chat').inboxSearch) { + storeRegistry.getState('chat').dispatch.toggleInboxSearch(false) + } + } + + if (s.revokedTrigger !== old.revokedTrigger) { + storeRegistry + .getState('daemon') + .dispatch.loadDaemonAccounts( + s.configuredAccounts.length, + s.loggedIn, + useConfigState.getState().dispatch.refreshAccounts + ) + } + + if (s.configuredAccounts !== old.configuredAccounts) { + const updates = s.configuredAccounts.map(account => ({ + info: {fullname: account.fullname ?? ''}, + name: account.username, + })) + if (updates.length > 0) { + storeRegistry.getState('users').dispatch.updates(updates) + } + } + + if (s.gregorPushState !== old.gregorPushState) { + const lastSeenItem = s.gregorPushState.find(i => i.item.category === 'whatsNewLastSeenVersion') + useWhatsNewState.getState().dispatch.updateLastSeen(lastSeenItem) + } + + if (s.active !== old.active) { + const cs = storeRegistry.getConvoState(getSelectedConversation()) + cs.dispatch.markThreadAsRead() + } + }) + + useDaemonState.subscribe((s, old) => { + if (s.handshakeVersion !== old.handshakeVersion) { + useDarkModeState.getState().dispatch.loadDarkPrefs() + storeRegistry.getState('chat').dispatch.loadStaticConfig() + const configState = useConfigState.getState() + s.dispatch.loadDaemonAccounts( + configState.configuredAccounts.length, + configState.loggedIn, + useConfigState.getState().dispatch.refreshAccounts + ) + } + + if (s.bootstrapStatus !== old.bootstrapStatus) { + const bootstrap = s.bootstrapStatus + if (bootstrap) { + const {deviceID, deviceName, loggedIn, uid, username, userReacjis} = bootstrap + useCurrentUserState.getState().dispatch.setBootstrap({deviceID, deviceName, uid, username}) + + const configDispatch = useConfigState.getState().dispatch + if (username) { + configDispatch.setDefaultUsername(username) + } + if (loggedIn) { + configDispatch.setUserSwitching(false) + } + configDispatch.setLoggedIn(loggedIn, false) + + if (bootstrap.httpSrvInfo) { + configDispatch.setHTTPSrvInfo(bootstrap.httpSrvInfo.address, bootstrap.httpSrvInfo.token) + } + + storeRegistry.getState('chat').dispatch.updateUserReacjis(userReacjis) + } + } + + if (s.handshakeState !== old.handshakeState) { + if (s.handshakeState === 'done') { + if (!_emitStartupOnLoadDaemonConnectedOnce) { + _emitStartupOnLoadDaemonConnectedOnce = true + useConfigState.getState().dispatch.loadOnStart('connectedToDaemonForFirstTime') + } + } + } + }) + + useProvisionState.subscribe((s, old) => { + if (s.startProvisionTrigger !== old.startProvisionTrigger) { + useConfigState.getState().dispatch.setLoginError() + useConfigState.getState().dispatch.resetRevokedSelf() + const f = async () => { + // If we're logged in, we're coming from the user switcher; log out first to prevent the service from getting out of sync with the GUI about our logged-in-ness + if (useConfigState.getState().loggedIn) { + await T.RPCGen.loginLogoutRpcPromise({force: false, keepSecrets: true}, 'config:loginAsOther') + } + } + ignorePromise(f()) + } + }) + + useRouterState.subscribe((s, old) => { + const next = s.navState as Util.NavState + const prev = old.navState as Util.NavState + if (prev === next) return + + const namespaces = ['chat2', 'crypto', 'teams', 'people'] as const + const namespaceToRoute = new Map([ + ['chat2', 'chatNewChat'], + ['crypto', 'cryptoTeamBuilder'], + ['teams', 'teamsTeamBuilder'], + ['people', 'peopleTeamBuilder'], + ]) + for (const namespace of namespaces) { + const wasTeamBuilding = namespaceToRoute.get(namespace) === Util.getVisibleScreen(prev)?.name + if (wasTeamBuilding) { + // team building or modal on top of that still + const isTeamBuilding = namespaceToRoute.get(namespace) === Util.getVisibleScreen(next)?.name + if (!isTeamBuilding) { + getTBStore(namespace).dispatch.cancelTeamBuilding() + } + } + } + + // Clear critical update when we nav away from tab + if ( + prev && + Util.getTab(prev) === Tabs.fsTab && + next && + Util.getTab(next) !== Tabs.fsTab && + storeRegistry.getState('fs').criticalUpdate + ) { + const {dispatch} = storeRegistry.getState('fs') + dispatch.setCriticalUpdate(false) + } + const fsRrouteNames = ['fsRoot', 'barePreview'] + const wasScreen = fsRrouteNames.includes(Util.getVisibleScreen(prev)?.name ?? '') + const isScreen = fsRrouteNames.includes(Util.getVisibleScreen(next)?.name ?? '') + if (wasScreen !== isScreen) { + const {dispatch} = storeRegistry.getState('fs') + if (wasScreen) { + dispatch.userOut() + } else { + dispatch.userIn() + } + } + + // Clear "just signed up email" when you leave the people tab after signup + if ( + prev && + Util.getTab(prev) === Tabs.peopleTab && + next && + Util.getTab(next) !== Tabs.peopleTab && + storeRegistry.getState('signup').justSignedUpEmail + ) { + storeRegistry.getState('signup').dispatch.clearJustSignedUpEmail() + } + + if (prev && Util.getTab(prev) === Tabs.peopleTab && next && Util.getTab(next) !== Tabs.peopleTab) { + storeRegistry.getState('people').dispatch.markViewed() + } + + if (prev && Util.getTab(prev) === Tabs.teamsTab && next && Util.getTab(next) !== Tabs.teamsTab) { + storeRegistry.getState('teams').dispatch.clearNavBadges() + } + + // Clear "check your inbox" in settings when you leave the settings tab + if ( + prev && + Util.getTab(prev) === Tabs.settingsTab && + next && + Util.getTab(next) !== Tabs.settingsTab && + storeRegistry.getState('settings-email').addedEmail + ) { + storeRegistry.getState('settings-email').dispatch.resetAddedEmail() + } + + storeRegistry.getState('chat').dispatch.onRouteChanged(prev, next) + }) + + initAutoResetCallbacks() + initChat2Callbacks() + initTeamBuildingCallbacks() + initTeamsCallbacks() + initFSCallbacks() + initNotificationsCallbacks() + initProfileCallbacks() + initPushCallbacks() + initRecoverPasswordCallbacks() + initSettingsCallbacks() + initSignupCallbacks() + initTracker2Callbacks() +} + +// This is to defer loading stores we don't need immediately. +export const _onEngineIncoming = (action: EngineGen.Actions) => { + switch (action.type) { + case EngineGen.keybase1NotifySimpleFSSimpleFSArchiveStatusChanged: + case EngineGen.chat1NotifyChatChatArchiveComplete: + case EngineGen.chat1NotifyChatChatArchiveProgress: + { + const {useArchiveState} = require('@/stores/archive') as typeof UseArchiveStateType + useArchiveState.getState().dispatch.onEngineIncomingImpl(action) + } + break + case EngineGen.keybase1NotifyBadgesBadgeState: + { + const {useAutoResetState} = require('@/stores/autoreset') as typeof UseAutoResetStateType + useAutoResetState.getState().dispatch.onEngineIncomingImpl(action) + + const {badgeState} = action.payload.params + const {newDevices, revokedDevices} = badgeState + const hasValue = (newDevices?.length ?? 0) + (revokedDevices?.length ?? 0) > 0 + if (_devicesLoaded || hasValue) { + _devicesLoaded = true + const {useDevicesState} = require('@/stores/devices') as typeof UseDevicesStateType + useDevicesState.getState().dispatch.onEngineIncomingImpl(action) + } + + const badges = new Set(badgeState.newGitRepoGlobalUniqueIDs) + if (_gitLoaded || badges.size) { + _gitLoaded = true + const {useGitState} = require('@/stores/git') as typeof UseGitStateType + useGitState.getState().dispatch.onEngineIncomingImpl(action) + } + + const {useNotifState} = require('@/stores/notifications') as typeof UseNotificationsStateType + useNotifState.getState().dispatch.onEngineIncomingImpl(action) + + const {useTeamsState} = require('@/stores/teams') as typeof UseTeamsStateType + useTeamsState.getState().dispatch.onEngineIncomingImpl(action) + + const {useChatState} = require('@/stores/chat2') as typeof UseChatStateType + useChatState.getState().dispatch.onEngineIncomingImpl(action) + } + break + case EngineGen.chat1ChatUiChatShowManageChannels: + case EngineGen.keybase1NotifyTeamTeamMetadataUpdate: + case EngineGen.chat1NotifyChatChatWelcomeMessageLoaded: + case EngineGen.keybase1NotifyTeamTeamTreeMembershipsPartial: + case EngineGen.keybase1NotifyTeamTeamTreeMembershipsDone: + case EngineGen.keybase1NotifyTeamTeamRoleMapChanged: + case EngineGen.keybase1NotifyTeamTeamChangedByID: + case EngineGen.keybase1NotifyTeamTeamDeleted: + case EngineGen.keybase1NotifyTeamTeamExit: + case EngineGen.keybase1GregorUIPushState: + { + const {useTeamsState} = require('@/stores/teams') as typeof UseTeamsStateType + useTeamsState.getState().dispatch.onEngineIncomingImpl(action) + } + break + case EngineGen.keybase1NotifyFeaturedBotsFeaturedBotsUpdate: + { + const {useBotsState} = require('@/stores/bots') as typeof UseBotsStateType + useBotsState.getState().dispatch.onEngineIncomingImpl(action) + } + break + case EngineGen.keybase1NotifyFSFSOverallSyncStatusChanged: + case EngineGen.keybase1NotifyFSFSSubscriptionNotifyPath: + case EngineGen.keybase1NotifyFSFSSubscriptionNotify: + { + const {useFSState} = require('@/stores/fs') as typeof UseFSStateType + useFSState.getState().dispatch.onEngineIncomingImpl(action) + } + break + case EngineGen.keybase1NotifyAuditRootAuditError: + case EngineGen.keybase1NotifyAuditBoxAuditError: + { + const {useNotifState} = require('@/stores/notifications') as typeof UseNotificationsStateType + useNotifState.getState().dispatch.onEngineIncomingImpl(action) + } + break + case EngineGen.keybase1HomeUIHomeUIRefresh: + case EngineGen.keybase1NotifyEmailAddressEmailAddressVerified: + { + const {usePeopleState} = require('@/stores/people') as typeof UsePeopleStateType + usePeopleState.getState().dispatch.onEngineIncomingImpl(action) + const emailAddress = action.payload.params?.emailAddress + if (emailAddress) { + storeRegistry.getState('settings-email').dispatch.notifyEmailVerified(emailAddress) + } + const {useSignupState} = require('@/stores/signup') as typeof UseSignupStateType + useSignupState.getState().dispatch.onEngineIncomingImpl(action) + } + break + case EngineGen.keybase1SecretUiGetPassphrase: + { + const {usePinentryState} = require('@/stores/pinentry') as typeof UsePinentryStateType + usePinentryState.getState().dispatch.onEngineIncomingImpl(action) + } + break + case EngineGen.keybase1NotifyUsersPasswordChanged: + { + const randomPW = action.payload.params.state === T.RPCGen.PassphraseState.random + const {usePWState} = require('@/stores/settings-password') as typeof UseSettingsPasswordStateType + usePWState.getState().dispatch.notifyUsersPasswordChanged(randomPW) + } + break + case EngineGen.keybase1NotifyPhoneNumberPhoneNumbersChanged: { + const {list} = action.payload.params + storeRegistry + .getState('settings-phone') + .dispatch.notifyPhoneNumberPhoneNumbersChanged(list ?? undefined) + break + } + case EngineGen.keybase1NotifyEmailAddressEmailsChanged: { + const list = action.payload.params.list ?? [] + storeRegistry.getState('settings-email').dispatch.notifyEmailAddressEmailsChanged(list) + break + } + case EngineGen.chat1ChatUiChatInboxFailed: + case EngineGen.chat1NotifyChatChatSetConvSettings: + case EngineGen.chat1NotifyChatChatAttachmentUploadStart: + case EngineGen.chat1NotifyChatChatPromptUnfurl: + case EngineGen.chat1NotifyChatChatPaymentInfo: + case EngineGen.chat1NotifyChatChatRequestInfo: + case EngineGen.chat1NotifyChatChatAttachmentDownloadProgress: + case EngineGen.chat1NotifyChatChatAttachmentDownloadComplete: + case EngineGen.chat1NotifyChatChatAttachmentUploadProgress: + case EngineGen.chat1ChatUiChatCommandMarkdown: + case EngineGen.chat1ChatUiChatGiphyToggleResultWindow: + case EngineGen.chat1ChatUiChatCommandStatus: + case EngineGen.chat1ChatUiChatBotCommandsUpdateStatus: + case EngineGen.chat1ChatUiChatGiphySearchResults: + case EngineGen.chat1NotifyChatChatParticipantsInfo: + case EngineGen.chat1ChatUiChatMaybeMentionUpdate: + case EngineGen.chat1NotifyChatChatConvUpdate: + case EngineGen.chat1ChatUiChatCoinFlipStatus: + case EngineGen.chat1NotifyChatChatThreadsStale: + case EngineGen.chat1NotifyChatChatSubteamRename: + case EngineGen.chat1NotifyChatChatTLFFinalize: + case EngineGen.chat1NotifyChatChatIdentifyUpdate: + case EngineGen.chat1ChatUiChatInboxUnverified: + case EngineGen.chat1NotifyChatChatInboxSyncStarted: + case EngineGen.chat1NotifyChatChatInboxSynced: + case EngineGen.chat1ChatUiChatInboxLayout: + case EngineGen.chat1NotifyChatChatInboxStale: + case EngineGen.chat1ChatUiChatInboxConversation: + case EngineGen.chat1NotifyChatNewChatActivity: + case EngineGen.chat1NotifyChatChatTypingUpdate: + case EngineGen.chat1NotifyChatChatSetConvRetention: + case EngineGen.chat1NotifyChatChatSetTeamRetention: + { + const {useChatState} = require('@/stores/chat2') as typeof UseChatStateType + useChatState.getState().dispatch.onEngineIncomingImpl(action) + } + break + case EngineGen.keybase1NotifyServiceHandleKeybaseLink: + { + const {link, deferred} = action.payload.params + if (deferred && !link.startsWith('keybase://team-invite-link/')) { + return + } + handleKeybaseLink(link) + } + break + case EngineGen.keybase1NotifyTeamAvatarUpdated: { + const {name} = action.payload.params + useAvatarState.getState().dispatch.updated(name) + break + } + case EngineGen.keybase1NotifyTrackingTrackingChanged: { + const {isTracking, username} = action.payload.params + useFollowerState.getState().dispatch.updateFollowing(username, isTracking) + const {useTrackerState} = require('@/stores/tracker2') as typeof UseTracker2StateType + useTrackerState.getState().dispatch.onEngineIncomingImpl(action) + break + } + case EngineGen.keybase1NotifyTrackingTrackingInfo: { + const {uid, followers: _newFollowers, followees: _newFollowing} = action.payload.params + if (useCurrentUserState.getState().uid !== uid) { + break + } + const newFollowers = new Set(_newFollowers) + const newFollowing = new Set(_newFollowing) + const {following: oldFollowing, followers: oldFollowers, dispatch} = useFollowerState.getState() + const following = isEqual(newFollowing, oldFollowing) ? oldFollowing : newFollowing + const followers = isEqual(newFollowers, oldFollowers) ? oldFollowers : newFollowers + dispatch.replace(followers, following) + break + } + case EngineGen.keybase1Identify3UiIdentify3Result: + case EngineGen.keybase1Identify3UiIdentify3ShowTracker: + case EngineGen.keybase1NotifyUsersUserChanged: + case EngineGen.keybase1NotifyTrackingNotifyUserBlocked: + case EngineGen.keybase1Identify3UiIdentify3UpdateRow: + case EngineGen.keybase1Identify3UiIdentify3UserReset: + case EngineGen.keybase1Identify3UiIdentify3UpdateUserCard: + case EngineGen.keybase1Identify3UiIdentify3Summary: + { + const {useTrackerState} = require('@/stores/tracker2') as typeof UseTracker2StateType + useTrackerState.getState().dispatch.onEngineIncomingImpl(action) + } + { + const {useUsersState} = require('@/stores/users') as typeof UseUsersStateType + useUsersState.getState().dispatch.onEngineIncomingImpl(action) + } + break + case EngineGen.keybase1NotifyUsersIdentifyUpdate: + { + const {useUsersState} = require('@/stores/users') as typeof UseUsersStateType + useUsersState.getState().dispatch.onEngineIncomingImpl(action) + } + break + case EngineGen.keybase1RekeyUIRefresh: + case EngineGen.keybase1RekeyUIDelegateRekeyUI: + { + const {useUnlockFoldersState} = require('@/stores/unlock-folders') as typeof UseUnlockFoldersStateType + useUnlockFoldersState.getState().dispatch.onEngineIncomingImpl(action) + } + break + default: + } + useConfigState.getState().dispatch.onEngineIncoming(action) +} diff --git a/shared/constants/notifications/util.tsx b/shared/constants/notifications/util.tsx deleted file mode 100644 index acc95a39dbf0..000000000000 --- a/shared/constants/notifications/util.tsx +++ /dev/null @@ -1,69 +0,0 @@ -import * as T from '../types' -import {ignorePromise} from '../utils' -import * as EngineGen from '@/actions/engine-gen-gen' -import {storeRegistry} from '../store-registry' -import {isMobile} from '../platform' -import logger from '@/logger' - -export const onEngineConnected = () => { - const f = async () => { - try { - await T.RPCGen.notifyCtlSetNotificationsRpcPromise({ - channels: { - allowChatNotifySkips: true, - app: true, - audit: true, - badges: true, - chat: true, - chatarchive: true, - chatattachments: true, - chatdev: false, - chatemoji: false, - chatemojicross: false, - chatkbfsedits: false, - deviceclone: false, - ephemeral: false, - favorites: false, - featuredBots: true, - kbfs: true, - kbfsdesktop: !isMobile, - kbfslegacy: false, - kbfsrequest: false, - kbfssubscription: true, - keyfamily: false, - notifysimplefs: true, - paperkeys: false, - pgp: true, - reachability: true, - runtimestats: true, - saltpack: true, - service: true, - session: true, - team: true, - teambot: false, - tracking: true, - users: true, - wallet: false, - }, - }) - } catch (error) { - if (error) { - logger.warn('error in toggling notifications: ', error) - } - } - } - ignorePromise(f()) -} - -export const onEngineIncoming = (action: EngineGen.Actions) => { - switch (action.type) { - case EngineGen.keybase1NotifyAuditRootAuditError: - case EngineGen.keybase1NotifyAuditBoxAuditError: - case EngineGen.keybase1NotifyBadgesBadgeState: - { - storeRegistry.getState('notifications').dispatch.onEngineIncomingImpl(action) - } - break - default: - } -} diff --git a/shared/constants/people/util.tsx b/shared/constants/people/util.tsx deleted file mode 100644 index 8deb5593c959..000000000000 --- a/shared/constants/people/util.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import * as T from '../types' -import {ignorePromise} from '../utils' -import * as EngineGen from '@/actions/engine-gen-gen' -import {storeRegistry} from '../store-registry' - -export const onEngineConnected = () => { - const f = async () => { - try { - await T.RPCGen.delegateUiCtlRegisterHomeUIRpcPromise() - console.log('Registered home UI') - } catch (error) { - console.warn('Error in registering home UI:', error) - } - } - ignorePromise(f()) -} - -export const onEngineIncoming = (action: EngineGen.Actions) => { - switch (action.type) { - case EngineGen.keybase1HomeUIHomeUIRefresh: - case EngineGen.keybase1NotifyEmailAddressEmailAddressVerified: - { - storeRegistry.getState('people').dispatch.onEngineIncomingImpl(action) - } - break - default: - } -} diff --git a/shared/constants/pinentry/util.tsx b/shared/constants/pinentry/util.tsx deleted file mode 100644 index 5ec547f0d1cf..000000000000 --- a/shared/constants/pinentry/util.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import * as T from '../types' -import {ignorePromise} from '../utils' -import * as EngineGen from '@/actions/engine-gen-gen' -import {storeRegistry} from '../store-registry' -import logger from '@/logger' - -export const onEngineConnected = () => { - const f = async () => { - try { - await T.RPCGen.delegateUiCtlRegisterSecretUIRpcPromise() - logger.info('Registered secret ui') - } catch (error) { - logger.warn('error in registering secret ui: ', error) - } - } - ignorePromise(f()) -} - -export const onEngineIncoming = (action: EngineGen.Actions) => { - switch (action.type) { - case EngineGen.keybase1SecretUiGetPassphrase: - { - storeRegistry.getState('pinentry').dispatch.onEngineIncomingImpl(action) - } - break - default: - } -} diff --git a/shared/constants/platform-specific/index.desktop.tsx b/shared/constants/platform-specific/index.desktop.tsx deleted file mode 100644 index b9f8f70f7ade..000000000000 --- a/shared/constants/platform-specific/index.desktop.tsx +++ /dev/null @@ -1,290 +0,0 @@ -import * as Chat from '../chat2' -import {ignorePromise} from '../utils' -import {storeRegistry} from '../store-registry' -import * as ConfigConstants from '../config' -import * as EngineGen from '@/actions/engine-gen-gen' -import * as T from '../types' -import InputMonitor from './input-monitor.desktop' -import KB2 from '@/util/electron.desktop' -import logger from '@/logger' -import type {RPCError} from '@/util/errors' -import {getEngine} from '@/engine' -import {isLinux, isWindows} from '../platform.desktop' -import {kbfsNotification} from './kbfs-notifications' -import {skipAppFocusActions} from '@/local-debug.desktop' -import NotifyPopup from '@/util/notify-popup' -import {noKBFSFailReason} from '@/constants/config/util' -import {wrapErrors} from '@/util/debug' - -const {showMainWindow, activeChanged, requestWindowsStartService, dumpNodeLogger} = KB2.functions -const {quitApp, exitApp, setOpenAtLogin, ctlQuit, copyToClipboard} = KB2.functions - -export const requestPermissionsToWrite = async () => { - return Promise.resolve(true) -} - -export function showShareActionSheet() { - throw new Error('Show Share Action - unsupported on this platform') -} -export async function saveAttachmentToCameraRoll() { - return Promise.reject(new Error('Save Attachment to camera roll - unsupported on this platform')) -} - -export const requestLocationPermission = async () => Promise.resolve() -export const watchPositionForMap = async () => Promise.resolve(() => {}) - -const maybePauseVideos = () => { - const {appFocused} = storeRegistry.getState('config') - const videos = document.querySelectorAll('video') - const allVideos = Array.from(videos) - - allVideos.forEach(v => { - if (appFocused) { - if (v.hasAttribute('data-focus-paused')) { - if (v.paused) { - v.play() - .then(() => {}) - .catch(() => {}) - } - } - } else { - // only pause looping videos - if (!v.paused && v.hasAttribute('loop') && v.hasAttribute('autoplay')) { - v.setAttribute('data-focus-paused', 'true') - v.pause() - } - } - }) -} - -export const dumpLogs = async (reason?: string) => { - await logger.dump() - await (dumpNodeLogger?.() ?? Promise.resolve([])) - // quit as soon as possible - if (reason === 'quitting through menu') { - ctlQuit?.() - } -} - -export const initPlatformListener = () => { - storeRegistry.getStore('config').setState(s => { - s.dispatch.dynamic.dumpLogsNative = dumpLogs - s.dispatch.dynamic.showMainNative = wrapErrors(() => showMainWindow?.()) - s.dispatch.dynamic.copyToClipboard = wrapErrors((s: string) => copyToClipboard?.(s)) - s.dispatch.dynamic.onEngineConnectedDesktop = wrapErrors(() => { - // Introduce ourselves to the service - const f = async () => { - await T.RPCGen.configHelloIAmRpcPromise({details: KB2.constants.helloDetails}) - } - ignorePromise(f()) - }) - - s.dispatch.dynamic.onEngineIncomingDesktop = wrapErrors((action: EngineGen.Actions) => { - switch (action.type) { - case EngineGen.keybase1LogsendPrepareLogsend: { - const f = async () => { - const response = action.payload.response - try { - await dumpLogs() - } finally { - response.result() - } - } - ignorePromise(f()) - break - } - case EngineGen.keybase1NotifyAppExit: - console.log('App exit requested') - exitApp?.(0) - break - case EngineGen.keybase1NotifyFSFSActivity: - kbfsNotification(action.payload.params.notification, NotifyPopup) - break - case EngineGen.keybase1NotifyPGPPgpKeyInSecretStoreFile: { - const f = async () => { - try { - await T.RPCGen.pgpPgpStorageDismissRpcPromise() - } catch (err) { - console.warn('Error in sending pgpPgpStorageDismissRpc:', err) - } - } - ignorePromise(f()) - break - } - case EngineGen.keybase1NotifyServiceShutdown: { - const {code} = action.payload.params - if (isWindows && code !== (T.RPCGen.ExitCode.restart as number)) { - console.log('Quitting due to service shutdown with code: ', code) - // Quit just the app, not the service - quitApp?.() - } - break - } - - case EngineGen.keybase1LogUiLog: { - const {params} = action.payload - const {level, text} = params - logger.info('keybase.1.logUi.log:', params.text.data) - if (level >= T.RPCGen.LogLevel.error) { - NotifyPopup(text.data) - } - break - } - - case EngineGen.keybase1NotifySessionClientOutOfDate: { - const {upgradeTo, upgradeURI, upgradeMsg} = action.payload.params - const body = upgradeMsg || `Please update to ${upgradeTo} by going to ${upgradeURI}` - NotifyPopup('Client out of date!', {body}, 60 * 60) - // This is from the API server. Consider notifications from server always critical. - storeRegistry - .getState('config') - .dispatch.setOutOfDate({critical: true, message: upgradeMsg, outOfDate: true, updating: false}) - break - } - default: - } - }) - }) - - storeRegistry.getStore('config').subscribe((s, old) => { - if (s.loggedIn === old.loggedIn) return - storeRegistry.getState('config').dispatch.osNetworkStatusChanged(navigator.onLine, 'notavailable', true) - }) - - storeRegistry.getStore('config').subscribe((s, prev) => { - if (s.appFocused !== prev.appFocused) { - maybePauseVideos() - } - }) - - storeRegistry.getStore('daemon').subscribe((s, old) => { - if (s.handshakeVersion === old.handshakeVersion) return - if (!isWindows) return - - const f = async () => { - const waitKey = 'pipeCheckFail' - const version = s.handshakeVersion - const {wait} = storeRegistry.getState('daemon').dispatch - wait(waitKey, version, true) - try { - logger.info('Checking RPC ownership') - if (KB2.functions.winCheckRPCOwnership) { - await KB2.functions.winCheckRPCOwnership() - } - wait(waitKey, version, false) - } catch (error_) { - // error will be logged in bootstrap check - getEngine().reset() - const error = error_ as RPCError - wait(waitKey, version, false, error.message || 'windows pipe owner fail', true) - } - } - ignorePromise(f()) - }) - - const handleWindowFocusEvents = () => { - const handle = (appFocused: boolean) => { - if (skipAppFocusActions) { - console.log('Skipping app focus actions!') - } else { - storeRegistry.getState('config').dispatch.changedFocus(appFocused) - } - } - window.addEventListener('focus', () => handle(true)) - window.addEventListener('blur', () => handle(false)) - } - handleWindowFocusEvents() - - const setupReachabilityWatcher = () => { - window.addEventListener('online', () => - storeRegistry.getState('config').dispatch.osNetworkStatusChanged(true, 'notavailable') - ) - window.addEventListener('offline', () => - storeRegistry.getState('config').dispatch.osNetworkStatusChanged(false, 'notavailable') - ) - } - setupReachabilityWatcher() - - storeRegistry.getStore('config').subscribe((s, old) => { - if (s.openAtLogin === old.openAtLogin) return - const {openAtLogin} = s - const f = async () => { - if (__DEV__) { - console.log('onSetOpenAtLogin disabled for dev mode') - return - } else { - await T.RPCGen.configGuiSetValueRpcPromise({ - path: ConfigConstants.openAtLoginKey, - value: {b: openAtLogin, isNull: false}, - }) - } - if (isLinux || isWindows) { - const enabled = - (await T.RPCGen.ctlGetOnLoginStartupRpcPromise()) === T.RPCGen.OnLoginStartupStatus.enabled - if (enabled !== openAtLogin) { - try { - await T.RPCGen.ctlSetOnLoginStartupRpcPromise({enabled: openAtLogin}) - } catch (error_) { - const error = error_ as RPCError - logger.warn(`Error in sending ctlSetOnLoginStartup: ${error.message}`) - } - } - } else { - logger.info(`Login item settings changed! now ${openAtLogin ? 'on' : 'off'}`) - await setOpenAtLogin?.(openAtLogin) - } - } - ignorePromise(f()) - }) - - storeRegistry.getStore('daemon').subscribe((s, old) => { - if (s.handshakeState === old.handshakeState || s.handshakeState !== 'done') return - storeRegistry.getState('config').dispatch.setStartupDetails({ - conversation: Chat.noConversationIDKey, - followUser: '', - link: '', - tab: undefined, - }) - }) - - if (isLinux) { - storeRegistry.getState('config').dispatch.initUseNativeFrame() - } - storeRegistry.getState('config').dispatch.initNotifySound() - storeRegistry.getState('config').dispatch.initForceSmallNav() - storeRegistry.getState('config').dispatch.initOpenAtLogin() - storeRegistry.getState('config').dispatch.initAppUpdateLoop() - - storeRegistry.getStore('profile').setState(s => { - s.dispatch.editAvatar = () => { - storeRegistry - .getState('router') - .dispatch.navigateAppend({props: {image: undefined}, selected: 'profileEditAvatar'}) - } - }) - - const initializeInputMonitor = () => { - const inputMonitor = new InputMonitor() - inputMonitor.notifyActive = (userActive: boolean) => { - if (skipAppFocusActions) { - console.log('Skipping app focus actions!') - } else { - storeRegistry.getState('active').dispatch.setActive(userActive) - // let node thread save file - activeChanged?.(Date.now(), userActive) - } - } - } - initializeInputMonitor() - - storeRegistry.getStore('daemon').setState(s => { - s.dispatch.onRestartHandshakeNative = () => { - const {handshakeFailedReason} = storeRegistry.getState('daemon') - if (isWindows && handshakeFailedReason === noKBFSFailReason) { - requestWindowsStartService?.() - } - } - }) - - ignorePromise(storeRegistry.getState('fs').dispatch.setupSubscriptions()) -} diff --git a/shared/constants/recover-password/utils.tsx b/shared/constants/recover-password/utils.tsx deleted file mode 100644 index 8b137891791f..000000000000 --- a/shared/constants/recover-password/utils.tsx +++ /dev/null @@ -1 +0,0 @@ - diff --git a/shared/constants/router2/util.tsx b/shared/constants/router2.tsx similarity index 98% rename from shared/constants/router2/util.tsx rename to shared/constants/router2.tsx index f47b6a479a5e..5da352fedcb7 100644 --- a/shared/constants/router2/util.tsx +++ b/shared/constants/router2.tsx @@ -1,6 +1,6 @@ import type * as React from 'react' -import type * as T from '../types' -import * as Tabs from '../tabs' +import type * as T from './types' +import * as Tabs from './tabs' import { StackActions, CommonActions, @@ -10,11 +10,11 @@ import { type NavigationState, } from '@react-navigation/core' import type {NavigateAppendType, RouteKeys, RootParamList as KBRootParamList} from '@/router-v2/route-params' -import type {GetOptionsRet} from '../types/router2' +import type {GetOptionsRet} from './types/router2' import {produce} from 'immer' import isEqual from 'lodash/isEqual' -import {isMobile, isTablet} from '../platform' -import {shallowEqual, type ViewPropsToPageProps} from '../utils' +import {isMobile, isTablet} from './platform' +import {shallowEqual, type ViewPropsToPageProps} from './utils' import {registerDebugClear} from '@/util/debug' export const navigationRef = createNavigationContainerRef() @@ -440,4 +440,3 @@ export const appendEncryptRecipientsBuilder = () => { selected: 'cryptoTeamBuilder', }) } - diff --git a/shared/constants/router2/index.tsx b/shared/constants/router2/index.tsx deleted file mode 100644 index 24c72281cad0..000000000000 --- a/shared/constants/router2/index.tsx +++ /dev/null @@ -1,182 +0,0 @@ -import type * as T from '../types' -import * as Z from '@/util/zustand' -import * as Tabs from '../tabs' -import type {RouteKeys} from '@/router-v2/route-params' -import {storeRegistry} from '../store-registry' -import * as Util from './util' - -export { - type NavState, - getTab, - navigationRef, - getRootState, - _getNavigator, - logState, - getVisiblePath, - getModalStack, - getVisibleScreen, - navToProfile, - navToThread, - getRouteTab, - getRouteLoggedIn, - useSafeFocusEffect, - makeScreen, -} from './util' -export type {PathParam, Navigator} from './util' - -type Store = T.Immutable<{ - navState?: unknown -}> - -const initialStore: Store = { - navState: undefined, -} - -export interface State extends Store { - dispatch: { - clearModals: () => void - dynamic: { - tabLongPress?: (tab: string) => void - } - navigateAppend: (path: Util.PathParam, replace?: boolean) => void - navigateUp: () => void - navUpToScreen: (name: RouteKeys) => void - popStack: () => void - resetState: () => void - setNavState: (ns: Util.NavState) => void - switchTab: (tab: Tabs.AppTab) => void - } - appendEncryptRecipientsBuilder: () => void - appendNewChatBuilder: () => void - appendNewTeamBuilder: (teamID: T.Teams.TeamID) => void - appendPeopleBuilder: () => void -} - -export const useRouterState = Z.createZustand((set, get) => { - const dispatch: State['dispatch'] = { - clearModals: Util.clearModals, - dynamic: { - tabLongPress: undefined, - }, - navUpToScreen: Util.navUpToScreen, - navigateAppend: Util.navigateAppend, - navigateUp: Util.navigateUp, - popStack: Util.popStack, - resetState: () => { - set(s => ({ - ...s, - dispatch: s.dispatch, - })) - }, - setNavState: next => { - const DEBUG_NAV = __DEV__ && (false as boolean) - DEBUG_NAV && console.log('[Nav] setNavState') - const prev = get().navState as Util.NavState - if (prev === next) return - set(s => { - s.navState = next - }) - - const updateTeamBuilding = () => { - const namespaces = ['chat2', 'crypto', 'teams', 'people'] as const - const namespaceToRoute = new Map([ - ['chat2', 'chatNewChat'], - ['crypto', 'cryptoTeamBuilder'], - ['teams', 'teamsTeamBuilder'], - ['people', 'peopleTeamBuilder'], - ]) - for (const namespace of namespaces) { - const wasTeamBuilding = namespaceToRoute.get(namespace) === Util.getVisibleScreen(prev)?.name - if (wasTeamBuilding) { - // team building or modal on top of that still - const isTeamBuilding = namespaceToRoute.get(namespace) === Util.getVisibleScreen(next)?.name - if (!isTeamBuilding) { - storeRegistry.getTBStore(namespace).dispatch.cancelTeamBuilding() - } - } - } - } - updateTeamBuilding() - - const updateFS = () => { - // Clear critical update when we nav away from tab - if ( - prev && - Util.getTab(prev) === Tabs.fsTab && - next && - Util.getTab(next) !== Tabs.fsTab && - storeRegistry.getState('fs').criticalUpdate - ) { - const {dispatch} = storeRegistry.getState('fs') - dispatch.setCriticalUpdate(false) - } - const fsRrouteNames = ['fsRoot', 'barePreview'] - const wasScreen = fsRrouteNames.includes(Util.getVisibleScreen(prev)?.name ?? '') - const isScreen = fsRrouteNames.includes(Util.getVisibleScreen(next)?.name ?? '') - if (wasScreen !== isScreen) { - const {dispatch} = storeRegistry.getState('fs') - if (wasScreen) { - dispatch.userOut() - } else { - dispatch.userIn() - } - } - } - updateFS() - - const updateSignup = () => { - // Clear "just signed up email" when you leave the people tab after signup - if ( - prev && - Util.getTab(prev) === Tabs.peopleTab && - next && - Util.getTab(next) !== Tabs.peopleTab && - storeRegistry.getState('signup').justSignedUpEmail - ) { - storeRegistry.getState('signup').dispatch.clearJustSignedUpEmail() - } - } - updateSignup() - - const updatePeople = () => { - if (prev && Util.getTab(prev) === Tabs.peopleTab && next && Util.getTab(next) !== Tabs.peopleTab) { - storeRegistry.getState('people').dispatch.markViewed() - } - } - updatePeople() - - const updateTeams = () => { - if (prev && Util.getTab(prev) === Tabs.teamsTab && next && Util.getTab(next) !== Tabs.teamsTab) { - storeRegistry.getState('teams').dispatch.clearNavBadges() - } - } - updateTeams() - - const updateSettings = () => { - // Clear "check your inbox" in settings when you leave the settings tab - if ( - prev && - Util.getTab(prev) === Tabs.settingsTab && - next && - Util.getTab(next) !== Tabs.settingsTab && - storeRegistry.getState('settings-email').addedEmail - ) { - storeRegistry.getState('settings-email').dispatch.resetAddedEmail() - } - } - updateSettings() - - storeRegistry.getState('chat').dispatch.onRouteChanged(prev, next) - }, - switchTab: Util.switchTab, - } - - return { - ...initialStore, - appendEncryptRecipientsBuilder: Util.appendEncryptRecipientsBuilder, - appendNewChatBuilder: Util.appendNewChatBuilder, - appendNewTeamBuilder: Util.appendNewTeamBuilder, - appendPeopleBuilder: Util.appendPeopleBuilder, - dispatch, - } -}) diff --git a/shared/constants/rpc-utils.tsx b/shared/constants/rpc-utils.tsx index 5969509c5d03..8c5993554391 100644 --- a/shared/constants/rpc-utils.tsx +++ b/shared/constants/rpc-utils.tsx @@ -1,6 +1,12 @@ import * as T from './types' import {uint8ArrayToString} from 'uint8array-extras' -import {type Device} from './provision' + +type Device = { + deviceNumberOfType: number + id: T.Devices.DeviceID + name: string + type: T.Devices.DeviceType +} export const bodyToJSON = (body?: Uint8Array): unknown => { if (!body) return undefined @@ -27,4 +33,3 @@ export const rpcDeviceToDevice = (d: T.RPCGen.Device): Device => { throw new Error('Invalid device type detected: ' + type) } } - diff --git a/shared/constants/settings/util.tsx b/shared/constants/settings.tsx similarity index 78% rename from shared/constants/settings/util.tsx rename to shared/constants/settings.tsx index 281dc0582fd5..b54c44d6f26e 100644 --- a/shared/constants/settings/util.tsx +++ b/shared/constants/settings.tsx @@ -1,6 +1,3 @@ -import * as EngineGen from '@/actions/engine-gen-gen' -import {storeRegistry} from '../store-registry' - export const traceInProgressKey = 'settings:traceInProgress' export const processorProfileInProgressKey = 'settings:processorProfileInProgress' @@ -47,17 +44,3 @@ export type SettingsTab = | typeof settingsCryptoTab | typeof settingsContactsTab | typeof settingsWhatsNewTab - -export const onEngineIncoming = (action: EngineGen.Actions) => { - switch (action.type) { - case EngineGen.keybase1NotifyEmailAddressEmailAddressVerified: - case EngineGen.keybase1NotifyUsersPasswordChanged: - case EngineGen.keybase1NotifyPhoneNumberPhoneNumbersChanged: - case EngineGen.keybase1NotifyEmailAddressEmailsChanged: - { - storeRegistry.getState('settings').dispatch.onEngineIncomingImpl(action) - } - break - default: - } -} diff --git a/shared/constants/signup/util.tsx b/shared/constants/signup/util.tsx deleted file mode 100644 index 7c58483bdb13..000000000000 --- a/shared/constants/signup/util.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import * as EngineGen from '@/actions/engine-gen-gen' -import {storeRegistry} from '../store-registry' - -export const onEngineIncoming = (action: EngineGen.Actions) => { - switch (action.type) { - case EngineGen.keybase1NotifyEmailAddressEmailAddressVerified: - { - storeRegistry.getState('signup').dispatch.onEngineIncomingImpl(action) - } - break - default: - } -} diff --git a/shared/constants/store-registry.tsx b/shared/constants/store-registry.tsx deleted file mode 100644 index f62f57f25e1f..000000000000 --- a/shared/constants/store-registry.tsx +++ /dev/null @@ -1,353 +0,0 @@ -// used to allow non-circular cross-calls between stores -// ONLY for zustand stores -import type * as T from './types' -import type * as TBType from './team-building' -import type * as ConvoStateType from './chat2/convostate' -import type {ConvoState} from './chat2/convostate' -import type {State as ActiveState, useActiveState} from './active' -import type {State as ArchiveState, useArchiveState} from './archive' -import type {State as AutoResetState, useAutoResetState} from './autoreset' -import type {State as AvatarState, useAvatarState} from '@/common-adapters/avatar/store' -import type {State as BotsState, useBotsState} from './bots' -import type {State as ChatState, useChatState} from './chat2' -import type {State as ConfigState, useConfigState} from './config' -import type {State as CryptoState, useCryptoState} from './crypto' -import type {State as CurrentUserState, useCurrentUserState} from './current-user' -import type {State as DaemonState, useDaemonState} from './daemon' -import type {State as DarkModeState, useDarkModeState} from './darkmode' -import type {State as DeepLinksState, useDeepLinksState} from './deeplinks' -import type {State as DevicesState, useDevicesState} from './devices' -import type {State as EngineState, useEngineState} from './engine' -import type {State as FollowersState, useFollowerState} from './followers' -import type {State as FSState, useFSState} from './fs' -import type {State as GitState, useGitState} from './git' -import type {State as LogoutState, useLogoutState} from './logout' -import type {State as NotificationsState, useNotifState} from './notifications' -import type {State as PeopleState, usePeopleState} from './people' -import type {State as PinentryState, usePinentryState} from './pinentry' -import type {State as ProfileState, useProfileState} from './profile' -import type {State as ProvisionState, useProvisionState} from './provision' -import type {State as PushState, usePushState} from './push' -import type {State as RecoverPasswordState, useState as useRecoverPasswordState} from './recover-password' -import type {State as RouterState, useRouterState} from './router2' -import type {State as SettingsState, useSettingsState} from './settings' -import type {State as SettingsChatState, useSettingsChatState} from './settings-chat' -import type {State as SettingsContactsState, useSettingsContactsState} from './settings-contacts' -import type {State as SettingsEmailState, useSettingsEmailState} from './settings-email' -import type {State as SettingsPasswordState, usePWState} from './settings-password' -import type {State as SettingsPhoneState, useSettingsPhoneState} from './settings-phone' -import type {State as SignupState, useSignupState} from './signup' -import type {State as TeamsState, useTeamsState} from './teams' -import type {State as Tracker2State, useTrackerState} from './tracker2' -import type {State as UnlockFoldersState, useUnlockFoldersState} from './unlock-folders' -import type {State as UsersState, useUsersState} from './users' -import type {State as WaitingState, useWaitingState} from './waiting' -import type {State as WhatsNewState, useWhatsNewState} from './whats-new' - -type StoreName = - | 'active' - | 'archive' - | 'autoreset' - | 'avatar' - | 'bots' - | 'chat' - | 'config' - | 'crypto' - | 'current-user' - | 'daemon' - | 'dark-mode' - | 'deeplinks' - | 'devices' - | 'engine' - | 'followers' - | 'fs' - | 'git' - | 'logout' - | 'notifications' - | 'people' - | 'pinentry' - | 'profile' - | 'provision' - | 'push' - | 'recover-password' - | 'router' - | 'settings' - | 'settings-chat' - | 'settings-contacts' - | 'settings-email' - | 'settings-password' - | 'settings-phone' - | 'signup' - | 'teams' - | 'tracker2' - | 'unlock-folders' - | 'users' - | 'waiting' - | 'whats-new' - -type StoreStates = { - active: ActiveState - archive: ArchiveState - autoreset: AutoResetState - avatar: AvatarState - bots: BotsState - chat: ChatState - config: ConfigState - crypto: CryptoState - 'current-user': CurrentUserState - daemon: DaemonState - 'dark-mode': DarkModeState - deeplinks: DeepLinksState - devices: DevicesState - engine: EngineState - followers: FollowersState - fs: FSState - git: GitState - logout: LogoutState - notifications: NotificationsState - people: PeopleState - pinentry: PinentryState - profile: ProfileState - provision: ProvisionState - push: PushState - 'recover-password': RecoverPasswordState - router: RouterState - settings: SettingsState - 'settings-chat': SettingsChatState - 'settings-contacts': SettingsContactsState - 'settings-email': SettingsEmailState - 'settings-password': SettingsPasswordState - 'settings-phone': SettingsPhoneState - signup: SignupState - teams: TeamsState - tracker2: Tracker2State - 'unlock-folders': UnlockFoldersState - users: UsersState - waiting: WaitingState - 'whats-new': WhatsNewState -} - -type StoreHooks = { - active: typeof useActiveState - archive: typeof useArchiveState - autoreset: typeof useAutoResetState - avatar: typeof useAvatarState - bots: typeof useBotsState - chat: typeof useChatState - config: typeof useConfigState - crypto: typeof useCryptoState - 'current-user': typeof useCurrentUserState - daemon: typeof useDaemonState - 'dark-mode': typeof useDarkModeState - deeplinks: typeof useDeepLinksState - devices: typeof useDevicesState - engine: typeof useEngineState - followers: typeof useFollowerState - fs: typeof useFSState - git: typeof useGitState - logout: typeof useLogoutState - notifications: typeof useNotifState - people: typeof usePeopleState - pinentry: typeof usePinentryState - profile: typeof useProfileState - provision: typeof useProvisionState - push: typeof usePushState - 'recover-password': typeof useRecoverPasswordState - router: typeof useRouterState - settings: typeof useSettingsState - 'settings-chat': typeof useSettingsChatState - 'settings-contacts': typeof useSettingsContactsState - 'settings-email': typeof useSettingsEmailState - 'settings-password': typeof usePWState - 'settings-phone': typeof useSettingsPhoneState - signup: typeof useSignupState - teams: typeof useTeamsState - tracker2: typeof useTrackerState - 'unlock-folders': typeof useUnlockFoldersState - users: typeof useUsersState - waiting: typeof useWaitingState - 'whats-new': typeof useWhatsNewState -} - -class StoreRegistry { - getStore(storeName: T): StoreHooks[T] { - /* eslint-disable @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-return */ - switch (storeName) { - case 'active': { - const {useActiveState} = require('./active') - return useActiveState - } - case 'archive': { - const {useArchiveState} = require('./archive') - return useArchiveState - } - case 'autoreset': { - const {useAutoResetState} = require('./autoreset') - return useAutoResetState - } - case 'avatar': { - const {useAvatarState} = require('@/common-adapters/avatar/store') - return useAvatarState - } - case 'bots': { - const {useBotsState} = require('./bots') - return useBotsState - } - case 'chat': { - const {useChatState} = require('./chat2') - return useChatState - } - case 'config': { - const {useConfigState} = require('./config') - return useConfigState - } - case 'current-user': { - const {useCurrentUserState} = require('./current-user') - return useCurrentUserState - } - case 'crypto': { - const {useCryptoState} = require('./crypto') - return useCryptoState - } - case 'daemon': { - const {useDaemonState} = require('./daemon') - return useDaemonState - } - case 'dark-mode': { - const {useDarkModeState} = require('./darkmode') - return useDarkModeState - } - case 'deeplinks': { - const {useDeepLinksState} = require('./deeplinks') - return useDeepLinksState - } - case 'devices': { - const {useDevicesState} = require('./devices') - return useDevicesState - } - case 'engine': { - const {useEngineState} = require('./engine') - return useEngineState - } - case 'followers': { - const {useFollowerState} = require('./followers') - return useFollowerState - } - case 'fs': { - const {useFSState} = require('./fs') - return useFSState - } - case 'git': { - const {useGitState} = require('./git') - return useGitState - } - case 'logout': { - const {useLogoutState} = require('./logout') - return useLogoutState - } - case 'notifications': { - const {useNotifState} = require('./notifications') - return useNotifState - } - case 'people': { - const {usePeopleState} = require('./people') - return usePeopleState - } - case 'pinentry': { - const {usePinentryState} = require('./pinentry') - return usePinentryState - } - case 'profile': { - const {useProfileState} = require('./profile') - return useProfileState - } - case 'provision': { - const {useProvisionState} = require('./provision') - return useProvisionState - } - case 'push': { - const {usePushState} = require('./push') - return usePushState - } - case 'recover-password': { - const {useState} = require('./recover-password') - return useState - } - case 'router': { - const {useRouterState} = require('./router2') - return useRouterState - } - case 'settings': { - const {useSettingsState} = require('./settings') - return useSettingsState - } - case 'settings-chat': { - const {useSettingsChatState} = require('./settings-chat') - return useSettingsChatState - } - case 'settings-contacts': { - const {useSettingsContactsState} = require('./settings-contacts') - return useSettingsContactsState - } - case 'settings-email': { - const {useSettingsEmailState} = require('./settings-email') - return useSettingsEmailState - } - case 'settings-password': { - const {usePWState} = require('./settings-password') - return usePWState - } - case 'settings-phone': { - const {useSettingsPhoneState} = require('./settings-phone') - return useSettingsPhoneState - } - case 'signup': { - const {useSignupState} = require('./signup') - return useSignupState - } - case 'teams': { - const {useTeamsState} = require('./teams') - return useTeamsState - } - case 'tracker2': { - const {useTrackerState} = require('./tracker2') - return useTrackerState - } - case 'unlock-folders': { - const {useUnlockFoldersState} = require('./unlock-folders') - return useUnlockFoldersState - } - case 'users': { - const {useUsersState} = require('./users') - return useUsersState - } - case 'waiting': { - const {useWaitingState} = require('./waiting') - return useWaitingState - } - case 'whats-new': { - const {useWhatsNewState} = require('./whats-new') - return useWhatsNewState - } - default: - throw new Error(`Unknown store: ${storeName}`) - } - } - - getState(storeName: T): StoreStates[T] { - return this.getStore(storeName).getState() as StoreStates[T] - } - - getTBStore(name: T.TB.AllowedNamespace): TBType.State { - const {createTBStore} = require('./team-building') as typeof TBType - const store = createTBStore(name) - return store.getState() - } - - getConvoState(id: T.Chat.ConversationIDKey): ConvoState { - const {getConvoState} = require('./chat2/convostate') as typeof ConvoStateType - return getConvoState(id) - } -} - -export const storeRegistry = new StoreRegistry() diff --git a/shared/constants/strings.tsx b/shared/constants/strings.tsx index 5fe715a7795c..cceff363d6c6 100644 --- a/shared/constants/strings.tsx +++ b/shared/constants/strings.tsx @@ -44,6 +44,8 @@ export const waitingKeyRecoverPassword = 'recover-password:waiting' export const waitingKeyCrypto = 'cryptoWaiting' +export const searchWaitingKey = 'teamBuilding:search' + export const waitingKeyTeamsLoaded = 'teams:loaded' export const waitingKeyTeamsJoinTeam = 'teams:joinTeam' export const waitingKeyTeamsTeam = (teamID: T.Teams.TeamID) => `team:${teamID}` @@ -96,6 +98,13 @@ export const waitingKeyFSStat = 'fs:stat' export const waitingKeyFSCommitEdit = 'fs:commitEditWaitingKey' export const waitingKeyFSSetSyncOnCellular = 'fs:setSyncOnCellular' +export const loadAccountsWaitingKey = 'wallets:loadAccounts' + +export const currentVersion: string = '5.5.0' +export const lastVersion: string = '5.4.0' +export const lastLastVersion: string = '5.3.0' +export const keybaseFM = 'Keybase FM 87.7' + export const waitingKeyGitLoading = 'git:loading' export const waitingKeyUsersGetUserBlocks = 'users:getUserBlocks' diff --git a/shared/constants/team-building/utils.tsx b/shared/constants/team-building.tsx similarity index 82% rename from shared/constants/team-building/utils.tsx rename to shared/constants/team-building.tsx index e277db5d587f..24e3c9d457e7 100644 --- a/shared/constants/team-building/utils.tsx +++ b/shared/constants/team-building.tsx @@ -1,4 +1,4 @@ -import type * as T from '../types' +import type * as T from './types' const searchServices: Array = ['keybase', 'twitter', 'github', 'reddit', 'hackernews'] @@ -16,5 +16,3 @@ export const selfToUser = (you: string): T.TB.User => ({ serviceMap: {}, username: you, }) - -export const searchWaitingKey = 'teamBuilding:search' diff --git a/shared/constants/teams/util.tsx b/shared/constants/teams.tsx similarity index 82% rename from shared/constants/teams/util.tsx rename to shared/constants/teams.tsx index 48167c472d34..5eb4fbea86ff 100644 --- a/shared/constants/teams/util.tsx +++ b/shared/constants/teams.tsx @@ -1,29 +1,6 @@ -import * as EngineGen from '@/actions/engine-gen-gen' -import * as T from '../types' -import {storeRegistry} from '../store-registry' +import * as T from './types' import invert from 'lodash/invert' -export const onEngineIncoming = (action: EngineGen.Actions) => { - switch (action.type) { - case EngineGen.chat1ChatUiChatShowManageChannels: - case EngineGen.keybase1NotifyTeamTeamMetadataUpdate: - case EngineGen.chat1NotifyChatChatWelcomeMessageLoaded: - case EngineGen.keybase1NotifyTeamTeamTreeMembershipsPartial: - case EngineGen.keybase1NotifyTeamTeamTreeMembershipsDone: - case EngineGen.keybase1NotifyTeamTeamRoleMapChanged: - case EngineGen.keybase1NotifyTeamTeamChangedByID: - case EngineGen.keybase1NotifyTeamTeamDeleted: - case EngineGen.keybase1NotifyTeamTeamExit: - case EngineGen.keybase1NotifyBadgesBadgeState: - case EngineGen.keybase1GregorUIPushState: - { - storeRegistry.getState('teams').dispatch.onEngineIncomingImpl(action) - } - break - default: - } -} - export const makeRetentionPolicy = ( r?: Partial ): T.Retention.RetentionPolicy => ({ diff --git a/shared/constants/tracker2/util.tsx b/shared/constants/tracker2/util.tsx deleted file mode 100644 index e6adb67ec4c6..000000000000 --- a/shared/constants/tracker2/util.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import * as T from '../types' -import {ignorePromise} from '../utils' -import * as EngineGen from '@/actions/engine-gen-gen' -import {storeRegistry} from '../store-registry' -import logger from '@/logger' - -export const onEngineConnected = () => { - const f = async () => { - try { - await T.RPCGen.delegateUiCtlRegisterIdentify3UIRpcPromise() - logger.info('Registered identify ui') - } catch (error) { - logger.warn('error in registering identify ui: ', error) - } - } - ignorePromise(f()) -} - -export const onEngineIncoming = (action: EngineGen.Actions) => { - switch (action.type) { - case EngineGen.keybase1NotifyTrackingTrackingChanged: - case EngineGen.keybase1Identify3UiIdentify3Result: - case EngineGen.keybase1Identify3UiIdentify3ShowTracker: - case EngineGen.keybase1NotifyUsersUserChanged: - case EngineGen.keybase1NotifyTrackingNotifyUserBlocked: - case EngineGen.keybase1Identify3UiIdentify3UpdateRow: - case EngineGen.keybase1Identify3UiIdentify3UserReset: - case EngineGen.keybase1Identify3UiIdentify3UpdateUserCard: - case EngineGen.keybase1Identify3UiIdentify3Summary: - { - storeRegistry.getState('tracker2').dispatch.onEngineIncomingImpl(action) - } - break - default: - } -} diff --git a/shared/constants/types/chat2/index.tsx b/shared/constants/types/chat2/index.tsx index 212ae677e11b..9742dd2548ca 100644 --- a/shared/constants/types/chat2/index.tsx +++ b/shared/constants/types/chat2/index.tsx @@ -5,11 +5,6 @@ import * as _Message from './message' import type * as Meta from './meta' import {uint8ArrayToHex, hexToUint8Array} from 'uint8array-extras' -export type PaymentConfirmInfo = { - error?: RPCTypes.Status - summary?: T.RPCChat.UIChatPaymentSummary -} - // Static config data we use for various things export type StaticConfig = { deletableByDeleteHistory: Set<_Message.MessageType> @@ -86,11 +81,6 @@ export type AttachmentViewInfo = { last: boolean } -export type AttachmentFullscreenSelection = { - autoPlay: boolean - message: _Message.Message -} - export type CommandStatusInfo = { displayText: string displayType: T.RPCChat.UICommandStatusDisplayTyp @@ -208,4 +198,3 @@ export const outboxIDToRpcOutboxID = (outboxID: _Message.OutboxID): T.RPCChat.Ou export * from './message' export * from './common' export type * from './meta' -export type * from './rowitem' diff --git a/shared/constants/types/chat2/message.tsx b/shared/constants/types/chat2/message.tsx index f7b7d1332f81..16a90713c8bb 100644 --- a/shared/constants/types/chat2/message.tsx +++ b/shared/constants/types/chat2/message.tsx @@ -51,11 +51,6 @@ export const outboxIDToString = (o: OutboxID): string => o export type MentionsAt = ReadonlySet export type MentionsChannel = 'none' | 'all' | 'here' -export interface MessageExplodeDescription { - text: string - seconds: number -} - export interface PathAndOutboxID { path: string outboxID?: T.RPCChat.OutboxID diff --git a/shared/constants/types/config.tsx b/shared/constants/types/config.tsx index e001a72201e9..837ec64ebb5e 100644 --- a/shared/constants/types/config.tsx +++ b/shared/constants/types/config.tsx @@ -1,5 +1,3 @@ -import type * as NetInfo from '@react-native-community/netinfo' - export type OutOfDate = { critical: boolean message: string @@ -8,8 +6,7 @@ export type OutOfDate = { } export type DaemonHandshakeState = 'starting' | 'waitingForWaiters' | 'done' export type ConfiguredAccount = { + fullname?: string hasStoredSecret: boolean username: string } -// 'notavailable' is the desktop default -export type ConnectionType = NetInfo.NetInfoStateType | 'notavailable' diff --git a/shared/constants/types/crypto.tsx b/shared/constants/types/crypto.tsx index db2f4c451ffb..ad568add5e73 100644 --- a/shared/constants/types/crypto.tsx +++ b/shared/constants/types/crypto.tsx @@ -1,4 +1,2 @@ -export type TextType = 'cipher' | 'plain' export type Operations = 'encrypt' | 'decrypt' | 'sign' | 'verify' export type InputTypes = 'text' | 'file' -export type OutputType = 'text' | 'file' diff --git a/shared/constants/types/git.tsx b/shared/constants/types/git.tsx index 3a0e54f885d1..c960905a7cdf 100644 --- a/shared/constants/types/git.tsx +++ b/shared/constants/types/git.tsx @@ -1,4 +1,3 @@ -import type * as T from '@/constants/types' export type GitInfo = { canDelete: boolean channelName?: string @@ -12,9 +11,3 @@ export type GitInfo = { teamname?: string url: string } - -export type State = T.Immutable<{ - readonly error?: Error - readonly idToInfo: Map - readonly isNew?: Set -}> diff --git a/shared/constants/types/push.tsx b/shared/constants/types/push.tsx index 8b35307b1543..ea0e33b9a4cc 100644 --- a/shared/constants/types/push.tsx +++ b/shared/constants/types/push.tsx @@ -1,8 +1,6 @@ import type * as ChatTypes from './chat2' import type * as RPCChatTypes from './rpc-chat-gen' -export type TokenType = 'apple' | 'appledev' | 'androidplay' - export type PushNotification = | { badges: number diff --git a/shared/constants/types/wallets.tsx b/shared/constants/types/wallets.tsx index 0be9a9ab413d..4c4b5488b782 100644 --- a/shared/constants/types/wallets.tsx +++ b/shared/constants/types/wallets.tsx @@ -1,10 +1,5 @@ import type * as StellarRPCTypes from './rpc-stellar-gen' -export type Reserve = { - amount: string - description: string // e.g. 'account' or 'KEYZ/keybase.io trust line' -} - export type AccountID = string export const noAccountID = 'NOACCOUNTID' export type PaymentID = StellarRPCTypes.PaymentID diff --git a/shared/constants/unlock-folders/util.tsx b/shared/constants/unlock-folders/util.tsx deleted file mode 100644 index 89d12305fd44..000000000000 --- a/shared/constants/unlock-folders/util.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import * as T from '../types' -import {ignorePromise} from '../utils' -import * as EngineGen from '@/actions/engine-gen-gen' -import {storeRegistry} from '../store-registry' -import logger from '@/logger' - -export const onEngineConnected = () => { - const f = async () => { - try { - await T.RPCGen.delegateUiCtlRegisterRekeyUIRpcPromise() - logger.info('Registered rekey ui') - } catch (error) { - logger.warn('error in registering rekey ui: ') - logger.debug('error in registering rekey ui: ', error) - } - } - ignorePromise(f()) -} - -export const onEngineIncoming = (action: EngineGen.Actions) => { - switch (action.type) { - case EngineGen.keybase1RekeyUIRefresh: - case EngineGen.keybase1RekeyUIDelegateRekeyUI: - { - storeRegistry.getState('unlock-folders').dispatch.onEngineIncomingImpl(action) - } - break - default: - } -} diff --git a/shared/constants/users/util.tsx b/shared/constants/users/util.tsx deleted file mode 100644 index cd70c647ccb5..000000000000 --- a/shared/constants/users/util.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import * as EngineGen from '@/actions/engine-gen-gen' -import {storeRegistry} from '../store-registry' - -export const onEngineIncoming = (action: EngineGen.Actions) => { - switch (action.type) { - case EngineGen.keybase1NotifyUsersIdentifyUpdate: - case EngineGen.keybase1NotifyTrackingNotifyUserBlocked: - { - storeRegistry.getState('users').dispatch.onEngineIncomingImpl(action) - } - break - default: - } -} diff --git a/shared/constants/values.tsx b/shared/constants/values.tsx index ddf79f1e2170..31ec39ad5cf5 100644 --- a/shared/constants/values.tsx +++ b/shared/constants/values.tsx @@ -1,2 +1,11 @@ export const maxHandshakeTries = 3 export const maxUsernameLength = 16 + +// Exit Codes +export const ExitCodeFuseKextError = 4 +export const ExitCodeFuseKextPermissionError = 5 +export const ExitCodeAuthCanceledError = 6 +// See Installer.m: KBExitFuseCriticalUpdate +export const ExitFuseCriticalUpdate = 8 +// See install_darwin.go: exitCodeFuseCriticalUpdateFailed +export const ExitFuseCriticalUpdateFailed = 300 diff --git a/shared/constants/wallets/utils.tsx b/shared/constants/wallets/utils.tsx deleted file mode 100644 index 6754f164b02d..000000000000 --- a/shared/constants/wallets/utils.tsx +++ /dev/null @@ -1,2 +0,0 @@ -export const loadAccountsWaitingKey = 'wallets:loadAccounts' - diff --git a/shared/constants/whats-new/index.tsx b/shared/constants/whats-new/index.tsx deleted file mode 100644 index 526d9e4473d7..000000000000 --- a/shared/constants/whats-new/index.tsx +++ /dev/null @@ -1,55 +0,0 @@ -import type * as T from '../types' -import * as Z from '@/util/zustand' -import {uint8ArrayToString} from 'uint8array-extras' -import {noVersion, getSeenVersions} from './utils' - -export {currentVersion, lastVersion, lastLastVersion, keybaseFM} from './utils' - -type SeenVersionsMap = {[key in string]: boolean} - -type Store = T.Immutable<{ - lastSeenVersion: string - seenVersions: SeenVersionsMap -}> -const initialStore: Store = { - lastSeenVersion: '', - seenVersions: getSeenVersions(''), -} -export interface State extends Store { - dispatch: { - resetState: 'default' - updateLastSeen: (lastSeenItem?: {md: T.RPCGen.Gregor1.Metadata; item: T.RPCGen.Gregor1.Item}) => void - } - anyVersionsUnseen: () => boolean -} -export const useWhatsNewState = Z.createZustand((set, get) => { - const dispatch: State['dispatch'] = { - resetState: 'default', - updateLastSeen: lastSeenItem => { - if (lastSeenItem) { - const {body} = lastSeenItem.item - const pushStateLastSeenVersion = uint8ArrayToString(body) - const lastSeenVersion = pushStateLastSeenVersion || noVersion - // Default to 0.0.0 (noVersion) if user has never marked a version as seen - set(s => { - s.lastSeenVersion = lastSeenVersion - s.seenVersions = getSeenVersions(lastSeenVersion) - }) - } else { - set(s => { - s.lastSeenVersion = noVersion - s.seenVersions = getSeenVersions(noVersion) - }) - } - }, - } - return { - ...initialStore, - anyVersionsUnseen: () => { - const {lastSeenVersion: ver} = get() - // On first load of what's new, lastSeenVersion == noVersion so everything is unseen - return ver !== '' && ver === noVersion ? true : Object.values(getSeenVersions(ver)).some(seen => !seen) - }, - dispatch, - } -}) diff --git a/shared/constants/whats-new/utils.tsx b/shared/constants/whats-new/utils.tsx deleted file mode 100644 index bc3a3e93f98e..000000000000 --- a/shared/constants/whats-new/utils.tsx +++ /dev/null @@ -1,69 +0,0 @@ -const semver = { - gte: (a: string, b: string) => { - const arra = a.split('.').map(i => parseInt(i)) - const [a1, a2, a3] = arra - const arrb = b.split('.').map(i => parseInt(i)) - const [b1, b2, b3] = arrb - if (arra.length === 3 && arrb.length === 3) { - return a1! >= b1! && a2! >= b2! && a3! >= b3! - } else { - return false - } - }, - valid: (v: string) => - v.split('.').reduce((cnt, i) => { - if (parseInt(i) >= 0) { - return cnt + 1 - } - return cnt - }, 0) === 3, -} - -const noVersion: string = '0.0.0' -export const currentVersion: string = '5.5.0' -export const lastVersion: string = '5.4.0' -export const lastLastVersion: string = '5.3.0' -const versions = [currentVersion, lastVersion, lastLastVersion, noVersion] as const -export const keybaseFM = 'Keybase FM 87.7' - -type SeenVersionsMap = {[key in string]: boolean} - -const isVersionValid = (version: string) => { - return version ? semver.valid(version) : false -} - -export const getSeenVersions = (lastSeenVersion: string): SeenVersionsMap => { - const initialMap: SeenVersionsMap = { - [currentVersion]: true, - [lastLastVersion]: true, - [lastVersion]: true, - [noVersion]: true, - } - - if (!lastSeenVersion || !semver.valid(lastSeenVersion)) { - return initialMap - } - if (lastSeenVersion === noVersion) { - return { - [currentVersion]: false, - [lastLastVersion]: false, - [lastVersion]: false, - [noVersion]: false, - } - } - - const validVersions = versions.filter(isVersionValid) - - const seenVersions = validVersions.reduce( - (acc, version) => ({ - ...acc, - [version]: version === noVersion ? true : semver.gte(lastSeenVersion, version), - }), - initialMap - ) - - return seenVersions -} - -export {noVersion} - diff --git a/shared/crypto/input.tsx b/shared/crypto/input.tsx index 575de4122740..273288164961 100644 --- a/shared/crypto/input.tsx +++ b/shared/crypto/input.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Crypto from '@/constants/crypto' +import * as Crypto from '@/stores/crypto' import * as React from 'react' import type * as T from '@/constants/types' import * as Kb from '@/common-adapters' diff --git a/shared/crypto/operations/decrypt.tsx b/shared/crypto/operations/decrypt.tsx index 4f553f9e7b06..61060317259c 100644 --- a/shared/crypto/operations/decrypt.tsx +++ b/shared/crypto/operations/decrypt.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Crypto from '@/constants/crypto' +import * as Crypto from '@/stores/crypto' import * as Kb from '@/common-adapters' import * as React from 'react' import {Input, DragAndDrop, InputActionsBar, OperationBanner} from '../input' diff --git a/shared/crypto/operations/encrypt.tsx b/shared/crypto/operations/encrypt.tsx index 856c4b40cb0c..7be3f776cec8 100644 --- a/shared/crypto/operations/encrypt.tsx +++ b/shared/crypto/operations/encrypt.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Crypto from '@/constants/crypto' +import * as Crypto from '@/stores/crypto' import * as Kb from '@/common-adapters' import * as React from 'react' import Recipients from '../recipients' diff --git a/shared/crypto/operations/sign.tsx b/shared/crypto/operations/sign.tsx index 5ec68a46802e..7b0267c7f2a5 100644 --- a/shared/crypto/operations/sign.tsx +++ b/shared/crypto/operations/sign.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Crypto from '@/constants/crypto' +import * as Crypto from '@/stores/crypto' import * as React from 'react' import * as Kb from '@/common-adapters' import openURL from '@/util/open-url' diff --git a/shared/crypto/operations/verify.tsx b/shared/crypto/operations/verify.tsx index 3b64988b45a3..56decc162d9d 100644 --- a/shared/crypto/operations/verify.tsx +++ b/shared/crypto/operations/verify.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Crypto from '@/constants/crypto' +import * as Crypto from '@/stores/crypto' import * as Kb from '@/common-adapters' import * as React from 'react' import {Input, InputActionsBar, DragAndDrop, OperationBanner} from '../input' diff --git a/shared/crypto/output.tsx b/shared/crypto/output.tsx index d349ae39efe5..6510f136f84b 100644 --- a/shared/crypto/output.tsx +++ b/shared/crypto/output.tsx @@ -1,6 +1,6 @@ import * as C from '@/constants' -import * as Chat from '@/constants/chat2' -import * as Crypto from '@/constants/crypto' +import * as Chat from '@/stores/chat2' +import * as Crypto from '@/stores/crypto' import * as Kb from '@/common-adapters' import * as Path from '@/util/path' import * as React from 'react' @@ -8,9 +8,9 @@ import capitalize from 'lodash/capitalize' import type * as T from '@/constants/types' import {pickFiles} from '@/util/pick-files' import type HiddenString from '@/util/hidden-string' -import {useFSState} from '@/constants/fs' +import {useFSState} from '@/stores/fs' import * as FS from '@/constants/fs' -import {useConfigState} from '@/constants/config' +import {useConfigState} from '@/stores/config' type OutputProps = {operation: T.Crypto.Operations} type OutputActionsBarProps = {operation: T.Crypto.Operations} @@ -181,7 +181,7 @@ export const OutputActionsBar = (props: OutputActionsBarProps) => { const actionsDisabled = waiting || !outputValid const openLocalPathInSystemFileManagerDesktop = useFSState( - s => s.dispatch.dynamic.openLocalPathInSystemFileManagerDesktop + s => s.dispatch.defer.openLocalPathInSystemFileManagerDesktop ) const onShowInFinder = () => { openLocalPathInSystemFileManagerDesktop?.(output.stringValue()) @@ -194,7 +194,7 @@ export const OutputActionsBar = (props: OutputActionsBarProps) => { previewConversation({participants: [username.stringValue()], reason: 'search'}) } - const copyToClipboard = useConfigState(s => s.dispatch.dynamic.copyToClipboard) + const copyToClipboard = useConfigState(s => s.dispatch.defer.copyToClipboard) const onCopyOutput = () => { copyToClipboard(output.stringValue()) } @@ -369,7 +369,7 @@ export const OperationOutput = (props: OutputProps) => { const output = _output.stringValue() const openLocalPathInSystemFileManagerDesktop = useFSState( - s => s.dispatch.dynamic.openLocalPathInSystemFileManagerDesktop + s => s.dispatch.defer.openLocalPathInSystemFileManagerDesktop ) const onShowInFinder = () => { if (!output) return diff --git a/shared/crypto/recipients.tsx b/shared/crypto/recipients.tsx index a4ad222b2a2d..a7ac327df09f 100644 --- a/shared/crypto/recipients.tsx +++ b/shared/crypto/recipients.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Crypto from '@/constants/crypto' +import * as Crypto from '@/stores/crypto' import * as Kb from '@/common-adapters' const placeholder = 'Search people' diff --git a/shared/crypto/routes.tsx b/shared/crypto/routes.tsx index da93e894f5bb..14200ecfdaf5 100644 --- a/shared/crypto/routes.tsx +++ b/shared/crypto/routes.tsx @@ -1,6 +1,6 @@ import * as React from 'react' import * as C from '@/constants' -import * as Crypto from '@/constants/crypto/util' +import * as Crypto from '@/constants/crypto' import {HeaderLeftCancel2, type HeaderBackButtonProps} from '@/common-adapters/header-hoc' import cryptoTeamBuilder from '../team-building/page' diff --git a/shared/crypto/sub-nav/index.desktop.tsx b/shared/crypto/sub-nav/index.desktop.tsx index 7cd6cdfdd883..47e4b97a6486 100644 --- a/shared/crypto/sub-nav/index.desktop.tsx +++ b/shared/crypto/sub-nav/index.desktop.tsx @@ -1,6 +1,6 @@ import * as React from 'react' import * as Kb from '@/common-adapters' -import * as Crypto from '@/constants/crypto' +import * as Crypto from '@/stores/crypto' import * as Common from '@/router-v2/common.desktop' import LeftNav from './left-nav.desktop' import { diff --git a/shared/crypto/sub-nav/index.native.tsx b/shared/crypto/sub-nav/index.native.tsx index c41261148096..f2ee824bec3b 100644 --- a/shared/crypto/sub-nav/index.native.tsx +++ b/shared/crypto/sub-nav/index.native.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Crypto from '@/constants/crypto' +import * as Crypto from '@/stores/crypto' import * as Kb from '@/common-adapters' import NavRow from './nav-row' diff --git a/shared/crypto/sub-nav/left-nav.desktop.tsx b/shared/crypto/sub-nav/left-nav.desktop.tsx index e44a26459acd..5dc1b78e412a 100644 --- a/shared/crypto/sub-nav/left-nav.desktop.tsx +++ b/shared/crypto/sub-nav/left-nav.desktop.tsx @@ -1,6 +1,6 @@ import type * as React from 'react' import * as Kb from '@/common-adapters' -import * as Crypto from '@/constants/crypto' +import * as Crypto from '@/stores/crypto' import NavRow from './nav-row' type Row = (typeof Crypto.Tabs)[number] & { diff --git a/shared/deeplinks/error.tsx b/shared/deeplinks/error.tsx index ed8a9cda94c5..6450b1b88b1f 100644 --- a/shared/deeplinks/error.tsx +++ b/shared/deeplinks/error.tsx @@ -1,6 +1,5 @@ import * as C from '@/constants' import * as Kb from '@/common-adapters' -import {useDeepLinksState} from '@/constants/deeplinks' type KeybaseLinkErrorBodyProps = { message: string @@ -21,9 +20,9 @@ export const KeybaseLinkErrorBody = (props: KeybaseLinkErrorBodyProps) => { ) } -const KeybaseLinkError = () => { - const deepError = useDeepLinksState(s => s.keybaseLinkError) - const message = deepError +const LinkError = (props: {error?: string}) => { + const error = props.error ?? 'Invalid page! (sorry)' + const message = error const isError = true const navigateUp = C.useRouterState(s => s.dispatch.navigateUp) const onClose = () => navigateUp() @@ -48,4 +47,6 @@ const styles = Kb.Styles.styleSheetCreate(() => ({ }), })) -export default KeybaseLinkError +type OwnProps = C.ViewPropsToPageProps +const Screen = (p: OwnProps) => +export default Screen diff --git a/shared/desktop/app/installer.desktop.tsx b/shared/desktop/app/installer.desktop.tsx index 3d6306fcbc41..3d4b50dd5e73 100644 --- a/shared/desktop/app/installer.desktop.tsx +++ b/shared/desktop/app/installer.desktop.tsx @@ -9,6 +9,13 @@ import {ctlQuit} from './ctl.desktop' import {isDarwin} from '@/constants/platform' import logger from '@/logger' import zlib from 'zlib' +import { + ExitCodeAuthCanceledError, + ExitCodeFuseKextError, + ExitCodeFuseKextPermissionError, + ExitFuseCriticalUpdate, + ExitFuseCriticalUpdateFailed, +} from '@/constants/values' const file = path.join(Electron.app.getPath('userData'), 'installer.json') @@ -56,18 +63,6 @@ type ResultType = | undefined const checkErrors = (result: ResultType, errors: Array, errorTypes: ErrorTypes) => { - // Copied from old constants/favorite.js - // See Installer.m: KBExitFuseKextError - const ExitCodeFuseKextError = 4 - // See Installer.m: KBExitFuseKextPermissionError - const ExitCodeFuseKextPermissionError = 5 - // See Installer.m: KBExitAuthCanceledError - const ExitCodeAuthCanceledError = 6 - // See Installer.m: KBExitFuseCriticalUpdate - const ExitFuseCriticalUpdate = 8 - // See install_darwin.go: exitCodeFuseCriticalUpdateFailed - const ExitFuseCriticalUpdateFailed = 300 - const results = result?.componentResults || [] results.forEach(cr => { if (cr.status?.code === 0) { diff --git a/shared/desktop/app/menu-bar.desktop.tsx b/shared/desktop/app/menu-bar.desktop.tsx index cbc76a7e902f..bca0da6b42de 100644 --- a/shared/desktop/app/menu-bar.desktop.tsx +++ b/shared/desktop/app/menu-bar.desktop.tsx @@ -8,7 +8,7 @@ import {menubar} from 'menubar' import {showDevTools, skipSecondaryDevtools} from '@/local-debug' import {getMainWindow} from './main-window.desktop' import {assetRoot, htmlPrefix} from './html-root.desktop' -import type {BadgeType} from '@/constants/notifications' +import type {BadgeType} from '@/stores/notifications' const getIcons = (iconType: BadgeType, badges: number) => { const size = isWindows ? 16 : 22 diff --git a/shared/desktop/app/node2.desktop.tsx b/shared/desktop/app/node2.desktop.tsx index 7d3b6c8db307..6b65e04f06ce 100644 --- a/shared/desktop/app/node2.desktop.tsx +++ b/shared/desktop/app/node2.desktop.tsx @@ -427,8 +427,7 @@ const plumbEvents = () => { () => {}, (c: boolean) => { R.remoteDispatch(RemoteGen.createEngineConnection({connected: c})) - }, - false + } ) const timeoutPromise = async (timeMs: number) => diff --git a/shared/desktop/electron-sums.tsx b/shared/desktop/electron-sums.tsx index 5e8e69930588..f02f8033b288 100644 --- a/shared/desktop/electron-sums.tsx +++ b/shared/desktop/electron-sums.tsx @@ -1,10 +1,10 @@ // Generated with: ./extract-electron-shasums.sh {ver} // prettier-ignore export const electronChecksums = { - 'electron-v39.2.7-darwin-arm64.zip': 'bda657a77c074ee0c6a0e5d5f6de17918d7cf959306b454f6fadb07a08588883', - 'electron-v39.2.7-darwin-x64.zip': 'd7535e64ad54efcf0fae84d7fea4c2ee4727eec99c78d2a5acc695285cb0a9f0', - 'electron-v39.2.7-linux-arm64.zip': '445465a43bd2ffaec09877f4ed46385065632a4683c2806cc6211cc73c110024', - 'electron-v39.2.7-linux-x64.zip': '2f5285ef563dca154aa247696dddef545d3d895dd9b227ed423ea0d43737c22c', - 'electron-v39.2.7-win32-x64.zip': '3464537fa4be6b7b073f1c9b694ac2eb1f632d6ec36f6eeac9e00d8a279f188c', - 'hunspell_dictionaries.zip': 'f1320ff95f2cce0f0f7225b45f2b9340aeb38b341b4090f0e58f58dc2da2f3a9', + 'electron-v40.4.0-darwin-arm64.zip': 'b35807b8a43f9f1b60eb5e99ccd4c5001f96579e1b061a2b96207ff88d339a91', + 'electron-v40.4.0-darwin-x64.zip': '33f6fb8e495d64235519de57ab178fc12611adbe2bd1bbb65e6f80c5b04e1978', + 'electron-v40.4.0-linux-arm64.zip': 'a0e38a4657c69a4ff488990b8f3575e1422a4f6aec6f0f67608c567aab77d112', + 'electron-v40.4.0-linux-x64.zip': '351e0b5df9bedf3ee30f32e4186f059699d3b60515398925213a61496fafd3c5', + 'electron-v40.4.0-win32-x64.zip': 'd3f15c97ba68dad570d10b139f4bf11b5b58efbc8409f2ffda310990a5a329a6', + 'hunspell_dictionaries.zip': '6ab010b97b1e024234032cad5ce28e8a161b257c96e99231957c5d213421fca4', } diff --git a/shared/desktop/package.desktop.tsx b/shared/desktop/package.desktop.tsx index 718fb3d37e5f..96a27cca95f3 100644 --- a/shared/desktop/package.desktop.tsx +++ b/shared/desktop/package.desktop.tsx @@ -1,7 +1,7 @@ import {rimrafSync} from 'rimraf' import fs from 'fs-extra' import os from 'os' -import packager, {type Options} from '@electron/packager' +import {packager, type Options} from '@electron/packager' import path from 'path' import webpack from 'webpack' import rootConfig from './webpack.config.babel' @@ -237,14 +237,14 @@ async function pack(plat: string, arch: string) { if (packageOutDir === '') packageOutDir = desktopPath(`release/${plat}-${arch}`) console.log('Packaging to', packageOutDir) - const opts = { + const opts: Options = { ...packagerOpts, - arch, + arch: arch as Options['arch'], out: packageOutDir, - platform: plat, + platform: plat as Options['platform'], ...(plat === 'win32' ? { - 'version-string': { + win32metadata: { CompanyName: companyName, FileDescription: appName, OriginalFilename: appName + '.exe', @@ -256,8 +256,7 @@ async function pack(plat: string, arch: string) { console.log('Building using options', opts) const ret = await packager(opts) - // sometimes returns bools, unclear why - return ret.filter(o => typeof o === 'string') + return ret } function postPack(appPaths: Array, plat: string, arch: string) { diff --git a/shared/desktop/remote/use-serialize-props.desktop.tsx b/shared/desktop/remote/use-serialize-props.desktop.tsx index 4bd25aacfdf1..3327ea8f6428 100644 --- a/shared/desktop/remote/use-serialize-props.desktop.tsx +++ b/shared/desktop/remote/use-serialize-props.desktop.tsx @@ -5,7 +5,7 @@ import * as React from 'react' import * as C from '@/constants' import KB2 from '@/util/electron.desktop' import isEqual from 'lodash/isEqual' -import {useConfigState} from '@/constants/config' +import {useConfigState} from '@/stores/config' const {rendererNewProps} = KB2.functions diff --git a/shared/desktop/renderer/main.desktop.tsx b/shared/desktop/renderer/main.desktop.tsx index 640693e7a1bd..f216bfdfa816 100644 --- a/shared/desktop/renderer/main.desktop.tsx +++ b/shared/desktop/renderer/main.desktop.tsx @@ -3,7 +3,7 @@ import './globals.desktop' import {isDarwin, isWindows} from '@/constants/platform' import '@/util/why-did-you-render' import KB2, {waitOnKB2Loaded} from '@/util/electron.desktop' -import * as DarkMode from '@/constants/darkmode' +import * as DarkMode from '@/stores/darkmode' waitOnKB2Loaded(() => { const {setSystemSupported, setSystemDarkMode} = DarkMode.useDarkModeState.getState().dispatch diff --git a/shared/desktop/renderer/main2.desktop.tsx b/shared/desktop/renderer/main2.desktop.tsx index 969a0917143b..e2d29cd73822 100644 --- a/shared/desktop/renderer/main2.desktop.tsx +++ b/shared/desktop/renderer/main2.desktop.tsx @@ -4,24 +4,229 @@ import Main from '@/app/main.desktop' import * as C from '@/constants' import * as React from 'react' import * as ReactDOM from 'react-dom/client' -import type * as RemoteGen from '@/actions/remote-gen' +import * as RemoteGen from '@/actions/remote-gen' import Root from './container.desktop' import {makeEngine} from '@/engine' import {disableDragDrop} from '@/util/drag-drop.desktop' -import {dumpLogs} from '@/constants/platform-specific/index.desktop' import {initDesktopStyles} from '@/styles/index.desktop' import {isWindows} from '@/constants/platform' import KB2 from '@/util/electron.desktop' +import {ignorePromise} from '@/constants/utils' +import {useConfigState} from '@/stores/config' +import {usePinentryState} from '@/stores/pinentry' +import * as T from '@/constants/types' +import {RPCError} from '@/util/errors' +import {switchTab} from '@/constants/router2' +import {storeRegistry} from '@/stores/store-registry' +import {onEngineConnected, onEngineDisconnected} from '@/constants/init/index.desktop' +import {handleAppLink} from '@/constants/deeplinks' +import * as Crypto from '@/constants/crypto' +import * as Tabs from '@/constants/tabs' +import {isPathSaltpackEncrypted, isPathSaltpackSigned} from '@/util/path' +import type HiddenString from '@/util/hidden-string' +import {useCryptoState} from '@/stores/crypto' +import logger from '@/logger' import {debugWarning} from '@/util/debug-warning' -import {useConfigState} from '@/constants/config' import type {default as NewMainType} from '../../app/main.desktop' import {setServiceDecoration} from '@/common-adapters/markdown/react' import ServiceDecoration from '@/common-adapters/markdown/service-decoration' -import {useDarkModeState} from '@/constants/darkmode' -import {initPlatformListener} from '@/constants/platform-specific' +import {useDarkModeState} from '@/stores/darkmode' +import {initPlatformListener, onEngineIncoming} from '@/constants/init/index.desktop' setServiceDecoration(ServiceDecoration) -const {ipcRendererOn, requestWindowsStartService, appStartedUp} = KB2.functions +const {ipcRendererOn, requestWindowsStartService, appStartedUp, ctlQuit, dumpNodeLogger} = KB2.functions + +const handleSaltPackOpen = (_path: string | HiddenString) => { + const path = typeof _path === 'string' ? _path : _path.stringValue() + + if (!useConfigState.getState().loggedIn) { + console.warn('Tried to open a saltpack file before being logged in') + return + } + let operation: T.Crypto.Operations | undefined + if (isPathSaltpackEncrypted(path)) { + operation = Crypto.Operations.Decrypt + } else if (isPathSaltpackSigned(path)) { + operation = Crypto.Operations.Verify + } else { + logger.warn( + 'Deeplink received saltpack file path not ending in ".encrypted.saltpack" or ".signed.saltpack"' + ) + return + } + useCryptoState.getState().dispatch.onSaltpackOpenFile(operation, path) + switchTab(Tabs.cryptoTab) +} + +const dumpLogs = async (reason?: string) => { + await logger.dump() + await (dumpNodeLogger?.() ?? Promise.resolve([])) + // quit as soon as possible + if (reason === 'quitting through menu') { + ctlQuit?.() + } +} + +const updateApp = () => { + const f = async () => { + await T.RPCGen.configStartUpdateIfNeededRpcPromise() + } + ignorePromise(f()) + // * If user choose to update: + // We'd get killed and it doesn't matter what happens here. + // * If user hits "Ignore": + // Note that we ignore the snooze here, so the state shouldn't change, + // and we'd back to where we think we still need an update. So we could + // have just unset the "updating" flag.However, in case server has + // decided to pull out the update between last time we asked the updater + // and now, we'd be in a wrong state if we didn't check with the service. + // Since user has interacted with it, we still ask the service to make + // sure. + + useConfigState.getState().dispatch.setUpdating() +} + +const eventFromRemoteWindows = (action: RemoteGen.Actions) => { + switch (action.type) { + case RemoteGen.resetStore: + break + case RemoteGen.openChatFromWidget: { + useConfigState.getState().dispatch.showMain() + storeRegistry.getConvoState(action.payload.conversationIDKey).dispatch.navigateToThread('inboxSmall') + break + } + case RemoteGen.inboxRefresh: { + storeRegistry.getState('chat').dispatch.inboxRefresh('widgetRefresh') + break + } + case RemoteGen.engineConnection: { + if (action.payload.connected) { + onEngineConnected() + } else { + onEngineDisconnected() + } + break + } + case RemoteGen.switchTab: { + switchTab(action.payload.tab) + break + } + case RemoteGen.setCriticalUpdate: { + storeRegistry.getState('fs').dispatch.setCriticalUpdate(action.payload.critical) + break + } + case RemoteGen.userFileEditsLoad: { + storeRegistry.getState('fs').dispatch.userFileEditsLoad() + break + } + case RemoteGen.openFilesFromWidget: { + storeRegistry.getState('fs').dispatch.defer.openFilesFromWidgetDesktop?.(action.payload.path) + break + } + case RemoteGen.saltpackFileOpen: { + handleSaltPackOpen(action.payload.path) + break + } + case RemoteGen.pinentryOnCancel: { + usePinentryState.getState().dispatch.dynamic.onCancel?.() + break + } + case RemoteGen.pinentryOnSubmit: { + usePinentryState.getState().dispatch.dynamic.onSubmit?.(action.payload.password) + break + } + case RemoteGen.openPathInSystemFileManager: { + storeRegistry.getState('fs').dispatch.defer.openPathInSystemFileManagerDesktop?.(action.payload.path) + break + } + case RemoteGen.unlockFoldersSubmitPaperKey: { + T.RPCGen.loginPaperKeySubmitRpcPromise({paperPhrase: action.payload.paperKey}, 'unlock-folders:waiting') + .then(() => { + useConfigState.getState().dispatch.openUnlockFolders([]) + }) + .catch((e: unknown) => { + if (!(e instanceof RPCError)) return + useConfigState.setState(s => { + s.unlockFoldersError = e.desc + }) + }) + break + } + case RemoteGen.closeUnlockFolders: { + T.RPCGen.rekeyRekeyStatusFinishRpcPromise() + .then(() => {}) + .catch(() => {}) + useConfigState.getState().dispatch.openUnlockFolders([]) + break + } + case RemoteGen.stop: { + storeRegistry.getState('settings').dispatch.stop(action.payload.exitCode) + break + } + case RemoteGen.trackerChangeFollow: { + storeRegistry.getState('tracker2').dispatch.changeFollow(action.payload.guiID, action.payload.follow) + break + } + case RemoteGen.trackerIgnore: { + storeRegistry.getState('tracker2').dispatch.ignore(action.payload.guiID) + break + } + case RemoteGen.trackerCloseTracker: { + storeRegistry.getState('tracker2').dispatch.closeTracker(action.payload.guiID) + break + } + case RemoteGen.trackerLoad: { + storeRegistry.getState('tracker2').dispatch.load(action.payload) + break + } + case RemoteGen.link: + { + const {link} = action.payload + handleAppLink(link) + } + break + case RemoteGen.installerRan: + useConfigState.getState().dispatch.installerRan() + break + case RemoteGen.updateNow: + updateApp() + break + case RemoteGen.powerMonitorEvent: + useConfigState.getState().dispatch.powerMonitorEvent(action.payload.event) + break + case RemoteGen.showMain: + useConfigState.getState().dispatch.showMain() + break + case RemoteGen.dumpLogs: + ignorePromise(useConfigState.getState().dispatch.dumpLogs(action.payload.reason)) + break + case RemoteGen.remoteWindowWantsProps: + useConfigState + .getState() + .dispatch.remoteWindowNeedsProps(action.payload.component, action.payload.param) + break + case RemoteGen.updateWindowMaxState: + useConfigState.setState(s => { + s.windowState.isMaximized = action.payload.max + }) + break + case RemoteGen.updateWindowState: + useConfigState.getState().dispatch.updateWindowState(action.payload.windowState) + break + case RemoteGen.updateWindowShown: { + const win = action.payload.component + useConfigState.setState(s => { + s.windowShownCount.set(win, (s.windowShownCount.get(win) ?? 0) + 1) + }) + break + } + case RemoteGen.previewConversation: + storeRegistry + .getState('chat') + .dispatch.previewConversation({participants: [action.payload.participant], reason: 'tracker'}) + break + } +} // node side plumbs through initial pref so we avoid flashes const darkModeFromNode = window.location.search.match(/darkMode=(light|dark)/) @@ -49,16 +254,20 @@ const setupApp = () => { disableDragDrop() const {batch} = C.useWaitingState.getState().dispatch - const eng = makeEngine(batch, () => { - // do nothing we wait for the remote version from node - }) + const eng = makeEngine( + batch, + () => { + // do nothing we wait for the remote version from node + }, + onEngineIncoming + ) initPlatformListener() eng.listenersAreReady() ipcRendererOn?.('KBdispatchAction', (_: unknown, action: unknown) => { setTimeout(() => { try { - useConfigState.getState().dispatch.eventFromRemoteWindows(action as RemoteGen.Actions) + eventFromRemoteWindows(action as RemoteGen.Actions) } catch {} }, 0) }) diff --git a/shared/desktop/webpack.config.babel.js b/shared/desktop/webpack.config.babel.js index 061ead3748fc..b14d20a5fbf0 100644 --- a/shared/desktop/webpack.config.babel.js +++ b/shared/desktop/webpack.config.babel.js @@ -8,22 +8,19 @@ import path from 'path' import webpack from 'webpack' import HtmlWebpackPlugin from 'html-webpack-plugin' import ReactRefreshWebpackPlugin from '@pmmmwh/react-refresh-webpack-plugin' -import CircularDependencyPlugin from 'circular-dependency-plugin' const ignoredModules = require('../ignored-modules') const enableWDYR = require('../util/why-did-you-render-enabled') const elecVersion = require('../package.json').devDependencies.electron // true if you want to debug unused code. This makes single chunks so you can grep for 'unused harmony' in the output in desktop/dist const debugUnusedChunks = false -const enableCircularDepCheck = false const evalDevtools = false -if (enableWDYR || debugUnusedChunks || enableCircularDepCheck || evalDevtools) { +if (enableWDYR || debugUnusedChunks || evalDevtools) { for (let i = 0; i < 10; ++i) { console.error('Webpack debugging on!!!', { enableWDYR, debugUnusedChunks, - enableCircularDepCheck, evalDevtools, }) } @@ -188,23 +185,6 @@ const config = (_, {mode}) => { new webpack.DefinePlugin(defines), // Inject some defines new webpack.IgnorePlugin({resourceRegExp: /^\.\/locale$/, contextRegExp: /moment$/}), // Skip a bunch of crap moment pulls in ...(enableWDYR ? [] : [new webpack.IgnorePlugin({resourceRegExp: /^lodash$/})]), // Disallow entire lodash, but needed by why did - ...(isDev && enableCircularDepCheck - ? [ - new CircularDependencyPlugin({ - // exclude detection of files based on a RegExp - exclude: /node_modules/, - // include specific files based on a RegExp - // include: /dir/, - // add errors to webpack instead of warnings - failOnError: true, - // allow import cycles that include an asyncronous import, - // e.g. via import(/* webpackMode: "weak" */ './file.js') - allowAsyncCycles: false, - // set the current working directory for displaying module paths - cwd: process.cwd(), - }), - ] - : []), ], resolve: { alias, diff --git a/shared/devices/add-device.tsx b/shared/devices/add-device.tsx index 3b5726f11013..e27c6efffcc5 100644 --- a/shared/devices/add-device.tsx +++ b/shared/devices/add-device.tsx @@ -1,8 +1,8 @@ import * as C from '@/constants' -import * as Devices from '@/constants/devices' +import * as Devices from '@/stores/devices' import * as React from 'react' import * as Kb from '@/common-adapters' -import {useProvisionState} from '@/constants/provision' +import {useProvisionState} from '@/stores/provision' type OwnProps = { highlight?: Array<'computer' | 'phone' | 'paper key'> diff --git a/shared/devices/device-icon.tsx b/shared/devices/device-icon.tsx index 0121619b3fd5..cc698e7fe61d 100644 --- a/shared/devices/device-icon.tsx +++ b/shared/devices/device-icon.tsx @@ -1,5 +1,5 @@ -import type * as Provision from '@/constants/provision' -import * as Devices from '@/constants/devices' +import type * as Provision from '@/stores/provision' +import * as Devices from '@/stores/devices' import * as Kb from '@/common-adapters' import type * as T from '@/constants/types' import type {IconStyle} from '@/common-adapters/icon' diff --git a/shared/devices/device-page.tsx b/shared/devices/device-page.tsx index dff22308c890..f270c62a1b27 100644 --- a/shared/devices/device-page.tsx +++ b/shared/devices/device-page.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Devices from '@/constants/devices' +import * as Devices from '@/stores/devices' import * as Kb from '@/common-adapters' import * as React from 'react' import type * as T from '@/constants/types' diff --git a/shared/devices/device-revoke.tsx b/shared/devices/device-revoke.tsx index 374bc6d13354..90e0850a35fe 100644 --- a/shared/devices/device-revoke.tsx +++ b/shared/devices/device-revoke.tsx @@ -1,11 +1,11 @@ import * as C from '@/constants' -import {useConfigState} from '@/constants/config' -import * as Devices from '@/constants/devices' +import {useConfigState} from '@/stores/config' +import * as Devices from '@/stores/devices' import * as Kb from '@/common-adapters' import * as React from 'react' import * as T from '@/constants/types' -import {settingsDevicesTab} from '@/constants/settings' -import {useCurrentUserState} from '@/constants/current-user' +import {settingsDevicesTab} from '@/stores/settings' +import {useCurrentUserState} from '@/stores/current-user' type OwnProps = {deviceID: string} @@ -97,7 +97,7 @@ const useRevoke = (deviceID = '') => { try { await T.RPCGen.loginDeprovisionRpcPromise({doRevoke: true, username}, C.waitingKeyDevices) load() - useConfigState.getState().dispatch.revoke(deviceName) + useConfigState.getState().dispatch.revoke(deviceName, wasCurrentDevice) } catch {} } else { try { @@ -106,7 +106,7 @@ const useRevoke = (deviceID = '') => { C.waitingKeyDevices ) load() - useConfigState.getState().dispatch.revoke(deviceName) + useConfigState.getState().dispatch.revoke(deviceName, wasCurrentDevice) navUpToScreen( C.isMobile ? (C.isTablet ? C.Tabs.settingsTab : settingsDevicesTab) : C.Tabs.devicesTab ) diff --git a/shared/devices/index.tsx b/shared/devices/index.tsx index ebef2b008e50..4df196248347 100644 --- a/shared/devices/index.tsx +++ b/shared/devices/index.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Devices from '@/constants/devices' +import * as Devices from '@/stores/devices' import * as Kb from '@/common-adapters' import * as React from 'react' import DeviceRow, {NewContext} from './row' diff --git a/shared/devices/nav-header.tsx b/shared/devices/nav-header.tsx index 2176976bb9c3..66506b11fbe8 100644 --- a/shared/devices/nav-header.tsx +++ b/shared/devices/nav-header.tsx @@ -1,10 +1,10 @@ import * as C from '@/constants' -import type * as DevicesType from '@/constants/devices' +import type * as DevicesType from '@/stores/devices' import * as Kb from '@/common-adapters' import * as React from 'react' export const HeaderTitle = () => { - const Devices = require('@/constants/devices') as typeof DevicesType + const Devices = require('@/stores/devices') as typeof DevicesType const numActive = Devices.useActiveDeviceCounts() const numRevoked = Devices.useRevokedDeviceCounts() return ( diff --git a/shared/devices/row.tsx b/shared/devices/row.tsx index 5a384bbb9d81..3817ce3ef759 100644 --- a/shared/devices/row.tsx +++ b/shared/devices/row.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Devices from '@/constants/devices' +import * as Devices from '@/stores/devices' import * as Kb from '@/common-adapters' import * as React from 'react' import DeviceIcon from './device-icon' diff --git a/shared/engine/index-impl.tsx b/shared/engine/index-impl.tsx index 14f389095d1d..14513d9c5fa5 100644 --- a/shared/engine/index-impl.tsx +++ b/shared/engine/index-impl.tsx @@ -12,7 +12,6 @@ import {printOutstandingRPCs} from '@/local-debug' import {resetClient, createClient, rpcLog, type CreateClientType, type PayloadType} from './index.platform' import {type RPCError, convertToError} from '@/util/errors' import type * as EngineGen from '../actions/engine-gen-gen' -import type * as EngineConst from '@/constants/engine' // delay incoming to stop react from queueing too many setState calls and stopping rendering // only while debugging for now @@ -47,6 +46,8 @@ class Engine { _listenersAreReady: boolean = false _emitWaiting: (changes: BatchParams) => void + _incomingTimeout: NodeJS.Timeout | undefined + _onEngineIncoming?: (action: EngineGen.Actions) => void _queuedChanges: Array<{error?: RPCError; increment: boolean; key: WaitingKey}> = [] dispatchWaitingAction = (key: WaitingKey, waiting: boolean, error?: RPCError) => { @@ -63,13 +64,18 @@ class Engine { constructor( emitWaiting: (changes: BatchParams) => void, onConnected: (c: boolean) => void, - allowIncomingCalls = true + onEngineIncoming?: (action: EngineGen.Actions) => void ) { this._onConnectedCB = onConnected + this._onEngineIncoming = onEngineIncoming // the node engine doesn't do this and we don't want to pull in any reqs - if (allowIncomingCalls) { - const {useEngineState} = require('@/constants/engine') as typeof EngineConst - this._engineConstantsIncomingCall = useEngineState.getState().dispatch.onEngineIncoming + if (onEngineIncoming) { + this._engineConstantsIncomingCall = (action: EngineGen.Actions) => { + // defer a frame so its more like before + this._incomingTimeout = setTimeout(() => { + this._onEngineIncoming?.(action) + }, 0) + } } this._emitWaiting = emitWaiting this._rpcClient = createClient( @@ -282,14 +288,14 @@ if (__DEV__) { const makeEngine = ( emitWaiting: (b: BatchParams) => void, onConnected: (c: boolean) => void, - allowIncomingCalls = true + onEngineIncoming?: (action: EngineGen.Actions) => void ) => { if (__DEV__ && engine) { logger.warn('makeEngine called multiple times') } if (!engine) { - engine = new Engine(emitWaiting, onConnected, allowIncomingCalls) + engine = new Engine(emitWaiting, onConnected, onEngineIncoming) initEngine(engine) initEngineListener(engineListener) } diff --git a/shared/engine/index.d.ts b/shared/engine/index.d.ts index cd74ba25e572..e352925abb33 100644 --- a/shared/engine/index.d.ts +++ b/shared/engine/index.d.ts @@ -29,7 +29,7 @@ export declare function getEngine(): Engine export declare function makeEngine( emitWaiting: (b: BatchParams) => void, onConnected: (c: boolean) => void, - allowIncomingCalls?: boolean + onEngineIncoming?: (action: EngineGen.Actions) => void ): Engine export default getEngine export type {IncomingCallMapType, CustomResponseIncomingCallMapType} diff --git a/shared/fs/banner/conflict-banner.tsx b/shared/fs/banner/conflict-banner.tsx index 09898a2b91e6..c43e0333121f 100644 --- a/shared/fs/banner/conflict-banner.tsx +++ b/shared/fs/banner/conflict-banner.tsx @@ -3,8 +3,8 @@ import * as React from 'react' import * as Kb from '@/common-adapters' import * as T from '@/constants/types' import openUrl from '@/util/open-url' -import {useFSState} from '@/constants/fs' -import * as FS from '@/constants/fs' +import {useFSState} from '@/stores/fs' +import * as FS from '@/stores/fs' type OwnProps = { path: T.FS.Path @@ -33,7 +33,7 @@ const ConnectedBanner = (ownProps: OwnProps) => { }, [startManualConflictResolution, path]) const openPathInSystemFileManagerDesktop = useFSState( - s => s.dispatch.dynamic.openPathInSystemFileManagerDesktop + s => s.dispatch.defer.openPathInSystemFileManagerDesktop ) const openInSystemFileManager = React.useCallback( diff --git a/shared/fs/banner/public-reminder.tsx b/shared/fs/banner/public-reminder.tsx index 16783c602a96..717e78175cf4 100644 --- a/shared/fs/banner/public-reminder.tsx +++ b/shared/fs/banner/public-reminder.tsx @@ -1,8 +1,8 @@ import * as React from 'react' import * as Kb from '@/common-adapters' import * as T from '@/constants/types' -import {useFSState} from '@/constants/fs' -import * as FS from '@/constants/fs' +import {useFSState} from '@/stores/fs' +import * as FS from '@/stores/fs' type Props = { path: T.FS.Path diff --git a/shared/fs/banner/reset-banner.tsx b/shared/fs/banner/reset-banner.tsx index 91e09bfd1b13..3606d16b8f2c 100644 --- a/shared/fs/banner/reset-banner.tsx +++ b/shared/fs/banner/reset-banner.tsx @@ -4,10 +4,10 @@ import * as T from '@/constants/types' import {folderNameWithoutUsers} from '@/util/kbfs' import * as Kb from '@/common-adapters' import * as RowTypes from '@/fs/browser/rows/types' -import {useTrackerState} from '@/constants/tracker2' -import {useFSState} from '@/constants/fs' -import * as FS from '@/constants/fs' -import {useProfileState} from '@/constants/profile' +import {useTrackerState} from '@/stores/tracker2' +import {useFSState} from '@/stores/fs' +import * as FS from '@/stores/fs' +import {useProfileState} from '@/stores/profile' type OwnProps = {path: T.FS.Path} @@ -21,7 +21,7 @@ const ConnectedBanner = (ownProps: OwnProps) => { if (pathElems.length < 3) return const filteredPathName = folderNameWithoutUsers(pathElems[2] ?? '', users) const filteredPath = T.FS.stringToPath(['', pathElems[0], pathElems[1], filteredPathName].join('/')) - FS.makeActionForOpenPathInFilesTab(filteredPath) + FS.navToPath(filteredPath) }, [] ) diff --git a/shared/fs/banner/system-file-manager-integration-banner/container.tsx b/shared/fs/banner/system-file-manager-integration-banner/container.tsx index 02877de920b0..232e32b74c5d 100644 --- a/shared/fs/banner/system-file-manager-integration-banner/container.tsx +++ b/shared/fs/banner/system-file-manager-integration-banner/container.tsx @@ -3,8 +3,8 @@ import * as C from '@/constants' import * as T from '@/constants/types' import * as Kb from '@/common-adapters' import * as Kbfs from '@/fs/common' -import {useFSState} from '@/constants/fs' -import * as FS from '@/constants/fs' +import {useFSState} from '@/stores/fs' +import * as FS from '@/stores/fs' type OwnProps = {alwaysShow?: boolean} @@ -14,7 +14,7 @@ const SFMIContainer = (op: OwnProps) => { driverDisable: s.dispatch.driverDisable, driverEnable: s.dispatch.driverEnable, driverStatus: s.sfmi.driverStatus, - setSfmiBannerDismissedDesktop: s.dispatch.dynamic.setSfmiBannerDismissedDesktop, + setSfmiBannerDismissedDesktop: s.dispatch.defer.setSfmiBannerDismissedDesktop, settings: s.settings, })) ) @@ -218,7 +218,7 @@ const JustEnabled = ({onDismiss}: JustEnabledProps) => { const {displayingMountDir, openLocalPathInSystemFileManagerDesktop} = useFSState( C.useShallow(s => ({ displayingMountDir: s.sfmi.preferredMountDirs[0] ?? '', - openLocalPathInSystemFileManagerDesktop: s.dispatch.dynamic.openLocalPathInSystemFileManagerDesktop, + openLocalPathInSystemFileManagerDesktop: s.dispatch.defer.openLocalPathInSystemFileManagerDesktop, })) ) const open = displayingMountDir diff --git a/shared/fs/banner/system-file-manager-integration-banner/kext-permission-popup.tsx b/shared/fs/banner/system-file-manager-integration-banner/kext-permission-popup.tsx index 759aa1d2fe21..c294c7b776ea 100644 --- a/shared/fs/banner/system-file-manager-integration-banner/kext-permission-popup.tsx +++ b/shared/fs/banner/system-file-manager-integration-banner/kext-permission-popup.tsx @@ -2,11 +2,11 @@ import * as React from 'react' import * as C from '@/constants' import * as T from '@/constants/types' import * as Kb from '@/common-adapters' -import {useFSState} from '@/constants/fs' +import {useFSState} from '@/stores/fs' const InstallSecurityPrefs = () => { const driverStatus = useFSState(s => s.sfmi.driverStatus) - const openSecurityPreferencesDesktop = useFSState(s => s.dispatch.dynamic.openSecurityPreferencesDesktop) + const openSecurityPreferencesDesktop = useFSState(s => s.dispatch.defer.openSecurityPreferencesDesktop) const onCancel = C.useRouterState(s => s.dispatch.navigateUp) const openSecurityPrefs = React.useCallback( () => openSecurityPreferencesDesktop?.(), diff --git a/shared/fs/browser/destination-picker.tsx b/shared/fs/browser/destination-picker.tsx index 9a407bb950b8..b00bdd56608b 100644 --- a/shared/fs/browser/destination-picker.tsx +++ b/shared/fs/browser/destination-picker.tsx @@ -8,8 +8,8 @@ import NavHeaderTitle from '@/fs/nav-header/title' import Root from './root' import Rows from './rows/rows-container' import {OriginalOrCompressedButton} from '@/incoming-share' -import {useFSState} from '@/constants/fs' -import * as FS from '@/constants/fs' +import {useFSState} from '@/stores/fs' +import * as FS from '@/stores/fs' type OwnProps = {index: number} diff --git a/shared/fs/browser/index.tsx b/shared/fs/browser/index.tsx index c4e8df63334d..29410ae9c1bc 100644 --- a/shared/fs/browser/index.tsx +++ b/shared/fs/browser/index.tsx @@ -10,8 +10,8 @@ import PublicReminder from '../banner/public-reminder' import Root from './root' import Rows from './rows/rows-container' import {asRows as resetBannerAsRows} from '../banner/reset-banner' -import {useFSState} from '@/constants/fs' -import * as FS from '@/constants/fs' +import {useFSState} from '@/stores/fs' +import * as FS from '@/stores/fs' type OwnProps = {path: T.FS.Path} @@ -68,7 +68,7 @@ const DragAndDrop = React.memo(function DragAndDrop(p: { rejectReason?: string }) { const {children, path, rejectReason} = p - const uploadFromDragAndDrop = useFSState(s => s.dispatch.dynamic.uploadFromDragAndDropDesktop) + const uploadFromDragAndDrop = useFSState(s => s.dispatch.defer.uploadFromDragAndDropDesktop) const onAttach = React.useCallback( (localPaths: Array) => uploadFromDragAndDrop?.(path, localPaths), [path, uploadFromDragAndDrop] diff --git a/shared/fs/browser/offline.tsx b/shared/fs/browser/offline.tsx index 51f045b4fdba..3a3273e64ffe 100644 --- a/shared/fs/browser/offline.tsx +++ b/shared/fs/browser/offline.tsx @@ -1,8 +1,8 @@ import * as Kb from '@/common-adapters' import * as T from '@/constants/types' import TopBar from '../top-bar' -import {useFSState} from '@/constants/fs' -import * as FS from '@/constants/fs' +import {useFSState} from '@/stores/fs' +import * as FS from '@/stores/fs' type Props = { path: T.FS.Path diff --git a/shared/fs/browser/root.tsx b/shared/fs/browser/root.tsx index af39fbc45ea1..1a1cef90548c 100644 --- a/shared/fs/browser/root.tsx +++ b/shared/fs/browser/root.tsx @@ -5,9 +5,9 @@ import TlfType from './rows/tlf-type' import Tlf from './rows/tlf' import SfmiBanner from '../banner/system-file-manager-integration-banner/container' import {WrapRow} from './rows/rows' -import {useFSState} from '@/constants/fs' -import * as FS from '@/constants/fs' -import {useCurrentUserState} from '@/constants/current-user' +import {useFSState} from '@/stores/fs' +import * as FS from '@/stores/fs' +import {useCurrentUserState} from '@/stores/current-user' type Props = { destinationPickerIndex?: number diff --git a/shared/fs/browser/rows/editing.tsx b/shared/fs/browser/rows/editing.tsx index e6e19567d756..99ac037113a7 100644 --- a/shared/fs/browser/rows/editing.tsx +++ b/shared/fs/browser/rows/editing.tsx @@ -3,8 +3,8 @@ import * as React from 'react' import * as T from '@/constants/types' import * as Kb from '@/common-adapters' import {rowStyles} from './common' -import {useFSState} from '@/constants/fs' -import * as FS from '@/constants/fs' +import {useFSState} from '@/stores/fs' +import * as FS from '@/stores/fs' type Props = { editID: T.FS.EditID diff --git a/shared/fs/browser/rows/rows-container.tsx b/shared/fs/browser/rows/rows-container.tsx index 9c3852859b96..37019066d45d 100644 --- a/shared/fs/browser/rows/rows-container.tsx +++ b/shared/fs/browser/rows/rows-container.tsx @@ -4,9 +4,9 @@ import * as RowTypes from './types' import {sortRowItems, type SortableRowItem} from './sort' import Rows, {type Props} from './rows' import {asRows as topBarAsRow} from '../../top-bar' -import {useFSState} from '@/constants/fs' -import * as FS from '@/constants/fs' -import {useCurrentUserState} from '@/constants/current-user' +import {useFSState} from '@/stores/fs' +import * as FS from '@/stores/fs' +import {useCurrentUserState} from '@/stores/current-user' type OwnProps = { path: T.FS.Path // path to the parent folder containering the rows, diff --git a/shared/fs/browser/rows/still.tsx b/shared/fs/browser/rows/still.tsx index c5b1655ff3b4..f0a2ad9abbef 100644 --- a/shared/fs/browser/rows/still.tsx +++ b/shared/fs/browser/rows/still.tsx @@ -4,8 +4,8 @@ import {useOpen} from '@/fs/common/use-open' import {rowStyles, StillCommon} from './common' import * as Kb from '@/common-adapters' import {LastModifiedLine, Filename} from '@/fs/common' -import {useFSState} from '@/constants/fs' -import * as FS from '@/constants/fs' +import {useFSState} from '@/stores/fs' +import * as FS from '@/stores/fs' type OwnProps = { destinationPickerIndex?: number diff --git a/shared/fs/browser/rows/tlf-type.tsx b/shared/fs/browser/rows/tlf-type.tsx index 908334f54352..e60d181d6f39 100644 --- a/shared/fs/browser/rows/tlf-type.tsx +++ b/shared/fs/browser/rows/tlf-type.tsx @@ -1,6 +1,6 @@ import * as T from '@/constants/types' import {useOpen} from '@/fs/common/use-open' -import * as FS from '@/constants/fs' +import * as FS from '@/stores/fs' import {rowStyles, StillCommon} from './common' import * as Kb from '@/common-adapters' diff --git a/shared/fs/browser/rows/tlf.tsx b/shared/fs/browser/rows/tlf.tsx index 001ba2b64cdd..1092deddbaff 100644 --- a/shared/fs/browser/rows/tlf.tsx +++ b/shared/fs/browser/rows/tlf.tsx @@ -3,9 +3,9 @@ import {useOpen} from '@/fs/common/use-open' import {rowStyles, StillCommon} from './common' import * as Kb from '@/common-adapters' import {useFsPathMetadata, TlfInfoLine, Filename} from '@/fs/common' -import {useFSState} from '@/constants/fs' -import * as FS from '@/constants/fs' -import {useCurrentUserState} from '@/constants/current-user' +import {useFSState} from '@/stores/fs' +import * as FS from '@/stores/fs' +import {useCurrentUserState} from '@/stores/current-user' export type OwnProps = { destinationPickerIndex?: number diff --git a/shared/fs/common/errs-container.tsx b/shared/fs/common/errs-container.tsx index 7e86e484dd46..ae768869412a 100644 --- a/shared/fs/common/errs-container.tsx +++ b/shared/fs/common/errs-container.tsx @@ -1,7 +1,7 @@ import * as React from 'react' import * as Kb from '@/common-adapters' import * as C from '@/constants' -import {useFSState} from '@/constants/fs' +import {useFSState} from '@/stores/fs' const ErrsContainer = () => { const {_errors, _dismiss} = useFSState( diff --git a/shared/fs/common/folder-view-filter-icon.tsx b/shared/fs/common/folder-view-filter-icon.tsx index a843e380bf81..135aff8989f2 100644 --- a/shared/fs/common/folder-view-filter-icon.tsx +++ b/shared/fs/common/folder-view-filter-icon.tsx @@ -1,8 +1,8 @@ import * as T from '@/constants/types' import * as Kb from '@/common-adapters' import type * as Styles from '@/styles' -import {useFSState} from '@/constants/fs' -import * as FS from '@/constants/fs' +import {useFSState} from '@/stores/fs' +import * as FS from '@/stores/fs' type Props = { onClick: () => void diff --git a/shared/fs/common/folder-view-filter.tsx b/shared/fs/common/folder-view-filter.tsx index 7cf7dc787a71..45e1c6d835da 100644 --- a/shared/fs/common/folder-view-filter.tsx +++ b/shared/fs/common/folder-view-filter.tsx @@ -3,8 +3,8 @@ import * as C from '@/constants' import * as Kb from '@/common-adapters' import * as React from 'react' import debounce from 'lodash/debounce' -import {useFSState} from '@/constants/fs' -import * as FS from '@/constants/fs' +import {useFSState} from '@/stores/fs' +import * as FS from '@/stores/fs' type Props = { onCancel?: () => void diff --git a/shared/fs/common/hooks.tsx b/shared/fs/common/hooks.tsx index be2ac0bcaa39..bb30566beb4f 100644 --- a/shared/fs/common/hooks.tsx +++ b/shared/fs/common/hooks.tsx @@ -3,8 +3,8 @@ import * as React from 'react' import * as T from '@/constants/types' import * as Kb from '@/common-adapters' import logger from '@/logger' -import * as FS from '@/constants/fs' -import {useFSState} from '@/constants/fs' +import * as FS from '@/stores/fs' +import {useFSState} from '@/stores/fs' const isPathItem = (path: T.FS.Path) => T.FS.getPathLevel(path) > 2 || FS.hasSpecialFileElement(path) @@ -185,8 +185,8 @@ export const useFsWatchDownloadForMobile = C.isMobile const {dlState, finishedDownloadWithIntentMobile, finishedRegularDownloadMobile} = useFSState( C.useShallow(s => ({ dlState: s.downloads.state.get(downloadID) || FS.emptyDownloadState, - finishedDownloadWithIntentMobile: s.dispatch.dynamic.finishedDownloadWithIntentMobile, - finishedRegularDownloadMobile: s.dispatch.dynamic.finishedRegularDownloadMobile, + finishedDownloadWithIntentMobile: s.dispatch.defer.finishedDownloadWithIntentMobile, + finishedRegularDownloadMobile: s.dispatch.defer.finishedRegularDownloadMobile, })) ) const finished = dlState !== FS.emptyDownloadState && !FS.downloadIsOngoing(dlState) diff --git a/shared/fs/common/item-icon.tsx b/shared/fs/common/item-icon.tsx index 303b8e533dc2..8b0d42789caf 100644 --- a/shared/fs/common/item-icon.tsx +++ b/shared/fs/common/item-icon.tsx @@ -1,8 +1,8 @@ import * as T from '@/constants/types' import * as Kb from '@/common-adapters' import type {IconType} from '@/common-adapters/icon' -import {useFSState} from '@/constants/fs' -import * as FS from '@/constants/fs' +import {useFSState} from '@/stores/fs' +import * as FS from '@/stores/fs' export type Size = 96 | 48 | 32 | 16 type SizeString = '96' | '48' | '32' | '16' diff --git a/shared/fs/common/kbfs-path.tsx b/shared/fs/common/kbfs-path.tsx index bc000fb40d44..c49cd7cd96e7 100644 --- a/shared/fs/common/kbfs-path.tsx +++ b/shared/fs/common/kbfs-path.tsx @@ -1,7 +1,7 @@ import * as React from 'react' import * as Kb from '@/common-adapters' import type * as T from '@/constants/types' -import * as FS from '@/constants/fs' +import * as FS from '@/stores/fs' import PathInfo from './path-info' import PathItemInfo from './path-item-info' @@ -18,7 +18,7 @@ type PopupProps = Props & { } const useOpenInFilesTab = (path: T.FS.Path) => { - return React.useCallback(() => FS.makeActionForOpenPathInFilesTab(path), [path]) + return React.useCallback(() => FS.navToPath(path), [path]) } const KbfsPathPopup = (props: PopupProps) => { diff --git a/shared/fs/common/last-modified-line.tsx b/shared/fs/common/last-modified-line.tsx index 7a8d7893f508..a11c6be3625f 100644 --- a/shared/fs/common/last-modified-line.tsx +++ b/shared/fs/common/last-modified-line.tsx @@ -1,8 +1,8 @@ import * as Kb from '@/common-adapters' import type * as T from '@/constants/types' import {formatTimeForFS} from '@/util/timestamp' -import {useFSState} from '@/constants/fs' -import * as FS from '@/constants/fs' +import {useFSState} from '@/stores/fs' +import * as FS from '@/stores/fs' export type OwnProps = { path: T.FS.Path diff --git a/shared/fs/common/open-in-system-file-manager.tsx b/shared/fs/common/open-in-system-file-manager.tsx index 42758a5a7420..44daf606fd2e 100644 --- a/shared/fs/common/open-in-system-file-manager.tsx +++ b/shared/fs/common/open-in-system-file-manager.tsx @@ -3,13 +3,13 @@ import * as Kb from '@/common-adapters' import * as T from '@/constants/types' import * as C from '@/constants' import SystemFileManagerIntegrationPopup from './sfmi-popup' -import {useFSState} from '@/constants/fs' +import {useFSState} from '@/stores/fs' type Props = {path: T.FS.Path} const OpenInSystemFileManager = React.memo(function OpenInSystemFileManager({path}: Props) { const openPathInSystemFileManagerDesktop = useFSState( - s => s.dispatch.dynamic.openPathInSystemFileManagerDesktop + s => s.dispatch.defer.openPathInSystemFileManagerDesktop ) const openInSystemFileManager = React.useCallback( () => openPathInSystemFileManagerDesktop?.(path), diff --git a/shared/fs/common/path-info.tsx b/shared/fs/common/path-info.tsx index 16e0f1ef9d51..25182ab0625a 100644 --- a/shared/fs/common/path-info.tsx +++ b/shared/fs/common/path-info.tsx @@ -1,8 +1,8 @@ import * as T from '@/constants/types' import {useFsPathInfo} from './hooks' import * as Kb from '@/common-adapters' -import {useFSState} from '@/constants/fs' -import * as FS from '@/constants/fs' +import {useFSState} from '@/stores/fs' +import * as FS from '@/stores/fs' type PathInfoProps = { containerStyle?: Kb.Styles.StylesCrossPlatform diff --git a/shared/fs/common/path-item-action/choose-view.tsx b/shared/fs/common/path-item-action/choose-view.tsx index 7029ddf85d26..23421a3c10cd 100644 --- a/shared/fs/common/path-item-action/choose-view.tsx +++ b/shared/fs/common/path-item-action/choose-view.tsx @@ -2,7 +2,7 @@ import * as T from '@/constants/types' import type {FloatingMenuProps} from './types' import Menu from './menu-container' import Confirm from './confirm' -import {useFSState} from '@/constants/fs' +import {useFSState} from '@/stores/fs' type OwnProps = { floatingMenuProps: FloatingMenuProps diff --git a/shared/fs/common/path-item-action/confirm-delete.tsx b/shared/fs/common/path-item-action/confirm-delete.tsx index fbc3d3afe1aa..d47af5f53184 100644 --- a/shared/fs/common/path-item-action/confirm-delete.tsx +++ b/shared/fs/common/path-item-action/confirm-delete.tsx @@ -2,8 +2,8 @@ import * as Kb from '@/common-adapters' import * as T from '@/constants/types' import * as C from '@/constants' import * as React from 'react' -import {useFSState} from '@/constants/fs' -import * as FS from '@/constants/fs' +import {useFSState} from '@/stores/fs' +import * as FS from '@/stores/fs' export type Props = { onBack: () => void diff --git a/shared/fs/common/path-item-action/confirm.tsx b/shared/fs/common/path-item-action/confirm.tsx index 73e19306d67c..c3fb1a363ea6 100644 --- a/shared/fs/common/path-item-action/confirm.tsx +++ b/shared/fs/common/path-item-action/confirm.tsx @@ -3,8 +3,8 @@ import * as React from 'react' import * as T from '@/constants/types' import type {FloatingMenuProps} from './types' import * as Kb from '@/common-adapters' -import {useFSState} from '@/constants/fs' -import * as FS from '@/constants/fs' +import {useFSState} from '@/stores/fs' +import * as FS from '@/stores/fs' type OwnProps = { floatingMenuProps: FloatingMenuProps diff --git a/shared/fs/common/path-item-action/index.tsx b/shared/fs/common/path-item-action/index.tsx index 46087ca280e2..00c24879ddbb 100644 --- a/shared/fs/common/path-item-action/index.tsx +++ b/shared/fs/common/path-item-action/index.tsx @@ -4,8 +4,8 @@ import * as C from '@/constants' import * as Kb from '@/common-adapters' import ChooseView from './choose-view' import type {SizeType} from '@/common-adapters/icon' -import {useFSState} from '@/constants/fs' -import * as FS from '@/constants/fs' +import {useFSState} from '@/stores/fs' +import * as FS from '@/stores/fs' export type ClickableProps = { onClick: () => void diff --git a/shared/fs/common/path-item-action/layout.tsx b/shared/fs/common/path-item-action/layout.tsx index ec63a3a52b64..89a91060c446 100644 --- a/shared/fs/common/path-item-action/layout.tsx +++ b/shared/fs/common/path-item-action/layout.tsx @@ -1,6 +1,6 @@ import * as C from '@/constants' import * as T from '@/constants/types' -import * as FS from '@/constants/fs' +import * as FS from '@/stores/fs' export type Layout = { archive: boolean diff --git a/shared/fs/common/path-item-action/menu-container.tsx b/shared/fs/common/path-item-action/menu-container.tsx index 996f366ea73c..74d8f8c7180b 100644 --- a/shared/fs/common/path-item-action/menu-container.tsx +++ b/shared/fs/common/path-item-action/menu-container.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as Kb from '@/common-adapters' import * as Kbfs from '@/fs/common/hooks' import * as React from 'react' @@ -8,9 +8,9 @@ import * as Util from '@/util/kbfs' import Header from './header' import type {FloatingMenuProps} from './types' import {getRootLayout, getShareLayout} from './layout' -import {useFSState} from '@/constants/fs' -import * as FS from '@/constants/fs' -import {useCurrentUserState} from '@/constants/current-user' +import {useFSState} from '@/stores/fs' +import * as FS from '@/stores/fs' +import {useCurrentUserState} from '@/stores/current-user' type OwnProps = { floatingMenuProps: FloatingMenuProps @@ -32,7 +32,7 @@ const Container = (op: OwnProps) => { const fileContext = s.fileContext.get(path) || FS.emptyFileContext const {cancelDownload, setPathItemActionMenuView, download, newFolderRow} = s.dispatch const {favoriteIgnore, startRename, dismissDownload} = s.dispatch - const {openPathInSystemFileManagerDesktop} = s.dispatch.dynamic + const {openPathInSystemFileManagerDesktop} = s.dispatch.defer const sfmiEnabled = s.sfmi.driverStatus.type === T.FS.DriverStatusType.Enabled return { cancelDownload, diff --git a/shared/fs/common/path-item-info.tsx b/shared/fs/common/path-item-info.tsx index 75d4bc69fb06..bd9d22d388f1 100644 --- a/shared/fs/common/path-item-info.tsx +++ b/shared/fs/common/path-item-info.tsx @@ -6,8 +6,8 @@ import ItemIcon from './item-icon' import CommaSeparatedName from './comma-separated-name' import {pluralize} from '@/util/string' import {useFsChildren, useFsPathMetadata, useFsOnlineStatus, useFsSoftError} from './hooks' -import {useFSState} from '@/constants/fs' -import * as FS from '@/constants/fs' +import {useFSState} from '@/stores/fs' +import * as FS from '@/stores/fs' type Props = { containerStyle?: Kb.Styles.StylesCrossPlatform diff --git a/shared/fs/common/path-status-icon-container.tsx b/shared/fs/common/path-status-icon-container.tsx index 9a1440a507e2..0e6cb624b358 100644 --- a/shared/fs/common/path-status-icon-container.tsx +++ b/shared/fs/common/path-status-icon-container.tsx @@ -1,8 +1,8 @@ import * as T from '@/constants/types' import * as C from '@/constants' import PathStatusIcon from './path-status-icon' -import {useFSState} from '@/constants/fs' -import * as FS from '@/constants/fs' +import {useFSState} from '@/stores/fs' +import * as FS from '@/stores/fs' type OwnPropsPathItem = { path: T.FS.Path diff --git a/shared/fs/common/refresh-driver-status-on-mount.tsx b/shared/fs/common/refresh-driver-status-on-mount.tsx index 9ce3cc4d12d5..a710da6894c8 100644 --- a/shared/fs/common/refresh-driver-status-on-mount.tsx +++ b/shared/fs/common/refresh-driver-status-on-mount.tsx @@ -1,8 +1,8 @@ import * as React from 'react' -import {useFSState} from '@/constants/fs' +import {useFSState} from '@/stores/fs' const RefreshDriverStatusOnMount = () => { - const refreshDriverStatusDesktop = useFSState(s => s.dispatch.dynamic.refreshDriverStatusDesktop) + const refreshDriverStatusDesktop = useFSState(s => s.dispatch.defer.refreshDriverStatusDesktop) const refresh = React.useCallback(() => refreshDriverStatusDesktop?.(), [refreshDriverStatusDesktop]) React.useEffect(() => { diff --git a/shared/fs/common/sfmi-popup.tsx b/shared/fs/common/sfmi-popup.tsx index 9fbf8764643b..33aa3bbe952b 100644 --- a/shared/fs/common/sfmi-popup.tsx +++ b/shared/fs/common/sfmi-popup.tsx @@ -3,7 +3,7 @@ import * as React from 'react' import * as Kb from '@/common-adapters' import * as T from '@/constants/types' import {useFuseClosedSourceConsent} from './hooks' -import {useFSState} from '@/constants/fs' +import {useFSState} from '@/stores/fs' type Props = { mode: 'Icon' | 'Button' diff --git a/shared/fs/common/tlf-info-line-container.tsx b/shared/fs/common/tlf-info-line-container.tsx index 8b84d8962edd..09803b935e53 100644 --- a/shared/fs/common/tlf-info-line-container.tsx +++ b/shared/fs/common/tlf-info-line-container.tsx @@ -1,8 +1,8 @@ import * as T from '@/constants/types' import TlfInfoLine from './tlf-info-line' -import {useFSState} from '@/constants/fs' -import * as FS from '@/constants/fs' -import {useCurrentUserState} from '@/constants/current-user' +import {useFSState} from '@/stores/fs' +import * as FS from '@/stores/fs' +import {useCurrentUserState} from '@/stores/current-user' export type OwnProps = { path: T.FS.Path diff --git a/shared/fs/common/upload-button.tsx b/shared/fs/common/upload-button.tsx index 593688df6905..3021fbfbfa05 100644 --- a/shared/fs/common/upload-button.tsx +++ b/shared/fs/common/upload-button.tsx @@ -3,8 +3,8 @@ import * as T from '@/constants/types' import * as C from '@/constants' import * as Kb from '@/common-adapters' import type * as Styles from '@/styles' -import {useFSState} from '@/constants/fs' -import * as FS from '@/constants/fs' +import {useFSState} from '@/stores/fs' +import * as FS from '@/stores/fs' type OwnProps = { path: T.FS.Path @@ -73,8 +73,8 @@ const UploadButton = (props: UploadButtonProps) => { const Container = (ownProps: OwnProps) => { const _pathItem = useFSState(s => FS.getPathItem(s.pathItems, ownProps.path)) - const openAndUploadDesktop = useFSState(s => s.dispatch.dynamic.openAndUploadDesktop) - const pickAndUploadMobile = useFSState(s => s.dispatch.dynamic.pickAndUploadMobile) + const openAndUploadDesktop = useFSState(s => s.dispatch.defer.openAndUploadDesktop) + const pickAndUploadMobile = useFSState(s => s.dispatch.defer.pickAndUploadMobile) const _openAndUploadBoth = () => { openAndUploadDesktop?.(T.FS.OpenDialogType.Both, ownProps.path) } diff --git a/shared/fs/common/use-open.tsx b/shared/fs/common/use-open.tsx index 0e15b1719686..74a9ea2be194 100644 --- a/shared/fs/common/use-open.tsx +++ b/shared/fs/common/use-open.tsx @@ -1,8 +1,8 @@ import * as C from '@/constants' import * as T from '@/constants/types' import {useSafeNavigation} from '@/util/safe-navigation' -import {useFSState} from '@/constants/fs' -import * as FS from '@/constants/fs' +import {useFSState} from '@/stores/fs' +import * as FS from '@/stores/fs' type Props = { path: T.FS.Path diff --git a/shared/fs/filepreview/bare-preview.tsx b/shared/fs/filepreview/bare-preview.tsx index bcbe04f750c7..f9d95b402178 100644 --- a/shared/fs/filepreview/bare-preview.tsx +++ b/shared/fs/filepreview/bare-preview.tsx @@ -1,7 +1,7 @@ import * as C from '@/constants' import * as T from '@/constants/types' import * as Kb from '@/common-adapters' -import * as FS from '@/constants/fs' +import * as FS from '@/stores/fs' import Footer from '../footer/footer' import View from './view' import * as Kbfs from '../common' diff --git a/shared/fs/filepreview/default-view.tsx b/shared/fs/filepreview/default-view.tsx index 41d252be7d54..3fa036521c01 100644 --- a/shared/fs/filepreview/default-view.tsx +++ b/shared/fs/filepreview/default-view.tsx @@ -3,8 +3,8 @@ import * as C from '@/constants' import * as Kb from '@/common-adapters' import {PathItemAction, LastModifiedLine, ItemIcon, type ClickableProps} from '../common' import {hasShare} from '../common/path-item-action/layout' -import * as FS from '@/constants/fs' -import {useFSState} from '@/constants/fs' +import * as FS from '@/stores/fs' +import {useFSState} from '@/stores/fs' type OwnProps = {path: T.FS.Path} @@ -19,7 +19,7 @@ const Container = (ownProps: OwnProps) => { C.useShallow(s => ({ _download: s.dispatch.download, fileContext: s.fileContext.get(path) || FS.emptyFileContext, - openPathInSystemFileManagerDesktop: s.dispatch.dynamic.openPathInSystemFileManagerDesktop, + openPathInSystemFileManagerDesktop: s.dispatch.defer.openPathInSystemFileManagerDesktop, pathItem: FS.getPathItem(s.pathItems, path), sfmiEnabled: s.sfmi.driverStatus.type === T.FS.DriverStatusType.Enabled, })) diff --git a/shared/fs/filepreview/view.tsx b/shared/fs/filepreview/view.tsx index 2765510266e7..aad5f4757683 100644 --- a/shared/fs/filepreview/view.tsx +++ b/shared/fs/filepreview/view.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as React from 'react' import * as T from '@/constants/types' import DefaultView from './default-view' @@ -7,8 +7,8 @@ import TextView from './text-view' import AVView from './av-view' import PdfView from './pdf-view' import * as Kb from '@/common-adapters' -import * as FS from '@/constants/fs' -import {useFSState} from '@/constants/fs' +import * as FS from '@/stores/fs' +import {useFSState} from '@/stores/fs' type Props = { path: T.FS.Path diff --git a/shared/fs/footer/download.tsx b/shared/fs/footer/download.tsx index 3d2177cbd799..9cf91a45cefb 100644 --- a/shared/fs/footer/download.tsx +++ b/shared/fs/footer/download.tsx @@ -4,8 +4,8 @@ import * as C from '@/constants' import * as T from '@/constants/types' import DownloadWrapper from './download-wrapper' import {formatDurationFromNowTo} from '@/util/timestamp' -import * as FS from '@/constants/fs' -import {useFSState} from '@/constants/fs' +import * as FS from '@/stores/fs' +import {useFSState} from '@/stores/fs' export type Props = { downloadID: string @@ -37,7 +37,7 @@ const Download = (props: Props) => { cancelDownload: s.dispatch.cancelDownload, dismissDownload: s.dispatch.dismissDownload, dlState: s.downloads.state.get(props.downloadID) || FS.emptyDownloadState, - openLocalPathInSystemFileManagerDesktop: s.dispatch.dynamic.openLocalPathInSystemFileManagerDesktop, + openLocalPathInSystemFileManagerDesktop: s.dispatch.defer.openLocalPathInSystemFileManagerDesktop, })) ) const open = dlState.localPath diff --git a/shared/fs/footer/downloads.tsx b/shared/fs/footer/downloads.tsx index 7983b96c0020..f00942abd8b0 100644 --- a/shared/fs/footer/downloads.tsx +++ b/shared/fs/footer/downloads.tsx @@ -2,7 +2,7 @@ import * as Kb from '@/common-adapters' import * as C from '@/constants' import * as Kbfs from '../common' import Download from './download' -import {useFSState} from '@/constants/fs' +import {useFSState} from '@/stores/fs' const Mobile = () => { Kbfs.useFsDownloadStatus() @@ -33,7 +33,7 @@ const Desktop = () => { const {downloadIDs, openLocalPathInSystemFileManagerDesktop} = useFSState( C.useShallow(s => ({ downloadIDs: s.downloads.regularDownloads, - openLocalPathInSystemFileManagerDesktop: s.dispatch.dynamic.openLocalPathInSystemFileManagerDesktop, + openLocalPathInSystemFileManagerDesktop: s.dispatch.defer.openLocalPathInSystemFileManagerDesktop, })) ) const openDownloadFolder = () => openLocalPathInSystemFileManagerDesktop?.(C.downloadFolder) diff --git a/shared/fs/footer/proof-broken.tsx b/shared/fs/footer/proof-broken.tsx index 186b5106bb28..852d5b5f925d 100644 --- a/shared/fs/footer/proof-broken.tsx +++ b/shared/fs/footer/proof-broken.tsx @@ -1,7 +1,7 @@ import * as Kb from '@/common-adapters' import type * as T from '@/constants/types' -import * as FS from '@/constants/fs' -import {useUsersState} from '@/constants/users' +import * as FS from '@/stores/fs' +import {useUsersState} from '@/stores/users' type Props = {path: T.FS.Path} diff --git a/shared/fs/footer/upload-container.tsx b/shared/fs/footer/upload-container.tsx index 73bfd3b2d3ef..31193f51a235 100644 --- a/shared/fs/footer/upload-container.tsx +++ b/shared/fs/footer/upload-container.tsx @@ -2,8 +2,8 @@ import * as T from '@/constants/types' import Upload from './upload' import {useUploadCountdown} from './use-upload-countdown' import * as C from '@/constants' -import * as FS from '@/constants/fs' -import {useFSState} from '@/constants/fs' +import * as FS from '@/stores/fs' +import {useFSState} from '@/stores/fs' // NOTE flip this to show a button to debug the upload banner animations. const enableDebugUploadBanner = false as boolean diff --git a/shared/fs/index.tsx b/shared/fs/index.tsx index 304ce8342ced..78fa22597830 100644 --- a/shared/fs/index.tsx +++ b/shared/fs/index.tsx @@ -5,8 +5,8 @@ import Browser from './browser' import {NormalPreview} from './filepreview' import * as Kbfs from './common' import * as SimpleScreens from './simple-screens' -import {useFSState} from '@/constants/fs' -import * as FS from '@/constants/fs' +import {useFSState} from '@/stores/fs' +import * as FS from '@/stores/fs' type ChooseComponentProps = { emitBarePreview: () => void diff --git a/shared/fs/nav-header/actions.tsx b/shared/fs/nav-header/actions.tsx index 497c67488b7f..014b0c7e1331 100644 --- a/shared/fs/nav-header/actions.tsx +++ b/shared/fs/nav-header/actions.tsx @@ -3,8 +3,8 @@ import * as T from '@/constants/types' import * as React from 'react' import * as Kb from '@/common-adapters' import * as Kbfs from '../common' -import * as FS from '@/constants/fs' -import {useFSState} from '@/constants/fs' +import * as FS from '@/stores/fs' +import {useFSState} from '@/stores/fs' type Props = { onTriggerFilterMobile: () => void diff --git a/shared/fs/nav-header/main-banner.tsx b/shared/fs/nav-header/main-banner.tsx index 73150635e474..4c24a1390ca4 100644 --- a/shared/fs/nav-header/main-banner.tsx +++ b/shared/fs/nav-header/main-banner.tsx @@ -1,9 +1,9 @@ import * as C from '@/constants' import * as Kb from '@/common-adapters' import * as T from '@/constants/types' -import * as FS from '@/constants/fs' -import {useFSState} from '@/constants/fs' -import {useCurrentUserState} from '@/constants/current-user' +import * as FS from '@/stores/fs' +import {useFSState} from '@/stores/fs' +import {useCurrentUserState} from '@/stores/current-user' type Props = { onRetry: () => void diff --git a/shared/fs/nav-header/mobile-header.tsx b/shared/fs/nav-header/mobile-header.tsx index 1364d912bc1c..3868d0e25ba9 100644 --- a/shared/fs/nav-header/mobile-header.tsx +++ b/shared/fs/nav-header/mobile-header.tsx @@ -5,8 +5,8 @@ import * as Kbfs from '../common' import type * as T from '@/constants/types' import Actions from './actions' import MainBanner from './main-banner' -import * as FS from '@/constants/fs' -import {useFSState} from '@/constants/fs' +import * as FS from '@/stores/fs' +import {useFSState} from '@/stores/fs' /* * diff --git a/shared/fs/nav-header/title.tsx b/shared/fs/nav-header/title.tsx index a1419632345f..1bcb3e156a77 100644 --- a/shared/fs/nav-header/title.tsx +++ b/shared/fs/nav-header/title.tsx @@ -3,7 +3,7 @@ import * as T from '@/constants/types' import * as Kb from '@/common-adapters' import * as Kbfs from '../common' import {useSafeNavigation} from '@/util/safe-navigation' -import * as FS from '@/constants/fs' +import * as FS from '@/stores/fs' type Props = { path: T.FS.Path diff --git a/shared/fs/routes.tsx b/shared/fs/routes.tsx index f96c5d4f17c3..f9d6796ef092 100644 --- a/shared/fs/routes.tsx +++ b/shared/fs/routes.tsx @@ -1,7 +1,7 @@ import * as React from 'react' import * as T from '@/constants/types' import * as C from '@/constants' -import * as FS from '@/constants/fs' +import * as FS from '@/stores/fs' import {Actions, MainBanner, MobileHeader, Title} from './nav-header' const FsRoot = React.lazy(async () => import('.')) diff --git a/shared/fs/top-bar/loading.tsx b/shared/fs/top-bar/loading.tsx index 1aee292386c8..214ecb0a9539 100644 --- a/shared/fs/top-bar/loading.tsx +++ b/shared/fs/top-bar/loading.tsx @@ -1,8 +1,8 @@ import * as T from '@/constants/types' import * as C from '@/constants' import * as Kb from '@/common-adapters' -import * as FS from '@/constants/fs' -import {useFSState} from '@/constants/fs' +import * as FS from '@/stores/fs' +import {useFSState} from '@/stores/fs' // The behavior is to only show spinner when user first time lands on a screen // and when don't have the data that drives it yet. Since RPCs happen diff --git a/shared/fs/top-bar/sort.tsx b/shared/fs/top-bar/sort.tsx index f2057895d10f..30db41b3f6be 100644 --- a/shared/fs/top-bar/sort.tsx +++ b/shared/fs/top-bar/sort.tsx @@ -2,8 +2,8 @@ import * as C from '@/constants' import * as T from '@/constants/types' import * as React from 'react' import * as Kb from '@/common-adapters' -import * as FS from '@/constants/fs' -import {useFSState} from '@/constants/fs' +import * as FS from '@/stores/fs' +import {useFSState} from '@/stores/fs' type OwnProps = { path: T.FS.Path diff --git a/shared/fs/top-bar/sync-toggle.tsx b/shared/fs/top-bar/sync-toggle.tsx index 437300313c8d..cdc1e573a46d 100644 --- a/shared/fs/top-bar/sync-toggle.tsx +++ b/shared/fs/top-bar/sync-toggle.tsx @@ -2,8 +2,8 @@ import * as C from '@/constants' import * as T from '@/constants/types' import * as React from 'react' import * as Kb from '@/common-adapters' -import * as FS from '@/constants/fs' -import {useFSState} from '@/constants/fs' +import * as FS from '@/stores/fs' +import {useFSState} from '@/stores/fs' type OwnProps = { tlfPath: T.FS.Path diff --git a/shared/git/delete-repo.tsx b/shared/git/delete-repo.tsx index 88b1804a1f83..1be08287171f 100644 --- a/shared/git/delete-repo.tsx +++ b/shared/git/delete-repo.tsx @@ -1,7 +1,7 @@ import * as C from '@/constants' import * as Kb from '@/common-adapters' import * as React from 'react' -import * as Git from '@/constants/git' +import * as Git from '@/stores/git' type OwnProps = {id: string} diff --git a/shared/git/index.tsx b/shared/git/index.tsx index 168a9715ed72..aaf29607752f 100644 --- a/shared/git/index.tsx +++ b/shared/git/index.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Git from '@/constants/git' +import * as Git from '@/stores/git' import * as Kb from '@/common-adapters' import * as React from 'react' import Row, {NewContext} from './row' diff --git a/shared/git/nav-header.tsx b/shared/git/nav-header.tsx index 5dafe65dd6ca..26d239bb94c9 100644 --- a/shared/git/nav-header.tsx +++ b/shared/git/nav-header.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Git from '@/constants/git' +import * as Git from '@/stores/git' import * as React from 'react' import * as Kb from '@/common-adapters' diff --git a/shared/git/new-repo.tsx b/shared/git/new-repo.tsx index fd7ded7e79f5..2672731dc6a7 100644 --- a/shared/git/new-repo.tsx +++ b/shared/git/new-repo.tsx @@ -1,6 +1,6 @@ import * as C from '@/constants' -import * as Git from '@/constants/git' -import * as Teams from '@/constants/teams' +import * as Git from '@/stores/git' +import * as Teams from '@/stores/teams' import * as Kb from '@/common-adapters' import * as React from 'react' diff --git a/shared/git/row.tsx b/shared/git/row.tsx index 4402b545eb79..af0167090a53 100644 --- a/shared/git/row.tsx +++ b/shared/git/row.tsx @@ -1,13 +1,13 @@ import * as C from '@/constants' -import * as Git from '@/constants/git' -import * as Teams from '@/constants/teams' +import * as Git from '@/stores/git' +import * as Teams from '@/stores/teams' import * as T from '@/constants/types' import * as Kb from '@/common-adapters' import * as React from 'react' import openURL from '@/util/open-url' -import {useTrackerState} from '@/constants/tracker2' -import * as FS from '@/constants/fs' -import {useCurrentUserState} from '@/constants/current-user' +import {useTrackerState} from '@/stores/tracker2' +import * as FS from '@/stores/fs' +import {useCurrentUserState} from '@/stores/current-user' export const NewContext = React.createContext>(new Set()) @@ -28,7 +28,7 @@ const ConnectedRow = React.memo(function ConnectedRow(ownProps: OwnProps) { const isNew = React.useContext(NewContext).has(id) const you = useCurrentUserState(s => s.username) const setTeamRepoSettings = Git.useGitState(s => s.dispatch.setTeamRepoSettings) - const _onBrowseGitRepo = FS.makeActionForOpenPathInFilesTab + const _onBrowseGitRepo = FS.navToPath const navigateAppend = C.useRouterState(s => s.dispatch.navigateAppend) const {url: gitURL, repoID, channelName, teamname, chatDisabled} = git diff --git a/shared/git/select-channel.tsx b/shared/git/select-channel.tsx index a2cee7e5f3cb..a4b40c3af1af 100644 --- a/shared/git/select-channel.tsx +++ b/shared/git/select-channel.tsx @@ -1,5 +1,5 @@ -import * as Git from '@/constants/git' -import * as Teams from '@/constants/teams' +import * as Git from '@/stores/git' +import * as Teams from '@/stores/teams' import {useSafeNavigation} from '@/util/safe-navigation' import * as Kb from '@/common-adapters' import * as React from 'react' diff --git a/shared/incoming-share/index.tsx b/shared/incoming-share/index.tsx index 44c54b0619ab..6a6786f8bdf8 100644 --- a/shared/incoming-share/index.tsx +++ b/shared/incoming-share/index.tsx @@ -1,15 +1,15 @@ import * as C from '@/constants' -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as React from 'react' import * as Kb from '@/common-adapters' import * as T from '@/constants/types' import * as FsCommon from '@/fs/common' import {MobileSendToChat} from '../chat/send-to-chat' -import {navigateAppend} from '@/constants/router2/util' -import {settingsFeedbackTab} from '@/constants/settings' -import * as FS from '@/constants/fs' -import {useFSState} from '@/constants/fs' -import {useConfigState} from '@/constants/config' +import {settingsFeedbackTab} from '@/stores/settings' +import * as FS from '@/stores/fs' +import {useConfigState} from '@/stores/config' +import {useFSState} from '@/stores/fs' +import {useRouterState} from '@/stores/router2' export const OriginalOrCompressedButton = ({incomingShareItems}: IncomingShareProps) => { const originalTotalSize = incomingShareItems.reduce((bytes, item) => bytes + (item.originalSize ?? 0), 0) @@ -204,6 +204,7 @@ type IncomingShareWithSelectionProps = IncomingShareProps & { } const IncomingShare = (props: IncomingShareWithSelectionProps) => { + const navigateAppend = useRouterState(s => s.dispatch.navigateAppend) const useOriginalValue = useConfigState(s => s.incomingShareUseOriginal) const {sendPaths, text} = props.incomingShareItems.reduce( ({sendPaths, text}, item) => { @@ -223,7 +224,7 @@ const IncomingShare = (props: IncomingShareWithSelectionProps) => { // Pre-selected conv: navToThread + attachments directly (skip MobileSendToChat) const selectedConversationIDKey = props.selectedConversationIDKey - const canDirectNav = selectedConversationIDKey && Chat.isValidConversationIDKey(selectedConversationIDKey) + const canDirectNav = selectedConversationIDKey && T.Chat.isValidConversationIDKey(selectedConversationIDKey) const hasNavigatedRef = React.useRef(false) React.useEffect(() => { if (!canDirectNav || hasNavigatedRef.current) return @@ -246,7 +247,7 @@ const IncomingShare = (props: IncomingShareWithSelectionProps) => { selected: 'chatAttachmentGetTitles', }) } - }, [canDirectNav, selectedConversationIDKey, sendPaths, text]) + }, [canDirectNav, selectedConversationIDKey, sendPaths, text, navigateAppend]) const header = useHeader(props.incomingShareItems) const footer = useFooter(props.incomingShareItems) diff --git a/shared/ios/Keybase/Info.plist b/shared/ios/Keybase/Info.plist index fb65c31b600d..fc0bc7c9f8f2 100644 --- a/shared/ios/Keybase/Info.plist +++ b/shared/ios/Keybase/Info.plist @@ -19,7 +19,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 6.6.0 + 6.7.0 CFBundleSignature ???? CFBundleURLTypes diff --git a/shared/ios/KeybaseShare/Info.plist b/shared/ios/KeybaseShare/Info.plist index d7a771229ac6..b699e9a1d1b1 100644 --- a/shared/ios/KeybaseShare/Info.plist +++ b/shared/ios/KeybaseShare/Info.plist @@ -17,7 +17,7 @@ CFBundlePackageType XPC! CFBundleShortVersionString - 6.6.0 + 6.7.0 CFBundleVersion 200 NSExtension diff --git a/shared/ios/Podfile.lock b/shared/ios/Podfile.lock index 6c16b2c5df90..db6ab05d2fa1 100644 --- a/shared/ios/Podfile.lock +++ b/shared/ios/Podfile.lock @@ -9,7 +9,7 @@ PODS: - EXImageLoader (6.0.0): - ExpoModulesCore - React-Core - - Expo (54.0.31): + - Expo (54.0.33): - boost - DoubleConversion - ExpoModulesCore @@ -52,7 +52,7 @@ PODS: - ExpoModulesCore - ExpoFileSystem (19.0.21): - ExpoModulesCore - - ExpoFont (14.0.10): + - ExpoFont (14.0.11): - ExpoModulesCore - ExpoHaptics (15.0.8): - ExpoModulesCore @@ -116,8 +116,8 @@ PODS: - hermes-engine/Pre-built (= 0.81.5) - hermes-engine/Pre-built (0.81.5) - KBCommon (1.0.0) - - libavif/core (0.11.1) - - libavif/libdav1d (0.11.1): + - libavif/core (1.0.0) + - libavif/libdav1d (1.0.0): - libavif/core - libdav1d (>= 0.6.0) - libdav1d (1.2.0) @@ -133,15 +133,15 @@ PODS: - libwebp/sharpyuv (1.5.0) - libwebp/webp (1.5.0): - libwebp/sharpyuv - - lottie-ios (4.5.0) - - lottie-react-native (7.3.5): + - lottie-ios (4.6.0) + - lottie-react-native (7.3.6): - boost - DoubleConversion - fast_float - fmt - glog - hermes-engine - - lottie-ios (= 4.5.0) + - lottie-ios (= 4.6.0) - RCT-Folly - RCT-Folly/Fabric - RCTRequired @@ -1932,7 +1932,7 @@ PODS: - ReactCommon/turbomodule/core - SocketRocket - Yoga - - react-native-keyboard-controller (1.20.6): + - react-native-keyboard-controller (1.20.7): - boost - DoubleConversion - fast_float @@ -1950,7 +1950,7 @@ PODS: - React-graphics - React-ImageManager - React-jsi - - react-native-keyboard-controller/common (= 1.20.6) + - react-native-keyboard-controller/common (= 1.20.7) - React-NativeModulesApple - React-RCTFabric - React-renderercss @@ -1961,7 +1961,7 @@ PODS: - ReactCommon/turbomodule/core - SocketRocket - Yoga - - react-native-keyboard-controller/common (1.20.6): + - react-native-keyboard-controller/common (1.20.7): - boost - DoubleConversion - fast_float @@ -2814,7 +2814,7 @@ PODS: - RNWorklets - SocketRocket - Yoga - - RNScreens (4.18.0): + - RNScreens (4.23.0): - boost - DoubleConversion - fast_float @@ -2841,10 +2841,10 @@ PODS: - ReactCodegen - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - - RNScreens/common (= 4.18.0) + - RNScreens/common (= 4.23.0) - SocketRocket - Yoga - - RNScreens/common (4.18.0): + - RNScreens/common (4.23.0): - boost - DoubleConversion - fast_float @@ -2873,7 +2873,7 @@ PODS: - ReactCommon/turbomodule/core - SocketRocket - Yoga - - RNWorklets (0.7.1): + - RNWorklets (0.7.3): - boost - DoubleConversion - fast_float @@ -2900,10 +2900,10 @@ PODS: - ReactCodegen - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - - RNWorklets/worklets (= 0.7.1) + - RNWorklets/worklets (= 0.7.3) - SocketRocket - Yoga - - RNWorklets/worklets (0.7.1): + - RNWorklets/worklets (0.7.3): - boost - DoubleConversion - fast_float @@ -2930,10 +2930,10 @@ PODS: - ReactCodegen - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - - RNWorklets/worklets/apple (= 0.7.1) + - RNWorklets/worklets/apple (= 0.7.3) - SocketRocket - Yoga - - RNWorklets/worklets/apple (0.7.1): + - RNWorklets/worklets/apple (0.7.3): - boost - DoubleConversion - fast_float @@ -2962,9 +2962,9 @@ PODS: - ReactCommon/turbomodule/core - SocketRocket - Yoga - - SDWebImage (5.21.5): - - SDWebImage/Core (= 5.21.5) - - SDWebImage/Core (5.21.5) + - SDWebImage (5.21.6): + - SDWebImage/Core (= 5.21.6) + - SDWebImage/Core (5.21.6) - SDWebImageAVIFCoder (0.11.1): - libavif/core (>= 0.11.0) - SDWebImage (~> 5.10) @@ -3323,13 +3323,13 @@ SPEC CHECKSUMS: EXAV: b60fcf142fae6684d295bc28cd7cfcb3335570ea EXConstants: fce59a631a06c4151602843667f7cfe35f81e271 EXImageLoader: 189e3476581efe3ad4d1d3fb4735b7179eb26f05 - Expo: 5fe0862b0c3de267afbba769891aa65f64c2800c + Expo: ec20d60a9ba93352323b8f773c0e59bc7aa1c492 ExpoAsset: f867e55ceb428aab99e1e8c082b5aee7c159ea18 ExpoCamera: 6a326deb45ba840749652e4c15198317aa78497e ExpoClipboard: b36b287d8356887844bb08ed5c84b5979bb4dd1e ExpoContacts: 1c976e6c0b9be9b256ea038eba5c86f5707a73ce ExpoFileSystem: 858a44267a3e6e9057e0888ad7c7cfbf55d52063 - ExpoFont: 35ac6191ed86bbf56b3ebd2d9154eda9fad5b509 + ExpoFont: f543ce20a228dd702813668b1a07b46f51878d47 ExpoHaptics: d3a6375d8dcc3a1083d003bc2298ff654fafb536 ExpoImage: 686f972bff29525733aa13357f6691dc90aa03d8 ExpoImagePicker: 1af3e4e31512d2f34c95c2a3018a3edc40aee748 @@ -3346,11 +3346,11 @@ SPEC CHECKSUMS: glog: 5683914934d5b6e4240e497e0f4a3b42d1854183 hermes-engine: 9f4dfe93326146a1c99eb535b1cb0b857a3cd172 KBCommon: bd1f35bb07924f4cc57417b00dab6734747b6423 - libavif: 84bbb62fb232c3018d6f1bab79beea87e35de7b7 + libavif: 5f8e715bea24debec477006f21ef9e95432e254d libdav1d: 23581a4d8ec811ff171ed5e2e05cd27bad64c39f libwebp: 02b23773aedb6ff1fd38cec7a77b81414c6842a8 - lottie-ios: a881093fab623c467d3bce374367755c272bdd59 - lottie-react-native: b01c4b468aed88931afefbde03d790fc4f8b010a + lottie-ios: 8f959969761e9c45d70353667d00af0e5b9cadb3 + lottie-react-native: e542fe4b6e8eddcf893d5a8cef9f8b5c6d2f7331 RCT-Folly: 846fda9475e61ec7bcbf8a3fe81edfcaeb090669 RCTDeprecation: 5eb1d2eeff5fb91151e8a8eef45b6c7658b6c897 RCTRequired: cebcf9442fc296c9b89ac791dfd463021d9f6f23 @@ -3385,7 +3385,7 @@ SPEC CHECKSUMS: React-Mapbuffer: 017336879e2e0fb7537bbc08c24f34e2384c9260 React-microtasksnativemodule: 63ee6730cec233feab9cdcc0c100dc28a12e4165 react-native-kb: 078843e8c52d210aff0c50cbb4c4abe888c28ee2 - react-native-keyboard-controller: 40b005f1202d68566a2d058ddc148a3ea8d73288 + react-native-keyboard-controller: a9e423beaa20d00a4b9664b0fa37f1cdf3a59975 react-native-netinfo: 64f05e94821ee3f3adcd9b67b35c1480e5564915 react-native-safe-area-context: 0a3b034bb63a5b684dd2f5fffd3c90ef6ed41ee8 react-native-webview: cdce419e8022d0ef6f07db21890631258e7a9e6e @@ -3423,9 +3423,9 @@ SPEC CHECKSUMS: RNCPicker: 35fc66f352403cdfe99d53b541f5180482ca2bc5 RNGestureHandler: 043d32e7e7ae6bdcca06264682d5a078712903a1 RNReanimated: e79d7f42b76ba026e7dc5fb3e3f81991c590d3af - RNScreens: 98771ad898d1c0528fc8139606bbacf5a2e9d237 - RNWorklets: 416ef974c176d76634e34c0aeda83f6b67084a88 - SDWebImage: e9c98383c7572d713c1a0d7dd2783b10599b9838 + RNScreens: 199799bdab32fa1e17ebf938b06fec52033e81e5 + RNWorklets: ff9b9ff38df9accccbbbad692b6e435fec64e24e + SDWebImage: 1bb6a1b84b6fe87b972a102bdc77dd589df33477 SDWebImageAVIFCoder: afe194a084e851f70228e4be35ef651df0fc5c57 SDWebImageSVGCoder: 15a300a97ec1c8ac958f009c02220ac0402e936c SDWebImageWebPCoder: e38c0a70396191361d60c092933e22c20d5b1380 diff --git a/shared/login/index.tsx b/shared/login/index.tsx index cd680fd69a09..1c052d3559d0 100644 --- a/shared/login/index.tsx +++ b/shared/login/index.tsx @@ -1,6 +1,6 @@ import * as React from 'react' -import {useConfigState} from '@/constants/config' -import {useDaemonState} from '@/constants/daemon' +import {useConfigState} from '@/stores/config' +import {useDaemonState} from '@/stores/daemon' const Loading = React.lazy(async () => import('./loading')) const Relogin = React.lazy(async () => import('./relogin/container')) diff --git a/shared/login/join-or-login.tsx b/shared/login/join-or-login.tsx index 543fb0be1d2c..0e4b0c267713 100644 --- a/shared/login/join-or-login.tsx +++ b/shared/login/join-or-login.tsx @@ -1,10 +1,10 @@ import * as C from '@/constants' import * as React from 'react' -import {useConfigState} from '@/constants/config' +import {useConfigState} from '@/stores/config' import * as Kb from '@/common-adapters' import {InfoIcon} from '@/signup/common' -import {useSignupState} from '@/constants/signup' -import {useProvisionState} from '@/constants/provision' +import {useSignupState} from '@/stores/signup' +import {useProvisionState} from '@/stores/provision' const Intro = () => { const justDeletedSelf = useConfigState(s => s.justDeletedSelf) diff --git a/shared/login/loading.tsx b/shared/login/loading.tsx index 7d48b3dec130..41237f127c88 100644 --- a/shared/login/loading.tsx +++ b/shared/login/loading.tsx @@ -1,7 +1,7 @@ import * as C from '@/constants' import * as React from 'react' import * as Kb from '@/common-adapters' -import {useDaemonState} from '@/constants/daemon' +import {useDaemonState} from '@/stores/daemon' const SplashContainer = () => { const failedReason = useDaemonState(s => s.handshakeFailedReason) diff --git a/shared/login/recover-password/device-selector.tsx b/shared/login/recover-password/device-selector.tsx index 150fb2d21f5a..e73f81372b15 100644 --- a/shared/login/recover-password/device-selector.tsx +++ b/shared/login/recover-password/device-selector.tsx @@ -1,5 +1,5 @@ import SelectOtherDevice from '@/provision/select-other-device' -import {useState as useRecoverState} from '@/constants/recover-password' +import {useState as useRecoverState} from '@/stores/recover-password' const RecoverPasswordDeviceSelector = () => { const devices = useRecoverState(s => s.devices) diff --git a/shared/login/recover-password/error-modal.tsx b/shared/login/recover-password/error-modal.tsx index efe62179c6c4..14cb7f80d1e5 100644 --- a/shared/login/recover-password/error-modal.tsx +++ b/shared/login/recover-password/error-modal.tsx @@ -1,7 +1,7 @@ import * as C from '@/constants' import * as Kb from '@/common-adapters' -import {useState as useRecoverState} from '@/constants/recover-password' -import {useConfigState} from '@/constants/config' +import {useState as useRecoverState} from '@/stores/recover-password' +import {useConfigState} from '@/stores/config' const styles = Kb.Styles.styleSheetCreate(() => ({ padding: { diff --git a/shared/login/recover-password/error.tsx b/shared/login/recover-password/error.tsx index 789cbf323102..c38307524d2e 100644 --- a/shared/login/recover-password/error.tsx +++ b/shared/login/recover-password/error.tsx @@ -2,8 +2,8 @@ import * as C from '@/constants' import * as Kb from '@/common-adapters' import type {ButtonType} from '@/common-adapters/button' import {SignupScreen} from '@/signup/common' -import {useState as useRecoverState} from '@/constants/recover-password' -import {useConfigState} from '@/constants/config' +import {useState as useRecoverState} from '@/stores/recover-password' +import {useConfigState} from '@/stores/config' const ConnectedError = () => { const loggedIn = useConfigState(s => s.loggedIn) diff --git a/shared/login/recover-password/explain-device.tsx b/shared/login/recover-password/explain-device.tsx index 06890c97dab8..f9bd16930337 100644 --- a/shared/login/recover-password/explain-device.tsx +++ b/shared/login/recover-password/explain-device.tsx @@ -3,7 +3,7 @@ import * as Kb from '@/common-adapters' import * as T from '@/constants/types' import type {ButtonType} from '@/common-adapters/button' import {SignupScreen} from '@/signup/common' -import {useState as useRecoverState} from '@/constants/recover-password' +import {useState as useRecoverState} from '@/stores/recover-password' const ConnectedExplainDevice = () => { const ed = useRecoverState(s => s.explainedDevice) diff --git a/shared/login/recover-password/paper-key.tsx b/shared/login/recover-password/paper-key.tsx index 5e2a332af6d9..9d1983af7a1d 100644 --- a/shared/login/recover-password/paper-key.tsx +++ b/shared/login/recover-password/paper-key.tsx @@ -3,7 +3,7 @@ import * as Kb from '@/common-adapters' import * as React from 'react' import type {ButtonType} from '@/common-adapters/button' import {SignupScreen} from '@/signup/common' -import {useState as useRecoverState} from '@/constants/recover-password' +import {useState as useRecoverState} from '@/stores/recover-password' const PaperKey = () => { const error = useRecoverState(s => s.paperKeyError) diff --git a/shared/login/recover-password/password.tsx b/shared/login/recover-password/password.tsx index 452ca011543a..2e61426b7edc 100644 --- a/shared/login/recover-password/password.tsx +++ b/shared/login/recover-password/password.tsx @@ -1,6 +1,6 @@ import * as C from '@/constants' import {UpdatePassword} from '@/settings/password' -import {useState as useRecoverState} from '@/constants/recover-password' +import {useState as useRecoverState} from '@/stores/recover-password' const Password = () => { const error = useRecoverState(s => s.passwordError) diff --git a/shared/login/recover-password/prompt-reset-shared.tsx b/shared/login/recover-password/prompt-reset-shared.tsx index da223aa06338..dff66e082417 100644 --- a/shared/login/recover-password/prompt-reset-shared.tsx +++ b/shared/login/recover-password/prompt-reset-shared.tsx @@ -1,12 +1,12 @@ import * as C from '@/constants' -import * as AutoReset from '@/constants/autoreset' +import * as AutoReset from '@/stores/autoreset' import * as React from 'react' import * as Kb from '@/common-adapters' import {useSafeNavigation} from '@/util/safe-navigation' import * as T from '@/constants/types' import {SignupScreen} from '@/signup/common' import type {ButtonType} from '@/common-adapters/button' -import {useState as useRecoverState} from '@/constants/recover-password' +import {useState as useRecoverState} from '@/stores/recover-password' export type Props = { resetPassword?: boolean diff --git a/shared/login/relogin/container.tsx b/shared/login/relogin/container.tsx index 2e80b7856bc6..ee18a0195aa3 100644 --- a/shared/login/relogin/container.tsx +++ b/shared/login/relogin/container.tsx @@ -1,11 +1,11 @@ import * as C from '@/constants' import * as React from 'react' -import {useConfigState} from '@/constants/config' +import {useConfigState} from '@/stores/config' import Login from '.' import sortBy from 'lodash/sortBy' -import {useState as useRecoverState} from '@/constants/recover-password' -import {useSignupState} from '@/constants/signup' -import {useProvisionState} from '@/constants/provision' +import {useState as useRecoverState} from '@/stores/recover-password' +import {useSignupState} from '@/stores/signup' +import {useProvisionState} from '@/stores/provision' const needPasswordError = 'passphrase cannot be empty' diff --git a/shared/login/reset/confirm.tsx b/shared/login/reset/confirm.tsx index 5ce7f9b7d53e..e8a22339520d 100644 --- a/shared/login/reset/confirm.tsx +++ b/shared/login/reset/confirm.tsx @@ -1,9 +1,9 @@ import * as C from '@/constants' -import * as AutoReset from '@/constants/autoreset' +import * as AutoReset from '@/stores/autoreset' import * as React from 'react' import * as Kb from '@/common-adapters' import * as T from '@/constants/types' -import {useState as useRecoverState} from '@/constants/recover-password' +import {useState as useRecoverState} from '@/stores/recover-password' const ConfirmReset = () => { const hasWallet = AutoReset.useAutoResetState(s => s.hasWallet) diff --git a/shared/login/reset/modal.tsx b/shared/login/reset/modal.tsx index 3aa3e2b5ef66..9e3e48bd8db4 100644 --- a/shared/login/reset/modal.tsx +++ b/shared/login/reset/modal.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as AutoReset from '@/constants/autoreset' +import * as AutoReset from '@/stores/autoreset' import * as React from 'react' import * as Kb from '@/common-adapters' import {formatDurationForAutoreset} from '@/util/timestamp' diff --git a/shared/login/reset/password-enter.tsx b/shared/login/reset/password-enter.tsx index c749c5f801e6..010a4cdaf290 100644 --- a/shared/login/reset/password-enter.tsx +++ b/shared/login/reset/password-enter.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as AutoReset from '@/constants/autoreset' +import * as AutoReset from '@/stores/autoreset' import * as React from 'react' import * as Kb from '@/common-adapters' import {SignupScreen} from '@/signup/common' diff --git a/shared/login/reset/password-known.tsx b/shared/login/reset/password-known.tsx index c606206718b4..f955e6b7d183 100644 --- a/shared/login/reset/password-known.tsx +++ b/shared/login/reset/password-known.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as AutoReset from '@/constants/autoreset' +import * as AutoReset from '@/stores/autoreset' import * as React from 'react' import * as Kb from '@/common-adapters' import {SignupScreen} from '@/signup/common' diff --git a/shared/login/reset/waiting.tsx b/shared/login/reset/waiting.tsx index 9dea7f219397..adf2b26d7ace 100644 --- a/shared/login/reset/waiting.tsx +++ b/shared/login/reset/waiting.tsx @@ -3,7 +3,7 @@ import * as Kb from '@/common-adapters' import {SignupScreen} from '@/signup/common' import {addTicker, removeTicker} from '@/util/second-timer' import * as C from '@/constants' -import * as AutoReset from '@/constants/autoreset' +import * as AutoReset from '@/stores/autoreset' import {useSafeNavigation} from '@/util/safe-navigation' import {formatDurationForAutoreset as formatDuration} from '@/util/timestamp' diff --git a/shared/login/routes.tsx b/shared/login/routes.tsx index f6d31a7081eb..06d196706d66 100644 --- a/shared/login/routes.tsx +++ b/shared/login/routes.tsx @@ -5,7 +5,7 @@ import {InfoIcon} from '@/signup/common' import {newRoutes as provisionRoutes} from '../provision/routes-sub' import {sharedNewRoutes as settingsRoutes} from '../settings/routes' import {newRoutes as signupRoutes} from './signup/routes' -import {settingsFeedbackTab} from '@/constants/settings/util' +import {settingsFeedbackTab} from '@/constants/settings' const recoverPasswordStyles = Kb.Styles.styleSheetCreate(() => ({ questionBox: Kb.Styles.padding(Kb.Styles.globalMargins.tiny, Kb.Styles.globalMargins.tiny, 0), diff --git a/shared/login/signup/error.tsx b/shared/login/signup/error.tsx index f646b1337698..04ff47e90e7b 100644 --- a/shared/login/signup/error.tsx +++ b/shared/login/signup/error.tsx @@ -1,7 +1,7 @@ import * as C from '@/constants' import * as Kb from '@/common-adapters' import {Wrapper, ContinueButton} from './common' -import {useSignupState} from '@/constants/signup' +import {useSignupState} from '@/stores/signup' const ConnectedSignupError = () => { const error = useSignupState(s => s.signupError) diff --git a/shared/menubar/chat-container.desktop.tsx b/shared/menubar/chat-container.desktop.tsx index e00fdc6bc561..6bcff1bb9438 100644 --- a/shared/menubar/chat-container.desktop.tsx +++ b/shared/menubar/chat-container.desktop.tsx @@ -1,6 +1,6 @@ import * as React from 'react' import * as C from '@/constants' -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as R from '@/constants/remote' import * as RemoteGen from '../actions/remote-gen' import * as Kb from '@/common-adapters' diff --git a/shared/menubar/files-container.desktop.tsx b/shared/menubar/files-container.desktop.tsx index 773a166b515b..aea7e75c0361 100644 --- a/shared/menubar/files-container.desktop.tsx +++ b/shared/menubar/files-container.desktop.tsx @@ -1,4 +1,4 @@ -import {useProfileState} from '@/constants/profile' +import {useProfileState} from '@/stores/profile' import * as R from '@/constants/remote' import * as T from '@/constants/types' import * as RemoteGen from '../actions/remote-gen' @@ -6,7 +6,7 @@ import * as FsUtil from '@/util/kbfs' import * as TimestampUtil from '@/util/timestamp' import {FilesPreview} from './files.desktop' import type {DeserializeProps} from './remote-serializer.desktop' -import {useCurrentUserState} from '@/constants/current-user' +import {useCurrentUserState} from '@/stores/current-user' const FilesContainer = (p: Pick) => { const {remoteTlfUpdates} = p diff --git a/shared/menubar/remote-container.desktop.tsx b/shared/menubar/remote-container.desktop.tsx index fa658dbb9793..ed5a78d8ee00 100644 --- a/shared/menubar/remote-container.desktop.tsx +++ b/shared/menubar/remote-container.desktop.tsx @@ -1,14 +1,14 @@ import * as React from 'react' -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import Menubar from './index.desktop' -import {useConfigState} from '@/constants/config' +import {useConfigState} from '@/stores/config' import type {DeserializeProps} from './remote-serializer.desktop' import {useAvatarState} from '@/common-adapters/avatar/store' -import {useUsersState} from '@/constants/users' -import {useFollowerState} from '@/constants/followers' -import {useCurrentUserState} from '@/constants/current-user' -import {useDaemonState} from '@/constants/daemon' -import {useDarkModeState} from '@/constants/darkmode' +import {useUsersState} from '@/stores/users' +import {useFollowerState} from '@/stores/followers' +import {useCurrentUserState} from '@/stores/current-user' +import {useDaemonState} from '@/stores/daemon' +import {useDarkModeState} from '@/stores/darkmode' const RemoteContainer = (d: DeserializeProps) => { const {avatarRefreshCounter, badgeMap, daemonHandshakeState, darkMode, diskSpaceStatus, endEstimate} = d diff --git a/shared/menubar/remote-proxy.desktop.tsx b/shared/menubar/remote-proxy.desktop.tsx index c32e0a8c65e7..4e79d95e0e4e 100644 --- a/shared/menubar/remote-proxy.desktop.tsx +++ b/shared/menubar/remote-proxy.desktop.tsx @@ -1,7 +1,7 @@ // A mirror of the remote menubar windows. import * as C from '@/constants' -import * as Chat from '@/constants/chat2' -import {useConfigState} from '@/constants/config' +import * as Chat from '@/stores/chat2' +import {useConfigState} from '@/stores/config' import * as T from '@/constants/types' import * as React from 'react' import KB2 from '@/util/electron.desktop' @@ -10,16 +10,16 @@ import {intersect} from '@/util/set' import {mapFilterByKey} from '@/util/map' import {serialize, type ProxyProps, type RemoteTlfUpdates} from './remote-serializer.desktop' import {useAvatarState} from '@/common-adapters/avatar/store' -import type * as NotifConstants from '@/constants/notifications' +import type * as NotifConstants from '@/stores/notifications' import {useColorScheme} from 'react-native' -import * as FS from '@/constants/fs' -import {useFSState} from '@/constants/fs' -import {useFollowerState} from '@/constants/followers' -import {useUsersState} from '@/constants/users' -import {useNotifState} from '@/constants/notifications' -import {useCurrentUserState} from '@/constants/current-user' -import {useDaemonState} from '@/constants/daemon' -import {useDarkModeState} from '@/constants/darkmode' +import * as FS from '@/stores/fs' +import {useFSState} from '@/stores/fs' +import {useFollowerState} from '@/stores/followers' +import {useUsersState} from '@/stores/users' +import {useNotifState} from '@/stores/notifications' +import {useCurrentUserState} from '@/stores/current-user' +import {useDaemonState} from '@/stores/daemon' +import {useDarkModeState} from '@/stores/darkmode' const {showTray} = KB2.functions diff --git a/shared/override-d.ts/misc/index.d.ts b/shared/override-d.ts/misc/index.d.ts index 07d59acc2e73..9b7bcf3372e9 100644 --- a/shared/override-d.ts/misc/index.d.ts +++ b/shared/override-d.ts/misc/index.d.ts @@ -1,16 +1,3 @@ -declare module 'qrcode-generator' { - const gen: ( - n: number, - s: string - ) => { - addData: (s: string) => void - make: () => void - getModuleCount: () => number - createDataURL: (n: number) => string - } - export default gen -} - declare module 'emoji-datasource-apple/img/apple/sheets/64.png' { var png: string export default png diff --git a/shared/package.json b/shared/package.json index 2b6cb61ae16f..688526747830 100644 --- a/shared/package.json +++ b/shared/package.json @@ -72,20 +72,20 @@ "dependencies": { "@gorhom/bottom-sheet": "5.2.8", "@gorhom/portal": "1.0.14", - "@khanacademy/simple-markdown": "2.1.4", - "@msgpack/msgpack": "3.1.2", + "@khanacademy/simple-markdown": "2.2.1", + "@msgpack/msgpack": "3.1.3", "@react-native-community/netinfo": "11.5.2", "@react-native-masked-view/masked-view": "0.3.2", "@react-native-picker/picker": "2.11.4", - "@react-navigation/bottom-tabs": "7.9.1", - "@react-navigation/core": "7.13.7", - "@react-navigation/native": "7.1.27", - "@react-navigation/native-stack": "7.9.1", + "@react-navigation/bottom-tabs": "7.13.0", + "@react-navigation/core": "7.14.0", + "@react-navigation/native": "7.1.28", + "@react-navigation/native-stack": "7.12.0", "classnames": "2.5.1", "date-fns": "4.1.0", "emoji-datasource-apple": "16.0.0", "emoji-regex": "10.6.0", - "expo": "54.0.31", + "expo": "54.0.33", "expo-av": "16.0.8", "expo-camera": "17.0.10", "expo-clipboard": "8.0.8", @@ -99,15 +99,14 @@ "expo-media-library": "18.2.1", "expo-sms": "14.0.8", "expo-task-manager": "14.0.9", - "framed-msgpack-rpc": "keybase/node-framed-msgpack-rpc#nojima/HOTPOT-use-buffer", - "google-libphonenumber": "3.2.43", + "framed-msgpack-rpc": "keybase/node-framed-msgpack-rpc#nojima/HOTPOT-use-buffer-iserror", + "google-libphonenumber": "3.2.44", "iced-runtime": "1.0.4", - "immer": "11.1.3", + "immer": "11.1.4", "lodash": "4.17.23", - "lottie-react-native": "7.3.5", + "lottie-react-native": "7.3.6", "lottie-web": "5.13.0", "menubar": "9.5.2", - "qrcode-generator": "1.4.4", "react": "19.1.0", "react-dom": "19.1.0", "react-error-boundary": "5.0.0", @@ -115,30 +114,30 @@ "react-list": "0.8.18", "react-native": "0.81.5", "react-native-gesture-handler": "2.30.0", - "react-native-keyboard-controller": "1.20.6", "react-native-kb": "file:../rnmodules/react-native-kb", + "react-native-keyboard-controller": "1.20.7", "react-native-reanimated": "4.2.1", - "react-native-worklets": "0.7.1", "react-native-safe-area-context": "5.6.2", - "react-native-screens": "4.18.0", + "react-native-screens": "4.23.0", "react-native-web": "0.21.2", "react-native-webview": "13.16.0", + "react-native-worklets": "0.7.3", "react-native-zoom-toolkit": "5.0.1", - "react-window": "2.2.5", + "react-window": "2.2.6", "shallowequal": "1.1.0", "uint8array-extras": "1.5.0", "url-parse": "1.5.10", "use-debounce": "10.1.0", "util": "0.12.5", - "zustand": "5.0.10" + "zustand": "5.0.11" }, "devDependencies": { - "@babel/core": "7.28.5", - "@babel/node": "7.28.0", - "@babel/preset-env": "7.28.5", + "@babel/core": "7.29.0", + "@babel/node": "7.29.0", + "@babel/preset-env": "7.29.0", "@babel/preset-react": "7.28.5", "@babel/preset-typescript": "7.28.5", - "@electron/packager": "18.4.4", + "@electron/packager": "19.0.5", "@pmmmwh/react-refresh-webpack-plugin": "0.6.2", "@react-native-community/cli": "20.1.1", "@react-native-community/cli-platform-android": "20.1.1", @@ -149,7 +148,7 @@ "@types/google-libphonenumber": "7.4.30", "@types/lodash": "4.17.23", "@types/lodash-es": "4.17.12", - "@types/react": "19.2.8", + "@types/react": "19.2.14", "@types/react-dom": "19.2.3", "@types/react-is": "19.2.0", "@types/react-list": "0.8.12", @@ -161,11 +160,10 @@ "babel-plugin-module-resolver": "5.0.2", "babel-plugin-react-compiler": "1.0.0", "babel-plugin-react-native-web": "0.21.2", - "babel-preset-expo": "54.0.9", - "circular-dependency-plugin": "5.2.2", - "cross-env": "7.0.3", - "css-loader": "7.1.2", - "electron": "39.2.7", + "babel-preset-expo": "54.0.10", + "cross-env": "10.1.0", + "css-loader": "7.1.3", + "electron": "40.4.0", "eslint": "9.39.2", "eslint-plugin-deprecation": "3.0.0", "eslint-plugin-promise": "7.2.1", @@ -173,11 +171,11 @@ "eslint-plugin-react-compiler": "19.1.0-rc.2", "eslint-plugin-react-hooks": "7.0.1", "fs-extra": "11.3.3", - "html-webpack-plugin": "5.6.5", + "html-webpack-plugin": "5.6.6", "json5": "2.2.3", "null-loader": "4.0.1", "patch-package": "8.0.1", - "prettier": "3.8.0", + "prettier": "3.8.1", "react-refresh": "0.18.0", "react-scan": "0.4.3", "react-test-renderer": "19.1.0", @@ -185,15 +183,15 @@ "style-loader": "4.0.0", "terser-webpack-plugin": "5.3.16", "typescript": "5.9.3", - "typescript-eslint": "8.53.0", - "webpack": "5.104.1", + "typescript-eslint": "8.55.0", + "webpack": "5.105.2", "webpack-cli": "6.0.1", "webpack-dev-server": "5.2.3", "webpack-merge": "6.0.1" }, "resolutions": { "**/purepack": "keybase/nullModule", - "**/@types/react": "19.2.8" + "**/@types/react": "19.2.14" }, "typecoverage": { "detail": true, diff --git a/shared/patches/qrcode-generator+1.4.4.patch b/shared/patches/qrcode-generator+1.4.4.patch deleted file mode 100644 index d112877b87a8..000000000000 --- a/shared/patches/qrcode-generator+1.4.4.patch +++ /dev/null @@ -1,29 +0,0 @@ -diff --git a/node_modules/qrcode-generator/qrcode.d.ts b/node_modules/qrcode-generator/qrcode.d.ts -index b34952e..e72e297 100644 ---- a/node_modules/qrcode-generator/qrcode.d.ts -+++ b/node_modules/qrcode-generator/qrcode.d.ts -@@ -53,5 +53,4 @@ interface QRCode { - declare var qrcode : QRCodeFactory; - - declare module 'qrcode-generator' { -- export = qrcode; - } -diff --git a/node_modules/qrcode-generator/qrcode.js b/node_modules/qrcode-generator/qrcode.js -index 76889b5..39c2a5d 100644 ---- a/node_modules/qrcode-generator/qrcode.js -+++ b/node_modules/qrcode-generator/qrcode.js -@@ -2041,10 +2041,10 @@ var qrcode = function() { - //--------------------------------- - // Global Color Map - -- // black -- out.writeByte(0x00); -- out.writeByte(0x00); -- out.writeByte(0x00); -+ // keybase blue -+ out.writeByte(0x4c); -+ out.writeByte(0x8e); -+ out.writeByte(0xff); - - // white - out.writeByte(0xff); diff --git a/shared/people/announcement.tsx b/shared/people/announcement.tsx index 7a6e55dd442c..d3807eff4157 100644 --- a/shared/people/announcement.tsx +++ b/shared/people/announcement.tsx @@ -1,11 +1,11 @@ import * as C from '@/constants' -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as T from '@/constants/types' import openURL from '@/util/open-url' import * as Kb from '@/common-adapters' import PeopleItem from './item' -import * as Settings from '@/constants/settings/util' -import {usePeopleState} from '@/constants/people' +import * as Settings from '@/constants/settings' +import {usePeopleState} from '@/stores/people' type OwnProps = { appLink?: T.RPCGen.AppLinkType diff --git a/shared/people/container.tsx b/shared/people/container.tsx index 6a5182b99177..fba20b5002cb 100644 --- a/shared/people/container.tsx +++ b/shared/people/container.tsx @@ -2,10 +2,10 @@ import * as C from '@/constants' import * as React from 'react' import * as Kb from '@/common-adapters' import People from '.' -import {useSignupState} from '@/constants/signup' -import {useProfileState} from '@/constants/profile' -import {usePeopleState, getPeopleDataWaitingKey} from '@/constants/people' -import {useCurrentUserState} from '@/constants/current-user' +import {useSignupState} from '@/stores/signup' +import {useProfileState} from '@/stores/profile' +import {usePeopleState, getPeopleDataWaitingKey} from '@/stores/people' +import {useCurrentUserState} from '@/stores/current-user' const waitToRefresh = 1000 * 60 * 5 diff --git a/shared/people/index.shared.tsx b/shared/people/index.shared.tsx index 66edb1f66e6d..e51585aa6339 100644 --- a/shared/people/index.shared.tsx +++ b/shared/people/index.shared.tsx @@ -7,8 +7,8 @@ import FollowNotification from './follow-notification' import FollowSuggestions from './follow-suggestions' import type {Props} from '.' import Todo from './todo' -import {useSignupState} from '@/constants/signup' -import {usePeopleState} from '@/constants/people' +import {useSignupState} from '@/stores/signup' +import {usePeopleState} from '@/stores/people' // import WotTask from './wot-task' const itemToComponent: (item: T.Immutable, props: Props) => React.ReactNode = ( diff --git a/shared/people/routes.tsx b/shared/people/routes.tsx index 1a73b36b39b9..f6c42a6f7051 100644 --- a/shared/people/routes.tsx +++ b/shared/people/routes.tsx @@ -3,7 +3,7 @@ import * as C from '@/constants' import * as Kb from '@/common-adapters' import peopleTeamBuilder from '../team-building/page' import ProfileSearch from '../profile/search' -import {useCurrentUserState} from '@/constants/current-user' +import {useCurrentUserState} from '@/stores/current-user' const HeaderAvatar = () => { const myUsername = useCurrentUserState(s => s.username) diff --git a/shared/people/todo.tsx b/shared/people/todo.tsx index 512f1545b35f..016bc55b3d41 100644 --- a/shared/people/todo.tsx +++ b/shared/people/todo.tsx @@ -1,18 +1,18 @@ import * as C from '@/constants' -import {useTeamsState} from '@/constants/teams' +import {useTeamsState} from '@/stores/teams' import * as React from 'react' import openURL from '@/util/open-url' import type * as T from '@/constants/types' import type {IconType} from '@/common-adapters/icon.constants-gen' import PeopleItem, {type TaskButton} from './item' import * as Kb from '@/common-adapters' -import {useSettingsPhoneState} from '@/constants/settings-phone' -import {useSettingsEmailState} from '@/constants/settings-email' -import {settingsAccountTab, settingsGitTab} from '@/constants/settings/util' -import {useTrackerState} from '@/constants/tracker2' -import {useProfileState} from '@/constants/profile' -import {usePeopleState, todoTypes} from '@/constants/people' -import {useCurrentUserState} from '@/constants/current-user' +import {useSettingsPhoneState} from '@/stores/settings-phone' +import {useSettingsEmailState} from '@/stores/settings-email' +import {settingsAccountTab, settingsGitTab} from '@/constants/settings' +import {useTrackerState} from '@/stores/tracker2' +import {useProfileState} from '@/stores/profile' +import {usePeopleState, todoTypes} from '@/stores/people' +import {useCurrentUserState} from '@/stores/current-user' type TodoOwnProps = { badged: boolean diff --git a/shared/pinentry/remote-container.desktop.tsx b/shared/pinentry/remote-container.desktop.tsx index 97f0967a1e16..316f528a9e14 100644 --- a/shared/pinentry/remote-container.desktop.tsx +++ b/shared/pinentry/remote-container.desktop.tsx @@ -3,7 +3,7 @@ import * as RemoteGen from '../actions/remote-gen' import * as R from '@/constants/remote' import Pinentry from './index.desktop' import type {DeserializeProps} from './remote-serializer.desktop' -import {useDarkModeState} from '@/constants/darkmode' +import {useDarkModeState} from '@/stores/darkmode' const RemoteContainer = (d: DeserializeProps) => { const {darkMode, ...rest} = d diff --git a/shared/pinentry/remote-proxy.desktop.tsx b/shared/pinentry/remote-proxy.desktop.tsx index 587043c04c1d..ea897586694b 100644 --- a/shared/pinentry/remote-proxy.desktop.tsx +++ b/shared/pinentry/remote-proxy.desktop.tsx @@ -6,7 +6,7 @@ import useBrowserWindow from '../desktop/remote/use-browser-window.desktop' import useSerializeProps from '../desktop/remote/use-serialize-props.desktop' import {serialize, type ProxyProps} from './remote-serializer.desktop' import {useColorScheme} from 'react-native' -import {usePinentryState} from '@/constants/pinentry' +import {usePinentryState} from '@/stores/pinentry' const windowOpts = {height: 230, width: 440} diff --git a/shared/pinentry/remote-serializer.desktop.tsx b/shared/pinentry/remote-serializer.desktop.tsx index 96f859f93cce..ca291a3014af 100644 --- a/shared/pinentry/remote-serializer.desktop.tsx +++ b/shared/pinentry/remote-serializer.desktop.tsx @@ -1,5 +1,5 @@ import * as T from '@/constants/types' -import type * as Constants from '@/constants/pinentry' +import type * as Constants from '@/stores/pinentry' import {produce} from 'immer' export type ProxyProps = { diff --git a/shared/profile/add-to-team.tsx b/shared/profile/add-to-team.tsx index 0c0228be9967..a4581d07b18b 100644 --- a/shared/profile/add-to-team.tsx +++ b/shared/profile/add-to-team.tsx @@ -1,6 +1,6 @@ import * as C from '@/constants' import * as React from 'react' -import * as Teams from '@/constants/teams' +import * as Teams from '@/stores/teams' import type * as T from '@/constants/types' import {FloatingRolePicker, sendNotificationFooter} from '@/teams/role-picker' import * as Kb from '@/common-adapters' diff --git a/shared/profile/confirm-or-pending.tsx b/shared/profile/confirm-or-pending.tsx index e0a49954bb5a..f33f9325224e 100644 --- a/shared/profile/confirm-or-pending.tsx +++ b/shared/profile/confirm-or-pending.tsx @@ -1,4 +1,4 @@ -import {useProfileState} from '@/constants/profile' +import {useProfileState} from '@/stores/profile' import * as T from '@/constants/types' import * as Kb from '@/common-adapters' import {subtitle} from '@/util/platforms' diff --git a/shared/profile/edit-avatar/hooks.tsx b/shared/profile/edit-avatar/hooks.tsx index cc54782f2283..9e5778f69af5 100644 --- a/shared/profile/edit-avatar/hooks.tsx +++ b/shared/profile/edit-avatar/hooks.tsx @@ -1,7 +1,7 @@ import * as React from 'react' import * as C from '@/constants' -import {useProfileState} from '@/constants/profile' -import * as Teams from '@/constants/teams' +import {useProfileState} from '@/stores/profile' +import * as Teams from '@/stores/teams' import * as Kb from '@/common-adapters' import * as T from '@/constants/types' import type {Props} from '.' diff --git a/shared/profile/edit-profile.tsx b/shared/profile/edit-profile.tsx index 61bb397bab2a..c153a4866cf4 100644 --- a/shared/profile/edit-profile.tsx +++ b/shared/profile/edit-profile.tsx @@ -1,9 +1,9 @@ import * as C from '@/constants' import * as Kb from '@/common-adapters' import * as React from 'react' -import {useTrackerState} from '@/constants/tracker2' -import {useProfileState} from '@/constants/profile' -import {useCurrentUserState} from '@/constants/current-user' +import {useTrackerState} from '@/stores/tracker2' +import {useProfileState} from '@/stores/profile' +import {useCurrentUserState} from '@/stores/current-user' const Container = () => { const username = useCurrentUserState(s => s.username) diff --git a/shared/profile/generic/enter-username.tsx b/shared/profile/generic/enter-username.tsx index 41fee450433c..550411e67338 100644 --- a/shared/profile/generic/enter-username.tsx +++ b/shared/profile/generic/enter-username.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import {useProfileState} from '@/constants/profile' +import {useProfileState} from '@/stores/profile' import openURL from '@/util/open-url' import * as React from 'react' import * as Kb from '@/common-adapters' diff --git a/shared/profile/generic/proofs-list.tsx b/shared/profile/generic/proofs-list.tsx index 20c8dae21a8a..ffbfa6fe9f16 100644 --- a/shared/profile/generic/proofs-list.tsx +++ b/shared/profile/generic/proofs-list.tsx @@ -5,8 +5,8 @@ import type * as T from '@/constants/types' import {SiteIcon} from './shared' import {makeInsertMatcher} from '@/util/string' import {useColorScheme} from 'react-native' -import {useTrackerState} from '@/constants/tracker2' -import {useProfileState} from '@/constants/profile' +import {useTrackerState} from '@/stores/tracker2' +import {useProfileState} from '@/stores/profile' const Container = () => { const _proofSuggestions = useTrackerState(s => s.proofSuggestions) diff --git a/shared/profile/generic/result.tsx b/shared/profile/generic/result.tsx index db0e0b49a419..765700b8b568 100644 --- a/shared/profile/generic/result.tsx +++ b/shared/profile/generic/result.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import {useProfileState} from '@/constants/profile' +import {useProfileState} from '@/stores/profile' import * as Kb from '@/common-adapters' import {SiteIcon} from './shared' diff --git a/shared/profile/pgp/finished/index.desktop.tsx b/shared/profile/pgp/finished/index.desktop.tsx index 89e7f6c192cb..22e9341df3e8 100644 --- a/shared/profile/pgp/finished/index.desktop.tsx +++ b/shared/profile/pgp/finished/index.desktop.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import {useProfileState} from '@/constants/profile' +import {useProfileState} from '@/stores/profile' import * as React from 'react' import * as Kb from '@/common-adapters' import Modal from '@/profile/modal' diff --git a/shared/profile/pgp/generate/index.desktop.tsx b/shared/profile/pgp/generate/index.desktop.tsx index ed1be8203b1a..b144be1e0b6b 100644 --- a/shared/profile/pgp/generate/index.desktop.tsx +++ b/shared/profile/pgp/generate/index.desktop.tsx @@ -1,6 +1,6 @@ import * as Kb from '@/common-adapters' import * as C from '@/constants' -import {useProfileState} from '@/constants/profile' +import {useProfileState} from '@/stores/profile' import Modal from '@/profile/modal' export default function Generate() { diff --git a/shared/profile/pgp/info/index.desktop.tsx b/shared/profile/pgp/info/index.desktop.tsx index af9859070160..8da249f27ec5 100644 --- a/shared/profile/pgp/info/index.desktop.tsx +++ b/shared/profile/pgp/info/index.desktop.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import {useProfileState} from '@/constants/profile' +import {useProfileState} from '@/stores/profile' import * as Kb from '@/common-adapters' import Modal from '@/profile/modal' diff --git a/shared/profile/post-proof.tsx b/shared/profile/post-proof.tsx index ddcc5b7e3b1c..74f1cc9fd63c 100644 --- a/shared/profile/post-proof.tsx +++ b/shared/profile/post-proof.tsx @@ -1,11 +1,11 @@ import * as C from '@/constants' -import {useProfileState} from '@/constants/profile' +import {useProfileState} from '@/stores/profile' import * as React from 'react' import * as Kb from '@/common-adapters' import {subtitle} from '@/util/platforms' import openUrl from '@/util/open-url' import Modal from './modal' -import {useConfigState} from '@/constants/config' +import {useConfigState} from '@/stores/config' const Container = () => { const platform = useProfileState(s => s.platform) @@ -49,7 +49,7 @@ const Container = () => { break } const platformUserName = username - const copyToClipboard = useConfigState(s => s.dispatch.dynamic.copyToClipboard) + const copyToClipboard = useConfigState(s => s.dispatch.defer.copyToClipboard) const clearModals = C.useRouterState(s => s.dispatch.clearModals) const onCancel = () => { clearModals() diff --git a/shared/profile/prove-enter-username.tsx b/shared/profile/prove-enter-username.tsx index f64207564d86..5bd8cc0d1398 100644 --- a/shared/profile/prove-enter-username.tsx +++ b/shared/profile/prove-enter-username.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import {useProfileState} from '@/constants/profile' +import {useProfileState} from '@/stores/profile' import * as React from 'react' import * as Kb from '@/common-adapters' import Modal from './modal' diff --git a/shared/profile/prove-website-choice.tsx b/shared/profile/prove-website-choice.tsx index 7eabba0143a9..ff6e664c9f14 100644 --- a/shared/profile/prove-website-choice.tsx +++ b/shared/profile/prove-website-choice.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import {useProfileState} from '@/constants/profile' +import {useProfileState} from '@/stores/profile' import * as Kb from '@/common-adapters' import Modal from './modal' diff --git a/shared/profile/revoke.tsx b/shared/profile/revoke.tsx index 5fa5097c28f8..aa34864f41ae 100644 --- a/shared/profile/revoke.tsx +++ b/shared/profile/revoke.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import {useProfileState} from '@/constants/profile' +import {useProfileState} from '@/stores/profile' import * as Kb from '@/common-adapters' import capitalize from 'lodash/capitalize' import {subtitle as platformSubtitle} from '@/util/platforms' diff --git a/shared/profile/showcase-team-offer.tsx b/shared/profile/showcase-team-offer.tsx index 0041a0725477..12c43db82598 100644 --- a/shared/profile/showcase-team-offer.tsx +++ b/shared/profile/showcase-team-offer.tsx @@ -1,10 +1,10 @@ import * as C from '@/constants' import * as Kb from '@/common-adapters' -import * as Teams from '@/constants/teams' +import * as Teams from '@/stores/teams' import type * as T from '@/constants/types' import {useTeamsSubscribe} from '@/teams/subscriber' -import {useTrackerState} from '@/constants/tracker2' -import {useCurrentUserState} from '@/constants/current-user' +import {useTrackerState} from '@/stores/tracker2' +import {useCurrentUserState} from '@/stores/current-user' const Container = () => { const waiting = C.useWaitingState(s => s.counts) diff --git a/shared/profile/user/actions/index.tsx b/shared/profile/user/actions/index.tsx index 5695e6e9051c..2dcf058334ef 100644 --- a/shared/profile/user/actions/index.tsx +++ b/shared/profile/user/actions/index.tsx @@ -4,11 +4,11 @@ import * as Kb from '@/common-adapters' import * as React from 'react' import FollowButton from './follow-button' import ChatButton from '@/chat/chat-button' -import {useBotsState} from '@/constants/bots' -import {useTrackerState} from '@/constants/tracker2' -import * as FS from '@/constants/fs' -import {useFollowerState} from '@/constants/followers' -import {useCurrentUserState} from '@/constants/current-user' +import {useBotsState} from '@/stores/bots' +import {useTrackerState} from '@/stores/tracker2' +import * as FS from '@/stores/fs' +import {useFollowerState} from '@/stores/followers' +import {useCurrentUserState} from '@/stores/current-user' type OwnProps = {username: string} @@ -28,7 +28,7 @@ const Container = (ownProps: OwnProps) => { const navigateAppend = C.useRouterState(s => s.dispatch.navigateAppend) const _onAddToTeam = (username: string) => navigateAppend({props: {username}, selected: 'profileAddToTeam'}) const _onBrowsePublicFolder = (username: string) => - FS.makeActionForOpenPathInFilesTab(T.FS.stringToPath(`/keybase/public/${username}`)) + FS.navToPath(T.FS.stringToPath(`/keybase/public/${username}`)) const _onEditProfile = () => navigateAppend('profileEdit') const changeFollow = useTrackerState(s => s.dispatch.changeFollow) @@ -39,7 +39,7 @@ const Container = (ownProps: OwnProps) => { const _onManageBlocking = (username: string) => navigateAppend({props: {username}, selected: 'chatBlockingModal'}) const _onOpenPrivateFolder = (myUsername: string, theirUsername: string) => - FS.makeActionForOpenPathInFilesTab(T.FS.stringToPath(`/keybase/private/${theirUsername},${myUsername}`)) + FS.navToPath(T.FS.stringToPath(`/keybase/private/${theirUsername},${myUsername}`)) const showUser = useTrackerState(s => s.dispatch.showUser) const _onReload = (username: string) => { showUser(username, false) diff --git a/shared/profile/user/friend.tsx b/shared/profile/user/friend.tsx index ef0815a24d86..462965aaa940 100644 --- a/shared/profile/user/friend.tsx +++ b/shared/profile/user/friend.tsx @@ -1,6 +1,6 @@ -import {useProfileState} from '@/constants/profile' +import {useProfileState} from '@/stores/profile' import * as Kb from '@/common-adapters' -import {useUsersState} from '@/constants/users' +import {useUsersState} from '@/stores/users' type OwnProps = { username: string diff --git a/shared/profile/user/hooks.tsx b/shared/profile/user/hooks.tsx index 0a2a49b3017e..b3848ac98bbd 100644 --- a/shared/profile/user/hooks.tsx +++ b/shared/profile/user/hooks.tsx @@ -2,10 +2,10 @@ import * as C from '@/constants' import type * as T from '@/constants/types' import {type BackgroundColorType} from '.' import {useColorScheme} from 'react-native' -import {useTrackerState} from '@/constants/tracker2' -import {useProfileState} from '@/constants/profile' -import {useFollowerState} from '@/constants/followers' -import {useCurrentUserState} from '@/constants/current-user' +import {useTrackerState} from '@/stores/tracker2' +import {useProfileState} from '@/stores/profile' +import {useFollowerState} from '@/stores/followers' +import {useCurrentUserState} from '@/stores/current-user' const headerBackgroundColorType = ( state: T.Tracker.DetailsState, diff --git a/shared/profile/user/teams/index.tsx b/shared/profile/user/teams/index.tsx index 7cbc8c427ee2..f78cfca43e24 100644 --- a/shared/profile/user/teams/index.tsx +++ b/shared/profile/user/teams/index.tsx @@ -1,12 +1,12 @@ import * as C from '@/constants' -import {useTeamsState} from '@/constants/teams' +import {useTeamsState} from '@/stores/teams' import * as T from '@/constants/types' import * as Kb from '@/common-adapters' import * as React from 'react' import OpenMeta from './openmeta' import {default as TeamInfo, type Props as TIProps} from './teaminfo' -import {useTrackerState} from '@/constants/tracker2' -import {useCurrentUserState} from '@/constants/current-user' +import {useTrackerState} from '@/stores/tracker2' +import {useCurrentUserState} from '@/stores/current-user' type OwnProps = {username: string} diff --git a/shared/provision/code-page/container.tsx b/shared/provision/code-page/container.tsx index bc731a76d534..a33c2113e0da 100644 --- a/shared/provision/code-page/container.tsx +++ b/shared/provision/code-page/container.tsx @@ -1,13 +1,13 @@ import * as C from '@/constants' -import * as Devices from '@/constants/devices' +import * as Devices from '@/stores/devices' import * as React from 'react' import * as Kb from '@/common-adapters' import QRImage from './qr-image' import QRScan from './qr-scan' import Troubleshooting from '../troubleshooting' import type * as T from '@/constants/types' -import {useCurrentUserState} from '@/constants/current-user' -import {type Device, useProvisionState} from '@/constants/provision' +import {useCurrentUserState} from '@/stores/current-user' +import {type Device, useProvisionState} from '@/stores/provision' const CodePageContainer = () => { const {deviceID, storeDeviceName} = useCurrentUserState( diff --git a/shared/provision/code-page/qr-image.tsx b/shared/provision/code-page/qr-image.tsx index df69d5410cdb..b3afa5342e99 100644 --- a/shared/provision/code-page/qr-image.tsx +++ b/shared/provision/code-page/qr-image.tsx @@ -1,6 +1,6 @@ import * as React from 'react' import {Image2} from '@/common-adapters' -import QRCodeGen from 'qrcode-generator' +import generateQRDataURL from '@/util/qr-code' const Kb = { Image2, @@ -13,11 +13,8 @@ type Props = { const QrImage = React.memo(function QrImage(p: Props) { const {code, cellSize = 8} = p - const qr = QRCodeGen(4, 'L') - qr.addData(code) - qr.make() - const size = qr.getModuleCount() * (cellSize / 2) // retina - const url = qr.createDataURL(cellSize) + const {url, moduleCount} = generateQRDataURL(code, cellSize) + const size = moduleCount * (cellSize / 2) // retina return }) diff --git a/shared/provision/code-page/qr-scan/hooks.tsx b/shared/provision/code-page/qr-scan/hooks.tsx index bf1687854d0b..c6515c36c5d4 100644 --- a/shared/provision/code-page/qr-scan/hooks.tsx +++ b/shared/provision/code-page/qr-scan/hooks.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import {useProvisionState} from '@/constants/provision' +import {useProvisionState} from '@/stores/provision' const useQR = () => { const submitTextCode = useProvisionState(s => s.dispatch.dynamic.submitTextCode) diff --git a/shared/provision/code-page/qr-scan/not-authorized.tsx b/shared/provision/code-page/qr-scan/not-authorized.tsx index ee5774db4a22..cdd21978b3b4 100644 --- a/shared/provision/code-page/qr-scan/not-authorized.tsx +++ b/shared/provision/code-page/qr-scan/not-authorized.tsx @@ -1,8 +1,8 @@ import * as Kb from '@/common-adapters' -import {useConfigState} from '@/constants/config' +import {useConfigState} from '@/stores/config' const QRScanNotAuthorized = () => { - const onOpenSettings = useConfigState(s => s.dispatch.dynamic.openAppSettings) + const onOpenSettings = useConfigState(s => s.dispatch.defer.openAppSettings) return ( diff --git a/shared/provision/error.tsx b/shared/provision/error.tsx index 93125fdfb30b..a5cafc24ae99 100644 --- a/shared/provision/error.tsx +++ b/shared/provision/error.tsx @@ -1,11 +1,11 @@ import * as C from '@/constants' -import * as AutoReset from '@/constants/autoreset' +import * as AutoReset from '@/stores/autoreset' import * as Kb from '@/common-adapters' import type * as React from 'react' import LoginContainer from '../login/forms/container' import openURL from '@/util/open-url' import * as T from '@/constants/types' -import {useProvisionState} from '@/constants/provision' +import {useProvisionState} from '@/stores/provision' const Wrapper = (p: {onBack: () => void; children: React.ReactNode}) => ( diff --git a/shared/provision/forgot-username.tsx b/shared/provision/forgot-username.tsx index 9b01068677b6..ad746c8e7579 100644 --- a/shared/provision/forgot-username.tsx +++ b/shared/provision/forgot-username.tsx @@ -2,8 +2,8 @@ import * as C from '@/constants' import * as React from 'react' import * as Kb from '@/common-adapters' import {SignupScreen, errorBanner} from '../signup/common' -import {useSettingsPhoneState} from '@/constants/settings-phone' -import {useProvisionState} from '@/constants/provision' +import {useSettingsPhoneState} from '@/stores/settings-phone' +import {useProvisionState} from '@/stores/provision' const ForgotUsername = () => { const defaultCountry = useSettingsPhoneState(s => s.defaultCountry) diff --git a/shared/provision/paper-key.tsx b/shared/provision/paper-key.tsx index 8fb1972d6f6a..0ae84be9dc0d 100644 --- a/shared/provision/paper-key.tsx +++ b/shared/provision/paper-key.tsx @@ -2,7 +2,7 @@ import * as C from '@/constants' import * as Kb from '@/common-adapters' import * as React from 'react' import {SignupScreen, errorBanner} from '../signup/common' -import {useProvisionState} from '@/constants/provision' +import {useProvisionState} from '@/stores/provision' const Container = () => { const error = useProvisionState(s => s.error) diff --git a/shared/provision/password.tsx b/shared/provision/password.tsx index 3d1f2adddff5..3bef094b69fc 100644 --- a/shared/provision/password.tsx +++ b/shared/provision/password.tsx @@ -3,8 +3,8 @@ import * as Kb from '@/common-adapters' import * as React from 'react' import UserCard from '../login/user-card' import {SignupScreen, errorBanner} from '../signup/common' -import {useState as useRecoverState} from '@/constants/recover-password' -import {useProvisionState} from '@/constants/provision' +import {useState as useRecoverState} from '@/stores/recover-password' +import {useProvisionState} from '@/stores/provision' const Password = () => { const error = useProvisionState(s => s.error) diff --git a/shared/provision/select-other-device-connected.tsx b/shared/provision/select-other-device-connected.tsx index 6f1baa6a45cc..69499e1c7547 100644 --- a/shared/provision/select-other-device-connected.tsx +++ b/shared/provision/select-other-device-connected.tsx @@ -1,9 +1,9 @@ import * as C from '@/constants' -import * as AutoReset from '@/constants/autoreset' +import * as AutoReset from '@/stores/autoreset' import * as React from 'react' import {useSafeSubmit} from '@/util/safe-submit' import SelectOtherDevice from './select-other-device' -import {useProvisionState} from '@/constants/provision' +import {useProvisionState} from '@/stores/provision' const SelectOtherDeviceContainer = () => { const devices = useProvisionState(s => s.devices) diff --git a/shared/provision/select-other-device.tsx b/shared/provision/select-other-device.tsx index b39e8c25308e..3ac49ebaba69 100644 --- a/shared/provision/select-other-device.tsx +++ b/shared/provision/select-other-device.tsx @@ -2,7 +2,7 @@ import * as Kb from '@/common-adapters' import * as React from 'react' import DeviceIcon from '../devices/device-icon' import {SignupScreen} from '../signup/common' -import {type Device} from '@/constants/provision' +import {type Device} from '@/stores/provision' type Props = { passwordRecovery?: boolean diff --git a/shared/provision/set-public-name.tsx b/shared/provision/set-public-name.tsx index d25bc7c81f22..3b5d7ba52058 100644 --- a/shared/provision/set-public-name.tsx +++ b/shared/provision/set-public-name.tsx @@ -1,11 +1,11 @@ import * as C from '@/constants' -import * as Devices from '@/constants/devices' +import * as Devices from '@/stores/devices' import {useSafeSubmit} from '@/util/safe-submit' import * as Kb from '@/common-adapters' import * as React from 'react' import debounce from 'lodash/debounce' import {SignupScreen, errorBanner} from '../signup/common' -import * as Provision from '@/constants/provision' +import * as Provision from '@/stores/provision' const SetPublicName = () => { const devices = Provision.useProvisionState(s => s.devices) diff --git a/shared/provision/troubleshooting.tsx b/shared/provision/troubleshooting.tsx index 0f00a0cc0e8b..e7aa4f2318c7 100644 --- a/shared/provision/troubleshooting.tsx +++ b/shared/provision/troubleshooting.tsx @@ -1,9 +1,9 @@ import * as React from 'react' import * as C from '@/constants' -import * as Devices from '@/constants/devices' +import * as Devices from '@/stores/devices' import * as Kb from '@/common-adapters' import type * as T from '@/constants/types' -import {useProvisionState} from '@/constants/provision' +import {useProvisionState} from '@/stores/provision' type Props = { mode: 'QR' | 'text' onCancel: () => void diff --git a/shared/provision/username-or-email.tsx b/shared/provision/username-or-email.tsx index 0ead30e44502..fd06d797850d 100644 --- a/shared/provision/username-or-email.tsx +++ b/shared/provision/username-or-email.tsx @@ -1,6 +1,6 @@ import * as C from '@/constants' -import * as AutoReset from '@/constants/autoreset' -import {useSignupState} from '@/constants/signup' +import * as AutoReset from '@/stores/autoreset' +import {useSignupState} from '@/stores/signup' import {useSafeSubmit} from '@/util/safe-submit' import * as T from '@/constants/types' import * as React from 'react' @@ -8,9 +8,9 @@ import type {RPCError} from '@/util/errors' import * as Kb from '@/common-adapters' import UserCard from '@/login/user-card' import {SignupScreen, errorBanner} from '@/signup/common' -import {useProvisionState} from '@/constants/provision' +import {useProvisionState} from '@/stores/provision' -type OwnProps = {fromReset?: boolean} +type OwnProps = {fromReset?: boolean; username?: string} const decodeInlineError = (inlineRPCError: RPCError | undefined) => { let inlineError = '' @@ -59,7 +59,12 @@ const UsernameOrEmailContainer = (op: OwnProps) => { }, [_setUsername, waiting] ) - const [username, setUsername] = React.useState(_username) + const [username, setUsername] = React.useState(op.username ?? _username) + React.useEffect(() => { + if (op.username && op.username !== _username) { + _setUsername?.(op.username) + } + }, [op.username, _username, _setUsername]) const onSubmit = React.useCallback(() => { _onSubmit(username) }, [_onSubmit, username]) diff --git a/shared/router-v2/account-switcher/index.tsx b/shared/router-v2/account-switcher/index.tsx index 7ed7d926f309..f4a67159ff4d 100644 --- a/shared/router-v2/account-switcher/index.tsx +++ b/shared/router-v2/account-switcher/index.tsx @@ -1,15 +1,15 @@ import * as C from '@/constants' import './account-switcher.css' -import {useConfigState} from '@/constants/config' +import {useConfigState} from '@/stores/config' import * as Kb from '@/common-adapters' import * as React from 'react' import type * as T from '@/constants/types' -import {settingsLogOutTab} from '@/constants/settings/util' -import {useTrackerState} from '@/constants/tracker2' -import {useProfileState} from '@/constants/profile' -import {useUsersState} from '@/constants/users' -import {useCurrentUserState} from '@/constants/current-user' -import {useProvisionState} from '@/constants/provision' +import {settingsLogOutTab} from '@/constants/settings' +import {useTrackerState} from '@/stores/tracker2' +import {useProfileState} from '@/stores/profile' +import {useUsersState} from '@/stores/users' +import {useCurrentUserState} from '@/stores/current-user' +import {useProvisionState} from '@/stores/provision' const prepareAccountRows = ( accountRows: ReadonlyArray, diff --git a/shared/router-v2/common.native.tsx b/shared/router-v2/common.native.tsx index af2f2056c052..8037a3caaedc 100644 --- a/shared/router-v2/common.native.tsx +++ b/shared/router-v2/common.native.tsx @@ -3,7 +3,7 @@ import * as Kb from '@/common-adapters' import {TabActions, type NavigationContainerRef} from '@react-navigation/core' import type {HeaderOptions} from '@react-navigation/elements' import {HeaderLeftArrowCanGoBack} from '@/common-adapters/header-hoc' -import type {NavState} from '@/constants/router2' +import type {NavState} from '@/stores/router2' export const headerDefaultStyle = { get backgroundColor() { diff --git a/shared/router-v2/header/index.desktop.tsx b/shared/router-v2/header/index.desktop.tsx index 2a46eb9be1c3..892c4fbde2a4 100644 --- a/shared/router-v2/header/index.desktop.tsx +++ b/shared/router-v2/header/index.desktop.tsx @@ -6,7 +6,7 @@ import {IconWithPopupDesktop as WhatsNewIconWithPopup} from '@/whats-new/icon' import * as ReactIs from 'react-is' import KB2 from '@/util/electron.desktop' import shallowEqual from 'shallowequal' -import {useConfigState} from '@/constants/config' +import {useConfigState} from '@/stores/config' const {closeWindow, minimizeWindow, toggleMaximizeWindow} = KB2.functions diff --git a/shared/router-v2/header/syncing-folders.tsx b/shared/router-v2/header/syncing-folders.tsx index 578aa416f544..722a91e2bb98 100644 --- a/shared/router-v2/header/syncing-folders.tsx +++ b/shared/router-v2/header/syncing-folders.tsx @@ -1,9 +1,9 @@ import * as C from '@/constants' -import * as Constants from '@/constants/fs' +import * as Constants from '@/stores/fs' import * as Kb from '@/common-adapters' import * as T from '@/constants/types' import PieSlice from '@/fs/common/pie-slice' -import {useFSState} from '@/constants/fs' +import {useFSState} from '@/stores/fs' type OwnProps = { negative?: boolean diff --git a/shared/router-v2/hooks.native.tsx b/shared/router-v2/hooks.native.tsx index e957781818b8..0c6ec0747e58 100644 --- a/shared/router-v2/hooks.native.tsx +++ b/shared/router-v2/hooks.native.tsx @@ -1,12 +1,12 @@ import * as C from '@/constants' -import * as Chat from '@/constants/chat2' -import {useConfigState} from '@/constants/config' +import * as Chat from '@/stores/chat2' +import {useConfigState} from '@/stores/config' import * as Tabs from '@/constants/tabs' import * as React from 'react' -import {useDeepLinksState} from '@/constants/deeplinks' +import {handleAppLink} from '@/constants/deeplinks' import {Linking} from 'react-native' import {useColorScheme} from 'react-native' -import {usePushState} from '@/constants/push' +import {usePushState} from '@/stores/push' type InitialStateState = 'init' | 'loading' | 'loaded' @@ -112,7 +112,7 @@ export const useInitialState = (loggedInLoaded: boolean) => { } if (url && isValidLink(url)) { - setTimeout(() => url && useDeepLinksState.getState().dispatch.handleAppLink(url), 1) + setTimeout(() => url && handleAppLink(url), 1) } else if (startupFollowUser && !startupConversation) { url = `keybase://profile/show/${startupFollowUser}` diff --git a/shared/router-v2/left-tab-navigator.desktop.tsx b/shared/router-v2/left-tab-navigator.desktop.tsx index bac3cb3c04a5..a30eceb6a845 100644 --- a/shared/router-v2/left-tab-navigator.desktop.tsx +++ b/shared/router-v2/left-tab-navigator.desktop.tsx @@ -4,7 +4,8 @@ import TabBar from './tab-bar.desktop' import {useNavigationBuilder, TabRouter, createNavigatorFactory} from '@react-navigation/core' import type {TypedNavigator, NavigatorTypeBagBase, StaticConfig} from '@react-navigation/native' import type * as Tabs from '@/constants/tabs' -import * as Router2 from '@/constants/router2' +import {useRouterState} from '@/stores/router2' +import {getModalStack} from '@/constants/router2' type BackBehavior = Parameters[0]['backBehavior'] type Props = Parameters[1] & {backBehavior: BackBehavior} @@ -50,7 +51,7 @@ const LeftTabNavigator = React.memo(function LeftTabNavigator({ setRendered(next) }, [selectedRoute, rendered]) - const hasModals = Router2.useRouterState(() => Router2.getModalStack().length > 0) + const hasModals = useRouterState(() => getModalStack().length > 0) return ( diff --git a/shared/router-v2/router.desktop.tsx b/shared/router-v2/router.desktop.tsx index 3173b32abf55..51816647e878 100644 --- a/shared/router-v2/router.desktop.tsx +++ b/shared/router-v2/router.desktop.tsx @@ -1,6 +1,6 @@ import * as Common from './common.desktop' import * as C from '@/constants' -import {useConfigState} from '@/constants/config' +import {useConfigState} from '@/stores/config' import * as Kb from '@/common-adapters' import * as React from 'react' import * as Shared from './router.shared' @@ -15,8 +15,8 @@ import {createNativeStackNavigator} from '@react-navigation/native-stack' import {modalRoutes, routes, loggedOutRoutes, tabRoots} from './routes' import {registerDebugClear} from '@/util/debug' import type {RootParamList} from '@/router-v2/route-params' -import {useCurrentUserState} from '@/constants/current-user' -import {useDaemonState} from '@/constants/daemon' +import {useCurrentUserState} from '@/stores/current-user' +import {useDaemonState} from '@/stores/daemon' import type {NativeStackNavigationOptions} from '@react-navigation/native-stack' import './router.css' diff --git a/shared/router-v2/router.native.tsx b/shared/router-v2/router.native.tsx index f807285c2920..53099d8043d7 100644 --- a/shared/router-v2/router.native.tsx +++ b/shared/router-v2/router.native.tsx @@ -1,7 +1,7 @@ import * as C from '@/constants' -import * as Constants from '@/constants/router2' -import {useConfigState} from '@/constants/config' -import {useDarkModeState} from '@/constants/darkmode' +import * as Constants from '@/stores/router2' +import {useConfigState} from '@/stores/config' +import {useDarkModeState} from '@/stores/darkmode' import * as Kb from '@/common-adapters' import * as React from 'react' import * as Shared from './router.shared' @@ -20,7 +20,7 @@ import * as Hooks from './hooks.native' import * as TabBar from './tab-bar.native' import type {RootParamList} from '@/router-v2/route-params' import {useColorScheme} from 'react-native' -import {useDaemonState} from '@/constants/daemon' +import {useDaemonState} from '@/stores/daemon' if (module.hot) { module.hot.accept('', () => {}) @@ -68,7 +68,7 @@ const tabStacks = tabs.map(tab => ( name={tab} listeners={{ tabLongPress: () => { - C.useRouterState.getState().dispatch.dynamic.tabLongPress?.(tab) + C.useRouterState.getState().dispatch.defer.tabLongPress?.(tab) }, }} component={TabStack} diff --git a/shared/router-v2/router.shared.tsx b/shared/router-v2/router.shared.tsx index 7a88668f6157..e0bb7847af68 100644 --- a/shared/router-v2/router.shared.tsx +++ b/shared/router-v2/router.shared.tsx @@ -4,8 +4,8 @@ import * as React from 'react' import {Splash} from '../login/loading' import type {Theme} from '@react-navigation/native' import {colors, darkColors, themed} from '@/styles/colors' -import {useFSState} from '@/constants/fs' -import {useDarkModeState} from '@/constants/darkmode' +import {useFSState} from '@/stores/fs' +import {useDarkModeState} from '@/stores/darkmode' export const SimpleLoading = React.memo(function SimpleLoading() { return ( diff --git a/shared/router-v2/tab-bar.desktop.tsx b/shared/router-v2/tab-bar.desktop.tsx index 843f25c36ead..67baf715faf2 100644 --- a/shared/router-v2/tab-bar.desktop.tsx +++ b/shared/router-v2/tab-bar.desktop.tsx @@ -1,6 +1,6 @@ import * as C from '@/constants' import * as Kb from '@/common-adapters' -import {useConfigState} from '@/constants/config' +import {useConfigState} from '@/stores/config' import * as Kbfs from '@/fs/common' import * as Platforms from '@/constants/platform' import * as T from '@/constants/types' @@ -13,13 +13,13 @@ import openURL from '@/util/open-url' import {isLinux} from '@/constants/platform' import KB2 from '@/util/electron.desktop' import './tab-bar.css' -import {settingsLogOutTab} from '@/constants/settings/util' -import {useTrackerState} from '@/constants/tracker2' -import {useFSState} from '@/constants/fs' -import {useProfileState} from '@/constants/profile' -import {useNotifState} from '@/constants/notifications' -import {useCurrentUserState} from '@/constants/current-user' -import {useProvisionState} from '@/constants/provision' +import {settingsLogOutTab} from '@/constants/settings' +import {useTrackerState} from '@/stores/tracker2' +import {useFSState} from '@/stores/fs' +import {useProfileState} from '@/stores/profile' +import {useNotifState} from '@/stores/notifications' +import {useCurrentUserState} from '@/stores/current-user' +import {useProvisionState} from '@/stores/provision' const {hideWindow, ctlQuit} = KB2.functions diff --git a/shared/router-v2/tab-bar.native.tsx b/shared/router-v2/tab-bar.native.tsx index 71bb34a5ff8f..30671cb861f4 100644 --- a/shared/router-v2/tab-bar.native.tsx +++ b/shared/router-v2/tab-bar.native.tsx @@ -6,8 +6,8 @@ import * as Shared from './router.shared' import {View} from 'react-native' import {useSafeAreaFrame} from 'react-native-safe-area-context' import {useColorScheme} from 'react-native' -import {usePushState} from '@/constants/push' -import {useNotifState} from '@/constants/notifications' +import {usePushState} from '@/stores/push' +import {useNotifState} from '@/stores/notifications' const settingsTabChildren = [Tabs.gitTab, Tabs.devicesTab, Tabs.settingsTab] as const const tabs = C.isTablet ? Tabs.tabletTabs : Tabs.phoneTabs diff --git a/shared/settings/account/add-modals.tsx b/shared/settings/account/add-modals.tsx index f5736b62172a..5d203251e85d 100644 --- a/shared/settings/account/add-modals.tsx +++ b/shared/settings/account/add-modals.tsx @@ -6,8 +6,8 @@ import {EnterEmailBody} from '@/signup/email' import {EnterPhoneNumberBody} from '@/signup/phone-number' import VerifyBody from '@/signup/phone-number/verify-body' import {e164ToDisplay} from '@/util/phone-numbers' -import {useSettingsPhoneState} from '@/constants/settings-phone' -import {useSettingsEmailState} from '@/constants/settings-email' +import {useSettingsPhoneState} from '@/stores/settings-phone' +import {useSettingsEmailState} from '@/stores/settings-email' export const Email = () => { const nav = useSafeNavigation() diff --git a/shared/settings/account/confirm-delete.tsx b/shared/settings/account/confirm-delete.tsx index cec9eaa58255..1b4fa2d6f9aa 100644 --- a/shared/settings/account/confirm-delete.tsx +++ b/shared/settings/account/confirm-delete.tsx @@ -2,8 +2,8 @@ import * as React from 'react' import * as Kb from '@/common-adapters' import * as PhoneUtil from '@/util/phone-numbers' import {useSafeNavigation} from '@/util/safe-navigation' -import {useSettingsPhoneState} from '@/constants/settings-phone' -import {useSettingsEmailState} from '@/constants/settings-email' +import {useSettingsPhoneState} from '@/stores/settings-phone' +import {useSettingsEmailState} from '@/stores/settings-email' type OwnProps = { address: string diff --git a/shared/settings/account/email-phone-row.tsx b/shared/settings/account/email-phone-row.tsx index b3b7f6e32a3d..1eec1a6336e7 100644 --- a/shared/settings/account/email-phone-row.tsx +++ b/shared/settings/account/email-phone-row.tsx @@ -2,8 +2,8 @@ import * as C from '@/constants' import * as React from 'react' import * as Kb from '@/common-adapters' import * as T from '@/constants/types' -import {useSettingsPhoneState} from '@/constants/settings-phone' -import {useSettingsEmailState} from '@/constants/settings-email' +import {useSettingsPhoneState} from '@/stores/settings-phone' +import {useSettingsEmailState} from '@/stores/settings-email' const addSpacer = (into: string, add: string) => { return into + (into.length ? ' • ' : '') + add diff --git a/shared/settings/account/index.tsx b/shared/settings/account/index.tsx index 1cc8a39c9352..c45c1d9725b0 100644 --- a/shared/settings/account/index.tsx +++ b/shared/settings/account/index.tsx @@ -2,10 +2,10 @@ import * as C from '@/constants' import * as Kb from '@/common-adapters' import type * as React from 'react' import EmailPhoneRow from './email-phone-row' -import {usePWState} from '@/constants/settings-password' -import {useSettingsPhoneState} from '@/constants/settings-phone' -import {useSettingsEmailState} from '@/constants/settings-email' -import {useSettingsState, settingsPasswordTab} from '@/constants/settings' +import {usePWState} from '@/stores/settings-password' +import {useSettingsPhoneState} from '@/stores/settings-phone' +import {useSettingsEmailState} from '@/stores/settings-email' +import {useSettingsState, settingsPasswordTab} from '@/stores/settings' export const SettingsSection = ({children}: {children: React.ReactNode}) => ( diff --git a/shared/settings/advanced.tsx b/shared/settings/advanced.tsx index cb9414ddbd4d..7f926fdc196a 100644 --- a/shared/settings/advanced.tsx +++ b/shared/settings/advanced.tsx @@ -3,10 +3,10 @@ import * as Kb from '@/common-adapters' import * as T from '@/constants/types' import * as React from 'react' import {ProxySettings} from './proxy' -import {useSettingsState, traceInProgressKey, processorProfileInProgressKey} from '@/constants/settings' -import {usePWState} from '@/constants/settings-password' -import {useFSState} from '@/constants/fs' -import {useConfigState} from '@/constants/config' +import {useSettingsState, traceInProgressKey, processorProfileInProgressKey} from '@/stores/settings' +import {usePWState} from '@/stores/settings-password' +import {useFSState} from '@/stores/fs' +import {useConfigState} from '@/stores/config' let initialUseNativeFrame: boolean | undefined diff --git a/shared/settings/archive/index.tsx b/shared/settings/archive/index.tsx index 3ec5cf413eee..ab07df9512c9 100644 --- a/shared/settings/archive/index.tsx +++ b/shared/settings/archive/index.tsx @@ -3,9 +3,10 @@ import * as C from '@/constants' import * as T from '@/constants/types' import * as Kb from '@/common-adapters' import {formatTimeForConversationList, formatTimeForChat} from '@/util/timestamp' -import {useArchiveState} from '@/constants/archive' -import * as FS from '@/constants/fs' -import {useFSState} from '@/constants/fs' +import {useArchiveState} from '@/stores/archive' +import * as FS from '@/stores/fs' +import {useFSState} from '@/stores/fs' +import {showShareActionSheet} from '@/util/platform-specific' const ChatJob = React.memo(function ChatJob(p: {index: number; id: string}) { const {id, index} = p @@ -29,7 +30,7 @@ const ChatJob = React.memo(function ChatJob(p: {index: number; id: string}) { resume(id) }, [resume, id]) - const openFinder = useFSState(s => s.dispatch.dynamic.openLocalPathInSystemFileManagerDesktop) + const openFinder = useFSState(s => s.dispatch.defer.openLocalPathInSystemFileManagerDesktop) const onShowFinder = React.useCallback(() => { if (!job) return openFinder?.(job.outPath) @@ -37,7 +38,7 @@ const ChatJob = React.memo(function ChatJob(p: {index: number; id: string}) { const onShare = React.useCallback(() => { if (!job?.outPath) return - C.PlatformSpecific.showShareActionSheet({ + showShareActionSheet({ filePath: job.outPath, mimeType: 'application/zip', }) @@ -152,7 +153,7 @@ const KBFSJob = React.memo(function KBFSJob(p: {index: number; id: string}) { loadKBFSJobFreshness(id) }) - const openFinder = useFSState(s => s.dispatch.dynamic.openLocalPathInSystemFileManagerDesktop) + const openFinder = useFSState(s => s.dispatch.defer.openLocalPathInSystemFileManagerDesktop) const onShowFinder = React.useCallback(() => { if (Kb.Styles.isMobile || !job) { return @@ -164,10 +165,7 @@ const KBFSJob = React.memo(function KBFSJob(p: {index: number; id: string}) { if (!Kb.Styles.isMobile || !job) { return } - C.PlatformSpecific.showShareActionSheet({ - filePath: job.zipFilePath, - mimeType: 'application/zip', - }) + showShareActionSheet({filePath: job.zipFilePath, mimeType: 'application/zip'}) .then(() => {}) .catch(() => {}) }, [job]) diff --git a/shared/settings/archive/modal.tsx b/shared/settings/archive/modal.tsx index ff13598e2693..1e77cb8b0a8c 100644 --- a/shared/settings/archive/modal.tsx +++ b/shared/settings/archive/modal.tsx @@ -4,8 +4,10 @@ import * as C from '@/constants' import type * as T from '@/constants/types' import {pickSave} from '@/util/pick-files' import * as FsCommon from '@/fs/common' -import {useArchiveState} from '@/constants/archive' -import {settingsArchiveTab} from '@/constants/settings' +import {useArchiveState} from '@/stores/archive' +import {settingsArchiveTab} from '@/stores/settings' +import {useCurrentUserState} from '@/stores/current-user' +import {getConvoState} from '@/stores/convostate' type Props = | {type: 'chatID'; conversationIDKey: T.Chat.ConversationIDKey} @@ -16,12 +18,29 @@ type Props = | {type: 'fsPath'; path: string} | {type: 'git'; gitURL: string} +const chatIDToDisplayname = (conversationIDKey: string) => { + const you = useCurrentUserState.getState().username + const cs = getConvoState(conversationIDKey) + const m = cs.meta + if (m.teamname) { + if (m.channelname) { + return `${m.teamname}#${m.channelname}` + } + return m.teamname + } + + const participants = cs.participants.name + if (participants.length === 1) { + return participants[0] ?? '' + } + return participants.filter(username => username !== you).join(',') +} + const ArchiveModal = (p: Props) => { const {type} = p - const chatIDToDisplayname = useArchiveState(s => s.chatIDToDisplayname) const displayname = React.useMemo(() => { return p.type === 'chatID' ? chatIDToDisplayname(p.conversationIDKey) : '' - }, [p, chatIDToDisplayname]) + }, [p]) let defaultPath = '' if (C.isElectron) { diff --git a/shared/settings/chat.tsx b/shared/settings/chat.tsx index 5a0fc327fbe0..1759ee2dbaa1 100644 --- a/shared/settings/chat.tsx +++ b/shared/settings/chat.tsx @@ -1,13 +1,13 @@ import * as C from '@/constants' import * as Kb from '@/common-adapters' -import * as Teams from '@/constants/teams' +import * as Teams from '@/stores/teams' import * as T from '@/constants/types' import * as React from 'react' import Group from './group' -import {useSettingsChatState as useSettingsChatState} from '@/constants/settings-chat' -import {useSettingsNotifState} from '@/constants/settings-notifications' -import {useSettingsState} from '@/constants/settings' -import {useConfigState} from '@/constants/config' +import {useSettingsChatState as useSettingsChatState} from '@/stores/settings-chat' +import {useSettingsNotifState} from '@/stores/settings-notifications' +import {useSettingsState} from '@/stores/settings' +import {useConfigState} from '@/stores/config' const emptyList = new Array() diff --git a/shared/settings/contacts-joined.tsx b/shared/settings/contacts-joined.tsx index 0650fa5d2e47..994dcd5a5a85 100644 --- a/shared/settings/contacts-joined.tsx +++ b/shared/settings/contacts-joined.tsx @@ -4,9 +4,9 @@ import type * as T from '@/constants/types' import {useSafeNavigation} from '@/util/safe-navigation' import * as React from 'react' import UnconnectedFollowButton from '@/profile/user/actions/follow-button' -import {useSettingsContactsState} from '@/constants/settings-contacts' -import {useTrackerState} from '@/constants/tracker2' -import {useFollowerState} from '@/constants/followers' +import {useSettingsContactsState} from '@/stores/settings-contacts' +import {useTrackerState} from '@/stores/tracker2' +import {useFollowerState} from '@/stores/followers' const renderItem = (_: number, item: T.RPCGen.ProcessedContact) => diff --git a/shared/settings/db-nuke.confirm.tsx b/shared/settings/db-nuke.confirm.tsx index 5c4b71083a5d..d286b5367ddd 100644 --- a/shared/settings/db-nuke.confirm.tsx +++ b/shared/settings/db-nuke.confirm.tsx @@ -1,6 +1,6 @@ import * as C from '@/constants' import * as Kb from '@/common-adapters' -import {useSettingsState} from '@/constants/settings' +import {useSettingsState} from '@/stores/settings' const DbNukeConfirm = () => { const navigateUp = C.useRouterState(s => s.dispatch.navigateUp) diff --git a/shared/settings/delete-confirm/check-passphrase.native.tsx b/shared/settings/delete-confirm/check-passphrase.native.tsx index a861c09207b8..00010f8bf4db 100644 --- a/shared/settings/delete-confirm/check-passphrase.native.tsx +++ b/shared/settings/delete-confirm/check-passphrase.native.tsx @@ -2,7 +2,7 @@ import * as C from '@/constants' import * as React from 'react' import * as Kb from '@/common-adapters' import {useSafeNavigation} from '@/util/safe-navigation' -import {useSettingsState} from '@/constants/settings' +import {useSettingsState} from '@/stores/settings' const CheckPassphraseMobile = () => { const [password, setPassword] = React.useState('') diff --git a/shared/settings/delete-confirm/index.tsx b/shared/settings/delete-confirm/index.tsx index 1a4a243a2ca5..e946c68aa858 100644 --- a/shared/settings/delete-confirm/index.tsx +++ b/shared/settings/delete-confirm/index.tsx @@ -2,9 +2,9 @@ import * as C from '@/constants' import * as React from 'react' import * as Kb from '@/common-adapters' import {useSafeNavigation} from '@/util/safe-navigation' -import {usePWState} from '@/constants/settings-password' -import {useSettingsState} from '@/constants/settings' -import {useCurrentUserState} from '@/constants/current-user' +import {usePWState} from '@/stores/settings-password' +import {useSettingsState} from '@/stores/settings' +import {useCurrentUserState} from '@/stores/current-user' type CheckboxesProps = { checkData: boolean diff --git a/shared/settings/disable-cert-pinning-modal.tsx b/shared/settings/disable-cert-pinning-modal.tsx index b84956f0114d..8fe63caf18c2 100644 --- a/shared/settings/disable-cert-pinning-modal.tsx +++ b/shared/settings/disable-cert-pinning-modal.tsx @@ -1,6 +1,6 @@ import * as C from '@/constants' import * as Kb from '@/common-adapters' -import {useSettingsState} from '@/constants/settings' +import {useSettingsState} from '@/stores/settings' const DisableCertPinningModal = () => { const navigateUp = C.useRouterState(s => s.dispatch.navigateUp) diff --git a/shared/settings/display.tsx b/shared/settings/display.tsx index b95ba9a6e786..f7ab52aafed4 100644 --- a/shared/settings/display.tsx +++ b/shared/settings/display.tsx @@ -3,8 +3,8 @@ import * as React from 'react' import * as Kb from '@/common-adapters' import * as T from '@/constants/types' import logger from '@/logger' -import {useConfigState} from '@/constants/config' -import * as DarkMode from '@/constants/darkmode' +import {useConfigState} from '@/stores/config' +import * as DarkMode from '@/stores/darkmode' const Display = () => { const allowAnimatedEmojis = useConfigState(s => s.allowAnimatedEmojis) diff --git a/shared/settings/feedback/container.desktop.tsx b/shared/settings/feedback/container.desktop.tsx index 83933ea0ad0d..5ebef2dcdd73 100644 --- a/shared/settings/feedback/container.desktop.tsx +++ b/shared/settings/feedback/container.desktop.tsx @@ -2,7 +2,7 @@ import * as C from '@/constants' import Feedback from '.' import type {Props} from './container' import {useSendFeedback} from './shared' -import {useConfigState} from '@/constants/config' +import {useConfigState} from '@/stores/config' const Container = (ownProps: Props) => { const {sendFeedback, error} = useSendFeedback() diff --git a/shared/settings/feedback/container.native.tsx b/shared/settings/feedback/container.native.tsx index b88db320114e..a504f4ab065a 100644 --- a/shared/settings/feedback/container.native.tsx +++ b/shared/settings/feedback/container.native.tsx @@ -1,5 +1,5 @@ -import {useConfigState} from '@/constants/config' -import {useCurrentUserState} from '@/constants/current-user' +import {useConfigState} from '@/stores/config' +import {useCurrentUserState} from '@/stores/current-user' import * as Kb from '@/common-adapters' import * as React from 'react' import Feedback from '.' @@ -9,7 +9,7 @@ import {getExtraChatLogsForLogSend} from './shared' import {isAndroid, version, pprofDir} from '@/constants/platform' import {logSend, appVersionName, appVersionCode} from 'react-native-kb' import type {Props as OwnProps} from './container' -import {usePushState} from '@/constants/push' +import {usePushState} from '@/stores/push' export type Props = { chat: object diff --git a/shared/settings/files/hooks.tsx b/shared/settings/files/hooks.tsx index 7cd0b2d730c2..e8e29a846900 100644 --- a/shared/settings/files/hooks.tsx +++ b/shared/settings/files/hooks.tsx @@ -1,6 +1,6 @@ import {defaultNotificationThreshold} from '.' import * as C from '@/constants' -import {useFSState} from '@/constants/fs' +import {useFSState} from '@/stores/fs' const useFiles = () => { const {areSettingsLoading, setSpaceAvailableNotificationThreshold, spaceAvailableNotificationThreshold} = diff --git a/shared/settings/files/index.desktop.tsx b/shared/settings/files/index.desktop.tsx index c8abd4d17097..f62f7811fba2 100644 --- a/shared/settings/files/index.desktop.tsx +++ b/shared/settings/files/index.desktop.tsx @@ -6,8 +6,8 @@ import * as Kbfs from '@/fs/common' import RefreshDriverStatusOnMount from '@/fs/common/refresh-driver-status-on-mount' import RefreshSettings from './refresh-settings' import useFiles from './hooks' -import * as FS from '@/constants/fs' -import {useFSState} from '@/constants/fs' +import * as FS from '@/stores/fs' +import {useFSState} from '@/stores/fs' type Props = ReturnType export const allowedNotificationThresholds = [100 * 1024 ** 2, 1024 ** 3, 3 * 1024 ** 3, 10 * 1024 ** 3] @@ -58,7 +58,7 @@ const FinderIntegration = () => { C.useShallow(s => ({ driverDisable: s.dispatch.driverDisable, driverStatus: s.sfmi.driverStatus, - openLocalPathInSystemFileManagerDesktop: s.dispatch.dynamic.openLocalPathInSystemFileManagerDesktop, + openLocalPathInSystemFileManagerDesktop: s.dispatch.defer.openLocalPathInSystemFileManagerDesktop, preferredMountDirs: s.sfmi.preferredMountDirs, })) ) diff --git a/shared/settings/files/index.native.tsx b/shared/settings/files/index.native.tsx index 66ecd2edf2f0..bbec8c0123d0 100644 --- a/shared/settings/files/index.native.tsx +++ b/shared/settings/files/index.native.tsx @@ -3,8 +3,8 @@ import * as React from 'react' import * as Kb from '@/common-adapters' import * as T from '@/constants/types' import useFiles from './hooks' -import * as FS from '@/constants/fs' -import {useFSState} from '@/constants/fs' +import * as FS from '@/stores/fs' +import {useFSState} from '@/stores/fs' type Props = ReturnType export const allowedNotificationThresholds = [100 * 1024 ** 2, 1024 ** 3, 3 * 1024 ** 3, 10 * 1024 ** 3] diff --git a/shared/settings/files/refresh-settings.tsx b/shared/settings/files/refresh-settings.tsx index aa4da1e72b56..383484e7b579 100644 --- a/shared/settings/files/refresh-settings.tsx +++ b/shared/settings/files/refresh-settings.tsx @@ -1,5 +1,5 @@ import * as React from 'react' -import {useFSState} from '@/constants/fs' +import {useFSState} from '@/stores/fs' const RefreshSettings = () => { const refresh = useFSState(s => s.dispatch.loadSettings) diff --git a/shared/settings/group.tsx b/shared/settings/group.tsx index 839ad9fa16b1..fefd140db264 100644 --- a/shared/settings/group.tsx +++ b/shared/settings/group.tsx @@ -1,5 +1,5 @@ import * as Kb from '@/common-adapters' -import type {NotificationsSettingsState} from '@/constants/settings-notifications' +import type {NotificationsSettingsState} from '@/stores/settings-notifications' type GroupProps = { allowEdit: boolean diff --git a/shared/settings/invites/index.desktop.tsx b/shared/settings/invites/index.desktop.tsx index 0636b633ea3b..c9dd76394d51 100644 --- a/shared/settings/invites/index.desktop.tsx +++ b/shared/settings/invites/index.desktop.tsx @@ -1,11 +1,11 @@ import * as Kb from '@/common-adapters' -import type {AcceptedInvite, PendingInvite} from '@/constants/settings-invites' +import type {AcceptedInvite, PendingInvite} from '@/stores/settings-invites' import * as React from 'react' import SubHeading from '../subheading' import * as dateFns from 'date-fns' import * as C from '@/constants' -import {useProfileState} from '@/constants/profile' -import {useState as useSettingsInvitesState} from '@/constants/settings-invites' +import {useProfileState} from '@/stores/profile' +import {useState as useSettingsInvitesState} from '@/stores/settings-invites' // Like intersperse but takes a function to define the separator function intersperseFn( diff --git a/shared/settings/logout.tsx b/shared/settings/logout.tsx index b5d765d9287c..c61c62e3656e 100644 --- a/shared/settings/logout.tsx +++ b/shared/settings/logout.tsx @@ -3,9 +3,9 @@ import {useSafeSubmit} from '@/util/safe-submit' import * as C from '@/constants' import * as Kb from '@/common-adapters' import {UpdatePassword} from './password' -import {usePWState} from '@/constants/settings-password' -import {useSettingsState} from '@/constants/settings' -import {useLogoutState} from '@/constants/logout' +import {usePWState} from '@/stores/settings-password' +import {useSettingsState} from '@/stores/settings' +import {useLogoutState} from '@/stores/logout' const LogoutContainer = () => { const {checkPassword, checkPasswordIsCorrect, resetCheckPassword} = useSettingsState( diff --git a/shared/settings/manage-contacts.tsx b/shared/settings/manage-contacts.tsx index fa5f9f6a3764..47cd4f558a43 100644 --- a/shared/settings/manage-contacts.tsx +++ b/shared/settings/manage-contacts.tsx @@ -2,9 +2,9 @@ import * as C from '@/constants' import * as React from 'react' import * as Kb from '@/common-adapters' import {SettingsSection} from './account' -import {useSettingsContactsState} from '@/constants/settings-contacts' -import {settingsFeedbackTab} from '@/constants/settings' -import {useConfigState} from '@/constants/config' +import {useSettingsContactsState} from '@/stores/settings-contacts' +import {settingsFeedbackTab} from '@/stores/settings' +import {useConfigState} from '@/stores/config' const enabledDescription = 'Your phone contacts are being synced on this device.' const disabledDescription = 'Import your phone contacts and start encrypted chats with your friends.' @@ -72,7 +72,7 @@ const ManageContactsBanner = () => { status: s.permissionStatus, })) ) - const onOpenAppSettings = useConfigState(s => s.dispatch.dynamic.openAppSettings) + const onOpenAppSettings = useConfigState(s => s.dispatch.defer.openAppSettings) const {appendNewChatBuilder, navigateAppend, switchTab} = C.useRouterState( C.useShallow(s => ({ appendNewChatBuilder: s.appendNewChatBuilder, diff --git a/shared/settings/notifications/hooks.tsx b/shared/settings/notifications/hooks.tsx index 210c1149d735..a3c0aecc59fd 100644 --- a/shared/settings/notifications/hooks.tsx +++ b/shared/settings/notifications/hooks.tsx @@ -1,7 +1,7 @@ import * as C from '@/constants' -import {useSettingsEmailState} from '@/constants/settings-email' -import {useSettingsNotifState} from '@/constants/settings-notifications' -import {useSettingsState, settingsAccountTab} from '@/constants/settings' +import {useSettingsEmailState} from '@/stores/settings-email' +import {useSettingsNotifState} from '@/stores/settings-notifications' +import {useSettingsState, settingsAccountTab} from '@/stores/settings' const useNotifications = () => { const _groups = useSettingsNotifState(s => s.groups) diff --git a/shared/settings/notifications/index.desktop.tsx b/shared/settings/notifications/index.desktop.tsx index dc0c8c53aa9e..cb3bf4b94e1b 100644 --- a/shared/settings/notifications/index.desktop.tsx +++ b/shared/settings/notifications/index.desktop.tsx @@ -1,8 +1,8 @@ import {Reloadable} from '@/common-adapters' import * as C from '@/constants' import Render from './render' -import {useSettingsNotifState} from '@/constants/settings-notifications' -import {useSettingsState} from '@/constants/settings' +import {useSettingsNotifState} from '@/stores/settings-notifications' +import {useSettingsState} from '@/stores/settings' const Notifications = () => { const loadSettings = useSettingsState(s => s.dispatch.loadSettings) diff --git a/shared/settings/notifications/index.native.tsx b/shared/settings/notifications/index.native.tsx index f06118f6b422..d7cf7fe2d81a 100644 --- a/shared/settings/notifications/index.native.tsx +++ b/shared/settings/notifications/index.native.tsx @@ -2,9 +2,9 @@ import * as C from '@/constants' import * as Kb from '@/common-adapters' import Notifications from './render' import {Reloadable} from '@/common-adapters' -import {useSettingsNotifState} from '@/constants/settings-notifications' -import {useSettingsState} from '@/constants/settings' -import {usePushState} from '@/constants/push' +import {useSettingsNotifState} from '@/stores/settings-notifications' +import {useSettingsState} from '@/stores/settings' +import {usePushState} from '@/stores/push' const MobileNotifications = () => { const loadSettings = useSettingsState(s => s.dispatch.loadSettings) diff --git a/shared/settings/notifications/push-prompt.tsx b/shared/settings/notifications/push-prompt.tsx index 9917a9110fc4..1cf01b98196a 100644 --- a/shared/settings/notifications/push-prompt.tsx +++ b/shared/settings/notifications/push-prompt.tsx @@ -1,7 +1,7 @@ import * as C from '@/constants' import * as React from 'react' import * as Kb from '@/common-adapters' -import {usePushState} from '@/constants/push' +import {usePushState} from '@/stores/push' const PushPrompt = () => { const rejectPermissions = usePushState(s => s.dispatch.rejectPermissions) diff --git a/shared/settings/notifications/render.tsx b/shared/settings/notifications/render.tsx index 7784aa673b12..fd67e846c9e0 100644 --- a/shared/settings/notifications/render.tsx +++ b/shared/settings/notifications/render.tsx @@ -1,7 +1,7 @@ import * as Kb from '@/common-adapters' import useNotifications from './hooks' import Group from '../group' -import {usePushState} from '@/constants/push' +import {usePushState} from '@/stores/push' type Props = ReturnType diff --git a/shared/settings/password.tsx b/shared/settings/password.tsx index afea9f8392ed..f9c157c2641b 100644 --- a/shared/settings/password.tsx +++ b/shared/settings/password.tsx @@ -1,7 +1,7 @@ import * as React from 'react' import * as Kb from '@/common-adapters' import * as C from '@/constants' -import {usePWState} from '@/constants/settings-password' +import {usePWState} from '@/stores/settings-password' type Props = { error: string diff --git a/shared/settings/proxy.tsx b/shared/settings/proxy.tsx index 06975d317b3d..3239af09aba9 100644 --- a/shared/settings/proxy.tsx +++ b/shared/settings/proxy.tsx @@ -2,7 +2,7 @@ import * as React from 'react' import * as C from '@/constants' import * as Kb from '@/common-adapters' import * as T from '@/constants/types' -import {useSettingsState} from '@/constants/settings' +import {useSettingsState} from '@/stores/settings' const useConnect = () => { const allowTlsMitmToggle = useSettingsState(s => s.didToggleCertificatePinning) diff --git a/shared/settings/root-desktop-tablet.tsx b/shared/settings/root-desktop-tablet.tsx index 7b29c0799c0d..8c76227cdd56 100644 --- a/shared/settings/root-desktop-tablet.tsx +++ b/shared/settings/root-desktop-tablet.tsx @@ -6,7 +6,7 @@ import LeftNav from './sub-nav/left-nav' import {useNavigationBuilder, TabRouter, createNavigatorFactory} from '@react-navigation/core' import type {TypedNavigator, NavigatorTypeBagBase, StaticConfig} from '@react-navigation/native' import {sharedNewRoutes} from './routes' -import {settingsAccountTab} from '@/constants/settings' +import {settingsAccountTab} from '@/stores/settings' const settingsSubRoutes = { ...sharedNewRoutes, diff --git a/shared/settings/root-phone.tsx b/shared/settings/root-phone.tsx index 0ed12c417a3b..e481ddc5883d 100644 --- a/shared/settings/root-phone.tsx +++ b/shared/settings/root-phone.tsx @@ -1,16 +1,16 @@ import * as C from '@/constants' -import {useConfigState} from '@/constants/config' +import {useConfigState} from '@/stores/config' import * as React from 'react' import * as Kb from '@/common-adapters' import * as T from '@/constants/types' -import {keybaseFM} from '@/constants/whats-new' +import {keybaseFM} from '@/stores/whats-new' import SettingsItem from './sub-nav/settings-item' import WhatsNewIcon from '../whats-new/icon' import noop from 'lodash/noop' -import {useSettingsContactsState} from '@/constants/settings-contacts' -import * as Settings from '@/constants/settings' -import {usePushState} from '@/constants/push' -import {useNotifState} from '@/constants/notifications' +import {useSettingsContactsState} from '@/stores/settings-contacts' +import * as Settings from '@/stores/settings' +import {usePushState} from '@/stores/push' +import {useNotifState} from '@/stores/notifications' const PerfRow = () => { const [toSubmit, setToSubmit] = React.useState('') diff --git a/shared/settings/routes.tsx b/shared/settings/routes.tsx index 71bf575d9e67..dead7baabb06 100644 --- a/shared/settings/routes.tsx +++ b/shared/settings/routes.tsx @@ -3,7 +3,7 @@ import * as C from '@/constants' import {newRoutes as devicesRoutes} from '../devices/routes' import {newRoutes as gitRoutes} from '../git/routes' import {newRoutes as walletsRoutes} from '../wallets/routes' -import * as Settings from '@/constants/settings/util' +import * as Settings from '@/constants/settings' const SettingsRootDesktop = React.lazy(async () => import('./root-desktop-tablet')) diff --git a/shared/settings/sub-nav/left-nav.tsx b/shared/settings/sub-nav/left-nav.tsx index 12ecd1a074d8..6e7337a14961 100644 --- a/shared/settings/sub-nav/left-nav.tsx +++ b/shared/settings/sub-nav/left-nav.tsx @@ -3,10 +3,10 @@ import * as React from 'react' import * as Kb from '@/common-adapters' import WhatsNewIcon from '@/whats-new/icon' import SettingsItem from './settings-item' -import {keybaseFM} from '@/constants/whats-new' -import * as Settings from '@/constants/settings' -import {usePushState} from '@/constants/push' -import {useNotifState} from '@/constants/notifications' +import {keybaseFM} from '@/stores/whats-new' +import * as Settings from '@/stores/settings' +import {usePushState} from '@/stores/push' +import {useNotifState} from '@/stores/notifications' type Props = { onClick: (s: string) => void diff --git a/shared/signup/common.tsx b/shared/signup/common.tsx index 3659e14d4c50..a7a4b486b22f 100644 --- a/shared/signup/common.tsx +++ b/shared/signup/common.tsx @@ -3,7 +3,7 @@ import * as React from 'react' import * as Kb from '@/common-adapters' import {type Props as ButtonProps} from '@/common-adapters/button' import openURL from '@/util/open-url' -import {useConfigState} from '@/constants/config' +import {useConfigState} from '@/stores/config' type InfoIconProps = { invisible?: boolean diff --git a/shared/signup/device-name.tsx b/shared/signup/device-name.tsx index 8abc7478d70d..d53ddce22c04 100644 --- a/shared/signup/device-name.tsx +++ b/shared/signup/device-name.tsx @@ -2,8 +2,8 @@ import * as C from '@/constants' import * as Kb from '@/common-adapters' import * as React from 'react' import {SignupScreen, errorBanner} from './common' -import * as Provision from '@/constants/provision' -import {useSignupState} from '@/constants/signup' +import * as Provision from '@/stores/provision' +import {useSignupState} from '@/stores/signup' const ConnectedEnterDevicename = () => { const error = useSignupState(s => s.devicenameError) diff --git a/shared/signup/email.tsx b/shared/signup/email.tsx index 1593cf89c1e0..b167e9e3db0b 100644 --- a/shared/signup/email.tsx +++ b/shared/signup/email.tsx @@ -2,9 +2,9 @@ import * as C from '@/constants' import * as React from 'react' import * as Kb from '@/common-adapters' import {SignupScreen, errorBanner} from './common' -import {useSettingsEmailState} from '@/constants/settings-email' -import {useSignupState} from '@/constants/signup' -import {usePushState} from '@/constants/push' +import {useSettingsEmailState} from '@/stores/settings-email' +import {useSignupState} from '@/stores/signup' +import {usePushState} from '@/stores/push' const ConnectedEnterEmail = () => { const _showPushPrompt = usePushState(s => C.isMobile && !s.hasPermissions && s.showPushPrompt) diff --git a/shared/signup/feedback.tsx b/shared/signup/feedback.tsx index 51c2ac8ace23..1bc404e8c6a3 100644 --- a/shared/signup/feedback.tsx +++ b/shared/signup/feedback.tsx @@ -4,7 +4,7 @@ import * as React from 'react' import FeedbackForm from '../settings/feedback/index' import {SignupScreen, errorBanner} from './common' import {useSendFeedback} from '../settings/feedback/shared' -import {useConfigState} from '@/constants/config' +import {useConfigState} from '@/stores/config' const SignupFeedback = () => { const {error: sendError, sendFeedback: onSendFeedback} = useSendFeedback() diff --git a/shared/signup/phone-number/index.tsx b/shared/signup/phone-number/index.tsx index 9467647febbe..03ef62e70146 100644 --- a/shared/signup/phone-number/index.tsx +++ b/shared/signup/phone-number/index.tsx @@ -2,7 +2,7 @@ import * as C from '@/constants' import * as React from 'react' import * as Kb from '@/common-adapters' import {SignupScreen, errorBanner} from '../common' -import {useSettingsPhoneState} from '@/constants/settings-phone' +import {useSettingsPhoneState} from '@/stores/settings-phone' type BodyProps = { autoFocus?: boolean diff --git a/shared/signup/phone-number/verify.tsx b/shared/signup/phone-number/verify.tsx index 1e58e3fde7aa..b3368353a527 100644 --- a/shared/signup/phone-number/verify.tsx +++ b/shared/signup/phone-number/verify.tsx @@ -4,7 +4,7 @@ import * as Kb from '@/common-adapters' import {SignupScreen} from '../common' import {e164ToDisplay} from '@/util/phone-numbers' import VerifyBody from './verify-body' -import {useSettingsPhoneState} from '@/constants/settings-phone' +import {useSettingsPhoneState} from '@/stores/settings-phone' const Container = () => { const error = useSettingsPhoneState(s => (s.verificationState === 'error' ? s.error : '')) diff --git a/shared/signup/username.tsx b/shared/signup/username.tsx index a2b38f4c9cc8..f3f32f1156b3 100644 --- a/shared/signup/username.tsx +++ b/shared/signup/username.tsx @@ -2,8 +2,8 @@ import * as C from '@/constants' import * as Kb from '@/common-adapters' import * as React from 'react' import {SignupScreen, errorBanner} from './common' -import {useSignupState} from '@/constants/signup' -import {useProvisionState} from '@/constants/provision' +import {useSignupState} from '@/stores/signup' +import {useProvisionState} from '@/stores/provision' const ConnectedEnterUsername = () => { const error = useSignupState(s => s.usernameError) diff --git a/shared/constants/archive/index.tsx b/shared/stores/archive.tsx similarity index 93% rename from shared/constants/archive/index.tsx rename to shared/stores/archive.tsx index 772f60224ffc..f5869166c8d0 100644 --- a/shared/constants/archive/index.tsx +++ b/shared/stores/archive.tsx @@ -1,12 +1,11 @@ -import * as T from '../types' +import * as T from '@/constants/types' import * as Z from '@/util/zustand' -import {ignorePromise} from '../utils' +import {ignorePromise} from '@/constants/utils' import * as EngineGen from '@/actions/engine-gen-gen' -import * as FS from '@/constants/fs' +import {pathToRPCPath} from '@/constants/fs' import {formatTimeForPopup} from '@/util/timestamp' import {uint8ArrayToHex} from 'uint8array-extras' -import {storeRegistry} from '../store-registry' -import {isMobile} from '../platform' +import {isMobile} from '@/constants/platform' type ChatJob = { id: string @@ -89,7 +88,6 @@ export interface State extends Store { onEngineIncomingImpl: (action: EngineGen.Actions) => void resetState: 'default' } - chatIDToDisplayname: (id: string) => string } export const useArchiveState = Z.createZustand((set, get) => { @@ -279,7 +277,7 @@ export const useArchiveState = Z.createZustand((set, get) => { await T.RPCGen.SimpleFSSimpleFSArchiveStartRpcPromise({ archiveJobStartPath: { archiveJobStartPathType: T.RPCGen.ArchiveJobStartPathType.kbfs, - kbfs: FS.pathToRPCPath(path).kbfs, + kbfs: pathToRPCPath(path).kbfs, }, outputPath: outPath, overwriteZip: true, @@ -447,23 +445,6 @@ export const useArchiveState = Z.createZustand((set, get) => { } return { ...initialStore, - chatIDToDisplayname: (conversationIDKey: string) => { - const you = storeRegistry.getState('current-user').username - const cs = storeRegistry.getConvoState(conversationIDKey) - const m = cs.meta - if (m.teamname) { - if (m.channelname) { - return `${m.teamname}#${m.channelname}` - } - return m.teamname - } - - const participants = cs.participants.name - if (participants.length === 1) { - return participants[0] ?? '' - } - return participants.filter(username => username !== you).join(',') - }, dispatch, } }) diff --git a/shared/constants/autoreset/index.tsx b/shared/stores/autoreset.tsx similarity index 85% rename from shared/constants/autoreset/index.tsx rename to shared/stores/autoreset.tsx index b7c0910473b7..5c066b41a4e5 100644 --- a/shared/constants/autoreset/index.tsx +++ b/shared/stores/autoreset.tsx @@ -1,12 +1,11 @@ import * as Z from '@/util/zustand' -import {ignorePromise} from '../utils' -import * as S from '../strings' +import {ignorePromise} from '@/constants/utils' +import * as S from '@/constants/strings' import * as T from '@/constants/types' import * as EngineGen from '@/actions/engine-gen-gen' import logger from '@/logger' import {RPCError} from '@/util/errors' -import {navigateAppend, navUpToScreen} from '../router2/util' -import {storeRegistry} from '../store-registry' +import {navigateAppend, navUpToScreen} from '@/constants/router2' type Store = T.Immutable<{ active: boolean @@ -33,6 +32,10 @@ const initialStore: Store = { export interface State extends Store { dispatch: { cancelReset: () => void + defer: { + onGetRecoverPasswordUsername: () => string + onStartProvision: (username: string, fromReset: boolean) => void + } onEngineIncomingImpl: (action: EngineGen.Actions) => void resetState: 'default' resetAccount: (password?: string) => void @@ -62,7 +65,7 @@ export const useAutoResetState = Z.createZustand((set, get) => { switch (error.code) { case T.RPCGen.StatusCode.scnosession: // We got logged out because we were revoked (which might have been - // becase the reset was completed and this device wasn't notified). + // because the reset was completed and this device wasn't notified). return undefined case T.RPCGen.StatusCode.scnotfound: // "User not in autoreset queue." @@ -82,6 +85,14 @@ export const useAutoResetState = Z.createZustand((set, get) => { } ignorePromise(f()) }, + defer: { + onGetRecoverPasswordUsername: () => { + throw new Error('onGetRecoverPasswordUsername not properly initialized') + }, + onStartProvision: (_username: string, _fromReset: boolean) => { + throw new Error('onStartProvision not properly initialized') + }, + }, onEngineIncomingImpl: action => { switch (action.type) { case EngineGen.keybase1NotifyBadgesBadgeState: { @@ -118,7 +129,7 @@ export const useAutoResetState = Z.createZustand((set, get) => { set(s => { s.error = '' }) - storeRegistry.getState('provision').dispatch.startProvision(get().username, true) + get().dispatch.defer.onStartProvision(get().username, true) } else { navUpToScreen('login') } @@ -140,12 +151,7 @@ export const useAutoResetState = Z.createZustand((set, get) => { s.endTime = params.endTime * 1000 }) } - storeRegistry - .getState('router') - .dispatch.navigateAppend( - {props: {pipelineStarted: !params.needVerify}, selected: 'resetWaiting'}, - true - ) + navigateAppend({props: {pipelineStarted: !params.needVerify}, selected: 'resetWaiting'}, true) }, }, params: { @@ -169,7 +175,7 @@ export const useAutoResetState = Z.createZustand((set, get) => { }, resetState: 'default', startAccountReset: (skipPassword, _username) => { - const username = _username || storeRegistry.getState('recover-password').username + const username = _username || get().dispatch.defer.onGetRecoverPasswordUsername() || '' set(s => { s.skipPassword = skipPassword s.error = '' diff --git a/shared/constants/bots/index.tsx b/shared/stores/bots.tsx similarity index 91% rename from shared/constants/bots/index.tsx rename to shared/stores/bots.tsx index beb76b45a4d4..e922a4538206 100644 --- a/shared/constants/bots/index.tsx +++ b/shared/stores/bots.tsx @@ -1,8 +1,8 @@ -import * as T from '../types' +import * as T from '@/constants/types' import * as EngineGen from '@/actions/engine-gen-gen' import * as Z from '@/util/zustand' -import {ignorePromise, RPCError, isNetworkErr} from '../utils' -import * as S from '../strings' +import {ignorePromise, RPCError, isNetworkErr} from '@/constants/utils' +import * as S from '@/constants/strings' import logger from '@/logger' type BotSearchResults = { @@ -183,4 +183,17 @@ export const useBotsState = Z.createZustand((set, get) => { } }) -export {getFeaturedSorted} from './util' +export const getFeaturedSorted = ( + featuredBotsMap: ReadonlyMap +): Array => { + const featured = [...featuredBotsMap.values()] + featured.sort((a: T.RPCGen.FeaturedBot, b: T.RPCGen.FeaturedBot) => { + if (a.rank < b.rank) { + return 1 + } else if (a.rank > b.rank) { + return -1 + } + return 0 + }) + return featured +} diff --git a/shared/stores/chat2.tsx b/shared/stores/chat2.tsx new file mode 100644 index 000000000000..693304814f4d --- /dev/null +++ b/shared/stores/chat2.tsx @@ -0,0 +1,2047 @@ +import * as Common from '@/constants/chat2/common' +import * as EngineGen from '@/actions/engine-gen-gen' +import * as Message from '@/constants/chat2/message' +import * as Meta from '@/constants/chat2/meta' +import * as S from '@/constants/strings' +import * as T from '@/constants/types' +import * as Tabs from '@/constants/tabs' +import * as TeamConstants from '@/constants/teams' +import * as Z from '@/util/zustand' +import isEqual from 'lodash/isEqual' +import logger from '@/logger' +import type * as Router2 from '@/stores/router2' +import {type ChatProviderProps, ProviderScreen} from '@/stores/convostate' +import type {GetOptionsRet} from '@/constants/types/router2' +import {RPCError} from '@/util/errors' +import {bodyToJSON} from '@/constants/rpc-utils' +import {clearChatStores, chatStores} from '@/stores/convostate' +import {ignorePromise, timeoutPromise, type ViewPropsToPageProps} from '@/constants/utils' +import {isMobile, isPhone} from '@/constants/platform' +import { + navigateAppend, + navUpToScreen, + switchTab, + getModalStack, + getTab, + getVisibleScreen, +} from '@/constants/router2' +import {storeRegistry} from '@/stores/store-registry' +import {uint8ArrayToString} from 'uint8array-extras' +import {useConfigState} from '@/stores/config' +import {useCurrentUserState} from '@/stores/current-user' +import {useWaitingState} from '@/stores/waiting' + +const defaultTopReacjis = [ + {name: ':+1:'}, + {name: ':-1:'}, + {name: ':tada:'}, + {name: ':joy:'}, + {name: ':sunglasses:'}, +] +const defaultSkinTone = 1 +const defaultUserReacjis = {skinTone: defaultSkinTone, topReacjis: defaultTopReacjis} + +// while we're debugging chat issues +export const DEBUG_CHAT_DUMP = true + +const blockButtonsGregorPrefix = 'blockButtons.' + +export const inboxSearchMaxTextMessages = 25 +export const inboxSearchMaxTextResults = 50 +export const inboxSearchMaxNameResults = 7 +export const inboxSearchMaxUnreadNameResults = isMobile ? 5 : 10 + +export const makeInboxSearchInfo = (): T.Chat.InboxSearchInfo => ({ + botsResults: [], + botsResultsSuggested: false, + botsStatus: 'initial', + indexPercent: 0, + nameResults: [], + nameResultsUnread: false, + nameStatus: 'initial', + openTeamsResults: [], + openTeamsResultsSuggested: false, + openTeamsStatus: 'initial', + query: '', + selectedIndex: 0, + textResults: [], + textStatus: 'initial', +}) + +const getInboxSearchSelected = ( + inboxSearch: T.Immutable +): + | undefined + | { + conversationIDKey: T.Chat.ConversationIDKey + query?: string + } => { + const {selectedIndex, nameResults, botsResults, openTeamsResults, textResults} = inboxSearch + const firstTextResultIdx = botsResults.length + openTeamsResults.length + nameResults.length + const firstOpenTeamResultIdx = nameResults.length + + if (selectedIndex < firstOpenTeamResultIdx) { + const maybeNameResults = nameResults[selectedIndex] + const conversationIDKey = maybeNameResults === undefined ? undefined : maybeNameResults.conversationIDKey + if (conversationIDKey) { + return { + conversationIDKey, + query: undefined, + } + } + } else if (selectedIndex < firstTextResultIdx) { + return + } else if (selectedIndex >= firstTextResultIdx) { + const result = textResults[selectedIndex - firstTextResultIdx] + if (result) { + return { + conversationIDKey: result.conversationIDKey, + query: result.query, + } + } + } + return +} + +export const getMessageKey = (message: T.Chat.Message) => + `${message.conversationIDKey}:${T.Chat.ordinalToNumber(message.ordinal)}` + +export const getBotsAndParticipants = ( + meta: T.Immutable, + participantInfo: T.Immutable, + sort?: boolean +) => { + const isAdhocTeam = meta.teamType === 'adhoc' + const teamMembers = + useChatState.getState().dispatch.defer.onGetTeamsTeamIDToMembers(meta.teamID) ?? + new Map() + let bots: Array = [] + if (isAdhocTeam) { + bots = participantInfo.all.filter(p => !participantInfo.name.includes(p)) + } else { + bots = [...teamMembers.values()] + .filter( + p => + TeamConstants.userIsRoleInTeamWithInfo(teamMembers, p.username, 'restrictedbot') || + TeamConstants.userIsRoleInTeamWithInfo(teamMembers, p.username, 'bot') + ) + .map(p => p.username) + .sort((l, r) => l.localeCompare(r)) + } + let participants: ReadonlyArray = participantInfo.all + if (meta.channelname === 'general') { + participants = [...teamMembers.values()].reduce>((l, mi) => { + l.push(mi.username) + return l + }, []) + } + participants = participants.filter(p => !bots.includes(p)) + participants = sort + ? participants + .map(p => ({ + isAdmin: !isAdhocTeam ? TeamConstants.userIsRoleInTeamWithInfo(teamMembers, p, 'admin') : false, + isOwner: !isAdhocTeam ? TeamConstants.userIsRoleInTeamWithInfo(teamMembers, p, 'owner') : false, + username: p, + })) + .sort((l, r) => { + const leftIsAdmin = l.isAdmin || l.isOwner + const rightIsAdmin = r.isAdmin || r.isOwner + if (leftIsAdmin && !rightIsAdmin) { + return -1 + } else if (!leftIsAdmin && rightIsAdmin) { + return 1 + } + return l.username.localeCompare(r.username) + }) + .map(p => p.username) + : participants + return {bots, participants} +} + +export const getTeamMentionName = (name: string, channel: string) => { + return name + (channel ? `#${channel}` : '') +} + +export const isAssertion = (username: string) => username.includes('@') + +export const clampImageSize = (width: number, height: number, maxWidth: number, maxHeight: number) => { + const aspectRatio = width / height + + let newWidth = width + let newHeight = height + + if (newWidth > maxWidth) { + newWidth = maxWidth + newHeight = newWidth / aspectRatio + } + + if (newHeight > maxHeight) { + newHeight = maxHeight + newWidth = newHeight * aspectRatio + } + + return { + height: Math.ceil(newHeight), + width: Math.ceil(newWidth), + } +} + +export const zoomImage = (width: number, height: number, maxThumbSize: number) => { + const dims = + height > width + ? {height: (maxThumbSize * height) / width, width: maxThumbSize} + : {height: maxThumbSize, width: (maxThumbSize * width) / height} + const marginHeight = dims.height > maxThumbSize ? (dims.height - maxThumbSize) / 2 : 0 + const marginWidth = dims.width > maxThumbSize ? (dims.width - maxThumbSize) / 2 : 0 + return { + dims, + margins: { + marginBottom: -marginHeight, + marginLeft: -marginWidth, + marginRight: -marginWidth, + marginTop: -marginHeight, + }, + } +} + +const uiParticipantsToParticipantInfo = ( + uiParticipants: ReadonlyArray +): T.Chat.ParticipantInfo => { + const participantInfo = {all: new Array(), contactName: new Map(), name: new Array()} + uiParticipants.forEach(part => { + const {assertion, contactName, inConvName} = part + participantInfo.all.push(assertion) + if (inConvName) { + participantInfo.name.push(assertion) + } + if (contactName) { + participantInfo.contactName.set(assertion, contactName) + } + }) + return participantInfo +} + +/** + * Returns true if the team is big and you're a member + */ +export const isBigTeam = (state: State, teamID: string): boolean => { + const bigTeams = state.inboxLayout?.bigTeams + return (bigTeams || []).some(v => v.state === T.RPCChat.UIInboxBigTeamRowTyp.label && v.label.id === teamID) +} + +// prettier-ignore +type PreviewReason = + | 'appLink' | 'channelHeader' | 'convertAdHoc' | 'files' | 'forward' | 'fromAReset' + | 'journeyCardPopular' | 'manageView' | 'memberView' | 'messageLink' | 'newChannel' + | 'profile' | 'requestedPayment' | 'resetChatWithoutThem' | 'search' | 'sentPayment' + | 'teamHeader' | 'teamInvite' | 'teamMember' | 'teamMention' | 'teamRow' | 'tracker' | 'transaction' + +type Store = T.Immutable<{ + botPublicCommands: Map + createConversationError?: T.Chat.CreateConversationError + smallTeamBadgeCount: number + bigTeamBadgeCount: number + smallTeamsExpanded: boolean // if we're showing all small teams, + lastCoord?: T.Chat.Coordinate + paymentStatusMap: Map + staticConfig?: T.Chat.StaticConfig // static config stuff from the service. only needs to be loaded once. if null, it hasn't been loaded, + trustedInboxHasLoaded: boolean // if we've done initial trusted inbox load, + userReacjis: T.Chat.UserReacjis + userEmojis?: Array + userEmojisForAutocomplete?: Array + infoPanelShowing: boolean + infoPanelSelectedTab?: 'settings' | 'members' | 'attachments' | 'bots' + inboxNumSmallRows?: number + inboxHasLoaded: boolean // if we've ever loaded, + inboxLayout?: T.RPCChat.UIInboxLayout // layout of the inbox + inboxSearch?: T.Chat.InboxSearchInfo + teamIDToGeneralConvID: Map + flipStatusMap: Map + maybeMentionMap: Map + blockButtonsMap: Map // Should we show block buttons for this team ID? +}> + +const initialStore: Store = { + bigTeamBadgeCount: 0, + blockButtonsMap: new Map(), + botPublicCommands: new Map(), + createConversationError: undefined, + flipStatusMap: new Map(), + inboxHasLoaded: false, + inboxLayout: undefined, + inboxNumSmallRows: 5, + inboxSearch: undefined, + infoPanelSelectedTab: undefined, + infoPanelShowing: false, + lastCoord: undefined, + maybeMentionMap: new Map(), + paymentStatusMap: new Map(), + smallTeamBadgeCount: 0, + smallTeamsExpanded: false, + staticConfig: undefined, + teamIDToGeneralConvID: new Map(), + trustedInboxHasLoaded: false, + userEmojis: undefined, + userEmojisForAutocomplete: undefined, + userReacjis: defaultUserReacjis, +} + +export type RefreshReason = + | 'bootstrap' + | 'componentNeverLoaded' + | 'inboxStale' + | 'inboxSyncedClear' + | 'inboxSyncedUnknown' + | 'joinedAConversation' + | 'leftAConversation' + | 'teamTypeChanged' + | 'maybeKickedFromTeam' + | 'widgetRefresh' + | 'shareConfigSearch' + +export interface State extends Store { + dispatch: { + badgesUpdated: (badgeState?: T.RPCGen.BadgeState) => void + clearMetas: () => void + defer: { + onGetDaemonState: () => {handshakeVersion: number; dispatch: any} + onGetTeamsTeamIDToMembers: ( + teamID: T.Teams.TeamID + ) => ReadonlyMap | undefined + onGetUsersInfoMap: () => ReadonlyMap + onTeamsGetMembers: (teamID: T.Teams.TeamID) => void + onTeamsUpdateTeamRetentionPolicy: (metas: ReadonlyArray) => void + onUsersUpdates: (updates: ReadonlyArray<{name: string; info: Partial}>) => void + } + conversationErrored: ( + allowedUsers: ReadonlyArray, + disallowedUsers: ReadonlyArray, + code: number, + message: string + ) => void + createConversation: (participants: ReadonlyArray, highlightMessageID?: T.Chat.MessageID) => void + ensureWidgetMetas: () => void + findGeneralConvIDFromTeamID: (teamID: T.Teams.TeamID) => void + fetchUserEmoji: (conversationIDKey?: T.Chat.ConversationIDKey, onlyInTeam?: boolean) => void + inboxRefresh: (reason: RefreshReason) => void + inboxSearch: (query: string) => void + inboxSearchMoveSelectedIndex: (increment: boolean) => void + inboxSearchSelect: ( + conversationIDKey?: T.Chat.ConversationIDKey, + query?: string, + selectedIndex?: number + ) => void + loadStaticConfig: () => void + loadedUserEmoji: (results: T.RPCChat.UserEmojiRes) => void + maybeChangeSelectedConv: () => void + messageSendByUsername: (username: string, text: string, waitingKey?: string) => void + metasReceived: ( + metas: ReadonlyArray, + removals?: ReadonlyArray // convs to remove + ) => void + navigateToInbox: (allowSwitchTab?: boolean) => void + onChatThreadStale: (action: EngineGen.Chat1NotifyChatChatThreadsStalePayload) => void + onEngineIncomingImpl: (action: EngineGen.Actions) => void + onChatInboxSynced: (action: EngineGen.Chat1NotifyChatChatInboxSyncedPayload) => void + onGetInboxConvsUnboxed: (action: EngineGen.Chat1ChatUiChatInboxConversationPayload) => void + onGetInboxUnverifiedConvs: (action: EngineGen.Chat1ChatUiChatInboxUnverifiedPayload) => void + onIncomingInboxUIItem: (inboxUIItem?: T.RPCChat.InboxUIItem) => void + onRouteChanged: (prev: T.Immutable, next: T.Immutable) => void + onTeamBuildingFinished: (users: ReadonlySet) => void + paymentInfoReceived: (paymentInfo: T.Chat.ChatPaymentInfo) => void + previewConversation: (p: { + participants?: ReadonlyArray + teamname?: string + channelname?: string + conversationIDKey?: T.Chat.ConversationIDKey // we only use this when we click on channel mentions. we could maybe change that plumbing but keeping it for now + highlightMessageID?: T.Chat.MessageID + reason: PreviewReason + }) => void + queueMetaToRequest: (ids: ReadonlyArray) => void + queueMetaHandle: () => void + refreshBotPublicCommands: (username: string) => void + resetConversationErrored: () => void + resetState: () => void + setMaybeMentionInfo: (name: string, info: T.RPCChat.UIMaybeMentionInfo) => void + setTrustedInboxHasLoaded: () => void + setInfoPanelTab: (tab: 'settings' | 'members' | 'attachments' | 'bots' | undefined) => void + setInboxNumSmallRows: (rows: number, ignoreWrite?: boolean) => void + toggleInboxSearch: (enabled: boolean) => void + toggleSmallTeamsExpanded: () => void + unboxRows: (ids: ReadonlyArray, force?: boolean) => void + updateCoinFlipStatus: (statuses: ReadonlyArray) => void + updateInboxLayout: (layout: string) => void + updateLastCoord: (coord: T.Chat.Coordinate) => void + updateUserReacjis: (userReacjis: T.RPCGen.UserReacjis) => void + updatedGregor: ( + items: ReadonlyArray<{md: T.RPCGen.Gregor1.Metadata; item: T.RPCGen.Gregor1.Item}> + ) => void + updateInfoPanel: (show: boolean, tab: 'settings' | 'members' | 'attachments' | 'bots' | undefined) => void + } + getBackCount: (conversationIDKey: T.Chat.ConversationIDKey) => number + getBadgeHiddenCount: (ids: ReadonlySet) => { + badgeCount: number + hiddenCount: number + } + getUnreadIndicies: (ids: ReadonlyArray) => Map +} + +// Only get the untrusted conversations out +const untrustedConversationIDKeys = (ids: ReadonlyArray) => + ids.filter(id => storeRegistry.getConvoState(id).meta.trustedState === 'untrusted') + +// generic chat store +export const useChatState = Z.createZustand((set, get) => { + // We keep a set of conversations to unbox + let metaQueue = new Set() + + const dispatch: State['dispatch'] = { + badgesUpdated: b => { + if (!b) return + // clear all first + for (const [, cs] of chatStores) { + cs.getState().dispatch.badgesUpdated(0) + } + b.conversations?.forEach(c => { + const id = T.Chat.conversationIDToKey(c.convID) + storeRegistry.getConvoState(id).dispatch.badgesUpdated(c.badgeCount) + storeRegistry.getConvoState(id).dispatch.unreadUpdated(c.unreadMessages) + }) + const {bigTeamBadgeCount, smallTeamBadgeCount} = b + set(s => { + s.smallTeamBadgeCount = smallTeamBadgeCount + s.bigTeamBadgeCount = bigTeamBadgeCount + }) + }, + clearMetas: () => { + for (const [, cs] of chatStores) { + cs.getState().dispatch.setMeta() + } + }, + conversationErrored: (allowedUsers, disallowedUsers, code, message) => { + set(s => { + s.createConversationError = T.castDraft({ + allowedUsers, + code, + disallowedUsers, + message, + }) + }) + }, + createConversation: (participants, highlightMessageID) => { + // TODO This will break if you try to make 2 new conversations at the same time because there is + // only one pending conversation state. + // The fix involves being able to make multiple pending conversations + const f = async () => { + const username = useCurrentUserState.getState().username + if (!username) { + logger.error('Making a convo while logged out?') + return + } + try { + const result = await T.RPCChat.localNewConversationLocalRpcPromise( + { + identifyBehavior: T.RPCGen.TLFIdentifyBehavior.chatGui, + membersType: T.RPCChat.ConversationMembersType.impteamnative, + tlfName: [...new Set([username, ...participants])].join(','), + tlfVisibility: T.RPCGen.TLFVisibility.private, + topicType: T.RPCChat.TopicType.chat, + }, + S.waitingKeyChatCreating + ) + const {conv, uiConv} = result + const conversationIDKey = T.Chat.conversationIDToKey(conv.info.id) + if (!conversationIDKey) { + logger.warn("Couldn't make a new conversation?") + } else { + const meta = Meta.inboxUIItemToConversationMeta(uiConv) + if (meta) { + get().dispatch.metasReceived([meta]) + } + + const participantInfo: T.Chat.ParticipantInfo = uiParticipantsToParticipantInfo( + uiConv.participants ?? [] + ) + if (participantInfo.all.length > 0) { + storeRegistry + .getConvoState(T.Chat.stringToConversationIDKey(uiConv.convID)) + .dispatch.setParticipants(participantInfo) + } + storeRegistry + .getConvoState(conversationIDKey) + .dispatch.navigateToThread('justCreated', highlightMessageID) + } + } catch (error) { + if (error instanceof RPCError) { + const f = error.fields as Array<{key?: string}> | undefined + const errUsernames = f?.filter(elem => elem.key === 'usernames') as + | undefined + | Array<{key: string; value: string}> + let disallowedUsers: Array = [] + if (errUsernames?.length) { + const {value} = errUsernames[0] ?? {value: ''} + disallowedUsers = value.split(',') + } + const allowedUsers = participants.filter(x => !disallowedUsers.includes(x)) + get().dispatch.conversationErrored(allowedUsers, disallowedUsers, error.code, error.desc) + storeRegistry + .getConvoState(T.Chat.pendingErrorConversationIDKey) + .dispatch.navigateToThread('justCreated', highlightMessageID) + } + } + } + ignorePromise(f()) + }, + defer: { + onGetDaemonState: () => { + throw new Error('onGetDaemonState not properly initialized') + }, + onGetTeamsTeamIDToMembers: (_teamID: T.Teams.TeamID) => { + throw new Error('onGetTeamsTeamIDToMembers not properly initialized') + }, + onGetUsersInfoMap: () => { + throw new Error('onGetUsersInfoMap not properly initialized') + }, + onTeamsGetMembers: (_teamID: T.Teams.TeamID) => { + throw new Error('onTeamsGetMembers not properly initialized') + }, + onTeamsUpdateTeamRetentionPolicy: (_metas: ReadonlyArray) => { + throw new Error('onTeamsUpdateTeamRetentionPolicy not properly initialized') + }, + onUsersUpdates: (_updates: ReadonlyArray<{name: string; info: Partial}>) => { + throw new Error('onUsersUpdates not properly initialized') + }, + }, + ensureWidgetMetas: () => { + const {inboxLayout} = get() + if (!inboxLayout?.widgetList) { + return + } + const missing = inboxLayout.widgetList.reduce>((l, v) => { + if (!storeRegistry.getConvoState(v.convID).isMetaGood()) { + l.push(v.convID) + } + return l + }, []) + if (missing.length === 0) { + return + } + get().dispatch.unboxRows(missing, true) + }, + fetchUserEmoji: (conversationIDKey, onlyInTeam) => { + const f = async () => { + const results = await T.RPCChat.localUserEmojisRpcPromise( + { + convID: + conversationIDKey && conversationIDKey !== T.Chat.noConversationIDKey + ? T.Chat.keyToConversationID(conversationIDKey) + : null, + opts: { + getAliases: true, + getCreationInfo: false, + onlyInTeam: onlyInTeam ?? false, + }, + }, + S.waitingKeyChatLoadingEmoji + ) + get().dispatch.loadedUserEmoji(results) + } + ignorePromise(f()) + }, + findGeneralConvIDFromTeamID: teamID => { + const f = async () => { + try { + const conv = await T.RPCChat.localFindGeneralConvFromTeamIDRpcPromise({teamID}) + const meta = Meta.inboxUIItemToConversationMeta(conv) + if (!meta) { + logger.info(`findGeneralConvIDFromTeamID: failed to convert to meta`) + return + } + get().dispatch.metasReceived([meta]) + set(s => { + s.teamIDToGeneralConvID.set(teamID, T.Chat.stringToConversationIDKey(conv.convID)) + }) + } catch (error) { + if (error instanceof RPCError) { + logger.info(`findGeneralConvIDFromTeamID: failed to get general conv: ${error.message}`) + } + } + } + ignorePromise(f()) + }, + inboxRefresh: reason => { + const f = async () => { + const {username} = useCurrentUserState.getState() + const {loggedIn} = useConfigState.getState() + if (!loggedIn || !username) { + return + } + const clearExistingMetas = reason === 'inboxSyncedClear' + const clearExistingMessages = reason === 'inboxSyncedClear' + + logger.info(`Inbox refresh due to ${reason}`) + const reselectMode = + get().inboxHasLoaded || isPhone + ? T.RPCChat.InboxLayoutReselectMode.default + : T.RPCChat.InboxLayoutReselectMode.force + await T.RPCChat.localRequestInboxLayoutRpcPromise({reselectMode}) + if (clearExistingMetas) { + get().dispatch.clearMetas() + } + if (clearExistingMessages) { + for (const [, cs] of chatStores) { + cs.getState().dispatch.messagesClear() + } + } + } + ignorePromise(f()) + }, + inboxSearch: query => { + set(s => { + const {inboxSearch} = s + if (inboxSearch) { + inboxSearch.query = query + } + }) + const f = async () => { + const teamType = (t: T.RPCChat.TeamType) => (t === T.RPCChat.TeamType.complex ? 'big' : 'small') + + const onConvHits = (resp: T.RPCChat.MessageTypes['chat.1.chatUi.chatSearchConvHits']['inParam']) => { + const results = (resp.hits.hits || []).reduce>((arr, h) => { + arr.push({ + conversationIDKey: T.Chat.stringToConversationIDKey(h.convID), + name: h.name, + teamType: teamType(h.teamType), + }) + return arr + }, []) + + set(s => { + const unread = resp.hits.unreadMatches + const {inboxSearch} = s + if (inboxSearch?.nameStatus === 'inprogress') { + inboxSearch.nameResults = results + inboxSearch.nameResultsUnread = unread + inboxSearch.nameStatus = 'success' + } + }) + + const missingMetas = results.reduce>((arr, r) => { + if (!storeRegistry.getConvoState(r.conversationIDKey).isMetaGood()) { + arr.push(r.conversationIDKey) + } + return arr + }, []) + if (missingMetas.length > 0) { + get().dispatch.unboxRows(missingMetas, true) + } + } + + const onOpenTeamHits = ( + resp: T.RPCChat.MessageTypes['chat.1.chatUi.chatSearchTeamHits']['inParam'] + ) => { + const results = (resp.hits.hits || []).reduce>((arr, h) => { + const {description, name, memberCount, inTeam} = h + arr.push({ + description: description ?? '', + inTeam, + memberCount, + name, + publicAdmins: [], + }) + return arr + }, []) + const suggested = resp.hits.suggestedMatches + set(s => { + const {inboxSearch} = s + if (inboxSearch?.openTeamsStatus === 'inprogress') { + inboxSearch.openTeamsResultsSuggested = suggested + inboxSearch.openTeamsResults = T.castDraft(results) + inboxSearch.openTeamsStatus = 'success' + } + }) + } + + const onBotsHits = (resp: T.RPCChat.MessageTypes['chat.1.chatUi.chatSearchBotHits']['inParam']) => { + const results = resp.hits.hits || [] + const suggested = resp.hits.suggestedMatches + set(s => { + const {inboxSearch} = s + if (inboxSearch?.botsStatus === 'inprogress') { + inboxSearch.botsResultsSuggested = suggested + inboxSearch.botsResults = T.castDraft(results) + inboxSearch.botsStatus = 'success' + } + }) + } + + const onTextHit = (resp: T.RPCChat.MessageTypes['chat.1.chatUi.chatSearchInboxHit']['inParam']) => { + const {convID, convName, hits, query, teamType: tt, time} = resp.searchHit + + const result = { + conversationIDKey: T.Chat.conversationIDToKey(convID), + name: convName, + numHits: hits?.length ?? 0, + query, + teamType: teamType(tt), + time, + } as const + set(s => { + const {inboxSearch} = s + if (inboxSearch?.textStatus === 'inprogress') { + const {conversationIDKey} = result + const textResults = inboxSearch.textResults.filter( + r => r.conversationIDKey !== conversationIDKey + ) + textResults.push(result) + inboxSearch.textResults = textResults.sort((l, r) => r.time - l.time) + } + }) + + if ( + storeRegistry.getConvoState(result.conversationIDKey).meta.conversationIDKey === + T.Chat.noConversationIDKey + ) { + get().dispatch.unboxRows([result.conversationIDKey], true) + } + } + const onStart = () => { + set(s => { + const {inboxSearch} = s + if (inboxSearch) { + inboxSearch.nameStatus = 'inprogress' + inboxSearch.selectedIndex = 0 + inboxSearch.textResults = [] + inboxSearch.textStatus = 'inprogress' + inboxSearch.openTeamsStatus = 'inprogress' + inboxSearch.botsStatus = 'inprogress' + } + }) + } + const onDone = () => { + set(s => { + const status = 'success' + const inboxSearch = s.inboxSearch ?? makeInboxSearchInfo() + s.inboxSearch = T.castDraft(inboxSearch) + inboxSearch.textStatus = status + }) + } + + const onIndexStatus = ( + resp: T.RPCChat.MessageTypes['chat.1.chatUi.chatSearchIndexStatus']['inParam'] + ) => { + const percent = resp.status.percentIndexed + set(s => { + const {inboxSearch} = s + if (inboxSearch?.textStatus === 'inprogress') { + inboxSearch.indexPercent = percent + } + }) + } + + try { + await T.RPCChat.localSearchInboxRpcListener({ + incomingCallMap: { + 'chat.1.chatUi.chatSearchBotHits': onBotsHits, + 'chat.1.chatUi.chatSearchConvHits': onConvHits, + 'chat.1.chatUi.chatSearchInboxDone': onDone, + 'chat.1.chatUi.chatSearchInboxHit': onTextHit, + 'chat.1.chatUi.chatSearchInboxStart': onStart, + 'chat.1.chatUi.chatSearchIndexStatus': onIndexStatus, + 'chat.1.chatUi.chatSearchTeamHits': onOpenTeamHits, + }, + params: { + identifyBehavior: T.RPCGen.TLFIdentifyBehavior.chatGui, + namesOnly: false, + opts: { + afterContext: 0, + beforeContext: 0, + isRegex: false, + matchMentions: false, + maxBots: 10, + maxConvsHit: inboxSearchMaxTextResults, + maxConvsSearched: 0, + maxHits: inboxSearchMaxTextMessages, + maxMessages: -1, + maxNameConvs: query.length > 0 ? inboxSearchMaxNameResults : inboxSearchMaxUnreadNameResults, + maxTeams: 10, + reindexMode: T.RPCChat.ReIndexingMode.postsearchSync, + sentAfter: 0, + sentBefore: 0, + sentBy: '', + sentTo: '', + skipBotCache: false, + }, + query, + }, + }) + } catch (error) { + if (error instanceof RPCError) { + if (!(error.code === T.RPCGen.StatusCode.sccanceled)) { + logger.error('search failed: ' + error.message) + set(s => { + const status = 'error' + const inboxSearch = s.inboxSearch ?? makeInboxSearchInfo() + s.inboxSearch = T.castDraft(inboxSearch) + inboxSearch.textStatus = status + }) + } + } + } + } + ignorePromise(f()) + }, + inboxSearchMoveSelectedIndex: increment => { + set(s => { + const {inboxSearch} = s + if (inboxSearch) { + const {selectedIndex} = inboxSearch + const totalResults = inboxSearch.nameResults.length + inboxSearch.textResults.length + if (increment && selectedIndex < totalResults - 1) { + inboxSearch.selectedIndex = selectedIndex + 1 + } else if (!increment && selectedIndex > 0) { + inboxSearch.selectedIndex = selectedIndex - 1 + } + } + }) + }, + inboxSearchSelect: (_conversationIDKey, q, selectedIndex) => { + let conversationIDKey = _conversationIDKey + let query = q + set(s => { + const {inboxSearch} = s + if (inboxSearch && selectedIndex !== undefined) { + inboxSearch.selectedIndex = selectedIndex + } + }) + + const {inboxSearch} = get() + if (!inboxSearch) { + return + } + const selected = getInboxSearchSelected(inboxSearch) + if (!conversationIDKey) { + conversationIDKey = selected?.conversationIDKey + } + + if (!conversationIDKey) { + return + } + if (!query) { + query = selected?.query + } + + storeRegistry.getConvoState(conversationIDKey).dispatch.navigateToThread('inboxSearch') + if (query) { + const cs = storeRegistry.getConvoState(conversationIDKey) + cs.dispatch.setThreadSearchQuery(query) + cs.dispatch.toggleThreadSearch(false) + cs.dispatch.threadSearch(query) + } else { + get().dispatch.toggleInboxSearch(false) + } + }, + loadStaticConfig: () => { + if (get().staticConfig) { + return + } + const {handshakeVersion, dispatch} = get().dispatch.defer.onGetDaemonState() + const f = async () => { + const name = 'chat.loadStatic' + dispatch.wait(name, handshakeVersion, true) + try { + const res = await T.RPCChat.localGetStaticConfigRpcPromise() + if (!res.deletableByDeleteHistory) { + logger.error('chat.loadStaticConfig: got no deletableByDeleteHistory in static config') + return + } + const deletableByDeleteHistory = res.deletableByDeleteHistory.reduce>( + (res, type) => { + const ourTypes = Message.serviceMessageTypeToMessageTypes(type) + res.push(...ourTypes) + return res + }, + [] + ) + set(s => { + s.staticConfig = { + builtinCommands: (res.builtinCommands || []).reduce( + (map, c) => { + map[c.typ] = T.castDraft(c.commands) || [] + return map + }, + { + [T.RPCChat.ConversationBuiltinCommandTyp.none]: [], + [T.RPCChat.ConversationBuiltinCommandTyp.adhoc]: [], + [T.RPCChat.ConversationBuiltinCommandTyp.smallteam]: [], + [T.RPCChat.ConversationBuiltinCommandTyp.bigteam]: [], + [T.RPCChat.ConversationBuiltinCommandTyp.bigteamgeneral]: [], + } + ), + deletableByDeleteHistory: new Set(deletableByDeleteHistory), + } + }) + } finally { + dispatch.wait(name, handshakeVersion, false) + } + } + ignorePromise(f()) + }, + loadedUserEmoji: results => { + set(s => { + const newEmojis: Array = [] + results.emojis.emojis?.forEach(group => { + group.emojis?.forEach(e => newEmojis.push(e)) + }) + s.userEmojisForAutocomplete = newEmojis + s.userEmojis = T.castDraft(results.emojis.emojis) ?? [] + }) + }, + maybeChangeSelectedConv: () => { + const {inboxLayout} = get() + const newConvID = inboxLayout?.reselectInfo?.newConvID + const oldConvID = inboxLayout?.reselectInfo?.oldConvID + + const selectedConversation = Common.getSelectedConversation() + + if (!newConvID && !oldConvID) { + return + } + + const existingValid = T.Chat.isValidConversationIDKey(selectedConversation) + // no new id, just take the opportunity to resolve + if (!newConvID) { + if (!existingValid && isPhone) { + logger.info(`maybeChangeSelectedConv: no new and no valid, so go to inbox`) + get().dispatch.navigateToInbox(false) + } + return + } + // not matching? + if (selectedConversation !== oldConvID) { + if (!existingValid && isPhone) { + logger.info(`maybeChangeSelectedConv: no new and no valid, so go to inbox`) + get().dispatch.navigateToInbox(false) + } + return + } + // matching + if (isPhone) { + // on mobile just head back to the inbox if we have something selected + if (T.Chat.isValidConversationIDKey(selectedConversation)) { + logger.info(`maybeChangeSelectedConv: mobile: navigating up on conv change`) + get().dispatch.navigateToInbox(false) + return + } + logger.info(`maybeChangeSelectedConv: mobile: ignoring conv change, no conv selected`) + return + } else { + logger.info( + `maybeChangeSelectedConv: selecting new conv: new:${newConvID} old:${oldConvID} prevselected ${selectedConversation}` + ) + storeRegistry.getConvoState(newConvID).dispatch.navigateToThread('findNewestConversation') + } + }, + messageSendByUsername: (username, text, waitingKey) => { + const f = async () => { + const tlfName = `${useCurrentUserState.getState().username},${username}` + try { + const result = await T.RPCChat.localNewConversationLocalRpcPromise( + { + identifyBehavior: T.RPCGen.TLFIdentifyBehavior.chatGui, + membersType: T.RPCChat.ConversationMembersType.impteamnative, + tlfName, + tlfVisibility: T.RPCGen.TLFVisibility.private, + topicType: T.RPCChat.TopicType.chat, + }, + waitingKey + ) + storeRegistry + .getConvoState(T.Chat.conversationIDToKey(result.conv.info.id)) + .dispatch.sendMessage(text) + } catch (error) { + if (error instanceof RPCError) { + logger.warn('Could not send in messageSendByUsernames', error.message) + } + } + } + ignorePromise(f()) + }, + metasReceived: (metas, removals) => { + removals?.forEach(r => { + storeRegistry.getConvoState(r).dispatch.setMeta() + }) + metas.forEach(m => { + const {meta: oldMeta, dispatch, isMetaGood} = storeRegistry.getConvoState(m.conversationIDKey) + if (isMetaGood()) { + dispatch.updateMeta(Meta.updateMeta(oldMeta, m)) + } else { + dispatch.setMeta(m) + } + }) + + const selectedConversation = Common.getSelectedConversation() + const {isMetaGood, meta} = storeRegistry.getConvoState(selectedConversation) + if (isMetaGood()) { + const {teamID} = meta + if (!get().dispatch.defer.onGetTeamsTeamIDToMembers(teamID) && meta.teamname) { + get().dispatch.defer.onTeamsGetMembers(teamID) + } + } + }, + navigateToInbox: (allowSwitchTab = true) => { + // components can call us during render sometimes so always defer + setTimeout(() => { + navUpToScreen('chatRoot') + if (allowSwitchTab) { + switchTab(Tabs.chatTab) + } + }, 1) + }, + onChatInboxSynced: action => { + const {syncRes} = action.payload.params + const {clear} = useWaitingState.getState().dispatch + const {inboxRefresh} = get().dispatch + clear(S.waitingKeyChatInboxSyncStarted) + + switch (syncRes.syncType) { + // Just clear it all + case T.RPCChat.SyncInboxResType.clear: + inboxRefresh('inboxSyncedClear') + break + // We're up to date + case T.RPCChat.SyncInboxResType.current: + break + // We got some new messages appended + case T.RPCChat.SyncInboxResType.incremental: { + const items = syncRes.incremental.items || [] + const selectedConversation = Common.getSelectedConversation() + let loadMore = false as boolean + const metas = items.reduce>((arr, i) => { + const meta = Meta.unverifiedInboxUIItemToConversationMeta(i.conv) + if (meta) { + arr.push(meta) + if (meta.conversationIDKey === selectedConversation) { + loadMore = true + } + } + return arr + }, []) + if (loadMore) { + storeRegistry.getConvoState(selectedConversation).dispatch.loadMoreMessages({reason: 'got stale'}) + } + const removals = syncRes.incremental.removals?.map(T.Chat.stringToConversationIDKey) + // Update new untrusted + if (metas.length || removals?.length) { + get().dispatch.metasReceived(metas, removals) + } + + get().dispatch.unboxRows( + items.filter(i => i.shouldUnbox).map(i => T.Chat.stringToConversationIDKey(i.conv.convID)), + true + ) + break + } + default: + inboxRefresh('inboxSyncedUnknown') + } + }, + onChatThreadStale: (action: EngineGen.Chat1NotifyChatChatThreadsStalePayload) => { + const {updates} = action.payload.params + const keys = ['clear', 'newactivity'] as const + if (__DEV__) { + if (keys.length * 2 !== Object.keys(T.RPCChat.StaleUpdateType).length) { + throw new Error('onChatThreadStale invalid enum') + } + } + let loadMore = false as boolean + const selectedConversation = Common.getSelectedConversation() + keys.forEach(key => { + const conversationIDKeys = (updates || []).reduce>((arr, u) => { + const cid = T.Chat.conversationIDToKey(u.convID) + if (u.updateType === T.RPCChat.StaleUpdateType[key]) { + arr.push(cid) + } + // mentioned? + if (cid === selectedConversation) { + loadMore = true + } + return arr + }, []) + // load the inbox instead + if (conversationIDKeys.length > 0) { + logger.info( + `onChatThreadStale: dispatching thread reload actions for ${conversationIDKeys.length} convs of type ${key}` + ) + get().dispatch.unboxRows(conversationIDKeys, true) + if (T.RPCChat.StaleUpdateType[key] === T.RPCChat.StaleUpdateType.clear) { + conversationIDKeys.forEach(convID => storeRegistry.getConvoState(convID).dispatch.messagesClear()) + } + } + }) + if (loadMore) { + storeRegistry.getConvoState(selectedConversation).dispatch.loadMoreMessages({reason: 'got stale'}) + } + }, + onEngineIncomingImpl: action => { + switch (action.type) { + case EngineGen.chat1ChatUiChatInboxFailed: // fallthrough + case EngineGen.chat1NotifyChatChatSetConvSettings: // fallthrough + case EngineGen.chat1NotifyChatChatAttachmentUploadStart: // fallthrough + case EngineGen.chat1NotifyChatChatPromptUnfurl: // fallthrough + case EngineGen.chat1NotifyChatChatPaymentInfo: // fallthrough + case EngineGen.chat1NotifyChatChatRequestInfo: // fallthrough + case EngineGen.chat1NotifyChatChatAttachmentDownloadProgress: //fallthrough + case EngineGen.chat1NotifyChatChatAttachmentDownloadComplete: //fallthrough + case EngineGen.chat1NotifyChatChatAttachmentUploadProgress: { + const {convID} = action.payload.params + const conversationIDKey = T.Chat.conversationIDToKey(convID) + storeRegistry.getConvoState(conversationIDKey).dispatch.onEngineIncoming(action) + break + } + case EngineGen.chat1ChatUiChatCommandMarkdown: //fallthrough + case EngineGen.chat1ChatUiChatGiphyToggleResultWindow: // fallthrough + case EngineGen.chat1ChatUiChatCommandStatus: // fallthrough + case EngineGen.chat1ChatUiChatBotCommandsUpdateStatus: //fallthrough + case EngineGen.chat1ChatUiChatGiphySearchResults: { + const {convID} = action.payload.params + const conversationIDKey = T.Chat.stringToConversationIDKey(convID) + storeRegistry.getConvoState(conversationIDKey).dispatch.onEngineIncoming(action) + break + } + case EngineGen.chat1NotifyChatChatParticipantsInfo: { + const {participants: participantMap} = action.payload.params + Object.keys(participantMap ?? {}).forEach(convIDStr => { + const participants = participantMap?.[convIDStr] + const conversationIDKey = T.Chat.stringToConversationIDKey(convIDStr) + if (participants) { + storeRegistry + .getConvoState(conversationIDKey) + .dispatch.setParticipants(uiParticipantsToParticipantInfo(participants)) + } + }) + break + } + case EngineGen.chat1ChatUiChatMaybeMentionUpdate: { + const {teamName, channel, info} = action.payload.params + get().dispatch.setMaybeMentionInfo(getTeamMentionName(teamName, channel), info) + break + } + case EngineGen.chat1NotifyChatChatConvUpdate: { + const {conv} = action.payload.params + if (conv) { + const meta = Meta.inboxUIItemToConversationMeta(conv) + meta && get().dispatch.metasReceived([meta]) + } + break + } + case EngineGen.chat1ChatUiChatCoinFlipStatus: { + const {statuses} = action.payload.params + get().dispatch.updateCoinFlipStatus(statuses || []) + break + } + case EngineGen.chat1NotifyChatChatThreadsStale: + get().dispatch.onChatThreadStale(action) + break + case EngineGen.chat1NotifyChatChatSubteamRename: { + const {convs} = action.payload.params + const conversationIDKeys = (convs ?? []).map(c => T.Chat.stringToConversationIDKey(c.convID)) + get().dispatch.unboxRows(conversationIDKeys, true) + break + } + case EngineGen.chat1NotifyChatChatTLFFinalize: + get().dispatch.unboxRows([T.Chat.conversationIDToKey(action.payload.params.convID)]) + break + case EngineGen.chat1NotifyChatChatIdentifyUpdate: { + // Some participants are broken/fixed now + const {update} = action.payload.params + const usernames = update.CanonicalName.split(',') + const broken = (update.breaks.breaks || []).map(b => b.user.username) + const updates = usernames.map(name => ({info: {broken: broken.includes(name)}, name})) + get().dispatch.defer.onUsersUpdates(updates) + break + } + case EngineGen.chat1ChatUiChatInboxUnverified: + get().dispatch.onGetInboxUnverifiedConvs(action) + break + case EngineGen.chat1NotifyChatChatInboxSyncStarted: + useWaitingState.getState().dispatch.increment(S.waitingKeyChatInboxSyncStarted) + break + + case EngineGen.chat1NotifyChatChatInboxSynced: + get().dispatch.onChatInboxSynced(action) + break + case EngineGen.chat1ChatUiChatInboxLayout: + get().dispatch.updateInboxLayout(action.payload.params.layout) + get().dispatch.maybeChangeSelectedConv() + get().dispatch.ensureWidgetMetas() + break + case EngineGen.chat1NotifyChatChatInboxStale: + get().dispatch.inboxRefresh('inboxStale') + break + case EngineGen.chat1ChatUiChatInboxConversation: + get().dispatch.onGetInboxConvsUnboxed(action) + break + case EngineGen.chat1NotifyChatNewChatActivity: { + const {activity} = action.payload.params + switch (activity.activityType) { + case T.RPCChat.ChatActivityType.incomingMessage: { + const {incomingMessage} = activity + const conversationIDKey = T.Chat.conversationIDToKey(incomingMessage.convID) + storeRegistry.getConvoState(conversationIDKey).dispatch.onIncomingMessage(incomingMessage) + get().dispatch.onIncomingInboxUIItem(incomingMessage.conv ?? undefined) + break + } + case T.RPCChat.ChatActivityType.setStatus: + get().dispatch.onIncomingInboxUIItem(activity.setStatus.conv ?? undefined) + break + case T.RPCChat.ChatActivityType.readMessage: + get().dispatch.onIncomingInboxUIItem(activity.readMessage.conv ?? undefined) + break + case T.RPCChat.ChatActivityType.newConversation: + get().dispatch.onIncomingInboxUIItem(activity.newConversation.conv ?? undefined) + break + case T.RPCChat.ChatActivityType.failedMessage: { + const {failedMessage} = activity + get().dispatch.onIncomingInboxUIItem(failedMessage.conv ?? undefined) + const {outboxRecords} = failedMessage + if (!outboxRecords) return + for (const outboxRecord of outboxRecords) { + const s = outboxRecord.state + if (s.state !== T.RPCChat.OutboxStateType.error) return + const {error} = s + const conversationIDKey = T.Chat.conversationIDToKey(outboxRecord.convID) + const outboxID = T.Chat.rpcOutboxIDToOutboxID(outboxRecord.outboxID) + // This is temp until fixed by CORE-7112. We get this error but not the call to let us show the red banner + const reason = Message.rpcErrorToString(error) + storeRegistry + .getConvoState(conversationIDKey) + .dispatch.onMessageErrored(outboxID, reason, error.typ) + + if (error.typ === T.RPCChat.OutboxErrorType.identify) { + // Find out the user who failed identify + const match = error.message.match(/"(.*)"/) + const tempForceRedBox = match?.[1] + if (tempForceRedBox) { + storeRegistry + .getState('users') + .dispatch.updates([{info: {broken: true}, name: tempForceRedBox}]) + } + } + } + break + } + case T.RPCChat.ChatActivityType.membersUpdate: + get().dispatch.unboxRows([T.Chat.conversationIDToKey(activity.membersUpdate.convID)], true) + break + case T.RPCChat.ChatActivityType.setAppNotificationSettings: { + const {setAppNotificationSettings} = activity + const conversationIDKey = T.Chat.conversationIDToKey(setAppNotificationSettings.convID) + const settings = setAppNotificationSettings.settings + const cs = storeRegistry.getConvoState(conversationIDKey) + if (cs.isMetaGood()) { + cs.dispatch.updateMeta(Meta.parseNotificationSettings(settings)) + } + break + } + case T.RPCChat.ChatActivityType.expunge: { + // Get actions to update messagemap / metamap when retention policy expunge happens + const {expunge} = activity + const conversationIDKey = T.Chat.conversationIDToKey(expunge.convID) + const staticConfig = get().staticConfig + // The types here are askew. It confuses frontend MessageType with protocol MessageType. + // Placeholder is an example where it doesn't make sense. + const deletableMessageTypes = staticConfig?.deletableByDeleteHistory || Common.allMessageTypes + storeRegistry.getConvoState(conversationIDKey).dispatch.messagesWereDeleted({ + deletableMessageTypes, + upToMessageID: T.Chat.numberToMessageID(expunge.expunge.upto), + }) + break + } + case T.RPCChat.ChatActivityType.ephemeralPurge: { + const {ephemeralPurge} = activity + // Get actions to update messagemap / metamap when ephemeral messages expire + const conversationIDKey = T.Chat.conversationIDToKey(ephemeralPurge.convID) + const messageIDs = ephemeralPurge.msgs?.reduce>((arr, msg) => { + const msgID = Message.getMessageID(msg) + if (msgID) { + arr.push(msgID) + } + return arr + }, []) + + !!messageIDs && + storeRegistry.getConvoState(conversationIDKey).dispatch.messagesExploded(messageIDs) + break + } + case T.RPCChat.ChatActivityType.reactionUpdate: { + // Get actions to update the messagemap when reactions are updated + const {reactionUpdate} = activity + const conversationIDKey = T.Chat.conversationIDToKey(reactionUpdate.convID) + if (!reactionUpdate.reactionUpdates || reactionUpdate.reactionUpdates.length === 0) { + logger.warn(`Got ReactionUpdateNotif with no reactionUpdates for convID=${conversationIDKey}`) + break + } + const updates = reactionUpdate.reactionUpdates.map(ru => ({ + reactions: Message.reactionMapToReactions(ru.reactions), + targetMsgID: T.Chat.numberToMessageID(ru.targetMsgID), + })) + logger.info(`Got ${updates.length} reaction updates for convID=${conversationIDKey}`) + storeRegistry.getConvoState(conversationIDKey).dispatch.updateReactions(updates) + get().dispatch.updateUserReacjis(reactionUpdate.userReacjis) + break + } + case T.RPCChat.ChatActivityType.messagesUpdated: { + const {messagesUpdated} = activity + const conversationIDKey = T.Chat.conversationIDToKey(messagesUpdated.convID) + storeRegistry.getConvoState(conversationIDKey).dispatch.onMessagesUpdated(messagesUpdated) + break + } + default: + } + break + } + case EngineGen.chat1NotifyChatChatTypingUpdate: { + const {typingUpdates} = action.payload.params + typingUpdates?.forEach(u => { + storeRegistry + .getConvoState(T.Chat.conversationIDToKey(u.convID)) + .dispatch.setTyping(new Set(u.typers?.map(t => t.username))) + }) + break + } + case EngineGen.chat1NotifyChatChatSetConvRetention: { + const {conv, convID} = action.payload.params + if (!conv) { + logger.warn('onChatSetConvRetention: no conv given') + return + } + const meta = Meta.inboxUIItemToConversationMeta(conv) + if (!meta) { + logger.warn(`onChatSetConvRetention: no meta found for ${convID.toString()}`) + return + } + const cs = storeRegistry.getConvoState(meta.conversationIDKey) + // only insert if the convo is already in the inbox + if (cs.isMetaGood()) { + cs.dispatch.setMeta(meta) + } + break + } + case EngineGen.chat1NotifyChatChatSetTeamRetention: { + const {convs} = action.payload.params + const metas = (convs ?? []).reduce>((l, c) => { + const meta = Meta.inboxUIItemToConversationMeta(c) + if (meta) { + l.push(meta) + } + return l + }, []) + if (metas.length) { + metas.forEach(meta => { + const cs = storeRegistry.getConvoState(meta.conversationIDKey) + // only insert if the convo is already in the inbox + if (cs.isMetaGood()) { + cs.dispatch.setMeta(meta) + } + }) + get().dispatch.defer.onTeamsUpdateTeamRetentionPolicy(metas) + } + // this is a more serious problem, but we don't need to bug the user about it + logger.error( + 'got NotifyChat.ChatSetTeamRetention with no attached InboxUIItems. The local version may be out of date' + ) + break + } + case EngineGen.keybase1NotifyBadgesBadgeState: { + const {badgeState} = action.payload.params + get().dispatch.badgesUpdated(badgeState) + break + } + case EngineGen.keybase1GregorUIPushState: { + const {state} = action.payload.params + const items = state.items || [] + const goodState = items.reduce>( + (arr, {md, item}) => { + md && item && arr.push({item, md}) + return arr + }, + [] + ) + if (goodState.length !== items.length) { + logger.warn('Lost some messages in filtering out nonNull gregor items') + } + get().dispatch.updatedGregor(goodState) + break + } + default: + } + }, + onGetInboxConvsUnboxed: (action: EngineGen.Chat1ChatUiChatInboxConversationPayload) => { + // TODO not reactive + const infoMap = get().dispatch.defer.onGetUsersInfoMap() + const {convs} = action.payload.params + const inboxUIItems = JSON.parse(convs) as Array + const metas: Array = [] + let added = false as boolean + const usernameToFullname: {[username: string]: string} = {} + inboxUIItems.forEach(inboxUIItem => { + const meta = Meta.inboxUIItemToConversationMeta(inboxUIItem) + if (meta) { + metas.push(meta) + } + const participantInfo: T.Chat.ParticipantInfo = uiParticipantsToParticipantInfo( + inboxUIItem.participants ?? [] + ) + if (participantInfo.all.length > 0) { + storeRegistry + .getConvoState(T.Chat.stringToConversationIDKey(inboxUIItem.convID)) + .dispatch.setParticipants(participantInfo) + } + inboxUIItem.participants?.forEach((part: T.RPCChat.UIParticipant) => { + const {assertion, fullName} = part + if (!infoMap.get(assertion) && fullName) { + added = true + usernameToFullname[assertion] = fullName + } + }) + }) + if (added) { + get().dispatch.defer.onUsersUpdates( + Object.keys(usernameToFullname).map(name => ({ + info: {fullname: usernameToFullname[name]}, + name, + })) + ) + } + if (metas.length > 0) { + get().dispatch.metasReceived(metas) + } + }, + onGetInboxUnverifiedConvs: (action: EngineGen.Chat1ChatUiChatInboxUnverifiedPayload) => { + const {inbox} = action.payload.params + const result = JSON.parse(inbox) as T.RPCChat.UnverifiedInboxUIItems + const items: ReadonlyArray = result.items ?? [] + // We get a subset of meta information from the cache even in the untrusted payload + const metas = items.reduce>((arr, item) => { + const m = Meta.unverifiedInboxUIItemToConversationMeta(item) + m && arr.push(m) + return arr + }, []) + get().dispatch.setTrustedInboxHasLoaded() + // Check if some of our existing stored metas might no longer be valid + get().dispatch.metasReceived(metas) + }, + onIncomingInboxUIItem: conv => { + if (!conv) return + const meta = Meta.inboxUIItemToConversationMeta(conv) + const usernameToFullname = (conv.participants ?? []).reduce<{[key: string]: string}>((map, part) => { + if (part.fullName) { + map[part.assertion] = part.fullName + } + return map + }, {}) + + get().dispatch.defer.onUsersUpdates( + Object.keys(usernameToFullname).map(name => ({ + info: {fullname: usernameToFullname[name]}, + name, + })) + ) + + if (meta) { + get().dispatch.metasReceived([meta]) + } + }, + onRouteChanged: (prev, next) => { + const maybeChangeChatSelection = () => { + const wasModal = prev && getModalStack(prev).length > 0 + const isModal = next && getModalStack(next).length > 0 + // ignore if changes involve a modal + if (wasModal || isModal) { + return + } + const p = getVisibleScreen(prev) + const n = getVisibleScreen(next) + const wasChat = p?.name === Common.threadRouteName + const isChat = n?.name === Common.threadRouteName + // nothing to do with chat + if (!wasChat && !isChat) { + return + } + const pParams = p?.params as undefined | {conversationIDKey?: T.Chat.ConversationIDKey} + const nParams = n?.params as undefined | {conversationIDKey?: T.Chat.ConversationIDKey} + const wasID = pParams?.conversationIDKey + const isID = nParams?.conversationIDKey + + logger.info('maybeChangeChatSelection ', {isChat, isID, wasChat, wasID}) + + // same? ignore + if (wasChat && isChat && wasID === isID) { + // if we've never loaded anything, keep going so we load it + if (!isID || storeRegistry.getConvoState(isID).loaded) { + return + } + } + + // deselect if there was one + const deselectAction = () => { + if (wasChat && wasID && T.Chat.isValidConversationIDKey(wasID)) { + get().dispatch.unboxRows([wasID], true) + // needed? + // storeRegistry.getConvoState(wasID).dispatch.clearOrangeLine('deselected') + } + } + + // still chatting? just select new one + if (wasChat && isChat && isID && T.Chat.isValidConversationIDKey(isID)) { + deselectAction() + storeRegistry.getConvoState(isID).dispatch.selectedConversation() + return + } + + // leaving a chat + if (wasChat && !isChat) { + deselectAction() + return + } + + // going into a chat + if (isChat && isID && T.Chat.isValidConversationIDKey(isID)) { + deselectAction() + storeRegistry.getConvoState(isID).dispatch.selectedConversation() + return + } + } + + const maybeChatTabSelected = () => { + if (getTab(prev) !== Tabs.chatTab && getTab(next) === Tabs.chatTab) { + const n = getVisibleScreen(next) + const nParams = n?.params as undefined | {conversationIDKey?: T.Chat.ConversationIDKey} + const isID = nParams?.conversationIDKey + isID && storeRegistry.getConvoState(isID).dispatch.tabSelected() + } + } + maybeChangeChatSelection() + maybeChatTabSelected() + }, + onTeamBuildingFinished: users => { + const f = async () => { + // need to let the mdoal hide first else its thrashy + await timeoutPromise(500) + storeRegistry + .getConvoState(T.Chat.pendingWaitingConversationIDKey) + .dispatch.navigateToThread('justCreated') + get().dispatch.createConversation([...users].map(u => u.id)) + } + ignorePromise(f()) + }, + paymentInfoReceived: paymentInfo => { + set(s => { + s.paymentStatusMap.set(paymentInfo.paymentID, paymentInfo) + }) + }, + previewConversation: p => { + // We always make adhoc convos and never preview it + const previewConversationPersonMakesAConversation = () => { + const {participants, teamname, highlightMessageID} = p + if (teamname) return + if (!participants) return + const toFind = [...participants].sort().join(',') + const toFindN = participants.length + for (const cs of chatStores.values()) { + const names = cs.getState().participants.name + if (names.length !== toFindN) continue + const p = [...names].sort().join(',') + if (p === toFind) { + storeRegistry + .getConvoState(cs.getState().id) + .dispatch.navigateToThread('justCreated', highlightMessageID) + return + } + } + + storeRegistry + .getConvoState(T.Chat.pendingWaitingConversationIDKey) + .dispatch.navigateToThread('justCreated') + get().dispatch.createConversation(participants, highlightMessageID) + } + + // We preview channels + const previewConversationTeam = async () => { + const {conversationIDKey, highlightMessageID, teamname, reason} = p + if (conversationIDKey) { + if ( + reason === 'messageLink' || + reason === 'teamMention' || + reason === 'channelHeader' || + reason === 'manageView' + ) { + // Add preview channel to inbox + await T.RPCChat.localPreviewConversationByIDLocalRpcPromise({ + convID: T.Chat.keyToConversationID(conversationIDKey), + }) + } + + storeRegistry + .getConvoState(conversationIDKey) + .dispatch.navigateToThread('previewResolved', highlightMessageID) + return + } + + if (!teamname) { + return + } + + const channelname = p.channelname || 'general' + try { + const results = await T.RPCChat.localFindConversationsLocalRpcPromise({ + identifyBehavior: T.RPCGen.TLFIdentifyBehavior.chatGui, + membersType: T.RPCChat.ConversationMembersType.team, + oneChatPerTLF: true, + tlfName: teamname, + topicName: channelname, + topicType: T.RPCChat.TopicType.chat, + visibility: T.RPCGen.TLFVisibility.private, + }) + const resultMetas = (results.uiConversations || []) + .map(row => Meta.inboxUIItemToConversationMeta(row)) + .filter(Boolean) + + const first = resultMetas[0] + if (!first) { + if (p.reason === 'appLink') { + navigateAppend({ + props: { + error: + "We couldn't find this team chat channel. Please check that you're a member of the team and the channel exists.", + }, + selected: 'keybaseLinkError', + }) + return + } else { + return + } + } + + const results2 = await T.RPCChat.localPreviewConversationByIDLocalRpcPromise({ + convID: T.Chat.keyToConversationID(first.conversationIDKey), + }) + const meta = Meta.inboxUIItemToConversationMeta(results2.conv) + if (meta) { + get().dispatch.metasReceived([meta]) + } + + storeRegistry + .getConvoState(first.conversationIDKey) + .dispatch.navigateToThread('previewResolved', highlightMessageID) + } catch (error) { + if ( + error instanceof RPCError && + error.code === T.RPCGen.StatusCode.scteamnotfound && + reason === 'appLink' + ) { + navigateAppend({ + props: { + error: + "We couldn't find this team. Please check that you're a member of the team and the channel exists.", + }, + selected: 'keybaseLinkError', + }) + return + } else { + throw error + } + } + } + previewConversationPersonMakesAConversation() + ignorePromise(previewConversationTeam()) + }, + queueMetaHandle: () => { + // Watch the meta queue and take up to 10 items. Choose the last items first since they're likely still visible + const f = async () => { + const maxToUnboxAtATime = 10 + const ar = [...metaQueue] + const maybeUnbox = ar.slice(0, maxToUnboxAtATime) + metaQueue = new Set(ar.slice(maxToUnboxAtATime)) + const conversationIDKeys = untrustedConversationIDKeys(maybeUnbox) + if (conversationIDKeys.length) { + get().dispatch.unboxRows(conversationIDKeys) + } + if (metaQueue.size && conversationIDKeys.length) { + await timeoutPromise(100) + } + if (metaQueue.size) { + get().dispatch.queueMetaHandle() + } + } + ignorePromise(f()) + }, + queueMetaToRequest: ids => { + let added = false as boolean + untrustedConversationIDKeys(ids).forEach(k => { + if (!metaQueue.has(k)) { + added = true + metaQueue.add(k) + } + }) + if (added) { + // only unboxMore if something changed + get().dispatch.queueMetaHandle() + } else { + logger.info('skipping meta queue run, queue unchanged') + } + }, + refreshBotPublicCommands: username => { + set(s => { + s.botPublicCommands.delete(username) + }) + const f = async () => { + let res: T.RPCChat.ListBotCommandsLocalRes | undefined + try { + res = await T.RPCChat.localListPublicBotCommandsLocalRpcPromise({ + username, + }) + } catch (error) { + if (error instanceof RPCError) { + logger.info('refreshBotPublicCommands: failed to get public commands: ' + error.message) + set(s => { + s.botPublicCommands.set(username, {commands: [], loadError: true}) + }) + } + } + const commands = (res?.commands ?? []).reduce>((l, c) => { + l.push(c.name) + return l + }, []) + + set(s => { + s.botPublicCommands.set(username, {commands, loadError: false}) + }) + } + ignorePromise(f()) + }, + resetConversationErrored: () => { + set(s => { + s.createConversationError = undefined + }) + }, + resetState: () => { + set(s => ({ + ...s, + ...initialStore, + dispatch: s.dispatch, + staticConfig: s.staticConfig, + })) + // also blow away convoState + clearChatStores() + }, + setInboxNumSmallRows: (rows, ignoreWrite) => { + set(s => { + if (rows > 0) { + s.inboxNumSmallRows = rows + } + }) + if (ignoreWrite) { + return + } + const {inboxNumSmallRows} = get() + if (inboxNumSmallRows === undefined || inboxNumSmallRows <= 0) { + return + } + const f = async () => { + try { + await T.RPCGen.configGuiSetValueRpcPromise({ + path: 'ui.inboxSmallRows', + value: {i: inboxNumSmallRows, isNull: false}, + }) + } catch {} + } + ignorePromise(f()) + }, + setInfoPanelTab: tab => { + set(s => { + s.infoPanelSelectedTab = tab + }) + }, + setMaybeMentionInfo: (name, info) => { + set(s => { + const {maybeMentionMap} = s + maybeMentionMap.set(name, T.castDraft(info)) + }) + }, + setTrustedInboxHasLoaded: () => { + set(s => { + s.trustedInboxHasLoaded = true + }) + }, + toggleInboxSearch: enabled => { + set(s => { + const {inboxSearch} = s + if (enabled && !inboxSearch) { + s.inboxSearch = T.castDraft(makeInboxSearchInfo()) + } else if (!enabled && inboxSearch) { + s.inboxSearch = undefined + } + }) + const f = async () => { + const {inboxSearch} = get() + if (!inboxSearch) { + await T.RPCChat.localCancelActiveInboxSearchRpcPromise() + return + } + if (inboxSearch.nameStatus === 'initial') { + get().dispatch.inboxSearch('') + } + } + ignorePromise(f()) + }, + toggleSmallTeamsExpanded: () => { + set(s => { + s.smallTeamsExpanded = !s.smallTeamsExpanded + }) + }, + unboxRows: (ids, force) => { + // We want to unbox rows that have scroll into view + const f = async () => { + if (!useConfigState.getState().loggedIn) { + return + } + + // Get valid keys that we aren't already loading or have loaded + const conversationIDKeys = ids.reduce((arr: Array, id) => { + if (id && T.Chat.isValidConversationIDKey(id)) { + const cs = storeRegistry.getConvoState(id) + const trustedState = cs.meta.trustedState + if (force || (trustedState !== 'requesting' && trustedState !== 'trusted')) { + arr.push(id) + cs.dispatch.updateMeta({trustedState: 'requesting'}) + } + } + return arr + }, []) + + if (!conversationIDKeys.length) { + return + } + logger.info( + `unboxRows: unboxing len: ${conversationIDKeys.length} convs: ${conversationIDKeys.join(',')}` + ) + try { + await T.RPCChat.localRequestInboxUnboxRpcPromise({ + convIDs: conversationIDKeys.map(k => T.Chat.keyToConversationID(k)), + }) + } catch (error) { + if (error instanceof RPCError) { + logger.info(`unboxRows: failed ${error.desc}`) + } + } + } + ignorePromise(f()) + }, + updateCoinFlipStatus: statuses => { + set(s => { + const {flipStatusMap} = s + statuses.forEach(status => { + flipStatusMap.set(status.gameID, T.castDraft(status)) + }) + }) + }, + updateInboxLayout: str => { + set(s => { + try { + const {inboxHasLoaded} = s + const _layout = JSON.parse(str) as unknown + if (!_layout || typeof _layout !== 'object') { + console.log('Invalid layout?') + return + } + const layout = _layout as T.RPCChat.UIInboxLayout + + if (!isEqual(s.inboxLayout, layout)) { + s.inboxLayout = T.castDraft(layout) + } + s.inboxHasLoaded = !!layout + if (!inboxHasLoaded) { + // on first layout, initialize any drafts and muted status + // After the first layout, any other updates will come in the form of meta updates. + layout.smallTeams?.forEach(t => { + const cs = storeRegistry.getConvoState(t.convID) + cs.dispatch.updateFromUIInboxLayout(t) + }) + layout.bigTeams?.forEach(t => { + if (t.state === T.RPCChat.UIInboxBigTeamRowTyp.channel) { + const cs = storeRegistry.getConvoState(t.channel.convID) + cs.dispatch.updateFromUIInboxLayout(t.channel) + } + }) + } + } catch (e) { + logger.info('failed to JSON parse inbox layout: ' + e) + } + }) + }, + updateInfoPanel: (show, tab) => { + set(s => { + s.infoPanelShowing = show + s.infoPanelSelectedTab = tab + }) + }, + updateLastCoord: coord => { + set(s => { + s.lastCoord = coord + }) + const f = async () => { + const {accuracy, lat, lon} = coord + await T.RPCChat.localLocationUpdateRpcPromise({coord: {accuracy, lat, lon}}) + } + ignorePromise(f()) + }, + updateUserReacjis: userReacjis => { + set(s => { + const {skinTone, topReacjis} = userReacjis + s.userReacjis.skinTone = skinTone + // filter out non-simple emojis + s.userReacjis.topReacjis = + T.castDraft(topReacjis)?.filter(r => /^:[^:]+:$/.test(r.name)) ?? defaultTopReacjis + }) + }, + updatedGregor: items => { + const explodingItems = items.filter(i => + i.item.category.startsWith(Common.explodingModeGregorKeyPrefix) + ) + if (!explodingItems.length) { + // No conversations have exploding modes, clear out what is set + for (const s of chatStores.values()) { + s.getState().dispatch.setExplodingMode(0, true) + } + } else { + // logger.info('Got push state with some exploding modes') + explodingItems.forEach(i => { + try { + const {category, body} = i.item + const secondsString = uint8ArrayToString(body) + const seconds = parseInt(secondsString, 10) + if (isNaN(seconds)) { + logger.warn(`Got dirty exploding mode ${secondsString} for category ${category}`) + return + } + const _conversationIDKey = category.substring(Common.explodingModeGregorKeyPrefix.length) + const conversationIDKey = T.Chat.stringToConversationIDKey(_conversationIDKey) + storeRegistry.getConvoState(conversationIDKey).dispatch.setExplodingMode(seconds, true) + } catch (e) { + logger.info('Error parsing exploding' + e) + } + }) + } + + set(s => { + const blockButtons = items.some(i => i.item.category.startsWith(blockButtonsGregorPrefix)) + if (blockButtons || s.blockButtonsMap.size > 0) { + const shouldKeepExistingBlockButtons = new Map() + s.blockButtonsMap.forEach((_, teamID: string) => shouldKeepExistingBlockButtons.set(teamID, false)) + items + .filter(i => i.item.category.startsWith(blockButtonsGregorPrefix)) + .forEach(i => { + try { + const teamID = i.item.category.substring(blockButtonsGregorPrefix.length) + if (!s.blockButtonsMap.get(teamID)) { + const body = bodyToJSON(i.item.body) as {adder: string} + const adder = body.adder + s.blockButtonsMap.set(teamID, {adder}) + } else { + shouldKeepExistingBlockButtons.set(teamID, true) + } + } catch (e) { + logger.info('block buttons parse fail', e) + } + }) + shouldKeepExistingBlockButtons.forEach((keep, teamID) => { + if (!keep) { + s.blockButtonsMap.delete(teamID) + } + }) + } + }) + }, + } + return { + ...initialStore, + dispatch, + getBackCount: conversationIDKey => { + let count = 0 + chatStores.forEach(s => { + const {id, badge} = s.getState() + // only show sum of badges that aren't for the current conversation + if (id !== conversationIDKey) { + count += badge + } + }) + return count + }, + getBadgeHiddenCount: ids => { + let badgeCount = 0 + let hiddenCount = 0 + + chatStores.forEach(s => { + const {id, badge} = s.getState() + if (ids.has(id)) { + badgeCount -= badge + hiddenCount -= 1 + } + }) + + return {badgeCount, hiddenCount} + }, + getUnreadIndicies: ids => { + const unreadIndices: Map = new Map() + ids.forEach((cur, idx) => { + Array.from(chatStores.values()).some(s => { + const {id, badge} = s.getState() + if (id === cur && badge > 0) { + unreadIndices.set(idx, badge) + return true + } + return false + }) + }) + return unreadIndices + }, + } +}) + +export function makeChatScreen>( + Component: COM, + options?: { + getOptions?: GetOptionsRet | ((props: ChatProviderProps>) => GetOptionsRet) + skipProvider?: boolean + canBeNullConvoID?: boolean + } +) { + return { + ...options, + screen: function Screen(p: ChatProviderProps>) { + const Comp = Component as any + return options?.skipProvider ? ( + + ) : ( + + + + ) + }, + } +} + +export * from '@/stores/convostate' +export * from '@/constants/chat2/common' +export * from '@/constants/chat2/meta' +export * from '@/constants/chat2/message' + +export { + noConversationIDKey, + pendingWaitingConversationIDKey, + pendingErrorConversationIDKey, + isValidConversationIDKey, + dummyConversationIDKey, +} from '@/constants/types/chat2/common' diff --git a/shared/constants/config/index.tsx b/shared/stores/config.tsx similarity index 67% rename from shared/constants/config/index.tsx rename to shared/stores/config.tsx index 5fb8ffbb8ea9..249d056e4de9 100644 --- a/shared/constants/config/index.tsx +++ b/shared/stores/config.tsx @@ -1,25 +1,24 @@ -import * as T from '../types' -import {ignorePromise, timeoutPromise} from '../utils' -import {serverConfigFileName} from '../platform' -import {waitingKeyConfigLogin} from '../strings' +import type * as NetInfo from '@react-native-community/netinfo' +import * as T from '@/constants/types' +import {ignorePromise, timeoutPromise} from '@/constants/utils' +import {waitingKeyConfigLogin} from '@/constants/strings' import * as EngineGen from '@/actions/engine-gen-gen' -import * as RemoteGen from '@/actions/remote-gen' import * as Stats from '@/engine/stats' import * as Z from '@/util/zustand' -import {noConversationIDKey} from '../types/chat2/common' +import {noConversationIDKey} from '@/constants/types/chat2/common' import isEqual from 'lodash/isEqual' import logger from '@/logger' -import type {Tab} from '../tabs' +import type {Tab} from '@/constants/tabs' import {RPCError, convertToError, isEOFError, isErrorTransient, niceError} from '@/util/errors' -import {defaultUseNativeFrame, isMobile} from '../platform' +import {defaultUseNativeFrame, isMobile} from '@/constants/platform' import {type CommonResponseHandler} from '@/engine/types' -import {invalidPasswordErrorString} from './util' -import {navigateAppend, switchTab} from '../router2/util' -import {storeRegistry} from '../store-registry' -import {getSelectedConversation} from '@/constants/chat2/common' +import {invalidPasswordErrorString} from '@/constants/config' +import {navigateAppend} from '@/constants/router2' + +export type ConnectionType = NetInfo.NetInfoStateType | 'notavailable' type Store = T.Immutable<{ - forceSmallNav: boolean + active: boolean allowAnimatedEmojis: boolean androidShare?: | {type: T.RPCGen.IncomingShareType.file; urls: Array} @@ -28,6 +27,7 @@ type Store = T.Immutable<{ badgeState?: T.RPCGen.BadgeState configuredAccounts: Array defaultUsername: string + forceSmallNav: boolean globalError?: Error | RPCError gregorReachable?: T.RPCGen.Reachable gregorPushState: Array<{md: T.RPCGregor.Metadata; item: T.RPCGregor.Item}> @@ -50,7 +50,7 @@ type Store = T.Immutable<{ | 'reloggedIn' | 'startupOrReloginButNotInARush' mobileAppState: 'active' | 'background' | 'inactive' | 'unknown' - networkStatus?: {online: boolean; type: T.Config.ConnectionType; isInit?: boolean} + networkStatus?: {online: boolean; type: ConnectionType; isInit?: boolean} notifySound: boolean openAtLogin: boolean outOfDate: T.Config.OutOfDate @@ -86,6 +86,7 @@ type Store = T.Immutable<{ }> const initialStore: Store = { + active: true, allowAnimatedEmojis: true, androidShare: undefined, appFocused: true, @@ -146,7 +147,7 @@ const initialStore: Store = { export interface State extends Store { dispatch: { - dynamic: { + defer: { copyToClipboard: (s: string) => void dumpLogsNative?: (reason: string) => Promise onFilePickerError?: (error: Error) => void @@ -163,7 +164,6 @@ export interface State extends Store { changedFocus: (f: boolean) => void checkForUpdate: () => void dumpLogs: (reason: string) => Promise - eventFromRemoteWindows: (action: RemoteGen.Actions) => void filePickerError: (error: Error) => void initAppUpdateLoop: () => void initNotifySound: () => void @@ -177,16 +177,17 @@ export interface State extends Store { setLoginError: (error?: RPCError) => void logoutAndTryToLogInAs: (username: string) => void onEngineConnected: () => void - onEngineDisonnected: () => void onEngineIncoming: (action: EngineGen.Actions) => void - osNetworkStatusChanged: (online: boolean, type: T.Config.ConnectionType, isInit?: boolean) => void + osNetworkStatusChanged: (online: boolean, type: ConnectionType, isInit?: boolean) => void openUnlockFolders: (devices: ReadonlyArray) => void powerMonitorEvent: (event: string) => void resetState: (isDebug?: boolean) => void remoteWindowNeedsProps: (component: string, params: string) => void resetRevokedSelf: () => void - revoke: (deviceName: string) => void + revoke: (deviceName: string, wasCurrentDevice: boolean) => void + refreshAccounts: () => Promise setAccounts: (a: Store['configuredAccounts']) => void + setActive: (a: boolean) => void setAndroidShare: (s: Store['androidShare']) => void setBadgeState: (b: State['badgeState']) => void setDefaultUsername: (u: string) => void @@ -201,8 +202,9 @@ export interface State extends Store { setStartupDetails: (st: Omit) => void setOpenAtLogin: (open: boolean) => void setOutOfDate: (outOfDate: T.Config.OutOfDate) => void - setUserSwitching: (sw: boolean) => void + setUpdating: () => void setUseNativeFrame: (use: boolean) => void + setUserSwitching: (sw: boolean) => void showMain: () => void toggleRuntimeStats: () => void updateGregorCategory: (category: string, body: string, dtime?: {offset: number; time: number}) => void @@ -245,15 +247,6 @@ export const useConfigState = Z.createZustand((set, get) => { set(s => { s.gregorReachable = r }) - // Re-get info about our account if you log in/we're done handshaking/became reachable - if (r === T.RPCGen.Reachable.yes) { - // not in waiting state - if (storeRegistry.getState('daemon').handshakeWaiters.size === 0) { - ignorePromise(storeRegistry.getState('daemon').dispatch.loadDaemonBootstrapStatus()) - } - } - - storeRegistry.getState('teams').dispatch.eagerLoadTeams() } const setGregorPushState = (state: T.RPCGen.Gregor1.State) => { @@ -276,29 +269,6 @@ export const useConfigState = Z.createZustand((set, get) => { set(s => { s.allowAnimatedEmojis = allowAnimatedEmojis }) - - const lastSeenItem = goodState.find(i => i.item.category === 'whatsNewLastSeenVersion') - storeRegistry.getState('whats-new').dispatch.updateLastSeen(lastSeenItem) - } - - const updateApp = () => { - const f = async () => { - await T.RPCGen.configStartUpdateIfNeededRpcPromise() - } - ignorePromise(f()) - // * If user choose to update: - // We'd get killed and it doesn't matter what happens here. - // * If user hits "Ignore": - // Note that we ignore the snooze here, so the state shouldn't change, - // and we'd back to where we think we still need an update. So we could - // have just unset the "updating" flag.However, in case server has - // decided to pull out the update between last time we asked the updater - // and now, we'd be in a wrong state if we didn't check with the service. - // Since user has interacted with it, we still ask the service to make - // sure. - set(s => { - s.outOfDate.updating = true - }) } const updateRuntimeStats = (stats?: T.RPCGen.RuntimeStats) => { @@ -320,13 +290,6 @@ export const useConfigState = Z.createZustand((set, get) => { set(s => { s.appFocused = f }) - - if (!isMobile || !f) { - return - } - const {dispatch} = storeRegistry.getConvoState(getSelectedConversation()) - dispatch.loadMoreMessages({reason: 'foregrounding'}) - dispatch.markThreadAsRead() }, checkForUpdate: () => { const f = async () => { @@ -334,10 +297,7 @@ export const useConfigState = Z.createZustand((set, get) => { } ignorePromise(f()) }, - dumpLogs: async reason => { - await get().dispatch.dynamic.dumpLogsNative?.(reason) - }, - dynamic: { + defer: { copyToClipboard: () => { throw new Error('copyToClipboard not implemented?????') }, @@ -353,156 +313,11 @@ export const useConfigState = Z.createZustand((set, get) => { showMainNative: undefined, showShareActionSheet: undefined, }, - eventFromRemoteWindows: (action: RemoteGen.Actions) => { - switch (action.type) { - case RemoteGen.resetStore: - break - case RemoteGen.openChatFromWidget: { - get().dispatch.showMain() - storeRegistry - .getConvoState(action.payload.conversationIDKey) - .dispatch.navigateToThread('inboxSmall') - break - } - case RemoteGen.inboxRefresh: { - storeRegistry.getState('chat').dispatch.inboxRefresh('widgetRefresh') - break - } - case RemoteGen.engineConnection: { - if (action.payload.connected) { - storeRegistry.getState('engine').dispatch.onEngineConnected() - } else { - storeRegistry.getState('engine').dispatch.onEngineDisconnected() - } - break - } - case RemoteGen.switchTab: { - switchTab(action.payload.tab) - break - } - case RemoteGen.setCriticalUpdate: { - storeRegistry.getState('fs').dispatch.setCriticalUpdate(action.payload.critical) - break - } - case RemoteGen.userFileEditsLoad: { - storeRegistry.getState('fs').dispatch.userFileEditsLoad() - break - } - case RemoteGen.openFilesFromWidget: { - storeRegistry.getState('fs').dispatch.dynamic.openFilesFromWidgetDesktop?.(action.payload.path) - break - } - case RemoteGen.saltpackFileOpen: { - storeRegistry.getState('deeplinks').dispatch.handleSaltPackOpen(action.payload.path) - break - } - case RemoteGen.pinentryOnCancel: { - storeRegistry.getState('pinentry').dispatch.dynamic.onCancel?.() - break - } - case RemoteGen.pinentryOnSubmit: { - storeRegistry.getState('pinentry').dispatch.dynamic.onSubmit?.(action.payload.password) - break - } - case RemoteGen.openPathInSystemFileManager: { - storeRegistry - .getState('fs') - .dispatch.dynamic.openPathInSystemFileManagerDesktop?.(action.payload.path) - break - } - case RemoteGen.unlockFoldersSubmitPaperKey: { - T.RPCGen.loginPaperKeySubmitRpcPromise( - {paperPhrase: action.payload.paperKey}, - 'unlock-folders:waiting' - ) - .then(() => { - get().dispatch.openUnlockFolders([]) - }) - .catch((e: unknown) => { - if (!(e instanceof RPCError)) return - set(s => { - s.unlockFoldersError = e.desc - }) - }) - break - } - case RemoteGen.closeUnlockFolders: { - T.RPCGen.rekeyRekeyStatusFinishRpcPromise() - .then(() => {}) - .catch(() => {}) - get().dispatch.openUnlockFolders([]) - break - } - case RemoteGen.stop: { - storeRegistry.getState('settings').dispatch.stop(action.payload.exitCode) - break - } - case RemoteGen.trackerChangeFollow: { - storeRegistry - .getState('tracker2') - .dispatch.changeFollow(action.payload.guiID, action.payload.follow) - break - } - case RemoteGen.trackerIgnore: { - storeRegistry.getState('tracker2').dispatch.ignore(action.payload.guiID) - break - } - case RemoteGen.trackerCloseTracker: { - storeRegistry.getState('tracker2').dispatch.closeTracker(action.payload.guiID) - break - } - case RemoteGen.trackerLoad: { - storeRegistry.getState('tracker2').dispatch.load(action.payload) - break - } - case RemoteGen.link: - { - const {link} = action.payload - storeRegistry.getState('deeplinks').dispatch.handleAppLink(link) - } - break - case RemoteGen.installerRan: - get().dispatch.installerRan() - break - case RemoteGen.updateNow: - updateApp() - break - case RemoteGen.powerMonitorEvent: - get().dispatch.powerMonitorEvent(action.payload.event) - break - case RemoteGen.showMain: - get().dispatch.showMain() - break - case RemoteGen.dumpLogs: - ignorePromise(get().dispatch.dumpLogs(action.payload.reason)) - break - case RemoteGen.remoteWindowWantsProps: - get().dispatch.remoteWindowNeedsProps(action.payload.component, action.payload.param) - break - case RemoteGen.updateWindowMaxState: - set(s => { - s.windowState.isMaximized = action.payload.max - }) - break - case RemoteGen.updateWindowState: - get().dispatch.updateWindowState(action.payload.windowState) - break - case RemoteGen.updateWindowShown: { - const win = action.payload.component - set(s => { - s.windowShownCount.set(win, (s.windowShownCount.get(win) ?? 0) + 1) - }) - break - } - case RemoteGen.previewConversation: - storeRegistry - .getState('chat') - .dispatch.previewConversation({participants: [action.payload.participant], reason: 'tracker'}) - break - } + dumpLogs: async reason => { + await get().dispatch.defer.dumpLogsNative?.(reason) }, filePickerError: error => { - get().dispatch.dynamic.onFilePickerError?.(error) + get().dispatch.defer.onFilePickerError?.(error) }, initAppUpdateLoop: () => { const f = async () => { @@ -571,8 +386,6 @@ export const useConfigState = Z.createZustand((set, get) => { set(s => { s.installerRanCount++ }) - - storeRegistry.getState('fs').dispatch.checkKbfsDaemonRpcStatus() }, loadIsOnline: () => { const f = async () => { @@ -592,59 +405,6 @@ export const useConfigState = Z.createZustand((set, get) => { set(s => { s.loadOnStartPhase = phase }) - - if (phase === 'startupOrReloginButNotInARush') { - const getFollowerInfo = () => { - const {uid} = storeRegistry.getState('current-user') - logger.info(`getFollowerInfo: init; uid=${uid}`) - if (uid) { - // request follower info in the background - T.RPCGen.configRequestFollowingAndUnverifiedFollowersRpcPromise() - .then(() => {}) - .catch(() => {}) - } - } - - const updateServerConfig = async () => { - if (get().loggedIn) { - try { - await T.RPCGen.configUpdateLastLoggedInAndServerConfigRpcPromise({ - serverConfigPath: serverConfigFileName, - }) - } catch {} - } - } - - const updateTeams = () => { - storeRegistry.getState('teams').dispatch.getTeams() - storeRegistry.getState('teams').dispatch.refreshTeamRoleMap() - } - - const updateSettings = () => { - storeRegistry.getState('settings-contacts').dispatch.loadContactImportEnabled() - } - - const updateChat = async () => { - // On login lets load the untrusted inbox. This helps make some flows easier - if (storeRegistry.getState('current-user').username) { - const {inboxRefresh} = storeRegistry.getState('chat').dispatch - inboxRefresh('bootstrap') - } - try { - const rows = await T.RPCGen.configGuiGetValueRpcPromise({path: 'ui.inboxSmallRows'}) - const ri = rows.i ?? -1 - if (ri > 0) { - storeRegistry.getState('chat').dispatch.setInboxNumSmallRows(ri, true) - } - } catch {} - } - - getFollowerInfo() - ignorePromise(updateServerConfig()) - updateTeams() - updateSettings() - ignorePromise(updateChat()) - } }, login: (username, passphrase) => { const cancelDesc = 'Canceling RPC' @@ -661,7 +421,7 @@ export const useConfigState = Z.createZustand((set, get) => { 'keybase.1.provisionUi.DisplayAndPromptSecret': cancelOnCallback, 'keybase.1.provisionUi.PromptNewDeviceName': (_, response) => { cancelOnCallback(undefined, response) - storeRegistry.getState('provision').dispatch.dynamic.setUsername?.(username) + navigateAppend({props: {username}, selected: 'username'}) }, 'keybase.1.provisionUi.chooseDevice': cancelOnCallback, 'keybase.1.provisionUi.chooseGPGMethod': cancelOnCallback, @@ -729,8 +489,6 @@ export const useConfigState = Z.createZustand((set, get) => { ignorePromise(f()) }, onEngineConnected: () => { - storeRegistry.getState('daemon').dispatch.startHandshake() - // The startReachability RPC call both starts and returns the current // reachability state. Then we'll get updates of changes from this state via reachabilityChanged. // This should be run on app start and service re-connect in case the service somehow crashed or was restarted manually. @@ -755,16 +513,9 @@ export const useConfigState = Z.createZustand((set, get) => { } ignorePromise(registerForGregorNotifications()) - get().dispatch.dynamic.onEngineConnectedDesktop?.() + get().dispatch.defer.onEngineConnectedDesktop?.() get().dispatch.loadOnStart('initialStartupAsEarlyAsPossible') }, - onEngineDisonnected: () => { - const f = async () => { - await logger.dump() - } - ignorePromise(f()) - storeRegistry.getState('daemon').dispatch.setError(new Error('Disconnected')) - }, onEngineIncoming: action => { switch (action.type) { case EngineGen.keybase1GregorUIPushState: { @@ -798,34 +549,6 @@ export const useConfigState = Z.createZustand((set, get) => { } break } - case EngineGen.keybase1NotifyTeamAvatarUpdated: { - const {name} = action.payload.params - storeRegistry.getState('avatar').dispatch.updated(name) - break - } - case EngineGen.keybase1NotifyTrackingTrackingChanged: { - const {isTracking, username} = action.payload.params - storeRegistry.getState('followers').dispatch.updateFollowing(username, isTracking) - break - } - case EngineGen.keybase1NotifyTrackingTrackingInfo: { - const {uid, followers: _newFollowers, followees: _newFollowing} = action.payload.params - if (storeRegistry.getState('current-user').uid !== uid) { - return - } - const newFollowers = new Set(_newFollowers) - const newFollowing = new Set(_newFollowing) - const { - following: oldFollowing, - followers: oldFollowers, - dispatch, - } = storeRegistry.getState('followers') - const following = isEqual(newFollowing, oldFollowing) ? oldFollowing : newFollowing - const followers = isEqual(newFollowers, oldFollowers) ? oldFollowers : newFollowers - dispatch.replace(followers, following) - break - } - case EngineGen.keybase1ReachabilityReachabilityChanged: if (get().loggedIn) { setGregorReachable(action.payload.params.reachability.reachable) @@ -843,7 +566,7 @@ export const useConfigState = Z.createZustand((set, get) => { })) }) }, - osNetworkStatusChanged: (online: boolean, type: T.Config.ConnectionType, isInit?: boolean) => { + osNetworkStatusChanged: (online: boolean, type: ConnectionType, isInit?: boolean) => { const old = get().networkStatus set(s => { if (!s.networkStatus) { @@ -881,6 +604,30 @@ export const useConfigState = Z.createZustand((set, get) => { } ignorePromise(f()) }, + refreshAccounts: async () => { + const defaultUsername = get().defaultUsername + const configuredAccounts = (await T.RPCGen.loginGetConfiguredAccountsRpcPromise()) ?? [] + const {setAccounts, setDefaultUsername} = get().dispatch + + let existingDefaultFound = false as boolean + let currentName = '' + const nextConfiguredAccounts: Array = [] + + configuredAccounts.forEach(account => { + const {username, isCurrent, fullname, hasStoredSecret} = account + if (username === defaultUsername) { + existingDefaultFound = true + } + if (isCurrent) { + currentName = account.username + } + nextConfiguredAccounts.push({fullname, hasStoredSecret, username}) + }) + if (!existingDefaultFound) { + setDefaultUsername(currentName) + } + setAccounts(nextConfiguredAccounts) + }, remoteWindowNeedsProps: (component, params) => { set(s => { const map = s.remoteWindowNeedsProps.get(component) ?? new Map() @@ -909,8 +656,7 @@ export const useConfigState = Z.createZustand((set, get) => { userSwitching: s.userSwitching, })) }, - revoke: name => { - const wasCurrentDevice = storeRegistry.getState('current-user').deviceName === name + revoke: (name, wasCurrentDevice) => { if (wasCurrentDevice) { const {configuredAccounts, defaultUsername} = get() const acc = configuredAccounts.find(n => n.username !== defaultUsername) @@ -920,7 +666,6 @@ export const useConfigState = Z.createZustand((set, get) => { s.justRevokedSelf = name s.revokedTrigger++ }) - storeRegistry.getState('daemon').dispatch.loadDaemonAccounts() } }, setAccounts: a => { @@ -930,6 +675,11 @@ export const useConfigState = Z.createZustand((set, get) => { } }) }, + setActive: a => { + set(s => { + s.active = a + }) + }, setAndroidShare: share => { set(s => { s.androidShare = T.castDraft(share) @@ -1015,11 +765,6 @@ export const useConfigState = Z.createZustand((set, get) => { if (!changed) return - if (loggedIn) { - ignorePromise(storeRegistry.getState('daemon').dispatch.loadDaemonBootstrapStatus()) - } - storeRegistry.getState('daemon').dispatch.loadDaemonAccounts() - const {loadOnStart} = get().dispatch if (loggedIn) { if (!causedByStartup) { @@ -1035,14 +780,6 @@ export const useConfigState = Z.createZustand((set, get) => { } else { Z.resetAllStores() } - - if (loggedIn) { - storeRegistry.getState('fs').dispatch.checkKbfsDaemonRpcStatus() - } - - if (!causedByStartup) { - ignorePromise(storeRegistry.getState('daemon').dispatch.refreshAccounts()) - } }, setLoginError: error => { set(s => { @@ -1057,9 +794,6 @@ export const useConfigState = Z.createZustand((set, get) => { set(s => { s.mobileAppState = nextAppState }) - if (nextAppState === 'background' && storeRegistry.getState('chat').inboxSearch) { - storeRegistry.getState('chat').dispatch.toggleInboxSearch(false) - } }, setNotifySound: n => { set(s => { @@ -1099,6 +833,11 @@ export const useConfigState = Z.createZustand((set, get) => { } }) }, + setUpdating: () => { + set(s => { + s.outOfDate.updating = true + }) + }, setUseNativeFrame: use => { set(s => { s.useNativeFrame = use @@ -1119,7 +858,7 @@ export const useConfigState = Z.createZustand((set, get) => { }) }, showMain: () => { - get().dispatch.dynamic.showMainNative?.() + get().dispatch.defer.showMainNative?.() }, toggleRuntimeStats: () => { const f = async () => { diff --git a/shared/constants/chat2/convostate.tsx b/shared/stores/convostate.tsx similarity index 94% rename from shared/constants/chat2/convostate.tsx rename to shared/stores/convostate.tsx index aa73c3798337..372a1e9cb9f1 100644 --- a/shared/constants/chat2/convostate.tsx +++ b/shared/stores/convostate.tsx @@ -1,7 +1,7 @@ // TODO remove useChatNavigateAppend // TODO remove -import * as TeamsUtil from '../teams/util' -import * as PlatformSpecific from '../platform-specific' +import * as TeamsUtil from '@/constants/teams' +import * as PlatformSpecific from '@/util/platform-specific' import { clearModals, navigateAppend, @@ -11,19 +11,19 @@ import { getVisibleScreen, getModalStack, navToThread, -} from '../router2/util' -import {isIOS} from '../platform' -import {updateImmer} from '../utils' -import * as T from '../types' +} from '@/constants/router2' +import {isIOS} from '@/constants/platform' +import {updateImmer} from '@/constants/utils' +import * as T from '@/constants/types' import * as Styles from '@/styles' -import * as Common from './common' -import * as Tabs from '../tabs' +import * as Common from '@/constants/chat2/common' +import * as Tabs from '@/constants/tabs' import * as EngineGen from '@/actions/engine-gen-gen' -import * as Message from './message' -import * as Meta from './meta' +import * as Message from '@/constants/chat2/message' +import * as Meta from '@/constants/chat2/meta' import * as React from 'react' import * as Z from '@/util/zustand' -import {makeActionForOpenPathInFilesTab} from '@/constants/fs/util' +import {navToPath} from '@/constants/fs' import HiddenString from '@/util/hidden-string' import isEqual from 'lodash/isEqual' import logger from '@/logger' @@ -32,21 +32,23 @@ import type {DebouncedFunc} from 'lodash' import {RPCError} from '@/util/errors' import {findLast} from '@/util/arrays' import {mapGetEnsureValue} from '@/util/map' -import {noConversationIDKey} from '../types/chat2/common' +import {noConversationIDKey} from '@/constants/types/chat2/common' import {type StoreApi, type UseBoundStore, useStore} from 'zustand' -import * as Platform from '../platform' +import * as Platform from '@/constants/platform' import KB2 from '@/util/electron' import NotifyPopup from '@/util/notify-popup' import {hexToUint8Array} from 'uint8array-extras' import assign from 'lodash/assign' import {clearChatTimeCache} from '@/util/timestamp' import {registerDebugClear} from '@/util/debug' -import * as Config from '@/constants/config/util' +import * as Config from '@/constants/config' import {isMobile} from '@/constants/platform' -import {enumKeys, ignorePromise, shallowEqual} from '../utils' +import {enumKeys, ignorePromise, shallowEqual} from '@/constants/utils' import * as Strings from '@/constants/strings' -import {storeRegistry} from '../store-registry' +import {useConfigState} from '@/stores/config' +import {useCurrentUserState} from '@/stores/current-user' +import type {useChatState, RefreshReason} from '@/stores/chat2' const {darwinCopyToChatTempUploadFile} = KB2.functions @@ -223,6 +225,25 @@ export interface ConvoState extends ConvoStore { botCommandsUpdateStatus: (b: T.RPCChat.UIBotCommandsUpdateStatus) => void channelSuggestionsTriggered: () => void clearAttachmentView: () => void + defer: { + chatBlockButtonsMapHas: (teamID: T.RPCGen.TeamID) => boolean + chatInboxLayoutSmallTeamsFirstConvID: () => T.Chat.ConversationIDKey | undefined + chatInboxRefresh: (reason: RefreshReason) => void + chatMetasReceived: (metas: ReadonlyArray) => void + chatNavigateToInbox: () => void + chatPaymentInfoReceived: (messageID: T.Chat.MessageID, paymentInfo: T.Chat.ChatPaymentInfo) => void + chatPreviewConversation: ( + p: Parameters['dispatch']['previewConversation']>[0] + ) => void + chatResetConversationErrored: () => void + chatUnboxRows: (convIDs: ReadonlyArray, force: boolean) => void + chatUpdateInfoPanel: ( + show: boolean, + tab: 'settings' | 'members' | 'attachments' | 'bots' | undefined + ) => void + teamsGetMembers: (teamID: T.RPCGen.TeamID) => void + usersGetBio: (username: string) => void + } dismissBottomBanner: () => void dismissBlockButtons: (teamID: T.RPCGen.TeamID) => void dismissJourneycard: (cardType: T.RPCChat.JourneycardType, ordinal: T.Chat.Ordinal) => void @@ -296,7 +317,6 @@ export interface ConvoState extends ConvoStore { setReplyTo: (o: T.Chat.Ordinal) => void setThreadSearchQuery: (query: string) => void setTyping: DebouncedFunc<(t: Set) => void> - setupSubscriptions: () => void showInfoPanel: (show: boolean, tab: 'settings' | 'members' | 'attachments' | 'bots' | undefined) => void tabSelected: () => void threadSearch: (query: string) => void @@ -372,11 +392,68 @@ type ScrollDirection = 'none' | 'back' | 'forward' export const numMessagesOnInitialLoad = isMobile ? 20 : 100 export const numMessagesOnScrollback = isMobile ? 100 : 100 -const createSlice: Z.ImmerStateCreator = (set, get) => { +const stubDefer: ConvoState['dispatch']['defer'] = { + chatBlockButtonsMapHas: () => { + throw new Error('convostate defer not initialized') + }, + chatInboxLayoutSmallTeamsFirstConvID: () => { + throw new Error('convostate defer not initialized') + }, + chatInboxRefresh: () => { + throw new Error('convostate defer not initialized') + }, + chatMetasReceived: () => { + throw new Error('convostate defer not initialized') + }, + chatNavigateToInbox: () => { + throw new Error('convostate defer not initialized') + }, + chatPaymentInfoReceived: () => { + throw new Error('convostate defer not initialized') + }, + chatPreviewConversation: () => { + throw new Error('convostate defer not initialized') + }, + chatResetConversationErrored: () => { + throw new Error('convostate defer not initialized') + }, + chatUnboxRows: () => { + throw new Error('convostate defer not initialized') + }, + chatUpdateInfoPanel: () => { + throw new Error('convostate defer not initialized') + }, + teamsGetMembers: () => { + throw new Error('convostate defer not initialized') + }, + usersGetBio: () => { + throw new Error('convostate defer not initialized') + }, +} + +let convoDeferImpl: ConvoState['dispatch']['defer'] | undefined + +export const setConvoDefer = (impl: ConvoState['dispatch']['defer']) => { + convoDeferImpl = impl + for (const store of chatStores.values()) { + const s = store.getState() + store.setState({ + ...s, + dispatch: { + ...s.dispatch, + defer: impl, + }, + }) + } +} + +const createSlice = (): Z.ImmerStateCreator => (set, get) => { + const defer = convoDeferImpl ?? stubDefer + const closeBotModal = () => { clearModals() if (get().meta.teamname) { - storeRegistry.getState('teams').dispatch.getMembers(get().meta.teamID) + get().dispatch.defer.teamsGetMembers(get().meta.teamID) } } @@ -493,13 +570,13 @@ const createSlice: Z.ImmerStateCreator = (set, get) => { } const onClick = () => { - storeRegistry.getState('config').dispatch.showMain() - storeRegistry.getState('chat').dispatch.navigateToInbox() + useConfigState.getState().dispatch.showMain() + get().dispatch.defer.chatNavigateToInbox() get().dispatch.navigateToThread('desktopNotification') } const onClose = () => {} logger.info('invoking NotifyPopup for chat notification') - const sound = storeRegistry.getState('config').notifySound + const sound = useConfigState.getState().notifySound const cleanBody = body.replaceAll(/!>(.*?) = (set, get) => { logger.error(errMsg) throw new Error(errMsg) } - storeRegistry.getState('chat').dispatch.paymentInfoReceived(paymentInfo) + get().dispatch.defer.chatPaymentInfoReceived(T.Chat.numberToMessageID(msgID), paymentInfo) getConvoState(conversationIDKey).dispatch.paymentInfoReceived(msgID, paymentInfo) } @@ -648,7 +725,7 @@ const createSlice: Z.ImmerStateCreator = (set, get) => { const refreshMutualTeamsInConv = () => { const f = async () => { const {id: conversationIDKey} = get() - const username = storeRegistry.getState('current-user').username + const username = useCurrentUserState.getState().username const otherParticipants = Meta.getRowParticipants(get().participants, username || '') const results = await T.RPCChat.localGetMutualTeamsLocalRpcPromise( {usernames: otherParticipants}, @@ -834,7 +911,7 @@ const createSlice: Z.ImmerStateCreator = (set, get) => { } const onInboxFailed = (convID: Uint8Array, error: T.RPCChat.InboxUIItemError) => { - const username = storeRegistry.getState('current-user').username + const username = useCurrentUserState.getState().username const conversationIDKey = T.Chat.conversationIDToKey(convID) switch (error.typ) { case T.RPCChat.ConversationErrorType.transient: @@ -1069,7 +1146,7 @@ const createSlice: Z.ImmerStateCreator = (set, get) => { } // If there are block buttons on this conversation, clear them. - if (storeRegistry.getState('chat').blockButtonsMap.has(meta.teamID)) { + if (get().dispatch.defer.chatBlockButtonsMapHas(meta.teamID)) { get().dispatch.dismissBlockButtons(meta.teamID) } @@ -1237,8 +1314,8 @@ const createSlice: Z.ImmerStateCreator = (set, get) => { }, blockConversation: reportUser => { const f = async () => { - storeRegistry.getState('chat').dispatch.navigateToInbox() - storeRegistry.getState('config').dispatch.dynamic.persistRoute?.(false, false) + get().dispatch.defer.chatNavigateToInbox() + useConfigState.getState().dispatch.defer.persistRoute?.(false, false) await T.RPCChat.localSetConversationStatusLocalRpcPromise({ conversationID: get().getConvID(), identifyBehavior: T.RPCGen.TLFIdentifyBehavior.chatGui, @@ -1270,6 +1347,7 @@ const createSlice: Z.ImmerStateCreator = (set, get) => { s.attachmentViewMap = new Map() }) }, + defer, dismissBlockButtons: teamID => { const f = async () => { try { @@ -1341,7 +1419,7 @@ const createSlice: Z.ImmerStateCreator = (set, get) => { // Nav to inbox but don't use findNewConversation since changeSelectedConversation // does that with better information. It knows the conversation is hidden even before // that state bounces back. - storeRegistry.getState('chat').dispatch.navigateToInbox() + get().dispatch.defer.chatNavigateToInbox() get().dispatch.showInfoPanel(false, undefined) } @@ -1394,7 +1472,7 @@ const createSlice: Z.ImmerStateCreator = (set, get) => { const params = vs?.params as undefined | {conversationIDKey?: T.Chat.ConversationIDKey} if (params?.conversationIDKey === get().id) { // select a convo - const next = storeRegistry.getState('chat').inboxLayout?.smallTeams?.[0]?.convID + const next = get().dispatch.defer.chatInboxLayoutSmallTeamsFirstConvID() if (next) { getConvoState(next).dispatch.navigateToThread('findNewestConversationFromLayout') } @@ -1419,8 +1497,8 @@ const createSlice: Z.ImmerStateCreator = (set, get) => { hit: T.RPCChat.MessageTypes['chat.1.chatUi.chatLoadGalleryHit']['inParam'] ) => { const getLastOrdinal = () => get().messageOrdinals?.at(-1) ?? T.Chat.numberToOrdinal(0) - const username = storeRegistry.getState('current-user').username - const devicename = storeRegistry.getState('current-user').deviceName + const username = useCurrentUserState.getState().username + const devicename = useCurrentUserState.getState().deviceName const m = Message.uiMessageToMessage( conversationIDKey, hit.message, @@ -1564,8 +1642,8 @@ const createSlice: Z.ImmerStateCreator = (set, get) => { s.loaded = true }) - const username = storeRegistry.getState('current-user').username - const devicename = storeRegistry.getState('current-user').deviceName + const username = useCurrentUserState.getState().username + const devicename = useCurrentUserState.getState().deviceName const getLastOrdinal = () => get().messageOrdinals?.at(-1) ?? T.Chat.numberToOrdinal(0) const uiMessages = JSON.parse(thread) as T.RPCChat.UIMessages @@ -1659,9 +1737,8 @@ const createSlice: Z.ImmerStateCreator = (set, get) => { logger.warn(`loadMoreMessages: error: ${error.desc}`) // no longer in team if (error.code === T.RPCGen.StatusCode.scchatnotinteam) { - const {inboxRefresh, navigateToInbox} = storeRegistry.getState('chat').dispatch - inboxRefresh('maybeKickedFromTeam') - navigateToInbox() + get().dispatch.defer.chatInboxRefresh('maybeKickedFromTeam') + get().dispatch.defer.chatNavigateToInbox() } if (error.code !== T.RPCGen.StatusCode.scteamreaderror) { // scteamreaderror = user is not in team. they'll see the rekey screen so don't throw for that @@ -1703,8 +1780,8 @@ const createSlice: Z.ImmerStateCreator = (set, get) => { }) if (result.message) { - const devicename = storeRegistry.getState('current-user').deviceName - const username = storeRegistry.getState('current-user').username + const devicename = useCurrentUserState.getState().deviceName + const username = useCurrentUserState.getState().username const getLastOrdinal = () => get().messageOrdinals?.at(-1) ?? T.Chat.numberToOrdinal(0) const goodMessage = Message.uiMessageToMessage( get().id, @@ -1753,7 +1830,7 @@ const createSlice: Z.ImmerStateCreator = (set, get) => { }, markTeamAsRead: teamID => { const f = async () => { - if (!storeRegistry.getState('config').loggedIn) { + if (!useConfigState.getState().loggedIn) { logger.info('bail on not logged in') return } @@ -1764,7 +1841,7 @@ const createSlice: Z.ImmerStateCreator = (set, get) => { }, markThreadAsRead: force => { const f = async () => { - if (!storeRegistry.getState('config').loggedIn) { + if (!useConfigState.getState().loggedIn) { logger.info('mark read bail on not logged in') return } @@ -1957,7 +2034,7 @@ const createSlice: Z.ImmerStateCreator = (set, get) => { logger.warn("messageReplyPrivately: can't find message to reply to", ordinal) return } - const username = storeRegistry.getState('current-user').username + const username = useCurrentUserState.getState().username if (!username) { throw new Error('messageReplyPrivately: making a convo while logged out?') } @@ -1988,7 +2065,7 @@ const createSlice: Z.ImmerStateCreator = (set, get) => { const text = formatTextForQuoting(message.text.stringValue()) getConvoState(newThreadCID).dispatch.injectIntoInput(text) - storeRegistry.getState('chat').dispatch.metasReceived([meta]) + get().dispatch.defer.chatMetasReceived([meta]) getConvoState(newThreadCID).dispatch.navigateToThread('createdMessagePrivately') } ignorePromise(f()) @@ -2133,7 +2210,7 @@ const createSlice: Z.ImmerStateCreator = (set, get) => { loadMessages() // load meta - storeRegistry.getState('chat').dispatch.unboxRows([get().id], true) + get().dispatch.defer.chatUnboxRows([get().id], true) const updateNav = () => { const reason = _reason @@ -2169,9 +2246,7 @@ const createSlice: Z.ImmerStateCreator = (set, get) => { clearModals() } - storeRegistry - .getState('router') - .dispatch.navigateAppend({props: {conversationIDKey}, selected: Common.threadRouteName}, replace) + navigateAppend({props: {conversationIDKey}, selected: Common.threadRouteName}, replace) } } updateNav() @@ -2249,7 +2324,7 @@ const createSlice: Z.ImmerStateCreator = (set, get) => { }, onIncomingMessage: incoming => { const {message: cMsg} = incoming - const username = storeRegistry.getState('current-user').username + const username = useCurrentUserState.getState().username // check for a reaction outbox notification before doing anything if ( cMsg.state === T.RPCChat.MessageUnboxedState.outbox && @@ -2275,7 +2350,7 @@ const createSlice: Z.ImmerStateCreator = (set, get) => { } const conversationIDKey = get().id - const devicename = storeRegistry.getState('current-user').deviceName + const devicename = useCurrentUserState.getState().deviceName const getLastOrdinal = () => get().messageOrdinals?.at(-1) ?? T.Chat.numberToOrdinal(0) // special case mutations @@ -2331,8 +2406,8 @@ const createSlice: Z.ImmerStateCreator = (set, get) => { }, onMessagesUpdated: messagesUpdated => { if (!messagesUpdated.updates) return - const username = storeRegistry.getState('current-user').username - const devicename = storeRegistry.getState('current-user').deviceName + const username = useCurrentUserState.getState().username + const devicename = useCurrentUserState.getState().deviceName const getLastOrdinal = () => get().messageOrdinals?.at(-1) ?? T.Chat.numberToOrdinal(0) const toAdd = new Array() messagesUpdated.updates.forEach(uimsg => { @@ -2363,7 +2438,7 @@ const createSlice: Z.ImmerStateCreator = (set, get) => { ? Config.teamFolder(meta.teamname) : Config.privateFolderWithUsers(participantInfo.name) ) - makeActionForOpenPathInFilesTab(path) + navToPath(path) }, paymentInfoReceived: (messageID, paymentInfo) => { set(s => { @@ -2461,7 +2536,7 @@ const createSlice: Z.ImmerStateCreator = (set, get) => { // remove all bad people const goodParticipants = new Set(participantInfo.all) meta.resetParticipants.forEach(r => goodParticipants.delete(r)) - storeRegistry.getState('chat').dispatch.previewConversation({ + get().dispatch.defer.chatPreviewConversation({ participants: [...goodParticipants], reason: 'resetChatWithoutThem', }) @@ -2493,7 +2568,7 @@ const createSlice: Z.ImmerStateCreator = (set, get) => { const fetchConversationBio = () => { const participantInfo = get().participants - const username = storeRegistry.getState('current-user').username + const username = useCurrentUserState.getState().username const otherParticipants = Meta.getRowParticipants(participantInfo, username || '') if (otherParticipants.length === 1) { // we're in a one-on-one convo @@ -2504,7 +2579,7 @@ const createSlice: Z.ImmerStateCreator = (set, get) => { return } - storeRegistry.getState('users').dispatch.getBio(username) + get().dispatch.defer.usersGetBio(username) } } @@ -2514,19 +2589,19 @@ const createSlice: Z.ImmerStateCreator = (set, get) => { if (isMetaGood()) { const {teamID, teamname} = meta if (teamname) { - storeRegistry.getState('teams').dispatch.getMembers(teamID) + get().dispatch.defer.teamsGetMembers(teamID) } } } ensureSelectedTeamLoaded() const participantInfo = get().participants const force = !get().isMetaGood() || participantInfo.all.length === 0 - storeRegistry.getState('chat').dispatch.unboxRows([conversationIDKey], force) + get().dispatch.defer.chatUnboxRows([conversationIDKey], force) set(s => { s.threadLoadStatus = T.RPCChat.UIChatThreadStatusTyp.none }) fetchConversationBio() - storeRegistry.getState('chat').dispatch.resetConversationErrored() + get().dispatch.defer.chatResetConversationErrored() }, sendAudioRecording: async (path, duration, amps) => { const outboxID = Common.generateOutboxID() @@ -2608,7 +2683,7 @@ const createSlice: Z.ImmerStateCreator = (set, get) => { let ordinal = T.Chat.numberToOrdinal(0) // Editing last message if (e === 'last') { - const editLastUser = storeRegistry.getState('current-user').username + const editLastUser = useCurrentUserState.getState().username // Editing your last message const ordinals = get().messageOrdinals const found = @@ -2703,7 +2778,7 @@ const createSlice: Z.ImmerStateCreator = (set, get) => { } const conversationIDKey = get().id const f = async () => { - if (!storeRegistry.getState('config').loggedIn) { + if (!useConfigState.getState().loggedIn) { logger.info('mark unread bail on not logged in') return } @@ -2846,9 +2921,8 @@ const createSlice: Z.ImmerStateCreator = (set, get) => { } }) }, 1000), - setupSubscriptions: () => {}, showInfoPanel: (show, tab) => { - storeRegistry.getState('chat').dispatch.updateInfoPanel(show, tab) + get().dispatch.defer.chatUpdateInfoPanel(show, tab) const conversationIDKey = get().id if (Platform.isPhone) { const visibleScreen = getVisibleScreen() @@ -2876,8 +2950,8 @@ const createSlice: Z.ImmerStateCreator = (set, get) => { const f = async () => { const conversationIDKey = get().id const getLastOrdinal = () => get().messageOrdinals?.at(-1) ?? T.Chat.numberToOrdinal(0) - const username = storeRegistry.getState('current-user').username - const devicename = storeRegistry.getState('current-user').deviceName + const username = useCurrentUserState.getState().username + const devicename = useCurrentUserState.getState().deviceName const onDone = () => { set(s => { s.threadSearchInfo.status = 'done' @@ -3252,10 +3326,9 @@ registerDebugClear(() => { const createConvoStore = (id: T.Chat.ConversationIDKey) => { const existing = chatStores.get(id) if (existing) return existing - const next = Z.createZustand(createSlice) + const next = Z.createZustand(createSlice()) next.setState({id}) chatStores.set(id, next) - next.getState().dispatch.setupSubscriptions() return next } @@ -3330,14 +3403,12 @@ export const ProviderScreen = React.memo(function ProviderScreen(p: { import type {NavigateAppendType} from '@/router-v2/route-params' export const useChatNavigateAppend = () => { - const useRouterState = storeRegistry.getStore('router') - const navigateAppend = useRouterState(s => s.dispatch.navigateAppend) const cid = useChatContext(s => s.id) return React.useCallback( (makePath: (cid: T.Chat.ConversationIDKey) => NavigateAppendType, replace?: boolean) => { navigateAppend(makePath(cid), replace) }, - [cid, navigateAppend] + [cid] ) } diff --git a/shared/constants/crypto/index.tsx b/shared/stores/crypto.tsx similarity index 97% rename from shared/constants/crypto/index.tsx rename to shared/stores/crypto.tsx index c4ee70dcfe73..99fd2fdd3781 100644 --- a/shared/constants/crypto/index.tsx +++ b/shared/stores/crypto.tsx @@ -1,15 +1,15 @@ import * as Z from '@/util/zustand' -import {ignorePromise} from '../utils' -import {isMobile} from '../platform' -import {waitingKeyCrypto} from '../strings' +import {ignorePromise} from '@/constants/utils' +import {isMobile} from '@/constants/platform' +import {waitingKeyCrypto} from '@/constants/strings' import HiddenString from '@/util/hidden-string' import logger from '@/logger' -import * as T from '../types' +import * as T from '@/constants/types' import {RPCError} from '@/util/errors' -import {navigateAppend} from '../router2/util' -import {storeRegistry} from '../store-registry' -import {Operations} from './util' -export * from './util' +import {navigateAppend} from '@/constants/router2' +import {useCurrentUserState} from '@/stores/current-user' +import {Operations} from '@/constants/crypto' +export * from '@/constants/crypto' type CommonStore = { bytesComplete: number @@ -214,7 +214,7 @@ export const useCryptoState = Z.createZustand((set, get) => { const encrypt = (destinationDir: string = '') => { const f = async () => { const start = get().encrypt - const username = storeRegistry.getState('current-user').username + const username = useCurrentUserState.getState().username const signed = start.options.sign const inputType = start.inputType const input = start.input.stringValue() @@ -349,7 +349,7 @@ export const useCryptoState = Z.createZustand((set, get) => { const output = await (inputType === 'text' ? callText() : callFile()) - const username = storeRegistry.getState('current-user').username + const username = useCurrentUserState.getState().username set(s => { onSuccess(s.sign, s.sign.input.stringValue() === input, '', output, inputType, true, username, '') }) @@ -530,7 +530,7 @@ export const useCryptoState = Z.createZustand((set, get) => { // User set themselves as a recipient, so don't show 'includeSelf' option // However we don't want to set hideIncludeSelf if we are also encrypting to an SBS user (since we must force includeSelf) - const currentUser = storeRegistry.getState('current-user').username + const currentUser = useCurrentUserState.getState().username const {options} = get().encrypt if (usernames.includes(currentUser) && !hasSBS) { get().dispatch.setEncryptOptions(options, true) diff --git a/shared/constants/current-user/index.tsx b/shared/stores/current-user.tsx similarity index 87% rename from shared/constants/current-user/index.tsx rename to shared/stores/current-user.tsx index 8219dbc286f9..aaba7e2cc2cd 100644 --- a/shared/constants/current-user/index.tsx +++ b/shared/stores/current-user.tsx @@ -1,6 +1,7 @@ -import type * as T from '../types' +import type * as T from '@/constants/types' import * as Z from '@/util/zustand' +// This store has no dependencies on other stores and is safe to import directly from other stores. type Store = T.Immutable<{ deviceID: T.RPCGen.DeviceID deviceName: string diff --git a/shared/constants/daemon/index.tsx b/shared/stores/daemon.tsx similarity index 70% rename from shared/constants/daemon/index.tsx rename to shared/stores/daemon.tsx index 15d3f95b7f9c..340fcd3949e5 100644 --- a/shared/constants/daemon/index.tsx +++ b/shared/stores/daemon.tsx @@ -1,25 +1,26 @@ import logger from '@/logger' -import {ignorePromise} from '../utils' -import * as T from '../types' +import {ignorePromise} from '@/constants/utils' +import * as T from '@/constants/types' import * as Z from '@/util/zustand' -import {storeRegistry} from '../store-registry' -import {maxHandshakeTries} from '../values' +import {maxHandshakeTries} from '@/constants/values' // Load accounts, this call can be slow so we attempt to continue w/o waiting if we determine we're logged in // normally this wouldn't be worth it but this is startup const getAccountsWaitKey = 'config.getAccounts' type Store = T.Immutable<{ + bootstrapStatus?: T.RPCGen.BootstrapStatus error?: Error - handshakeState: T.Config.DaemonHandshakeState handshakeFailedReason: string handshakeRetriesLeft: number + handshakeState: T.Config.DaemonHandshakeState + handshakeVersion: number handshakeWaiters: Map // if we ever restart handshake up this so we can ignore any waiters for old things - handshakeVersion: number }> const initialStore: Store = { + bootstrapStatus: undefined, handshakeFailedReason: '', handshakeRetriesLeft: maxHandshakeTries, handshakeState: 'starting', @@ -29,9 +30,8 @@ const initialStore: Store = { export interface State extends Store { dispatch: { - loadDaemonAccounts: () => void + loadDaemonAccounts: (configuredAccountsLength: number, loggedIn: boolean, refreshAccounts: () => Promise) => void loadDaemonBootstrapStatus: () => Promise - refreshAccounts: () => Promise resetState: () => void setError: (e?: Error) => void setFailed: (r: string) => void @@ -91,7 +91,6 @@ export const useDaemonState = Z.createZustand((set, get) => { // When there are no more waiters, we can show the actual app - let _emitStartupOnLoadDaemonConnectedOnce = false const dispatch: State['dispatch'] = { daemonHandshake: version => { get().dispatch.setState('waitingForWaiters') @@ -109,22 +108,19 @@ export const useDaemonState = Z.createZustand((set, get) => { wait(name, version, true) try { await get().dispatch.loadDaemonBootstrapStatus() - storeRegistry.getState('dark-mode').dispatch.loadDarkPrefs() - storeRegistry.getState('chat').dispatch.loadStaticConfig() } finally { wait(name, version, false) } } ignorePromise(f()) - get().dispatch.loadDaemonAccounts() }, daemonHandshakeDone: () => { get().dispatch.setState('done') }, - loadDaemonAccounts: () => { + loadDaemonAccounts: (configuredAccountsLength: number, loggedIn: boolean, refreshAccounts: () => Promise) => { const f = async () => { const version = get().handshakeVersion - if (storeRegistry.getState('config').configuredAccounts.length) { + if (configuredAccountsLength) { // bail on already loaded return } @@ -133,7 +129,7 @@ export const useDaemonState = Z.createZustand((set, get) => { const handshakeVersion = version // did we beat getBootstrapStatus? - if (!storeRegistry.getState('config').loggedIn) { + if (!loggedIn) { handshakeWait = true } @@ -143,7 +139,7 @@ export const useDaemonState = Z.createZustand((set, get) => { wait(getAccountsWaitKey, handshakeVersion, true) } - await get().dispatch.refreshAccounts() + await refreshAccounts() if (handshakeWait) { // someone dismissed this already? @@ -170,32 +166,22 @@ export const useDaemonState = Z.createZustand((set, get) => { const {wait} = get().dispatch const f = async () => { - const {setBootstrap} = storeRegistry.getState('current-user').dispatch - const {setDefaultUsername} = storeRegistry.getState('config').dispatch const s = await T.RPCGen.configGetBootstrapStatusRpcPromise() - const {userReacjis, deviceName, deviceID, uid, loggedIn, username} = s - setBootstrap({deviceID, deviceName, uid, username}) - if (username) { - setDefaultUsername(username) - } - if (loggedIn) { - storeRegistry.getState('config').dispatch.setUserSwitching(false) - } + set(state => { + state.bootstrapStatus = T.castDraft(s) + }) - logger.info(`[Bootstrap] loggedIn: ${loggedIn ? 1 : 0}`) - storeRegistry.getState('config').dispatch.setLoggedIn(loggedIn, false) - storeRegistry.getState('chat').dispatch.updateUserReacjis(userReacjis) + logger.info(`[Bootstrap] loggedIn: ${s.loggedIn ? 1 : 0}`) // set HTTP srv info if (s.httpSrvInfo) { logger.info(`[Bootstrap] http server: addr: ${s.httpSrvInfo.address} token: ${s.httpSrvInfo.token}`) - storeRegistry.getState('config').dispatch.setHTTPSrvInfo(s.httpSrvInfo.address, s.httpSrvInfo.token) } else { logger.info(`[Bootstrap] http server: no info given`) } // if we're logged in act like getAccounts is done already - if (loggedIn) { + if (s.loggedIn) { const {handshakeWaiters} = get() if (handshakeWaiters.get(getAccountsWaitKey)) { wait(getAccountsWaitKey, version, false) @@ -205,39 +191,6 @@ export const useDaemonState = Z.createZustand((set, get) => { return await f() }, onRestartHandshakeNative: _onRestartHandshakeNative, - refreshAccounts: async () => { - const configuredAccounts = (await T.RPCGen.loginGetConfiguredAccountsRpcPromise()) ?? [] - // already have one? - const {defaultUsername} = storeRegistry.getState('config') - const {setAccounts, setDefaultUsername} = storeRegistry.getState('config').dispatch - - let existingDefaultFound = false as boolean - let currentName = '' - const nextConfiguredAccounts: Array = [] - const usernameToFullname: {[username: string]: string} = {} - - configuredAccounts.forEach(account => { - const {username, isCurrent, fullname, hasStoredSecret} = account - if (username === defaultUsername) { - existingDefaultFound = true - } - if (isCurrent) { - currentName = account.username - } - nextConfiguredAccounts.push({hasStoredSecret, username}) - usernameToFullname[username] = fullname - }) - if (!existingDefaultFound) { - setDefaultUsername(currentName) - } - setAccounts(nextConfiguredAccounts) - storeRegistry.getState('users').dispatch.updates( - Object.keys(usernameToFullname).map(name => ({ - info: {fullname: usernameToFullname[name]}, - name, - })) - ) - }, resetState: () => { set(s => ({ ...s, @@ -268,13 +221,6 @@ export const useDaemonState = Z.createZustand((set, get) => { set(s => { s.handshakeState = ds }) - - if (ds !== 'done') return - - if (!_emitStartupOnLoadDaemonConnectedOnce) { - _emitStartupOnLoadDaemonConnectedOnce = true - storeRegistry.getState('config').dispatch.loadOnStart('connectedToDaemonForFirstTime') - } }, startHandshake: () => { get().dispatch.setError() diff --git a/shared/constants/darkmode/index.tsx b/shared/stores/darkmode.tsx similarity index 95% rename from shared/constants/darkmode/index.tsx rename to shared/stores/darkmode.tsx index f5e240e3a4d3..c616904f1e4e 100644 --- a/shared/constants/darkmode/index.tsx +++ b/shared/stores/darkmode.tsx @@ -1,10 +1,11 @@ -import * as T from '../types' +import * as T from '@/constants/types' import * as Z from '@/util/zustand' import {Appearance} from 'react-native' -import {isMobile} from '../platform' +import {isMobile} from '@/constants/platform' export type DarkModePreference = 'system' | 'alwaysDark' | 'alwaysLight' +// This store has no dependencies on other stores and is safe to import directly from other stores. type Store = T.Immutable<{ darkModePreference: DarkModePreference systemDarkMode: boolean diff --git a/shared/constants/devices/index.tsx b/shared/stores/devices.tsx similarity index 96% rename from shared/constants/devices/index.tsx rename to shared/stores/devices.tsx index 61608a6b712f..a8404ad69f98 100644 --- a/shared/constants/devices/index.tsx +++ b/shared/stores/devices.tsx @@ -1,8 +1,8 @@ import * as React from 'react' import * as Z from '@/util/zustand' -import * as S from '../strings' -import {ignorePromise, updateImmerMap} from '../utils' -import * as T from '../types' +import * as S from '@/constants/strings' +import {ignorePromise, updateImmerMap} from '@/constants/utils' +import * as T from '@/constants/types' import * as EngineGen from '@/actions/engine-gen-gen' import debounce from 'lodash/debounce' diff --git a/shared/constants/followers/index.tsx b/shared/stores/followers.tsx similarity index 92% rename from shared/constants/followers/index.tsx rename to shared/stores/followers.tsx index f7f550f23102..b403722e07af 100644 --- a/shared/constants/followers/index.tsx +++ b/shared/stores/followers.tsx @@ -1,6 +1,6 @@ import * as T from '@/constants/types' import * as Z from '@/util/zustand' - +// This store has no dependencies on other stores and is safe to import directly from other stores. type Store = T.Immutable<{ followers: Set following: Set diff --git a/shared/constants/fs/index.tsx b/shared/stores/fs.tsx similarity index 68% rename from shared/constants/fs/index.tsx rename to shared/stores/fs.tsx index b5f112cfcbe5..f3bdde88ab18 100644 --- a/shared/constants/fs/index.tsx +++ b/shared/stores/fs.tsx @@ -1,160 +1,32 @@ import * as EngineGen from '@/actions/engine-gen-gen' -import {ignorePromise, timeoutPromise} from '../utils' -import * as S from '../strings' -import {requestPermissionsToWrite} from '../platform-specific' -import * as Tabs from '../tabs' -import * as T from '../types' +import {ignorePromise, timeoutPromise} from '@/constants/utils' +import * as S from '@/constants/strings' +import {requestPermissionsToWrite} from '@/util/platform-specific' +import * as Tabs from '@/constants/tabs' +import * as T from '@/constants/types' import * as Z from '@/util/zustand' import NotifyPopup from '@/util/notify-popup' import {RPCError} from '@/util/errors' import logger from '@/logger' -import {isLinux, isMobile} from '../platform' import {tlfToPreferredOrder} from '@/util/kbfs' import isObject from 'lodash/isObject' import isEqual from 'lodash/isEqual' -import {settingsFsTab} from '../settings/util' -import {navigateAppend, navigateUp} from '../router2/util' -import {storeRegistry} from '../store-registry' +import {navigateAppend, navigateUp} from '@/constants/router2' +import {useConfigState} from '@/stores/config' +import {useCurrentUserState} from '@/stores/current-user' +import * as Constants from '@/constants/fs' -export {makeActionForOpenPathInFilesTab} from './util' +export * from '@/constants/fs' -const subscriptionDeduplicateIntervalSecond = 1 -export const defaultPath = T.FS.stringToPath('/keybase') - -export const rpcFolderTypeToTlfType = (rpcFolderType: T.RPCGen.FolderType) => { - switch (rpcFolderType) { - case T.RPCGen.FolderType.private: - return T.FS.TlfType.Private - case T.RPCGen.FolderType.public: - return T.FS.TlfType.Public - case T.RPCGen.FolderType.team: - return T.FS.TlfType.Team - default: - return null - } -} - -export const rpcConflictStateToConflictState = ( - rpcConflictState?: T.RPCGen.ConflictState -): T.FS.ConflictState => { - if (rpcConflictState) { - if (rpcConflictState.conflictStateType === T.RPCGen.ConflictStateType.normalview) { - const nv = rpcConflictState.normalview - return makeConflictStateNormalView({ - localViewTlfPaths: (nv.localViews || []).reduce>((arr, p) => { - p.PathType === T.RPCGen.PathType.kbfs && arr.push(rpcPathToPath(p.kbfs)) - return arr - }, []), - resolvingConflict: nv.resolvingConflict, - stuckInConflict: nv.stuckInConflict, - }) - } else { - const nv = rpcConflictState.manualresolvinglocalview.normalView - return makeConflictStateManualResolvingLocalView({ - normalViewTlfPath: nv.PathType === T.RPCGen.PathType.kbfs ? rpcPathToPath(nv.kbfs) : defaultPath, - }) - } - } else { - return tlfNormalViewWithNoConflict - } -} - -export const getSyncConfigFromRPC = ( - tlfName: string, - tlfType: T.FS.TlfType, - config?: T.RPCGen.FolderSyncConfig -): T.FS.TlfSyncConfig => { - if (!config) { - return tlfSyncDisabled - } - switch (config.mode) { - case T.RPCGen.FolderSyncMode.disabled: - return tlfSyncDisabled - case T.RPCGen.FolderSyncMode.enabled: - return tlfSyncEnabled - case T.RPCGen.FolderSyncMode.partial: - return makeTlfSyncPartial({ - enabledPaths: config.paths - ? config.paths.map(str => T.FS.getPathFromRelative(tlfName, tlfType, str)) - : [], - }) - default: - return tlfSyncDisabled - } -} - -// See Installer.m: KBExitFuseKextError -export const ExitCodeFuseKextError = 4 -// See Installer.m: KBExitFuseKextPermissionError -export const ExitCodeFuseKextPermissionError = 5 -// See Installer.m: KBExitAuthCanceledError -export const ExitCodeAuthCanceledError = 6 - -export const emptyNewFolder: T.FS.Edit = { - error: undefined, - name: 'New Folder', - originalName: 'New Folder', - parentPath: T.FS.stringToPath('/keybase'), - type: T.FS.EditType.NewFolder, -} - -export const prefetchNotStarted: T.FS.PrefetchNotStarted = { - state: T.FS.PrefetchState.NotStarted, -} - -export const prefetchComplete: T.FS.PrefetchComplete = { - state: T.FS.PrefetchState.Complete, -} - -export const emptyPrefetchInProgress: T.FS.PrefetchInProgress = { - bytesFetched: 0, - bytesTotal: 0, - endEstimate: 0, - startTime: 0, - state: T.FS.PrefetchState.InProgress, -} - -const pathItemMetadataDefault = { - lastModifiedTimestamp: 0, - lastWriter: '', - name: 'unknown', - prefetchStatus: prefetchNotStarted, - size: 0, - writable: false, -} - -export const emptyFolder: T.FS.FolderPathItem = { - ...pathItemMetadataDefault, - children: new Set(), - progress: T.FS.ProgressType.Pending, - type: T.FS.PathType.Folder, -} - -export const emptyFile: T.FS.FilePathItem = { - ...pathItemMetadataDefault, - type: T.FS.PathType.File, -} - -export const emptySymlink: T.FS.SymlinkPathItem = { - ...pathItemMetadataDefault, - linkTarget: '', - type: T.FS.PathType.Symlink, -} - -export const unknownPathItem: T.FS.UnknownPathItem = { - ...pathItemMetadataDefault, - type: T.FS.PathType.Unknown, -} - -export const tlfSyncEnabled: T.FS.TlfSyncEnabled = { +const tlfSyncEnabled: T.FS.TlfSyncEnabled = { mode: T.FS.TlfSyncMode.Enabled, } -export const tlfSyncDisabled: T.FS.TlfSyncDisabled = { +const tlfSyncDisabled: T.FS.TlfSyncDisabled = { mode: T.FS.TlfSyncMode.Disabled, } -export const makeTlfSyncPartial = ({ +const makeTlfSyncPartial = ({ enabledPaths, }: { enabledPaths?: T.FS.TlfSyncPartial['enabledPaths'] @@ -163,7 +35,7 @@ export const makeTlfSyncPartial = ({ mode: T.FS.TlfSyncMode.Partial, }) -export const makeConflictStateNormalView = ({ +const makeConflictStateNormalView = ({ localViewTlfPaths, resolvingConflict, stuckInConflict, @@ -174,16 +46,16 @@ export const makeConflictStateNormalView = ({ type: T.FS.ConflictStateType.NormalView, }) -export const tlfNormalViewWithNoConflict = makeConflictStateNormalView({}) +const tlfNormalViewWithNoConflict = makeConflictStateNormalView({}) -export const makeConflictStateManualResolvingLocalView = ({ +const makeConflictStateManualResolvingLocalView = ({ normalViewTlfPath, }: Partial): T.FS.ConflictStateManualResolvingLocalView => ({ - normalViewTlfPath: normalViewTlfPath || defaultPath, + normalViewTlfPath: normalViewTlfPath || Constants.defaultPath, type: T.FS.ConflictStateType.ManualResolvingLocalView, }) -export const makeTlf = (p: Partial): T.FS.Tlf => { +const makeTlf = (p: Partial): T.FS.Tlf => { const {conflictState, isFavorite, isIgnored, isNew, name, resetParticipants, syncConfig, teamId, tlfMtime} = p return { @@ -204,186 +76,90 @@ export const makeTlf = (p: Partial): T.FS.Tlf => { } } -export const emptySyncingFoldersProgress: T.FS.SyncingFoldersProgress = { - bytesFetched: 0, - bytesTotal: 0, - endEstimate: 0, - start: 0, -} - -export const emptyOverallSyncStatus: T.FS.OverallSyncStatus = { - diskSpaceStatus: T.FS.DiskSpaceStatus.Ok, - showingBanner: false, - syncingFoldersProgress: emptySyncingFoldersProgress, -} - -export const defaultPathUserSetting: T.FS.PathUserSetting = { - sort: T.FS.SortSetting.NameAsc, -} - -export const defaultTlfListPathUserSetting: T.FS.PathUserSetting = { - sort: T.FS.SortSetting.TimeAsc, -} - -export const emptyDownloadState: T.FS.DownloadState = { - canceled: false, - done: false, - endEstimate: 0, - error: '', - localPath: '', - progress: 0, -} - -export const emptyDownloadInfo: T.FS.DownloadInfo = { - filename: '', - isRegularDownload: false, - path: defaultPath, - startTime: 0, -} - -export const emptyPathItemActionMenu: T.FS.PathItemActionMenu = { - downloadID: undefined, - downloadIntent: undefined, - previousView: T.FS.PathItemActionMenuView.Root, - view: T.FS.PathItemActionMenuView.Root, -} - -export const driverStatusUnknown: T.FS.DriverStatusUnknown = { - type: T.FS.DriverStatusType.Unknown, -} as const - -export const emptyDriverStatusEnabled: T.FS.DriverStatusEnabled = { - dokanOutdated: false, - dokanUninstallExecPath: undefined, - isDisabling: false, - type: T.FS.DriverStatusType.Enabled, -} as const - -export const emptyDriverStatusDisabled: T.FS.DriverStatusDisabled = { - isEnabling: false, - kextPermissionError: false, - type: T.FS.DriverStatusType.Disabled, -} as const - -export const defaultDriverStatus: T.FS.DriverStatus = isLinux ? emptyDriverStatusEnabled : driverStatusUnknown - -export const unknownKbfsDaemonStatus: T.FS.KbfsDaemonStatus = { - onlineStatus: T.FS.KbfsDaemonOnlineStatus.Unknown, - rpcStatus: T.FS.KbfsDaemonRpcStatus.Waiting, -} - -export const emptySettings: T.FS.Settings = { - isLoading: false, - loaded: false, - sfmiBannerDismissed: false, - spaceAvailableNotificationThreshold: 0, - syncOnCellular: false, -} - -export const emptyPathInfo: T.FS.PathInfo = { - deeplinkPath: '', - platformAfterMountPath: '', -} - -export const emptyFileContext: T.FS.FileContext = { - contentType: '', - url: '', - viewType: T.RPCGen.GUIViewType.default, +const rpcFolderTypeToTlfType = (rpcFolderType: T.RPCGen.FolderType) => { + switch (rpcFolderType) { + case T.RPCGen.FolderType.private: + return T.FS.TlfType.Private + case T.RPCGen.FolderType.public: + return T.FS.TlfType.Public + case T.RPCGen.FolderType.team: + return T.FS.TlfType.Team + default: + return null + } } -export const getPathItem = ( - pathItems: T.Immutable>, - path: T.Immutable -): T.Immutable => pathItems.get(path) || (unknownPathItem as T.FS.PathItem) +const rpcPathToPath = (rpcPath: T.RPCGen.KBFSPath) => T.FS.pathConcat(Constants.defaultPath, rpcPath.path) -// RPC expects a string that's interpreted as [16]byte on Go side and it has to -// be unique among all ongoing ops at any given time. uuidv1 may exceed 16 -// bytes, so just roll something simple that's seeded with time. -// -// MAX_SAFE_INTEGER after toString(36) is 11 characters, so this should take <= -// 12 chars -const uuidSeed = Date.now().toString(36) + '-' -let counter = 0 -// We have 36^4=1,679,616 of space to work with in order to not exceed 16 -// bytes. -const counterMod = 36 * 36 * 36 * 36 -export const makeUUID = () => { - counter = (counter + 1) % counterMod - return uuidSeed + counter.toString(36) +const pathFromFolderRPC = (folder: T.RPCGen.Folder): T.FS.Path => { + const visibility = T.FS.getVisibilityFromRPCFolderType(folder.folderType) + if (!visibility) return T.FS.stringToPath('') + return T.FS.stringToPath(`/keybase/${visibility}/${folder.name}`) } -export const clientID = makeUUID() -export const pathToRPCPath = ( - path: T.FS.Path -): {PathType: T.RPCGen.PathType.kbfs; kbfs: T.RPCGen.KBFSPath} => ({ - PathType: T.RPCGen.PathType.kbfs, - kbfs: { - identifyBehavior: T.RPCGen.TLFIdentifyBehavior.fsGui, - path: T.FS.pathToString(path).substring('/keybase'.length) || '/', - }, -}) - -export const rpcPathToPath = (rpcPath: T.RPCGen.KBFSPath) => T.FS.pathConcat(defaultPath, rpcPath.path) +const folderRPCFromPath = (path: T.FS.Path): T.RPCGen.FolderHandle | undefined => { + const pathElems = T.FS.getPathElements(path) + if (pathElems.length === 0) return undefined -export const pathTypeToTextType = (type: T.FS.PathType) => - type === T.FS.PathType.Folder ? 'BodySemibold' : 'Body' + const visibility = T.FS.getVisibilityFromElems(pathElems) + if (visibility === undefined) return undefined -export const splitTlfIntoUsernames = (tlf: string): ReadonlyArray => - tlf.split(' ')[0]?.replace(/#/g, ',').split(',') ?? [] + const name = T.FS.getPathNameFromElems(pathElems) + if (name === '') return undefined -export const getUsernamesFromPath = (path: T.FS.Path): ReadonlyArray => { - const elems = T.FS.getPathElements(path) - return elems.length < 3 ? [] : splitTlfIntoUsernames(elems[2]!) + return { + created: false, + folderType: T.FS.getRPCFolderTypeFromVisibility(visibility), + name, + } } -export const humanReadableFileSize = (size: number) => { - const kib = 1024 - const mib = kib * kib - const gib = mib * kib - const tib = gib * kib - - if (!size) return '' - if (size >= tib) return `${Math.round(size / tib)} TB` - if (size >= gib) return `${Math.round(size / gib)} GB` - if (size >= mib) return `${Math.round(size / mib)} MB` - if (size >= kib) return `${Math.round(size / kib)} KB` - return `${size} B` +const rpcConflictStateToConflictState = (rpcConflictState?: T.RPCGen.ConflictState): T.FS.ConflictState => { + if (rpcConflictState) { + if (rpcConflictState.conflictStateType === T.RPCGen.ConflictStateType.normalview) { + const nv = rpcConflictState.normalview + return makeConflictStateNormalView({ + localViewTlfPaths: (nv.localViews || []).reduce>((arr, p) => { + p.PathType === T.RPCGen.PathType.kbfs && arr.push(rpcPathToPath(p.kbfs)) + return arr + }, []), + resolvingConflict: nv.resolvingConflict, + stuckInConflict: nv.stuckInConflict, + }) + } else { + const nv = rpcConflictState.manualresolvinglocalview.normalView + return makeConflictStateManualResolvingLocalView({ + normalViewTlfPath: + nv.PathType === T.RPCGen.PathType.kbfs ? rpcPathToPath(nv.kbfs) : Constants.defaultPath, + }) + } + } else { + return tlfNormalViewWithNoConflict + } } -export const downloadIsOngoing = (dlState: T.FS.DownloadState) => - dlState !== emptyDownloadState && !dlState.error && !dlState.done && !dlState.canceled - -export const getDownloadIntent = ( - path: T.FS.Path, - downloads: T.FS.Downloads, - pathItemActionMenu: T.FS.PathItemActionMenu -): T.FS.DownloadIntent | undefined => { - const found = [...downloads.info].find(([_, info]) => info.path === path) - if (!found) { - return undefined - } - const [downloadID] = found - const dlState = downloads.state.get(downloadID) || emptyDownloadState - if (!downloadIsOngoing(dlState)) { - return undefined +const getSyncConfigFromRPC = ( + tlfName: string, + tlfType: T.FS.TlfType, + config?: T.RPCGen.FolderSyncConfig +): T.FS.TlfSyncConfig => { + if (!config) { + return tlfSyncDisabled } - if (pathItemActionMenu.downloadID === downloadID) { - return pathItemActionMenu.downloadIntent + switch (config.mode) { + case T.RPCGen.FolderSyncMode.disabled: + return tlfSyncDisabled + case T.RPCGen.FolderSyncMode.enabled: + return tlfSyncEnabled + case T.RPCGen.FolderSyncMode.partial: + return makeTlfSyncPartial({ + enabledPaths: config.paths + ? config.paths.map(str => T.FS.getPathFromRelative(tlfName, tlfType, str)) + : [], + }) + default: + return tlfSyncDisabled } - return T.FS.DownloadIntent.None -} - -export const emptyTlfUpdate: T.FS.TlfUpdate = { - history: [], - path: T.FS.stringToPath(''), - serverTime: 0, - writer: '', -} - -export const emptyTlfEdit: T.FS.TlfEdit = { - editType: T.FS.FileEditType.Unknown, - filename: '', - serverTime: 0, } const fsNotificationTypeToEditType = ( @@ -403,7 +179,7 @@ const fsNotificationTypeToEditType = ( } } -export const userTlfHistoryRPCToState = ( +const userTlfHistoryRPCToState = ( history: ReadonlyArray ): T.FS.UserTlfUpdates => { let updates: Array = [] @@ -429,567 +205,46 @@ export const userTlfHistoryRPCToState = ( return updates } -export const canSaveMedia = (pathItem: T.FS.PathItem, fileContext: T.FS.FileContext): boolean => { - if (pathItem.type !== T.FS.PathType.File || fileContext === emptyFileContext) { - return false - } - return ( - fileContext.viewType === T.RPCGen.GUIViewType.image || fileContext.viewType === T.RPCGen.GUIViewType.video - ) -} - -export const folderRPCFromPath = (path: T.FS.Path): T.RPCGen.FolderHandle | undefined => { - const pathElems = T.FS.getPathElements(path) - if (pathElems.length === 0) return undefined - - const visibility = T.FS.getVisibilityFromElems(pathElems) - if (visibility === undefined) return undefined - - const name = T.FS.getPathNameFromElems(pathElems) - if (name === '') return undefined - - return { - created: false, - folderType: T.FS.getRPCFolderTypeFromVisibility(visibility), - name, - } -} - -export const pathFromFolderRPC = (folder: T.RPCGen.Folder): T.FS.Path => { - const visibility = T.FS.getVisibilityFromRPCFolderType(folder.folderType) - if (!visibility) return T.FS.stringToPath('') - return T.FS.stringToPath(`/keybase/${visibility}/${folder.name}`) -} +const subscriptionDeduplicateIntervalSecond = 1 -export const showIgnoreFolder = (path: T.FS.Path, username?: string): boolean => { - const elems = T.FS.getPathElements(path) - if (elems.length !== 3) { - return false - } - return ['public', 'private'].includes(elems[1]!) && elems[2]! !== username +// RPC expects a string that's interpreted as [16]byte on Go side and it has to +// be unique among all ongoing ops at any given time. uuidv1 may exceed 16 +// bytes, so just roll something simple that's seeded with time. +// +// MAX_SAFE_INTEGER after toString(36) is 11 characters, so this should take <= +// 12 chars +const uuidSeed = Date.now().toString(36) + '-' +let counter = 0 +// We have 36^4=1,679,616 of space to work with in order to not exceed 16 +// bytes. +const counterMod = 36 * 36 * 36 * 36 +export const makeUUID = () => { + counter = (counter + 1) % counterMod + return uuidSeed + counter.toString(36) } -export const syntheticEventToTargetRect = (evt?: React.SyntheticEvent): DOMRect | undefined => - isMobile ? undefined : evt ? (evt.target as HTMLElement).getBoundingClientRect() : undefined - -export const invalidTokenError = new Error('invalid token') -export const notFoundError = new Error('not found') +export const clientID = makeUUID() export const makeEditID = (): T.FS.EditID => T.FS.stringToEditID(makeUUID()) -export const getTlfListFromType = ( - tlfs: T.Immutable, - tlfType: T.Immutable -): T.Immutable => { - switch (tlfType) { - case T.FS.TlfType.Private: - return tlfs.private - case T.FS.TlfType.Public: - return tlfs.public - case T.FS.TlfType.Team: - return tlfs.team - default: - return new Map() - } -} - -export const computeBadgeNumberForTlfList = (tlfList: T.Immutable): number => - [...tlfList.values()].reduce((accumulator, tlf) => (tlfIsBadged(tlf) ? accumulator + 1 : accumulator), 0) - -export const computeBadgeNumberForAll = (tlfs: T.Immutable): number => - [T.FS.TlfType.Private, T.FS.TlfType.Public, T.FS.TlfType.Team] - .map(tlfType => computeBadgeNumberForTlfList(getTlfListFromType(tlfs, tlfType))) - .reduce((sum, count) => sum + count, 0) - -export const getTlfPath = (path: T.FS.Path): T.FS.Path => { - const elems = T.FS.getPathElements(path) - return elems.length > 2 ? T.FS.pathConcat(T.FS.pathConcat(defaultPath, elems[1]!), elems[2]!) : undefined -} - -export const getTlfListAndTypeFromPath = ( - tlfs: T.Immutable, - path: T.Immutable -): T.Immutable<{ - tlfList: T.FS.TlfList - tlfType: T.FS.TlfType -}> => { - const visibility = T.FS.getPathVisibility(path) - switch (visibility) { - case T.FS.TlfType.Private: - case T.FS.TlfType.Public: - case T.FS.TlfType.Team: { - const tlfType: T.FS.TlfType = visibility - return {tlfList: getTlfListFromType(tlfs, tlfType), tlfType} - } - default: - return {tlfList: new Map(), tlfType: T.FS.TlfType.Private} - } -} - -export const unknownTlf = makeTlf({}) -export const getTlfFromPathInFavoritesOnly = (tlfs: T.Immutable, path: T.FS.Path): T.FS.Tlf => { - const elems = T.FS.getPathElements(path) - if (elems.length < 3) { - return unknownTlf - } - const {tlfList} = getTlfListAndTypeFromPath(tlfs, path) - return tlfList.get(elems[2]!) || unknownTlf -} - -export const getTlfFromPath = (tlfs: T.Immutable, path: T.FS.Path): T.FS.Tlf => { - const fromFavorites = getTlfFromPathInFavoritesOnly(tlfs, path) - return fromFavorites !== unknownTlf - ? fromFavorites - : tlfs.additionalTlfs.get(getTlfPath(path)) || unknownTlf -} - -export const getTlfFromTlfs = (tlfs: T.FS.Tlfs, tlfType: T.FS.TlfType, name: string): T.FS.Tlf => { - switch (tlfType) { - case T.FS.TlfType.Private: - return tlfs.private.get(name) || unknownTlf - case T.FS.TlfType.Public: - return tlfs.public.get(name) || unknownTlf - case T.FS.TlfType.Team: - return tlfs.team.get(name) || unknownTlf - default: - return unknownTlf - } -} - -export const tlfTypeAndNameToPath = (tlfType: T.FS.TlfType, name: string): T.FS.Path => - T.FS.stringToPath(`/keybase/${tlfType}/${name}`) - export const resetBannerType = (s: State, path: T.FS.Path): T.FS.ResetBannerType => { - const resetParticipants = getTlfFromPath(s.tlfs, path).resetParticipants + const resetParticipants = Constants.getTlfFromPath(s.tlfs, path).resetParticipants if (resetParticipants.length === 0) { return T.FS.ResetBannerNoOthersType.None } - const you = storeRegistry.getState('current-user').username + const you = useCurrentUserState.getState().username if (resetParticipants.findIndex(username => username === you) >= 0) { return T.FS.ResetBannerNoOthersType.Self } return resetParticipants.length } -export const getUploadedPath = (parentPath: T.FS.Path, localPath: string) => - T.FS.pathConcat(parentPath, T.FS.getLocalPathName(localPath)) - -export const usernameInPath = (username: string, path: T.FS.Path) => { - const elems = T.FS.getPathElements(path) - return elems.length >= 3 && elems[2]!.split(',').includes(username) -} - -export const getUsernamesFromTlfName = (tlfName: string): Array => { - const split = splitTlfIntoReadersAndWriters(tlfName) - return split.writers.concat(split.readers || []) -} - -export const isOfflineUnsynced = ( - daemonStatus: T.FS.KbfsDaemonStatus, - pathItem: T.FS.PathItem, - path: T.FS.Path -) => - daemonStatus.onlineStatus === T.FS.KbfsDaemonOnlineStatus.Offline && - T.FS.getPathLevel(path) > 2 && - pathItem.prefetchStatus !== prefetchComplete - -// To make sure we have consistent badging, all badging related stuff should go -// through this function. That is: -// * When calculating number of TLFs being badged, a TLF should be counted if -// and only if this function returns true. -// * When an individual TLF is shown (e.g. as a row), it should be badged if -// and only if this funciton returns true. -// -// If we add more badges, this function should be updated. -export const tlfIsBadged = (tlf: T.FS.Tlf) => !tlf.isIgnored && tlf.isNew - -export const pathsInSameTlf = (a: T.FS.Path, b: T.FS.Path): boolean => { - const elemsA = T.FS.getPathElements(a) - const elemsB = T.FS.getPathElements(b) - return elemsA.length >= 3 && elemsB.length >= 3 && elemsA[1] === elemsB[1] && elemsA[2] === elemsB[2] -} - -const slashKeybaseSlashLength = '/keybase/'.length -// TODO: move this to Go -export const escapePath = (path: T.FS.Path): string => - 'keybase://' + - encodeURIComponent(T.FS.pathToString(path).slice(slashKeybaseSlashLength)).replace( - // We need to do this because otherwise encodeURIComponent would encode - // "/"s. - /%2F/g, - '/' - ) - -export const parsedPathRoot: T.FS.ParsedPathRoot = {kind: T.FS.PathKind.Root} - -export const parsedPathPrivateList: T.FS.ParsedPathTlfList = { - kind: T.FS.PathKind.TlfList, - tlfType: T.FS.TlfType.Private, -} - -export const parsedPathPublicList: T.FS.ParsedPathTlfList = { - kind: T.FS.PathKind.TlfList, - tlfType: T.FS.TlfType.Public, -} - -export const parsedPathTeamList: T.FS.ParsedPathTlfList = { - kind: T.FS.PathKind.TlfList, - tlfType: T.FS.TlfType.Team, -} - -const splitTlfIntoReadersAndWriters = ( - tlf: string -): { - readers?: Array - writers: Array -} => { - const [w, r] = tlf.split('#') - return { - readers: r ? r.split(',').filter(i => !!i) : undefined, - writers: w?.split(',').filter(i => !!i) ?? [], - } -} - -// returns parsedPathRoot if unknown -export const parsePath = (path: T.FS.Path): T.FS.ParsedPath => { - const elems = T.FS.getPathElements(path) - if (elems.length <= 1) { - return parsedPathRoot - } - switch (elems[1]) { - case 'private': - switch (elems.length) { - case 2: - return parsedPathPrivateList - case 3: - return { - kind: T.FS.PathKind.GroupTlf, - tlfName: elems[2]!, - tlfType: T.FS.TlfType.Private, - ...splitTlfIntoReadersAndWriters(elems[2]!), - } - default: - return { - kind: T.FS.PathKind.InGroupTlf, - rest: elems.slice(3), - tlfName: elems[2] ?? '', - tlfType: T.FS.TlfType.Private, - ...splitTlfIntoReadersAndWriters(elems[2] ?? ''), - } - } - case 'public': - switch (elems.length) { - case 2: - return parsedPathPublicList - case 3: - return { - kind: T.FS.PathKind.GroupTlf, - tlfName: elems[2]!, - tlfType: T.FS.TlfType.Public, - ...splitTlfIntoReadersAndWriters(elems[2]!), - } - default: - return { - kind: T.FS.PathKind.InGroupTlf, - rest: elems.slice(3), - tlfName: elems[2] ?? '', - tlfType: T.FS.TlfType.Public, - ...splitTlfIntoReadersAndWriters(elems[2] ?? ''), - } - } - case 'team': - switch (elems.length) { - case 2: - return parsedPathTeamList - case 3: - return { - kind: T.FS.PathKind.TeamTlf, - team: elems[2]!, - tlfName: elems[2]!, - tlfType: T.FS.TlfType.Team, - } - default: - return { - kind: T.FS.PathKind.InTeamTlf, - rest: elems.slice(3), - team: elems[2] ?? '', - tlfName: elems[2] ?? '', - tlfType: T.FS.TlfType.Team, - } - } - default: - return parsedPathRoot - } -} - -export const rebasePathToDifferentTlf = (path: T.FS.Path, newTlfPath: T.FS.Path) => - T.FS.pathConcat(newTlfPath, T.FS.getPathElements(path).slice(3).join('/')) - -export const canChat = (path: T.FS.Path) => { - const parsedPath = parsePath(path) - switch (parsedPath.kind) { - case T.FS.PathKind.Root: - case T.FS.PathKind.TlfList: - return false - case T.FS.PathKind.GroupTlf: - case T.FS.PathKind.TeamTlf: - return true - case T.FS.PathKind.InGroupTlf: - case T.FS.PathKind.InTeamTlf: - return true - default: - return false - } -} - -export const isTeamPath = (path: T.FS.Path): boolean => { - const parsedPath = parsePath(path) - return parsedPath.kind !== T.FS.PathKind.Root && parsedPath.tlfType === T.FS.TlfType.Team -} - -export const getChatTarget = (path: T.FS.Path, me: string): string => { - const parsedPath = parsePath(path) - if (parsedPath.kind !== T.FS.PathKind.Root && parsedPath.tlfType === T.FS.TlfType.Team) { - return 'team conversation' - } - if (parsedPath.kind === T.FS.PathKind.GroupTlf || parsedPath.kind === T.FS.PathKind.InGroupTlf) { - if (parsedPath.writers.length === 1 && !parsedPath.readers && parsedPath.writers[0] === me) { - return 'yourself' - } - if (parsedPath.writers.length + (parsedPath.readers ? parsedPath.readers.length : 0) === 2) { - const notMe = parsedPath.writers.concat(parsedPath.readers || []).filter(u => u !== me) - if (notMe.length === 1) { - return notMe[0]! - } - } - return 'group conversation' - } - return 'conversation' -} - -export const getSharePathArrayDescription = (paths: ReadonlyArray): string => { - return !paths.length ? '' : paths.length === 1 ? T.FS.getPathName(paths[0]) : `${paths.length} items` -} - -export const getDestinationPickerPathName = (picker: T.FS.DestinationPicker): string => - picker.source.type === T.FS.DestinationPickerSource.MoveOrCopy - ? T.FS.getPathName(picker.source.path) - : picker.source.type === T.FS.DestinationPickerSource.IncomingShare - ? getSharePathArrayDescription( - picker.source.source - .map(({originalPath}) => (originalPath ? T.FS.getLocalPathName(originalPath) : '')) - .filter(Boolean) - ) - : '' - -const isPathEnabledForSync = (syncConfig: T.FS.TlfSyncConfig, path: T.FS.Path): boolean => { - switch (syncConfig.mode) { - case T.FS.TlfSyncMode.Disabled: - return false - case T.FS.TlfSyncMode.Enabled: - return true - case T.FS.TlfSyncMode.Partial: - // TODO: when we enable partial sync lookup, remember to deal with - // potential ".." traversal as well. - return syncConfig.enabledPaths.includes(path) - default: - return false - } -} - -export const getUploadIconForTlfType = ( - kbfsDaemonStatus: T.FS.KbfsDaemonStatus, - uploads: T.FS.Uploads, - tlfList: T.FS.TlfList, - tlfType: T.FS.TlfType -): T.FS.UploadIcon | undefined => { - if ( - [...tlfList].some( - ([_, tlf]) => - tlf.conflictState.type === T.FS.ConflictStateType.NormalView && tlf.conflictState.stuckInConflict - ) - ) { - return T.FS.UploadIcon.UploadingStuck - } - - const prefix = T.FS.pathToString(T.FS.getTlfTypePathFromTlfType(tlfType)) - if ( - [...uploads.syncingPaths].some(p => T.FS.pathToString(p).startsWith(prefix)) || - [...uploads.writingToJournal.keys()].some(p => T.FS.pathToString(p).startsWith(prefix)) - ) { - return kbfsDaemonStatus.onlineStatus === T.FS.KbfsDaemonOnlineStatus.Offline - ? T.FS.UploadIcon.AwaitingToUpload - : T.FS.UploadIcon.Uploading - } - - return undefined -} - -export const tlfIsStuckInConflict = (tlf: T.FS.Tlf) => - tlf.conflictState.type === T.FS.ConflictStateType.NormalView && tlf.conflictState.stuckInConflict - -export const getPathStatusIconInMergeProps = ( - kbfsDaemonStatus: T.FS.KbfsDaemonStatus, - tlf: T.Immutable, - pathItem: T.Immutable, - uploadingPaths: T.Immutable>, - path: T.Immutable -): T.FS.PathStatusIcon => { - // There's no upload or sync for local conflict view. - if (tlf.conflictState.type === T.FS.ConflictStateType.ManualResolvingLocalView) { - return T.FS.LocalConflictStatus - } - - // uploading state has higher priority - if (uploadingPaths.has(path)) { - // eslint-disable-next-line - return tlf.conflictState.type === T.FS.ConflictStateType.NormalView && tlf.conflictState.stuckInConflict - ? T.FS.UploadIcon.UploadingStuck - : kbfsDaemonStatus.onlineStatus === T.FS.KbfsDaemonOnlineStatus.Offline - ? T.FS.UploadIcon.AwaitingToUpload - : T.FS.UploadIcon.Uploading - } - if (!isPathEnabledForSync(tlf.syncConfig, path)) { - return T.FS.NonUploadStaticSyncStatus.OnlineOnly - } - - if (pathItem === unknownPathItem && tlf.syncConfig.mode !== T.FS.TlfSyncMode.Disabled) { - return T.FS.NonUploadStaticSyncStatus.Unknown - } - - // TODO: what about 'sync-error'? - - // We don't have an upload state, and sync is enabled for this path. - switch (pathItem.prefetchStatus.state) { - case T.FS.PrefetchState.NotStarted: - return T.FS.NonUploadStaticSyncStatus.AwaitingToSync - case T.FS.PrefetchState.Complete: - return T.FS.NonUploadStaticSyncStatus.Synced - case T.FS.PrefetchState.InProgress: { - if (kbfsDaemonStatus.onlineStatus === T.FS.KbfsDaemonOnlineStatus.Offline) { - return T.FS.NonUploadStaticSyncStatus.AwaitingToSync - } - const inProgress: T.FS.PrefetchInProgress = pathItem.prefetchStatus - if (inProgress.bytesTotal === 0) { - return T.FS.NonUploadStaticSyncStatus.AwaitingToSync - } - return inProgress.bytesFetched / inProgress.bytesTotal - } - default: - return T.FS.NonUploadStaticSyncStatus.Unknown - } -} - export const makeActionsForDestinationPickerOpen = (index: number, path: T.FS.Path) => { useFSState.getState().dispatch.setDestinationPickerParentPath(index, path) navigateAppend({props: {index}, selected: 'destinationPicker'}) } -export const fsRootRouteForNav1 = isMobile ? [Tabs.settingsTab, settingsFsTab] : [Tabs.fsTab] - -export const getMainBannerType = ( - kbfsDaemonStatus: T.FS.KbfsDaemonStatus, - overallSyncStatus: T.FS.OverallSyncStatus -): T.FS.MainBannerType => { - if (kbfsDaemonStatus.onlineStatus === T.FS.KbfsDaemonOnlineStatus.Offline) { - return T.FS.MainBannerType.Offline - } else if (kbfsDaemonStatus.onlineStatus === T.FS.KbfsDaemonOnlineStatus.Trying) { - return T.FS.MainBannerType.TryingToConnect - } else if (overallSyncStatus.diskSpaceStatus === T.FS.DiskSpaceStatus.Error) { - return T.FS.MainBannerType.OutOfSpace - } else { - return T.FS.MainBannerType.None - } -} - -export const isFolder = (path: T.FS.Path, pathItem: T.FS.PathItem) => - T.FS.getPathLevel(path) <= 3 || pathItem.type === T.FS.PathType.Folder - -export const isInTlf = (path: T.FS.Path) => T.FS.getPathLevel(path) > 2 - -export const humanizeBytes = (n: number, numDecimals: number): string => { - const kb = 1024 - const mb = kb * 1024 - const gb = mb * 1024 - - if (n < kb) { - return `${n} bytes` - } else if (n < mb) { - return `${(n / kb).toFixed(numDecimals)} KB` - } else if (n < gb) { - return `${(n / mb).toFixed(numDecimals)} MB` - } - return `${(n / gb).toFixed(numDecimals)} GB` -} - -export const humanizeBytesOfTotal = (n: number, d: number): string => { - const kb = 1024 - const mb = kb * 1024 - const gb = mb * 1024 - - if (d < kb) { - return `${n} of ${d} bytes` - } else if (d < mb) { - return `${(n / kb).toFixed(2)} of ${(d / kb).toFixed(2)} KB` - } else if (d < gb) { - return `${(n / mb).toFixed(2)} of ${(d / mb).toFixed(2)} MB` - } - return `${(n / gb).toFixed(2)} of ${(d / gb).toFixed(2)} GB` -} - -export const hasPublicTag = (path: T.FS.Path): boolean => { - const publicPrefix = '/keybase/public/' - // The slash after public in `publicPrefix` prevents /keybase/public from counting. - return T.FS.pathToString(path).startsWith(publicPrefix) -} - -export const getPathUserSetting = ( - pathUserSettings: T.Immutable>, - path: T.Immutable -): T.FS.PathUserSetting => - pathUserSettings.get(path) || - (T.FS.getPathLevel(path) < 3 ? defaultTlfListPathUserSetting : defaultPathUserSetting) - -export const showSortSetting = ( - path: T.FS.Path, - pathItem: T.FS.PathItem, - kbfsDaemonStatus: T.FS.KbfsDaemonStatus -) => - !isMobile && - path !== defaultPath && - (T.FS.getPathLevel(path) === 2 || (pathItem.type === T.FS.PathType.Folder && !!pathItem.children.size)) && - !isOfflineUnsynced(kbfsDaemonStatus, pathItem, path) - -export const getSoftError = (softErrors: T.FS.SoftErrors, path: T.FS.Path): T.FS.SoftError | undefined => { - const pathError = softErrors.pathErrors.get(path) - if (pathError) { - return pathError - } - if (!softErrors.tlfErrors.size) { - return undefined - } - const tlfPath = getTlfPath(path) - return (tlfPath && softErrors.tlfErrors.get(tlfPath)) || undefined -} - -export const hasSpecialFileElement = (path: T.FS.Path): boolean => - T.FS.getPathElements(path).some(elem => elem.startsWith('.kbfs')) - -export const sfmiInfoLoaded = (settings: T.FS.Settings, driverStatus: T.FS.DriverStatus): boolean => - settings.loaded && driverStatus !== driverStatusUnknown - -// This isn't perfect since it doesn't cover the case of multi-writer public -// TLFs or where a team TLF is readonly to the user. But to do that we'd need -// some new caching in KBFS to plumb it into the Tlfs structure without -// awful overhead. -export const hideOrDisableInDestinationPicker = ( - tlfType: T.FS.TlfType, - name: string, - username: string, - destinationPickerIndex?: number -) => typeof destinationPickerIndex === 'number' && tlfType === T.FS.TlfType.Public && name !== username - const noAccessErrorCodes: Array = [ T.RPCGen.StatusCode.scsimplefsnoaccess, T.RPCGen.StatusCode.scteamnotfound, @@ -1021,7 +276,7 @@ export const errorToActionOrThrow = (error: unknown, path?: T.FS.Path) => { return } if (path && code && noAccessErrorCodes.includes(code)) { - const tlfPath = getTlfPath(path) + const tlfPath = Constants.getTlfPath(path) if (tlfPath) { useFSState.getState().dispatch.setTlfSoftError(tlfPath, T.FS.SoftError.NoAccess) return @@ -1076,17 +331,17 @@ const initialStore: Store = { errors: [], fileContext: new Map(), folderViewFilter: undefined, - kbfsDaemonStatus: unknownKbfsDaemonStatus, + kbfsDaemonStatus: Constants.unknownKbfsDaemonStatus, lastPublicBannerClosedTlf: '', - overallSyncStatus: emptyOverallSyncStatus, + overallSyncStatus: Constants.emptyOverallSyncStatus, pathInfos: new Map(), - pathItemActionMenu: emptyPathItemActionMenu, + pathItemActionMenu: Constants.emptyPathItemActionMenu, pathItems: new Map(), pathUserSettings: new Map(), - settings: emptySettings, + settings: Constants.emptySettings, sfmi: { directMountDir: '', - driverStatus: defaultDriverStatus, + driverStatus: Constants.defaultDriverStatus, preferredMountDirs: [], }, softErrors: { @@ -1124,7 +379,7 @@ export interface State extends Store { driverDisabling: () => void driverEnable: (isRetry?: boolean) => void driverKextPermissionError: () => void - dynamic: { + defer: { afterDriverDisable?: () => void afterDriverDisabling?: () => void afterDriverEnabled?: (isRetry: boolean) => void @@ -1145,6 +400,8 @@ export interface State extends Store { refreshMountDirsDesktop?: () => void setSfmiBannerDismissedDesktop?: (dismissed: boolean) => void uploadFromDragAndDropDesktop?: (parentPath: T.FS.Path, localPaths: Array) => void + onBadgeApp?: (key: 'kbfsUploading' | 'outOfSpace', on: boolean) => void + onSetBadgeCounts?: (counts: Map) => void } editError: (editID: T.FS.EditID, error: string) => void editSuccess: (editID: T.FS.EditID) => void @@ -1200,7 +457,6 @@ export interface State extends Store { setTlfsAsUnloaded: () => void setTlfSyncConfig: (tlfPath: T.FS.Path, enabled: boolean) => void setSorting: (path: T.FS.Path, sortSetting: T.FS.SortSetting) => void - setupSubscriptions: () => Promise showIncomingShare: (initialDestinationParentPath: T.FS.Path) => void showMoveOrCopy: (initialDestinationParentPath: T.FS.Path) => void startManualConflictResolution: (tlfPath: T.FS.Path) => void @@ -1218,13 +474,21 @@ export interface State extends Store { getUploadIconForFilesTab: () => T.FS.UploadIcon | undefined } +const emptyPrefetchInProgress: T.FS.PrefetchInProgress = { + bytesFetched: 0, + bytesTotal: 0, + endEstimate: 0, + startTime: 0, + state: T.FS.PrefetchState.InProgress, +} + const getPrefetchStatusFromRPC = ( prefetchStatus: T.RPCGen.PrefetchStatus, prefetchProgress: T.RPCGen.PrefetchProgress ) => { switch (prefetchStatus) { case T.RPCGen.PrefetchStatus.notStarted: - return prefetchNotStarted + return Constants.prefetchNotStarted case T.RPCGen.PrefetchStatus.inProgress: return { ...emptyPrefetchInProgress, @@ -1234,9 +498,9 @@ const getPrefetchStatusFromRPC = ( startTime: prefetchProgress.start, } case T.RPCGen.PrefetchStatus.complete: - return prefetchComplete + return Constants.prefetchComplete default: - return prefetchNotStarted + return Constants.prefetchNotStarted } } @@ -1253,21 +517,21 @@ const makeEntry = (d: T.RPCGen.Dirent, children?: Set): T.FS.PathItem => switch (d.direntType) { case T.RPCGen.DirentType.dir: return { - ...emptyFolder, + ...Constants.emptyFolder, ...direntToMetadata(d), children: new Set(children || []), progress: children ? T.FS.ProgressType.Loaded : T.FS.ProgressType.Pending, } as T.FS.PathItem case T.RPCGen.DirentType.sym: return { - ...emptySymlink, + ...Constants.emptySymlink, ...direntToMetadata(d), // TODO: plumb link target } as T.FS.PathItem case T.RPCGen.DirentType.file: case T.RPCGen.DirentType.exec: return { - ...emptyFile, + ...Constants.emptyFile, ...direntToMetadata(d), } as T.FS.PathItem } @@ -1379,7 +643,7 @@ export const useFSState = Z.createZustand((set, get) => { try { await T.RPCGen.SimpleFSSimpleFSOpenRpcPromise( { - dest: pathToRPCPath(T.FS.pathConcat(edit.parentPath, edit.name)), + dest: Constants.pathToRPCPath(T.FS.pathConcat(edit.parentPath, edit.name)), flags: T.RPCGen.OpenFlags.directory, opID: makeUUID(), }, @@ -1395,10 +659,10 @@ export const useFSState = Z.createZustand((set, get) => { try { const opID = makeUUID() await T.RPCGen.SimpleFSSimpleFSMoveRpcPromise({ - dest: pathToRPCPath(T.FS.pathConcat(edit.parentPath, edit.name)), + dest: Constants.pathToRPCPath(T.FS.pathConcat(edit.parentPath, edit.name)), opID, overwriteExistingFiles: false, - src: pathToRPCPath(T.FS.pathConcat(edit.parentPath, edit.originalName)), + src: Constants.pathToRPCPath(T.FS.pathConcat(edit.parentPath, edit.originalName)), }) await T.RPCGen.SimpleFSSimpleFSWaitRpcPromise({opID}, S.waitingKeyFSCommitEdit) get().dispatch.editSuccess(editID) @@ -1422,13 +686,37 @@ export const useFSState = Z.createZustand((set, get) => { } ignorePromise(f()) }, + defer: { + afterDriverDisable: undefined, + afterDriverDisabling: undefined, + afterDriverEnabled: undefined, + afterKbfsDaemonRpcStatusChanged: undefined, + finishedDownloadWithIntentMobile: undefined, + finishedRegularDownloadMobile: undefined, + onBadgeApp: () => { + throw new Error('onBadgeApp not implemented') + }, + onSetBadgeCounts: () => { + throw new Error('onSetBadgeCounts not implemented') + }, + openAndUploadDesktop: undefined, + openFilesFromWidgetDesktop: undefined, + openLocalPathInSystemFileManagerDesktop: undefined, + openPathInSystemFileManagerDesktop: undefined, + openSecurityPreferencesDesktop: undefined, + pickAndUploadMobile: undefined, + refreshDriverStatusDesktop: undefined, + refreshMountDirsDesktop: undefined, + setSfmiBannerDismissedDesktop: undefined, + uploadFromDragAndDropDesktop: undefined, + }, deleteFile: path => { const f = async () => { const opID = makeUUID() try { await T.RPCGen.SimpleFSSimpleFSRemoveRpcPromise({ opID, - path: pathToRPCPath(path), + path: Constants.pathToRPCPath(path), recursive: true, }) await T.RPCGen.SimpleFSSimpleFSWaitRpcPromise({opID}) @@ -1467,7 +755,7 @@ export const useFSState = Z.createZustand((set, get) => { await requestPermissionsToWrite() const downloadID = await T.RPCGen.SimpleFSSimpleFSStartDownloadRpcPromise({ isRegularDownload: type === 'download', - path: pathToRPCPath(path).kbfs, + path: Constants.pathToRPCPath(path).kbfs, }) if (type !== 'download') { get().dispatch.setPathItemActionMenuDownload( @@ -1479,7 +767,7 @@ export const useFSState = Z.createZustand((set, get) => { ignorePromise(f()) }, driverDisable: () => { - get().dispatch.dynamic.afterDriverDisable?.() + get().dispatch.defer.afterDriverDisable?.() }, driverDisabling: () => { set(s => { @@ -1487,7 +775,7 @@ export const useFSState = Z.createZustand((set, get) => { s.sfmi.driverStatus.isDisabling = true } }) - get().dispatch.dynamic.afterDriverDisabling?.() + get().dispatch.defer.afterDriverDisabling?.() }, driverEnable: isRetry => { set(s => { @@ -1495,7 +783,7 @@ export const useFSState = Z.createZustand((set, get) => { s.sfmi.driverStatus.isEnabling = true } }) - get().dispatch.dynamic.afterDriverEnabled?.(!!isRetry) + get().dispatch.defer.afterDriverEnabled?.(!!isRetry) }, driverKextPermissionError: () => { set(s => { @@ -1505,24 +793,6 @@ export const useFSState = Z.createZustand((set, get) => { } }) }, - dynamic: { - afterDriverDisable: undefined, - afterDriverDisabling: undefined, - afterDriverEnabled: undefined, - afterKbfsDaemonRpcStatusChanged: undefined, - finishedDownloadWithIntentMobile: undefined, - finishedRegularDownloadMobile: undefined, - openAndUploadDesktop: undefined, - openFilesFromWidgetDesktop: undefined, - openLocalPathInSystemFileManagerDesktop: undefined, - openPathInSystemFileManagerDesktop: undefined, - openSecurityPreferencesDesktop: undefined, - pickAndUploadMobile: undefined, - refreshDriverStatusDesktop: undefined, - refreshMountDirsDesktop: undefined, - setSfmiBannerDismissedDesktop: undefined, - uploadFromDragAndDropDesktop: undefined, - }, editError: (editID, error) => { set(s => { const e = s.edits.get(editID) @@ -1554,7 +824,7 @@ export const useFSState = Z.createZustand((set, get) => { s.tlfs[visibility].set( elems[2] ?? '', T.castDraft({ - ...(s.tlfs[visibility].get(elems[2] ?? '') || unknownTlf), + ...(s.tlfs[visibility].get(elems[2] ?? '') || Constants.unknownTlf), isIgnored: false, }) ) @@ -1571,7 +841,7 @@ export const useFSState = Z.createZustand((set, get) => { s.tlfs[visibility].set( elems[2] ?? '', T.castDraft({ - ...(s.tlfs[visibility].get(elems[2] ?? '') || unknownTlf), + ...(s.tlfs[visibility].get(elems[2] ?? '') || Constants.unknownTlf), isIgnored: true, }) ) @@ -1581,7 +851,7 @@ export const useFSState = Z.createZustand((set, get) => { favoritesLoad: () => { const f = async () => { try { - if (!storeRegistry.getState('config').loggedIn) { + if (!useConfigState.getState().loggedIn) { return } const results = await T.RPCGen.SimpleFSSimpleFSListFavoritesRpcPromise() @@ -1606,7 +876,7 @@ export const useFSState = Z.createZustand((set, get) => { const tlfType = rpcFolderTypeToTlfType(folder.folderType) const tlfName = tlfType === T.FS.TlfType.Private || tlfType === T.FS.TlfType.Public - ? tlfToPreferredOrder(folder.name, storeRegistry.getState('current-user').username) + ? tlfToPreferredOrder(folder.name, useCurrentUserState.getState().username) : folder.name tlfType && payload[tlfType].set( @@ -1634,8 +904,8 @@ export const useFSState = Z.createZustand((set, get) => { s.tlfs.loaded = true }) const counts = new Map() - counts.set(Tabs.fsTab, computeBadgeNumberForAll(get().tlfs)) - storeRegistry.getState('notifications').dispatch.setBadgeCounts(counts) + counts.set(Tabs.fsTab, Constants.computeBadgeNumberForAll(get().tlfs)) + get().dispatch.defer.onSetBadgeCounts?.(counts) } } catch (e) { errorToActionOrThrow(e) @@ -1647,7 +917,7 @@ export const useFSState = Z.createZustand((set, get) => { finishManualConflictResolution: localViewTlfPath => { const f = async () => { await T.RPCGen.SimpleFSSimpleFSFinishResolvingConflictRpcPromise({ - path: pathToRPCPath(localViewTlfPath), + path: Constants.pathToRPCPath(localViewTlfPath), }) get().dispatch.favoritesLoad() } @@ -1662,14 +932,14 @@ export const useFSState = Z.createZustand((set, get) => { depth: 1, filter: T.RPCGen.ListFilter.filterSystemHidden, opID, - path: pathToRPCPath(rootPath), + path: Constants.pathToRPCPath(rootPath), refreshSubscription: false, }) } else { await T.RPCGen.SimpleFSSimpleFSListRpcPromise({ filter: T.RPCGen.ListFilter.filterSystemHidden, opID, - path: pathToRPCPath(rootPath), + path: Constants.pathToRPCPath(rootPath), refreshSubscription: false, }) } @@ -1719,11 +989,11 @@ export const useFSState = Z.createZustand((set, get) => { // Get metadata fields of the directory that we just loaded from state to // avoid overriding them. - const rootPathItem = getPathItem(get().pathItems, rootPath) + const rootPathItem = Constants.getPathItem(get().pathItems, rootPath) const rootFolder: T.FS.FolderPathItem = { ...(rootPathItem.type === T.FS.PathType.Folder ? rootPathItem - : {...emptyFolder, name: T.FS.getPathName(rootPath)}), + : {...Constants.emptyFolder, name: T.FS.getPathName(rootPath)}), children: new Set(childMap.get(rootPath)), progress: T.FS.ProgressType.Loaded, } @@ -1734,7 +1004,7 @@ export const useFSState = Z.createZustand((set, get) => { ] as const) set(s => { pathItems.forEach((pathItemFromAction, path) => { - const oldPathItem = getPathItem(s.pathItems, path) + const oldPathItem = Constants.getPathItem(s.pathItems, path) const newPathItem = updatePathItem(oldPathItem, pathItemFromAction) oldPathItem.type === T.FS.PathType.Folder && oldPathItem.children.forEach( @@ -1751,7 +1021,7 @@ export const useFSState = Z.createZustand((set, get) => { if (edit.type !== T.FS.EditType.Rename) { return true } - const parent = getPathItem(s.pathItems, edit.parentPath) + const parent = Constants.getPathItem(s.pathItems, edit.parentPath) if (parent.type === T.FS.PathType.Folder && parent.children.has(edit.name)) { return true } @@ -1878,7 +1148,7 @@ export const useFSState = Z.createZustand((set, get) => { } subscribeAndLoadJournalStatus() // how this works isn't great. This function gets called way early before we set this - get().dispatch.dynamic.afterKbfsDaemonRpcStatusChanged?.() + get().dispatch.defer.afterKbfsDaemonRpcStatusChanged?.() }, letResetUserBackIn: (id, username) => { const f = async () => { @@ -1898,12 +1168,12 @@ export const useFSState = Z.createZustand((set, get) => { } try { const {folder, isFavorite, isIgnored, isNew} = await T.RPCGen.SimpleFSSimpleFSGetFolderRpcPromise({ - path: pathToRPCPath(tlfPath).kbfs, + path: Constants.pathToRPCPath(tlfPath).kbfs, }) const tlfType = rpcFolderTypeToTlfType(folder.folderType) const tlfName = tlfType === T.FS.TlfType.Private || tlfType === T.FS.TlfType.Public - ? tlfToPreferredOrder(folder.name, storeRegistry.getState('current-user').username) + ? tlfToPreferredOrder(folder.name, useCurrentUserState.getState().username) : folder.name if (tlfType) { @@ -2005,7 +1275,7 @@ export const useFSState = Z.createZustand((set, get) => { const f = async () => { try { const res = await T.RPCGen.SimpleFSSimpleFSGetGUIFileContextRpcPromise({ - path: pathToRPCPath(path).kbfs, + path: Constants.pathToRPCPath(path).kbfs, }) set(s => { @@ -2058,7 +1328,7 @@ export const useFSState = Z.createZustand((set, get) => { try { const dirent = await T.RPCGen.SimpleFSSimpleFSStatRpcPromise( { - path: pathToRPCPath(path), + path: Constants.pathToRPCPath(path), refreshSubscription: false, }, S.waitingKeyFSStat @@ -2066,7 +1336,7 @@ export const useFSState = Z.createZustand((set, get) => { const pathItem = makeEntry(dirent) set(s => { - const oldPathItem = getPathItem(s.pathItems, path) + const oldPathItem = Constants.getPathItem(s.pathItems, path) s.pathItems.set(path, T.castDraft(updatePathItem(oldPathItem, pathItem))) s.softErrors.pathErrors.delete(path) s.softErrors.tlfErrors.delete(path) @@ -2103,13 +1373,13 @@ export const useFSState = Z.createZustand((set, get) => { }, loadTlfSyncConfig: tlfPath => { const f = async () => { - const parsedPath = parsePath(tlfPath) + const parsedPath = Constants.parsePath(tlfPath) if (parsedPath.kind !== T.FS.PathKind.GroupTlf && parsedPath.kind !== T.FS.PathKind.TeamTlf) { return } try { const result = await T.RPCGen.SimpleFSSimpleFSFolderSyncConfigAndStatusRpcPromise({ - path: pathToRPCPath(tlfPath), + path: Constants.pathToRPCPath(tlfPath), }) const syncConfig = getSyncConfigFromRPC(parsedPath.tlfName, parsedPath.tlfType, result.config) const tlfName = parsedPath.tlfName @@ -2117,17 +1387,17 @@ export const useFSState = Z.createZustand((set, get) => { set(s => { const oldTlfList = s.tlfs[tlfType] - const oldTlfFromFavorites = oldTlfList.get(tlfName) || unknownTlf - if (oldTlfFromFavorites !== unknownTlf) { + const oldTlfFromFavorites = oldTlfList.get(tlfName) || Constants.unknownTlf + if (oldTlfFromFavorites !== Constants.unknownTlf) { s.tlfs[tlfType] = T.castDraft( new Map([...oldTlfList, [tlfName, {...oldTlfFromFavorites, syncConfig}]]) ) return } - const tlfPath = T.FS.pathConcat(T.FS.pathConcat(defaultPath, tlfType), tlfName) - const oldTlfFromAdditional = s.tlfs.additionalTlfs.get(tlfPath) || unknownTlf - if (oldTlfFromAdditional !== unknownTlf) { + const tlfPath = T.FS.pathConcat(T.FS.pathConcat(Constants.defaultPath, tlfType), tlfName) + const oldTlfFromAdditional = s.tlfs.additionalTlfs.get(tlfPath) || Constants.unknownTlf + if (oldTlfFromAdditional !== Constants.unknownTlf) { s.tlfs.additionalTlfs = T.castDraft( new Map([...s.tlfs.additionalTlfs, [tlfPath, {...oldTlfFromAdditional, syncConfig}]]) ) @@ -2189,7 +1459,7 @@ export const useFSState = Z.createZustand((set, get) => { zState.destinationPicker.source.type === T.FS.DestinationPickerSource.MoveOrCopy ? [ { - dest: pathToRPCPath( + dest: Constants.pathToRPCPath( T.FS.pathConcat( destinationParentPath, T.FS.getPathName(zState.destinationPicker.source.path) @@ -2197,14 +1467,14 @@ export const useFSState = Z.createZustand((set, get) => { ), opID: makeUUID(), overwriteExistingFiles: false, - src: pathToRPCPath(zState.destinationPicker.source.path), + src: Constants.pathToRPCPath(zState.destinationPicker.source.path), }, ] : zState.destinationPicker.source.source .map(item => ({originalPath: item.originalPath ?? '', scaledPath: item.scaledPath})) .filter(({originalPath}) => !!originalPath) .map(({originalPath, scaledPath}) => ({ - dest: pathToRPCPath( + dest: Constants.pathToRPCPath( T.FS.pathConcat( destinationParentPath, T.FS.getLocalPathName(originalPath) @@ -2216,7 +1486,7 @@ export const useFSState = Z.createZustand((set, get) => { src: { PathType: T.RPCGen.PathType.local, local: T.FS.getNormalizedLocalPath( - storeRegistry.getState('config').incomingShareUseOriginal + useConfigState.getState().incomingShareUseOriginal ? originalPath : scaledPath || originalPath ), @@ -2241,7 +1511,7 @@ export const useFSState = Z.createZustand((set, get) => { ignorePromise(f()) }, newFolderRow: parentPath => { - const parentPathItem = getPathItem(get().pathItems, parentPath) + const parentPathItem = Constants.getPathItem(get().pathItems, parentPath) if (parentPathItem.type !== T.FS.PathType.Folder) { console.warn(`bad parentPath: ${parentPathItem.type}`) return @@ -2258,7 +1528,7 @@ export const useFSState = Z.createZustand((set, get) => { set(s => { s.edits.set(makeEditID(), { - ...emptyNewFolder, + ...Constants.emptyNewFolder, name: newFolderName, originalName: newFolderName, parentPath, @@ -2375,12 +1645,12 @@ export const useFSState = Z.createZustand((set, get) => { if (totalSyncingBytes <= 0 && !syncingPaths?.length) { break } - storeRegistry.getState('notifications').dispatch.badgeApp('kbfsUploading', true) + get().dispatch.defer.onBadgeApp?.('kbfsUploading', true) await timeoutPromise(getWaitDuration(endEstimate || undefined, 100, 4000)) // 0.1s to 4s } } finally { pollJournalStatusPolling = false - storeRegistry.getState('notifications').dispatch.badgeApp('kbfsUploading', false) + get().dispatch.defer.onBadgeApp?.('kbfsUploading', false) get().dispatch.checkKbfsDaemonRpcStatus() } } @@ -2423,7 +1693,7 @@ export const useFSState = Z.createZustand((set, get) => { set(s => { s.sfmi.driverStatus = driverStatus }) - get().dispatch.dynamic.refreshMountDirsDesktop?.() + get().dispatch.defer.refreshMountDirsDesktop?.() }, setEditName: (editID, name) => { set(s => { @@ -2488,7 +1758,7 @@ export const useFSState = Z.createZustand((set, get) => { if (old) { old.sort = sortSetting } else { - s.pathUserSettings.set(path, {...defaultPathUserSetting, sort: sortSetting}) + s.pathUserSettings.set(path, {...Constants.defaultPathUserSetting, sort: sortSetting}) } }) }, @@ -2515,7 +1785,7 @@ export const useFSState = Z.createZustand((set, get) => { await T.RPCGen.SimpleFSSimpleFSSetFolderSyncConfigRpcPromise( { config: {mode: enabled ? T.RPCGen.FolderSyncMode.enabled : T.RPCGen.FolderSyncMode.disabled}, - path: pathToRPCPath(tlfPath), + path: Constants.pathToRPCPath(tlfPath), }, S.waitingKeyFSSyncToggle ) @@ -2528,10 +1798,6 @@ export const useFSState = Z.createZustand((set, get) => { s.tlfs.loaded = false }) }, - setupSubscriptions: async () => { - const initPlatformSpecific = await import('./platform-specific') - initPlatformSpecific.default() - }, showIncomingShare: initialDestinationParentPath => { set(s => { if (s.destinationPicker.source.type !== T.FS.DestinationPickerSource.IncomingShare) { @@ -2539,9 +1805,7 @@ export const useFSState = Z.createZustand((set, get) => { } s.destinationPicker.destinationParentPath = [initialDestinationParentPath] }) - storeRegistry - .getState('router') - .dispatch.navigateAppend({props: {index: 0}, selected: 'destinationPicker'}) + navigateAppend({props: {index: 0}, selected: 'destinationPicker'}) }, showMoveOrCopy: initialDestinationParentPath => { set(s => { @@ -2549,21 +1813,19 @@ export const useFSState = Z.createZustand((set, get) => { s.destinationPicker.source.type === T.FS.DestinationPickerSource.MoveOrCopy ? s.destinationPicker.source : { - path: defaultPath, + path: Constants.defaultPath, type: T.FS.DestinationPickerSource.MoveOrCopy, } s.destinationPicker.destinationParentPath = [initialDestinationParentPath] }) - storeRegistry - .getState('router') - .dispatch.navigateAppend({props: {index: 0}, selected: 'destinationPicker'}) + navigateAppend({props: {index: 0}, selected: 'destinationPicker'}) }, startManualConflictResolution: tlfPath => { const f = async () => { await T.RPCGen.SimpleFSSimpleFSClearConflictStateRpcPromise({ - path: pathToRPCPath(tlfPath), + path: Constants.pathToRPCPath(tlfPath), }) get().dispatch.favoritesLoad() } @@ -2641,12 +1903,12 @@ export const useFSState = Z.createZustand((set, get) => { body: 'You are out of disk space. Some folders could not be synced.', sound: true, }) - storeRegistry.getState('notifications').dispatch.badgeApp('outOfSpace', status.outOfSyncSpace) + get().dispatch.defer.onBadgeApp?.('outOfSpace', status.outOfSyncSpace) break } case T.FS.DiskSpaceStatus.Warning: { - const threshold = humanizeBytes(get().settings.spaceAvailableNotificationThreshold, 0) + const threshold = Constants.humanizeBytes(get().settings.spaceAvailableNotificationThreshold, 0) NotifyPopup('Disk Space Low', { body: `You have less than ${threshold} of storage space left.`, }) @@ -2682,7 +1944,7 @@ export const useFSState = Z.createZustand((set, get) => { try { await T.RPCGen.SimpleFSSimpleFSStartUploadRpcPromise({ sourceLocalPath: T.FS.getNormalizedLocalPath(localPath), - targetParentPath: pathToRPCPath(parentPath).kbfs, + targetParentPath: Constants.pathToRPCPath(parentPath).kbfs, }) } catch (err) { errorToActionOrThrow(err) diff --git a/shared/constants/git/index.tsx b/shared/stores/git.tsx similarity index 91% rename from shared/constants/git/index.tsx rename to shared/stores/git.tsx index ffe070ae3f3f..6e7854c3ab9c 100644 --- a/shared/constants/git/index.tsx +++ b/shared/stores/git.tsx @@ -1,11 +1,18 @@ -import * as S from '../strings' -import * as T from '../types' -import {ignorePromise} from '../utils' +import * as S from '@/constants/strings' +import * as T from '@/constants/types' +import {ignorePromise} from '@/constants/utils' import * as EngineGen from '@/actions/engine-gen-gen' import * as dateFns from 'date-fns' import * as Z from '@/util/zustand' import debounce from 'lodash/debounce' -import {storeRegistry} from '../store-registry' +import {navigateAppend} from '@/constants/router2' +import {useConfigState} from '@/stores/config' + +type Store = T.Immutable<{ + readonly error?: Error + readonly idToInfo: Map + readonly isNew?: Set +}> const parseRepos = (results: ReadonlyArray) => { const errors: Array = [] @@ -56,13 +63,13 @@ const parseRepoError = (result: T.RPCGen.GitRepoResult): Error => { return new Error(`Git repo error: ${errStr}`) } -const initialStore: T.Git.State = { +const initialStore: Store = { error: undefined, idToInfo: new Map(), isNew: new Set(), } -export interface State extends T.Git.State { +export interface State extends Store { dispatch: { setError: (err?: Error) => void clearBadges: () => void @@ -105,7 +112,7 @@ export const useGitState = Z.createZustand((set, get) => { async () => { const results = await T.RPCGen.gitGetAllGitMetadataRpcPromise(undefined, S.waitingKeyGitLoading) const {errors, repos} = parseRepos(results || []) - const {setGlobalError} = storeRegistry.getState('config').dispatch + const {setGlobalError} = useConfigState.getState().dispatch errors.forEach(e => setGlobalError(e)) set(s => { s.idToInfo = repos @@ -151,9 +158,7 @@ export const useGitState = Z.createZustand((set, get) => { await _load() for (const [, info] of get().idToInfo) { if (info.repoID === repoID && info.teamname === teamname) { - storeRegistry - .getState('router') - .dispatch.navigateAppend({props: {expanded: info.id}, selected: 'gitRoot'}) + navigateAppend({props: {expanded: info.id}, selected: 'gitRoot'}) break } } diff --git a/shared/constants/logout/index.tsx b/shared/stores/logout.tsx similarity index 88% rename from shared/constants/logout/index.tsx rename to shared/stores/logout.tsx index c1e85a767cad..1348cca9b47b 100644 --- a/shared/constants/logout/index.tsx +++ b/shared/stores/logout.tsx @@ -1,13 +1,14 @@ import logger from '@/logger' -import {ignorePromise, timeoutPromise} from '../utils' +import {ignorePromise, timeoutPromise} from '@/constants/utils' import * as T from '@/constants/types' // normally util.container but it re-exports from us so break the cycle import * as Z from '@/util/zustand' -import {settingsPasswordTab} from '../settings' -import {navigateAppend} from '../router2/util' -import {isMobile} from '../platform' -import * as Tabs from '../tabs' +import {settingsPasswordTab} from '@/constants/settings' +import {navigateAppend} from '@/constants/router2' +import {isMobile} from '@/constants/platform' +import * as Tabs from '@/constants/tabs' +// This store has no dependencies on other stores and is safe to import directly from other stores. type Store = T.Immutable<{ waiters: Map // if we ever restart handshake up this so we can ignore any waiters for old things diff --git a/shared/constants/notifications/index.tsx b/shared/stores/notifications.tsx similarity index 89% rename from shared/constants/notifications/index.tsx rename to shared/stores/notifications.tsx index 6d9881b80fa1..ba83285d4302 100644 --- a/shared/constants/notifications/index.tsx +++ b/shared/stores/notifications.tsx @@ -1,10 +1,11 @@ import * as Z from '@/util/zustand' import * as EngineGen from '@/actions/engine-gen-gen' -import type * as T from '../types' -import {isMobile} from '../platform' +import type * as T from '@/constants/types' +import {isMobile} from '@/constants/platform' import isEqual from 'lodash/isEqual' -import * as Tabs from '../tabs' -import {storeRegistry} from '../store-registry' +import * as Tabs from '@/constants/tabs' +import {useConfigState} from '@/stores/config' +import {useCurrentUserState} from '@/stores/current-user' export type BadgeType = 'regular' | 'update' | 'error' | 'uploading' export type NotificationKeys = 'kbfsUploading' | 'outOfSpace' @@ -28,6 +29,9 @@ const initialStore: Store = { export interface State extends Store { dispatch: { + defer: { + onFavoritesLoad?: () => void + } onEngineIncomingImpl: (action: EngineGen.Actions) => void resetState: 'default' badgeApp: (key: NotificationKeys, on: boolean) => void @@ -67,7 +71,7 @@ const badgeStateToBadgeCounts = (bs: T.RPCGen.BadgeState) => { revokedDevices.forEach(d => allDeviceChanges.add(d)) // don't see badges related to this device - const deviceID = storeRegistry.getState('current-user').deviceID + const deviceID = useCurrentUserState.getState().deviceID counts.set(Tabs.devicesTab, allDeviceChanges.size - (allDeviceChanges.has(deviceID) ? 1 : 0)) counts.set(Tabs.chatTab, bs.smallTeamBadgeCount + bs.bigTeamBadgeCount) counts.set(Tabs.gitTab, newGitRepoGlobalUniqueIDs.length) @@ -99,19 +103,24 @@ export const useNotifState = Z.createZustand((set, get) => { updateWidgetBadge(s) }) }, + defer: { + onFavoritesLoad: () => { + throw new Error('onFavoritesLoad not implemented') + }, + }, onEngineIncomingImpl: action => { switch (action.type) { case EngineGen.keybase1NotifyAuditRootAuditError: - storeRegistry - .getState('config') + useConfigState + .getState() .dispatch.setGlobalError( new Error(`Keybase is buggy, please report this: ${action.payload.params.message}`) ) break case EngineGen.keybase1NotifyAuditBoxAuditError: - storeRegistry - .getState('config') + useConfigState + .getState() .dispatch.setGlobalError( new Error( `Keybase had a problem loading a team, please report this with \`keybase log send\`: ${action.payload.params.message}` @@ -120,11 +129,11 @@ export const useNotifState = Z.createZustand((set, get) => { break case EngineGen.keybase1NotifyBadgesBadgeState: { const badgeState = action.payload.params.badgeState - storeRegistry.getState('config').dispatch.setBadgeState(badgeState) + useConfigState.getState().dispatch.setBadgeState(badgeState) const counts = badgeStateToBadgeCounts(badgeState) if (!isMobile && shouldTriggerTlfLoad(badgeState)) { - storeRegistry.getState('fs').dispatch.favoritesLoad() + get().dispatch.defer.onFavoritesLoad?.() } if (counts) { get().dispatch.setBadgeCounts(counts) diff --git a/shared/constants/people/index.tsx b/shared/stores/people.tsx similarity index 97% rename from shared/constants/people/index.tsx rename to shared/stores/people.tsx index ba8f0024f463..27a6a2746e2d 100644 --- a/shared/constants/people/index.tsx +++ b/shared/stores/people.tsx @@ -1,16 +1,17 @@ import * as EngineGen from '@/actions/engine-gen-gen' -import {ignorePromise} from '../utils' +import {ignorePromise} from '@/constants/utils' import * as Z from '@/util/zustand' import invert from 'lodash/invert' import isEqual from 'lodash/isEqual' import logger from '@/logger' -import * as T from '../types' +import * as T from '@/constants/types' import type {IconType} from '@/common-adapters/icon.constants-gen' // do NOT pull in all of common-adapters -import {isMobile} from '../platform' +import {isMobile} from '@/constants/platform' import type {e164ToDisplay as e164ToDisplayType} from '@/util/phone-numbers' import debounce from 'lodash/debounce' -import {storeRegistry} from '../store-registry' -import {RPCError, isNetworkErr} from '../utils' +import {useConfigState} from '@/stores/config' +import {useFollowerState} from '@/stores/followers' +import {RPCError, isNetworkErr} from '@/constants/utils' // set this to true to have all todo items + a contact joined notification show up all the time const debugTodo = false as boolean @@ -387,7 +388,7 @@ export const usePeopleState = Z.createZustand((set, get) => { logger.info( 'getPeopleData: appFocused:', 'loggedIn', - storeRegistry.getState('config').loggedIn, + useConfigState.getState().loggedIn, 'action', {markViewed, numFollowSuggestionsWanted} ) @@ -397,7 +398,7 @@ export const usePeopleState = Z.createZustand((set, get) => { {markViewed, numFollowSuggestionsWanted}, getPeopleDataWaitingKey ) - const {following, followers} = storeRegistry.getState('followers') + const {following, followers} = useFollowerState.getState() const oldItems: Array = (data.items ?? []) .filter(item => !item.badged && item.data.t !== T.RPCGen.HomeScreenItemType.todo) .reduce(reduceRPCItemToPeopleItem, []) diff --git a/shared/constants/pinentry/index.tsx b/shared/stores/pinentry.tsx similarity index 92% rename from shared/constants/pinentry/index.tsx rename to shared/stores/pinentry.tsx index 1dae7f694344..8dc26ad61ace 100644 --- a/shared/constants/pinentry/index.tsx +++ b/shared/stores/pinentry.tsx @@ -1,10 +1,11 @@ import * as Z from '@/util/zustand' import * as EngineGen from '@/actions/engine-gen-gen' -import * as T from '../types' +import * as T from '@/constants/types' import logger from '@/logger' -import {invalidPasswordErrorString} from '@/constants/config/util' -import {wrapErrors} from '../utils' +import {invalidPasswordErrorString} from '@/constants/config' +import {wrapErrors} from '@/constants/utils' +// This store has no dependencies on other stores and is safe to import directly from other stores. export type Store = T.Immutable<{ cancelLabel?: string prompt: string diff --git a/shared/constants/profile/index.tsx b/shared/stores/profile.tsx similarity index 89% rename from shared/constants/profile/index.tsx rename to shared/stores/profile.tsx index 0d01f9582e9c..154178603e1c 100644 --- a/shared/constants/profile/index.tsx +++ b/shared/stores/profile.tsx @@ -1,15 +1,16 @@ -import * as T from '../types' -import {generateGUIID, ignorePromise, wrapErrors} from '../utils' -import * as S from '../strings' +import * as T from '@/constants/types' +import {generateGUIID, ignorePromise, wrapErrors} from '@/constants/utils' +import * as S from '@/constants/strings' import * as Validators from '@/util/simple-validators' import * as Z from '@/util/zustand' import logger from '@/logger' import openURL from '@/util/open-url' import {RPCError} from '@/util/errors' import {fixCrop} from '@/util/crop' -import {clearModals, navigateAppend, navigateUp} from '../router2/util' -import {storeRegistry} from '../store-registry' -import {navToProfile} from '../router2' +import {clearModals, navigateAppend, navigateUp} from '@/constants/router2' +import {useCurrentUserState} from '@/stores/current-user' +import {navToProfile} from '@/constants/router2' +import type {useTrackerState} from '@/stores/tracker2' type ProveGenericParams = { logoBlack: T.Tracker.SiteIconSet @@ -97,6 +98,14 @@ const initialStore: Store = { export interface State extends Store { dispatch: { + defer: { + onTracker2GetDetails?: (username: string) => T.Tracker.Details | undefined + onTracker2Load?: ( + params: Parameters['dispatch']['load']>[0] + ) => void + onTracker2ShowUser?: (username: string, asTracker: boolean, skipNav?: boolean) => void + onTracker2UpdateResult?: (guiID: string, result: T.Tracker.DetailsState, reason?: string) => void + } dynamic: { afterCheckProof?: () => void cancelAddProof?: () => void @@ -265,8 +274,8 @@ export const useProfileState = Z.createZustand((set, get) => { let canceled = false const loadAfter = () => - storeRegistry.getState('tracker2').dispatch.load({ - assertion: storeRegistry.getState('current-user').username, + get().dispatch.defer.onTracker2Load?.({ + assertion: useCurrentUserState.getState().username, guiID: generateGUIID(), inTracker: false, reason: '', @@ -407,12 +416,13 @@ export const useProfileState = Z.createZustand((set, get) => { s.errorCode = error.code }) if (error.code === T.RPCGen.StatusCode.scgeneric && reason === 'appLink') { - storeRegistry - .getState('deeplinks') - .dispatch.setLinkError( - "We couldn't find a valid service for proofs in this link. The link might be bad, or your Keybase app might be out of date and need to be updated." - ) - navigateAppend('keybaseLinkError') + navigateAppend({ + props: { + error: + "We couldn't find a valid service for proofs in this link. The link might be bad, or your Keybase app might be out of date and need to be updated.", + }, + selected: 'keybaseLinkError', + }) } } if (genericService) { @@ -435,7 +445,7 @@ export const useProfileState = Z.createZustand((set, get) => { backToProfile: () => { clearModals() setTimeout(() => { - get().dispatch.showUserProfile(storeRegistry.getState('current-user').username) + get().dispatch.showUserProfile(useCurrentUserState.getState().username) }, 100) }, checkProof: () => { @@ -483,6 +493,20 @@ export const useProfileState = Z.createZustand((set, get) => { clearErrors(s) }) }, + defer: { + onTracker2GetDetails: () => { + throw new Error('onTracker2GetDetails not implemented') + }, + onTracker2Load: () => { + throw new Error('onTracker2Load not implemented') + }, + onTracker2ShowUser: () => { + throw new Error('onTracker2ShowUser not implemented') + }, + onTracker2UpdateResult: () => { + throw new Error('onTracker2UpdateResult not implemented') + }, + }, dynamic: { cancelAddProof: _cancelAddProof, cancelPgpGen: undefined, @@ -494,15 +518,15 @@ export const useProfileState = Z.createZustand((set, get) => { editProfile: (bio, fullName, location) => { const f = async () => { await T.RPCGen.userProfileEditRpcPromise({bio, fullName, location}, S.waitingKeyTracker) - get().dispatch.showUserProfile(storeRegistry.getState('current-user').username) + get().dispatch.showUserProfile(useCurrentUserState.getState().username) } ignorePromise(f()) }, finishRevoking: () => { - const username = storeRegistry.getState('current-user').username + const username = useCurrentUserState.getState().username get().dispatch.showUserProfile(username) - storeRegistry.getState('tracker2').dispatch.load({ - assertion: storeRegistry.getState('current-user').username, + get().dispatch.defer.onTracker2Load?.({ + assertion: useCurrentUserState.getState().username, guiID: generateGUIID(), inTracker: false, reason: '', @@ -597,9 +621,7 @@ export const useProfileState = Z.createZustand((set, get) => { }) const f = async () => { await T.RPCGen.proveCheckProofRpcPromise({sigID}, S.waitingKeyProfile) - storeRegistry - .getState('tracker2') - .dispatch.showUser(storeRegistry.getState('current-user').username, false) + get().dispatch.defer.onTracker2ShowUser?.(useCurrentUserState.getState().username, false) } ignorePromise(f()) }, @@ -626,7 +648,7 @@ export const useProfileState = Z.createZustand((set, get) => { set(s => { s.blockUserModal = undefined }) - storeRegistry.getState('tracker2').dispatch.load({ + get().dispatch.defer.onTracker2Load?.({ assertion: username, guiID: generateGUIID(), inTracker: false, @@ -647,10 +669,8 @@ export const useProfileState = Z.createZustand((set, get) => { }, submitRevokeProof: proofId => { const f = async () => { - const you = storeRegistry - .getState('tracker2') - .getDetails(storeRegistry.getState('current-user').username) - if (!you.assertions) return + const you = get().dispatch.defer.onTracker2GetDetails?.(useCurrentUserState.getState().username) + if (!you?.assertions) return const proof = [...you.assertions.values()].find(a => a.sigID === proofId) if (!proof) return @@ -681,7 +701,7 @@ export const useProfileState = Z.createZustand((set, get) => { const f = async () => { try { await T.RPCGen.userUnblockUserRpcPromise({username}) - storeRegistry.getState('tracker2').dispatch.load({ + get().dispatch.defer.onTracker2Load?.({ assertion: username, guiID: generateGUIID(), inTracker: false, @@ -693,9 +713,11 @@ export const useProfileState = Z.createZustand((set, get) => { } const error = _error logger.warn(`Error unblocking user ${username}`, error) - storeRegistry - .getState('tracker2') - .dispatch.updateResult(guiID, 'error', `Failed to unblock ${username}: ${error.desc}`) + get().dispatch.defer.onTracker2UpdateResult?.( + guiID, + 'error', + `Failed to unblock ${username}: ${error.desc}` + ) } } ignorePromise(f()) diff --git a/shared/constants/provision/index.tsx b/shared/stores/provision.tsx similarity index 86% rename from shared/constants/provision/index.tsx rename to shared/stores/provision.tsx index c49fcbf3c1d7..cf3f5c95f5ad 100644 --- a/shared/constants/provision/index.tsx +++ b/shared/stores/provision.tsx @@ -1,15 +1,15 @@ -import * as C from '..' -import * as T from '../types' -import {ignorePromise} from '../utils' +import * as T from '@/constants/types' +import {ignorePromise, wrapErrors} from '@/constants/utils' +import {waitingKeyProvision, waitingKeyProvisionForgotUsername} from '@/constants/strings' import * as Z from '@/util/zustand' import {RPCError} from '@/util/errors' -import {isMobile} from '../platform' +import {isMobile} from '@/constants/platform' import {type CommonResponseHandler} from '@/engine/types' import isEqual from 'lodash/isEqual' -import {rpcDeviceToDevice} from '../rpc-utils' -import {invalidPasswordErrorString} from '@/constants/config/util' -import {clearModals, navigateAppend} from '../router2/util' -import {storeRegistry} from '../store-registry' +import {rpcDeviceToDevice} from '@/constants/rpc-utils' +import {invalidPasswordErrorString} from '@/constants/config' +import {clearModals, navigateAppend} from '@/constants/router2' +import {useWaitingState} from '@/stores/waiting' export type Device = { deviceNumberOfType: number @@ -82,6 +82,7 @@ type Store = T.Immutable<{ inlineError?: RPCError passphrase: string provisionStep: number + startProvisionTrigger: number username: string }> const initialStore: Store = { @@ -99,6 +100,7 @@ const initialStore: Store = { inlineError: undefined, passphrase: '', provisionStep: 0, + startProvisionTrigger: 0, username: '', } @@ -121,15 +123,15 @@ export interface State extends Store { } export const useProvisionState = Z.createZustand((set, get) => { - const _cancel = C.wrapErrors((ignoreWarning?: boolean) => { - storeRegistry.getState('waiting').dispatch.clear(C.waitingKeyProvision) + const _cancel = wrapErrors((ignoreWarning?: boolean) => { + useWaitingState.getState().dispatch.clear(waitingKeyProvision) if (!ignoreWarning) { console.log('Provision: cancel called while not overloaded') } }) // add a new value to submit and clear things behind - const _updateAutoSubmit = C.wrapErrors((step: Store['autoSubmit'][0]) => { + const _updateAutoSubmit = wrapErrors((step: Store['autoSubmit'][0]) => { set(s => { const idx = s.autoSubmit.findIndex(a => a.type === step.type) if (idx !== -1) { @@ -139,16 +141,7 @@ export const useProvisionState = Z.createZustand((set, get) => { }) }) - const _setUsername = C.wrapErrors((username: string, restart: boolean = true) => { - set(s => { - s.username = username - s.autoSubmit = [{type: 'username'}] - }) - if (restart) { - get().dispatch.restartProvisioning() - } - }) - const _setPassphrase = C.wrapErrors((passphrase: string, restart: boolean = true) => { + const _setPassphrase = wrapErrors((passphrase: string, restart: boolean = true) => { set(s => { s.passphrase = passphrase }) @@ -158,7 +151,7 @@ export const useProvisionState = Z.createZustand((set, get) => { } }) - const _setDeviceName = C.wrapErrors((name: string, restart: boolean = true) => { + const _setDeviceName = wrapErrors((name: string, restart: boolean = true) => { set(s => { s.deviceName = name }) @@ -168,7 +161,7 @@ export const useProvisionState = Z.createZustand((set, get) => { } }) - const _submitDeviceSelect = C.wrapErrors((name: string, restart: boolean = true) => { + const _submitDeviceSelect = wrapErrors((name: string, restart: boolean = true) => { const devices = get().devices const selectedDevice = devices.find(d => d.name === name) if (!selectedDevice) { @@ -183,7 +176,7 @@ export const useProvisionState = Z.createZustand((set, get) => { } }) - const _submitTextCode = C.wrapErrors((_code: string) => { + const _submitTextCode = wrapErrors((_code: string) => { console.log('Provision, unwatched submitTextCode called') get().dispatch.restartProvisioning() }) @@ -204,7 +197,7 @@ export const useProvisionState = Z.createZustand((set, get) => { let cancelled = false const setupCancel = (response: CommonResponseHandler) => { set(s => { - s.dispatch.dynamic.cancel = C.wrapErrors(() => { + s.dispatch.dynamic.cancel = wrapErrors(() => { set(s => { s.dispatch.dynamic.cancel = _cancel }) @@ -231,7 +224,7 @@ export const useProvisionState = Z.createZustand((set, get) => { set(s => { s.error = previousErr s.codePageIncomingTextCode = phrase - s.dispatch.dynamic.submitTextCode = C.wrapErrors((code: string) => { + s.dispatch.dynamic.submitTextCode = wrapErrors((code: string) => { set(s => { s.dispatch.dynamic.submitTextCode = _submitTextCode }) @@ -259,13 +252,13 @@ export const useProvisionState = Z.createZustand((set, get) => { }, incomingCallMap: { 'keybase.1.provisionUi.DisplaySecretExchanged': () => { - storeRegistry.getState('waiting').dispatch.increment(C.waitingKeyProvision) + useWaitingState.getState().dispatch.increment(waitingKeyProvision) }, 'keybase.1.provisionUi.ProvisioneeSuccess': () => {}, 'keybase.1.provisionUi.ProvisionerSuccess': () => {}, }, params: undefined, - waitingKey: C.waitingKeyProvision, + waitingKey: waitingKeyProvision, }) } catch { } finally { @@ -273,7 +266,6 @@ export const useProvisionState = Z.createZustand((set, get) => { s.dispatch.dynamic.cancel = _cancel s.dispatch.dynamic.setDeviceName = _setDeviceName s.dispatch.dynamic.setPassphrase = _setPassphrase - s.dispatch.dynamic.setUsername = _setUsername s.dispatch.dynamic.submitDeviceSelect = _submitDeviceSelect s.dispatch.dynamic.submitTextCode = _submitTextCode }) @@ -286,7 +278,15 @@ export const useProvisionState = Z.createZustand((set, get) => { cancel: _cancel, setDeviceName: _setDeviceName, setPassphrase: _setPassphrase, - setUsername: _setUsername, + setUsername: wrapErrors((username: string, restart: boolean = true) => { + set(s => { + s.username = username + s.autoSubmit = [{type: 'username'}] + }) + if (restart) { + get().dispatch.restartProvisioning() + } + }), submitDeviceSelect: _submitDeviceSelect, submitTextCode: _submitTextCode, }, @@ -296,7 +296,7 @@ export const useProvisionState = Z.createZustand((set, get) => { try { await T.RPCGen.accountRecoverUsernameWithEmailRpcPromise( {email}, - C.waitingKeyProvisionForgotUsername + waitingKeyProvisionForgotUsername ) set(s => { s.forgotUsernameResult = 'success' @@ -314,7 +314,7 @@ export const useProvisionState = Z.createZustand((set, get) => { try { await T.RPCGen.accountRecoverUsernameWithPhoneRpcPromise( {phone}, - C.waitingKeyProvisionForgotUsername + waitingKeyProvisionForgotUsername ) set(s => { s.forgotUsernameResult = 'success' @@ -366,7 +366,7 @@ export const useProvisionState = Z.createZustand((set, get) => { // Make cancel set the flag and cancel the current rpc const setupCancel = (response: CommonResponseHandler) => { set(s => { - s.dispatch.dynamic.cancel = C.wrapErrors(() => { + s.dispatch.dynamic.cancel = wrapErrors(() => { set(s => { s.dispatch.dynamic.cancel = _cancel }) @@ -397,7 +397,7 @@ export const useProvisionState = Z.createZustand((set, get) => { set(s => { s.error = previousErr s.codePageIncomingTextCode = phrase - s.dispatch.dynamic.submitTextCode = C.wrapErrors((code: string) => { + s.dispatch.dynamic.submitTextCode = wrapErrors((code: string) => { set(s => { s.dispatch.dynamic.submitTextCode = _submitTextCode }) @@ -418,7 +418,7 @@ export const useProvisionState = Z.createZustand((set, get) => { set(s => { s.error = errorMessage s.existingDevices = T.castDraft(existingDevices ?? []) - s.dispatch.dynamic.setDeviceName = C.wrapErrors((name: string) => { + s.dispatch.dynamic.setDeviceName = wrapErrors((name: string) => { set(s => { s.dispatch.dynamic.setDeviceName = _setDeviceName }) @@ -443,7 +443,7 @@ export const useProvisionState = Z.createZustand((set, get) => { set(s => { s.error = '' s.devices = devices - s.dispatch.dynamic.submitDeviceSelect = C.wrapErrors((device: string) => { + s.dispatch.dynamic.submitDeviceSelect = wrapErrors((device: string) => { set(s => { s.dispatch.dynamic.submitDeviceSelect = _submitDeviceSelect }) @@ -472,7 +472,7 @@ export const useProvisionState = Z.createZustand((set, get) => { // Service asking us again due to an error? set(s => { s.error = retryLabel === invalidPasswordErrorString ? 'Incorrect password.' : retryLabel - s.dispatch.dynamic.setPassphrase = C.wrapErrors((passphrase: string) => { + s.dispatch.dynamic.setPassphrase = wrapErrors((passphrase: string) => { set(s => { s.dispatch.dynamic.setPassphrase = _setPassphrase }) @@ -502,7 +502,7 @@ export const useProvisionState = Z.createZustand((set, get) => { incomingCallMap: { 'keybase.1.loginUi.displayPrimaryPaperKey': () => {}, 'keybase.1.provisionUi.DisplaySecretExchanged': () => { - storeRegistry.getState('waiting').dispatch.increment(C.waitingKeyProvision) + useWaitingState.getState().dispatch.increment(waitingKeyProvision) }, 'keybase.1.provisionUi.ProvisioneeSuccess': () => {}, 'keybase.1.provisionUi.ProvisionerSuccess': () => {}, @@ -515,7 +515,7 @@ export const useProvisionState = Z.createZustand((set, get) => { paperKey: '', username, }, - waitingKey: C.waitingKeyProvision, + waitingKey: waitingKeyProvision, }) get().dispatch.resetState() } catch (_finalError) { @@ -551,23 +551,11 @@ export const useProvisionState = Z.createZustand((set, get) => { }, startProvision: (name = '', fromReset = false) => { get().dispatch.dynamic.cancel?.(true) - storeRegistry.getState('config').dispatch.setLoginError() - storeRegistry.getState('config').dispatch.resetRevokedSelf() - set(s => { + s.startProvisionTrigger++ s.username = name }) - const f = async () => { - // If we're logged in, we're coming from the user switcher; log out first to prevent the service from getting out of sync with the GUI about our logged-in-ness - if (storeRegistry.getState('config').loggedIn) { - await T.RPCGen.loginLogoutRpcPromise( - {force: false, keepSecrets: true}, - C.waitingKeyConfigLoginAsOther - ) - } - navigateAppend({props: {fromReset}, selected: 'username'}) - } - ignorePromise(f()) + navigateAppend({props: {fromReset}, selected: 'username'}) }, } diff --git a/shared/constants/push.d.ts b/shared/stores/push.d.ts similarity index 70% rename from shared/constants/push.d.ts rename to shared/stores/push.d.ts index ae8fd435e57a..29e8b1ff366f 100644 --- a/shared/constants/push.d.ts +++ b/shared/stores/push.d.ts @@ -1,4 +1,4 @@ -import type * as T from './types' +import type * as T from '@/constants/types' import type {UseBoundStore, StoreApi} from 'zustand' type Store = T.Immutable<{ @@ -10,6 +10,15 @@ type Store = T.Immutable<{ export type State = Store & { dispatch: { + defer: { + onGetDaemonHandshakeState?: () => T.Config.DaemonHandshakeState + onNavigateToThread?: ( + conversationIDKey: T.Chat.ConversationIDKey, + reason: 'push' | 'extension', + pushBody?: string + ) => void + onShowUserProfile?: (username: string) => void + } checkPermissions: () => Promise deleteToken: (version: number) => void handlePush: (notification: T.Push.PushNotification) => void diff --git a/shared/constants/push.desktop.tsx b/shared/stores/push.desktop.tsx similarity index 75% rename from shared/constants/push.desktop.tsx rename to shared/stores/push.desktop.tsx index b3fed7e84595..4a59a43a8a35 100644 --- a/shared/constants/push.desktop.tsx +++ b/shared/stores/push.desktop.tsx @@ -1,5 +1,5 @@ import * as Z from '@/util/zustand' -import {type Store, type State} from './push' +import {type Store, type State} from '@/stores/push' export const tokenType = '' @@ -15,6 +15,13 @@ export const usePushState = Z.createZustand(() => { checkPermissions: async () => { return Promise.resolve(false) }, + defer: { + onGetDaemonHandshakeState: () => { + return 'done' + }, + onNavigateToThread: () => {}, + onShowUserProfile: () => {}, + }, deleteToken: () => {}, handlePush: () => {}, initialPermissionsCheck: () => {}, diff --git a/shared/constants/push.native.tsx b/shared/stores/push.native.tsx similarity index 84% rename from shared/constants/push.native.tsx rename to shared/stores/push.native.tsx index 605401cfb53f..d780cd2cb941 100644 --- a/shared/constants/push.native.tsx +++ b/shared/stores/push.native.tsx @@ -1,13 +1,16 @@ -import * as Tabs from './tabs' -import * as S from './strings' -import {ignorePromise, neverThrowPromiseFunc, timeoutPromise} from './utils' -import {navigateAppend, navUpToScreen, switchTab} from './router2/util' -import {storeRegistry} from './store-registry' +import * as Tabs from '@/constants/tabs' +import * as S from '@/constants/strings' +import {ignorePromise, neverThrowPromiseFunc, timeoutPromise} from '@/constants/utils' +import {navigateAppend, navUpToScreen, switchTab} from '@/constants/router2' +import {useConfigState} from '@/stores/config' +import {useCurrentUserState} from '@/stores/current-user' +import {useLogoutState} from '@/stores/logout' +import {useWaitingState} from '@/stores/waiting' import * as Z from '@/util/zustand' import logger from '@/logger' -import * as T from './types' +import * as T from '@/constants/types' import {isDevApplePushToken} from '@/local-debug' -import {isIOS} from './platform' +import {isIOS} from '@/constants/platform' import { checkPushPermissions, getRegistrationToken, @@ -15,7 +18,7 @@ import { requestPushPermissions, removeAllPendingNotificationRequests, } from 'react-native-kb' -import {type Store, type State} from './push' +import {type Store, type State} from '@/stores/push' export const tokenType = isIOS ? (isDevApplePushToken ? 'appledev' : 'apple') : 'androidplay' @@ -69,7 +72,7 @@ export const usePushState = Z.createZustand((set, get) => { const {conversationIDKey, unboxPayload, membersType} = notification - storeRegistry.getConvoState(conversationIDKey).dispatch.navigateToThread('push', undefined, unboxPayload) + get().dispatch.defer.onNavigateToThread?.(conversationIDKey, 'push', unboxPayload) if (unboxPayload && membersType && !isIOS) { try { await T.RPCChat.localUnboxMobilePushNotificationRpcPromise({ @@ -112,12 +115,23 @@ export const usePushState = Z.createZustand((set, get) => { return false } }, + defer: { + onGetDaemonHandshakeState: () => { + throw new Error('onGetDaemonHandshakeState not implemented') + }, + onNavigateToThread: () => { + throw new Error('onNavigateToThread not implemented') + }, + onShowUserProfile: () => { + throw new Error('onShowUserProfile not implemented') + }, + }, deleteToken: version => { const f = async () => { const waitKey = 'push:deleteToken' - storeRegistry.getState('logout').dispatch.wait(waitKey, version, true) + useLogoutState.getState().dispatch.wait(waitKey, version, true) try { - const deviceID = storeRegistry.getState('current-user').deviceID + const deviceID = useCurrentUserState.getState().deviceID if (!deviceID) { logger.info('[PushToken] no device id') return @@ -133,7 +147,7 @@ export const usePushState = Z.createZustand((set, get) => { } catch (e) { logger.error('[PushToken] delete failed', e) } finally { - storeRegistry.getState('logout').dispatch.wait(waitKey, version, false) + useLogoutState.getState().dispatch.wait(waitKey, version, false) } } ignorePromise(f()) @@ -156,17 +170,17 @@ export const usePushState = Z.createZustand((set, get) => { // We only care if the user clicked while in session if (notification.userInteraction) { const {username} = notification - storeRegistry.getState('profile').dispatch.showUserProfile(username) + get().dispatch.defer.onShowUserProfile?.(username) } break case 'chat.extension': { const {conversationIDKey} = notification - storeRegistry.getConvoState(conversationIDKey).dispatch.navigateToThread('extension') + get().dispatch.defer.onNavigateToThread?.(conversationIDKey, 'extension') } break case 'settings.contacts': - if (storeRegistry.getState('config').loggedIn) { + if (useConfigState.getState().loggedIn) { switchTab(Tabs.peopleTab) navUpToScreen('peopleRoot') } @@ -230,14 +244,14 @@ export const usePushState = Z.createZustand((set, get) => { const shownPushPrompt = await askNativeIfSystemPushPromptHasBeenShown() if (shownPushPrompt) { // we've already shown the prompt, take them to settings - storeRegistry.getState('config').dispatch.dynamic.openAppSettings?.() + useConfigState.getState().dispatch.defer.openAppSettings?.() get().dispatch.showPermissionsPrompt({persistSkip: true, show: false}) return } } try { - storeRegistry.getState('config').dispatch.dynamic.openAppSettings?.() - const {increment} = storeRegistry.getState('waiting').dispatch + useConfigState.getState().dispatch.defer.openAppSettings?.() + const {increment} = useWaitingState.getState().dispatch increment(S.waitingKeyPushPermissionsRequesting) await requestPermissionsFromNative() const permissions = await checkPermissionsFromNative() @@ -253,7 +267,7 @@ export const usePushState = Z.createZustand((set, get) => { }) } } finally { - const {decrement} = storeRegistry.getState('waiting').dispatch + const {decrement} = useWaitingState.getState().dispatch decrement(S.waitingKeyPushPermissionsRequesting) get().dispatch.showPermissionsPrompt({persistSkip: true, show: false}) } @@ -267,7 +281,7 @@ export const usePushState = Z.createZustand((set, get) => { }) const uploadPushToken = async () => { - const {deviceID, username} = storeRegistry.getState('current-user') + const {deviceID, username} = useCurrentUserState.getState() if (!username || !deviceID) { return } @@ -303,8 +317,8 @@ export const usePushState = Z.createZustand((set, get) => { // permissions checker finishes after the routeToInitialScreen is done. if ( p.show && - storeRegistry.getState('config').loggedIn && - storeRegistry.getState('daemon').handshakeState === 'done' && + useConfigState.getState().loggedIn && + get().dispatch.defer.onGetDaemonHandshakeState?.() === 'done' && !get().justSignedUp && !get().hasPermissions ) { diff --git a/shared/constants/recover-password/index.tsx b/shared/stores/recover-password.tsx similarity index 87% rename from shared/constants/recover-password/index.tsx rename to shared/stores/recover-password.tsx index 47d6eec523fa..6136a01a06ec 100644 --- a/shared/constants/recover-password/index.tsx +++ b/shared/stores/recover-password.tsx @@ -1,13 +1,13 @@ -import * as T from '../types' -import {ignorePromise, wrapErrors} from '../utils' -import {waitingKeyRecoverPassword} from '../strings' +import * as T from '@/constants/types' +import {ignorePromise, wrapErrors} from '@/constants/utils' +import {waitingKeyRecoverPassword} from '@/constants/strings' import * as Z from '@/util/zustand' import logger from '@/logger' import {RPCError} from '@/util/errors' -import {type Device} from '../provision' -import {rpcDeviceToDevice} from '../rpc-utils' -import {clearModals, navigateAppend, navigateUp} from '../router2/util' -import {storeRegistry} from '../store-registry' +import {type Device} from '@/stores/provision' +import {rpcDeviceToDevice} from '@/constants/rpc-utils' +import {clearModals, navigateAppend, navigateUp} from '@/constants/router2' +import {useConfigState} from '@/stores/config' type Store = T.Immutable<{ devices: Array @@ -34,6 +34,10 @@ const initialStore: Store = { export interface State extends Store { dispatch: { + defer: { + onProvisionCancel?: (ignoreWarning?: boolean) => void + onStartAccountReset?: (skipPassword: boolean, username: string) => void + } dynamic: { cancel?: () => void submitDeviceSelect?: (name: string) => void @@ -48,6 +52,14 @@ export interface State extends Store { export const useState = Z.createZustand((set, get) => { const dispatch: State['dispatch'] = { + defer: { + onProvisionCancel: () => { + throw new Error('onProvisionCancel not implemented') + }, + onStartAccountReset: () => { + throw new Error('onStartAccountReset not implemented') + }, + }, dynamic: { cancel: undefined, submitDeviceSelect: undefined, @@ -74,7 +86,7 @@ export const useState = Z.createZustand((set, get) => { const f = async () => { if (p.abortProvisioning) { - storeRegistry.getState('provision').dispatch.dynamic.cancel?.() + get().dispatch.defer.onProvisionCancel?.() } let hadError = false try { @@ -107,17 +119,13 @@ export const useState = Z.createZustand((set, get) => { } }) }) - storeRegistry - .getState('router') - .dispatch.navigateAppend('recoverPasswordDeviceSelector', !!replaceRoute) + navigateAppend('recoverPasswordDeviceSelector', !!replaceRoute) }, 'keybase.1.loginUi.promptPassphraseRecovery': () => {}, // This same RPC is called at the beginning and end of the 7-day wait by the service. 'keybase.1.loginUi.promptResetAccount': (params, response) => { if (params.prompt.t === T.RPCGen.ResetPromptType.enterResetPw) { - storeRegistry - .getState('router') - .dispatch.navigateAppend('recoverPasswordPromptResetPassword') + navigateAppend('recoverPasswordPromptResetPassword') const clear = () => { set(s => { s.dispatch.dynamic.submitResetPassword = undefined @@ -142,8 +150,7 @@ export const useState = Z.createZustand((set, get) => { }) }) } else { - const {startAccountReset} = storeRegistry.getState('autoreset').dispatch - startAccountReset(true, '') + get().dispatch.defer.onStartAccountReset?.(true, '') response.result(T.RPCGen.ResetPromptResponse.nothing) } }, @@ -227,14 +234,10 @@ export const useState = Z.createZustand((set, get) => { set(s => { s.error = msg }) - storeRegistry - .getState('router') - .dispatch.navigateAppend( - storeRegistry.getState('config').loggedIn - ? 'recoverPasswordErrorModal' - : 'recoverPasswordError', - true - ) + navigateAppend( + useConfigState.getState().loggedIn ? 'recoverPasswordErrorModal' : 'recoverPasswordError', + true + ) } } finally { set(s => { diff --git a/shared/stores/router2.tsx b/shared/stores/router2.tsx new file mode 100644 index 000000000000..0a1cf4cc0804 --- /dev/null +++ b/shared/stores/router2.tsx @@ -0,0 +1,90 @@ +import type * as T from '@/constants/types' +import * as Z from '@/util/zustand' +import type * as Tabs from '@/constants/tabs' +import type {RouteKeys} from '@/router-v2/route-params' +import * as Util from '@/constants/router2' + +export { + type NavState, + getTab, + navigationRef, + getRootState, + _getNavigator, + logState, + getVisiblePath, + getModalStack, + getVisibleScreen, + navToProfile, + navToThread, + getRouteTab, + getRouteLoggedIn, + useSafeFocusEffect, + makeScreen, +} from '@/constants/router2' +export type {PathParam, Navigator} from '@/constants/router2' + +type Store = T.Immutable<{ + navState?: unknown +}> + +const initialStore: Store = { + navState: undefined, +} + +export interface State extends Store { + dispatch: { + clearModals: () => void + defer: { + tabLongPress?: (tab: string) => void + } + navigateAppend: (path: Util.PathParam, replace?: boolean) => void + navigateUp: () => void + navUpToScreen: (name: RouteKeys) => void + popStack: () => void + resetState: () => void + setNavState: (ns: Util.NavState) => void + switchTab: (tab: Tabs.AppTab) => void + } + appendEncryptRecipientsBuilder: () => void + appendNewChatBuilder: () => void + appendNewTeamBuilder: (teamID: T.Teams.TeamID) => void + appendPeopleBuilder: () => void +} + +export const useRouterState = Z.createZustand((set, get) => { + const dispatch: State['dispatch'] = { + clearModals: Util.clearModals, + defer: { + tabLongPress: undefined, + }, + navUpToScreen: Util.navUpToScreen, + navigateAppend: Util.navigateAppend, + navigateUp: Util.navigateUp, + popStack: Util.popStack, + resetState: () => { + set(s => ({ + ...s, + dispatch: s.dispatch, + })) + }, + setNavState: next => { + const DEBUG_NAV = __DEV__ && (false as boolean) + DEBUG_NAV && console.log('[Nav] setNavState') + const prev = get().navState as Util.NavState + if (prev === next) return + set(s => { + s.navState = next + }) + }, + switchTab: Util.switchTab, + } + + return { + ...initialStore, + appendEncryptRecipientsBuilder: Util.appendEncryptRecipientsBuilder, + appendNewChatBuilder: Util.appendNewChatBuilder, + appendNewTeamBuilder: Util.appendNewTeamBuilder, + appendPeopleBuilder: Util.appendPeopleBuilder, + dispatch, + } +}) diff --git a/shared/constants/settings-chat/index.tsx b/shared/stores/settings-chat.tsx similarity index 91% rename from shared/constants/settings-chat/index.tsx rename to shared/stores/settings-chat.tsx index 24b2b4a1024f..71e6af5aca23 100644 --- a/shared/constants/settings-chat/index.tsx +++ b/shared/stores/settings-chat.tsx @@ -1,8 +1,8 @@ -import * as T from '../types' -import {ignorePromise} from '../utils' -import * as S from '../strings' +import * as T from '@/constants/types' +import {ignorePromise} from '@/constants/utils' +import * as S from '@/constants/strings' import * as Z from '@/util/zustand' -import {storeRegistry} from '../store-registry' +import {useConfigState} from '@/stores/config' export type ChatUnfurlState = { unfurlMode?: T.RPCChat.UnfurlMode @@ -49,7 +49,7 @@ export const useSettingsChatState = Z.createZustand((set, get) => { const dispatch: State['dispatch'] = { contactSettingsRefresh: () => { const f = async () => { - if (!storeRegistry.getState('config').loggedIn) { + if (!useConfigState.getState().loggedIn) { return } try { @@ -67,7 +67,7 @@ export const useSettingsChatState = Z.createZustand((set, get) => { }, contactSettingsSaved: (enabled, indirectFollowees, teamsEnabled, teamsList) => { const f = async () => { - if (!storeRegistry.getState('config').loggedIn) { + if (!useConfigState.getState().loggedIn) { return } @@ -100,7 +100,7 @@ export const useSettingsChatState = Z.createZustand((set, get) => { resetState: 'default', unfurlSettingsRefresh: () => { const f = async () => { - if (!storeRegistry.getState('config').loggedIn) { + if (!useConfigState.getState().loggedIn) { return } try { @@ -128,7 +128,7 @@ export const useSettingsChatState = Z.createZustand((set, get) => { s.unfurl = T.castDraft({unfurlError: undefined, unfurlMode, unfurlWhitelist}) }) const f = async () => { - if (!storeRegistry.getState('config').loggedIn) { + if (!useConfigState.getState().loggedIn) { return } try { diff --git a/shared/constants/settings-contacts.d.ts b/shared/stores/settings-contacts.d.ts similarity index 95% rename from shared/constants/settings-contacts.d.ts rename to shared/stores/settings-contacts.d.ts index 3f48c273c201..2920c6643055 100644 --- a/shared/constants/settings-contacts.d.ts +++ b/shared/stores/settings-contacts.d.ts @@ -1,4 +1,4 @@ -import type * as T from './types' +import type * as T from '@/constants/types' import type {UseBoundStore, StoreApi} from 'zustand' type PermissionStatus = 'granted' | 'denied' | 'undetermined' | 'unknown' diff --git a/shared/constants/settings-contacts.desktop.tsx b/shared/stores/settings-contacts.desktop.tsx similarity index 100% rename from shared/constants/settings-contacts.desktop.tsx rename to shared/stores/settings-contacts.desktop.tsx diff --git a/shared/constants/settings-contacts.native.tsx b/shared/stores/settings-contacts.native.tsx similarity index 90% rename from shared/constants/settings-contacts.native.tsx rename to shared/stores/settings-contacts.native.tsx index 3d0f7da0407e..5043bf04d1f3 100644 --- a/shared/constants/settings-contacts.native.tsx +++ b/shared/stores/settings-contacts.native.tsx @@ -1,17 +1,19 @@ -import * as C from '.' import * as Contacts from 'expo-contacts' -import {ignorePromise} from './utils' -import * as T from './types' +import {ignorePromise} from '@/constants/utils' +import {importContactsWaitingKey} from '@/constants/strings' +import * as T from '@/constants/types' import * as Z from '@/util/zustand' import {addNotificationRequest} from 'react-native-kb' import logger from '@/logger' import type {Store, State} from './settings-contacts' import {RPCError} from '@/util/errors' import {getDefaultCountryCode} from 'react-native-kb' -import {getE164} from './settings-phone' +import {getE164} from '@/util/phone-numbers' import {pluralize} from '@/util/string' -import {navigateAppend} from './router2/util' -import {storeRegistry} from './store-registry' +import {navigateAppend} from '@/constants/router2' +import {useConfigState} from '@/stores/config' +import {useCurrentUserState} from '@/stores/current-user' +import {useWaitingState} from '@/stores/waiting' const importContactsConfigKey = (username: string) => `ui.importContacts.${username}` @@ -80,14 +82,14 @@ export const useSettingsContactsState = Z.createZustand((set, get) => { }) } const f = async () => { - const username = storeRegistry.getState('current-user').username + const username = useCurrentUserState.getState().username if (!username) { logger.warn('no username') return } await T.RPCGen.configGuiSetValueRpcPromise( {path: importContactsConfigKey(username), value: {b: enable, isNull: false}}, - C.importContactsWaitingKey + importContactsWaitingKey ) get().dispatch.loadContactImportEnabled() } @@ -100,10 +102,10 @@ export const useSettingsContactsState = Z.createZustand((set, get) => { }, loadContactImportEnabled: () => { const f = async () => { - if (!storeRegistry.getState('config').loggedIn) { + if (!useConfigState.getState().loggedIn) { return } - const username = storeRegistry.getState('current-user').username + const username = useCurrentUserState.getState().username if (!username) { logger.warn('no username') return @@ -112,7 +114,7 @@ export const useSettingsContactsState = Z.createZustand((set, get) => { try { const value = await T.RPCGen.configGuiGetValueRpcPromise( {path: importContactsConfigKey(username)}, - C.importContactsWaitingKey + importContactsWaitingKey ) enabled = !!value.b && !value.isNull } catch (error) { @@ -241,8 +243,8 @@ export const useSettingsContactsState = Z.createZustand((set, get) => { }, requestPermissions: (thenToggleImportOn?: boolean, fromSettings?: boolean) => { const f = async () => { - const {decrement, increment} = storeRegistry.getState('waiting').dispatch - increment(C.importContactsWaitingKey) + const {decrement, increment} = useWaitingState.getState().dispatch + increment(importContactsWaitingKey) const status = (await Contacts.requestPermissionsAsync()).status if (status === Contacts.PermissionStatus.GRANTED && thenToggleImportOn) { @@ -251,7 +253,7 @@ export const useSettingsContactsState = Z.createZustand((set, get) => { set(s => { s.permissionStatus = status }) - decrement(C.importContactsWaitingKey) + decrement(importContactsWaitingKey) } ignorePromise(f()) }, diff --git a/shared/constants/settings-email/index.tsx b/shared/stores/settings-email.tsx similarity index 97% rename from shared/constants/settings-email/index.tsx rename to shared/stores/settings-email.tsx index 7cb1d649961a..935f43208173 100644 --- a/shared/constants/settings-email/index.tsx +++ b/shared/stores/settings-email.tsx @@ -1,7 +1,7 @@ import * as Z from '@/util/zustand' -import {addEmailWaitingKey} from '../strings' -import {ignorePromise} from '../utils' -import * as T from '../types' +import {addEmailWaitingKey} from '@/constants/strings' +import {ignorePromise} from '@/constants/utils' +import * as T from '@/constants/types' import {isValidEmail} from '@/util/simple-validators' import {RPCError} from '@/util/errors' import logger from '@/logger' diff --git a/shared/constants/settings-invites/index.tsx b/shared/stores/settings-invites.tsx similarity index 95% rename from shared/constants/settings-invites/index.tsx rename to shared/stores/settings-invites.tsx index c6b6edc8e582..93e5df2833bf 100644 --- a/shared/constants/settings-invites/index.tsx +++ b/shared/stores/settings-invites.tsx @@ -1,11 +1,11 @@ import * as Z from '@/util/zustand' -import {ignorePromise} from '../utils' -import {waitingKeySettingsGeneric} from '../strings' +import {ignorePromise} from '@/constants/utils' +import {waitingKeySettingsGeneric} from '@/constants/strings' import {RPCError} from '@/util/errors' import logger from '@/logger' import trim from 'lodash/trim' -import * as T from '../types' -import {navigateAppend} from '../router2/util' +import * as T from '@/constants/types' +import {navigateAppend} from '@/constants/router2' type InviteBase = { id: string diff --git a/shared/constants/settings-notifications/index.tsx b/shared/stores/settings-notifications.tsx similarity index 97% rename from shared/constants/settings-notifications/index.tsx rename to shared/stores/settings-notifications.tsx index f93adaae9fdc..c22a90a587f0 100644 --- a/shared/constants/settings-notifications/index.tsx +++ b/shared/stores/settings-notifications.tsx @@ -1,10 +1,10 @@ import * as Z from '@/util/zustand' -import * as S from '../strings' -import {isAndroidNewerThanN} from '../platform' -import {ignorePromise, timeoutPromise} from '../utils' +import * as S from '@/constants/strings' +import {isAndroidNewerThanN} from '@/constants/platform' +import {ignorePromise, timeoutPromise} from '@/constants/utils' import {RPCError} from '@/util/errors' import logger from '@/logger' -import * as T from '../types' +import * as T from '@/constants/types' const securityGroup = 'security' const soundGroup = 'sound' diff --git a/shared/constants/settings-password/index.tsx b/shared/stores/settings-password.tsx similarity index 93% rename from shared/constants/settings-password/index.tsx rename to shared/stores/settings-password.tsx index ac305c0f6159..0b3c441f2422 100644 --- a/shared/constants/settings-password/index.tsx +++ b/shared/stores/settings-password.tsx @@ -1,11 +1,11 @@ import * as Z from '@/util/zustand' -import {ignorePromise} from '../utils' -import {waitingKeySettingsGeneric} from '../strings' +import {ignorePromise} from '@/constants/utils' +import {waitingKeySettingsGeneric} from '@/constants/strings' import logger from '@/logger' import {RPCError} from '@/util/errors' -import * as T from '../types' -import {navigateUp} from '../router2/util' -import {storeRegistry} from '../store-registry' +import * as T from '@/constants/types' +import {navigateUp} from '@/constants/router2' +import {useLogoutState} from '@/stores/logout' type Store = T.Immutable<{ error: string @@ -141,7 +141,7 @@ export const usePWState = Z.createZustand((set, get) => { ) if (thenLogout) { - storeRegistry.getState('logout').dispatch.requestLogout() + useLogoutState.getState().dispatch.requestLogout() } navigateUp() } catch (error) { diff --git a/shared/constants/settings-phone/index.tsx b/shared/stores/settings-phone.tsx similarity index 88% rename from shared/constants/settings-phone/index.tsx rename to shared/stores/settings-phone.tsx index cc19e82aa27d..eefe6932475d 100644 --- a/shared/constants/settings-phone/index.tsx +++ b/shared/stores/settings-phone.tsx @@ -1,15 +1,10 @@ -import * as T from '../types' -import * as S from '../strings' -import {ignorePromise} from '../utils' +import * as T from '@/constants/types' +import * as S from '@/constants/strings' +import {ignorePromise} from '@/constants/utils' import * as Z from '@/util/zustand' import logger from '@/logger' import {RPCError} from '@/util/errors' -import type { - e164ToDisplay as e164ToDisplayType, - phoneUtil as phoneUtilType, - ValidationResult as ValidationResultType, - PhoneNumberFormat as PhoneNumberFormatType, -} from '@/util/phone-numbers' +import type {e164ToDisplay as e164ToDisplayType} from '@/util/phone-numbers' export const makePhoneRow = (): PhoneRow => ({ displayNumber: '', @@ -19,25 +14,6 @@ export const makePhoneRow = (): PhoneRow => ({ verified: false, }) -// Get phone number in e.164, or null if we can't parse it. -export const getE164 = (phoneNumber: string, countryCode?: string) => { - const {phoneUtil, ValidationResult, PhoneNumberFormat} = require('@/util/phone-numbers') as { - phoneUtil: typeof phoneUtilType - ValidationResult: typeof ValidationResultType - PhoneNumberFormat: typeof PhoneNumberFormatType - } - try { - const parsed = countryCode ? phoneUtil.parse(phoneNumber, countryCode) : phoneUtil.parse(phoneNumber) - const reason = phoneUtil.isPossibleNumberWithReason(parsed) - if (reason !== ValidationResult.IS_POSSIBLE) { - return null - } - return phoneUtil.format(parsed, PhoneNumberFormat.E164) - } catch { - return null - } -} - const toPhoneRow = (p: T.RPCGen.UserPhoneNumber) => { const {e164ToDisplay} = require('@/util/phone-numbers') as {e164ToDisplay: typeof e164ToDisplayType} return { @@ -67,7 +43,7 @@ export const makePhoneError = (e: RPCError) => { } } -type PhoneRow = { +export type PhoneRow = { displayNumber: string e164: string searchable: boolean diff --git a/shared/constants/settings/index.tsx b/shared/stores/settings.tsx similarity index 70% rename from shared/constants/settings/index.tsx rename to shared/stores/settings.tsx index ce2327c8888d..6c9ed5b1a946 100644 --- a/shared/constants/settings/index.tsx +++ b/shared/stores/settings.tsx @@ -1,18 +1,20 @@ -import * as T from '../types' -import {ignorePromise, timeoutPromise} from '../utils' -import * as S from '../strings' -import {androidIsTestDevice, pprofDir} from '../platform' -import * as EngineGen from '@/actions/engine-gen-gen' +import * as T from '@/constants/types' +import {ignorePromise, timeoutPromise} from '@/constants/utils' +import * as S from '@/constants/strings' +import {androidIsTestDevice, pprofDir} from '@/constants/platform' import openURL from '@/util/open-url' import * as Z from '@/util/zustand' import {RPCError} from '@/util/errors' -import * as Tabs from '../tabs' +import * as Tabs from '@/constants/tabs' import logger from '@/logger' -import {clearModals, navigateAppend, switchTab} from '../router2/util' -import {storeRegistry} from '../store-registry' -import {processorProfileInProgressKey, traceInProgressKey} from './util' +import {clearModals, navigateAppend, switchTab} from '@/constants/router2' +import {useConfigState} from '@/stores/config' +import {useCurrentUserState} from '@/stores/current-user' +import {useWaitingState} from '@/stores/waiting' +import {processorProfileInProgressKey, traceInProgressKey} from '@/constants/settings' +import type {PhoneRow} from '@/stores/settings-phone' -export * from './util' +export * from '@/constants/settings' type Store = T.Immutable<{ checkPasswordIsCorrect?: boolean @@ -31,14 +33,18 @@ const initialStore: Store = { export interface State extends Store { dispatch: { checkPassword: (password: string) => void - dbNuke: () => void clearLogs: () => void + dbNuke: () => void + defer: { + getSettingsPhonePhones: () => undefined | ReadonlyMap + onSettingsEmailNotifyEmailsChanged: (list: ReadonlyArray) => void + onSettingsPhoneSetNumbers: (phoneNumbers?: ReadonlyArray) => void + } deleteAccountForever: (passphrase?: string) => void loadLockdownMode: () => void loadProxyData: () => void loadSettings: () => void loginBrowserViaWebAuthToken: () => void - onEngineIncomingImpl: (action: EngineGen.Actions) => void processorProfile: (durationSeconds: number) => void resetCheckPassword: () => void resetState: 'default' @@ -51,14 +57,14 @@ export interface State extends Store { } let maybeLoadAppLinkOnce = false -export const useSettingsState = Z.createZustand(set => { +export const useSettingsState = Z.createZustand((set, get) => { const maybeLoadAppLink = () => { - const phones = storeRegistry.getState('settings-phone').phones + const phones = get().dispatch.defer.getSettingsPhonePhones() if (!phones || phones.size > 0) { return } - if (maybeLoadAppLinkOnce || !storeRegistry.getState('config').startup.link.endsWith('/phone-app')) { + if (maybeLoadAppLinkOnce || !useConfigState.getState().startup.link.endsWith('/phone-app')) { return } maybeLoadAppLinkOnce = true @@ -95,9 +101,20 @@ export const useSettingsState = Z.createZustand(set => { } ignorePromise(f()) }, + defer: { + getSettingsPhonePhones: () => { + throw new Error('getSettingsPhonePhones not implemented') + }, + onSettingsEmailNotifyEmailsChanged: () => { + throw new Error('onSettingsEmailNotifyEmailsChanged not implemented') + }, + onSettingsPhoneSetNumbers: () => { + throw new Error('onSettingsPhoneSetNumbers not implemented') + }, + }, deleteAccountForever: passphrase => { const f = async () => { - const username = storeRegistry.getState('current-user').username + const username = useCurrentUserState.getState().username if (!username) { throw new Error('Unable to delete account: no username set') @@ -108,7 +125,7 @@ export const useSettingsState = Z.createZustand(set => { } await T.RPCGen.loginAccountDeleteRpcPromise({passphrase}, S.waitingKeySettingsGeneric) - storeRegistry.getState('config').dispatch.setJustDeletedSelf(username) + useConfigState.getState().dispatch.setJustDeletedSelf(username) clearModals() navigateAppend(Tabs.loginTab) } @@ -116,7 +133,7 @@ export const useSettingsState = Z.createZustand(set => { }, loadLockdownMode: () => { const f = async () => { - if (!storeRegistry.getState('config').loggedIn) { + if (!useConfigState.getState().loggedIn) { return } try { @@ -148,7 +165,7 @@ export const useSettingsState = Z.createZustand(set => { }, loadSettings: () => { const f = async () => { - if (!storeRegistry.getState('config').loggedIn) { + if (!useConfigState.getState().loggedIn) { return } try { @@ -156,10 +173,8 @@ export const useSettingsState = Z.createZustand(set => { undefined, S.waitingKeySettingsLoadSettings ) - storeRegistry - .getState('settings-email') - .dispatch.notifyEmailAddressEmailsChanged(settings.emails ?? []) - storeRegistry.getState('settings-phone').dispatch.setNumbers(settings.phoneNumbers ?? undefined) + get().dispatch.defer.onSettingsEmailNotifyEmailsChanged(settings.emails ?? []) + get().dispatch.defer.onSettingsPhoneSetNumbers(settings.phoneNumbers ?? undefined) maybeLoadAppLink() } catch (error) { if (!(error instanceof RPCError)) { @@ -178,41 +193,13 @@ export const useSettingsState = Z.createZustand(set => { } ignorePromise(f()) }, - onEngineIncomingImpl: action => { - switch (action.type) { - case EngineGen.keybase1NotifyEmailAddressEmailAddressVerified: - logger.info('email verified') - storeRegistry - .getState('settings-email') - .dispatch.notifyEmailVerified(action.payload.params.emailAddress) - break - case EngineGen.keybase1NotifyUsersPasswordChanged: { - const randomPW = action.payload.params.state === T.RPCGen.PassphraseState.random - storeRegistry.getState('settings-password').dispatch.notifyUsersPasswordChanged(randomPW) - break - } - case EngineGen.keybase1NotifyPhoneNumberPhoneNumbersChanged: { - const {list} = action.payload.params - storeRegistry - .getState('settings-phone') - .dispatch.notifyPhoneNumberPhoneNumbersChanged(list ?? undefined) - break - } - case EngineGen.keybase1NotifyEmailAddressEmailsChanged: { - const list = action.payload.params.list ?? [] - storeRegistry.getState('settings-email').dispatch.notifyEmailAddressEmailsChanged(list) - break - } - default: - } - }, processorProfile: profileDurationSeconds => { const f = async () => { await T.RPCGen.pprofLogProcessorProfileRpcPromise({ logDirForMobile: pprofDir, profileDurationSeconds, }) - const {decrement, increment} = storeRegistry.getState('waiting').dispatch + const {decrement, increment} = useWaitingState.getState().dispatch increment(processorProfileInProgressKey) await timeoutPromise(profileDurationSeconds * 1_000) decrement(processorProfileInProgressKey) @@ -232,7 +219,7 @@ export const useSettingsState = Z.createZustand(set => { }, setLockdownMode: enabled => { const f = async () => { - if (!storeRegistry.getState('config').loggedIn) { + if (!useConfigState.getState().loggedIn) { return } try { @@ -270,7 +257,7 @@ export const useSettingsState = Z.createZustand(set => { logDirForMobile: pprofDir, traceDurationSeconds: durationSeconds, }) - const {decrement, increment} = storeRegistry.getState('waiting').dispatch + const {decrement, increment} = useWaitingState.getState().dispatch increment(traceInProgressKey) await timeoutPromise(durationSeconds * 1_000) decrement(traceInProgressKey) diff --git a/shared/constants/signup/index.tsx b/shared/stores/signup.tsx similarity index 90% rename from shared/constants/signup/index.tsx rename to shared/stores/signup.tsx index d5526f28da51..3d35852a6978 100644 --- a/shared/constants/signup/index.tsx +++ b/shared/stores/signup.tsx @@ -1,15 +1,15 @@ -import * as Platforms from '../platform' -import {ignorePromise} from '../utils' -import * as S from '../strings' +import * as Platforms from '@/constants/platform' +import {ignorePromise} from '@/constants/utils' +import * as S from '@/constants/strings' import * as EngineGen from '@/actions/engine-gen-gen' -import * as T from '../types' +import * as T from '@/constants/types' import * as Z from '@/util/zustand' import logger from '@/logger' import trim from 'lodash/trim' import {RPCError} from '@/util/errors' import {isValidEmail, isValidName, isValidUsername} from '@/util/simple-validators' -import {navigateAppend, navigateUp} from '../router2/util' -import {storeRegistry} from '../store-registry' +import {navigateAppend, navigateUp} from '@/constants/router2' +import {useConfigState} from '@/stores/config' type Store = T.Immutable<{ devicename: string @@ -45,6 +45,10 @@ const initialStore: Store = { export interface State extends Store { dispatch: { + defer: { + onEditEmail?: (p: {email: string; makeSearchable: boolean}) => void + onShowPermissionsPrompt?: (p: {justSignedUp?: boolean}) => void + } checkDeviceName: (devicename: string) => void checkInviteCode: () => void checkUsername: (username: string) => void @@ -80,7 +84,7 @@ export const useSignupState = Z.createZustand((set, get) => { } try { - storeRegistry.getState('push').dispatch.showPermissionsPrompt({justSignedUp: true}) + get().dispatch.defer.onShowPermissionsPrompt?.({justSignedUp: true}) await T.RPCGen.signupSignupRpcListener({ customResponseIncomingCallMap: { @@ -121,7 +125,7 @@ export const useSignupState = Z.createZustand((set, get) => { } // If the email was set to be visible during signup, we need to set that with a separate RPC. if (noErrors() && get().emailVisible) { - storeRegistry.getState('settings-email').dispatch.editEmail({email: get().email, makeSearchable: true}) + get().dispatch.defer.onEditEmail?.({email: get().email, makeSearchable: true}) } } catch (_error) { if (_error instanceof RPCError) { @@ -130,7 +134,7 @@ export const useSignupState = Z.createZustand((set, get) => { s.signupError = error }) navigateAppend('signupError') - storeRegistry.getState('push').dispatch.showPermissionsPrompt({justSignedUp: false}) + get().dispatch.defer.onShowPermissionsPrompt?.({justSignedUp: false}) } } } @@ -228,6 +232,14 @@ export const useSignupState = Z.createZustand((set, get) => { s.justSignedUpEmail = '' }) }, + defer: { + onEditEmail: () => { + throw new Error('onEditEmail not implemented') + }, + onShowPermissionsPrompt: () => { + throw new Error('onShowPermissionsPrompt not implemented') + }, + }, goBackAndClearErrors: () => { set(s => { s.devicenameError = '' @@ -255,7 +267,7 @@ export const useSignupState = Z.createZustand((set, get) => { }) const f = async () => { // If we're logged in, we're coming from the user switcher; log out first to prevent the service from getting out of sync with the GUI about our logged-in-ness - if (storeRegistry.getState('config').loggedIn) { + if (useConfigState.getState().loggedIn) { await T.RPCGen.loginLogoutRpcPromise({force: false, keepSecrets: true}) } try { diff --git a/shared/stores/store-registry.tsx b/shared/stores/store-registry.tsx new file mode 100644 index 000000000000..dbae6e710f48 --- /dev/null +++ b/shared/stores/store-registry.tsx @@ -0,0 +1,157 @@ +// used to allow non-circular cross-calls between stores +// ONLY for zustand stores +import type * as T from '@/constants/types' +import type * as ConvoStateType from '@/stores/convostate' +import type {ConvoState} from '@/stores/convostate' +import type {State as ChatState, useChatState} from '@/stores/chat2' +import type {State as DaemonState, useDaemonState} from '@/stores/daemon' +import type {State as FSState, useFSState} from '@/stores/fs' +import type {State as PeopleState, usePeopleState} from '@/stores/people' +import type {State as ProfileState, useProfileState} from '@/stores/profile' +import type {State as ProvisionState, useProvisionState} from '@/stores/provision' +import type {State as PushState, usePushState} from '@/stores/push' +import type { + State as RecoverPasswordState, + useState as useRecoverPasswordState, +} from '@/stores/recover-password' +import type {State as SettingsState, useSettingsState} from '@/stores/settings' +import type {State as SettingsEmailState, useSettingsEmailState} from '@/stores/settings-email' +import type {State as SettingsPhoneState, useSettingsPhoneState} from '@/stores/settings-phone' +import type {State as SignupState, useSignupState} from '@/stores/signup' +import type {State as TeamsState, useTeamsState} from '@/stores/teams' +import type {State as Tracker2State, useTrackerState} from '@/stores/tracker2' +import type {State as UsersState, useUsersState} from '@/stores/users' + +type StoreName = + | 'chat' + | 'daemon' + | 'fs' + | 'people' + | 'profile' + | 'provision' + | 'push' + | 'recover-password' + | 'settings' + | 'settings-email' + | 'settings-phone' + | 'signup' + | 'teams' + | 'tracker2' + | 'users' + +type StoreStates = { + chat: ChatState + daemon: DaemonState + fs: FSState + people: PeopleState + profile: ProfileState + provision: ProvisionState + push: PushState + 'recover-password': RecoverPasswordState + settings: SettingsState + 'settings-email': SettingsEmailState + 'settings-phone': SettingsPhoneState + signup: SignupState + teams: TeamsState + tracker2: Tracker2State + users: UsersState +} + +type StoreHooks = { + chat: typeof useChatState + daemon: typeof useDaemonState + fs: typeof useFSState + people: typeof usePeopleState + profile: typeof useProfileState + provision: typeof useProvisionState + push: typeof usePushState + 'recover-password': typeof useRecoverPasswordState + settings: typeof useSettingsState + 'settings-email': typeof useSettingsEmailState + 'settings-phone': typeof useSettingsPhoneState + signup: typeof useSignupState + teams: typeof useTeamsState + tracker2: typeof useTrackerState + users: typeof useUsersState +} + +class StoreRegistry { + getStore(storeName: T): StoreHooks[T] { + /* eslint-disable @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-return */ + switch (storeName) { + case 'chat': { + const {useChatState} = require('@/stores/chat2') + return useChatState + } + case 'daemon': { + const {useDaemonState} = require('@/stores/daemon') + return useDaemonState + } + case 'fs': { + const {useFSState} = require('@/stores/fs') + return useFSState + } + case 'people': { + const {usePeopleState} = require('@/stores/people') + return usePeopleState + } + case 'profile': { + const {useProfileState} = require('@/stores/profile') + return useProfileState + } + case 'provision': { + const {useProvisionState} = require('@/stores/provision') + return useProvisionState + } + case 'push': { + const {usePushState} = require('@/stores/push') + return usePushState + } + case 'recover-password': { + const {useState} = require('@/stores/recover-password') + return useState + } + case 'settings': { + const {useSettingsState} = require('@/stores/settings') + return useSettingsState + } + case 'settings-email': { + const {useSettingsEmailState} = require('@/stores/settings-email') + return useSettingsEmailState + } + case 'settings-phone': { + const {useSettingsPhoneState} = require('@/stores/settings-phone') + return useSettingsPhoneState + } + case 'signup': { + const {useSignupState} = require('@/stores/signup') + return useSignupState + } + case 'teams': { + const {useTeamsState} = require('@/stores/teams') + return useTeamsState + } + case 'tracker2': { + const {useTrackerState} = require('@/stores/tracker2') + return useTrackerState + } + case 'users': { + const {useUsersState} = require('@/stores/users') + return useUsersState + } + default: + throw new Error(`Unknown store: ${storeName}`) + } + } + + getState(storeName: T): StoreStates[T] { + return this.getStore(storeName).getState() as StoreStates[T] + } + + getConvoState(id: T.Chat.ConversationIDKey): ConvoState { + const {getConvoState} = require('@/stores/convostate') as typeof ConvoStateType + return getConvoState(id) + } +} + +export const storeRegistry = new StoreRegistry() diff --git a/shared/constants/team-building/index.tsx b/shared/stores/team-building.tsx similarity index 82% rename from shared/constants/team-building/index.tsx rename to shared/stores/team-building.tsx index 0d838f7c2332..fb68799d16c0 100644 --- a/shared/constants/team-building/index.tsx +++ b/shared/stores/team-building.tsx @@ -1,6 +1,5 @@ -import * as T from '../types' -import {ignorePromise} from '../utils' -import * as Router2 from '../router2' +import * as T from '@/constants/types' +import {ignorePromise} from '@/constants/utils' import * as React from 'react' import * as Z from '@/util/zustand' import logger from '@/logger' @@ -11,10 +10,10 @@ import {serviceIdFromString} from '@/util/platforms' import {type StoreApi, type UseBoundStore, useStore} from 'zustand' import {validateEmailAddress} from '@/util/email-address' import {registerDebugClear} from '@/util/debug' -import {searchWaitingKey} from './utils' -import {navigateUp} from '../router2/util' -import {storeRegistry} from '../store-registry' -export {allServices, selfToUser, searchWaitingKey} from './utils' +import {searchWaitingKey} from '@/constants/strings' +import {navigateUp, getModalStack} from '@/constants/router2' +export {allServices, selfToUser} from '@/constants/team-building' +export {searchWaitingKey} from '@/constants/strings' type Store = T.Immutable<{ namespace: T.TB.AllowedNamespace @@ -54,6 +53,16 @@ export interface State extends Store { cancelTeamBuilding: () => void changeSendNotification: (sendNotification: boolean) => void closeTeamBuilding: () => void + defer: { + onAddMembersWizardPushMembers: (members: Array) => void + onFinishedTeamBuildingChat: (users: ReadonlySet) => void + onFinishedTeamBuildingCrypto: (users: ReadonlySet) => void + onGetSettingsContactsImportEnabled: () => boolean | undefined + onGetSettingsContactsUserCountryCode: () => string | undefined + onShowUserProfile: (username: string) => void + onUsersGetBlockState: (usernames: ReadonlyArray) => void + onUsersUpdates: (infos: ReadonlyArray<{name: string; info: Partial}>) => void + } fetchUserRecs: () => void finishTeamBuilding: () => void finishedTeamBuilding: () => void @@ -271,7 +280,7 @@ const createSlice: Z.ImmerStateCreator = (set, get) => { // we want the first item for (const user of teamSoFar) { const username = user.serviceMap.keybase || user.id - storeRegistry.getState('profile').dispatch.showUserProfile(username) + get().dispatch.defer.onShowUserProfile(username) break } }, 100) @@ -291,13 +300,39 @@ const createSlice: Z.ImmerStateCreator = (set, get) => { }) }, closeTeamBuilding: () => { - const modals = Router2.getModalStack() + const modals = getModalStack() const routeNames = [...namespaceToRoute.values()] const routeName = modals.at(-1)?.name if (routeNames.includes(routeName ?? '')) { navigateUp() } }, + defer: { + onAddMembersWizardPushMembers: (_members: Array) => { + throw new Error('onAddMembersWizardPushMembers not properly initialized') + }, + onFinishedTeamBuildingChat: (_users: ReadonlySet) => { + throw new Error('onFinishedTeamBuildingChat not properly initialized') + }, + onFinishedTeamBuildingCrypto: (_users: ReadonlySet) => { + throw new Error('onFinishedTeamBuildingCrypto not properly initialized') + }, + onGetSettingsContactsImportEnabled: () => { + throw new Error('onGetSettingsContactsImportEnabled not properly initialized') + }, + onGetSettingsContactsUserCountryCode: () => { + throw new Error('onGetSettingsContactsUserCountryCode not properly initialized') + }, + onShowUserProfile: (_username: string) => { + throw new Error('onShowUserProfile not properly initialized') + }, + onUsersGetBlockState: (_usernames: ReadonlyArray) => { + throw new Error('onUsersGetBlockState not properly initialized') + }, + onUsersUpdates: (_infos: ReadonlyArray<{name: string; info: Partial}>) => { + throw new Error('onUsersUpdates not properly initialized') + }, + }, fetchUserRecs: () => { const includeContacts = get().namespace === 'chat2' const f = async () => { @@ -313,7 +348,7 @@ const createSlice: Z.ImmerStateCreator = (set, get) => { const contacts = contactRes.map(contactToUser) let suggestions = suggestionRes.map(interestingPersonToUser) const expectingContacts = - storeRegistry.getState('settings-contacts').importEnabled && includeContacts + get().dispatch.defer.onGetSettingsContactsImportEnabled() && includeContacts if (expectingContacts) { suggestions = suggestions.slice(0, 10) } @@ -336,11 +371,9 @@ const createSlice: Z.ImmerStateCreator = (set, get) => { get().dispatch.closeTeamBuilding() const {teamSoFar} = get() if (get().namespace === 'teams') { - storeRegistry - .getState('teams') - .dispatch.addMembersWizardPushMembers( - [...teamSoFar].map(user => ({assertion: user.id, role: 'writer'})) - ) + get().dispatch.defer.onAddMembersWizardPushMembers( + [...teamSoFar].map(user => ({assertion: user.id, role: 'writer'})) + ) get().dispatch.finishedTeamBuilding() } }, @@ -360,11 +393,11 @@ const createSlice: Z.ImmerStateCreator = (set, get) => { const {finishedTeam, namespace} = get() switch (namespace) { case 'crypto': { - storeRegistry.getState('crypto').dispatch.onTeamBuildingFinished(finishedTeam) + get().dispatch.defer.onFinishedTeamBuildingCrypto(finishedTeam) break } case 'chat2': { - storeRegistry.getState('chat').dispatch.onTeamBuildingFinished(finishedTeam) + get().dispatch.defer.onFinishedTeamBuildingChat(finishedTeam) break } default: @@ -415,7 +448,7 @@ const createSlice: Z.ImmerStateCreator = (set, get) => { let users: typeof _users if (selectedService === 'keybase') { // If we are on Keybase tab, do additional search if query is phone/email. - const userRegion = storeRegistry.getState('settings-contacts').userCountryCode + const userRegion = get().dispatch.defer.onGetSettingsContactsUserCountryCode() users = await specialContactSearch(_users, searchQuery, userRegion) } else { users = _users @@ -432,7 +465,7 @@ const createSlice: Z.ImmerStateCreator = (set, get) => { } return arr }, new Array<{info: {fullname: string}; name: string}>()) - storeRegistry.getState('users').dispatch.updates(updates) + get().dispatch.defer.onUsersUpdates(updates) const blocks = users.reduce((arr, {serviceMap}) => { const {keybase} = serviceMap if (keybase) { @@ -440,7 +473,9 @@ const createSlice: Z.ImmerStateCreator = (set, get) => { } return arr }, new Array()) - blocks.length && storeRegistry.getState('users').dispatch.getBlockState(blocks) + if (blocks.length) { + get().dispatch.defer.onUsersGetBlockState(blocks) + } } ignorePromise(f()) }, @@ -477,6 +512,9 @@ export const createTBStore = (namespace: T.TB.AllowedNamespace) => { return next } +export const getTBStore = (namespace: T.TB.AllowedNamespace): State => + createTBStore(namespace).getState() + const Context = React.createContext(null) type TBProviderProps = React.PropsWithChildren<{namespace: T.TB.AllowedNamespace}> diff --git a/shared/constants/teams/index.tsx b/shared/stores/teams.tsx similarity index 96% rename from shared/constants/teams/index.tsx rename to shared/stores/teams.tsx index d221afa1fddd..ac4b61953748 100644 --- a/shared/constants/teams/index.tsx +++ b/shared/stores/teams.tsx @@ -1,6 +1,6 @@ -import * as S from '../strings' -import {ignorePromise, wrapErrors} from '../utils' -import * as T from '../types' +import * as S from '@/constants/strings' +import {ignorePromise, wrapErrors} from '@/constants/utils' +import * as T from '@/constants/types' import * as EngineGen from '@/actions/engine-gen-gen' import { getVisibleScreen, @@ -9,19 +9,23 @@ import { navigateUp, navUpToScreen, navToProfile, -} from '../router2/util' +} from '@/constants/router2' import * as Z from '@/util/zustand' import invert from 'lodash/invert' import logger from '@/logger' import openSMS from '@/util/sms' import {RPCError, logError} from '@/util/errors' -import {isMobile, isPhone} from '../platform' +import {isMobile, isPhone} from '@/constants/platform' import {mapGetEnsureValue} from '@/util/map' -import {bodyToJSON} from '../rpc-utils' +import {bodyToJSON} from '@/constants/rpc-utils' import {fixCrop} from '@/util/crop' -import {storeRegistry} from '../store-registry' -import * as Util from './util' -import {getTab} from '../router2/util' +import {getTBStore} from '@/stores/team-building' +import {storeRegistry} from '@/stores/store-registry' +import {useConfigState} from '@/stores/config' +import {type useChatState} from '@/stores/chat2' +import {useCurrentUserState} from '@/stores/current-user' +import * as Util from '@/constants/teams' +import {getTab} from '@/constants/router2' export { baseRetentionPolicies, @@ -31,7 +35,7 @@ export { teamRoleByEnum, retentionPolicyToServiceRetentionPolicy, userIsRoleInTeamWithInfo, -} from './util' +} from '@/constants/teams' export const teamRoleTypes = ['reader', 'writer', 'admin', 'owner'] as const @@ -304,7 +308,7 @@ export const getDisabledReasonsForRolePicker = ( theyAreOwner = membersToModify.some(username => members.get(username)?.type === 'owner') } - const myUsername = storeRegistry.getState('current-user').username + const myUsername = useCurrentUserState.getState().username const you = members.get(myUsername) // Fallback to the lowest role, although this shouldn't happen const yourRole = you?.type ?? 'reader' @@ -862,6 +866,13 @@ const initialStore: Store = { export interface State extends Store { dispatch: { + defer: { + onChatNavigateToInbox?: (allowSwitchTab?: boolean) => void + onChatPreviewConversation?: ( + p: Parameters['dispatch']['previewConversation']>[0] + ) => void + onUsersUpdates?: (updates: ReadonlyArray<{name: string; info: Partial}>) => void + } dynamic: { respondToInviteLink?: (accept: boolean) => void } @@ -1044,7 +1055,7 @@ export interface State extends Store { sendChatNotification: boolean, crop?: T.RPCGen.ImageCropRect ) => void - updateTeamRetentionPolicy: (metas: Array) => void + updateTeamRetentionPolicy: (metas: ReadonlyArray) => void } } @@ -1220,7 +1231,7 @@ export const useTeamsState = Z.createZustand((set, get) => { ) if (res.notAdded && res.notAdded.length > 0) { const usernames = res.notAdded.map(elem => elem.username) - storeRegistry.getTBStore('teams').dispatch.finishedTeamBuilding() + getTBStore('teams').dispatch.finishedTeamBuilding() navigateAppend({ props: {source: 'teamAddSomeFailed', usernames}, selected: 'contactRestricted', @@ -1232,7 +1243,7 @@ export const useTeamsState = Z.createZustand((set, get) => { s.errorInAddToTeam = '' }) if (fromTeamBuilder) { - storeRegistry.getTBStore('teams').dispatch.finishedTeamBuilding() + getTBStore('teams').dispatch.finishedTeamBuilding() } } catch (error) { if (!(error instanceof RPCError)) { @@ -1244,7 +1255,7 @@ export const useTeamsState = Z.createZustand((set, get) => { ?.filter(elem => elem?.key === 'usernames') .map(elem => elem?.value) const usernames = users?.[0]?.split(',') ?? [] - storeRegistry.getTBStore('teams').dispatch.finishedTeamBuilding() + getTBStore('teams').dispatch.finishedTeamBuilding() navigateAppend({ props: {source: 'teamAddAllFailed', usernames}, selected: 'contactRestricted', @@ -1258,7 +1269,7 @@ export const useTeamsState = Z.createZustand((set, get) => { }) // TODO this should not error on member already in team if (fromTeamBuilder) { - storeRegistry.getTBStore('teams').dispatch.setError(msg) + getTBStore('teams').dispatch.setError(msg) } } } @@ -1423,7 +1434,7 @@ export const useTeamsState = Z.createZustand((set, get) => { get().dispatch.loadTeamChannelList(teamID) // Select the new channel, and switch to the chat tab. if (navToChatOnSuccess) { - storeRegistry.getState('chat').dispatch.previewConversation({ + get().dispatch.defer.onChatPreviewConversation?.({ channelname, conversationIDKey: newConversationIDKey, reason: 'newChannel', @@ -1493,16 +1504,17 @@ export const useTeamsState = Z.createZustand((set, get) => { if (fromChat) { clearModals() - const {previewConversation, navigateToInbox} = storeRegistry.getState('chat').dispatch - navigateToInbox() - previewConversation({channelname: 'general', reason: 'convertAdHoc', teamname}) + get().dispatch.defer.onChatNavigateToInbox?.() + get().dispatch.defer.onChatPreviewConversation?.({ + channelname: 'general', + reason: 'convertAdHoc', + teamname, + }) } else { clearModals() navigateAppend({props: {teamID}, selected: 'team'}) if (isMobile) { - storeRegistry - .getState('router') - .dispatch.navigateAppend({props: {createdTeam: true, teamID}, selected: 'profileEditAvatar'}) + navigateAppend({props: {createdTeam: true, teamID}, selected: 'profileEditAvatar'}) } } } catch (error) { @@ -1519,7 +1531,7 @@ export const useTeamsState = Z.createZustand((set, get) => { set(s => { s.errorInTeamCreation = '' }) - const me = storeRegistry.getState('current-user').username + const me = useCurrentUserState.getState().username const participantInfo = storeRegistry.getConvoState(conversationIDKey).participants // exclude bots from the newly created team, they can be added back later. const participants = participantInfo.name.filter(p => p !== me) // we will already be in as 'owner' @@ -1529,6 +1541,22 @@ export const useTeamsState = Z.createZustand((set, get) => { })) get().dispatch.createNewTeam(teamname, false, true, {sendChatNotification: true, users}) }, + defer: { + onChatNavigateToInbox: (_allowSwitchTab?: boolean) => { + throw new Error('onChatNavigateToInbox not implemented') + }, + onChatPreviewConversation: (_p: { + channelname?: string + conversationIDKey?: T.Chat.ConversationIDKey + reason?: string + teamname?: string + }) => { + throw new Error('onChatPreviewConversation not implemented') + }, + onUsersUpdates: (_updates: ReadonlyArray<{name: string; info: Partial}>) => { + throw new Error('onUsersUpdates not implemented') + }, + }, deleteChannelConfirmed: (teamID, conversationIDKey) => { const f = async () => { // channelName is only needed for confirmation, so since we handle @@ -1725,7 +1753,7 @@ export const useTeamsState = Z.createZustand((set, get) => { set(s => { s.teamIDToMembers.set(teamID, members) }) - storeRegistry.getState('users').dispatch.updates( + get().dispatch.defer.onUsersUpdates?.( [...members.values()].map(m => ({ info: {fullname: m.fullName}, name: m.username, @@ -1790,8 +1818,8 @@ export const useTeamsState = Z.createZustand((set, get) => { } const f = async () => { - const username = storeRegistry.getState('current-user').username - const loggedIn = storeRegistry.getState('config').loggedIn + const username = useCurrentUserState.getState().username + const loggedIn = useConfigState.getState().loggedIn if (!username || !loggedIn) { logger.warn('getTeams while logged out') return @@ -2029,9 +2057,7 @@ export const useTeamsState = Z.createZustand((set, get) => { ) logger.info(`leaveTeam: left ${teamname} successfully`) clearModals() - storeRegistry - .getState('router') - .dispatch.navUpToScreen(context === 'chat' ? 'chatRoot' : 'teamsRoot') + navUpToScreen(context === 'chat' ? 'chatRoot' : 'teamsRoot') get().dispatch.getTeams() } catch (error) { if (error instanceof RPCError) { @@ -2163,9 +2189,7 @@ export const useTeamsState = Z.createZustand((set, get) => { }) }, manageChatChannels: teamID => { - storeRegistry - .getState('router') - .dispatch.navigateAppend({props: {teamID}, selected: 'teamAddToChannels'}) + navigateAppend({props: {teamID}, selected: 'teamAddToChannels'}) }, notifyTeamTeamRoleMapChanged: (newVersion: number) => { const loadedVersion = get().teamRoleMap.loadedVersion @@ -2307,7 +2331,7 @@ export const useTeamsState = Z.createZustand((set, get) => { break case EngineGen.keybase1NotifyBadgesBadgeState: { const {badgeState} = action.payload.params - const loggedIn = storeRegistry.getState('config').loggedIn + const loggedIn = useConfigState.getState().loggedIn if (loggedIn) { const deletedTeams = badgeState.deletedTeams || [] const newTeams = new Set(badgeState.newTeams || []) @@ -2538,14 +2562,14 @@ export const useTeamsState = Z.createZustand((set, get) => { const convID = T.Chat.keyToConversationID(conversationIDKey) await T.RPCChat.localJoinConversationByIDLocalRpcPromise({convID}, waitingKey) } catch (error) { - storeRegistry.getState('config').dispatch.setGlobalError(error) + useConfigState.getState().dispatch.setGlobalError(error) } } else { try { const convID = T.Chat.keyToConversationID(conversationIDKey) await T.RPCChat.localLeaveConversationLocalRpcPromise({convID}, waitingKey) } catch (error) { - storeRegistry.getState('config').dispatch.setGlobalError(error) + useConfigState.getState().dispatch.setGlobalError(error) } } } @@ -2658,14 +2682,14 @@ export const useTeamsState = Z.createZustand((set, get) => { teamID, }) } catch (payload) { - storeRegistry.getState('config').dispatch.setGlobalError(payload) + useConfigState.getState().dispatch.setGlobalError(payload) } } if (ignoreAccessRequests !== settings.ignoreAccessRequests) { try { await T.RPCGen.teamsSetTarsDisabledRpcPromise({disabled: settings.ignoreAccessRequests, teamID}) } catch (payload) { - storeRegistry.getState('config').dispatch.setGlobalError(payload) + useConfigState.getState().dispatch.setGlobalError(payload) } } if (publicityAnyMember !== settings.publicityAnyMember) { @@ -2675,7 +2699,7 @@ export const useTeamsState = Z.createZustand((set, get) => { teamID, }) } catch (payload) { - storeRegistry.getState('config').dispatch.setGlobalError(payload) + useConfigState.getState().dispatch.setGlobalError(payload) } } if (publicityMember !== settings.publicityMember) { @@ -2685,14 +2709,14 @@ export const useTeamsState = Z.createZustand((set, get) => { teamID, }) } catch (payload) { - storeRegistry.getState('config').dispatch.setGlobalError(payload) + useConfigState.getState().dispatch.setGlobalError(payload) } } if (publicityTeam !== settings.publicityTeam) { try { await T.RPCGen.teamsSetTeamShowcaseRpcPromise({isShowcased: settings.publicityTeam, teamID}) } catch (payload) { - storeRegistry.getState('config').dispatch.setGlobalError(payload) + useConfigState.getState().dispatch.setGlobalError(payload) } } } @@ -2871,13 +2895,9 @@ export const useTeamsState = Z.createZustand((set, get) => { logger.info(`team="${teamname}" cannot be loaded:`, err) // navigate to team page for team we're not in logger.info(`showing external team page, join=${join}`) - storeRegistry - .getState('router') - .dispatch.navigateAppend({props: {teamname}, selected: 'teamExternalTeam'}) + navigateAppend({props: {teamname}, selected: 'teamExternalTeam'}) if (join) { - storeRegistry - .getState('router') - .dispatch.navigateAppend({props: {initialTeamname: teamname}, selected: 'teamJoinTeamDialog'}) + navigateAppend({props: {initialTeamname: teamname}, selected: 'teamJoinTeamDialog'}) } return } @@ -2899,9 +2919,7 @@ export const useTeamsState = Z.createZustand((set, get) => { return } } - storeRegistry - .getState('router') - .dispatch.navigateAppend({props: {initialTab, teamID}, selected: 'team'}) + navigateAppend({props: {initialTab, teamID}, selected: 'team'}) if (addMembers) { navigateAppend({ props: {namespace: 'teams', teamID, title: ''}, diff --git a/shared/constants/tracker2/index.tsx b/shared/stores/tracker2.tsx similarity index 93% rename from shared/constants/tracker2/index.tsx rename to shared/stores/tracker2.tsx index 8ea35a1b511e..8b9475cb04a8 100644 --- a/shared/constants/tracker2/index.tsx +++ b/shared/stores/tracker2.tsx @@ -1,13 +1,13 @@ -import * as S from '../strings' +import * as S from '@/constants/strings' import * as EngineGen from '@/actions/engine-gen-gen' -import {generateGUIID, ignorePromise} from '../utils' +import {generateGUIID, ignorePromise} from '@/constants/utils' import * as Z from '@/util/zustand' import logger from '@/logger' -import * as T from '../types' +import * as T from '@/constants/types' import {RPCError} from '@/util/errors' import {mapGetEnsureValue} from '@/util/map' -import {navigateAppend, navigateUp} from '../router2/util' -import {storeRegistry} from '../store-registry' +import {navigateAppend, navigateUp} from '@/constants/router2' +import {useCurrentUserState} from '@/stores/current-user' export const noDetails: T.Tracker.Details = { assertions: new Map(), @@ -163,6 +163,10 @@ const initialStore: Store = { export interface State extends Store { dispatch: { + defer: { + onShowUserProfile?: (username: string) => void + onUsersUpdates?: (updates: ReadonlyArray<{name: string; info: Partial}>) => void + } changeFollow: (guiID: string, follow: boolean) => void closeTracker: (guiID: string) => void getProofSuggestions: () => void @@ -227,6 +231,14 @@ export const useTrackerState = Z.createZustand((set, get) => { s.showTrackerSet.delete(username) }) }, + defer: { + onShowUserProfile: () => { + throw new Error('onShowUserProfile not implemented') + }, + onUsersUpdates: () => { + throw new Error('onUsersUpdates not implemented') + }, + }, getProofSuggestions: () => { const f = async () => { try { @@ -290,13 +302,13 @@ export const useTrackerState = Z.createZustand((set, get) => { } else if (error.code === T.RPCGen.StatusCode.scnotfound) { // we're on the profile page for a user that does not exist. Currently the only way // to get here is with an invalid link or deeplink. - storeRegistry - .getState('deeplinks') - .dispatch.setLinkError( - `You followed a profile link for a user (${assertion}) that does not exist.` - ) navigateUp() - navigateAppend('keybaseLinkError') + navigateAppend({ + props: { + error: `You followed a profile link for a user (${assertion}) that does not exist.`, + }, + selected: 'keybaseLinkError', + }) } // hooked into reloadable logger.error(`Error loading profile: ${error.message}`) @@ -319,9 +331,9 @@ export const useTrackerState = Z.createZustand((set, get) => { d.followersCount = d.followers.size }) if (fs.users) { - storeRegistry - .getState('users') - .dispatch.updates(fs.users.map(u => ({info: {fullname: u.fullName}, name: u.username}))) + get().dispatch.defer.onUsersUpdates?.( + fs.users.map(u => ({info: {fullname: u.fullName}, name: u.username})) + ) } } catch (error) { if (error instanceof RPCError) { @@ -345,9 +357,9 @@ export const useTrackerState = Z.createZustand((set, get) => { d.followingCount = d.following.size }) if (fs.users) { - storeRegistry - .getState('users') - .dispatch.updates(fs.users.map(u => ({info: {fullname: u.fullName}, name: u.username}))) + get().dispatch.defer.onUsersUpdates?.( + fs.users.map(u => ({info: {fullname: u.fullName}, name: u.username})) + ) } } catch (error) { if (error instanceof RPCError) { @@ -435,8 +447,7 @@ export const useTrackerState = Z.createZustand((set, get) => { ) d.hidFromFollowers = hidFromFollowers }) - username && - storeRegistry.getState('users').dispatch.updates([{info: {fullname: card.fullName}, name: username}]) + username && get().dispatch.defer.onUsersUpdates?.([{info: {fullname: card.fullName}, name: username}]) }, notifyReset: guiID => { set(s => { @@ -531,11 +542,11 @@ export const useTrackerState = Z.createZustand((set, get) => { } // if we mutated somehow reload ourselves and reget the suggestions case EngineGen.keybase1NotifyUsersUserChanged: { - if (storeRegistry.getState('current-user').uid !== action.payload.params.uid) { + if (useCurrentUserState.getState().uid !== action.payload.params.uid) { return } get().dispatch.load({ - assertion: storeRegistry.getState('current-user').username, + assertion: useCurrentUserState.getState().username, forceDisplay: false, fromDaemon: false, guiID: generateGUIID(), @@ -595,7 +606,7 @@ export const useTrackerState = Z.createZustand((set, get) => { }) if (!skipNav) { // go to profile page - storeRegistry.getState('profile').dispatch.showUserProfile(username) + get().dispatch.defer.onShowUserProfile?.(username) } }, updateResult: (guiID, result, reason) => { diff --git a/shared/constants/unlock-folders/index.tsx b/shared/stores/unlock-folders.tsx similarity index 86% rename from shared/constants/unlock-folders/index.tsx rename to shared/stores/unlock-folders.tsx index dc4a1d43ca00..d8fa1d8dd573 100644 --- a/shared/constants/unlock-folders/index.tsx +++ b/shared/stores/unlock-folders.tsx @@ -1,10 +1,9 @@ import * as EngineGen from '@/actions/engine-gen-gen' -import * as T from '../types' +import * as T from '@/constants/types' import * as Z from '@/util/zustand' import logger from '@/logger' import {getEngine} from '@/engine/require' -import type {State as ConfigStore} from '../config' -import {storeRegistry} from '../store-registry' +import {useConfigState, type State as ConfigStore} from '@/stores/config' type Store = T.Immutable<{ devices: ConfigStore['unlockFoldersDevices'] @@ -39,7 +38,7 @@ export const useUnlockFoldersState = Z.createZustand((set, _get) => { case EngineGen.keybase1RekeyUIRefresh: { const {problemSetDevices} = action.payload.params logger.info('Asked for rekey') - storeRegistry.getState('config').dispatch.openUnlockFolders(problemSetDevices.devices ?? []) + useConfigState.getState().dispatch.openUnlockFolders(problemSetDevices.devices ?? []) break } case EngineGen.keybase1RekeyUIDelegateRekeyUI: { @@ -49,7 +48,7 @@ export const useUnlockFoldersState = Z.createZustand((set, _get) => { dangling: true, incomingCallMap: { 'keybase.1.rekeyUI.refresh': ({problemSetDevices}) => { - storeRegistry.getState('config').dispatch.openUnlockFolders(problemSetDevices.devices ?? []) + useConfigState.getState().dispatch.openUnlockFolders(problemSetDevices.devices ?? []) }, 'keybase.1.rekeyUI.rekeySendEvent': () => {}, // ignored debug call from daemon }, diff --git a/shared/constants/users/index.tsx b/shared/stores/users.tsx similarity index 96% rename from shared/constants/users/index.tsx rename to shared/stores/users.tsx index 642c3c0f3ec9..618264f110ac 100644 --- a/shared/constants/users/index.tsx +++ b/shared/stores/users.tsx @@ -1,11 +1,11 @@ import * as EngineGen from '@/actions/engine-gen-gen' import * as Z from '@/util/zustand' import logger from '@/logger' -import * as T from '../types' +import * as T from '@/constants/types' import {mapGetEnsureValue} from '@/util/map' -import {ignorePromise} from '../utils' -import {RPCError, isNetworkErr} from '../utils' -import * as S from '../strings' +import {ignorePromise} from '@/constants/utils' +import {RPCError, isNetworkErr} from '@/constants/utils' +import * as S from '@/constants/strings' type Store = T.Immutable<{ blockMap: Map diff --git a/shared/constants/waiting/index.tsx b/shared/stores/waiting.tsx similarity index 94% rename from shared/constants/waiting/index.tsx rename to shared/stores/waiting.tsx index 4ba0a2f8388a..effcb51b5006 100644 --- a/shared/constants/waiting/index.tsx +++ b/shared/stores/waiting.tsx @@ -1,7 +1,8 @@ import type {RPCError} from '@/util/errors' -import type * as T from '../types' +import type * as T from '@/constants/types' import * as Z from '@/util/zustand' +// This store has no dependencies on other stores and is safe to import directly from other stores. const initialStore: T.Waiting.State = { counts: new Map(), errors: new Map(), diff --git a/shared/constants/wallets/index.tsx b/shared/stores/wallets.tsx similarity index 84% rename from shared/constants/wallets/index.tsx rename to shared/stores/wallets.tsx index b6d0b28eff96..0305e954dfd6 100644 --- a/shared/constants/wallets/index.tsx +++ b/shared/stores/wallets.tsx @@ -1,10 +1,10 @@ -import * as T from '../types' -import {ignorePromise} from '../utils' +import * as T from '@/constants/types' +import {ignorePromise} from '@/constants/utils' import * as Z from '@/util/zustand' -import {loadAccountsWaitingKey} from './utils' -import {storeRegistry} from '../store-registry' +import {loadAccountsWaitingKey} from '@/constants/strings' +import {useConfigState} from '@/stores/config' -export {loadAccountsWaitingKey} from './utils' +export {loadAccountsWaitingKey} from '@/constants/strings' export type Account = { accountID: string @@ -33,7 +33,7 @@ export const useState = Z.createZustand((set, get) => { const dispatch: State['dispatch'] = { load: () => { const f = async () => { - if (!storeRegistry.getState('config').loggedIn) { + if (!useConfigState.getState().loggedIn) { return } const res = await T.RPCStellar.localGetWalletAccountsLocalRpcPromise(undefined, [ diff --git a/shared/stores/whats-new.tsx b/shared/stores/whats-new.tsx new file mode 100644 index 000000000000..c32512b33505 --- /dev/null +++ b/shared/stores/whats-new.tsx @@ -0,0 +1,118 @@ +import type * as T from '@/constants/types' +import * as Z from '@/util/zustand' +import {uint8ArrayToString} from 'uint8array-extras' +import {currentVersion, lastVersion, lastLastVersion} from '@/constants/strings' +export {currentVersion, lastVersion, lastLastVersion, keybaseFM} from '@/constants/strings' + +const noVersion: string = '0.0.0' +export {noVersion} + +// This store has no dependencies on other stores and is safe to import directly from other stores. +type SeenVersionsMap = {[key in string]: boolean} + +const semver = { + gte: (a: string, b: string) => { + const arra = a.split('.').map(i => parseInt(i)) + const [a1, a2, a3] = arra + const arrb = b.split('.').map(i => parseInt(i)) + const [b1, b2, b3] = arrb + if (arra.length === 3 && arrb.length === 3) { + return a1! >= b1! && a2! >= b2! && a3! >= b3! + } else { + return false + } + }, + valid: (v: string) => + v.split('.').reduce((cnt, i) => { + if (parseInt(i) >= 0) { + return cnt + 1 + } + return cnt + }, 0) === 3, +} + +const versions = [currentVersion, lastVersion, lastLastVersion, noVersion] as const + +const isVersionValid = (version: string) => { + return version ? semver.valid(version) : false +} + +const getSeenVersions = (lastSeenVersion: string): SeenVersionsMap => { + const initialMap: SeenVersionsMap = { + [currentVersion]: true, + [lastLastVersion]: true, + [lastVersion]: true, + [noVersion]: true, + } + + if (!lastSeenVersion || !semver.valid(lastSeenVersion)) { + return initialMap + } + if (lastSeenVersion === noVersion) { + return { + [currentVersion]: false, + [lastLastVersion]: false, + [lastVersion]: false, + [noVersion]: false, + } + } + + const validVersions = versions.filter(isVersionValid) + + const seenVersions = validVersions.reduce( + (acc, version) => ({ + ...acc, + [version]: version === noVersion ? true : semver.gte(lastSeenVersion, version), + }), + initialMap + ) + + return seenVersions +} + +type Store = T.Immutable<{ + lastSeenVersion: string + seenVersions: SeenVersionsMap +}> +const initialStore: Store = { + lastSeenVersion: '', + seenVersions: getSeenVersions(''), +} +export interface State extends Store { + dispatch: { + resetState: 'default' + updateLastSeen: (lastSeenItem?: {md: T.RPCGen.Gregor1.Metadata; item: T.RPCGen.Gregor1.Item}) => void + } + anyVersionsUnseen: () => boolean +} +export const useWhatsNewState = Z.createZustand((set, get) => { + const dispatch: State['dispatch'] = { + resetState: 'default', + updateLastSeen: lastSeenItem => { + if (lastSeenItem) { + const {body} = lastSeenItem.item + const pushStateLastSeenVersion = uint8ArrayToString(body) + const lastSeenVersion = pushStateLastSeenVersion || noVersion + // Default to 0.0.0 (noVersion) if user has never marked a version as seen + set(s => { + s.lastSeenVersion = lastSeenVersion + s.seenVersions = getSeenVersions(lastSeenVersion) + }) + } else { + set(s => { + s.lastSeenVersion = noVersion + s.seenVersions = getSeenVersions(noVersion) + }) + } + }, + } + return { + ...initialStore, + anyVersionsUnseen: () => { + const {lastSeenVersion: ver} = get() + // On first load of what's new, lastSeenVersion == noVersion so everything is unseen + return ver !== '' && ver === noVersion ? true : Object.values(getSeenVersions(ver)).some(seen => !seen) + }, + dispatch, + } +}) diff --git a/shared/styles/colors.tsx b/shared/styles/colors.tsx index f84ad80c59b3..76c0da24acae 100644 --- a/shared/styles/colors.tsx +++ b/shared/styles/colors.tsx @@ -1,5 +1,5 @@ // the _on_white are precomputed colors so we can do less blending on mobile -import {useDarkModeState} from '@/constants/darkmode' +import {useDarkModeState} from '@/stores/darkmode' import {isIOS, isAndroid} from '@/constants/platform' import type {DynamicColorIOS as DynamicColorIOSType} from 'react-native' import type {Opaque} from '@/constants/types/ts' diff --git a/shared/styles/index.native.tsx b/shared/styles/index.native.tsx index 0fc8b52b108c..a15e6e2db217 100644 --- a/shared/styles/index.native.tsx +++ b/shared/styles/index.native.tsx @@ -2,7 +2,7 @@ import * as Shared from './shared' import {colors as lightColors} from './colors' import styleSheetCreateProxy, {type MapToStyles} from './style-sheet-proxy' import {StyleSheet, Dimensions} from 'react-native' -import {useDarkModeState} from '@/constants/darkmode' +import {useDarkModeState} from '@/stores/darkmode' import {isIOS, isTablet} from '@/constants/platform' const font = isIOS diff --git a/shared/styles/style-sheet-proxy.tsx b/shared/styles/style-sheet-proxy.tsx index 37cb8404f801..a6d33b090b3b 100644 --- a/shared/styles/style-sheet-proxy.tsx +++ b/shared/styles/style-sheet-proxy.tsx @@ -1,4 +1,4 @@ -import {useDarkModeState} from '@/constants/darkmode' +import {useDarkModeState} from '@/stores/darkmode' import type {StylesCrossPlatform} from '.' // Support a closure to enable simple dark mode. diff --git a/shared/team-building/contacts.tsx b/shared/team-building/contacts.tsx index 6d39827419c6..06edc5fb79f2 100644 --- a/shared/team-building/contacts.tsx +++ b/shared/team-building/contacts.tsx @@ -1,8 +1,8 @@ import * as React from 'react' import * as Kb from '@/common-adapters' import type * as T from '@/constants/types' -import {useSettingsContactsState} from '@/constants/settings-contacts' -import {useTBContext} from '@/constants/team-building' +import {useSettingsContactsState} from '@/stores/settings-contacts' +import {useTBContext} from '@/stores/team-building' const useContactsProps = () => { const contactsImported = useSettingsContactsState(s => s.importEnabled) diff --git a/shared/team-building/email-search.tsx b/shared/team-building/email-search.tsx index 669e819c92a4..2f5f8f17930d 100644 --- a/shared/team-building/email-search.tsx +++ b/shared/team-building/email-search.tsx @@ -1,11 +1,12 @@ import * as C from '@/constants' -import * as TB from '@/constants/team-building' +import * as TB from '@/stores/team-building' import * as React from 'react' import * as Kb from '@/common-adapters' import type * as T from '@/constants/types' import {validateEmailAddress} from '@/util/email-address' import {UserMatchMention} from './phone-search' import ContinueButton from './continue-button' +import {searchWaitingKey} from '@/constants/strings' type EmailSearchProps = { continueLabel: string @@ -17,7 +18,7 @@ const EmailSearch = ({continueLabel, namespace, search}: EmailSearchProps) => { const teamBuildingSearchResults = TB.useTBContext(s => s.searchResults) const [isEmailValid, setEmailValidity] = React.useState(false) const [emailString, setEmailString] = React.useState('') - const waiting = C.Waiting.useAnyWaiting(TB.searchWaitingKey) + const waiting = C.Waiting.useAnyWaiting(searchWaitingKey) const user: T.TB.User | undefined = teamBuildingSearchResults.get(emailString)?.get('email')?.[0] const canSubmit = !!user && !waiting && isEmailValid diff --git a/shared/team-building/filtered-service-tab-bar.tsx b/shared/team-building/filtered-service-tab-bar.tsx index 353ce9fa5665..b882965f5429 100644 --- a/shared/team-building/filtered-service-tab-bar.tsx +++ b/shared/team-building/filtered-service-tab-bar.tsx @@ -1,7 +1,7 @@ import * as React from 'react' import type * as T from '@/constants/types' import {ServiceTabBar} from './service-tab-bar' -import * as TeamBuilding from '@/constants/team-building' +import * as TeamBuilding from '@/stores/team-building' export const FilteredServiceTabBar = ( props: Omit, 'services'> & { diff --git a/shared/team-building/index.tsx b/shared/team-building/index.tsx index bbc6518549f4..7b94ab175862 100644 --- a/shared/team-building/index.tsx +++ b/shared/team-building/index.tsx @@ -1,6 +1,6 @@ import * as C from '@/constants' -import * as TB from '@/constants/team-building' -import * as Teams from '@/constants/teams' +import * as TB from '@/stores/team-building' +import * as Teams from '@/stores/teams' import * as Kb from '@/common-adapters' import * as React from 'react' import * as T from '@/constants/types' diff --git a/shared/team-building/list-body.tsx b/shared/team-building/list-body.tsx index d9cb0fcbeb9a..20b0bcd9dac5 100644 --- a/shared/team-building/list-body.tsx +++ b/shared/team-building/list-body.tsx @@ -1,7 +1,7 @@ import * as React from 'react' import * as C from '@/constants' -import * as TB from '@/constants/team-building' -import {useTeamsState} from '@/constants/teams' +import * as TB from '@/stores/team-building' +import {useTeamsState} from '@/stores/teams' import * as Kb from '@/common-adapters' import * as Shared from './shared' import PeopleResult from './search-result/people-result' @@ -14,9 +14,9 @@ import type {RootRouteProps} from '@/router-v2/route-params' import {RecsAndRecos, numSectionLabel} from './recs-and-recos' import {formatAnyPhoneNumbers} from '@/util/phone-numbers' import {useRoute} from '@react-navigation/native' -import {useSettingsContactsState} from '@/constants/settings-contacts' -import {useFollowerState} from '@/constants/followers' -import {useCurrentUserState} from '@/constants/current-user' +import {useSettingsContactsState} from '@/stores/settings-contacts' +import {useFollowerState} from '@/stores/followers' +import {useCurrentUserState} from '@/stores/current-user' // import {useAnimatedScrollHandler} from '@/common-adapters/reanimated' import {useColorScheme} from 'react-native' diff --git a/shared/team-building/page.tsx b/shared/team-building/page.tsx index a5f899216400..083c6b96837b 100644 --- a/shared/team-building/page.tsx +++ b/shared/team-building/page.tsx @@ -1,7 +1,7 @@ import type * as C from '@/constants' import * as Kb from '@/common-adapters' import * as React from 'react' -import {TBProvider} from '@/constants/team-building' +import {TBProvider} from '@/stores/team-building' const getOptions = ({route}: OwnProps) => { const namespace: unknown = route.params.namespace diff --git a/shared/team-building/phone-search.tsx b/shared/team-building/phone-search.tsx index 4a93fd2dd078..7c3a04a330bd 100644 --- a/shared/team-building/phone-search.tsx +++ b/shared/team-building/phone-search.tsx @@ -1,10 +1,11 @@ import * as C from '@/constants' -import * as TB from '@/constants/team-building' +import * as TB from '@/stores/team-building' import * as React from 'react' import * as Kb from '@/common-adapters/index' -import type * as T from 'constants/types' +import type * as T from '@/constants/types' import ContinueButton from './continue-button' -import {useSettingsPhoneState} from '@/constants/settings-phone' +import {useSettingsPhoneState} from '@/stores/settings-phone' +import {searchWaitingKey} from '@/constants/strings' type PhoneSearchProps = { continueLabel: string @@ -18,7 +19,7 @@ const PhoneSearch = (props: PhoneSearchProps) => { const [isPhoneValid, setPhoneValidity] = React.useState(false) const [phoneNumber, setPhoneNumber] = React.useState('') const [phoneInputKey, setPhoneInputKey] = React.useState(0) - const waiting = C.Waiting.useAnyWaiting(TB.searchWaitingKey) + const waiting = C.Waiting.useAnyWaiting(searchWaitingKey) const loadDefaultPhoneCountry = useSettingsPhoneState(s => s.dispatch.loadDefaultPhoneCountry) // trigger a default phone number country rpc if it's not already loaded const defaultCountry = useSettingsPhoneState(s => s.defaultCountry) diff --git a/shared/team-building/search-result/hellobot-result.tsx b/shared/team-building/search-result/hellobot-result.tsx index 029e730955d1..8e080121a890 100644 --- a/shared/team-building/search-result/hellobot-result.tsx +++ b/shared/team-building/search-result/hellobot-result.tsx @@ -1,6 +1,6 @@ -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as React from 'react' -import {useTBContext} from '@/constants/team-building' +import {useTBContext} from '@/stores/team-building' import * as Kb from '@/common-adapters' import CommonResult, {type ResultProps} from './common-result' diff --git a/shared/team-building/search-result/people-result.tsx b/shared/team-building/search-result/people-result.tsx index 82bf99580a2c..7925b8767ac3 100644 --- a/shared/team-building/search-result/people-result.tsx +++ b/shared/team-building/search-result/people-result.tsx @@ -1,12 +1,12 @@ import * as C from '@/constants' -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as React from 'react' import * as Kb from '@/common-adapters' import * as T from '@/constants/types' -import * as FS from '@/constants/fs' +import * as FS from '@/stores/fs' import CommonResult, {type ResultProps} from './common-result' -import {useUsersState} from '@/constants/users' -import {useCurrentUserState} from '@/constants/current-user' +import {useUsersState} from '@/stores/users' +import {useCurrentUserState} from '@/stores/current-user' /* * This component is intended to be a drop-in replacement for UserResult. @@ -32,14 +32,14 @@ const PeopleResult = React.memo(function PeopleResult(props: ResultProps) { const navigateUp = C.useRouterState(s => s.dispatch.navigateUp) const onOpenPrivateFolder = React.useCallback(() => { navigateUp() - FS.makeActionForOpenPathInFilesTab( + FS.navToPath( T.FS.stringToPath(`/keybase/private/${decoratedUsername},${myUsername}`) ) }, [navigateUp, decoratedUsername, myUsername]) const onBrowsePublicFolder = React.useCallback(() => { navigateUp() - FS.makeActionForOpenPathInFilesTab(T.FS.stringToPath(`/keybase/public/${decoratedUsername}`)) + FS.navToPath(T.FS.stringToPath(`/keybase/public/${decoratedUsername}`)) }, [navigateUp, decoratedUsername]) const onManageBlocking = React.useCallback(() => { diff --git a/shared/team-building/search-result/you-result.tsx b/shared/team-building/search-result/you-result.tsx index 0e98651cd20a..0c482c089fc9 100644 --- a/shared/team-building/search-result/you-result.tsx +++ b/shared/team-building/search-result/you-result.tsx @@ -1,6 +1,6 @@ -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as React from 'react' -import {useTBContext} from '@/constants/team-building' +import {useTBContext} from '@/stores/team-building' import * as Kb from '@/common-adapters' import CommonResult, {type ResultProps} from './common-result' diff --git a/shared/team-building/shared.tsx b/shared/team-building/shared.tsx index 6342bd06fd0b..9303fdcff0ef 100644 --- a/shared/team-building/shared.tsx +++ b/shared/team-building/shared.tsx @@ -1,7 +1,7 @@ import type * as T from '@/constants/types' import * as Kb from '@/common-adapters' import type {IconType} from '@/common-adapters/icon.constants-gen' -import * as TeamBuilding from '@/constants/team-building' +import * as TeamBuilding from '@/stores/team-building' const services: { [K in T.TB.ServiceIdWithContact]: { diff --git a/shared/teams/add-members-wizard/add-contacts.native.tsx b/shared/teams/add-members-wizard/add-contacts.native.tsx index 6808ecaea271..e5e430e0bea1 100644 --- a/shared/teams/add-members-wizard/add-contacts.native.tsx +++ b/shared/teams/add-members-wizard/add-contacts.native.tsx @@ -1,6 +1,6 @@ import * as C from '@/constants' import * as React from 'react' -import {useTeamsState} from '@/constants/teams' +import {useTeamsState} from '@/stores/teams' import * as Kb from '@/common-adapters' import * as T from '@/constants/types' import {pluralize} from '@/util/string' diff --git a/shared/teams/add-members-wizard/add-email.tsx b/shared/teams/add-members-wizard/add-email.tsx index 26cf7893dc31..c19e84f68547 100644 --- a/shared/teams/add-members-wizard/add-email.tsx +++ b/shared/teams/add-members-wizard/add-email.tsx @@ -1,6 +1,6 @@ import * as C from '@/constants' import * as React from 'react' -import {useTeamsState} from '@/constants/teams' +import {useTeamsState} from '@/stores/teams' import * as Kb from '@/common-adapters' import {useSafeNavigation} from '@/util/safe-navigation' import * as T from '@/constants/types' diff --git a/shared/teams/add-members-wizard/add-from-where.tsx b/shared/teams/add-members-wizard/add-from-where.tsx index 08c3f171a503..8bda843d03cf 100644 --- a/shared/teams/add-members-wizard/add-from-where.tsx +++ b/shared/teams/add-members-wizard/add-from-where.tsx @@ -1,6 +1,6 @@ import * as C from '@/constants' import * as Kb from '@/common-adapters' -import * as Teams from '@/constants/teams' +import * as Teams from '@/stores/teams' import * as T from '@/constants/types' import {ModalTitle} from '../common' import {useSafeNavigation} from '@/util/safe-navigation' diff --git a/shared/teams/add-members-wizard/add-phone.tsx b/shared/teams/add-members-wizard/add-phone.tsx index 9a93ef6f9c4b..5ed2a3a15ae2 100644 --- a/shared/teams/add-members-wizard/add-phone.tsx +++ b/shared/teams/add-members-wizard/add-phone.tsx @@ -1,11 +1,11 @@ import * as C from '@/constants' import * as React from 'react' -import {useTeamsState} from '@/constants/teams' +import {useTeamsState} from '@/stores/teams' import * as Kb from '@/common-adapters' import * as T from '@/constants/types' import {ModalTitle, usePhoneNumberList} from '../common' import {useSafeNavigation} from '@/util/safe-navigation' -import {useSettingsPhoneState} from '@/constants/settings-phone' +import {useSettingsPhoneState} from '@/stores/settings-phone' const waitingKey = 'phoneLookup' diff --git a/shared/teams/add-members-wizard/confirm.tsx b/shared/teams/add-members-wizard/confirm.tsx index 743ef7110357..6c4fab1bef42 100644 --- a/shared/teams/add-members-wizard/confirm.tsx +++ b/shared/teams/add-members-wizard/confirm.tsx @@ -1,8 +1,8 @@ import * as C from '@/constants' -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as React from 'react' -import * as Teams from '@/constants/teams' -import {useTeamsState} from '@/constants/teams' +import * as Teams from '@/stores/teams' +import {useTeamsState} from '@/stores/teams' import * as Kb from '@/common-adapters' import * as T from '@/constants/types' import {assertionToDisplay} from '@/common-adapters/usernames' diff --git a/shared/teams/channel/create-channels.tsx b/shared/teams/channel/create-channels.tsx index 2a11aa71da5b..ab94118ac4c5 100644 --- a/shared/teams/channel/create-channels.tsx +++ b/shared/teams/channel/create-channels.tsx @@ -1,5 +1,5 @@ import * as React from 'react' -import {useTeamsState} from '@/constants/teams' +import {useTeamsState} from '@/stores/teams' import * as Kb from '@/common-adapters' import type * as T from '@/constants/types' import {CreateChannelsModal} from '../new-team/wizard/create-channels' diff --git a/shared/teams/channel/header.tsx b/shared/teams/channel/header.tsx index ea4654017c5b..16a840f36f2e 100644 --- a/shared/teams/channel/header.tsx +++ b/shared/teams/channel/header.tsx @@ -1,9 +1,9 @@ import * as T from '@/constants/types' import * as C from '@/constants' -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as React from 'react' -import * as Teams from '@/constants/teams' -import {useTeamsState} from '@/constants/teams' +import * as Teams from '@/stores/teams' +import {useTeamsState} from '@/stores/teams' import * as Kb from '@/common-adapters' import {pluralize} from '@/util/string' import {Activity, useChannelParticipants} from '../common' diff --git a/shared/teams/channel/index.tsx b/shared/teams/channel/index.tsx index 0bad5f015636..0af5d5e9274d 100644 --- a/shared/teams/channel/index.tsx +++ b/shared/teams/channel/index.tsx @@ -1,7 +1,7 @@ import * as C from '@/constants' -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as React from 'react' -import * as Teams from '@/constants/teams' +import * as Teams from '@/stores/teams' import * as Kb from '@/common-adapters' import type * as T from '@/constants/types' import { @@ -15,8 +15,8 @@ import ChannelMemberRow from './rows' import BotRow from '../team/rows/bot-row/bot' import SettingsList from '../../chat/conversation/info-panel/settings' import EmptyRow from '../team/rows/empty-row' -import {useBotsState} from '@/constants/bots' -import {useUsersState} from '@/constants/users' +import {useBotsState} from '@/stores/bots' +import {useUsersState} from '@/stores/users' export type OwnProps = { teamID: T.Teams.TeamID diff --git a/shared/teams/channel/rows.tsx b/shared/teams/channel/rows.tsx index e07037b57201..5c77b0f6a518 100644 --- a/shared/teams/channel/rows.tsx +++ b/shared/teams/channel/rows.tsx @@ -1,13 +1,13 @@ import * as C from '@/constants' -import * as Chat from '@/constants/chat2' -import {useProfileState} from '@/constants/profile' -import * as Teams from '@/constants/teams' +import * as Chat from '@/stores/chat2' +import {useProfileState} from '@/stores/profile' +import * as Teams from '@/stores/teams' import type * as T from '@/constants/types' import * as React from 'react' import * as Kb from '@/common-adapters' import MenuHeader from '../team/rows/menu-header.new' -import {useUsersState} from '@/constants/users' -import {useCurrentUserState} from '@/constants/current-user' +import {useUsersState} from '@/stores/users' +import {useCurrentUserState} from '@/stores/current-user' type Props = { conversationIDKey: T.Chat.ConversationIDKey diff --git a/shared/teams/channel/tabs.tsx b/shared/teams/channel/tabs.tsx index 795cfb89ab9e..bc8d5526ea3a 100644 --- a/shared/teams/channel/tabs.tsx +++ b/shared/teams/channel/tabs.tsx @@ -1,7 +1,7 @@ import type * as T from '@/constants/types' import * as Kb from '@/common-adapters' import type {Tab as TabType} from '@/common-adapters/tabs' -import {useTeamsState} from '@/constants/teams' +import {useTeamsState} from '@/stores/teams' export type TabKey = 'members' | 'attachments' | 'bots' | 'settings' | 'loading' diff --git a/shared/teams/common/activity.tsx b/shared/teams/common/activity.tsx index 6ebbf1f4c098..f31a80050dff 100644 --- a/shared/teams/common/activity.tsx +++ b/shared/teams/common/activity.tsx @@ -1,6 +1,6 @@ import * as React from 'react' -import * as Teams from '@/constants/teams' -import {useTeamsState} from '@/constants/teams' +import * as Teams from '@/stores/teams' +import {useTeamsState} from '@/stores/teams' import * as Kb from '@/common-adapters' import * as T from '@/constants/types' diff --git a/shared/teams/common/channel-hooks.tsx b/shared/teams/common/channel-hooks.tsx index 934b7100704b..7736f46a4e49 100644 --- a/shared/teams/common/channel-hooks.tsx +++ b/shared/teams/common/channel-hooks.tsx @@ -1,8 +1,8 @@ import * as T from '@/constants/types' import * as C from '@/constants' -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as React from 'react' -import * as Teams from '@/constants/teams' +import * as Teams from '@/stores/teams' // Filter bots out using team role info, isolate to only when related state changes export const useChannelParticipants = ( diff --git a/shared/teams/common/enable-contacts.tsx b/shared/teams/common/enable-contacts.tsx index 133d5cb95967..477d3faf51dd 100644 --- a/shared/teams/common/enable-contacts.tsx +++ b/shared/teams/common/enable-contacts.tsx @@ -1,6 +1,6 @@ import * as React from 'react' import * as Kb from '@/common-adapters' -import {useConfigState} from '@/constants/config' +import {useConfigState} from '@/stores/config' /** * Popup explaining that Keybase doesn't have contact permissions with a link to @@ -11,7 +11,7 @@ import {useConfigState} from '@/constants/config' * popup. */ const EnableContactsPopup = ({noAccess, onClose}: {noAccess: boolean; onClose: () => void}) => { - const onOpenSettings = useConfigState(s => s.dispatch.dynamic.openAppSettings) + const onOpenSettings = useConfigState(s => s.dispatch.defer.openAppSettings) const [showingPopup, setShowingPopup] = React.useState(noAccess) React.useEffect(() => { diff --git a/shared/teams/common/selection-popup.tsx b/shared/teams/common/selection-popup.tsx index 829c6d3200c9..531efe9d707a 100644 --- a/shared/teams/common/selection-popup.tsx +++ b/shared/teams/common/selection-popup.tsx @@ -1,8 +1,8 @@ import * as C from '@/constants' -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as Kb from '@/common-adapters' -import * as Teams from '@/constants/teams' -import {useTeamsState} from '@/constants/teams' +import * as Teams from '@/stores/teams' +import {useTeamsState} from '@/stores/teams' import * as React from 'react' import type * as T from '@/constants/types' import {FloatingRolePicker} from '../role-picker' diff --git a/shared/teams/common/use-contacts.native.tsx b/shared/teams/common/use-contacts.native.tsx index 822c30fea955..aa8329ca7130 100644 --- a/shared/teams/common/use-contacts.native.tsx +++ b/shared/teams/common/use-contacts.native.tsx @@ -3,8 +3,8 @@ import * as React from 'react' import {e164ToDisplay} from '@/util/phone-numbers' import logger from '@/logger' import {getDefaultCountryCode} from 'react-native-kb' -import {useSettingsContactsState} from '@/constants/settings-contacts' -import {getE164} from '@/constants/settings-phone' +import {useSettingsContactsState} from '@/stores/settings-contacts' +import {getE164} from '@/util/phone-numbers' // Contact info coming from the native contacts library. export type Contact = { diff --git a/shared/teams/confirm-modals/confirm-kick-out.tsx b/shared/teams/confirm-modals/confirm-kick-out.tsx index 5a8a0d0624e1..4c9d358d20a3 100644 --- a/shared/teams/confirm-modals/confirm-kick-out.tsx +++ b/shared/teams/confirm-modals/confirm-kick-out.tsx @@ -1,7 +1,7 @@ import * as C from '@/constants' import * as React from 'react' -import * as Teams from '@/constants/teams' -import {useTeamsState} from '@/constants/teams' +import * as Teams from '@/stores/teams' +import {useTeamsState} from '@/stores/teams' import * as Kb from '@/common-adapters' import type * as T from '@/constants/types' import {useSafeNavigation} from '@/util/safe-navigation' diff --git a/shared/teams/confirm-modals/confirm-remove-from-channel.tsx b/shared/teams/confirm-modals/confirm-remove-from-channel.tsx index 4c346846133f..c05aa0d0d84b 100644 --- a/shared/teams/confirm-modals/confirm-remove-from-channel.tsx +++ b/shared/teams/confirm-modals/confirm-remove-from-channel.tsx @@ -1,8 +1,8 @@ import * as T from '@/constants/types' import * as C from '@/constants' import * as React from 'react' -import * as Teams from '@/constants/teams' -import {useTeamsState} from '@/constants/teams' +import * as Teams from '@/stores/teams' +import {useTeamsState} from '@/stores/teams' import * as Kb from '@/common-adapters' import {useSafeNavigation} from '@/util/safe-navigation' diff --git a/shared/teams/confirm-modals/delete-channel.tsx b/shared/teams/confirm-modals/delete-channel.tsx index 9ad085f55ab3..c2e44e152a52 100644 --- a/shared/teams/confirm-modals/delete-channel.tsx +++ b/shared/teams/confirm-modals/delete-channel.tsx @@ -2,7 +2,7 @@ import * as C from '@/constants' import * as Kb from '@/common-adapters' import * as React from 'react' import type * as T from '@/constants/types' -import {useTeamsState} from '@/constants/teams' +import {useTeamsState} from '@/stores/teams' import {pluralize} from '@/util/string' import {useAllChannelMetas} from '@/teams/common/channel-hooks' diff --git a/shared/teams/confirm-modals/really-leave-team/index.tsx b/shared/teams/confirm-modals/really-leave-team/index.tsx index 684726a1e8a4..9d4340be3b68 100644 --- a/shared/teams/confirm-modals/really-leave-team/index.tsx +++ b/shared/teams/confirm-modals/really-leave-team/index.tsx @@ -1,7 +1,7 @@ import * as React from 'react' import * as C from '@/constants' -import * as Teams from '@/constants/teams' -import {useTeamsState} from '@/constants/teams' +import * as Teams from '@/stores/teams' +import {useTeamsState} from '@/stores/teams' import * as Kb from '@/common-adapters' import {useSafeSubmit} from '@/util/safe-submit' import type * as T from '@/constants/types' diff --git a/shared/teams/container.tsx b/shared/teams/container.tsx index f3219023e020..890288fea27c 100644 --- a/shared/teams/container.tsx +++ b/shared/teams/container.tsx @@ -1,15 +1,15 @@ import * as C from '@/constants' -import * as Teams from '@/constants/teams' +import * as Teams from '@/stores/teams' import * as React from 'react' import * as Kb from '@/common-adapters' import * as T from '@/constants/types' -import * as FS from '@/constants/fs' +import * as FS from '@/stores/fs' import Main from './main' import openURL from '@/util/open-url' import {useTeamsSubscribe} from './subscriber' import {useActivityLevels} from './common' import {useSafeNavigation} from '@/util/safe-navigation' -import {useConfigState} from '@/constants/config' +import {useConfigState} from '@/stores/config' const orderTeams = ( teams: ReadonlyMap, @@ -78,7 +78,7 @@ const Connected = () => { updateGregorCategory('sawChatBanner', 'true') } const onOpenFolder = (teamname: T.Teams.Teamname) => { - FS.makeActionForOpenPathInFilesTab(T.FS.stringToPath(`/keybase/team/${teamname}`)) + FS.navToPath(T.FS.stringToPath(`/keybase/team/${teamname}`)) } const onReadMore = () => { openURL('https://keybase.io/blog/introducing-keybase-teams') diff --git a/shared/teams/delete-team.tsx b/shared/teams/delete-team.tsx index e0dfd2750e0f..4c69295c90ac 100644 --- a/shared/teams/delete-team.tsx +++ b/shared/teams/delete-team.tsx @@ -6,8 +6,8 @@ import * as Kb from '@/common-adapters' import {pluralize} from '@/util/string' import {useTeamDetailsSubscribe} from './subscriber' import noop from 'lodash/noop' -import * as Teams from '@/constants/teams' -import {useTeamsState} from '@/constants/teams' +import * as Teams from '@/stores/teams' +import {useTeamsState} from '@/stores/teams' type OwnProps = {teamID: T.Teams.TeamID} diff --git a/shared/teams/edit-team-description.tsx b/shared/teams/edit-team-description.tsx index 7be608185f4c..71e0934031c3 100644 --- a/shared/teams/edit-team-description.tsx +++ b/shared/teams/edit-team-description.tsx @@ -1,6 +1,6 @@ import * as C from '@/constants' import * as React from 'react' -import * as Teams from '@/constants/teams' +import * as Teams from '@/stores/teams' import * as Kb from '@/common-adapters' import * as T from '@/constants/types' import {ModalTitle} from './common' diff --git a/shared/teams/emojis/add-alias.tsx b/shared/teams/emojis/add-alias.tsx index cfbf1a23f600..728b3e93a2a4 100644 --- a/shared/teams/emojis/add-alias.tsx +++ b/shared/teams/emojis/add-alias.tsx @@ -1,6 +1,6 @@ import * as T from '@/constants/types' import * as C from '@/constants' -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as React from 'react' import * as Kb from '@/common-adapters' import {EmojiPickerDesktop} from '@/chat/emoji-picker/container' diff --git a/shared/teams/emojis/add-emoji.tsx b/shared/teams/emojis/add-emoji.tsx index b1f9a3343e17..81c4e896a1f7 100644 --- a/shared/teams/emojis/add-emoji.tsx +++ b/shared/teams/emojis/add-emoji.tsx @@ -1,6 +1,6 @@ import * as T from '@/constants/types' import * as C from '@/constants' -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as React from 'react' import * as Kb from '@/common-adapters' import {AliasInput, Modal} from './common' diff --git a/shared/teams/external-team.tsx b/shared/teams/external-team.tsx index 642461031ab6..9fd9da9f4f66 100644 --- a/shared/teams/external-team.tsx +++ b/shared/teams/external-team.tsx @@ -1,10 +1,10 @@ import * as C from '@/constants' -import * as Chat from '@/constants/chat2' -import {useProfileState} from '@/constants/profile' +import * as Chat from '@/stores/chat2' +import {useProfileState} from '@/stores/profile' import * as React from 'react' import * as Kb from '@/common-adapters' import * as T from '@/constants/types' -import * as Teams from '@/constants/teams' +import * as Teams from '@/stores/teams' import {useTeamLinkPopup} from './common' import {pluralize} from '@/util/string' import capitalize from 'lodash/capitalize' diff --git a/shared/teams/get-options.tsx b/shared/teams/get-options.tsx index e4aca9f077a2..f0cf90c88b0f 100644 --- a/shared/teams/get-options.tsx +++ b/shared/teams/get-options.tsx @@ -1,7 +1,7 @@ import * as Kb from '@/common-adapters' import {HeaderRightActions} from './main/header' import {useSafeNavigation} from '@/util/safe-navigation' -import {useTeamsState} from '@/constants/teams' +import {useTeamsState} from '@/stores/teams' const useHeaderActions = () => { const nav = useSafeNavigation() diff --git a/shared/teams/invite-by-contact/team-invite-by-contacts.native.tsx b/shared/teams/invite-by-contact/team-invite-by-contacts.native.tsx index c0afab8186de..cffc7f924255 100644 --- a/shared/teams/invite-by-contact/team-invite-by-contacts.native.tsx +++ b/shared/teams/invite-by-contact/team-invite-by-contacts.native.tsx @@ -1,11 +1,11 @@ import * as React from 'react' -import * as Teams from '@/constants/teams' +import * as Teams from '@/stores/teams' import type * as T from '@/constants/types' import useContacts, {type Contact} from '../common/use-contacts.native' import {InviteByContact, type ContactRowProps} from './index.native' import {useTeamDetailsSubscribe} from '../subscriber' import {useSafeNavigation} from '@/util/safe-navigation' -import {getE164} from '@/constants/settings-phone' +import {getE164} from '@/util/phone-numbers' // Seitan invite names (labels) look like this: "[name] ([phone number])". Try // to derive E164 phone number based on seitan invite name and user's region. diff --git a/shared/teams/invite-by-email.tsx b/shared/teams/invite-by-email.tsx index eb81f145ac77..f0201964e4d5 100644 --- a/shared/teams/invite-by-email.tsx +++ b/shared/teams/invite-by-email.tsx @@ -1,6 +1,6 @@ import * as C from '@/constants' import type * as T from '@/constants/types' -import * as Teams from '@/constants/teams' +import * as Teams from '@/stores/teams' import * as React from 'react' import * as Kb from '@/common-adapters' import {FloatingRolePicker} from './role-picker' diff --git a/shared/teams/join-team/container.tsx b/shared/teams/join-team/container.tsx index abbdbc6f3941..7a1e2e4df322 100644 --- a/shared/teams/join-team/container.tsx +++ b/shared/teams/join-team/container.tsx @@ -1,6 +1,6 @@ import * as C from '@/constants' import upperFirst from 'lodash/upperFirst' -import {useTeamsState} from '@/constants/teams' +import {useTeamsState} from '@/stores/teams' import * as React from 'react' import * as Kb from '@/common-adapters' diff --git a/shared/teams/join-team/join-from-invite.tsx b/shared/teams/join-team/join-from-invite.tsx index fc4fa2ea5d33..f87440ceac61 100644 --- a/shared/teams/join-team/join-from-invite.tsx +++ b/shared/teams/join-team/join-from-invite.tsx @@ -1,6 +1,6 @@ import * as C from '@/constants' import * as React from 'react' -import {useTeamsState} from '@/constants/teams' +import {useTeamsState} from '@/stores/teams' import * as Kb from '@/common-adapters' import {Success} from './container' import {useSafeNavigation} from '@/util/safe-navigation' diff --git a/shared/teams/main/index.tsx b/shared/teams/main/index.tsx index 71ee7d1b4daa..7ef9fd136b08 100644 --- a/shared/teams/main/index.tsx +++ b/shared/teams/main/index.tsx @@ -4,7 +4,7 @@ import type * as T from '@/constants/types' import Banner from './banner' import TeamsFooter from './footer' import TeamRowNew from './team-row' -import {useTeamsState} from '@/constants/teams' +import {useTeamsState} from '@/stores/teams' type DeletedTeam = { teamName: string diff --git a/shared/teams/main/team-row.tsx b/shared/teams/main/team-row.tsx index a2550414088d..524f5e399dca 100644 --- a/shared/teams/main/team-row.tsx +++ b/shared/teams/main/team-row.tsx @@ -1,4 +1,4 @@ -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as Kb from '@/common-adapters' import * as React from 'react' import type * as T from '@/constants/types' @@ -6,8 +6,8 @@ import TeamMenu from '../team/menu-container' import {pluralize} from '@/util/string' import {Activity} from '../common' import {useSafeNavigation} from '@/util/safe-navigation' -import * as Teams from '@/constants/teams' -import {useTeamsState} from '@/constants/teams' +import * as Teams from '@/stores/teams' +import {useTeamsState} from '@/stores/teams' type Props = { firstItem: boolean diff --git a/shared/teams/new-team/index.tsx b/shared/teams/new-team/index.tsx index 864bc75f6e6d..13d7883eaa0d 100644 --- a/shared/teams/new-team/index.tsx +++ b/shared/teams/new-team/index.tsx @@ -1,6 +1,6 @@ import * as C from '@/constants' import * as React from 'react' -import * as Teams from '@/constants/teams' +import * as Teams from '@/stores/teams' import * as Kb from '@/common-adapters' import * as T from '@/constants/types' import openUrl from '@/util/open-url' diff --git a/shared/teams/new-team/wizard/add-subteam-members.tsx b/shared/teams/new-team/wizard/add-subteam-members.tsx index dc439ccc5bb5..6d9484b18811 100644 --- a/shared/teams/new-team/wizard/add-subteam-members.tsx +++ b/shared/teams/new-team/wizard/add-subteam-members.tsx @@ -1,4 +1,4 @@ -import * as Teams from '@/constants/teams' +import * as Teams from '@/stores/teams' import * as Kb from '@/common-adapters' import * as React from 'react' import * as T from '@/constants/types' @@ -6,7 +6,7 @@ import {ModalTitle} from '@/teams/common' import {pluralize} from '@/util/string' import {useTeamDetailsSubscribe} from '@/teams/subscriber' import {useSafeNavigation} from '@/util/safe-navigation' -import {useCurrentUserState} from '@/constants/current-user' +import {useCurrentUserState} from '@/stores/current-user' const AddSubteamMembers = () => { const nav = useSafeNavigation() diff --git a/shared/teams/new-team/wizard/create-channels.tsx b/shared/teams/new-team/wizard/create-channels.tsx index b2c22534ece2..dda466eb062e 100644 --- a/shared/teams/new-team/wizard/create-channels.tsx +++ b/shared/teams/new-team/wizard/create-channels.tsx @@ -1,5 +1,5 @@ import * as React from 'react' -import {useTeamsState} from '@/constants/teams' +import {useTeamsState} from '@/stores/teams' import * as Kb from '@/common-adapters' import * as T from '@/constants/types' import {pluralize} from '@/util/string' diff --git a/shared/teams/new-team/wizard/create-subteams.tsx b/shared/teams/new-team/wizard/create-subteams.tsx index e45764eff76c..89a3097a83d6 100644 --- a/shared/teams/new-team/wizard/create-subteams.tsx +++ b/shared/teams/new-team/wizard/create-subteams.tsx @@ -4,7 +4,7 @@ import * as T from '@/constants/types' import {pluralize} from '@/util/string' import {ModalTitle} from '@/teams/common' import {useSafeNavigation} from '@/util/safe-navigation' -import {useTeamsState} from '@/constants/teams' +import {useTeamsState} from '@/stores/teams' const cleanSubteamName = (name: string) => name.replace(/[^0-9a-zA-Z_]/, '') diff --git a/shared/teams/new-team/wizard/make-big-team.tsx b/shared/teams/new-team/wizard/make-big-team.tsx index 016a8edc4b71..071a0e4420f8 100644 --- a/shared/teams/new-team/wizard/make-big-team.tsx +++ b/shared/teams/new-team/wizard/make-big-team.tsx @@ -2,7 +2,7 @@ import * as Kb from '@/common-adapters' import * as T from '@/constants/types' import {ModalTitle} from '@/teams/common' import {useSafeNavigation} from '@/util/safe-navigation' -import {useTeamsState} from '@/constants/teams' +import {useTeamsState} from '@/stores/teams' const MakeBigTeam = () => { const nav = useSafeNavigation() diff --git a/shared/teams/new-team/wizard/new-team-info.tsx b/shared/teams/new-team/wizard/new-team-info.tsx index cb9661e3b0ee..0bfb4183f529 100644 --- a/shared/teams/new-team/wizard/new-team-info.tsx +++ b/shared/teams/new-team/wizard/new-team-info.tsx @@ -3,8 +3,8 @@ import * as React from 'react' import * as Kb from '@/common-adapters' import {ModalTitle} from '@/teams/common' import * as T from '@/constants/types' -import * as Teams from '@/constants/teams' -import {useTeamsState} from '@/constants/teams' +import * as Teams from '@/stores/teams' +import {useTeamsState} from '@/stores/teams' import {pluralize} from '@/util/string' import {InlineDropdown} from '@/common-adapters/dropdown' import {FloatingRolePicker} from '../../role-picker' diff --git a/shared/teams/new-team/wizard/team-purpose.tsx b/shared/teams/new-team/wizard/team-purpose.tsx index 2d75aad151e8..4fa97617bb83 100644 --- a/shared/teams/new-team/wizard/team-purpose.tsx +++ b/shared/teams/new-team/wizard/team-purpose.tsx @@ -2,7 +2,7 @@ import * as Kb from '@/common-adapters' import {ModalTitle} from '@/teams/common' import * as T from '@/constants/types' import {useSafeNavigation} from '@/util/safe-navigation' -import {useTeamsState} from '@/constants/teams' +import {useTeamsState} from '@/stores/teams' const TeamPurpose = () => { const nav = useSafeNavigation() diff --git a/shared/teams/rename-team.tsx b/shared/teams/rename-team.tsx index 3b080205a545..bdcb775f658d 100644 --- a/shared/teams/rename-team.tsx +++ b/shared/teams/rename-team.tsx @@ -1,7 +1,7 @@ import * as C from '@/constants' import * as React from 'react' import * as Kb from '@/common-adapters' -import {useTeamsState} from '@/constants/teams' +import {useTeamsState} from '@/stores/teams' type OwnProps = {teamname: string} diff --git a/shared/teams/routes.tsx b/shared/teams/routes.tsx index 5692f5f15451..3d57aef29f3a 100644 --- a/shared/teams/routes.tsx +++ b/shared/teams/routes.tsx @@ -1,6 +1,6 @@ import * as React from 'react' import * as C from '@/constants' -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import contactRestricted from '../team-building/contact-restricted.page' import teamsTeamBuilder from '../team-building/page' import teamsRootGetOptions from './get-options' diff --git a/shared/teams/subscriber.tsx b/shared/teams/subscriber.tsx index e4dd3276faef..8ea982cb3645 100644 --- a/shared/teams/subscriber.tsx +++ b/shared/teams/subscriber.tsx @@ -1,6 +1,6 @@ import * as C from '@/constants' import * as React from 'react' -import {useTeamsState} from '@/constants/teams' +import {useTeamsState} from '@/stores/teams' import type * as T from '@/constants/types' // NOTE: If you are in a floating box or otherwise outside the navigation diff --git a/shared/teams/team/index.tsx b/shared/teams/team/index.tsx index 0d83779ee857..9c2ad37aa0a9 100644 --- a/shared/teams/team/index.tsx +++ b/shared/teams/team/index.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Teams from '@/constants/teams' +import * as Teams from '@/stores/teams' import * as React from 'react' import * as Kb from '@/common-adapters' import type * as T from '@/constants/types' @@ -18,7 +18,7 @@ import { type Section, type Item, } from './rows' -import {useBotsState} from '@/constants/bots' +import {useBotsState} from '@/stores/bots' type Props = { teamID: T.Teams.TeamID diff --git a/shared/teams/team/member/add-to-channels.tsx b/shared/teams/team/member/add-to-channels.tsx index 79922967b9e1..3e3035cb798e 100644 --- a/shared/teams/team/member/add-to-channels.tsx +++ b/shared/teams/team/member/add-to-channels.tsx @@ -1,14 +1,14 @@ import * as C from '@/constants' -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as T from '@/constants/types' -import * as Teams from '@/constants/teams' +import * as Teams from '@/stores/teams' import * as React from 'react' import * as Kb from '@/common-adapters' import * as Common from '@/teams/common' import {pluralize} from '@/util/string' import {useAllChannelMetas} from '@/teams/common/channel-hooks' import {useSafeNavigation} from '@/util/safe-navigation' -import {useCurrentUserState} from '@/constants/current-user' +import {useCurrentUserState} from '@/stores/current-user' type Props = { teamID: T.Teams.TeamID diff --git a/shared/teams/team/member/edit-channel.tsx b/shared/teams/team/member/edit-channel.tsx index 95679a91a100..1f1a49e5f552 100644 --- a/shared/teams/team/member/edit-channel.tsx +++ b/shared/teams/team/member/edit-channel.tsx @@ -1,6 +1,6 @@ import * as C from '@/constants' import * as Kb from '@/common-adapters' -import {useTeamsState} from '@/constants/teams' +import {useTeamsState} from '@/stores/teams' import * as React from 'react' import type * as T from '@/constants/types' import {ModalTitle} from '@/teams/common' diff --git a/shared/teams/team/member/index.new.tsx b/shared/teams/team/member/index.new.tsx index 3ac20ce63dfa..c5bcd10e5aa9 100644 --- a/shared/teams/team/member/index.new.tsx +++ b/shared/teams/team/member/index.new.tsx @@ -1,8 +1,8 @@ import * as C from '@/constants' -import * as Chat from '@/constants/chat2' -import {useCurrentUserState} from '@/constants/current-user' -import * as Teams from '@/constants/teams' -import {useProfileState} from '@/constants/profile' +import * as Chat from '@/stores/chat2' +import {useCurrentUserState} from '@/stores/current-user' +import * as Teams from '@/stores/teams' +import {useProfileState} from '@/stores/profile' import * as Kb from '@/common-adapters' import * as T from '@/constants/types' import * as React from 'react' diff --git a/shared/teams/team/menu-container.tsx b/shared/teams/team/menu-container.tsx index 4e63c738378f..5ea551600be8 100644 --- a/shared/teams/team/menu-container.tsx +++ b/shared/teams/team/menu-container.tsx @@ -1,8 +1,8 @@ import * as C from '@/constants' import * as Kb from '@/common-adapters' import type * as React from 'react' -import * as FS from '@/constants/fs/util' -import * as Teams from '@/constants/teams' +import * as FS from '@/constants/fs' +import * as Teams from '@/stores/teams' import capitalize from 'lodash/capitalize' import * as T from '@/constants/types' import {pluralize} from '@/util/string' @@ -98,7 +98,7 @@ const Container = (ownProps: OwnProps) => { navigateAppend({props: {teamID}, selected: 'teamReallyLeaveTeam'}) } const onOpenFolder = (teamname: string) => { - FS.makeActionForOpenPathInFilesTab(T.FS.stringToPath(`/keybase/team/${teamname}`)) + FS.navToPath(T.FS.stringToPath(`/keybase/team/${teamname}`)) } const items: Kb.MenuItems = ['Divider'] diff --git a/shared/teams/team/new-header.tsx b/shared/teams/team/new-header.tsx index bbdf3a4bb0aa..b5f05946e03b 100644 --- a/shared/teams/team/new-header.tsx +++ b/shared/teams/team/new-header.tsx @@ -1,15 +1,15 @@ import * as C from '@/constants' -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as React from 'react' -import * as Teams from '@/constants/teams' +import * as Teams from '@/stores/teams' import * as Kb from '@/common-adapters' import TeamMenu from './menu-container' import {pluralize} from '@/util/string' import {Activity, useActivityLevels, useTeamLinkPopup} from '../common' import type * as T from '@/constants/types' import {useSafeNavigation} from '@/util/safe-navigation' -import {useCurrentUserState} from '@/constants/current-user' -import {useTeamsState} from '@/constants/teams' +import {useCurrentUserState} from '@/stores/current-user' +import {useTeamsState} from '@/stores/teams' const AddPeopleButton = ({teamID}: {teamID: T.Teams.TeamID}) => { const startAddMembersWizard = useTeamsState(s => s.dispatch.startAddMembersWizard) diff --git a/shared/teams/team/rows/bot-row/bot.tsx b/shared/teams/team/rows/bot-row/bot.tsx index a6fb1ef29367..4724d6429e39 100644 --- a/shared/teams/team/rows/bot-row/bot.tsx +++ b/shared/teams/team/rows/bot-row/bot.tsx @@ -1,12 +1,12 @@ import * as C from '@/constants' import * as React from 'react' -import * as Teams from '@/constants/teams' +import * as Teams from '@/stores/teams' import * as Kb from '@/common-adapters' import type * as T from '@/constants/types' import BotMenu from './bot-menu' -import {useBotsState} from '@/constants/bots' -import {useTrackerState} from '@/constants/tracker2' -import {useProfileState} from '@/constants/profile' +import {useBotsState} from '@/stores/bots' +import {useTrackerState} from '@/stores/tracker2' +import {useProfileState} from '@/stores/profile' export type Props = { botAlias: string diff --git a/shared/teams/team/rows/channel-row/channel.tsx b/shared/teams/team/rows/channel-row/channel.tsx index 846b88deb7e1..3b8b31017ac0 100644 --- a/shared/teams/team/rows/channel-row/channel.tsx +++ b/shared/teams/team/rows/channel-row/channel.tsx @@ -4,8 +4,8 @@ import type * as T from '@/constants/types' import {Activity, useChannelParticipants} from '@/teams/common' import {pluralize} from '@/util/string' import {useSafeNavigation} from '@/util/safe-navigation' -import * as Teams from '@/constants/teams' -import {useTeamsState} from '@/constants/teams' +import * as Teams from '@/stores/teams' +import {useTeamsState} from '@/stores/teams' type ChannelRowProps = { conversationIDKey: T.Chat.ConversationIDKey diff --git a/shared/teams/team/rows/emoji-row/add.tsx b/shared/teams/team/rows/emoji-row/add.tsx index b4381e54f327..b48b58e984e4 100644 --- a/shared/teams/team/rows/emoji-row/add.tsx +++ b/shared/teams/team/rows/emoji-row/add.tsx @@ -1,8 +1,8 @@ import * as Kb from '@/common-adapters' import type * as T from '@/constants/types' import {useSafeNavigation} from '@/util/safe-navigation' -import * as Teams from '@/constants/teams' -import {useTeamsState} from '@/constants/teams' +import * as Teams from '@/stores/teams' +import {useTeamsState} from '@/stores/teams' type OwnProps = { teamID: T.Teams.TeamID diff --git a/shared/teams/team/rows/emoji-row/item.tsx b/shared/teams/team/rows/emoji-row/item.tsx index dfc547d4cafc..41c72d14f68b 100644 --- a/shared/teams/team/rows/emoji-row/item.tsx +++ b/shared/teams/team/rows/emoji-row/item.tsx @@ -1,6 +1,6 @@ import * as C from '@/constants' import * as T from '@/constants/types' -import * as Teams from '@/constants/teams' +import * as Teams from '@/stores/teams' import * as React from 'react' import * as Kb from '@/common-adapters' import * as dateFns from 'date-fns' @@ -8,7 +8,7 @@ import {RPCToEmojiData} from '@/common-adapters/emoji' import EmojiMenu from './emoji-menu' import {useEmojiState} from '@/teams/emojis/use-emoji' import {useSafeNavigation} from '@/util/safe-navigation' -import {useCurrentUserState} from '@/constants/current-user' +import {useCurrentUserState} from '@/stores/current-user' type OwnProps = { conversationIDKey: T.Chat.ConversationIDKey diff --git a/shared/teams/team/rows/empty-row.tsx b/shared/teams/team/rows/empty-row.tsx index b30917726587..df3f9f02eaaa 100644 --- a/shared/teams/team/rows/empty-row.tsx +++ b/shared/teams/team/rows/empty-row.tsx @@ -1,10 +1,10 @@ import type * as T from '@/constants/types' import * as C from '@/constants' -import * as Chat from '@/constants/chat2' -import * as Teams from '@/constants/teams' +import * as Chat from '@/stores/chat2' +import * as Teams from '@/stores/teams' import * as Kb from '@/common-adapters' import {useSafeNavigation} from '@/util/safe-navigation' -import {useCurrentUserState} from '@/constants/current-user' +import {useCurrentUserState} from '@/stores/current-user' type Props = { type: 'channelsEmpty' | 'channelsFew' | 'members' | 'subteams' diff --git a/shared/teams/team/rows/index.tsx b/shared/teams/team/rows/index.tsx index cef7d9413a38..ddfe2b0808fa 100644 --- a/shared/teams/team/rows/index.tsx +++ b/shared/teams/team/rows/index.tsx @@ -1,7 +1,7 @@ import * as C from '@/constants' -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as T from '@/constants/types' -import * as Teams from '@/constants/teams' +import * as Teams from '@/stores/teams' import * as Kb from '@/common-adapters' import * as React from 'react' import EmptyRow from './empty-row' @@ -14,7 +14,7 @@ import {RequestRow, InviteRow} from './invite-row' import {SubteamAddRow, SubteamInfoRow, SubteamTeamRow} from './subteam-row' import {getOrderedMemberArray, sortInvites, getOrderedBotsArray} from './helpers' import {useEmojiState} from '../../emojis/use-emoji' -import {useCurrentUserState} from '@/constants/current-user' +import {useCurrentUserState} from '@/stores/current-user' type Requests = Omit, 'firstItem' | 'teamID'> diff --git a/shared/teams/team/rows/invite-row/invite.tsx b/shared/teams/team/rows/invite-row/invite.tsx index 78b2c553a7c0..ff3f9958b352 100644 --- a/shared/teams/team/rows/invite-row/invite.tsx +++ b/shared/teams/team/rows/invite-row/invite.tsx @@ -2,8 +2,8 @@ import * as React from 'react' import * as Kb from '@/common-adapters' import type * as T from '@/constants/types' import {formatPhoneNumber} from '@/util/phone-numbers' -import * as Teams from '@/constants/teams' -import {useTeamsState} from '@/constants/teams' +import * as Teams from '@/stores/teams' +import {useTeamsState} from '@/stores/teams' export type Props = { isKeybaseUser?: boolean diff --git a/shared/teams/team/rows/invite-row/request.tsx b/shared/teams/team/rows/invite-row/request.tsx index a4342c9d8e08..36be8c8e829c 100644 --- a/shared/teams/team/rows/invite-row/request.tsx +++ b/shared/teams/team/rows/invite-row/request.tsx @@ -1,8 +1,8 @@ import * as React from 'react' import * as C from '@/constants' -import * as Chat from '@/constants/chat2' -import * as Teams from '@/constants/teams' -import {useProfileState} from '@/constants/profile' +import * as Chat from '@/stores/chat2' +import * as Teams from '@/stores/teams' +import {useProfileState} from '@/stores/profile' import type * as T from '@/constants/types' import * as Kb from '@/common-adapters' import {FloatingRolePicker, sendNotificationFooter} from '@/teams/role-picker' diff --git a/shared/teams/team/rows/member-row.tsx b/shared/teams/team/rows/member-row.tsx index 5c74bd5d754e..7fc8920b2a01 100644 --- a/shared/teams/team/rows/member-row.tsx +++ b/shared/teams/team/rows/member-row.tsx @@ -1,15 +1,15 @@ import * as C from '@/constants' -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as Kb from '@/common-adapters' -import * as Teams from '@/constants/teams' +import * as Teams from '@/stores/teams' import * as React from 'react' import type * as T from '@/constants/types' import MenuHeader from './menu-header.new' import {useSafeNavigation} from '@/util/safe-navigation' -import {useTrackerState} from '@/constants/tracker2' -import {useProfileState} from '@/constants/profile' -import {useUsersState} from '@/constants/users' -import {useCurrentUserState} from '@/constants/current-user' +import {useTrackerState} from '@/stores/tracker2' +import {useProfileState} from '@/stores/profile' +import {useUsersState} from '@/stores/users' +import {useCurrentUserState} from '@/stores/current-user' export type Props = { firstItem: boolean diff --git a/shared/teams/team/rows/subteam-row/add.tsx b/shared/teams/team/rows/subteam-row/add.tsx index 6e80257e3113..58102c169fa9 100644 --- a/shared/teams/team/rows/subteam-row/add.tsx +++ b/shared/teams/team/rows/subteam-row/add.tsx @@ -1,5 +1,5 @@ import * as React from 'react' -import {useTeamsState} from '@/constants/teams' +import {useTeamsState} from '@/stores/teams' import * as Kb from '@/common-adapters' import type * as T from '@/constants/types' diff --git a/shared/teams/team/settings-tab/default-channels.tsx b/shared/teams/team/settings-tab/default-channels.tsx index 4bd9dbf45d56..76524135f8bc 100644 --- a/shared/teams/team/settings-tab/default-channels.tsx +++ b/shared/teams/team/settings-tab/default-channels.tsx @@ -4,8 +4,8 @@ import * as Kb from '@/common-adapters' import * as T from '@/constants/types' import type {RPCError} from '@/util/errors' import {ChannelsWidget} from '@/teams/common' -import * as Teams from '@/constants/teams' -import {useTeamsState} from '@/constants/teams' +import * as Teams from '@/stores/teams' +import {useTeamsState} from '@/stores/teams' type Props = { teamID: T.Teams.TeamID diff --git a/shared/teams/team/settings-tab/index.tsx b/shared/teams/team/settings-tab/index.tsx index 264e7690abad..5b83a2071295 100644 --- a/shared/teams/team/settings-tab/index.tsx +++ b/shared/teams/team/settings-tab/index.tsx @@ -1,7 +1,7 @@ import * as React from 'react' import * as C from '@/constants' -import * as Chat from '@/constants/chat2' -import * as Teams from '@/constants/teams' +import * as Chat from '@/stores/chat2' +import * as Teams from '@/stores/teams' import type * as T from '@/constants/types' import * as Kb from '@/common-adapters' import {FloatingRolePicker} from '@/teams/role-picker' diff --git a/shared/teams/team/settings-tab/retention/index.tsx b/shared/teams/team/settings-tab/retention/index.tsx index 3fc393dd4026..0c539384b43c 100644 --- a/shared/teams/team/settings-tab/retention/index.tsx +++ b/shared/teams/team/settings-tab/retention/index.tsx @@ -1,8 +1,8 @@ import * as C from '@/constants' -import * as Chat from '@/constants/chat2' +import * as Chat from '@/stores/chat2' import * as React from 'react' -import * as Teams from '@/constants/teams' -import {useTeamsState} from '@/constants/teams' +import * as Teams from '@/stores/teams' +import {useTeamsState} from '@/stores/teams' import * as Kb from '@/common-adapters' import type * as T from '@/constants/types' import type {StylesCrossPlatform} from '@/styles' diff --git a/shared/teams/team/tabs.tsx b/shared/teams/team/tabs.tsx index a27c8027232c..a2fcf15ecd24 100644 --- a/shared/teams/team/tabs.tsx +++ b/shared/teams/team/tabs.tsx @@ -1,8 +1,8 @@ import type * as T from '@/constants/types' import * as Kb from '@/common-adapters' import * as C from '@/constants' -import * as Chat from '@/constants/chat2' -import * as Teams from '@/constants/teams' +import * as Chat from '@/stores/chat2' +import * as Teams from '@/stores/teams' import type {Tab as TabType} from '@/common-adapters/tabs' type TeamTabsProps = { diff --git a/shared/teams/team/team-info.tsx b/shared/teams/team/team-info.tsx index 5c1a170c27e5..4eb6002f45b9 100644 --- a/shared/teams/team/team-info.tsx +++ b/shared/teams/team/team-info.tsx @@ -1,6 +1,6 @@ import * as C from '@/constants' import * as React from 'react' -import * as Teams from '@/constants/teams' +import * as Teams from '@/stores/teams' import * as Kb from '@/common-adapters' import type * as T from '@/constants/types' import {ModalTitle} from '../common' diff --git a/shared/todo.txt b/shared/todo.txt index ae8511c890cb..a13900cfbaa4 100644 --- a/shared/todo.txt +++ b/shared/todo.txt @@ -1,8 +1,8 @@ -android: +TOOD: +react-native-screens header doesn't handle dyanmic colors still https://github.com/software-mansion/react-native-screens/issues/3570 -ios: -input paste view removed <<<<<<<<<<<< +ios: ipad: expo-av to expo-video / expo-audio (not ready yet can't get video size...), missing stuff we need like on full screen change diff --git a/shared/tracker2/assertion.tsx b/shared/tracker2/assertion.tsx index 57a0014647c3..1cf078bf5ece 100644 --- a/shared/tracker2/assertion.tsx +++ b/shared/tracker2/assertion.tsx @@ -1,16 +1,16 @@ import * as React from 'react' import * as C from '@/constants' -import {useConfigState} from '@/constants/config' -import {useCurrentUserState} from '@/constants/current-user' +import {useConfigState} from '@/stores/config' +import {useCurrentUserState} from '@/stores/current-user' import type * as T from '@/constants/types' import openUrl from '@/util/open-url' import * as Kb from '@/common-adapters' import {SiteIcon} from '@/profile/generic/shared' import {formatTimeForAssertionPopup} from '@/util/timestamp' import {useColorScheme} from 'react-native' -import * as Tracker from '@/constants/tracker2' -import {useTrackerState} from '@/constants/tracker2' -import {useProfileState} from '@/constants/profile' +import * as Tracker from '@/stores/tracker2' +import {useTrackerState} from '@/stores/tracker2' +import {useProfileState} from '@/stores/profile' type OwnProps = { isSuggestion?: boolean @@ -424,7 +424,7 @@ const assertionColorToColor = (c: T.Tracker.AssertionColor) => { const StellarValue = (p: {value: string; color: T.Tracker.AssertionColor}) => { const {value, color} = p - const copyToClipboard = useConfigState(s => s.dispatch.dynamic.copyToClipboard) + const copyToClipboard = useConfigState(s => s.dispatch.defer.copyToClipboard) const onCopyAddress = React.useCallback(() => { copyToClipboard(value) }, [copyToClipboard, value]) diff --git a/shared/tracker2/bio.tsx b/shared/tracker2/bio.tsx index 9113b0f41b25..e26d67af9457 100644 --- a/shared/tracker2/bio.tsx +++ b/shared/tracker2/bio.tsx @@ -1,7 +1,7 @@ import * as C from '@/constants' import * as Kb from '@/common-adapters' -import {useTrackerState} from '@/constants/tracker2' -import {useFollowerState} from '@/constants/followers' +import {useTrackerState} from '@/stores/tracker2' +import {useFollowerState} from '@/stores/followers' type OwnProps = { inTracker: boolean diff --git a/shared/tracker2/remote-container.desktop.tsx b/shared/tracker2/remote-container.desktop.tsx index 16c3549ec2ca..4433a19c09b2 100644 --- a/shared/tracker2/remote-container.desktop.tsx +++ b/shared/tracker2/remote-container.desktop.tsx @@ -1,7 +1,7 @@ // Inside tracker we use an embedded Avatar which is connected. import * as React from 'react' import * as C from '@/constants' -import {useConfigState} from '@/constants/config' +import {useConfigState} from '@/stores/config' import * as R from '@/constants/remote' import * as RemoteGen from '../actions/remote-gen' import type * as T from '@/constants/types' @@ -9,11 +9,11 @@ import Tracker from './index.desktop' import type {DeserializeProps} from './remote-serializer.desktop' import KB2 from '@/util/electron.desktop' import {useAvatarState} from '@/common-adapters/avatar/store' -import {useTrackerState} from '@/constants/tracker2' -import {useUsersState} from '@/constants/users' -import {useFollowerState} from '@/constants/followers' -import {useCurrentUserState} from '@/constants/current-user' -import {useDarkModeState} from '@/constants/darkmode' +import {useTrackerState} from '@/stores/tracker2' +import {useUsersState} from '@/stores/users' +import {useFollowerState} from '@/stores/followers' +import {useCurrentUserState} from '@/stores/current-user' +import {useDarkModeState} from '@/stores/darkmode' const {closeWindow} = KB2.functions diff --git a/shared/tracker2/remote-proxy.desktop.tsx b/shared/tracker2/remote-proxy.desktop.tsx index f8ca55fb0c87..0cb25baaab0f 100644 --- a/shared/tracker2/remote-proxy.desktop.tsx +++ b/shared/tracker2/remote-proxy.desktop.tsx @@ -1,7 +1,7 @@ // A mirror of the remote tracker windows. import * as C from '@/constants' import {useAvatarState} from '@/common-adapters/avatar/store' -import {useConfigState} from '@/constants/config' +import {useConfigState} from '@/stores/config' import * as React from 'react' import useSerializeProps from '../desktop/remote/use-serialize-props.desktop' import useBrowserWindow from '../desktop/remote/use-browser-window.desktop' @@ -9,10 +9,10 @@ import {serialize, type ProxyProps} from './remote-serializer.desktop' import {intersect} from '@/util/set' import {mapFilterByKey} from '@/util/map' import {useColorScheme} from 'react-native' -import {useTrackerState} from '@/constants/tracker2' -import {useUsersState} from '@/constants/users' -import {useFollowerState} from '@/constants/followers' -import {useCurrentUserState} from '@/constants/current-user' +import {useTrackerState} from '@/stores/tracker2' +import {useUsersState} from '@/stores/users' +import {useFollowerState} from '@/stores/followers' +import {useCurrentUserState} from '@/stores/current-user' const MAX_TRACKERS = 5 const windowOpts = {hasShadow: false, height: 470, transparent: true, width: 320} diff --git a/shared/unlock-folders/device-list.desktop.tsx b/shared/unlock-folders/device-list.desktop.tsx index 9a2456b13680..ab64aeb86072 100644 --- a/shared/unlock-folders/device-list.desktop.tsx +++ b/shared/unlock-folders/device-list.desktop.tsx @@ -1,5 +1,5 @@ import * as Kb from '@/common-adapters' -import type {State as ConfigStore} from '@/constants/config' +import type {State as ConfigStore} from '@/stores/config' export type Props = { devices: ConfigStore['unlockFoldersDevices'] diff --git a/shared/unlock-folders/index.desktop.tsx b/shared/unlock-folders/index.desktop.tsx index d8cd03f4894a..af45ee616ebd 100644 --- a/shared/unlock-folders/index.desktop.tsx +++ b/shared/unlock-folders/index.desktop.tsx @@ -4,8 +4,8 @@ import DeviceList from './device-list.desktop' import DragHeader from '../desktop/remote/drag-header.desktop' import PaperKeyInput from './paper-key-input.desktop' import Success from './success.desktop' -import type * as Constants from '@/constants/unlock-folders' -import type {State as ConfigStore} from '@/constants/config' +import type * as Constants from '@/stores/unlock-folders' +import type {State as ConfigStore} from '@/stores/config' export type Props = { phase: Constants.State['phase'] diff --git a/shared/unlock-folders/remote-container.desktop.tsx b/shared/unlock-folders/remote-container.desktop.tsx index 15446563ffc6..18db5590d794 100644 --- a/shared/unlock-folders/remote-container.desktop.tsx +++ b/shared/unlock-folders/remote-container.desktop.tsx @@ -3,8 +3,8 @@ import * as React from 'react' import * as RemoteGen from '../actions/remote-gen' import UnlockFolders from './index.desktop' import type {DeserializeProps} from './remote-serializer.desktop' -import {useUnlockFoldersState as useUFState} from '@/constants/unlock-folders' -import {useDarkModeState} from '@/constants/darkmode' +import {useUnlockFoldersState as useUFState} from '@/stores/unlock-folders' +import {useDarkModeState} from '@/stores/darkmode' const RemoteContainer = (d: DeserializeProps) => { const {darkMode, devices, waiting, paperKeyError: _error} = d diff --git a/shared/unlock-folders/remote-proxy.desktop.tsx b/shared/unlock-folders/remote-proxy.desktop.tsx index 34ae7ba99c1d..ebe89bf3d4b7 100644 --- a/shared/unlock-folders/remote-proxy.desktop.tsx +++ b/shared/unlock-folders/remote-proxy.desktop.tsx @@ -4,7 +4,7 @@ import useBrowserWindow from '../desktop/remote/use-browser-window.desktop' import useSerializeProps from '../desktop/remote/use-serialize-props.desktop' import {serialize, type ProxyProps} from './remote-serializer.desktop' import {useColorScheme} from 'react-native' -import {useConfigState} from '@/constants/config' +import {useConfigState} from '@/stores/config' const windowOpts = {height: 300, width: 500} diff --git a/shared/unlock-folders/remote-serializer.desktop.tsx b/shared/unlock-folders/remote-serializer.desktop.tsx index 0370a4c3a86c..b46524dc672d 100644 --- a/shared/unlock-folders/remote-serializer.desktop.tsx +++ b/shared/unlock-folders/remote-serializer.desktop.tsx @@ -1,5 +1,5 @@ import * as T from '@/constants/types' -import type * as ConfigConstants from '@/constants/config' +import type * as ConfigConstants from '@/stores/config' import {produce} from 'immer' export type ProxyProps = { diff --git a/shared/util/crop.tsx b/shared/util/crop.tsx index b06f65bb002b..2f3b745a0c57 100644 --- a/shared/util/crop.tsx +++ b/shared/util/crop.tsx @@ -1,4 +1,4 @@ -import type * as T from '../constants/types' +import type * as T from '@/constants/types' export const fixCrop = (c?: T.RPCChat.Keybase1.ImageCropRect) => { return c diff --git a/shared/util/phone-numbers/index.tsx b/shared/util/phone-numbers/index.tsx index 7c419b18d118..587795dbaf09 100644 --- a/shared/util/phone-numbers/index.tsx +++ b/shared/util/phone-numbers/index.tsx @@ -2,10 +2,10 @@ import * as C from '@/constants' import libphonenumber from 'google-libphonenumber' const PNF = libphonenumber.PhoneNumberFormat -export const PhoneNumberFormat = PNF +const PhoneNumberFormat = PNF -export const phoneUtil = libphonenumber.PhoneNumberUtil.getInstance() -export const ValidationResult = libphonenumber.PhoneNumberUtil.ValidationResult +const phoneUtil = libphonenumber.PhoneNumberUtil.getInstance() +const ValidationResult = libphonenumber.PhoneNumberUtil.ValidationResult const supported = phoneUtil.getSupportedRegions() export type CountryData = { @@ -188,3 +188,17 @@ export const formatPhoneNumberInternational = (rawNumber: string): string | unde return undefined } } + +// Get phone number in e.164, or null if we can't parse it. +export const getE164 = (phoneNumber: string, countryCode?: string) => { + try { + const parsed = countryCode ? phoneUtil.parse(phoneNumber, countryCode) : phoneUtil.parse(phoneNumber) + const reason = phoneUtil.isPossibleNumberWithReason(parsed) + if (reason !== ValidationResult.IS_POSSIBLE) { + return null + } + return phoneUtil.format(parsed, PhoneNumberFormat.E164) + } catch { + return null + } +} diff --git a/shared/constants/platform-specific/index.d.ts b/shared/util/platform-specific/index.d.ts similarity index 86% rename from shared/constants/platform-specific/index.d.ts rename to shared/util/platform-specific/index.d.ts index 7eefb9fc101e..caecba1087ae 100644 --- a/shared/constants/platform-specific/index.d.ts +++ b/shared/util/platform-specific/index.d.ts @@ -1,4 +1,4 @@ -import type * as T from '../types' +import type * as T from '@/constants/types' type NextURI = string @@ -15,5 +15,4 @@ export declare function saveAttachmentToCameraRoll(fileURL: string, mimeType: st export declare function requestLocationPermission(mode: T.RPCChat.UIWatchPositionPerm): Promise export declare function watchPositionForMap(conversationIDKey: T.Chat.ConversationIDKey): Promise<() => void> -export declare function initPlatformListener(): void export declare function requestPermissionsToWrite(): Promise diff --git a/shared/util/platform-specific/index.desktop.tsx b/shared/util/platform-specific/index.desktop.tsx new file mode 100644 index 000000000000..425732a96ab7 --- /dev/null +++ b/shared/util/platform-specific/index.desktop.tsx @@ -0,0 +1,13 @@ +export const requestPermissionsToWrite = async () => { + return Promise.resolve(true) +} + +export function showShareActionSheet() { + throw new Error('Show Share Action - unsupported on this platform') +} +export async function saveAttachmentToCameraRoll() { + return Promise.reject(new Error('Save Attachment to camera roll - unsupported on this platform')) +} + +export const requestLocationPermission = async () => Promise.resolve() +export const watchPositionForMap = async () => Promise.resolve(() => {}) diff --git a/shared/util/platform-specific/index.native.tsx b/shared/util/platform-specific/index.native.tsx new file mode 100644 index 000000000000..8b1f620d5d75 --- /dev/null +++ b/shared/util/platform-specific/index.native.tsx @@ -0,0 +1,111 @@ +import * as T from '@/constants/types' +import * as ExpoLocation from 'expo-location' +import * as MediaLibrary from 'expo-media-library' +import {addNotificationRequest} from 'react-native-kb' +import logger from '@/logger' +import {ActionSheetIOS} from 'react-native' +import {isIOS, isAndroid} from '@/constants/platform.native' +import {androidShare, androidShareText, androidUnlink} from 'react-native-kb' + +export const requestPermissionsToWrite = async () => { + if (isAndroid) { + const p = await MediaLibrary.requestPermissionsAsync(false) + return p.granted ? Promise.resolve() : Promise.reject(new Error('Unable to acquire storage permissions')) + } + return Promise.resolve() +} + +export const requestLocationPermission = async (mode: T.RPCChat.UIWatchPositionPerm) => { + if (isIOS) { + logger.info('[location] Requesting location perms', mode) + switch (mode) { + case T.RPCChat.UIWatchPositionPerm.base: + { + const iosFGPerms = await ExpoLocation.requestForegroundPermissionsAsync() + if (iosFGPerms.ios?.scope === 'none') { + throw new Error('Please allow Keybase to access your location in the phone settings.') + } + } + break + case T.RPCChat.UIWatchPositionPerm.always: { + const iosBGPerms = await ExpoLocation.requestBackgroundPermissionsAsync() + if (iosBGPerms.status !== ExpoLocation.PermissionStatus.GRANTED) { + throw new Error( + 'Please allow Keybase to access your location even if the app is not running for live location.' + ) + } + break + } + } + } else if (isAndroid) { + const androidBGPerms = await ExpoLocation.requestForegroundPermissionsAsync() + if (androidBGPerms.status !== ExpoLocation.PermissionStatus.GRANTED) { + throw new Error('Unable to acquire location permissions') + } + } +} + +export async function saveAttachmentToCameraRoll(filePath: string, mimeType: string): Promise { + const fileURL = 'file://' + filePath + const saveType: 'video' | 'photo' = mimeType.startsWith('video') ? 'video' : 'photo' + const logPrefix = '[saveAttachmentToCameraRoll] ' + try { + try { + // see it we can keep going anyways, android perms are needed sometimes and sometimes not w/ 33 + await requestPermissionsToWrite() + } catch {} + logger.info(logPrefix + `Attempting to save as ${saveType}`) + await MediaLibrary.saveToLibraryAsync(fileURL) + logger.info(logPrefix + 'Success') + } catch (e) { + // This can fail if the user backgrounds too quickly, so throw up a local notification + // just in case to get their attention. + addNotificationRequest({ + body: `Failed to save ${saveType} to camera roll`, + id: Math.floor(Math.random() * 2 ** 32).toString(), + }).catch(() => {}) + logger.debug(logPrefix + 'failed to save: ' + e) + throw e + } finally { + try { + await androidUnlink(filePath) + } catch { + logger.warn('failed to unlink') + } + } +} + +export const showShareActionSheet = async (options: { + filePath?: string + message?: string + mimeType: string +}) => { + if (isIOS) { + return new Promise((resolve, reject) => { + ActionSheetIOS.showShareActionSheetWithOptions( + { + message: options.message, + url: options.filePath, + }, + reject, + resolve + ) + }) + } else { + if (!options.filePath && options.message) { + try { + await androidShareText(options.message, options.mimeType) + return {completed: true, method: ''} + } catch (e) { + throw new Error('Failed to share: ' + String(e)) + } + } + + try { + await androidShare(options.filePath ?? '', options.mimeType) + return {completed: true, method: ''} + } catch (e) { + throw new Error('Failed to share: ' + String(e)) + } + } +} diff --git a/shared/constants/platform-specific/input-monitor.desktop.tsx b/shared/util/platform-specific/input-monitor.desktop.tsx similarity index 100% rename from shared/constants/platform-specific/input-monitor.desktop.tsx rename to shared/util/platform-specific/input-monitor.desktop.tsx diff --git a/shared/constants/platform-specific/kbfs-notifications.tsx b/shared/util/platform-specific/kbfs-notifications.tsx similarity index 98% rename from shared/constants/platform-specific/kbfs-notifications.tsx rename to shared/util/platform-specific/kbfs-notifications.tsx index f06fd2b4c629..1ee3f17113f6 100644 --- a/shared/constants/platform-specific/kbfs-notifications.tsx +++ b/shared/util/platform-specific/kbfs-notifications.tsx @@ -1,5 +1,5 @@ import {pathSep} from '@/constants/platform' -import {storeRegistry} from '@/constants/store-registry' +import {useCurrentUserState} from '@/stores/current-user' import capitalize from 'lodash/capitalize' import * as T from '@/constants/types' import {parseFolderNameToUsers} from '@/util/kbfs' @@ -200,7 +200,7 @@ export function kbfsNotification( let title = `KBFS: ${action}` let body = `Chat or files with ${usernames} ${notification.status}` - const user = storeRegistry.getState('current-user').username + const user = useCurrentUserState.getState().username let rateLimitKey: string const isError = notification.statusCode === T.RPCGen.FSStatusCode.error diff --git a/shared/util/qr-code.tsx b/shared/util/qr-code.tsx new file mode 100644 index 000000000000..4476b5c11fe2 --- /dev/null +++ b/shared/util/qr-code.tsx @@ -0,0 +1,643 @@ +// QR Code Generator — Version 4, ECL L, Byte mode only +// Generates GIF data URL with Keybase blue (#4C8EFF) dark modules +// +// Based on qrcode-generator by Kazuhiko Arase (MIT license) +// https://github.com/nicokoch/qrcode-generator + +// ============================================================ +// GF(2^8) arithmetic for Reed-Solomon error correction +// ============================================================ + +const EXP_TABLE: number[] = [] +const LOG_TABLE: number[] = [] + +for (let i = 0; i < 8; i++) EXP_TABLE[i] = 1 << i +for (let i = 8; i < 256; i++) { + EXP_TABLE[i] = EXP_TABLE[i - 4]! ^ EXP_TABLE[i - 5]! ^ EXP_TABLE[i - 6]! ^ EXP_TABLE[i - 8]! +} +for (let i = 0; i < 255; i++) LOG_TABLE[EXP_TABLE[i]!] = i + +const glog = (n: number) => LOG_TABLE[n]! +const gexp = (n: number): number => { + let v = n + while (v < 0) v += 255 + while (v >= 256) v -= 255 + return EXP_TABLE[v]! +} + +// ============================================================ +// Polynomial operations (Reed-Solomon) +// ============================================================ + +// Strip leading zeros and append `shift` zero terms +function makePoly(num: number[], shift: number): number[] { + let offset = 0 + while (offset < num.length && num[offset] === 0) offset++ + const result = new Array(num.length - offset + shift).fill(0) + for (let i = 0; i < num.length - offset; i++) result[i] = num[i + offset]! + return result +} + +function polyMultiply(a: number[], b: number[]): number[] { + const num = new Array(a.length + b.length - 1).fill(0) + for (let i = 0; i < a.length; i++) { + for (let j = 0; j < b.length; j++) { + num[i + j] = num[i + j]! ^ gexp(glog(a[i]!) + glog(b[j]!)) + } + } + return makePoly(num, 0) +} + +function polyMod(a: number[], b: number[]): number[] { + if (a.length - b.length < 0) return a + const ratio = glog(a[0]!) - glog(b[0]!) + const num = a.slice() + for (let i = 0; i < b.length; i++) { + num[i] = num[i]! ^ gexp(glog(b[i]!) + ratio) + } + return polyMod(makePoly(num, 0), b) +} + +function getErrorCorrectPolynomial(ecLength: number): number[] { + let poly = [1] + for (let i = 0; i < ecLength; i++) { + poly = polyMultiply(poly, makePoly([1, gexp(i)], 0)) + } + return poly +} + +// ============================================================ +// Bit buffer for data encoding +// ============================================================ + +function createBitBuffer() { + const buffer: number[] = [] + let length = 0 + return { + getBuffer: () => buffer, + getLengthInBits: () => length, + put(num: number, len: number) { + for (let i = 0; i < len; i++) { + this.putBit(((num >>> (len - i - 1)) & 1) === 1) + } + }, + putBit(bit: boolean) { + const bufIndex = Math.floor(length / 8) + if (buffer.length <= bufIndex) buffer.push(0) + if (bit) buffer[bufIndex] = buffer[bufIndex]! | (0x80 >>> (length % 8)) + length++ + }, + } +} + +// ============================================================ +// BCH encoding for format information +// ============================================================ + +const G15 = (1 << 10) | (1 << 8) | (1 << 5) | (1 << 4) | (1 << 2) | (1 << 1) | (1 << 0) +const G15_MASK = (1 << 14) | (1 << 12) | (1 << 10) | (1 << 4) | (1 << 1) + +function getBCHDigit(data: number): number { + let digit = 0 + let d = data + while (d !== 0) { + digit++ + d >>>= 1 + } + return digit +} + +function getBCHTypeInfo(data: number): number { + let d = data << 10 + while (getBCHDigit(d) - getBCHDigit(G15) >= 0) { + d ^= G15 << (getBCHDigit(d) - getBCHDigit(G15)) + } + return ((data << 10) | d) ^ G15_MASK +} + +// ============================================================ +// Mask patterns +// ============================================================ + +const MASK_FUNCTIONS: Array<(i: number, j: number) => boolean> = [ + (i, j) => (i + j) % 2 === 0, + (i, _j) => i % 2 === 0, + (_i, j) => j % 3 === 0, + (i, j) => (i + j) % 3 === 0, + (i, j) => (Math.floor(i / 2) + Math.floor(j / 3)) % 2 === 0, + (i, j) => ((i * j) % 2) + ((i * j) % 3) === 0, + (i, j) => (((i * j) % 2) + ((i * j) % 3)) % 2 === 0, + (i, j) => (((i * j) % 3) + ((i + j) % 2)) % 2 === 0, +] + +// ============================================================ +// Penalty scoring for mask selection +// ============================================================ + +function getLostPoint(modules: boolean[][], moduleCount: number): number { + let lostPoint = 0 + + // LEVEL1: same-color neighbors + for (let row = 0; row < moduleCount; row++) { + for (let col = 0; col < moduleCount; col++) { + let sameCount = 0 + const dark = modules[row]![col]! + for (let r = -1; r <= 1; r++) { + if (row + r < 0 || moduleCount <= row + r) continue + for (let c = -1; c <= 1; c++) { + if (col + c < 0 || moduleCount <= col + c) continue + if (r === 0 && c === 0) continue + if (dark === modules[row + r]![col + c]!) sameCount++ + } + } + if (sameCount > 5) lostPoint += 3 + sameCount - 5 + } + } + + // LEVEL2: 2×2 same-color blocks + for (let row = 0; row < moduleCount - 1; row++) { + for (let col = 0; col < moduleCount - 1; col++) { + let count = 0 + if (modules[row]![col]!) count++ + if (modules[row + 1]![col]!) count++ + if (modules[row]![col + 1]!) count++ + if (modules[row + 1]![col + 1]!) count++ + if (count === 0 || count === 4) lostPoint += 3 + } + } + + // LEVEL3: 1:1:3:1:1 pattern in rows + for (let row = 0; row < moduleCount; row++) { + for (let col = 0; col < moduleCount - 6; col++) { + if ( + modules[row]![col]! && + !modules[row]![col + 1]! && + modules[row]![col + 2]! && + modules[row]![col + 3]! && + modules[row]![col + 4]! && + !modules[row]![col + 5]! && + modules[row]![col + 6]! + ) { + lostPoint += 40 + } + } + } + + // LEVEL3: 1:1:3:1:1 pattern in columns + for (let col = 0; col < moduleCount; col++) { + for (let row = 0; row < moduleCount - 6; row++) { + if ( + modules[row]![col]! && + !modules[row + 1]![col]! && + modules[row + 2]![col]! && + modules[row + 3]![col]! && + modules[row + 4]![col]! && + !modules[row + 5]![col]! && + modules[row + 6]![col]! + ) { + lostPoint += 40 + } + } + } + + // LEVEL4: dark/light ratio + let darkCount = 0 + for (let col = 0; col < moduleCount; col++) { + for (let row = 0; row < moduleCount; row++) { + if (modules[row]![col]!) darkCount++ + } + } + const ratio = Math.abs((100 * darkCount) / moduleCount / moduleCount - 50) / 5 + lostPoint += ratio * 10 + + return lostPoint +} + +// ============================================================ +// QR matrix construction (Version 4, 33×33) +// ============================================================ + +const MODULE_COUNT = 33 // Version 4: 4*4 + 17 +const ECL_L = 1 + +function createModules(): Array> { + const modules: Array> = [] + for (let row = 0; row < MODULE_COUNT; row++) { + modules[row] = new Array(MODULE_COUNT).fill(null) + } + return modules +} + +function setupFinderPattern(modules: Array>, row: number, col: number) { + for (let r = -1; r <= 7; r++) { + if (row + r <= -1 || MODULE_COUNT <= row + r) continue + for (let c = -1; c <= 7; c++) { + if (col + c <= -1 || MODULE_COUNT <= col + c) continue + if ( + (0 <= r && r <= 6 && (c === 0 || c === 6)) || + (0 <= c && c <= 6 && (r === 0 || r === 6)) || + (2 <= r && r <= 4 && 2 <= c && c <= 4) + ) { + modules[row + r]![col + c] = true + } else { + modules[row + r]![col + c] = false + } + } + } +} + +function setupAlignmentPattern(modules: Array>) { + // Version 4 alignment positions: [6, 26] + const pos = [6, 26] + for (const pRow of pos) { + for (const pCol of pos) { + if (modules[pRow]![pCol] !== null) continue + for (let r = -2; r <= 2; r++) { + for (let c = -2; c <= 2; c++) { + modules[pRow + r]![pCol + c] = + r === -2 || r === 2 || c === -2 || c === 2 || (r === 0 && c === 0) + } + } + } + } +} + +function setupTimingPattern(modules: Array>) { + for (let r = 8; r < MODULE_COUNT - 8; r++) { + if (modules[r]![6] !== null) continue + modules[r]![6] = r % 2 === 0 + } + for (let c = 8; c < MODULE_COUNT - 8; c++) { + if (modules[6]![c] !== null) continue + modules[6]![c] = c % 2 === 0 + } +} + +function setupTypeInfo( + modules: Array>, + test: boolean, + maskPattern: number +) { + const data = (ECL_L << 3) | maskPattern + const bits = getBCHTypeInfo(data) + + for (let i = 0; i < 15; i++) { + const mod = !test && ((bits >> i) & 1) === 1 + if (i < 6) { + modules[i]![8] = mod + } else if (i < 8) { + modules[i + 1]![8] = mod + } else { + modules[MODULE_COUNT - 15 + i]![8] = mod + } + } + + for (let i = 0; i < 15; i++) { + const mod = !test && ((bits >> i) & 1) === 1 + if (i < 8) { + modules[8]![MODULE_COUNT - i - 1] = mod + } else if (i < 9) { + modules[8]![15 - i - 1 + 1] = mod + } else { + modules[8]![15 - i - 1] = mod + } + } + + modules[MODULE_COUNT - 8]![8] = !test +} + +function mapData( + modules: Array>, + data: number[], + maskPattern: number +) { + let inc = -1 + let row = MODULE_COUNT - 1 + let bitIndex = 7 + let byteIndex = 0 + const maskFunc = MASK_FUNCTIONS[maskPattern]! + + for (let col = MODULE_COUNT - 1; col > 0; col -= 2) { + if (col === 6) col -= 1 + while (true) { + for (let c = 0; c < 2; c++) { + if (modules[row]![col - c] === null) { + let dark = false + if (byteIndex < data.length) { + dark = ((data[byteIndex]! >>> bitIndex) & 1) === 1 + } + if (maskFunc(row, col - c)) dark = !dark + modules[row]![col - c] = dark + bitIndex-- + if (bitIndex === -1) { + byteIndex++ + bitIndex = 7 + } + } + } + row += inc + if (row < 0 || MODULE_COUNT <= row) { + row -= inc + inc = -inc + break + } + } + } +} + +// ============================================================ +// Data encoding and Reed-Solomon error correction +// ============================================================ + +function encodeData(str: string): number[] { + const PAD0 = 0xec + const PAD1 = 0x11 + + // Convert string to bytes (charCode & 0xff, matching original library) + const bytes: number[] = [] + for (let i = 0; i < str.length; i++) bytes.push(str.charCodeAt(i) & 0xff) + + const buffer = createBitBuffer() + + // Byte mode indicator: MODE_8BIT_BYTE = 4 + buffer.put(4, 4) + + // Character count (8 bits for versions 1-9) + buffer.put(bytes.length, 8) + + // Data bytes + for (const b of bytes) buffer.put(b, 8) + + // Total data capacity: 80 bytes = 640 bits (version 4, ECL L) + const totalDataBits = 80 * 8 + + // Terminator + if (buffer.getLengthInBits() + 4 <= totalDataBits) { + buffer.put(0, 4) + } + + // Byte-align + while (buffer.getLengthInBits() % 8 !== 0) { + buffer.putBit(false) + } + + // Pad to capacity + while (buffer.getLengthInBits() < totalDataBits) { + buffer.put(PAD0, 8) + if (buffer.getLengthInBits() >= totalDataBits) break + buffer.put(PAD1, 8) + } + + // Reed-Solomon error correction + const ecCount = 20 + const dataCount = 80 + const dcData: number[] = [] + for (let i = 0; i < dataCount; i++) dcData.push(0xff & buffer.getBuffer()[i]!) + + const rsPoly = getErrorCorrectPolynomial(ecCount) + const rawPoly = makePoly(dcData, rsPoly.length - 1) + const modPoly = polyMod(rawPoly, rsPoly) + + const ecData: number[] = [] + for (let i = 0; i < rsPoly.length - 1; i++) { + const modIndex = i + modPoly.length - (rsPoly.length - 1) + ecData.push(modIndex >= 0 ? modPoly[modIndex]! : 0) + } + + // Single block: data codewords then EC codewords + return [...dcData, ...ecData] +} + +// ============================================================ +// Build complete QR matrix +// ============================================================ + +function buildMatrix(data: string): boolean[][] { + const codewords = encodeData(data) + + // Evaluate all 8 mask patterns to find best + let bestMask = 0 + let minPenalty = Infinity + + for (let mask = 0; mask < 8; mask++) { + const modules = createModules() + setupFinderPattern(modules, 0, 0) + setupFinderPattern(modules, MODULE_COUNT - 7, 0) + setupFinderPattern(modules, 0, MODULE_COUNT - 7) + setupAlignmentPattern(modules) + setupTimingPattern(modules) + setupTypeInfo(modules, true, mask) + mapData(modules, codewords, mask) + + const penalty = getLostPoint(modules as boolean[][], MODULE_COUNT) + if (mask === 0 || minPenalty > penalty) { + minPenalty = penalty + bestMask = mask + } + } + + // Build final matrix with best mask + const modules = createModules() + setupFinderPattern(modules, 0, 0) + setupFinderPattern(modules, MODULE_COUNT - 7, 0) + setupFinderPattern(modules, 0, MODULE_COUNT - 7) + setupAlignmentPattern(modules) + setupTimingPattern(modules) + setupTypeInfo(modules, false, bestMask) + mapData(modules, codewords, bestMask) + + return modules as boolean[][] +} + +// ============================================================ +// GIF generation with LZW compression +// ============================================================ + +function getLZWRaster(data: number[], lzwMinCodeSize: number): number[] { + const clearCode = 1 << lzwMinCodeSize + const endCode = (1 << lzwMinCodeSize) + 1 + let bitLength = lzwMinCodeSize + 1 + + // LZW table using string keys (matching original library exactly) + const map: {[key: string]: number} = {} + let tableSize = 0 + const tableAdd = (key: string) => { + map[key] = tableSize + tableSize++ + } + + for (let i = 0; i < clearCode; i++) tableAdd(String.fromCharCode(i)) + tableAdd(String.fromCharCode(clearCode)) + tableAdd(String.fromCharCode(endCode)) + + // LSB-first bit output stream + const outBytes: number[] = [] + let bitBuffer = 0 + let bitBufLen = 0 + + const writeBits = (d: number, len: number) => { + let vd = d + let vl = len + while (bitBufLen + vl >= 8) { + outBytes.push(0xff & ((vd << bitBufLen) | bitBuffer)) + vl -= 8 - bitBufLen + vd >>>= 8 - bitBufLen + bitBuffer = 0 + bitBufLen = 0 + } + bitBuffer = (vd << bitBufLen) | bitBuffer + bitBufLen += vl + } + + // Write clear code + writeBits(clearCode, bitLength) + + let dataIndex = 0 + let s = String.fromCharCode(data[dataIndex]!) + dataIndex++ + + while (dataIndex < data.length) { + const c = String.fromCharCode(data[dataIndex]!) + dataIndex++ + + if (s + c in map) { + s = s + c + } else { + writeBits(map[s]!, bitLength) + if (tableSize < 0xfff) { + if (tableSize === 1 << bitLength) bitLength++ + tableAdd(s + c) + } + s = c + } + } + + writeBits(map[s]!, bitLength) + writeBits(endCode, bitLength) + + // Flush remaining bits + if (bitBufLen > 0) outBytes.push(bitBuffer) + + return outBytes +} + +// ============================================================ +// Base64 encoding (matching original library's custom encoder) +// ============================================================ + +const B64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' + +function base64Encode(bytes: number[]): string { + let buffer = 0 + let buflen = 0 + let result = '' + let length = 0 + + for (const byte of bytes) { + buffer = (buffer << 8) | (byte & 0xff) + buflen += 8 + length++ + while (buflen >= 6) { + result += B64.charAt((buffer >>> (buflen - 6)) & 0x3f) + buflen -= 6 + } + } + + if (buflen > 0) { + result += B64.charAt((buffer << (6 - buflen)) & 0x3f) + } + + if (length % 3 !== 0) { + const padlen = 3 - (length % 3) + for (let i = 0; i < padlen; i++) result += '=' + } + + return result +} + +// ============================================================ +// Main exported function +// ============================================================ + +export default function generateQRDataURL( + data: string, + cellSize: number +): {url: string; moduleCount: number} { + const modules = buildMatrix(data) + const margin = cellSize * 4 + const size = MODULE_COUNT * cellSize + margin * 2 + const min = margin + const max = size - margin + + // Build pixel data (0 = blue/dark, 1 = white/light) + const pixelData = new Array(size * size) + for (let y = 0; y < size; y++) { + for (let x = 0; x < size; x++) { + if (min <= x && x < max && min <= y && y < max) { + const c = Math.floor((x - min) / cellSize) + const r = Math.floor((y - min) / cellSize) + pixelData[y * size + x] = modules[r]![c]! ? 0 : 1 + } else { + pixelData[y * size + x] = 1 + } + } + } + + // Build GIF + const out: number[] = [] + const writeByte = (b: number) => out.push(b & 0xff) + const writeShort = (i: number) => { + writeByte(i) + writeByte(i >>> 8) + } + const writeString = (s: string) => { + for (let i = 0; i < s.length; i++) writeByte(s.charCodeAt(i)) + } + + // GIF87a header + writeString('GIF87a') + writeShort(size) + writeShort(size) + writeByte(0x80) // GCT flag, 2 colors + writeByte(0) + writeByte(0) + + // Global Color Table: index 0 = Keybase blue, index 1 = white + writeByte(0x4c) + writeByte(0x8e) + writeByte(0xff) + writeByte(0xff) + writeByte(0xff) + writeByte(0xff) + + // Image Descriptor + writeString(',') + writeShort(0) + writeShort(0) + writeShort(size) + writeShort(size) + writeByte(0) + + // LZW compressed raster data + const lzwMinCodeSize = 2 + const raster = getLZWRaster(pixelData, lzwMinCodeSize) + writeByte(lzwMinCodeSize) + + let offset = 0 + while (raster.length - offset > 255) { + writeByte(255) + for (let i = 0; i < 255; i++) writeByte(raster[i + offset]!) + offset += 255 + } + writeByte(raster.length - offset) + for (let i = 0; i < raster.length - offset; i++) writeByte(raster[i + offset]!) + writeByte(0x00) + + // GIF Terminator + writeString(';') + + return { + moduleCount: MODULE_COUNT, + url: 'data:image/gif;base64,' + base64Encode(out), + } +} diff --git a/shared/util/zustand.tsx b/shared/util/zustand.tsx index 340308eb6d95..b3d223636d5b 100644 --- a/shared/util/zustand.tsx +++ b/shared/util/zustand.tsx @@ -9,7 +9,13 @@ import {wrapErrors} from '@/util/debug' // needed for tsc export type {WritableDraft} from 'immer' -type HasReset = {dispatch: {resetDeleteMe?: boolean; resetState: 'default' | (() => void)}} +type HasReset = { + dispatch: { + defer?: Record + resetDeleteMe?: boolean + resetState: 'default' | (() => void) + } +} const resetters: ((isDebug?: boolean) => void)[] = [] const resettersAndDelete: ((isDebug?: boolean) => void)[] = [] @@ -39,8 +45,9 @@ export const createZustand = ( let resetFunc: () => void if (reset === 'default') { resetFunc = () => { + const currentDefer = store.getState().dispatch.defer // eslint-disable-next-line - store.setState(initialState as any, true) + store.setState({...initialState, dispatch: {...initialState.dispatch, defer: currentDefer}} as any, true) } } else { resetFunc = reset diff --git a/shared/wallets/index.tsx b/shared/wallets/index.tsx index 796c7dc738ab..9cdbccfe61df 100644 --- a/shared/wallets/index.tsx +++ b/shared/wallets/index.tsx @@ -2,8 +2,8 @@ import * as C from '@/constants' import * as React from 'react' import * as Kb from '@/common-adapters' import * as T from '@/constants/types' -import * as Wallets from '@/constants/wallets' -import {useState as useWalletsState} from '@/constants/wallets' +import * as Wallets from '@/stores/wallets' +import {useState as useWalletsState} from '@/stores/wallets' const Row = (p: {account: Wallets.Account}) => { const {account} = p diff --git a/shared/wallets/really-remove-account.tsx b/shared/wallets/really-remove-account.tsx index e72a8cf90bba..aa7b88fb2143 100644 --- a/shared/wallets/really-remove-account.tsx +++ b/shared/wallets/really-remove-account.tsx @@ -3,9 +3,9 @@ import * as Kb from '@/common-adapters' import * as T from '@/constants/types' import * as React from 'react' import WalletPopup from './wallet-popup' -import * as Wallets from '@/constants/wallets' -import {useState as useWalletsState} from '@/constants/wallets' -import {useConfigState} from '@/constants/config' +import * as Wallets from '@/stores/wallets' +import {useState as useWalletsState} from '@/stores/wallets' +import {useConfigState} from '@/stores/config' type OwnProps = {accountID: string} @@ -17,7 +17,7 @@ const ReallyRemoveAccountPopup = (props: OwnProps) => { const attachmentRef = React.useRef(null) const setShowToastFalseLater = Kb.useTimeout(() => setShowToast(false), 2000) - const copyToClipboard = useConfigState(s => s.dispatch.dynamic.copyToClipboard) + const copyToClipboard = useConfigState(s => s.dispatch.defer.copyToClipboard) const [sk, setSK] = React.useState('') const loading = !sk diff --git a/shared/wallets/remove-account.tsx b/shared/wallets/remove-account.tsx index 6088a87265c4..4847147c1825 100644 --- a/shared/wallets/remove-account.tsx +++ b/shared/wallets/remove-account.tsx @@ -1,7 +1,7 @@ import * as C from '@/constants' import * as Kb from '@/common-adapters' import WalletPopup from './wallet-popup' -import {useState as useWalletsState} from '@/constants/wallets' +import {useState as useWalletsState} from '@/stores/wallets' type OwnProps = {accountID: string} diff --git a/shared/whats-new/container.tsx b/shared/whats-new/container.tsx index 605376435ff3..5f2fbf69501d 100644 --- a/shared/whats-new/container.tsx +++ b/shared/whats-new/container.tsx @@ -1,10 +1,10 @@ import * as C from '@/constants' import openURL from '@/util/open-url' -import {currentVersion} from '@/constants/whats-new' +import {currentVersion} from '@/stores/whats-new' import {Current, Last, LastLast} from './versions' import WhatsNew from '.' -import {useWhatsNewState as useWNState} from '@/constants/whats-new' -import {useConfigState} from '@/constants/config' +import {useWhatsNewState as useWNState} from '@/stores/whats-new' +import {useConfigState} from '@/stores/config' const WhatsNewContainer = () => { const _onNavigateExternal = (url: string) => { diff --git a/shared/whats-new/icon/index.tsx b/shared/whats-new/icon/index.tsx index 05ca4fb3b455..08ee61d17832 100644 --- a/shared/whats-new/icon/index.tsx +++ b/shared/whats-new/icon/index.tsx @@ -1,10 +1,10 @@ import * as React from 'react' import * as Kb from '@/common-adapters' import type {IconStyle} from '@/common-adapters/icon' -import {keybaseFM} from '@/constants/whats-new' +import {keybaseFM} from '@/stores/whats-new' import Popup from './popup' import './icon.css' -import {useWhatsNewState as useWNState} from '@/constants/whats-new' +import {useWhatsNewState as useWNState} from '@/stores/whats-new' type OwnProps = { color?: string diff --git a/shared/whats-new/index.tsx b/shared/whats-new/index.tsx index f52f93af4ef6..f15609db9263 100644 --- a/shared/whats-new/index.tsx +++ b/shared/whats-new/index.tsx @@ -1,7 +1,7 @@ import type * as React from 'react' import * as Kb from '@/common-adapters' import type * as C from '@/constants' -import {currentVersion, lastVersion, lastLastVersion} from '@/constants/whats-new' +import {currentVersion, lastVersion, lastLastVersion} from '@/stores/whats-new' import type {VersionProps} from './versions' type Props = { diff --git a/shared/whats-new/versions.tsx b/shared/whats-new/versions.tsx index cd3763ab6b12..ddf79961c28c 100644 --- a/shared/whats-new/versions.tsx +++ b/shared/whats-new/versions.tsx @@ -1,10 +1,10 @@ import * as C from '@/constants' -import {encryptTab} from '@/constants/crypto/util' +import {encryptTab} from '@/constants/crypto' import type * as React from 'react' import * as Kb from '@/common-adapters' -import {keybaseFM} from '@/constants/whats-new' +import {keybaseFM} from '@/stores/whats-new' import NewFeatureRow from './new-feature-row' -import {settingsCryptoTab, settingsDisplayTab} from '@/constants/settings/util' +import {settingsCryptoTab, settingsDisplayTab} from '@/constants/settings' export type VersionProps = { seen: boolean diff --git a/shared/yarn.lock b/shared/yarn.lock index b1bcd0b269be..0aab599e89da 100644 --- a/shared/yarn.lock +++ b/shared/yarn.lock @@ -7,41 +7,41 @@ resolved "https://registry.yarnpkg.com/@0no-co/graphql.web/-/graphql.web-1.2.0.tgz#296d00581bfaaabfda1e976849d927824aaea81b" integrity sha512-/1iHy9TTr63gE1YcR5idjx8UREz1s0kFhydf3bBLCXyqjhkIc6igAzTOx3zPifCwFR87tsh/4Pa9cNts6d2otw== -"@babel/code-frame@7.10.4", "@babel/code-frame@~7.10.4": +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.20.0", "@babel/code-frame@^7.24.7", "@babel/code-frame@^7.28.6", "@babel/code-frame@^7.29.0": + version "7.29.0" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.29.0.tgz#7cd7a59f15b3cc0dcd803038f7792712a7d0b15c" + integrity sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw== + dependencies: + "@babel/helper-validator-identifier" "^7.28.5" + js-tokens "^4.0.0" + picocolors "^1.1.1" + +"@babel/code-frame@~7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.10.4.tgz#168da1a36e90da68ae8d49c0f1b48c7c6249213a" integrity sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg== dependencies: "@babel/highlight" "^7.10.4" -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.20.0", "@babel/code-frame@^7.24.7", "@babel/code-frame@^7.27.1": - version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.27.1.tgz#200f715e66d52a23b221a9435534a91cc13ad5be" - integrity sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg== - dependencies: - "@babel/helper-validator-identifier" "^7.27.1" - js-tokens "^4.0.0" - picocolors "^1.1.1" - -"@babel/compat-data@^7.27.2", "@babel/compat-data@^7.27.7", "@babel/compat-data@^7.28.5": - version "7.28.5" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.28.5.tgz#a8a4962e1567121ac0b3b487f52107443b455c7f" - integrity sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA== - -"@babel/core@7.28.5", "@babel/core@^7.11.6", "@babel/core@^7.12.3", "@babel/core@^7.20.0", "@babel/core@^7.24.4", "@babel/core@^7.25.2", "@babel/core@^7.26.0": - version "7.28.5" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.28.5.tgz#4c81b35e51e1b734f510c99b07dfbc7bbbb48f7e" - integrity sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw== - dependencies: - "@babel/code-frame" "^7.27.1" - "@babel/generator" "^7.28.5" - "@babel/helper-compilation-targets" "^7.27.2" - "@babel/helper-module-transforms" "^7.28.3" - "@babel/helpers" "^7.28.4" - "@babel/parser" "^7.28.5" - "@babel/template" "^7.27.2" - "@babel/traverse" "^7.28.5" - "@babel/types" "^7.28.5" +"@babel/compat-data@^7.28.6", "@babel/compat-data@^7.29.0": + version "7.29.0" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.29.0.tgz#00d03e8c0ac24dd9be942c5370990cbe1f17d88d" + integrity sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg== + +"@babel/core@7.29.0", "@babel/core@^7.11.6", "@babel/core@^7.12.3", "@babel/core@^7.20.0", "@babel/core@^7.24.4", "@babel/core@^7.25.2", "@babel/core@^7.26.0": + version "7.29.0" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.29.0.tgz#5286ad785df7f79d656e88ce86e650d16ca5f322" + integrity sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA== + dependencies: + "@babel/code-frame" "^7.29.0" + "@babel/generator" "^7.29.0" + "@babel/helper-compilation-targets" "^7.28.6" + "@babel/helper-module-transforms" "^7.28.6" + "@babel/helpers" "^7.28.6" + "@babel/parser" "^7.29.0" + "@babel/template" "^7.28.6" + "@babel/traverse" "^7.29.0" + "@babel/types" "^7.29.0" "@jridgewell/remapping" "^2.3.5" convert-source-map "^2.0.0" debug "^4.1.0" @@ -50,21 +50,21 @@ semver "^6.3.1" "@babel/eslint-parser@^7.25.1": - version "7.28.5" - resolved "https://registry.yarnpkg.com/@babel/eslint-parser/-/eslint-parser-7.28.5.tgz#0b8883a4a1c2cbed7b3cd9d7765d80e8f480b9ae" - integrity sha512-fcdRcWahONYo+JRnJg1/AekOacGvKx12Gu0qXJXFi2WBqQA1i7+O5PaxRB7kxE/Op94dExnCiiar6T09pvdHpA== + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/eslint-parser/-/eslint-parser-7.28.6.tgz#6a294a4add732ebe7ded8a8d2792dd03dd81dc3f" + integrity sha512-QGmsKi2PBO/MHSQk+AAgA9R6OHQr+VqnniFE0eMWZcVcfBZoA2dKn2hUsl3Csg/Plt9opRUWdY7//VXsrIlEiA== dependencies: "@nicolo-ribaudo/eslint-scope-5-internals" "5.1.1-v1" eslint-visitor-keys "^2.1.0" semver "^6.3.1" -"@babel/generator@^7.20.5", "@babel/generator@^7.25.0", "@babel/generator@^7.26.2", "@babel/generator@^7.28.5": - version "7.28.5" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.28.5.tgz#712722d5e50f44d07bc7ac9fe84438742dd61298" - integrity sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ== +"@babel/generator@^7.20.5", "@babel/generator@^7.25.0", "@babel/generator@^7.26.2", "@babel/generator@^7.29.0": + version "7.29.1" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.29.1.tgz#d09876290111abbb00ef962a7b83a5307fba0d50" + integrity sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw== dependencies: - "@babel/parser" "^7.28.5" - "@babel/types" "^7.28.5" + "@babel/parser" "^7.29.0" + "@babel/types" "^7.29.0" "@jridgewell/gen-mapping" "^0.3.12" "@jridgewell/trace-mapping" "^0.3.28" jsesc "^3.0.2" @@ -76,31 +76,31 @@ dependencies: "@babel/types" "^7.27.3" -"@babel/helper-compilation-targets@^7.27.1", "@babel/helper-compilation-targets@^7.27.2": - version "7.27.2" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz#46a0f6efab808d51d29ce96858dd10ce8732733d" - integrity sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ== +"@babel/helper-compilation-targets@^7.27.1", "@babel/helper-compilation-targets@^7.27.2", "@babel/helper-compilation-targets@^7.28.6": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz#32c4a3f41f12ed1532179b108a4d746e105c2b25" + integrity sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA== dependencies: - "@babel/compat-data" "^7.27.2" + "@babel/compat-data" "^7.28.6" "@babel/helper-validator-option" "^7.27.1" browserslist "^4.24.0" lru-cache "^5.1.1" semver "^6.3.1" -"@babel/helper-create-class-features-plugin@^7.18.6", "@babel/helper-create-class-features-plugin@^7.27.1", "@babel/helper-create-class-features-plugin@^7.28.3", "@babel/helper-create-class-features-plugin@^7.28.5": - version "7.28.5" - resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.28.5.tgz#472d0c28028850968979ad89f173594a6995da46" - integrity sha512-q3WC4JfdODypvxArsJQROfupPBq9+lMwjKq7C33GhbFYJsufD0yd/ziwD+hJucLeWsnFPWZjsU2DNFqBPE7jwQ== +"@babel/helper-create-class-features-plugin@^7.18.6", "@babel/helper-create-class-features-plugin@^7.27.1", "@babel/helper-create-class-features-plugin@^7.28.6": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.28.6.tgz#611ff5482da9ef0db6291bcd24303400bca170fb" + integrity sha512-dTOdvsjnG3xNT9Y0AUg1wAl38y+4Rl4sf9caSQZOXdNqVn+H+HbbJ4IyyHaIqNR6SW9oJpA/RuRjsjCw2IdIow== dependencies: "@babel/helper-annotate-as-pure" "^7.27.3" "@babel/helper-member-expression-to-functions" "^7.28.5" "@babel/helper-optimise-call-expression" "^7.27.1" - "@babel/helper-replace-supers" "^7.27.1" + "@babel/helper-replace-supers" "^7.28.6" "@babel/helper-skip-transparent-expression-wrappers" "^7.27.1" - "@babel/traverse" "^7.28.5" + "@babel/traverse" "^7.28.6" semver "^6.3.1" -"@babel/helper-create-regexp-features-plugin@^7.18.6", "@babel/helper-create-regexp-features-plugin@^7.27.1": +"@babel/helper-create-regexp-features-plugin@^7.18.6", "@babel/helper-create-regexp-features-plugin@^7.27.1", "@babel/helper-create-regexp-features-plugin@^7.28.5": version "7.28.5" resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.28.5.tgz#7c1ddd64b2065c7f78034b25b43346a7e19ed997" integrity sha512-N1EhvLtHzOvj7QQOUCCS3NrPJP8c5W6ZXCHDn7Yialuy1iu4r5EmIYkXlKNqT99Ciw+W0mDqWoR6HWMZlFP3hw== @@ -109,23 +109,23 @@ regexpu-core "^6.3.1" semver "^6.3.1" -"@babel/helper-define-polyfill-provider@^0.6.5": - version "0.6.5" - resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.5.tgz#742ccf1cb003c07b48859fc9fa2c1bbe40e5f753" - integrity sha512-uJnGFcPsWQK8fvjgGP5LZUZZsYGIoPeRjSF5PGwrelYgq7Q15/Ft9NGFp1zglwgIv//W0uG4BevRuSJRyylZPg== +"@babel/helper-define-polyfill-provider@^0.6.5", "@babel/helper-define-polyfill-provider@^0.6.6": + version "0.6.6" + resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.6.tgz#714dfe33d8bd710f556df59953720f6eeb6c1a14" + integrity sha512-mOAsxeeKkUKayvZR3HeTYD/fICpCPLJrU5ZjelT/PA6WHtNDBOE436YiaEUvHN454bRM3CebhDsIpieCc4texA== dependencies: - "@babel/helper-compilation-targets" "^7.27.2" - "@babel/helper-plugin-utils" "^7.27.1" - debug "^4.4.1" + "@babel/helper-compilation-targets" "^7.28.6" + "@babel/helper-plugin-utils" "^7.28.6" + debug "^4.4.3" lodash.debounce "^4.0.8" - resolve "^1.22.10" + resolve "^1.22.11" "@babel/helper-globals@^7.28.0": version "7.28.0" resolved "https://registry.yarnpkg.com/@babel/helper-globals/-/helper-globals-7.28.0.tgz#b9430df2aa4e17bc28665eadeae8aa1d985e6674" integrity sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw== -"@babel/helper-member-expression-to-functions@^7.27.1", "@babel/helper-member-expression-to-functions@^7.28.5": +"@babel/helper-member-expression-to-functions@^7.28.5": version "7.28.5" resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.28.5.tgz#f3e07a10be37ed7a63461c63e6929575945a6150" integrity sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg== @@ -133,22 +133,22 @@ "@babel/traverse" "^7.28.5" "@babel/types" "^7.28.5" -"@babel/helper-module-imports@^7.25.9", "@babel/helper-module-imports@^7.27.1": - version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz#7ef769a323e2655e126673bb6d2d6913bbead204" - integrity sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w== +"@babel/helper-module-imports@^7.25.9", "@babel/helper-module-imports@^7.28.6": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz#60632cbd6ffb70b22823187201116762a03e2d5c" + integrity sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw== dependencies: - "@babel/traverse" "^7.27.1" - "@babel/types" "^7.27.1" + "@babel/traverse" "^7.28.6" + "@babel/types" "^7.28.6" -"@babel/helper-module-transforms@^7.27.1", "@babel/helper-module-transforms@^7.28.3": - version "7.28.3" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz#a2b37d3da3b2344fe085dab234426f2b9a2fa5f6" - integrity sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw== +"@babel/helper-module-transforms@^7.27.1", "@babel/helper-module-transforms@^7.28.6": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz#9312d9d9e56edc35aeb6e95c25d4106b50b9eb1e" + integrity sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA== dependencies: - "@babel/helper-module-imports" "^7.27.1" - "@babel/helper-validator-identifier" "^7.27.1" - "@babel/traverse" "^7.28.3" + "@babel/helper-module-imports" "^7.28.6" + "@babel/helper-validator-identifier" "^7.28.5" + "@babel/traverse" "^7.28.6" "@babel/helper-optimise-call-expression@^7.27.1": version "7.27.1" @@ -157,10 +157,10 @@ dependencies: "@babel/types" "^7.27.1" -"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.27.1", "@babel/helper-plugin-utils@^7.8.0": - version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz#ddb2f876534ff8013e6c2b299bf4d39b3c51d44c" - integrity sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw== +"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.27.1", "@babel/helper-plugin-utils@^7.28.6", "@babel/helper-plugin-utils@^7.8.0": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz#6f13ea251b68c8532e985fd532f28741a8af9ac8" + integrity sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug== "@babel/helper-remap-async-to-generator@^7.27.1": version "7.27.1" @@ -171,14 +171,14 @@ "@babel/helper-wrap-function" "^7.27.1" "@babel/traverse" "^7.27.1" -"@babel/helper-replace-supers@^7.27.1": - version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.27.1.tgz#b1ed2d634ce3bdb730e4b52de30f8cccfd692bc0" - integrity sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA== +"@babel/helper-replace-supers@^7.27.1", "@babel/helper-replace-supers@^7.28.6": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.28.6.tgz#94aa9a1d7423a00aead3f204f78834ce7d53fe44" + integrity sha512-mq8e+laIk94/yFec3DxSjCRD2Z0TAjhVbEJY3UQrlwVo15Lmt7C2wAUbK4bjnTs4APkwsYLTahXRraQXhb1WCg== dependencies: - "@babel/helper-member-expression-to-functions" "^7.27.1" + "@babel/helper-member-expression-to-functions" "^7.28.5" "@babel/helper-optimise-call-expression" "^7.27.1" - "@babel/traverse" "^7.27.1" + "@babel/traverse" "^7.28.6" "@babel/helper-skip-transparent-expression-wrappers@^7.27.1": version "7.27.1" @@ -193,7 +193,7 @@ resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz#54da796097ab19ce67ed9f88b47bb2ec49367687" integrity sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA== -"@babel/helper-validator-identifier@^7.25.9", "@babel/helper-validator-identifier@^7.27.1", "@babel/helper-validator-identifier@^7.28.5": +"@babel/helper-validator-identifier@^7.25.9", "@babel/helper-validator-identifier@^7.28.5": version "7.28.5" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz#010b6938fab7cb7df74aa2bbc06aa503b8fe5fb4" integrity sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q== @@ -204,21 +204,21 @@ integrity sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg== "@babel/helper-wrap-function@^7.27.1": - version "7.28.3" - resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.28.3.tgz#fe4872092bc1438ffd0ce579e6f699609f9d0a7a" - integrity sha512-zdf983tNfLZFletc0RRXYrHrucBEg95NIFMkn6K9dbeMYnsgHaSBGcQqdsCSStG2PYwRre0Qc2NNSCXbG+xc6g== + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.28.6.tgz#4e349ff9222dab69a93a019cc296cdd8442e279a" + integrity sha512-z+PwLziMNBeSQJonizz2AGnndLsP2DeGHIxDAn+wdHOGuo4Fo1x1HBPPXeE9TAOPHNNWQKCSlA2VZyYyyibDnQ== dependencies: - "@babel/template" "^7.27.2" - "@babel/traverse" "^7.28.3" - "@babel/types" "^7.28.2" + "@babel/template" "^7.28.6" + "@babel/traverse" "^7.28.6" + "@babel/types" "^7.28.6" -"@babel/helpers@^7.28.4": - version "7.28.4" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.28.4.tgz#fe07274742e95bdf7cf1443593eeb8926ab63827" - integrity sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w== +"@babel/helpers@^7.28.6": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.28.6.tgz#fca903a313ae675617936e8998b814c415cbf5d7" + integrity sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw== dependencies: - "@babel/template" "^7.27.2" - "@babel/types" "^7.28.4" + "@babel/template" "^7.28.6" + "@babel/types" "^7.28.6" "@babel/highlight@^7.10.4": version "7.25.9" @@ -230,24 +230,24 @@ js-tokens "^4.0.0" picocolors "^1.0.0" -"@babel/node@7.28.0": - version "7.28.0" - resolved "https://registry.yarnpkg.com/@babel/node/-/node-7.28.0.tgz#fe52d05121ca064e6919215ffe54fea480a8746f" - integrity sha512-6u1Mmn3SIMUH8uwTq543L062X3JDgms9HPf06o/pIGdDjeD/zNQ+dfZPQD27sCyvtP0ZOlJtwnl2RIdPe9bHeQ== +"@babel/node@7.29.0": + version "7.29.0" + resolved "https://registry.yarnpkg.com/@babel/node/-/node-7.29.0.tgz#278d903d17bf80d361ed6ac9552125eebd5eab5f" + integrity sha512-9UeU8F3rx2lOZXneEW2HTnTYdA8+fXP0kr54tk7d0fPomWNlZ6WJ2H9lunr5dSvr8FNY0CDnop3Km6jZ5NAUsQ== dependencies: - "@babel/register" "^7.27.1" + "@babel/register" "^7.28.6" commander "^6.2.0" - core-js "^3.30.2" + core-js "^3.48.0" node-environment-flags "^1.0.5" regenerator-runtime "^0.14.0" v8flags "^3.1.1" -"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.7", "@babel/parser@^7.24.4", "@babel/parser@^7.25.3", "@babel/parser@^7.27.2", "@babel/parser@^7.28.5": - version "7.28.5" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.28.5.tgz#0b0225ee90362f030efd644e8034c99468893b08" - integrity sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ== +"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.7", "@babel/parser@^7.24.4", "@babel/parser@^7.25.3", "@babel/parser@^7.28.6", "@babel/parser@^7.29.0": + version "7.29.0" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.29.0.tgz#669ef345add7d057e92b7ed15f0bac07611831b6" + integrity sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww== dependencies: - "@babel/types" "^7.28.5" + "@babel/types" "^7.29.0" "@babel/plugin-bugfix-firefox-class-in-computed-class-key@^7.28.5": version "7.28.5" @@ -280,22 +280,22 @@ "@babel/helper-skip-transparent-expression-wrappers" "^7.27.1" "@babel/plugin-transform-optional-chaining" "^7.27.1" -"@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@^7.28.3": - version "7.28.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.28.3.tgz#373f6e2de0016f73caf8f27004f61d167743742a" - integrity sha512-b6YTX108evsvE4YgWyQ921ZAFFQm3Bn+CA3+ZXlNVnPhx+UfsVURoPjfGAPCjBgrqo30yX/C2nZGX96DxvR9Iw== +"@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@^7.28.6": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.28.6.tgz#0e8289cec28baaf05d54fd08d81ae3676065f69f" + integrity sha512-a0aBScVTlNaiUe35UtfxAN7A/tehvvG4/ByO6+46VPKTRSlfnAFsgKy0FUh+qAkQrDTmhDkT+IBOKlOoMUxQ0g== dependencies: - "@babel/helper-plugin-utils" "^7.27.1" - "@babel/traverse" "^7.28.3" + "@babel/helper-plugin-utils" "^7.28.6" + "@babel/traverse" "^7.28.6" "@babel/plugin-proposal-decorators@^7.12.9": - version "7.28.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.28.0.tgz#419c8acc31088e05a774344c021800f7ddc39bf0" - integrity sha512-zOiZqvANjWDUaUS9xMxbMcK/Zccztbe/6ikvUXaG9nsPH3w6qh5UaPGAnirI/WhIbZ8m3OHU0ReyPrknG+ZKeg== + version "7.29.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.29.0.tgz#d159f26f78740e47bf3ef075882b155b2d54ca81" + integrity sha512-CVBVv3VY/XRMxRYq5dwr2DS7/MvqPm23cOCjbwNnVrfOqcWlnefua1uUs0sjdKOGjvPUG633o07uWzJq4oI6dA== dependencies: - "@babel/helper-create-class-features-plugin" "^7.27.1" - "@babel/helper-plugin-utils" "^7.27.1" - "@babel/plugin-syntax-decorators" "^7.27.1" + "@babel/helper-create-class-features-plugin" "^7.28.6" + "@babel/helper-plugin-utils" "^7.28.6" + "@babel/plugin-syntax-decorators" "^7.28.6" "@babel/plugin-proposal-export-default-from@^7.24.7": version "7.27.1" @@ -345,12 +345,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-syntax-decorators@^7.27.1": - version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.27.1.tgz#ee7dd9590aeebc05f9d4c8c0560007b05979a63d" - integrity sha512-YMq8Z87Lhl8EGkmb0MwYkt36QnxC+fzCgrl66ereamPlYToRpIk5nUjKUY3QKLWq8mwUB1BgbeXcTJhZOCDg5A== +"@babel/plugin-syntax-decorators@^7.28.6": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.28.6.tgz#8c3293a0fef033e4c786b35ce1e159fc1d676153" + integrity sha512-71EYI0ONURHJBL4rSFXnITXqXrrY8q4P0q006DPfN+Rk+ASM+++IBXem/ruokgBZR8YNEWZ8R6B+rCb8VcUTqA== dependencies: - "@babel/helper-plugin-utils" "^7.27.1" + "@babel/helper-plugin-utils" "^7.28.6" "@babel/plugin-syntax-dynamic-import@^7.8.3": version "7.8.3" @@ -360,32 +360,32 @@ "@babel/helper-plugin-utils" "^7.8.0" "@babel/plugin-syntax-export-default-from@^7.24.7": - version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-export-default-from/-/plugin-syntax-export-default-from-7.27.1.tgz#8efed172e79ab657c7fa4d599224798212fb7e18" - integrity sha512-eBC/3KSekshx19+N40MzjWqJd7KTEdOoLesAfa4IDFI8eRz5a47i5Oszus6zG/cwIXN63YhgLOMSSNJx49sENg== + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-export-default-from/-/plugin-syntax-export-default-from-7.28.6.tgz#8e19047560a8a48b11f1f5b46881f445f8692830" + integrity sha512-Svlx1fjJFnNz0LZeUaybRukSxZI3KkpApUmIRzEdXC5k8ErTOz0OD0kNrICi5Vc3GlpP5ZCeRyRO+mfWTSz+iQ== dependencies: - "@babel/helper-plugin-utils" "^7.27.1" + "@babel/helper-plugin-utils" "^7.28.6" "@babel/plugin-syntax-flow@^7.12.1", "@babel/plugin-syntax-flow@^7.27.1": - version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.27.1.tgz#6c83cf0d7d635b716827284b7ecd5aead9237662" - integrity sha512-p9OkPbZ5G7UT1MofwYFigGebnrzGJacoBSQM0/6bi/PUMVE+qlWDD/OalvQKbwgQzU6dl0xAv6r4X7Jme0RYxA== + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.28.6.tgz#447559a225e66c4cd477a3ffb1a74d8c1fe25a62" + integrity sha512-D+OrJumc9McXNEBI/JmFnc/0uCM2/Y3PEBG3gfV3QIYkKv5pvnpzFrl1kYCrcHJP8nOeFB/SHi1IHz29pNGuew== dependencies: - "@babel/helper-plugin-utils" "^7.27.1" + "@babel/helper-plugin-utils" "^7.28.6" -"@babel/plugin-syntax-import-assertions@^7.27.1": - version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.27.1.tgz#88894aefd2b03b5ee6ad1562a7c8e1587496aecd" - integrity sha512-UT/Jrhw57xg4ILHLFnzFpPDlMbcdEicaAtjPQpbj9wa8T4r5KVWCimHcL/460g8Ht0DMxDyjsLgiWSkVjnwPFg== +"@babel/plugin-syntax-import-assertions@^7.28.6": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.28.6.tgz#ae9bc1923a6ba527b70104dd2191b0cd872c8507" + integrity sha512-pSJUpFHdx9z5nqTSirOCMtYVP2wFgoWhP0p3g8ONK/4IHhLIBd0B9NYqAvIUAhq+OkhO4VM1tENCt0cjlsNShw== dependencies: - "@babel/helper-plugin-utils" "^7.27.1" + "@babel/helper-plugin-utils" "^7.28.6" -"@babel/plugin-syntax-import-attributes@^7.24.7", "@babel/plugin-syntax-import-attributes@^7.27.1": - version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.27.1.tgz#34c017d54496f9b11b61474e7ea3dfd5563ffe07" - integrity sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww== +"@babel/plugin-syntax-import-attributes@^7.24.7", "@babel/plugin-syntax-import-attributes@^7.28.6": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.28.6.tgz#b71d5914665f60124e133696f17cd7669062c503" + integrity sha512-jiLC0ma9XkQT3TKJ9uYvlakm66Pamywo+qwL+oL8HJOvc6TWdZXVfhqJr8CCzbSGUAbDOzlGHJC1U+vRfLQDvw== dependencies: - "@babel/helper-plugin-utils" "^7.27.1" + "@babel/helper-plugin-utils" "^7.28.6" "@babel/plugin-syntax-import-meta@^7.10.4": version "7.10.4" @@ -401,12 +401,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-jsx@^7.27.1": - version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.27.1.tgz#2f9beb5eff30fa507c5532d107daac7b888fa34c" - integrity sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w== +"@babel/plugin-syntax-jsx@^7.27.1", "@babel/plugin-syntax-jsx@^7.28.6": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.28.6.tgz#f8ca28bbd84883b5fea0e447c635b81ba73997ee" + integrity sha512-wgEmr06G6sIpqr8YDwA2dSRTE3bJ+V0IfpzfSY3Lfgd7YWOaAdlykvJi13ZKBt8cZHfgH1IXN+CL656W3uUa4w== dependencies: - "@babel/helper-plugin-utils" "^7.27.1" + "@babel/helper-plugin-utils" "^7.28.6" "@babel/plugin-syntax-logical-assignment-operators@^7.10.4": version "7.10.4" @@ -464,12 +464,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-syntax-typescript@^7.27.1": - version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.27.1.tgz#5147d29066a793450f220c63fa3a9431b7e6dd18" - integrity sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ== +"@babel/plugin-syntax-typescript@^7.28.6": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.28.6.tgz#c7b2ddf1d0a811145b1de800d1abd146af92e3a2" + integrity sha512-+nDNmQye7nlnuuHDboPbGm00Vqg3oO8niRRL27/4LYHUsHYh0zJ1xWOz0uRwNFmM1Avzk8wZbc6rdiYhomzv/A== dependencies: - "@babel/helper-plugin-utils" "^7.27.1" + "@babel/helper-plugin-utils" "^7.28.6" "@babel/plugin-syntax-unicode-sets-regex@^7.18.6": version "7.18.6" @@ -486,22 +486,22 @@ dependencies: "@babel/helper-plugin-utils" "^7.27.1" -"@babel/plugin-transform-async-generator-functions@^7.25.4", "@babel/plugin-transform-async-generator-functions@^7.28.0": - version "7.28.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.28.0.tgz#1276e6c7285ab2cd1eccb0bc7356b7a69ff842c2" - integrity sha512-BEOdvX4+M765icNPZeidyADIvQ1m1gmunXufXxvRESy/jNNyfovIqUyE7MVgGBjWktCoJlzvFA1To2O4ymIO3Q== +"@babel/plugin-transform-async-generator-functions@^7.25.4", "@babel/plugin-transform-async-generator-functions@^7.29.0": + version "7.29.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.29.0.tgz#63ed829820298f0bf143d5a4a68fb8c06ffd742f" + integrity sha512-va0VdWro4zlBr2JsXC+ofCPB2iG12wPtVGTWFx2WLDOM3nYQZZIGP82qku2eW/JR83sD+k2k+CsNtyEbUqhU6w== dependencies: - "@babel/helper-plugin-utils" "^7.27.1" + "@babel/helper-plugin-utils" "^7.28.6" "@babel/helper-remap-async-to-generator" "^7.27.1" - "@babel/traverse" "^7.28.0" + "@babel/traverse" "^7.29.0" -"@babel/plugin-transform-async-to-generator@^7.24.7", "@babel/plugin-transform-async-to-generator@^7.27.1": - version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.27.1.tgz#9a93893b9379b39466c74474f55af03de78c66e7" - integrity sha512-NREkZsZVJS4xmTr8qzE5y8AfIPqsdQfRuUiLRTEzb7Qii8iFWCyDKaUV2c0rCuh4ljDZ98ALHP/PetiBV2nddA== +"@babel/plugin-transform-async-to-generator@^7.24.7", "@babel/plugin-transform-async-to-generator@^7.28.6": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.28.6.tgz#bd97b42237b2d1bc90d74bcb486c39be5b4d7e77" + integrity sha512-ilTRcmbuXjsMmcZ3HASTe4caH5Tpo93PkTxF9oG2VZsSWsahydmcEHhix9Ik122RcTnZnUzPbmux4wh1swfv7g== dependencies: - "@babel/helper-module-imports" "^7.27.1" - "@babel/helper-plugin-utils" "^7.27.1" + "@babel/helper-module-imports" "^7.28.6" + "@babel/helper-plugin-utils" "^7.28.6" "@babel/helper-remap-async-to-generator" "^7.27.1" "@babel/plugin-transform-block-scoped-functions@^7.27.1": @@ -511,14 +511,14 @@ dependencies: "@babel/helper-plugin-utils" "^7.27.1" -"@babel/plugin-transform-block-scoping@^7.25.0", "@babel/plugin-transform-block-scoping@^7.28.5": - version "7.28.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.28.5.tgz#e0d3af63bd8c80de2e567e690a54e84d85eb16f6" - integrity sha512-45DmULpySVvmq9Pj3X9B+62Xe+DJGov27QravQJU1LLcapR6/10i+gYVAucGGJpHBp5mYxIMK4nDAT/QDLr47g== +"@babel/plugin-transform-block-scoping@^7.25.0", "@babel/plugin-transform-block-scoping@^7.28.6": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.28.6.tgz#e1ef5633448c24e76346125c2534eeb359699a99" + integrity sha512-tt/7wOtBmwHPNMPu7ax4pdPz6shjFrmHDghvNC+FG9Qvj7D6mJcoRQIF5dy4njmxR941l6rgtvfSB2zX3VlUIw== dependencies: - "@babel/helper-plugin-utils" "^7.27.1" + "@babel/helper-plugin-utils" "^7.28.6" -"@babel/plugin-transform-class-properties@7.27.1", "@babel/plugin-transform-class-properties@^7.25.4", "@babel/plugin-transform-class-properties@^7.27.1": +"@babel/plugin-transform-class-properties@7.27.1": version "7.27.1" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.27.1.tgz#dd40a6a370dfd49d32362ae206ddaf2bb082a925" integrity sha512-D0VcalChDMtuRvJIu3U/fwWjf8ZMykz5iZsg77Nuj821vCKI3zCyRLwRdWbsuJ/uRwZhZ002QtCqIkwC/ZkvbA== @@ -526,15 +526,23 @@ "@babel/helper-create-class-features-plugin" "^7.27.1" "@babel/helper-plugin-utils" "^7.27.1" -"@babel/plugin-transform-class-static-block@^7.27.1", "@babel/plugin-transform-class-static-block@^7.28.3": - version "7.28.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.28.3.tgz#d1b8e69b54c9993bc558203e1f49bfc979bfd852" - integrity sha512-LtPXlBbRoc4Njl/oh1CeD/3jC+atytbnf/UqLoqTDcEYGUPj022+rvfkbDYieUrSj3CaV4yHDByPE+T2HwfsJg== +"@babel/plugin-transform-class-properties@^7.25.4", "@babel/plugin-transform-class-properties@^7.28.6": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.28.6.tgz#d274a4478b6e782d9ea987fda09bdb6d28d66b72" + integrity sha512-dY2wS3I2G7D697VHndN91TJr8/AAfXQNt5ynCTI/MpxMsSzHp+52uNivYT5wCPax3whc47DR8Ba7cmlQMg24bw== dependencies: - "@babel/helper-create-class-features-plugin" "^7.28.3" - "@babel/helper-plugin-utils" "^7.27.1" + "@babel/helper-create-class-features-plugin" "^7.28.6" + "@babel/helper-plugin-utils" "^7.28.6" -"@babel/plugin-transform-classes@7.28.4", "@babel/plugin-transform-classes@^7.25.4", "@babel/plugin-transform-classes@^7.28.4": +"@babel/plugin-transform-class-static-block@^7.27.1", "@babel/plugin-transform-class-static-block@^7.28.6": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.28.6.tgz#1257491e8259c6d125ac4d9a6f39f9d2bf3dba70" + integrity sha512-rfQ++ghVwTWTqQ7w8qyDxL1XGihjBss4CmTgGRCTAC9RIbhVpyp4fOeZtta0Lbf+dTNIVJer6ych2ibHwkZqsQ== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.28.6" + "@babel/helper-plugin-utils" "^7.28.6" + +"@babel/plugin-transform-classes@7.28.4": version "7.28.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.28.4.tgz#75d66175486788c56728a73424d67cbc7473495c" integrity sha512-cFOlhIYPBv/iBoc+KS3M6et2XPtbT2HiCRfBXWtfpc9OAyostldxIf9YAYB6ypURBBbx+Qv6nyrLzASfJe+hBA== @@ -546,15 +554,27 @@ "@babel/helper-replace-supers" "^7.27.1" "@babel/traverse" "^7.28.4" -"@babel/plugin-transform-computed-properties@^7.24.7", "@babel/plugin-transform-computed-properties@^7.27.1": - version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.27.1.tgz#81662e78bf5e734a97982c2b7f0a793288ef3caa" - integrity sha512-lj9PGWvMTVksbWiDT2tW68zGS/cyo4AkZ/QTp0sQT0mjPopCmrSkzxeXkznjqBxzDI6TclZhOJbBmbBLjuOZUw== +"@babel/plugin-transform-classes@^7.25.4", "@babel/plugin-transform-classes@^7.28.6": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.28.6.tgz#8f6fb79ba3703978e701ce2a97e373aae7dda4b7" + integrity sha512-EF5KONAqC5zAqT783iMGuM2ZtmEBy+mJMOKl2BCvPZ2lVrwvXnB6o+OBWCS+CoeCCpVRF2sA2RBKUxvT8tQT5Q== dependencies: - "@babel/helper-plugin-utils" "^7.27.1" - "@babel/template" "^7.27.1" + "@babel/helper-annotate-as-pure" "^7.27.3" + "@babel/helper-compilation-targets" "^7.28.6" + "@babel/helper-globals" "^7.28.0" + "@babel/helper-plugin-utils" "^7.28.6" + "@babel/helper-replace-supers" "^7.28.6" + "@babel/traverse" "^7.28.6" -"@babel/plugin-transform-destructuring@^7.24.8", "@babel/plugin-transform-destructuring@^7.28.0", "@babel/plugin-transform-destructuring@^7.28.5": +"@babel/plugin-transform-computed-properties@^7.24.7", "@babel/plugin-transform-computed-properties@^7.28.6": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.28.6.tgz#936824fc71c26cb5c433485776d79c8e7b0202d2" + integrity sha512-bcc3k0ijhHbc2lEfpFHgx7eYw9KNXqOerKWfzbxEHUGKnS3sz9C4CNL9OiFN1297bDNfUiSO7DaLzbvHQQQ1BQ== + dependencies: + "@babel/helper-plugin-utils" "^7.28.6" + "@babel/template" "^7.28.6" + +"@babel/plugin-transform-destructuring@^7.24.8", "@babel/plugin-transform-destructuring@^7.28.5": version "7.28.5" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.28.5.tgz#b8402764df96179a2070bb7b501a1586cf8ad7a7" integrity sha512-Kl9Bc6D0zTUcFUvkNuQh4eGXPKKNDOJQXVyyM4ZAQPMveniJdxi8XMJwLo+xSoW3MIq81bD33lcUe9kZpl0MCw== @@ -562,13 +582,13 @@ "@babel/helper-plugin-utils" "^7.27.1" "@babel/traverse" "^7.28.5" -"@babel/plugin-transform-dotall-regex@^7.27.1": - version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.27.1.tgz#aa6821de864c528b1fecf286f0a174e38e826f4d" - integrity sha512-gEbkDVGRvjj7+T1ivxrfgygpT7GUd4vmODtYpbs0gZATdkX8/iSnOtZSxiZnsgm1YjTgjI6VKBGSJJevkrclzw== +"@babel/plugin-transform-dotall-regex@^7.28.6": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.28.6.tgz#def31ed84e0fb6e25c71e53c124e7b76a4ab8e61" + integrity sha512-SljjowuNKB7q5Oayv4FoPzeB74g3QgLt8IVJw9ADvWy3QnUb/01aw8I4AVv8wYnPvQz2GDDZ/g3GhcNyDBI4Bg== dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.27.1" - "@babel/helper-plugin-utils" "^7.27.1" + "@babel/helper-create-regexp-features-plugin" "^7.28.5" + "@babel/helper-plugin-utils" "^7.28.6" "@babel/plugin-transform-duplicate-keys@^7.27.1": version "7.27.1" @@ -577,13 +597,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.27.1" -"@babel/plugin-transform-duplicate-named-capturing-groups-regex@^7.27.1": - version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.27.1.tgz#5043854ca620a94149372e69030ff8cb6a9eb0ec" - integrity sha512-hkGcueTEzuhB30B3eJCbCYeCaaEQOmQR0AdvzpD4LoN0GXMWzzGSuRrxR2xTnCrvNbVwK9N6/jQ92GSLfiZWoQ== +"@babel/plugin-transform-duplicate-named-capturing-groups-regex@^7.29.0": + version "7.29.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.29.0.tgz#8014b8a6cfd0e7b92762724443bf0d2400f26df1" + integrity sha512-zBPcW2lFGxdiD8PUnPwJjag2J9otbcLQzvbiOzDxpYXyCuYX9agOwMPGn1prVH0a4qzhCKu24rlH4c1f7yA8rw== dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.27.1" - "@babel/helper-plugin-utils" "^7.27.1" + "@babel/helper-create-regexp-features-plugin" "^7.28.5" + "@babel/helper-plugin-utils" "^7.28.6" "@babel/plugin-transform-dynamic-import@^7.27.1": version "7.27.1" @@ -592,20 +612,20 @@ dependencies: "@babel/helper-plugin-utils" "^7.27.1" -"@babel/plugin-transform-explicit-resource-management@^7.28.0": - version "7.28.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-explicit-resource-management/-/plugin-transform-explicit-resource-management-7.28.0.tgz#45be6211b778dbf4b9d54c4e8a2b42fa72e09a1a" - integrity sha512-K8nhUcn3f6iB+P3gwCv/no7OdzOZQcKchW6N389V6PD8NUWKZHzndOd9sPDVbMoBsbmjMqlB4L9fm+fEFNVlwQ== +"@babel/plugin-transform-explicit-resource-management@^7.28.6": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-explicit-resource-management/-/plugin-transform-explicit-resource-management-7.28.6.tgz#dd6788f982c8b77e86779d1d029591e39d9d8be7" + integrity sha512-Iao5Konzx2b6g7EPqTy40UZbcdXE126tTxVFr/nAIj+WItNxjKSYTEw3RC+A2/ZetmdJsgueL1KhaMCQHkLPIg== dependencies: - "@babel/helper-plugin-utils" "^7.27.1" - "@babel/plugin-transform-destructuring" "^7.28.0" + "@babel/helper-plugin-utils" "^7.28.6" + "@babel/plugin-transform-destructuring" "^7.28.5" -"@babel/plugin-transform-exponentiation-operator@^7.28.5": - version "7.28.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.28.5.tgz#7cc90a8170e83532676cfa505278e147056e94fe" - integrity sha512-D4WIMaFtwa2NizOp+dnoFjRez/ClKiC2BqqImwKd1X28nqBtZEyCYJ2ozQrrzlxAFrcrjxo39S6khe9RNDlGzw== +"@babel/plugin-transform-exponentiation-operator@^7.28.6": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.28.6.tgz#5e477eb7eafaf2ab5537a04aaafcf37e2d7f1091" + integrity sha512-WitabqiGjV/vJ0aPOLSFfNY1u9U3R7W36B03r5I2KoNix+a3sOhJ3pKFB3R5It9/UiK78NiO0KE9P21cMhlPkw== dependencies: - "@babel/helper-plugin-utils" "^7.27.1" + "@babel/helper-plugin-utils" "^7.28.6" "@babel/plugin-transform-export-namespace-from@^7.25.9", "@babel/plugin-transform-export-namespace-from@^7.27.1": version "7.27.1" @@ -639,12 +659,12 @@ "@babel/helper-plugin-utils" "^7.27.1" "@babel/traverse" "^7.27.1" -"@babel/plugin-transform-json-strings@^7.27.1": - version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.27.1.tgz#a2e0ce6ef256376bd527f290da023983527a4f4c" - integrity sha512-6WVLVJiTjqcQauBhn1LkICsR2H+zm62I3h9faTDKt1qP4jn2o72tSvqMwtGFKGTpojce0gJs+76eZ2uCHRZh0Q== +"@babel/plugin-transform-json-strings@^7.28.6": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.28.6.tgz#4c8c15b2dc49e285d110a4cf3dac52fd2dfc3038" + integrity sha512-Nr+hEN+0geQkzhbdgQVPoqr47lZbm+5fCUmO70722xJZd0Mvb59+33QLImGj6F+DkK3xgDi1YVysP8whD6FQAw== dependencies: - "@babel/helper-plugin-utils" "^7.27.1" + "@babel/helper-plugin-utils" "^7.28.6" "@babel/plugin-transform-literals@^7.25.2", "@babel/plugin-transform-literals@^7.27.1": version "7.27.1" @@ -653,12 +673,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.27.1" -"@babel/plugin-transform-logical-assignment-operators@^7.24.7", "@babel/plugin-transform-logical-assignment-operators@^7.28.5": - version "7.28.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.28.5.tgz#d028fd6db8c081dee4abebc812c2325e24a85b0e" - integrity sha512-axUuqnUTBuXyHGcJEVVh9pORaN6wC5bYfE7FGzPiaWa3syib9m7g+/IT/4VgCOe2Upef43PHzeAvcrVek6QuuA== +"@babel/plugin-transform-logical-assignment-operators@^7.24.7", "@babel/plugin-transform-logical-assignment-operators@^7.28.6": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.28.6.tgz#53028a3d77e33c50ef30a8fce5ca17065936e605" + integrity sha512-+anKKair6gpi8VsM/95kmomGNMD0eLz1NQ8+Pfw5sAwWH9fGYXT50E55ZpV0pHUHWf6IUTWPM+f/7AAff+wr9A== dependencies: - "@babel/helper-plugin-utils" "^7.27.1" + "@babel/helper-plugin-utils" "^7.28.6" "@babel/plugin-transform-member-expression-literals@^7.27.1": version "7.27.1" @@ -675,23 +695,23 @@ "@babel/helper-module-transforms" "^7.27.1" "@babel/helper-plugin-utils" "^7.27.1" -"@babel/plugin-transform-modules-commonjs@^7.24.8", "@babel/plugin-transform-modules-commonjs@^7.27.1": - version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.27.1.tgz#8e44ed37c2787ecc23bdc367f49977476614e832" - integrity sha512-OJguuwlTYlN0gBZFRPqwOGNWssZjfIUdS7HMYtN8c1KmwpwHFBwTeFZrg9XZa+DFTitWOW5iTAG7tyCUPsCCyw== +"@babel/plugin-transform-modules-commonjs@^7.24.8", "@babel/plugin-transform-modules-commonjs@^7.27.1", "@babel/plugin-transform-modules-commonjs@^7.28.6": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.28.6.tgz#c0232e0dfe66a734cc4ad0d5e75fc3321b6fdef1" + integrity sha512-jppVbf8IV9iWWwWTQIxJMAJCWBuuKx71475wHwYytrRGQ2CWiDvYlADQno3tcYpS/T2UUWFQp3nVtYfK/YBQrA== dependencies: - "@babel/helper-module-transforms" "^7.27.1" - "@babel/helper-plugin-utils" "^7.27.1" + "@babel/helper-module-transforms" "^7.28.6" + "@babel/helper-plugin-utils" "^7.28.6" -"@babel/plugin-transform-modules-systemjs@^7.28.5": - version "7.28.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.28.5.tgz#7439e592a92d7670dfcb95d0cbc04bd3e64801d2" - integrity sha512-vn5Jma98LCOeBy/KpeQhXcV2WZgaRUtjwQmjoBuLNlOmkg0fB5pdvYVeWRYI69wWKwK2cD1QbMiUQnoujWvrew== +"@babel/plugin-transform-modules-systemjs@^7.29.0": + version "7.29.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.29.0.tgz#e458a95a17807c415924106a3ff188a3b8dee964" + integrity sha512-PrujnVFbOdUpw4UHiVwKvKRLMMic8+eC0CuNlxjsyZUiBjhFdPsewdXCkveh2KqBA9/waD0W1b4hXSOBQJezpQ== dependencies: - "@babel/helper-module-transforms" "^7.28.3" - "@babel/helper-plugin-utils" "^7.27.1" + "@babel/helper-module-transforms" "^7.28.6" + "@babel/helper-plugin-utils" "^7.28.6" "@babel/helper-validator-identifier" "^7.28.5" - "@babel/traverse" "^7.28.5" + "@babel/traverse" "^7.29.0" "@babel/plugin-transform-modules-umd@^7.27.1": version "7.27.1" @@ -701,13 +721,13 @@ "@babel/helper-module-transforms" "^7.27.1" "@babel/helper-plugin-utils" "^7.27.1" -"@babel/plugin-transform-named-capturing-groups-regex@^7.24.7", "@babel/plugin-transform-named-capturing-groups-regex@^7.27.1": - version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.27.1.tgz#f32b8f7818d8fc0cc46ee20a8ef75f071af976e1" - integrity sha512-SstR5JYy8ddZvD6MhV0tM/j16Qds4mIpJTOd1Yu9J9pJjH93bxHECF7pgtc28XvkzTD6Pxcm/0Z73Hvk7kb3Ng== +"@babel/plugin-transform-named-capturing-groups-regex@^7.24.7", "@babel/plugin-transform-named-capturing-groups-regex@^7.29.0": + version "7.29.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.29.0.tgz#a26cd51e09c4718588fc4cce1c5d1c0152102d6a" + integrity sha512-1CZQA5KNAD6ZYQLPw7oi5ewtDNxH/2vuCh+6SmvgDfhumForvs8a1o9n0UrEoBD8HU4djO2yWngTQlXl1NDVEQ== dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.27.1" - "@babel/helper-plugin-utils" "^7.27.1" + "@babel/helper-create-regexp-features-plugin" "^7.28.5" + "@babel/helper-plugin-utils" "^7.28.6" "@babel/plugin-transform-new-target@^7.27.1": version "7.27.1" @@ -716,30 +736,37 @@ dependencies: "@babel/helper-plugin-utils" "^7.27.1" -"@babel/plugin-transform-nullish-coalescing-operator@7.27.1", "@babel/plugin-transform-nullish-coalescing-operator@^7.24.7", "@babel/plugin-transform-nullish-coalescing-operator@^7.27.1": +"@babel/plugin-transform-nullish-coalescing-operator@7.27.1": version "7.27.1" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.27.1.tgz#4f9d3153bf6782d73dd42785a9d22d03197bc91d" integrity sha512-aGZh6xMo6q9vq1JGcw58lZ1Z0+i0xB2x0XaauNIUXd6O1xXc3RwoWEBlsTQrY4KQ9Jf0s5rgD6SiNkaUdJegTA== dependencies: "@babel/helper-plugin-utils" "^7.27.1" -"@babel/plugin-transform-numeric-separator@^7.24.7", "@babel/plugin-transform-numeric-separator@^7.27.1": - version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.27.1.tgz#614e0b15cc800e5997dadd9bd6ea524ed6c819c6" - integrity sha512-fdPKAcujuvEChxDBJ5c+0BTaS6revLV7CJL08e4m3de8qJfNIuCc2nc7XJYOjBoTMJeqSmwXJ0ypE14RCjLwaw== +"@babel/plugin-transform-nullish-coalescing-operator@^7.24.7", "@babel/plugin-transform-nullish-coalescing-operator@^7.28.6": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.28.6.tgz#9bc62096e90ab7a887f3ca9c469f6adec5679757" + integrity sha512-3wKbRgmzYbw24mDJXT7N+ADXw8BC/imU9yo9c9X9NKaLF1fW+e5H1U5QjMUBe4Qo4Ox/o++IyUkl1sVCLgevKg== dependencies: - "@babel/helper-plugin-utils" "^7.27.1" + "@babel/helper-plugin-utils" "^7.28.6" -"@babel/plugin-transform-object-rest-spread@^7.24.7", "@babel/plugin-transform-object-rest-spread@^7.28.4": - version "7.28.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.28.4.tgz#9ee1ceca80b3e6c4bac9247b2149e36958f7f98d" - integrity sha512-373KA2HQzKhQCYiRVIRr+3MjpCObqzDlyrM6u4I201wL8Mp2wHf7uB8GhDwis03k2ti8Zr65Zyyqs1xOxUF/Ew== +"@babel/plugin-transform-numeric-separator@^7.24.7", "@babel/plugin-transform-numeric-separator@^7.28.6": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.28.6.tgz#1310b0292762e7a4a335df5f580c3320ee7d9e9f" + integrity sha512-SJR8hPynj8outz+SlStQSwvziMN4+Bq99it4tMIf5/Caq+3iOc0JtKyse8puvyXkk3eFRIA5ID/XfunGgO5i6w== dependencies: - "@babel/helper-compilation-targets" "^7.27.2" - "@babel/helper-plugin-utils" "^7.27.1" - "@babel/plugin-transform-destructuring" "^7.28.0" + "@babel/helper-plugin-utils" "^7.28.6" + +"@babel/plugin-transform-object-rest-spread@^7.24.7", "@babel/plugin-transform-object-rest-spread@^7.28.6": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.28.6.tgz#fdd4bc2d72480db6ca42aed5c051f148d7b067f7" + integrity sha512-5rh+JR4JBC4pGkXLAcYdLHZjXudVxWMXbB6u6+E9lRL5TrGVbHt1TjxGbZ8CkmYw9zjkB7jutzOROArsqtncEA== + dependencies: + "@babel/helper-compilation-targets" "^7.28.6" + "@babel/helper-plugin-utils" "^7.28.6" + "@babel/plugin-transform-destructuring" "^7.28.5" "@babel/plugin-transform-parameters" "^7.27.7" - "@babel/traverse" "^7.28.4" + "@babel/traverse" "^7.28.6" "@babel/plugin-transform-object-super@^7.27.1": version "7.27.1" @@ -749,12 +776,12 @@ "@babel/helper-plugin-utils" "^7.27.1" "@babel/helper-replace-supers" "^7.27.1" -"@babel/plugin-transform-optional-catch-binding@^7.24.7", "@babel/plugin-transform-optional-catch-binding@^7.27.1": - version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.27.1.tgz#84c7341ebde35ccd36b137e9e45866825072a30c" - integrity sha512-txEAEKzYrHEX4xSZN4kJ+OfKXFVSWKB2ZxM9dpcE3wT7smwkNmXo5ORRlVzMVdJbD+Q8ILTgSD7959uj+3Dm3Q== +"@babel/plugin-transform-optional-catch-binding@^7.24.7", "@babel/plugin-transform-optional-catch-binding@^7.28.6": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.28.6.tgz#75107be14c78385978201a49c86414a150a20b4c" + integrity sha512-R8ja/Pyrv0OGAvAXQhSTmWyPJPml+0TMqXlO5w+AsMEiwb2fg3WkOvob7UxFSL3OIttFSGSRFKQsOhJ/X6HQdQ== dependencies: - "@babel/helper-plugin-utils" "^7.27.1" + "@babel/helper-plugin-utils" "^7.28.6" "@babel/plugin-transform-optional-chaining@7.27.1": version "7.27.1" @@ -764,12 +791,12 @@ "@babel/helper-plugin-utils" "^7.27.1" "@babel/helper-skip-transparent-expression-wrappers" "^7.27.1" -"@babel/plugin-transform-optional-chaining@^7.24.8", "@babel/plugin-transform-optional-chaining@^7.27.1", "@babel/plugin-transform-optional-chaining@^7.28.5": - version "7.28.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.28.5.tgz#8238c785f9d5c1c515a90bf196efb50d075a4b26" - integrity sha512-N6fut9IZlPnjPwgiQkXNhb+cT8wQKFlJNqcZkWlcTqkcqx6/kU4ynGmLFoa4LViBSirn05YAwk+sQBbPfxtYzQ== +"@babel/plugin-transform-optional-chaining@^7.24.8", "@babel/plugin-transform-optional-chaining@^7.27.1", "@babel/plugin-transform-optional-chaining@^7.28.6": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.28.6.tgz#926cf150bd421fc8362753e911b4a1b1ce4356cd" + integrity sha512-A4zobikRGJTsX9uqVFdafzGkqD30t26ck2LmOzAuLL8b2x6k3TIqRiT2xVvA9fNmFeTX484VpsdgmKNA0bS23w== dependencies: - "@babel/helper-plugin-utils" "^7.27.1" + "@babel/helper-plugin-utils" "^7.28.6" "@babel/helper-skip-transparent-expression-wrappers" "^7.27.1" "@babel/plugin-transform-parameters@^7.24.7", "@babel/plugin-transform-parameters@^7.27.7": @@ -779,22 +806,22 @@ dependencies: "@babel/helper-plugin-utils" "^7.27.1" -"@babel/plugin-transform-private-methods@^7.24.7", "@babel/plugin-transform-private-methods@^7.27.1": - version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.27.1.tgz#fdacbab1c5ed81ec70dfdbb8b213d65da148b6af" - integrity sha512-10FVt+X55AjRAYI9BrdISN9/AQWHqldOeZDUoLyif1Kn05a56xVBXb8ZouL8pZ9jem8QpXaOt8TS7RHUIS+GPA== +"@babel/plugin-transform-private-methods@^7.24.7", "@babel/plugin-transform-private-methods@^7.28.6": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.28.6.tgz#c76fbfef3b86c775db7f7c106fff544610bdb411" + integrity sha512-piiuapX9CRv7+0st8lmuUlRSmX6mBcVeNQ1b4AYzJxfCMuBfB0vBXDiGSmm03pKJw1v6cZ8KSeM+oUnM6yAExg== dependencies: - "@babel/helper-create-class-features-plugin" "^7.27.1" - "@babel/helper-plugin-utils" "^7.27.1" + "@babel/helper-create-class-features-plugin" "^7.28.6" + "@babel/helper-plugin-utils" "^7.28.6" -"@babel/plugin-transform-private-property-in-object@^7.24.7", "@babel/plugin-transform-private-property-in-object@^7.27.1": - version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.27.1.tgz#4dbbef283b5b2f01a21e81e299f76e35f900fb11" - integrity sha512-5J+IhqTi1XPa0DXF83jYOaARrX+41gOewWbkPyjMNRDqgOCqdffGh8L3f/Ek5utaEBZExjSAzcyjmV9SSAWObQ== +"@babel/plugin-transform-private-property-in-object@^7.24.7", "@babel/plugin-transform-private-property-in-object@^7.28.6": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.28.6.tgz#4fafef1e13129d79f1d75ac180c52aafefdb2811" + integrity sha512-b97jvNSOb5+ehyQmBpmhOCiUC5oVK4PMnpRvO7+ymFBoqYjeDHIU9jnrNUuwHOiL9RpGDoKBpSViarV+BU+eVA== dependencies: - "@babel/helper-annotate-as-pure" "^7.27.1" - "@babel/helper-create-class-features-plugin" "^7.27.1" - "@babel/helper-plugin-utils" "^7.27.1" + "@babel/helper-annotate-as-pure" "^7.27.3" + "@babel/helper-create-class-features-plugin" "^7.28.6" + "@babel/helper-plugin-utils" "^7.28.6" "@babel/plugin-transform-property-literals@^7.27.1": version "7.27.1" @@ -832,15 +859,15 @@ "@babel/helper-plugin-utils" "^7.27.1" "@babel/plugin-transform-react-jsx@^7.25.2", "@babel/plugin-transform-react-jsx@^7.27.1": - version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.27.1.tgz#1023bc94b78b0a2d68c82b5e96aed573bcfb9db0" - integrity sha512-2KH4LWGSrJIkVf5tSiBFYuXDAoWRq2MMwgivCf+93dd0GQi8RXLjKA/0EvRnVV5G0hrHczsquXuD01L8s6dmBw== + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.28.6.tgz#f51cb70a90b9529fbb71ee1f75ea27b7078eed62" + integrity sha512-61bxqhiRfAACulXSLd/GxqmAedUSrRZIu/cbaT18T1CetkTmtDN15it7i80ru4DVqRK1WMxQhXs+Lf9kajm5Ow== dependencies: - "@babel/helper-annotate-as-pure" "^7.27.1" - "@babel/helper-module-imports" "^7.27.1" - "@babel/helper-plugin-utils" "^7.27.1" - "@babel/plugin-syntax-jsx" "^7.27.1" - "@babel/types" "^7.27.1" + "@babel/helper-annotate-as-pure" "^7.27.3" + "@babel/helper-module-imports" "^7.28.6" + "@babel/helper-plugin-utils" "^7.28.6" + "@babel/plugin-syntax-jsx" "^7.28.6" + "@babel/types" "^7.28.6" "@babel/plugin-transform-react-pure-annotations@^7.27.1": version "7.27.1" @@ -850,20 +877,20 @@ "@babel/helper-annotate-as-pure" "^7.27.1" "@babel/helper-plugin-utils" "^7.27.1" -"@babel/plugin-transform-regenerator@^7.24.7", "@babel/plugin-transform-regenerator@^7.28.4": - version "7.28.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.28.4.tgz#9d3fa3bebb48ddd0091ce5729139cd99c67cea51" - integrity sha512-+ZEdQlBoRg9m2NnzvEeLgtvBMO4tkFBw5SQIUgLICgTrumLoU7lr+Oghi6km2PFj+dbUt2u1oby2w3BDO9YQnA== +"@babel/plugin-transform-regenerator@^7.24.7", "@babel/plugin-transform-regenerator@^7.29.0": + version "7.29.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.29.0.tgz#dec237cec1b93330876d6da9992c4abd42c9d18b" + integrity sha512-FijqlqMA7DmRdg/aINBSs04y8XNTYw/lr1gJ2WsmBnnaNw1iS43EPkJW+zK7z65auG3AWRFXWj+NcTQwYptUog== dependencies: - "@babel/helper-plugin-utils" "^7.27.1" + "@babel/helper-plugin-utils" "^7.28.6" -"@babel/plugin-transform-regexp-modifiers@^7.27.1": - version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.27.1.tgz#df9ba5577c974e3f1449888b70b76169998a6d09" - integrity sha512-TtEciroaiODtXvLZv4rmfMhkCv8jx3wgKpL68PuiPh2M4fvz5jhsA7697N1gMvkvr/JTF13DrFYyEbY9U7cVPA== +"@babel/plugin-transform-regexp-modifiers@^7.28.6": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.28.6.tgz#7ef0163bd8b4a610481b2509c58cf217f065290b" + integrity sha512-QGWAepm9qxpaIs7UM9FvUSnCGlb8Ua1RhyM4/veAxLwt3gMat/LSGrZixyuj4I6+Kn9iwvqCyPTtbdxanYoWYg== dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.27.1" - "@babel/helper-plugin-utils" "^7.27.1" + "@babel/helper-create-regexp-features-plugin" "^7.28.5" + "@babel/helper-plugin-utils" "^7.28.6" "@babel/plugin-transform-reserved-words@^7.27.1": version "7.27.1" @@ -873,12 +900,12 @@ "@babel/helper-plugin-utils" "^7.27.1" "@babel/plugin-transform-runtime@^7.24.7": - version "7.28.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.28.5.tgz#ae3e21fbefe2831ebac04dfa6b463691696afe17" - integrity sha512-20NUVgOrinudkIBzQ2bNxP08YpKprUkRTiRSd2/Z5GOdPImJGkoN4Z7IQe1T5AdyKI1i5L6RBmluqdSzvaq9/w== + version "7.29.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.29.0.tgz#a5fded13cc656700804bfd6e5ebd7fffd5266803" + integrity sha512-jlaRT5dJtMaMCV6fAuLbsQMSwz/QkvaHOHOSXRitGGwSpR1blCY4KUKoyP2tYO8vJcqYe8cEj96cqSztv3uF9w== dependencies: - "@babel/helper-module-imports" "^7.27.1" - "@babel/helper-plugin-utils" "^7.27.1" + "@babel/helper-module-imports" "^7.28.6" + "@babel/helper-plugin-utils" "^7.28.6" babel-plugin-polyfill-corejs2 "^0.4.14" babel-plugin-polyfill-corejs3 "^0.13.0" babel-plugin-polyfill-regenerator "^0.6.5" @@ -891,12 +918,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.27.1" -"@babel/plugin-transform-spread@^7.24.7", "@babel/plugin-transform-spread@^7.27.1": - version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.27.1.tgz#1a264d5fc12750918f50e3fe3e24e437178abb08" - integrity sha512-kpb3HUqaILBJcRFVhFUs6Trdd4mkrzcGXss+6/mxUd273PfbWqSDHRzMT2234gIg2QYfAjvXLSquP1xECSg09Q== +"@babel/plugin-transform-spread@^7.24.7", "@babel/plugin-transform-spread@^7.28.6": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.28.6.tgz#40a2b423f6db7b70f043ad027a58bcb44a9757b6" + integrity sha512-9U4QObUC0FtJl05AsUcodau/RWDytrU6uKgkxu09mLR9HLDAtUMoPuuskm5huQsoktmsYpI+bGmq+iapDcriKA== dependencies: - "@babel/helper-plugin-utils" "^7.27.1" + "@babel/helper-plugin-utils" "^7.28.6" "@babel/helper-skip-transparent-expression-wrappers" "^7.27.1" "@babel/plugin-transform-sticky-regex@^7.24.7", "@babel/plugin-transform-sticky-regex@^7.27.1": @@ -921,15 +948,15 @@ "@babel/helper-plugin-utils" "^7.27.1" "@babel/plugin-transform-typescript@^7.25.2", "@babel/plugin-transform-typescript@^7.27.1", "@babel/plugin-transform-typescript@^7.28.5": - version "7.28.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.28.5.tgz#441c5f9a4a1315039516c6c612fc66d5f4594e72" - integrity sha512-x2Qa+v/CuEoX7Dr31iAfr0IhInrVOWZU/2vJMJ00FOR/2nM0BcBEclpaf9sWCDc+v5e9dMrhSH8/atq/kX7+bA== + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.28.6.tgz#1e93d96da8adbefdfdade1d4956f73afa201a158" + integrity sha512-0YWL2RFxOqEm9Efk5PvreamxPME8OyY0wM5wh5lHjF+VtVhdneCWGzZeSqzOfiobVqQaNCd2z0tQvnI9DaPWPw== dependencies: "@babel/helper-annotate-as-pure" "^7.27.3" - "@babel/helper-create-class-features-plugin" "^7.28.5" - "@babel/helper-plugin-utils" "^7.27.1" + "@babel/helper-create-class-features-plugin" "^7.28.6" + "@babel/helper-plugin-utils" "^7.28.6" "@babel/helper-skip-transparent-expression-wrappers" "^7.27.1" - "@babel/plugin-syntax-typescript" "^7.27.1" + "@babel/plugin-syntax-typescript" "^7.28.6" "@babel/plugin-transform-unicode-escapes@^7.27.1": version "7.27.1" @@ -938,13 +965,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.27.1" -"@babel/plugin-transform-unicode-property-regex@^7.27.1": - version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.27.1.tgz#bdfe2d3170c78c5691a3c3be934c8c0087525956" - integrity sha512-uW20S39PnaTImxp39O5qFlHLS9LJEmANjMG7SxIhap8rCHqu0Ik+tLEPX5DKmHn6CsWQ7j3lix2tFOa5YtL12Q== +"@babel/plugin-transform-unicode-property-regex@^7.28.6": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.28.6.tgz#63a7a6c21a0e75dae9b1861454111ea5caa22821" + integrity sha512-4Wlbdl/sIZjzi/8St0evF0gEZrgOswVO6aOzqxh1kDZOl9WmLrHq2HtGhnOJZmHZYKP8WZ1MDLCt5DAWwRo57A== dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.27.1" - "@babel/helper-plugin-utils" "^7.27.1" + "@babel/helper-create-regexp-features-plugin" "^7.28.5" + "@babel/helper-plugin-utils" "^7.28.6" "@babel/plugin-transform-unicode-regex@7.27.1", "@babel/plugin-transform-unicode-regex@^7.24.7", "@babel/plugin-transform-unicode-regex@^7.27.1": version "7.27.1" @@ -954,88 +981,88 @@ "@babel/helper-create-regexp-features-plugin" "^7.27.1" "@babel/helper-plugin-utils" "^7.27.1" -"@babel/plugin-transform-unicode-sets-regex@^7.27.1": - version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.27.1.tgz#6ab706d10f801b5c72da8bb2548561fa04193cd1" - integrity sha512-EtkOujbc4cgvb0mlpQefi4NTPBzhSIevblFevACNLUspmrALgmEBdL/XfnyyITfd8fKBZrZys92zOWcik7j9Tw== +"@babel/plugin-transform-unicode-sets-regex@^7.28.6": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.28.6.tgz#924912914e5df9fe615ec472f88ff4788ce04d4e" + integrity sha512-/wHc/paTUmsDYN7SZkpWxogTOBNnlx7nBQYfy6JJlCT7G3mVhltk3e++N7zV0XfgGsrqBxd4rJQt9H16I21Y1Q== dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.27.1" - "@babel/helper-plugin-utils" "^7.27.1" + "@babel/helper-create-regexp-features-plugin" "^7.28.5" + "@babel/helper-plugin-utils" "^7.28.6" -"@babel/preset-env@7.28.5": - version "7.28.5" - resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.28.5.tgz#82dd159d1563f219a1ce94324b3071eb89e280b0" - integrity sha512-S36mOoi1Sb6Fz98fBfE+UZSpYw5mJm0NUHtIKrOuNcqeFauy1J6dIvXm2KRVKobOSaGq4t/hBXdN4HGU3wL9Wg== +"@babel/preset-env@7.29.0": + version "7.29.0" + resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.29.0.tgz#c55db400c515a303662faaefd2d87e796efa08d0" + integrity sha512-fNEdfc0yi16lt6IZo2Qxk3knHVdfMYX33czNb4v8yWhemoBhibCpQK/uYHtSKIiO+p/zd3+8fYVXhQdOVV608w== dependencies: - "@babel/compat-data" "^7.28.5" - "@babel/helper-compilation-targets" "^7.27.2" - "@babel/helper-plugin-utils" "^7.27.1" + "@babel/compat-data" "^7.29.0" + "@babel/helper-compilation-targets" "^7.28.6" + "@babel/helper-plugin-utils" "^7.28.6" "@babel/helper-validator-option" "^7.27.1" "@babel/plugin-bugfix-firefox-class-in-computed-class-key" "^7.28.5" "@babel/plugin-bugfix-safari-class-field-initializer-scope" "^7.27.1" "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression" "^7.27.1" "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.27.1" - "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly" "^7.28.3" + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly" "^7.28.6" "@babel/plugin-proposal-private-property-in-object" "7.21.0-placeholder-for-preset-env.2" - "@babel/plugin-syntax-import-assertions" "^7.27.1" - "@babel/plugin-syntax-import-attributes" "^7.27.1" + "@babel/plugin-syntax-import-assertions" "^7.28.6" + "@babel/plugin-syntax-import-attributes" "^7.28.6" "@babel/plugin-syntax-unicode-sets-regex" "^7.18.6" "@babel/plugin-transform-arrow-functions" "^7.27.1" - "@babel/plugin-transform-async-generator-functions" "^7.28.0" - "@babel/plugin-transform-async-to-generator" "^7.27.1" + "@babel/plugin-transform-async-generator-functions" "^7.29.0" + "@babel/plugin-transform-async-to-generator" "^7.28.6" "@babel/plugin-transform-block-scoped-functions" "^7.27.1" - "@babel/plugin-transform-block-scoping" "^7.28.5" - "@babel/plugin-transform-class-properties" "^7.27.1" - "@babel/plugin-transform-class-static-block" "^7.28.3" - "@babel/plugin-transform-classes" "^7.28.4" - "@babel/plugin-transform-computed-properties" "^7.27.1" + "@babel/plugin-transform-block-scoping" "^7.28.6" + "@babel/plugin-transform-class-properties" "^7.28.6" + "@babel/plugin-transform-class-static-block" "^7.28.6" + "@babel/plugin-transform-classes" "^7.28.6" + "@babel/plugin-transform-computed-properties" "^7.28.6" "@babel/plugin-transform-destructuring" "^7.28.5" - "@babel/plugin-transform-dotall-regex" "^7.27.1" + "@babel/plugin-transform-dotall-regex" "^7.28.6" "@babel/plugin-transform-duplicate-keys" "^7.27.1" - "@babel/plugin-transform-duplicate-named-capturing-groups-regex" "^7.27.1" + "@babel/plugin-transform-duplicate-named-capturing-groups-regex" "^7.29.0" "@babel/plugin-transform-dynamic-import" "^7.27.1" - "@babel/plugin-transform-explicit-resource-management" "^7.28.0" - "@babel/plugin-transform-exponentiation-operator" "^7.28.5" + "@babel/plugin-transform-explicit-resource-management" "^7.28.6" + "@babel/plugin-transform-exponentiation-operator" "^7.28.6" "@babel/plugin-transform-export-namespace-from" "^7.27.1" "@babel/plugin-transform-for-of" "^7.27.1" "@babel/plugin-transform-function-name" "^7.27.1" - "@babel/plugin-transform-json-strings" "^7.27.1" + "@babel/plugin-transform-json-strings" "^7.28.6" "@babel/plugin-transform-literals" "^7.27.1" - "@babel/plugin-transform-logical-assignment-operators" "^7.28.5" + "@babel/plugin-transform-logical-assignment-operators" "^7.28.6" "@babel/plugin-transform-member-expression-literals" "^7.27.1" "@babel/plugin-transform-modules-amd" "^7.27.1" - "@babel/plugin-transform-modules-commonjs" "^7.27.1" - "@babel/plugin-transform-modules-systemjs" "^7.28.5" + "@babel/plugin-transform-modules-commonjs" "^7.28.6" + "@babel/plugin-transform-modules-systemjs" "^7.29.0" "@babel/plugin-transform-modules-umd" "^7.27.1" - "@babel/plugin-transform-named-capturing-groups-regex" "^7.27.1" + "@babel/plugin-transform-named-capturing-groups-regex" "^7.29.0" "@babel/plugin-transform-new-target" "^7.27.1" - "@babel/plugin-transform-nullish-coalescing-operator" "^7.27.1" - "@babel/plugin-transform-numeric-separator" "^7.27.1" - "@babel/plugin-transform-object-rest-spread" "^7.28.4" + "@babel/plugin-transform-nullish-coalescing-operator" "^7.28.6" + "@babel/plugin-transform-numeric-separator" "^7.28.6" + "@babel/plugin-transform-object-rest-spread" "^7.28.6" "@babel/plugin-transform-object-super" "^7.27.1" - "@babel/plugin-transform-optional-catch-binding" "^7.27.1" - "@babel/plugin-transform-optional-chaining" "^7.28.5" + "@babel/plugin-transform-optional-catch-binding" "^7.28.6" + "@babel/plugin-transform-optional-chaining" "^7.28.6" "@babel/plugin-transform-parameters" "^7.27.7" - "@babel/plugin-transform-private-methods" "^7.27.1" - "@babel/plugin-transform-private-property-in-object" "^7.27.1" + "@babel/plugin-transform-private-methods" "^7.28.6" + "@babel/plugin-transform-private-property-in-object" "^7.28.6" "@babel/plugin-transform-property-literals" "^7.27.1" - "@babel/plugin-transform-regenerator" "^7.28.4" - "@babel/plugin-transform-regexp-modifiers" "^7.27.1" + "@babel/plugin-transform-regenerator" "^7.29.0" + "@babel/plugin-transform-regexp-modifiers" "^7.28.6" "@babel/plugin-transform-reserved-words" "^7.27.1" "@babel/plugin-transform-shorthand-properties" "^7.27.1" - "@babel/plugin-transform-spread" "^7.27.1" + "@babel/plugin-transform-spread" "^7.28.6" "@babel/plugin-transform-sticky-regex" "^7.27.1" "@babel/plugin-transform-template-literals" "^7.27.1" "@babel/plugin-transform-typeof-symbol" "^7.27.1" "@babel/plugin-transform-unicode-escapes" "^7.27.1" - "@babel/plugin-transform-unicode-property-regex" "^7.27.1" + "@babel/plugin-transform-unicode-property-regex" "^7.28.6" "@babel/plugin-transform-unicode-regex" "^7.27.1" - "@babel/plugin-transform-unicode-sets-regex" "^7.27.1" + "@babel/plugin-transform-unicode-sets-regex" "^7.28.6" "@babel/preset-modules" "0.1.6-no-external-plugins" - babel-plugin-polyfill-corejs2 "^0.4.14" - babel-plugin-polyfill-corejs3 "^0.13.0" - babel-plugin-polyfill-regenerator "^0.6.5" - core-js-compat "^3.43.0" + babel-plugin-polyfill-corejs2 "^0.4.15" + babel-plugin-polyfill-corejs3 "^0.14.0" + babel-plugin-polyfill-regenerator "^0.6.6" + core-js-compat "^3.48.0" semver "^6.3.1" "@babel/preset-modules@0.1.6-no-external-plugins": @@ -1081,10 +1108,10 @@ "@babel/plugin-transform-modules-commonjs" "^7.27.1" "@babel/plugin-transform-typescript" "^7.28.5" -"@babel/register@^7.27.1": - version "7.28.3" - resolved "https://registry.yarnpkg.com/@babel/register/-/register-7.28.3.tgz#abd8a3753480c799bdaf9c9092d6745d16e052c2" - integrity sha512-CieDOtd8u208eI49bYl4z1J22ySFw87IGwE+IswFEExH7e3rLgKb0WNQeumnacQ1+VoDJLYI5QFA3AJZuyZQfA== +"@babel/register@^7.28.6": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/register/-/register-7.28.6.tgz#f54461dd32f6a418c1eb1f583c95ed0b7266ea4c" + integrity sha512-pgcbbEl/dWQYb6L6Yew6F94rdwygfuv+vJ/tXfwIOYAfPB6TNWpXUMEtEq3YuTeHRdvMIhvz13bkT9CNaS+wqA== dependencies: clone-deep "^4.0.1" find-cache-dir "^2.0.0" @@ -1093,49 +1120,49 @@ source-map-support "^0.5.16" "@babel/runtime@^7.12.5", "@babel/runtime@^7.18.6", "@babel/runtime@^7.20.0", "@babel/runtime@^7.25.0": - version "7.28.4" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.28.4.tgz#a70226016fabe25c5783b2f22d3e1c9bc5ca3326" - integrity sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ== + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.28.6.tgz#d267a43cb1836dc4d182cce93ae75ba954ef6d2b" + integrity sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA== -"@babel/template@^7.25.0", "@babel/template@^7.27.1", "@babel/template@^7.27.2", "@babel/template@^7.3.3": - version "7.27.2" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.27.2.tgz#fa78ceed3c4e7b63ebf6cb39e5852fca45f6809d" - integrity sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw== +"@babel/template@^7.25.0", "@babel/template@^7.28.6", "@babel/template@^7.3.3": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.28.6.tgz#0e7e56ecedb78aeef66ce7972b082fce76a23e57" + integrity sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ== dependencies: - "@babel/code-frame" "^7.27.1" - "@babel/parser" "^7.27.2" - "@babel/types" "^7.27.1" + "@babel/code-frame" "^7.28.6" + "@babel/parser" "^7.28.6" + "@babel/types" "^7.28.6" "@babel/traverse--for-generate-function-map@npm:@babel/traverse@^7.25.3": - version "7.28.5" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.28.5.tgz#450cab9135d21a7a2ca9d2d35aa05c20e68c360b" - integrity sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ== + version "7.29.0" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.29.0.tgz#f323d05001440253eead3c9c858adbe00b90310a" + integrity sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA== dependencies: - "@babel/code-frame" "^7.27.1" - "@babel/generator" "^7.28.5" + "@babel/code-frame" "^7.29.0" + "@babel/generator" "^7.29.0" "@babel/helper-globals" "^7.28.0" - "@babel/parser" "^7.28.5" - "@babel/template" "^7.27.2" - "@babel/types" "^7.28.5" + "@babel/parser" "^7.29.0" + "@babel/template" "^7.28.6" + "@babel/types" "^7.29.0" debug "^4.3.1" -"@babel/traverse@^7.25.3", "@babel/traverse@^7.27.1", "@babel/traverse@^7.28.0", "@babel/traverse@^7.28.3", "@babel/traverse@^7.28.4", "@babel/traverse@^7.28.5": - version "7.28.5" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.28.5.tgz#450cab9135d21a7a2ca9d2d35aa05c20e68c360b" - integrity sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ== +"@babel/traverse@^7.25.3", "@babel/traverse@^7.27.1", "@babel/traverse@^7.28.4", "@babel/traverse@^7.28.5", "@babel/traverse@^7.28.6", "@babel/traverse@^7.29.0": + version "7.29.0" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.29.0.tgz#f323d05001440253eead3c9c858adbe00b90310a" + integrity sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA== dependencies: - "@babel/code-frame" "^7.27.1" - "@babel/generator" "^7.28.5" + "@babel/code-frame" "^7.29.0" + "@babel/generator" "^7.29.0" "@babel/helper-globals" "^7.28.0" - "@babel/parser" "^7.28.5" - "@babel/template" "^7.27.2" - "@babel/types" "^7.28.5" + "@babel/parser" "^7.29.0" + "@babel/template" "^7.28.6" + "@babel/types" "^7.29.0" debug "^4.3.1" -"@babel/types@^7.0.0", "@babel/types@^7.20.7", "@babel/types@^7.25.2", "@babel/types@^7.26.0", "@babel/types@^7.27.1", "@babel/types@^7.27.3", "@babel/types@^7.28.2", "@babel/types@^7.28.4", "@babel/types@^7.28.5", "@babel/types@^7.3.3", "@babel/types@^7.4.4": - version "7.28.5" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.28.5.tgz#10fc405f60897c35f07e85493c932c7b5ca0592b" - integrity sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA== +"@babel/types@^7.0.0", "@babel/types@^7.20.7", "@babel/types@^7.25.2", "@babel/types@^7.26.0", "@babel/types@^7.27.1", "@babel/types@^7.27.3", "@babel/types@^7.28.2", "@babel/types@^7.28.5", "@babel/types@^7.28.6", "@babel/types@^7.29.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4": + version "7.29.0" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.29.0.tgz#9f5b1e838c446e72cf3cd4b918152b8c605e37c7" + integrity sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A== dependencies: "@babel/helper-string-parser" "^7.27.1" "@babel/helper-validator-identifier" "^7.28.5" @@ -1169,14 +1196,14 @@ dependencies: "@types/hammerjs" "^2.0.36" -"@electron/asar@^3.2.13", "@electron/asar@^3.3.1": - version "3.4.1" - resolved "https://registry.yarnpkg.com/@electron/asar/-/asar-3.4.1.tgz#4e9196a4b54fba18c56cd8d5cac67c5bdc588065" - integrity sha512-i4/rNPRS84t0vSRa2HorerGRXWyF4vThfHesw0dmcWHp+cspK743UanA0suA5Q5y8kzY2y6YKrvbIUn69BCAiA== +"@electron/asar@^4.0.0", "@electron/asar@^4.0.1": + version "4.0.1" + resolved "https://registry.yarnpkg.com/@electron/asar/-/asar-4.0.1.tgz#0f0edc51ddb5bf30acb49f706d616b11a0b90668" + integrity sha512-F4Ykm1jiBGY1WV/o8Q8oFW8Nq0u+S2/vPujzNJtdSJ6C4LHC4CiGLn7c17s7SolZ23gcvCebMncmZtNc+MkxPQ== dependencies: - commander "^5.0.0" - glob "^7.1.6" - minimatch "^3.0.4" + commander "^13.1.0" + glob "^11.0.1" + minimatch "^10.0.1" "@electron/get@^2.0.0": version "2.0.3" @@ -1193,231 +1220,352 @@ optionalDependencies: global-agent "^3.0.0" -"@electron/get@^3.0.0": - version "3.1.0" - resolved "https://registry.yarnpkg.com/@electron/get/-/get-3.1.0.tgz#22c5a0bd917ab201badeb77bc4ad18cba54cb4ec" - integrity sha512-F+nKc0xW+kVbBRhFzaMgPy3KwmuNTYX1fx6+FxxoSnNgwYX6LD7AKBTWkU0MQ6IBoe7dz069CNkR673sPAgkCQ== +"@electron/get@^4.0.2": + version "4.0.2" + resolved "https://registry.yarnpkg.com/@electron/get/-/get-4.0.2.tgz#fbac5bf11e94a53cca90b81559a7fcb10fb532e6" + integrity sha512-n9fRt/nzzOOZdDtTP3kT6GVdo0ro9FgMKCoS520kQMIiKBhpGmPny6yK/lER3tOCKr+wLYW1O25D9oI6ZinwCA== dependencies: debug "^4.1.1" - env-paths "^2.2.0" - fs-extra "^8.1.0" - got "^11.8.5" + env-paths "^3.0.0" + got "^14.4.5" + graceful-fs "^4.2.11" progress "^2.0.3" - semver "^6.2.0" + semver "^7.6.3" sumchecker "^3.0.1" optionalDependencies: global-agent "^3.0.0" -"@electron/notarize@^2.1.0": - version "2.5.0" - resolved "https://registry.yarnpkg.com/@electron/notarize/-/notarize-2.5.0.tgz#d4d25356adfa29df4a76bd64a8bd347237cd251e" - integrity sha512-jNT8nwH1f9X5GEITXaQ8IF/KdskvIkOFfB2CvwumsveVidzpSc+mvhhTMdAGSYF3O+Nq49lJ7y+ssODRXu06+A== +"@electron/notarize@^3.1.0": + version "3.1.1" + resolved "https://registry.yarnpkg.com/@electron/notarize/-/notarize-3.1.1.tgz#428d96ab84e333506f2a16fcf12db7ca12fdc7e0" + integrity sha512-uQQSlOiJnqRkTL1wlEBAxe90nVN/Fc/hEmk0bqpKk8nKjV1if/tXLHKUPePtv9Xsx90PtZU8aidx5lAiOpjkQQ== dependencies: - debug "^4.1.1" - fs-extra "^9.0.1" + debug "^4.4.0" promise-retry "^2.0.1" -"@electron/osx-sign@^1.0.5": - version "1.3.3" - resolved "https://registry.yarnpkg.com/@electron/osx-sign/-/osx-sign-1.3.3.tgz#af751510488318d9f7663694af85819690d75583" - integrity sha512-KZ8mhXvWv2rIEgMbWZ4y33bDHyUKMXnx4M0sTyPNK/vcB81ImdeY9Ggdqy0SWbMDgmbqyQ+phgejh6V3R2QuSg== +"@electron/osx-sign@^2.2.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@electron/osx-sign/-/osx-sign-2.3.0.tgz#9418be8bacd65d211ef8a738b9094bdb5277dd00" + integrity sha512-qh/BkWUHITP2W1grtCWn8Pm4EML3q1Rfo2tnd/KHo+f1le5gAYotBc6yEfK6X2VHyN21mPZ3CjvOiYOwjimBlA== dependencies: - compare-version "^0.1.2" debug "^4.3.4" - fs-extra "^10.0.0" isbinaryfile "^4.0.8" - minimist "^1.2.6" plist "^3.0.5" - -"@electron/packager@18.4.4": - version "18.4.4" - resolved "https://registry.yarnpkg.com/@electron/packager/-/packager-18.4.4.tgz#708458f73639c2aa919ce5f6250ac519fa53ee5c" - integrity sha512-fTUCmgL25WXTcFpM1M72VmFP8w3E4d+KNzWxmTDRpvwkfn/S206MAtM2cy0GF78KS9AwASMOUmlOIzCHeNxcGQ== - dependencies: - "@electron/asar" "^3.2.13" - "@electron/get" "^3.0.0" - "@electron/notarize" "^2.1.0" - "@electron/osx-sign" "^1.0.5" - "@electron/universal" "^2.0.1" - "@electron/windows-sign" "^1.0.0" + semver "^7.7.1" + +"@electron/packager@19.0.5": + version "19.0.5" + resolved "https://registry.yarnpkg.com/@electron/packager/-/packager-19.0.5.tgz#9c35a7e1314555be2c675cd289a1741a3074b913" + integrity sha512-HSWJp5ZTYkavzoLViPBK8sUaINrHbhHMueMrYpQea5DkzOplxYCgAKmy+FKsBk7WUm6iYZZ0gJe8f8nIEScqJQ== + dependencies: + "@electron/asar" "^4.0.1" + "@electron/get" "^4.0.2" + "@electron/notarize" "^3.1.0" + "@electron/osx-sign" "^2.2.0" + "@electron/universal" "^3.0.1" + "@electron/windows-sign" "^2.0.2" "@malept/cross-spawn-promise" "^2.0.0" - debug "^4.0.1" - extract-zip "^2.0.0" - filenamify "^4.1.0" - fs-extra "^11.1.0" - galactus "^1.0.0" + debug "^4.4.1" + extract-zip "^2.0.1" + filenamify "^6.0.0" + galactus "^2.0.2" get-package-info "^1.0.0" - junk "^3.1.0" + graceful-fs "^4.2.11" + junk "^4.0.1" parse-author "^2.0.0" - plist "^3.0.0" - prettier "^3.4.2" - resedit "^2.0.0" - resolve "^1.1.6" - semver "^7.1.3" - yargs-parser "^21.1.1" + plist "^3.1.0" + resedit "^2.0.3" + resolve "^1.22.10" + semver "^7.7.2" + yargs-parser "^22.0.0" -"@electron/universal@^2.0.1": - version "2.0.3" - resolved "https://registry.yarnpkg.com/@electron/universal/-/universal-2.0.3.tgz#1680df6ced8f128ca0ff24e29c2165d41d78b3ce" - integrity sha512-Wn9sPYIVFRFl5HmwMJkARCCf7rqK/EurkfQ/rJZ14mHP3iYTjZSIOSVonEAnhWeAXwtw7zOekGRlc6yTtZ0t+g== +"@electron/universal@^3.0.1": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@electron/universal/-/universal-3.0.2.tgz#d21fabd2729f9f5edbf2d8ab088718c4ac29244c" + integrity sha512-2/NBjJhw/VXQayIIj8Mu3ZeZ+lRjx90l2aKqkQiVrA2HiFTc/KHKN8Fjj3Ta7xMAxn45mAKJCatR8xeJ/eW7Tg== dependencies: - "@electron/asar" "^3.3.1" + "@electron/asar" "^4.0.0" "@malept/cross-spawn-promise" "^2.0.0" debug "^4.3.1" dir-compare "^4.2.0" - fs-extra "^11.1.1" minimatch "^9.0.3" plist "^3.1.0" -"@electron/windows-sign@^1.0.0": - version "1.2.2" - resolved "https://registry.yarnpkg.com/@electron/windows-sign/-/windows-sign-1.2.2.tgz#8ceaad52d5c1eb18702f48103d5f3bc7c338fa9d" - integrity sha512-dfZeox66AvdPtb2lD8OsIIQh12Tp0GNCRUDfBHIKGpbmopZto2/A8nSpYYLoedPIHpqkeblZ/k8OV0Gy7PYuyQ== +"@electron/windows-sign@^2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@electron/windows-sign/-/windows-sign-2.0.2.tgz#7396f18c19e407751ffe8528628e292cba52c634" + integrity sha512-9Lldk4pvRBh/BWhwopW4CxCnVoztEAVWdxvVVwpvrFd/3QU3dVn15IRmVB9i46IqpAg1Y42cFtRT0NQKZPpc5A== dependencies: - cross-dirname "^0.1.0" debug "^4.3.4" - fs-extra "^11.1.1" - minimist "^1.2.8" + graceful-fs "^4.2.11" postject "^1.0.0-alpha.6" +"@epic-web/invariant@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@epic-web/invariant/-/invariant-1.0.0.tgz#1073e5dee6dd540410784990eb73e4acd25c9813" + integrity sha512-lrTPqgvfFQtR/eY/qkIzp98OGdNJu0m5ji3q/nJI8v3SXkRKEnWiOxMmbvcSoAIzv/cGiuvRy57k4suKQSAdwA== + "@esbuild/aix-ppc64@0.25.12": version "0.25.12" resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz#80fcbe36130e58b7670511e888b8e88a259ed76c" integrity sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA== +"@esbuild/aix-ppc64@0.27.3": + version "0.27.3" + resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.27.3.tgz#815b39267f9bffd3407ea6c376ac32946e24f8d2" + integrity sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg== + "@esbuild/android-arm64@0.25.12": version "0.25.12" resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz#8aa4965f8d0a7982dc21734bf6601323a66da752" integrity sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg== +"@esbuild/android-arm64@0.27.3": + version "0.27.3" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.27.3.tgz#19b882408829ad8e12b10aff2840711b2da361e8" + integrity sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg== + "@esbuild/android-arm@0.25.12": version "0.25.12" resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.25.12.tgz#300712101f7f50f1d2627a162e6e09b109b6767a" integrity sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg== +"@esbuild/android-arm@0.27.3": + version "0.27.3" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.27.3.tgz#90be58de27915efa27b767fcbdb37a4470627d7b" + integrity sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA== + "@esbuild/android-x64@0.25.12": version "0.25.12" resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.25.12.tgz#87dfb27161202bdc958ef48bb61b09c758faee16" integrity sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg== +"@esbuild/android-x64@0.27.3": + version "0.27.3" + resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.27.3.tgz#d7dcc976f16e01a9aaa2f9b938fbec7389f895ac" + integrity sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ== + "@esbuild/darwin-arm64@0.25.12": version "0.25.12" resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz#79197898ec1ff745d21c071e1c7cc3c802f0c1fd" integrity sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg== +"@esbuild/darwin-arm64@0.27.3": + version "0.27.3" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.27.3.tgz#9f6cac72b3a8532298a6a4493ed639a8988e8abd" + integrity sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg== + "@esbuild/darwin-x64@0.25.12": version "0.25.12" resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz#146400a8562133f45c4d2eadcf37ddd09718079e" integrity sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA== +"@esbuild/darwin-x64@0.27.3": + version "0.27.3" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.27.3.tgz#ac61d645faa37fd650340f1866b0812e1fb14d6a" + integrity sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg== + "@esbuild/freebsd-arm64@0.25.12": version "0.25.12" resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz#1c5f9ba7206e158fd2b24c59fa2d2c8bb47ca0fe" integrity sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg== +"@esbuild/freebsd-arm64@0.27.3": + version "0.27.3" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.3.tgz#b8625689d73cf1830fe58c39051acdc12474ea1b" + integrity sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w== + "@esbuild/freebsd-x64@0.25.12": version "0.25.12" resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz#ea631f4a36beaac4b9279fa0fcc6ca29eaeeb2b3" integrity sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ== +"@esbuild/freebsd-x64@0.27.3": + version "0.27.3" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.27.3.tgz#07be7dd3c9d42fe0eccd2ab9f9ded780bc53bead" + integrity sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA== + "@esbuild/linux-arm64@0.25.12": version "0.25.12" resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz#e1066bce58394f1b1141deec8557a5f0a22f5977" integrity sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ== +"@esbuild/linux-arm64@0.27.3": + version "0.27.3" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.27.3.tgz#bf31918fe5c798586460d2b3d6c46ed2c01ca0b6" + integrity sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg== + "@esbuild/linux-arm@0.25.12": version "0.25.12" resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz#452cd66b20932d08bdc53a8b61c0e30baf4348b9" integrity sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw== +"@esbuild/linux-arm@0.27.3": + version "0.27.3" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.27.3.tgz#28493ee46abec1dc3f500223cd9f8d2df08f9d11" + integrity sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw== + "@esbuild/linux-ia32@0.25.12": version "0.25.12" resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz#b24f8acc45bcf54192c7f2f3be1b53e6551eafe0" integrity sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA== +"@esbuild/linux-ia32@0.27.3": + version "0.27.3" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.27.3.tgz#750752a8b30b43647402561eea764d0a41d0ee29" + integrity sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg== + "@esbuild/linux-loong64@0.25.12": version "0.25.12" resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz#f9cfffa7fc8322571fbc4c8b3268caf15bd81ad0" integrity sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng== +"@esbuild/linux-loong64@0.27.3": + version "0.27.3" + resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.27.3.tgz#a5a92813a04e71198c50f05adfaf18fc1e95b9ed" + integrity sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA== + "@esbuild/linux-mips64el@0.25.12": version "0.25.12" resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz#575a14bd74644ffab891adc7d7e60d275296f2cd" integrity sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw== +"@esbuild/linux-mips64el@0.27.3": + version "0.27.3" + resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.27.3.tgz#deb45d7fd2d2161eadf1fbc593637ed766d50bb1" + integrity sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw== + "@esbuild/linux-ppc64@0.25.12": version "0.25.12" resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz#75b99c70a95fbd5f7739d7692befe60601591869" integrity sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA== +"@esbuild/linux-ppc64@0.27.3": + version "0.27.3" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.27.3.tgz#6f39ae0b8c4d3d2d61a65b26df79f6e12a1c3d78" + integrity sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA== + "@esbuild/linux-riscv64@0.25.12": version "0.25.12" resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz#2e3259440321a44e79ddf7535c325057da875cd6" integrity sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w== +"@esbuild/linux-riscv64@0.27.3": + version "0.27.3" + resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.27.3.tgz#4c5c19c3916612ec8e3915187030b9df0b955c1d" + integrity sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ== + "@esbuild/linux-s390x@0.25.12": version "0.25.12" resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz#17676cabbfe5928da5b2a0d6df5d58cd08db2663" integrity sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg== +"@esbuild/linux-s390x@0.27.3": + version "0.27.3" + resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.27.3.tgz#9ed17b3198fa08ad5ccaa9e74f6c0aff7ad0156d" + integrity sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw== + "@esbuild/linux-x64@0.25.12": version "0.25.12" resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz#0583775685ca82066d04c3507f09524d3cd7a306" integrity sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw== +"@esbuild/linux-x64@0.27.3": + version "0.27.3" + resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.27.3.tgz#12383dcbf71b7cf6513e58b4b08d95a710bf52a5" + integrity sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA== + "@esbuild/netbsd-arm64@0.25.12": version "0.25.12" resolved "https://registry.yarnpkg.com/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz#f04c4049cb2e252fe96b16fed90f70746b13f4a4" integrity sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg== +"@esbuild/netbsd-arm64@0.27.3": + version "0.27.3" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.3.tgz#dd0cb2fa543205fcd931df44f4786bfcce6df7d7" + integrity sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA== + "@esbuild/netbsd-x64@0.25.12": version "0.25.12" resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz#77da0d0a0d826d7c921eea3d40292548b258a076" integrity sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ== +"@esbuild/netbsd-x64@0.27.3": + version "0.27.3" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.27.3.tgz#028ad1807a8e03e155153b2d025b506c3787354b" + integrity sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA== + "@esbuild/openbsd-arm64@0.25.12": version "0.25.12" resolved "https://registry.yarnpkg.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz#6296f5867aedef28a81b22ab2009c786a952dccd" integrity sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A== +"@esbuild/openbsd-arm64@0.27.3": + version "0.27.3" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.3.tgz#e3c16ff3490c9b59b969fffca87f350ffc0e2af5" + integrity sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw== + "@esbuild/openbsd-x64@0.25.12": version "0.25.12" resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz#f8d23303360e27b16cf065b23bbff43c14142679" integrity sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw== +"@esbuild/openbsd-x64@0.27.3": + version "0.27.3" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.27.3.tgz#c5a4693fcb03d1cbecbf8b422422468dfc0d2a8b" + integrity sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ== + "@esbuild/openharmony-arm64@0.25.12": version "0.25.12" resolved "https://registry.yarnpkg.com/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz#49e0b768744a3924be0d7fd97dd6ce9b2923d88d" integrity sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg== +"@esbuild/openharmony-arm64@0.27.3": + version "0.27.3" + resolved "https://registry.yarnpkg.com/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.3.tgz#082082444f12db564a0775a41e1991c0e125055e" + integrity sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g== + "@esbuild/sunos-x64@0.25.12": version "0.25.12" resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz#a6ed7d6778d67e528c81fb165b23f4911b9b13d6" integrity sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w== +"@esbuild/sunos-x64@0.27.3": + version "0.27.3" + resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.27.3.tgz#5ab036c53f929e8405c4e96e865a424160a1b537" + integrity sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA== + "@esbuild/win32-arm64@0.25.12": version "0.25.12" resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz#9ac14c378e1b653af17d08e7d3ce34caef587323" integrity sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg== +"@esbuild/win32-arm64@0.27.3": + version "0.27.3" + resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.27.3.tgz#38de700ef4b960a0045370c171794526e589862e" + integrity sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA== + "@esbuild/win32-ia32@0.25.12": version "0.25.12" resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz#918942dcbbb35cc14fca39afb91b5e6a3d127267" integrity sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ== +"@esbuild/win32-ia32@0.27.3": + version "0.27.3" + resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.27.3.tgz#451b93dc03ec5d4f38619e6cd64d9f9eff06f55c" + integrity sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q== + "@esbuild/win32-x64@0.25.12": version "0.25.12" resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz#9bdad8176be7811ad148d1f8772359041f46c6c5" integrity sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA== -"@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0", "@eslint-community/eslint-utils@^4.8.0": - version "4.9.0" - resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz#7308df158e064f0dd8b8fdb58aa14fa2a7f913b3" - integrity sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g== - dependencies: - eslint-visitor-keys "^3.4.3" +"@esbuild/win32-x64@0.27.3": + version "0.27.3" + resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.27.3.tgz#0eaf705c941a218a43dba8e09f1df1d6cd2f1f17" + integrity sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA== -"@eslint-community/eslint-utils@^4.9.1": +"@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0", "@eslint-community/eslint-utils@^4.8.0", "@eslint-community/eslint-utils@^4.9.1": version "4.9.1" resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz#4e90af67bc51ddee6cdef5284edf572ec376b595" integrity sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ== @@ -1453,9 +1601,9 @@ "@types/json-schema" "^7.0.15" "@eslint/eslintrc@^3.3.1": - version "3.3.1" - resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-3.3.1.tgz#e55f7f1dd400600dd066dbba349c4c0bac916964" - integrity sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ== + version "3.3.3" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-3.3.3.tgz#26393a0806501b5e2b6a43aa588a4d8df67880ac" + integrity sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ== dependencies: ajv "^6.12.4" debug "^4.3.2" @@ -1463,7 +1611,7 @@ globals "^14.0.0" ignore "^5.2.0" import-fresh "^3.2.1" - js-yaml "^4.1.0" + js-yaml "^4.1.1" minimatch "^3.1.2" strip-json-comments "^3.1.1" @@ -1485,10 +1633,10 @@ "@eslint/core" "^0.17.0" levn "^0.4.1" -"@expo/cli@54.0.21": - version "54.0.21" - resolved "https://registry.yarnpkg.com/@expo/cli/-/cli-54.0.21.tgz#196ea08f539535b26717e09661937b24b38f25e7" - integrity sha512-L/FdpyZDsg/Nq6xW6kfiyF9DUzKfLZCKFXEVZcDqCNar6bXxQVotQyvgexRvtUF5nLinuT/UafLOdC3FUALUmA== +"@expo/cli@54.0.23": + version "54.0.23" + resolved "https://registry.yarnpkg.com/@expo/cli/-/cli-54.0.23.tgz#e8a7dc4e1f2a8a5361afd80bcc352014b57a87ac" + integrity sha512-km0h72SFfQCmVycH/JtPFTVy69w6Lx1cHNDmfLfQqgKFYeeHTjx7LVDP4POHCtNxFP2UeRazrygJhlh4zz498g== dependencies: "@0no-co/graphql.web" "^1.0.8" "@expo/code-signing-certificates" "^0.0.6" @@ -1499,9 +1647,9 @@ "@expo/image-utils" "^0.8.8" "@expo/json-file" "^10.0.8" "@expo/metro" "~54.2.0" - "@expo/metro-config" "~54.0.13" + "@expo/metro-config" "~54.0.14" "@expo/osascript" "^2.3.8" - "@expo/package-manager" "^1.9.9" + "@expo/package-manager" "^1.9.10" "@expo/plist" "^0.4.8" "@expo/prebuild-config" "^54.0.8" "@expo/schema-utils" "^0.1.8" @@ -1561,26 +1709,6 @@ dependencies: node-forge "^1.3.3" -"@expo/config-plugins@~54.0.3": - version "54.0.3" - resolved "https://registry.yarnpkg.com/@expo/config-plugins/-/config-plugins-54.0.3.tgz#2b9ffd68a48e3b51299cdbe3ee777b9f5163fc03" - integrity sha512-tBIUZIxLQfCu5jmqTO+UOeeDUGIB0BbK6xTMkPRObAXRQeTLPPfokZRCo818d2owd+Bcmq1wBaDz0VY3g+glfw== - dependencies: - "@expo/config-types" "^54.0.9" - "@expo/json-file" "~10.0.7" - "@expo/plist" "^0.4.7" - "@expo/sdk-runtime-versions" "^1.0.0" - chalk "^4.1.2" - debug "^4.3.5" - getenv "^2.0.0" - glob "^13.0.0" - resolve-from "^5.0.0" - semver "^7.5.4" - slash "^3.0.0" - slugify "^1.6.6" - xcode "^3.0.1" - xml2js "0.6.0" - "@expo/config-plugins@~54.0.4": version "54.0.4" resolved "https://registry.yarnpkg.com/@expo/config-plugins/-/config-plugins-54.0.4.tgz#b31cb16f6651342abcdafba600118245ecd9fb00" @@ -1606,30 +1734,6 @@ resolved "https://registry.yarnpkg.com/@expo/config-types/-/config-types-54.0.10.tgz#688f4338255d2fdea970f44e2dfd8e8d37dec292" integrity sha512-/J16SC2an1LdtCZ67xhSkGXpALYUVUNyZws7v+PVsFZxClYehDSoKLqyRaGkpHlYrCc08bS0RF5E0JV6g50psA== -"@expo/config-types@^54.0.9": - version "54.0.9" - resolved "https://registry.yarnpkg.com/@expo/config-types/-/config-types-54.0.9.tgz#b9279c47fe249b774fbd3358b6abddea08f1bcec" - integrity sha512-Llf4jwcrAnrxgE5WCdAOxtMf8FGwS4Sk0SSgI0NnIaSyCnmOCAm80GPFvsK778Oj19Ub4tSyzdqufPyeQPksWw== - -"@expo/config@~12.0.12": - version "12.0.12" - resolved "https://registry.yarnpkg.com/@expo/config/-/config-12.0.12.tgz#5e26390ec209ee56432e980451c08b361e0f07dd" - integrity sha512-X2MW86+ulLpMGvdgfvpl2EOBAKUlwvnvoPwdaZeeyWufGopn1nTUeh4C9gMsplDaP1kXv9sLXVhOoUoX6g9PvQ== - dependencies: - "@babel/code-frame" "~7.10.4" - "@expo/config-plugins" "~54.0.3" - "@expo/config-types" "^54.0.10" - "@expo/json-file" "^10.0.8" - deepmerge "^4.3.1" - getenv "^2.0.0" - glob "^13.0.0" - require-from-string "^2.0.2" - resolve-from "^5.0.0" - resolve-workspace-root "^2.0.0" - semver "^7.6.0" - slugify "^1.3.4" - sucrase "~3.35.1" - "@expo/config@~12.0.13": version "12.0.13" resolved "https://registry.yarnpkg.com/@expo/config/-/config-12.0.13.tgz#8e696e6121c3c364e1dd527f595cf0a1d9386828" @@ -1716,18 +1820,10 @@ "@babel/code-frame" "~7.10.4" json5 "^2.2.3" -"@expo/json-file@~10.0.7": - version "10.0.7" - resolved "https://registry.yarnpkg.com/@expo/json-file/-/json-file-10.0.7.tgz#e4f58fdc03fc62f13610eeafe086d84e6e44fe01" - integrity sha512-z2OTC0XNO6riZu98EjdNHC05l51ySeTto6GP7oSQrCvQgG9ARBwD1YvMQaVZ9wU7p/4LzSf1O7tckL3B45fPpw== - dependencies: - "@babel/code-frame" "~7.10.4" - json5 "^2.2.3" - -"@expo/metro-config@54.0.13", "@expo/metro-config@~54.0.13": - version "54.0.13" - resolved "https://registry.yarnpkg.com/@expo/metro-config/-/metro-config-54.0.13.tgz#a78f0da6ba7eca4d3b1fa0fa2487be4d947e4fd6" - integrity sha512-RRufMCgLR2Za1WGsh02OatIJo5qZFt31yCnIOSfoubNc3Qqe92Z41pVsbrFnmw5CIaisv1NgdBy05DHe7pEyuw== +"@expo/metro-config@54.0.14", "@expo/metro-config@~54.0.14": + version "54.0.14" + resolved "https://registry.yarnpkg.com/@expo/metro-config/-/metro-config-54.0.14.tgz#e455dfb2bae9473ec665bc830d651baa709c1e8a" + integrity sha512-hxpLyDfOR4L23tJ9W1IbJJsG7k4lv2sotohBm/kTYyiG+pe1SYCAWsRmgk+H42o/wWf/HQjE5k45S5TomGLxNA== dependencies: "@babel/code-frame" "^7.20.0" "@babel/core" "^7.20.0" @@ -1779,10 +1875,10 @@ "@expo/spawn-async" "^1.7.2" exec-async "^2.2.0" -"@expo/package-manager@^1.9.9": - version "1.9.9" - resolved "https://registry.yarnpkg.com/@expo/package-manager/-/package-manager-1.9.9.tgz#dd030d2bccebd095e02bfb6976852afaddcd122a" - integrity sha512-Nv5THOwXzPprMJwbnXU01iXSrCp3vJqly9M4EJ2GkKko9Ifer2ucpg7x6OUsE09/lw+npaoUnHMXwkw7gcKxlg== +"@expo/package-manager@^1.9.10": + version "1.9.10" + resolved "https://registry.yarnpkg.com/@expo/package-manager/-/package-manager-1.9.10.tgz#5da3f4943f6fa44ddd7f0efe32a360040edd734a" + integrity sha512-axJm+NOj3jVxep49va/+L3KkF3YW/dkV+RwzqUJedZrv4LeTqOG4rhrCaCPXHTvLqCTDKu6j0Xyd28N7mnxsGA== dependencies: "@expo/json-file" "^10.0.8" "@expo/spawn-async" "^1.7.2" @@ -1791,15 +1887,6 @@ ora "^3.4.0" resolve-workspace-root "^2.0.0" -"@expo/plist@^0.4.7": - version "0.4.7" - resolved "https://registry.yarnpkg.com/@expo/plist/-/plist-0.4.7.tgz#40fa796e93d5be0452ce72e5110e69c8ac915403" - integrity sha512-dGxqHPvCZKeRKDU1sJZMmuyVtcASuSYh1LPFVaM1DuffqPL36n6FMEL0iUqq2Tx3xhWk8wCnWl34IKplUjJDdA== - dependencies: - "@xmldom/xmldom" "^0.8.8" - base64-js "^1.2.3" - xmlbuilder "^15.1.1" - "@expo/plist@^0.4.8": version "0.4.8" resolved "https://registry.yarnpkg.com/@expo/plist/-/plist-0.4.8.tgz#e014511a4a5008cf2b832b91caa8e9f2704127cc" @@ -1858,13 +1945,12 @@ integrity sha512-nDRbLmSrJar7abvUjp3smDwH8HcbZcoOEa5jVPUv9/9CajgmWw20JNRwTuBRzWIWIkEJDkz20GoNA+tSwUqk0Q== "@expo/xcpretty@^4.3.0": - version "4.3.2" - resolved "https://registry.yarnpkg.com/@expo/xcpretty/-/xcpretty-4.3.2.tgz#12dba1295167a9c8dde4be783d74f7e81648ca5d" - integrity sha512-ReZxZ8pdnoI3tP/dNnJdnmAk7uLT4FjsKDGW7YeDdvdOMz2XCQSmSCM9IWlrXuWtMF9zeSB6WJtEhCQ41gQOfw== + version "4.4.0" + resolved "https://registry.yarnpkg.com/@expo/xcpretty/-/xcpretty-4.4.0.tgz#7a5aaf9ce5d538f84ae6518655d175f5cc94ce81" + integrity sha512-o2qDlTqJ606h4xR36H2zWTywmZ3v3842K6TU8Ik2n1mfW0S580VHlt3eItVYdLYz+klaPp7CXqanja8eASZjRw== dependencies: - "@babel/code-frame" "7.10.4" + "@babel/code-frame" "^7.20.0" chalk "^4.1.0" - find-up "^5.0.0" js-yaml "^4.1.0" "@gorhom/bottom-sheet@5.2.8": @@ -1922,13 +2008,18 @@ resolved "https://registry.yarnpkg.com/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz#3081dadbc3460661b751e7591d7faea5df39dd29" integrity sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ== -"@isaacs/brace-expansion@^5.0.0": +"@isaacs/brace-expansion@^5.0.1": version "5.0.1" resolved "https://registry.yarnpkg.com/@isaacs/brace-expansion/-/brace-expansion-5.0.1.tgz#0ef5a92d91f2fff2a37646ce54da9e5f599f6eff" integrity sha512-WMz71T1JS624nWj2n2fnYAuPovhv7EUhk69R6i9dsVyzxt5eM3bjwvgk9L+APE1TRscGysAVMANkB0jh0LQZrQ== dependencies: "@isaacs/balanced-match" "^4.0.1" +"@isaacs/cliui@^9.0.0": + version "9.0.0" + resolved "https://registry.yarnpkg.com/@isaacs/cliui/-/cliui-9.0.0.tgz#4d0a3f127058043bf2e7ee169eaf30ed901302f3" + integrity sha512-AokJm4tuBHillT+FpMtxQ60n8ObyXBatq7jD2/JA9dxbDDokKQm8KMht5ibGzLVU9IJDIKK4TPKgMHEYMn3lMg== + "@isaacs/fs-minipass@^4.0.0": version "4.0.1" resolved "https://registry.yarnpkg.com/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz#2d59ae3ab4b38fb4270bfa23d30f8e2e86c7fe32" @@ -2068,21 +2159,107 @@ "@jridgewell/resolve-uri" "^3.1.0" "@jridgewell/sourcemap-codec" "^1.4.14" +"@jsonjoy.com/base64@17.67.0": + version "17.67.0" + resolved "https://registry.yarnpkg.com/@jsonjoy.com/base64/-/base64-17.67.0.tgz#7eeda3cb41138d77a90408fd2e42b2aba10576d7" + integrity sha512-5SEsJGsm15aP8TQGkDfJvz9axgPwAEm98S5DxOuYe8e1EbfajcDmgeXXzccEjh+mLnjqEKrkBdjHWS5vFNwDdw== + "@jsonjoy.com/base64@^1.1.2": version "1.1.2" resolved "https://registry.yarnpkg.com/@jsonjoy.com/base64/-/base64-1.1.2.tgz#cf8ea9dcb849b81c95f14fc0aaa151c6b54d2578" integrity sha512-q6XAnWQDIMA3+FTiOYajoYqySkO+JSat0ytXGSuRdq9uXE7o92gzuQwQM14xaCRlBLGq3v5miDGC4vkVTn54xA== +"@jsonjoy.com/buffers@17.67.0", "@jsonjoy.com/buffers@^17.65.0": + version "17.67.0" + resolved "https://registry.yarnpkg.com/@jsonjoy.com/buffers/-/buffers-17.67.0.tgz#5c58dbcdeea8824ce296bd1cfce006c2eb167b3d" + integrity sha512-tfExRpYxBvi32vPs9ZHaTjSP4fHAfzSmcahOfNxtvGHcyJel+aibkPlGeBB+7AoC6hL7lXIE++8okecBxx7lcw== + "@jsonjoy.com/buffers@^1.0.0", "@jsonjoy.com/buffers@^1.2.0": version "1.2.1" resolved "https://registry.yarnpkg.com/@jsonjoy.com/buffers/-/buffers-1.2.1.tgz#8d99c7f67eaf724d3428dfd9826c6455266a5c83" integrity sha512-12cdlDwX4RUM3QxmUbVJWqZ/mrK6dFQH4Zxq6+r1YXKXYBNgZXndx2qbCJwh3+WWkCSn67IjnlG3XYTvmvYtgA== +"@jsonjoy.com/codegen@17.67.0": + version "17.67.0" + resolved "https://registry.yarnpkg.com/@jsonjoy.com/codegen/-/codegen-17.67.0.tgz#3635fd8769d77e19b75dc5574bc9756019b2e591" + integrity sha512-idnkUplROpdBOV0HMcwhsCUS5TRUi9poagdGs70A6S4ux9+/aPuKbh8+UYRTLYQHtXvAdNfQWXDqZEx5k4Dj2Q== + "@jsonjoy.com/codegen@^1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/@jsonjoy.com/codegen/-/codegen-1.0.0.tgz#5c23f796c47675f166d23b948cdb889184b93207" integrity sha512-E8Oy+08cmCf0EK/NMxpaJZmOxPqM+6iSe2S4nlSBrPZOORoDJILxtbSUEDKQyTamm/BVAhIGllOBNU79/dwf0g== +"@jsonjoy.com/fs-core@4.56.10": + version "4.56.10" + resolved "https://registry.yarnpkg.com/@jsonjoy.com/fs-core/-/fs-core-4.56.10.tgz#320728b4b7bef63abb60e7630351623899237411" + integrity sha512-PyAEA/3cnHhsGcdY+AmIU+ZPqTuZkDhCXQ2wkXypdLitSpd6d5Ivxhnq4wa2ETRWFVJGabYynBWxIijOswSmOw== + dependencies: + "@jsonjoy.com/fs-node-builtins" "4.56.10" + "@jsonjoy.com/fs-node-utils" "4.56.10" + thingies "^2.5.0" + +"@jsonjoy.com/fs-fsa@4.56.10": + version "4.56.10" + resolved "https://registry.yarnpkg.com/@jsonjoy.com/fs-fsa/-/fs-fsa-4.56.10.tgz#02bac88c4968ddf2effbd7452861aaed60ba3557" + integrity sha512-/FVK63ysNzTPOnCCcPoPHt77TOmachdMS422txM4KhxddLdbW1fIbFMYH0AM0ow/YchCyS5gqEjKLNyv71j/5Q== + dependencies: + "@jsonjoy.com/fs-core" "4.56.10" + "@jsonjoy.com/fs-node-builtins" "4.56.10" + "@jsonjoy.com/fs-node-utils" "4.56.10" + thingies "^2.5.0" + +"@jsonjoy.com/fs-node-builtins@4.56.10": + version "4.56.10" + resolved "https://registry.yarnpkg.com/@jsonjoy.com/fs-node-builtins/-/fs-node-builtins-4.56.10.tgz#a32a5bcb093f8b34a99aa8957e993a52ec316662" + integrity sha512-uUnKz8R0YJyKq5jXpZtkGV9U0pJDt8hmYcLRrPjROheIfjMXsz82kXMgAA/qNg0wrZ1Kv+hrg7azqEZx6XZCVw== + +"@jsonjoy.com/fs-node-to-fsa@4.56.10": + version "4.56.10" + resolved "https://registry.yarnpkg.com/@jsonjoy.com/fs-node-to-fsa/-/fs-node-to-fsa-4.56.10.tgz#33fc503e50d283ac5fc510e3accced7fccecf2f4" + integrity sha512-oH+O6Y4lhn9NyG6aEoFwIBNKZeYy66toP5LJcDOMBgL99BKQMUf/zWJspdRhMdn/3hbzQsZ8EHHsuekbFLGUWw== + dependencies: + "@jsonjoy.com/fs-fsa" "4.56.10" + "@jsonjoy.com/fs-node-builtins" "4.56.10" + "@jsonjoy.com/fs-node-utils" "4.56.10" + +"@jsonjoy.com/fs-node-utils@4.56.10": + version "4.56.10" + resolved "https://registry.yarnpkg.com/@jsonjoy.com/fs-node-utils/-/fs-node-utils-4.56.10.tgz#788e95052aa99744f6e8e55b5098afc203df2b9e" + integrity sha512-8EuPBgVI2aDPwFdaNQeNpHsyqPi3rr+85tMNG/lHvQLiVjzoZsvxA//Xd8aB567LUhy4QS03ptT+unkD/DIsNg== + dependencies: + "@jsonjoy.com/fs-node-builtins" "4.56.10" + +"@jsonjoy.com/fs-node@4.56.10": + version "4.56.10" + resolved "https://registry.yarnpkg.com/@jsonjoy.com/fs-node/-/fs-node-4.56.10.tgz#70b18bfaf14544a9820d2016e913dde12c6de991" + integrity sha512-7R4Gv3tkUdW3dXfXiOkqxkElxKNVdd8BDOWC0/dbERd0pXpPY+s2s1Mino+aTvkGrFPiY+mmVxA7zhskm4Ue4Q== + dependencies: + "@jsonjoy.com/fs-core" "4.56.10" + "@jsonjoy.com/fs-node-builtins" "4.56.10" + "@jsonjoy.com/fs-node-utils" "4.56.10" + "@jsonjoy.com/fs-print" "4.56.10" + "@jsonjoy.com/fs-snapshot" "4.56.10" + glob-to-regex.js "^1.0.0" + thingies "^2.5.0" + +"@jsonjoy.com/fs-print@4.56.10": + version "4.56.10" + resolved "https://registry.yarnpkg.com/@jsonjoy.com/fs-print/-/fs-print-4.56.10.tgz#7c181b9aefcc1b268be0e6233bff26310c355335" + integrity sha512-JW4fp5mAYepzFsSGrQ48ep8FXxpg4niFWHdF78wDrFGof7F3tKDJln72QFDEn/27M1yHd4v7sKHHVPh78aWcEw== + dependencies: + "@jsonjoy.com/fs-node-utils" "4.56.10" + tree-dump "^1.1.0" + +"@jsonjoy.com/fs-snapshot@4.56.10": + version "4.56.10" + resolved "https://registry.yarnpkg.com/@jsonjoy.com/fs-snapshot/-/fs-snapshot-4.56.10.tgz#05aadd2c0eaa855b13d6cb17d29b7c8cee239c8c" + integrity sha512-DkR6l5fj7+qj0+fVKm/OOXMGfDFCGXLfyHkORH3DF8hxkpDgIHbhf/DwncBMs2igu/ST7OEkexn1gIqoU6Y+9g== + dependencies: + "@jsonjoy.com/buffers" "^17.65.0" + "@jsonjoy.com/fs-node-utils" "4.56.10" + "@jsonjoy.com/json-pack" "^17.65.0" + "@jsonjoy.com/util" "^17.65.0" + "@jsonjoy.com/json-pack@^1.11.0": version "1.21.0" resolved "https://registry.yarnpkg.com/@jsonjoy.com/json-pack/-/json-pack-1.21.0.tgz#93f8dd57fe3a3a92132b33d1eb182dcd9e7629fa" @@ -2097,6 +2274,27 @@ thingies "^2.5.0" tree-dump "^1.1.0" +"@jsonjoy.com/json-pack@^17.65.0": + version "17.67.0" + resolved "https://registry.yarnpkg.com/@jsonjoy.com/json-pack/-/json-pack-17.67.0.tgz#8dd8ff65dd999c5d4d26df46c63915c7bdec093a" + integrity sha512-t0ejURcGaZsn1ClbJ/3kFqSOjlryd92eQY465IYrezsXmPcfHPE/av4twRSxf6WE+TkZgLY+71vCZbiIiFKA/w== + dependencies: + "@jsonjoy.com/base64" "17.67.0" + "@jsonjoy.com/buffers" "17.67.0" + "@jsonjoy.com/codegen" "17.67.0" + "@jsonjoy.com/json-pointer" "17.67.0" + "@jsonjoy.com/util" "17.67.0" + hyperdyperid "^1.2.0" + thingies "^2.5.0" + tree-dump "^1.1.0" + +"@jsonjoy.com/json-pointer@17.67.0": + version "17.67.0" + resolved "https://registry.yarnpkg.com/@jsonjoy.com/json-pointer/-/json-pointer-17.67.0.tgz#74439573dc046e0c9a3a552fb94b391bc75313b8" + integrity sha512-+iqOFInH+QZGmSuaybBUNdh7yvNrXvqR+h3wjXm0N/3JK1EyyFAeGJvqnmQL61d1ARLlk/wJdFKSL+LHJ1eaUA== + dependencies: + "@jsonjoy.com/util" "17.67.0" + "@jsonjoy.com/json-pointer@^1.0.2": version "1.0.2" resolved "https://registry.yarnpkg.com/@jsonjoy.com/json-pointer/-/json-pointer-1.0.2.tgz#049cb530ac24e84cba08590c5e36b431c4843408" @@ -2105,6 +2303,14 @@ "@jsonjoy.com/codegen" "^1.0.0" "@jsonjoy.com/util" "^1.9.0" +"@jsonjoy.com/util@17.67.0", "@jsonjoy.com/util@^17.65.0": + version "17.67.0" + resolved "https://registry.yarnpkg.com/@jsonjoy.com/util/-/util-17.67.0.tgz#7c4288fc3808233e55c7610101e7bb4590cddd3f" + integrity sha512-6+8xBaz1rLSohlGh68D1pdw3AwDi9xydm8QNlAFkvnavCJYSze+pxoW2VKP8p308jtlMRLs5NTHfPlZLd4w7ew== + dependencies: + "@jsonjoy.com/buffers" "17.67.0" + "@jsonjoy.com/codegen" "17.67.0" + "@jsonjoy.com/util@^1.9.0": version "1.9.0" resolved "https://registry.yarnpkg.com/@jsonjoy.com/util/-/util-1.9.0.tgz#7ee95586aed0a766b746cd8d8363e336c3c47c46" @@ -2113,15 +2319,20 @@ "@jsonjoy.com/buffers" "^1.0.0" "@jsonjoy.com/codegen" "^1.0.0" +"@keyv/serialize@^1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@keyv/serialize/-/serialize-1.1.1.tgz#0c01dd3a3483882af7cf3878d4e71d505c81fc4a" + integrity sha512-dXn3FZhPv0US+7dtJsIi2R+c7qWYiReoEh5zUntWCf4oSpMNib8FDhSoed6m3QyZdx5hK7iLFkYk3rNxwt8vTA== + "@khanacademy/perseus-utils@2.1.4": version "2.1.4" resolved "https://registry.yarnpkg.com/@khanacademy/perseus-utils/-/perseus-utils-2.1.4.tgz#f87462f16781c7a40311ac0932e13a1f67ab9418" integrity sha512-TQLLUZQWc0K0gCLejNTd7G2y1qMhF3BtM9rBGvSObo10EVL4LwA2nCgm0FaPLowCp0ujweZACh9ty+xlekkGXA== -"@khanacademy/simple-markdown@2.1.4": - version "2.1.4" - resolved "https://registry.yarnpkg.com/@khanacademy/simple-markdown/-/simple-markdown-2.1.4.tgz#1673e79fd1d4a2f4c3f697c5421b0a1289ed6f0e" - integrity sha512-2WBPGtnaCuMS2b6DewdXPbgy14QFo6V22FuMf2PxREQaAHakIOhjQ7sgeswN7ZdAZ1uyGi4TnDbN1qSgIbaC3Q== +"@khanacademy/simple-markdown@2.2.1": + version "2.2.1" + resolved "https://registry.yarnpkg.com/@khanacademy/simple-markdown/-/simple-markdown-2.2.1.tgz#3a477c2bcb026fbf25c0aa0272fbb9b3b0f7058c" + integrity sha512-9xmLOLIh//ZiWwQFtJETnV8Ha24J3KgHpcwrROjqjuXD3tNiNZrVMBBZU93Aw1/jeocA7p5jiYBrErZiqGlLag== dependencies: "@khanacademy/perseus-utils" "2.1.4" @@ -2142,10 +2353,10 @@ resolved "https://registry.yarnpkg.com/@msgpack/msgpack/-/msgpack-2.8.0.tgz#4210deb771ee3912964f14a15ddfb5ff877e70b9" integrity sha512-h9u4u/jiIRKbq25PM+zymTyW6bhTzELvOoUd+AvYriWOAKpLGnIamaET3pnHYoI5iYphAHBI4ayx0MehR+VVPQ== -"@msgpack/msgpack@3.1.2": - version "3.1.2" - resolved "https://registry.yarnpkg.com/@msgpack/msgpack/-/msgpack-3.1.2.tgz#fdd25cc2202297519798bbaf4689152ad9609e19" - integrity sha512-JEW4DEtBzfe8HvUYecLU9e6+XJnKDlUAIve8FvPzF3Kzs6Xo/KuZkZJsDH0wJXl/qEZbeeE7edxDNY3kMs39hQ== +"@msgpack/msgpack@3.1.3": + version "3.1.3" + resolved "https://registry.yarnpkg.com/@msgpack/msgpack/-/msgpack-3.1.3.tgz#c4bff2b9539faf0882f3ee03537a7e9a4b3a7864" + integrity sha512-47XIizs9XZXvuJgoaJUIE2lFoID8ugvc0jzSHP+Ptfk8nTbnR8g788wv48N03Kx0UkAv559HWRQ3yzOgzlRNUA== "@nicolo-ribaudo/eslint-scope-5-internals@5.1.1-v1": version "5.1.1-v1" @@ -2326,9 +2537,9 @@ integrity sha512-slT6XeTCAbdql61GVLlGU4x7XHI7kCZV5Um5uhE4zLX4ApgiiXc0UYFvVOKq06xcovzp7p+61l68oPi563ARKg== "@preact/signals@^1.3.1": - version "1.3.3" - resolved "https://registry.yarnpkg.com/@preact/signals/-/signals-1.3.3.tgz#fca03bc348bbcf7bf88f84d6d4cfa05b42c93b0f" - integrity sha512-FieIS2Z3iHAmjYTqsD3U3DEad1/bOicGN8wT34bbrdp422kQfkP7iwYgqTgx53wPdnsENaNkNNmScbi3RVZq8Q== + version "1.3.4" + resolved "https://registry.yarnpkg.com/@preact/signals/-/signals-1.3.4.tgz#83509e1ce1ba5572b2dfb45039121066c8851563" + integrity sha512-TPMkStdT0QpSc8FpB63aOwXoSiZyIrPsP9Uj347KopdS6olZdAYeeird/5FZv/M1Yc1ge5qstub2o8VDbvkT4g== dependencies: "@preact/signals-core" "^1.7.0" @@ -2760,19 +2971,19 @@ invariant "^2.2.4" nullthrows "^1.1.1" -"@react-navigation/bottom-tabs@7.9.1": - version "7.9.1" - resolved "https://registry.yarnpkg.com/@react-navigation/bottom-tabs/-/bottom-tabs-7.9.1.tgz#1a228e68c85f27bb3f0d2b814bdddb25eeb60360" - integrity sha512-1MHn1b5tWFa8t8LXUvaNtlg5WGVXcNTsiV00ygQDBFDusMcu0ZPOU1exqELZwHf6kDntmTQE96/NRM+Cd2QR+A== +"@react-navigation/bottom-tabs@7.13.0": + version "7.13.0" + resolved "https://registry.yarnpkg.com/@react-navigation/bottom-tabs/-/bottom-tabs-7.13.0.tgz#cef50768633cdbd272809aec7751ac37ce36dab0" + integrity sha512-qxxjRDpjhZ4vIZqG4rBU1Vx2jgOAO/ciUKc9sJqVlTM005E2E+aK1EaE3lGaBDyZxTpjonollAucZcqL7OTscQ== dependencies: - "@react-navigation/elements" "^2.9.4" + "@react-navigation/elements" "^2.9.5" color "^4.2.3" sf-symbols-typescript "^2.1.0" -"@react-navigation/core@7.13.7", "@react-navigation/core@^7.13.7": - version "7.13.7" - resolved "https://registry.yarnpkg.com/@react-navigation/core/-/core-7.13.7.tgz#1263903a703b5f183a08c22dfc5c3735ac4ef91c" - integrity sha512-k2ABo3250vq1ovOh/iVwXS6Hwr5PVRGXoPh/ewVFOOuEKTvOx9i//OBzt8EF+HokBxS2HBRlR2b+aCOmscRqBw== +"@react-navigation/core@7.14.0", "@react-navigation/core@^7.14.0": + version "7.14.0" + resolved "https://registry.yarnpkg.com/@react-navigation/core/-/core-7.14.0.tgz#d24f93d424ab33f645262dc4775e4708aa3d9a8b" + integrity sha512-tMpzskBzVp0E7CRNdNtJIdXjk54Kwe/TF9ViXAef+YFM1kSfGv4e/B2ozfXE+YyYgmh4WavTv8fkdJz1CNyu+g== dependencies: "@react-navigation/routers" "^7.5.3" escape-string-regexp "^4.0.0" @@ -2783,31 +2994,31 @@ use-latest-callback "^0.2.4" use-sync-external-store "^1.5.0" -"@react-navigation/elements@^2.9.4": - version "2.9.4" - resolved "https://registry.yarnpkg.com/@react-navigation/elements/-/elements-2.9.4.tgz#72101e7cf74e6952a303ffd3a69aab5e9ba178a8" - integrity sha512-TMFh+QzwesEuSaKpvZk4BFC5105t5gJs9eq+jG7jtfdlMKyrcFwheu2/dy1zfrW4WYbVcoMxhzFqCU4mKwI4fw== +"@react-navigation/elements@^2.9.5": + version "2.9.5" + resolved "https://registry.yarnpkg.com/@react-navigation/elements/-/elements-2.9.5.tgz#29f68c4975351724dcfe1d3bdc76c4d6dc65fc33" + integrity sha512-iHZU8rRN1014Upz73AqNVXDvSMZDh5/ktQ1CMe21rdgnOY79RWtHHBp9qOS3VtqlUVYGkuX5GEw5mDt4tKdl0g== dependencies: color "^4.2.3" use-latest-callback "^0.2.4" use-sync-external-store "^1.5.0" -"@react-navigation/native-stack@7.9.1": - version "7.9.1" - resolved "https://registry.yarnpkg.com/@react-navigation/native-stack/-/native-stack-7.9.1.tgz#0d8f576a9c2ca34344b114ae1e380ad0933dc7b1" - integrity sha512-DIRv72UliHvCWkBO1xwvik0maRka4aebn10huL9u4lPRXZZjumlMFHhA9z8vOaj02ri54m8pQUybRdbVzNeqgQ== +"@react-navigation/native-stack@7.12.0": + version "7.12.0" + resolved "https://registry.yarnpkg.com/@react-navigation/native-stack/-/native-stack-7.12.0.tgz#0511234ac6030ed6d716561a380cb2225541373c" + integrity sha512-XmNJsPshjkNsahgbxNgGWQUq4s1l6HqH/Fei4QsjBNn/0mTvVrRVZwJ1XrY9YhWYvyiYkAN6/OmarWQaQJ0otQ== dependencies: - "@react-navigation/elements" "^2.9.4" + "@react-navigation/elements" "^2.9.5" color "^4.2.3" sf-symbols-typescript "^2.1.0" warn-once "^0.1.1" -"@react-navigation/native@7.1.27": - version "7.1.27" - resolved "https://registry.yarnpkg.com/@react-navigation/native/-/native-7.1.27.tgz#5be53e9fac9f6173d229b334fe41c2208684bac2" - integrity sha512-kW7LGP/RrisktpGyizTKw6HwSeQJdXnAN9L8GyQJcJAlgL9YtfEg6yEyD5n9RWH90CL8G0cRyUhphKIAFf4lVw== +"@react-navigation/native@7.1.28": + version "7.1.28" + resolved "https://registry.yarnpkg.com/@react-navigation/native/-/native-7.1.28.tgz#1ee75cf3a8b3e4365f94c5d657bb3c015e387720" + integrity sha512-d1QDn+KNHfHGt3UIwOZvupvdsDdiHYZBEj7+wL2yDVo3tMezamYy60H9s3EnNVE1Ae1ty0trc7F2OKqo/RmsdQ== dependencies: - "@react-navigation/core" "^7.13.7" + "@react-navigation/core" "^7.14.0" escape-string-regexp "^4.0.0" fast-deep-equal "^3.1.3" nanoid "^3.3.11" @@ -2829,6 +3040,11 @@ estree-walker "^2.0.2" picomatch "^4.0.2" +"@sec-ant/readable-stream@^0.4.1": + version "0.4.1" + resolved "https://registry.yarnpkg.com/@sec-ant/readable-stream/-/readable-stream-0.4.1.tgz#60de891bb126abfdc5410fdc6166aca065f10a0c" + integrity sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg== + "@sideway/address@^4.1.5": version "4.1.5" resolved "https://registry.yarnpkg.com/@sideway/address/-/address-4.1.5.tgz#4bc149a0076623ced99ca8208ba780d65a99b9d5" @@ -2847,15 +3063,20 @@ integrity sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ== "@sinclair/typebox@^0.27.8": - version "0.27.8" - resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e" - integrity sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA== + version "0.27.10" + resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.27.10.tgz#beefe675f1853f73676aecc915b2bd2ac98c4fc6" + integrity sha512-MTBk/3jGLNB2tVxv6uLlFh1iu64iYOQ2PbdOSK3NW8JZsmlaOh2q6sdtKowBhfw8QFLmYNzTW4/oK4uATIi6ZA== "@sindresorhus/is@^4.0.0": version "4.6.0" resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-4.6.0.tgz#3c7c9c46e678feefe7a2e5bb609d3dbd665ffb3f" integrity sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw== +"@sindresorhus/is@^7.0.1": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-7.2.0.tgz#7c594e1a64336d2008d99d814056d459421504d4" + integrity sha512-P1Cz1dWaFfR4IR+U13mqqiGsLFf1KbayybWwdd2vfctdV6hDpUkgCY0nKOLLTMSoRd/jJNjtbqzf13K8DCCXQw== + "@sinonjs/commons@^3.0.0": version "3.0.1" resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-3.0.1.tgz#1029357e44ca901a615585f6d27738dbc89084cd" @@ -2972,9 +3193,9 @@ integrity sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w== "@types/express-serve-static-core@*", "@types/express-serve-static-core@^5.0.0": - version "5.1.0" - resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-5.1.0.tgz#74f47555b3d804b54cb7030e6f9aa0c7485cfc5b" - integrity sha512-jnHMsrd0Mwa9Cf4IdOzbz543y4XJepXrbia2T4b6+spXC2We3t1y6K44D3mR8XMFSXMCf3/l7rCgddfx7UNVBA== + version "5.1.1" + resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-5.1.1.tgz#1a77faffee9572d39124933259be2523837d7eaa" + integrity sha512-v4zIMr/cX7/d2BpAEX3KNKL/JrT1s43s96lLvvdTmza1oEvDudCqK9aF/djc/SWgy8Yh0h30TZx5VpzqFCxk5A== dependencies: "@types/node" "*" "@types/qs" "*" @@ -2982,9 +3203,9 @@ "@types/send" "*" "@types/express-serve-static-core@^4.17.21", "@types/express-serve-static-core@^4.17.33": - version "4.19.7" - resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.19.7.tgz#f1d306dcc03b1aafbfb6b4fe684cce8a31cffc10" - integrity sha512-FvPtiIf1LfhzsaIXhv/PHan/2FeQBbtBDtfX2QfvPxdUelMDEckK08SM6nqo1MIZY3RUlfA+HV8+hFUSio78qg== + version "4.19.8" + resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.19.8.tgz#99b960322a4d576b239a640ab52ef191989b036f" + integrity sha512-02S5fmqeoKzVZCHPZid4b8JH2eM5HzQLZWN2FohQEy/0eXTq8VXZfSN6Pcr3F6N9R/vNrj7cpgbhjie6m/1tCA== dependencies: "@types/node" "*" "@types/qs" "*" @@ -2992,13 +3213,13 @@ "@types/send" "*" "@types/express@*": - version "5.0.5" - resolved "https://registry.yarnpkg.com/@types/express/-/express-5.0.5.tgz#3ba069177caa34ab96585ca23b3984d752300cdc" - integrity sha512-LuIQOcb6UmnF7C1PCFmEU1u2hmiHL43fgFQX67sN3H4Z+0Yk0Neo++mFsBjhOAuLzvlQeqAAkeDOZrJs9rzumQ== + version "5.0.6" + resolved "https://registry.yarnpkg.com/@types/express/-/express-5.0.6.tgz#2d724b2c990dcb8c8444063f3580a903f6d500cc" + integrity sha512-sKYVuV7Sv9fbPIt/442koC7+IIwK5olP1KWeD88e/idgoJqDm3JV/YUiPwkoKK92ylff2MGxSz1CSjsXelx0YA== dependencies: "@types/body-parser" "*" "@types/express-serve-static-core" "^5.0.0" - "@types/serve-static" "^1" + "@types/serve-static" "^2" "@types/express@^4.17.25": version "4.17.25" @@ -3032,10 +3253,10 @@ resolved "https://registry.yarnpkg.com/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz#4fc33a00c1d0c16987b1a20cf92d20614c55ac35" integrity sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg== -"@types/http-cache-semantics@*": - version "4.0.4" - resolved "https://registry.yarnpkg.com/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz#b979ebad3919799c979b17c72621c0bc0a31c6c4" - integrity sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA== +"@types/http-cache-semantics@*", "@types/http-cache-semantics@^4.0.4": + version "4.2.0" + resolved "https://registry.yarnpkg.com/@types/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz#f6a7788f438cbfde15f29acad46512b4c01913b3" + integrity sha512-L3LgimLHXtGkWikKnsPg0/VFx9OGZaC+eN1u4r+OB1XRqH3meBIAVC2zr1WdMH+RHmnRkqliQAOHNJ/E0j/e0Q== "@types/http-errors@*": version "2.0.5" @@ -3087,12 +3308,7 @@ dependencies: "@types/lodash" "*" -"@types/lodash@*": - version "4.17.20" - resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.17.20.tgz#1ca77361d7363432d29f5e55950d9ec1e1c6ea93" - integrity sha512-H3MHACvFUEiujabxhaI/ImO6gUrd8oOurg7LQtS7mbwIXA/cUqWrvBsaeJ23aZEPk1TAYkurjfMbSELfoCXlGA== - -"@types/lodash@4.17.23": +"@types/lodash@*", "@types/lodash@4.17.23": version "4.17.23" resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.17.23.tgz#c1bb06db218acc8fc232da0447473fc2fb9d9841" integrity sha512-RDvF6wTulMPjrNdCoYRC8gNR880JNGT8uB+REUpC2Ns4pRqQJhGz90wh7rgdXDPpCczF3VGktDuFGVnz8zP7HA== @@ -3103,25 +3319,25 @@ integrity sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w== "@types/node@*": - version "24.10.1" - resolved "https://registry.yarnpkg.com/@types/node/-/node-24.10.1.tgz#91e92182c93db8bd6224fca031e2370cef9a8f01" - integrity sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ== + version "25.2.3" + resolved "https://registry.yarnpkg.com/@types/node/-/node-25.2.3.tgz#9c18245be768bdb4ce631566c7da303a5c99a7f8" + integrity sha512-m0jEgYlYz+mDJZ2+F4v8D1AyQb+QzsNqRuI7xg1VQX/KlKS0qT9r1Mo16yo5F/MtifXFgaofIFsdFMox2SxIbQ== dependencies: undici-types "~7.16.0" "@types/node@^20.17.9": - version "20.19.25" - resolved "https://registry.yarnpkg.com/@types/node/-/node-20.19.25.tgz#467da94a2fd966b57cc39c357247d68047611190" - integrity sha512-ZsJzA5thDQMSQO788d7IocwwQbI8B5OPzmqNvpf3NY/+MHDAS759Wo0gd2WQeXYt5AAAQjzcrTVC6SKCuYgoCQ== + version "20.19.33" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.19.33.tgz#ac8364c623b72d43125f0e7dd722bbe968f0c65e" + integrity sha512-Rs1bVAIdBs5gbTIKza/tgpMuG1k3U/UMJLWecIMxNdJFDMzcM5LOiLVRYh3PilWEYDIeUDv7bpiHPLPsbydGcw== dependencies: undici-types "~6.21.0" -"@types/node@^22.7.7": - version "22.19.1" - resolved "https://registry.yarnpkg.com/@types/node/-/node-22.19.1.tgz#1188f1ddc9f46b4cc3aec76749050b4e1f459b7b" - integrity sha512-LCCV0HdSZZZb34qifBsyWlUmok6W7ouER+oQIGBScS8EsZsQbrtFTUrDX4hOl+CS6p7cnNC4td+qrSVGSCTUfQ== +"@types/node@^24.9.0": + version "24.10.13" + resolved "https://registry.yarnpkg.com/@types/node/-/node-24.10.13.tgz#2fac25c0e30f3848e19912c3b8791a28370e9e07" + integrity sha512-oH72nZRfDv9lADUBSo104Aq7gPHpQZc4BTx38r9xf9pg5LfP6EzSyH2n7qFmmxRQXh7YlUXODcYsg6PuTDSxGg== dependencies: - undici-types "~6.21.0" + undici-types "~7.16.0" "@types/qs@*": version "6.14.0" @@ -3164,10 +3380,10 @@ resolved "https://registry.yarnpkg.com/@types/react-reconciler/-/react-reconciler-0.28.9.tgz#d24b4864c384e770c83275b3fe73fba00269c83b" integrity sha512-HHM3nxyUZ3zAylX8ZEyrDNd2XZOnQ0D5XfunJF5FLQnZbHHYq4UWvW1QfelQNXv1ICNkwYhfxjwfnqivYB6bFg== -"@types/react@*", "@types/react@19.2.8": - version "19.2.8" - resolved "https://registry.yarnpkg.com/@types/react/-/react-19.2.8.tgz#307011c9f5973a6abab8e17d0293f48843627994" - integrity sha512-3MbSL37jEchWZz2p2mjntRZtPt837ij10ApxKfgmXCTuHWagYg7iA5bqPw6C8BMPfwidlvfPI/fxOc42HLhcyg== +"@types/react@*", "@types/react@19.2.14": + version "19.2.14" + resolved "https://registry.yarnpkg.com/@types/react/-/react-19.2.14.tgz#39604929b5e3957e3a6fa0001dafb17c7af70bad" + integrity sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w== dependencies: csstype "^3.2.2" @@ -3219,6 +3435,14 @@ "@types/node" "*" "@types/send" "<1" +"@types/serve-static@^2": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-2.2.0.tgz#d4a447503ead0d1671132d1ab6bd58b805d8de6a" + integrity sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ== + dependencies: + "@types/http-errors" "*" + "@types/node" "*" + "@types/shallowequal@1.1.5": version "1.1.5" resolved "https://registry.yarnpkg.com/@types/shallowequal/-/shallowequal-1.1.5.tgz#37e4871c464981b4abee74990c73c8f414cd13dd" @@ -3267,16 +3491,16 @@ dependencies: "@types/node" "*" -"@typescript-eslint/eslint-plugin@8.53.0": - version "8.53.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.53.0.tgz#afb966c66a2fdc6158cf81118204a971a36d0fc5" - integrity sha512-eEXsVvLPu8Z4PkFibtuFJLJOTAV/nPdgtSjkGoPpddpFk3/ym2oy97jynY6ic2m6+nc5M8SE1e9v/mHKsulcJg== +"@typescript-eslint/eslint-plugin@8.55.0": + version "8.55.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.55.0.tgz#086d2ef661507b561f7b17f62d3179d692a0765f" + integrity sha512-1y/MVSz0NglV1ijHC8OT49mPJ4qhPYjiK08YUQVbIOyu+5k862LKUHFkpKHWu//zmr7hDR2rhwUm6gnCGNmGBQ== dependencies: "@eslint-community/regexpp" "^4.12.2" - "@typescript-eslint/scope-manager" "8.53.0" - "@typescript-eslint/type-utils" "8.53.0" - "@typescript-eslint/utils" "8.53.0" - "@typescript-eslint/visitor-keys" "8.53.0" + "@typescript-eslint/scope-manager" "8.55.0" + "@typescript-eslint/type-utils" "8.55.0" + "@typescript-eslint/utils" "8.55.0" + "@typescript-eslint/visitor-keys" "8.55.0" ignore "^7.0.5" natural-compare "^1.4.0" ts-api-utils "^2.4.0" @@ -3296,15 +3520,15 @@ natural-compare "^1.4.0" ts-api-utils "^1.3.0" -"@typescript-eslint/parser@8.53.0": - version "8.53.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.53.0.tgz#d8bed6f12dc74e03751e5f947510ff2b165990c6" - integrity sha512-npiaib8XzbjtzS2N4HlqPvlpxpmZ14FjSJrteZpPxGUaYPlvhzlzUZ4mZyABo0EFrOWnvyd0Xxroq//hKhtAWg== +"@typescript-eslint/parser@8.55.0": + version "8.55.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.55.0.tgz#6eace4e9e95f178d3447ed1f17f3d6a5dfdb345c" + integrity sha512-4z2nCSBfVIMnbuu8uinj+f0o4qOeggYJLbjpPHka3KH1om7e+H9yLKTYgksTaHcGco+NClhhY2vyO3HsMH1RGw== dependencies: - "@typescript-eslint/scope-manager" "8.53.0" - "@typescript-eslint/types" "8.53.0" - "@typescript-eslint/typescript-estree" "8.53.0" - "@typescript-eslint/visitor-keys" "8.53.0" + "@typescript-eslint/scope-manager" "8.55.0" + "@typescript-eslint/types" "8.55.0" + "@typescript-eslint/typescript-estree" "8.55.0" + "@typescript-eslint/visitor-keys" "8.55.0" debug "^4.4.3" "@typescript-eslint/parser@^7.1.1": @@ -3318,13 +3542,13 @@ "@typescript-eslint/visitor-keys" "7.18.0" debug "^4.3.4" -"@typescript-eslint/project-service@8.53.0": - version "8.53.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/project-service/-/project-service-8.53.0.tgz#327c67c61c16a1c8b12a440b0779b41eb77cc7df" - integrity sha512-Bl6Gdr7NqkqIP5yP9z1JU///Nmes4Eose6L1HwpuVHwScgDPPuEWbUVhvlZmb8hy0vX9syLk5EGNL700WcBlbg== +"@typescript-eslint/project-service@8.55.0": + version "8.55.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/project-service/-/project-service-8.55.0.tgz#b8a71c06a625bdad481c24d5614b68e252f3ae9b" + integrity sha512-zRcVVPFUYWa3kNnjaZGXSu3xkKV1zXy8M4nO/pElzQhFweb7PPtluDLQtKArEOGmjXoRjnUZ29NjOiF0eCDkcQ== dependencies: - "@typescript-eslint/tsconfig-utils" "^8.53.0" - "@typescript-eslint/types" "^8.53.0" + "@typescript-eslint/tsconfig-utils" "^8.55.0" + "@typescript-eslint/types" "^8.55.0" debug "^4.4.3" "@typescript-eslint/scope-manager@5.62.0": @@ -3343,18 +3567,18 @@ "@typescript-eslint/types" "7.18.0" "@typescript-eslint/visitor-keys" "7.18.0" -"@typescript-eslint/scope-manager@8.53.0": - version "8.53.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.53.0.tgz#f922fcbf0d42e72f065297af31779ccf19de9a97" - integrity sha512-kWNj3l01eOGSdVBnfAF2K1BTh06WS0Yet6JUgb9Cmkqaz3Jlu0fdVUjj9UI8gPidBWSMqDIglmEXifSgDT/D0g== +"@typescript-eslint/scope-manager@8.55.0": + version "8.55.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.55.0.tgz#8a0752c31c788651840dc98f840b0c2ebe143b8c" + integrity sha512-fVu5Omrd3jeqeQLiB9f1YsuK/iHFOwb04bCtY4BSCLgjNbOD33ZdV6KyEqplHr+IlpgT0QTZ/iJ+wT7hvTx49Q== dependencies: - "@typescript-eslint/types" "8.53.0" - "@typescript-eslint/visitor-keys" "8.53.0" + "@typescript-eslint/types" "8.55.0" + "@typescript-eslint/visitor-keys" "8.55.0" -"@typescript-eslint/tsconfig-utils@8.53.0", "@typescript-eslint/tsconfig-utils@^8.53.0": - version "8.53.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.53.0.tgz#105279d7969a7abdc8345cc9c57cff83cf910f8f" - integrity sha512-K6Sc0R5GIG6dNoPdOooQ+KtvT5KCKAvTcY8h2rIuul19vxH5OTQk7ArKkd4yTzkw66WnNY0kPPzzcmWA+XRmiA== +"@typescript-eslint/tsconfig-utils@8.55.0", "@typescript-eslint/tsconfig-utils@^8.55.0": + version "8.55.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.55.0.tgz#62f1d005419985e09d37a040b2f1450e4e805afa" + integrity sha512-1R9cXqY7RQd7WuqSN47PK9EDpgFUK3VqdmbYrvWJZYDd0cavROGn+74ktWBlmJ13NXUQKlZ/iAEQHI/V0kKe0Q== "@typescript-eslint/type-utils@7.18.0": version "7.18.0" @@ -3366,14 +3590,14 @@ debug "^4.3.4" ts-api-utils "^1.3.0" -"@typescript-eslint/type-utils@8.53.0": - version "8.53.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.53.0.tgz#81a0de5c01fc68f6df0591d03cd8226bda01c91f" - integrity sha512-BBAUhlx7g4SmcLhn8cnbxoxtmS7hcq39xKCgiutL3oNx1TaIp+cny51s8ewnKMpVUKQUGb41RAUWZ9kxYdovuw== +"@typescript-eslint/type-utils@8.55.0": + version "8.55.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.55.0.tgz#195d854b3e56308ce475fdea2165313bb1190200" + integrity sha512-x1iH2unH4qAt6I37I2CGlsNs+B9WGxurP2uyZLRz6UJoZWDBx9cJL1xVN/FiOmHEONEg6RIufdvyT0TEYIgC5g== dependencies: - "@typescript-eslint/types" "8.53.0" - "@typescript-eslint/typescript-estree" "8.53.0" - "@typescript-eslint/utils" "8.53.0" + "@typescript-eslint/types" "8.55.0" + "@typescript-eslint/typescript-estree" "8.55.0" + "@typescript-eslint/utils" "8.55.0" debug "^4.4.3" ts-api-utils "^2.4.0" @@ -3387,10 +3611,10 @@ resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-7.18.0.tgz#b90a57ccdea71797ffffa0321e744f379ec838c9" integrity sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ== -"@typescript-eslint/types@8.53.0", "@typescript-eslint/types@^8.53.0": - version "8.53.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.53.0.tgz#1adcad3fa32bc2c4cbf3785ba07a5e3151819efb" - integrity sha512-Bmh9KX31Vlxa13+PqPvt4RzKRN1XORYSLlAE+sO1i28NkisGbTtSLFVB3l7PWdHtR3E0mVMuC7JilWJ99m2HxQ== +"@typescript-eslint/types@8.55.0", "@typescript-eslint/types@^8.55.0": + version "8.55.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.55.0.tgz#8449c5a7adac61184cac92dbf6315733569708c2" + integrity sha512-ujT0Je8GI5BJWi+/mMoR0wxwVEQaxM+pi30xuMiJETlX80OPovb2p9E8ss87gnSVtYXtJoU9U1Cowcr6w2FE0w== "@typescript-eslint/typescript-estree@5.62.0": version "5.62.0" @@ -3419,15 +3643,15 @@ semver "^7.6.0" ts-api-utils "^1.3.0" -"@typescript-eslint/typescript-estree@8.53.0": - version "8.53.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.53.0.tgz#7805b46b7a8ce97e91b7bb56fc8b1ba26ca8ef52" - integrity sha512-pw0c0Gdo7Z4xOG987u3nJ8akL9093yEEKv8QTJ+Bhkghj1xyj8cgPaavlr9rq8h7+s6plUJ4QJYw2gCZodqmGw== +"@typescript-eslint/typescript-estree@8.55.0": + version "8.55.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.55.0.tgz#c83ac92c11ce79bedd984937c7780a65e7f7b2e3" + integrity sha512-EwrH67bSWdx/3aRQhCoxDaHM+CrZjotc2UCCpEDVqfCE+7OjKAGWNY2HsCSTEVvWH2clYQK8pdeLp42EVs+xQw== dependencies: - "@typescript-eslint/project-service" "8.53.0" - "@typescript-eslint/tsconfig-utils" "8.53.0" - "@typescript-eslint/types" "8.53.0" - "@typescript-eslint/visitor-keys" "8.53.0" + "@typescript-eslint/project-service" "8.55.0" + "@typescript-eslint/tsconfig-utils" "8.55.0" + "@typescript-eslint/types" "8.55.0" + "@typescript-eslint/visitor-keys" "8.55.0" debug "^4.4.3" minimatch "^9.0.5" semver "^7.7.3" @@ -3444,15 +3668,15 @@ "@typescript-eslint/types" "7.18.0" "@typescript-eslint/typescript-estree" "7.18.0" -"@typescript-eslint/utils@8.53.0": - version "8.53.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.53.0.tgz#bf0a4e2edaf1afc9abce209fc02f8cab0b74af13" - integrity sha512-XDY4mXTez3Z1iRDI5mbRhH4DFSt46oaIFsLg+Zn97+sYrXACziXSQcSelMybnVZ5pa1P6xYkPr5cMJyunM1ZDA== +"@typescript-eslint/utils@8.55.0": + version "8.55.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.55.0.tgz#c1744d94a3901deb01f58b09d3478d811f96d619" + integrity sha512-BqZEsnPGdYpgyEIkDC1BadNY8oMwckftxBT+C8W0g1iKPdeqKZBtTfnvcq0nf60u7MkjFO8RBvpRGZBPw4L2ow== dependencies: "@eslint-community/eslint-utils" "^4.9.1" - "@typescript-eslint/scope-manager" "8.53.0" - "@typescript-eslint/types" "8.53.0" - "@typescript-eslint/typescript-estree" "8.53.0" + "@typescript-eslint/scope-manager" "8.55.0" + "@typescript-eslint/types" "8.55.0" + "@typescript-eslint/typescript-estree" "8.55.0" "@typescript-eslint/utils@^5.10.0": version "5.62.0" @@ -3484,12 +3708,12 @@ "@typescript-eslint/types" "7.18.0" eslint-visitor-keys "^3.4.3" -"@typescript-eslint/visitor-keys@8.53.0": - version "8.53.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.53.0.tgz#9a785664ddae7e3f7e570ad8166e48dbc9c6cf02" - integrity sha512-LZ2NqIHFhvFwxG0qZeLL9DvdNAHPGCY5dIRwBhyYeU+LfLhcStE1ImjsuTG/WaVh3XysGaeLW8Rqq7cGkPCFvw== +"@typescript-eslint/visitor-keys@8.55.0": + version "8.55.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.55.0.tgz#3d9a40fd4e3705c63d8fae3af58988add3ed464d" + integrity sha512-AxNRwEie8Nn4eFS1FzDMJWIISMGoXMb037sgCBJ3UR6o0fQTzr2tqN9WT+DkWJPhIdQCfV7T6D387566VtnCJA== dependencies: - "@typescript-eslint/types" "8.53.0" + "@typescript-eslint/types" "8.55.0" eslint-visitor-keys "^4.2.1" "@ungap/structured-clone@^1.3.0": @@ -3514,9 +3738,9 @@ wonka "^6.3.2" "@vscode/sudo-prompt@^9.0.0": - version "9.3.1" - resolved "https://registry.yarnpkg.com/@vscode/sudo-prompt/-/sudo-prompt-9.3.1.tgz#c562334bc6647733649fd42afc96c0eea8de3b65" - integrity sha512-9ORTwwS74VaTn38tNbQhsA5U44zkJfcb0BdTSyyG6frP4e8KMtHuTXYmwefe5dpL8XB1aGSIVTaLjD3BbWb5iA== + version "9.3.2" + resolved "https://registry.yarnpkg.com/@vscode/sudo-prompt/-/sudo-prompt-9.3.2.tgz#692ba38df40bd3502ccc4e9f099fbbaedbd5f04e" + integrity sha512-gcXoCN00METUNFeQOFJ+C9xUI0DKB+0EGMVg7wbVYRHBw2Eq3fKisDZOkRdOz3kqXRKOENMfShPOmypw1/8nOw== "@webassemblyjs/ast@1.14.1", "@webassemblyjs/ast@^1.14.1": version "1.14.1" @@ -3688,7 +3912,7 @@ abort-controller@^3.0.0: dependencies: event-target-shim "^5.0.0" -accepts@^1.3.7, accepts@^1.3.8, accepts@~1.3.4, accepts@~1.3.7, accepts@~1.3.8: +accepts@^1.3.7, accepts@^1.3.8, accepts@~1.3.8: version "1.3.8" resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e" integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw== @@ -3761,9 +3985,9 @@ anser@^1.4.9: integrity sha512-hCv9AqTQ8ycjpSd3upOJd7vFwW1JaoYQ7tpham03GJ1ca8/65rqn0RpaWpItOAd6ylW9wAw6luXYPJIyPFVOww== anser@^2.1.1: - version "2.3.3" - resolved "https://registry.yarnpkg.com/anser/-/anser-2.3.3.tgz#84bca8b8a668ae3c7cf49355affb99494444ccc1" - integrity sha512-QGY1oxYE7/kkeNmbtY/2ZjQ07BCG3zYdz+k/+sf69kMzEIxb93guHkPnIXITQ+BYi61oQwG74twMOX1tD4aesg== + version "2.3.5" + resolved "https://registry.yarnpkg.com/anser/-/anser-2.3.5.tgz#3435896b68b93e5e744842499d0ce3e6f6d013ee" + integrity sha512-vcZjxvvVoxTeR5XBNJB38oTu/7eDCZlwdz32N1eNgpyPF7j/Z7Idf+CUwQOkKKpJ7RJyjxgLHCM7vdIK0iCNMQ== ansi-escapes@^4.2.1: version "4.3.2" @@ -3914,7 +4138,7 @@ array.prototype.flatmap@^1.3.3: es-abstract "^1.23.5" es-shim-unscopables "^1.0.2" -array.prototype.reduce@^1.0.6: +array.prototype.reduce@^1.0.8: version "1.0.8" resolved "https://registry.yarnpkg.com/array.prototype.reduce/-/array.prototype.reduce-1.0.8.tgz#42f97f5078daedca687d4463fd3c05cbfd83da57" integrity sha512-DwuEqgXFBwbmZSRqt3BpQigWNUoqw9Ml2dTWdF3B2zQlQX4OeUE0zyuzX0fX0IbTvjdkZbcBTU3idgpO78qkTw== @@ -3981,11 +4205,6 @@ async-limiter@~1.0.0: resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd" integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ== -at-least-node@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2" - integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg== - author-regex@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/author-regex/-/author-regex-1.0.0.tgz#d08885be6b9bbf9439fe087c76287245f0a81450" @@ -4050,13 +4269,13 @@ babel-plugin-module-resolver@5.0.2: reselect "^4.1.7" resolve "^1.22.8" -babel-plugin-polyfill-corejs2@^0.4.14: - version "0.4.14" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.14.tgz#8101b82b769c568835611542488d463395c2ef8f" - integrity sha512-Co2Y9wX854ts6U8gAAPXfn0GmAyctHuK8n0Yhfjd6t30g7yvKjspvvOo9yG+z52PZRgFErt7Ka2pYnXCjLKEpg== +babel-plugin-polyfill-corejs2@^0.4.14, babel-plugin-polyfill-corejs2@^0.4.15: + version "0.4.15" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.15.tgz#808fa349686eea4741807cfaaa2aa3aa57ce120a" + integrity sha512-hR3GwrRwHUfYwGfrisXPIDP3JcYfBrW7wKE7+Au6wDYl7fm/ka1NEII6kORzxNU556JjfidZeBsO10kYvtV1aw== dependencies: - "@babel/compat-data" "^7.27.7" - "@babel/helper-define-polyfill-provider" "^0.6.5" + "@babel/compat-data" "^7.28.6" + "@babel/helper-define-polyfill-provider" "^0.6.6" semver "^6.3.1" babel-plugin-polyfill-corejs3@^0.13.0: @@ -4067,12 +4286,20 @@ babel-plugin-polyfill-corejs3@^0.13.0: "@babel/helper-define-polyfill-provider" "^0.6.5" core-js-compat "^3.43.0" -babel-plugin-polyfill-regenerator@^0.6.5: - version "0.6.5" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.5.tgz#32752e38ab6f6767b92650347bf26a31b16ae8c5" - integrity sha512-ISqQ2frbiNU9vIJkzg7dlPpznPZ4jOiUQ1uSmB0fEHeowtN3COYRsXr/xexn64NpU13P06jc/L5TgiJXOgrbEg== +babel-plugin-polyfill-corejs3@^0.14.0: + version "0.14.0" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.14.0.tgz#65b06cda48d6e447e1e926681f5a247c6ae2b9cf" + integrity sha512-AvDcMxJ34W4Wgy4KBIIePQTAOP1Ie2WFwkQp3dB7FQ/f0lI5+nM96zUnYEOE1P9sEg0es5VCP0HxiWu5fUHZAQ== dependencies: - "@babel/helper-define-polyfill-provider" "^0.6.5" + "@babel/helper-define-polyfill-provider" "^0.6.6" + core-js-compat "^3.48.0" + +babel-plugin-polyfill-regenerator@^0.6.5, babel-plugin-polyfill-regenerator@^0.6.6: + version "0.6.6" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.6.tgz#69f5dd263cab933c42fe5ea05e83443b374bd4bf" + integrity sha512-hYm+XLYRMvupxiQzrvXUj7YyvFFVfv5gI0R71AJzudg1g2AI2vyCPPIFEBjk162/wFzti3inBHo7isWFuEVS/A== + dependencies: + "@babel/helper-define-polyfill-provider" "^0.6.6" babel-plugin-react-compiler@1.0.0, babel-plugin-react-compiler@^1.0.0: version "1.0.0" @@ -4121,10 +4348,10 @@ babel-preset-current-node-syntax@^1.0.0: "@babel/plugin-syntax-private-property-in-object" "^7.14.5" "@babel/plugin-syntax-top-level-await" "^7.14.5" -babel-preset-expo@54.0.9, babel-preset-expo@~54.0.9: - version "54.0.9" - resolved "https://registry.yarnpkg.com/babel-preset-expo/-/babel-preset-expo-54.0.9.tgz#88af355f08dc49b4b54ac559c02ce8890ab08930" - integrity sha512-8J6hRdgEC2eJobjoft6mKJ294cLxmi3khCUy2JJQp4htOYYkllSLUq6vudWJkTJiIuGdVR4bR6xuz2EvJLWHNg== +babel-preset-expo@54.0.10, babel-preset-expo@~54.0.10: + version "54.0.10" + resolved "https://registry.yarnpkg.com/babel-preset-expo/-/babel-preset-expo-54.0.10.tgz#3b70f4af3a5f65f945d7957ef511ee016e8f2fd6" + integrity sha512-wTt7POavLFypLcPW/uC5v8y+mtQKDJiyGLzYCjqr9tx0Qc3vCXcDKk1iCFIj/++Iy5CWhhTflEa7VvVPNWeCfw== dependencies: "@babel/helper-module-imports" "^7.25.9" "@babel/plugin-proposal-decorators" "^7.12.9" @@ -4167,7 +4394,7 @@ base64-js@^1.2.3, base64-js@^1.3.1, base64-js@^1.5.1: resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== -baseline-browser-mapping@^2.8.25, baseline-browser-mapping@^2.9.0: +baseline-browser-mapping@^2.9.0: version "2.9.19" resolved "https://registry.yarnpkg.com/baseline-browser-mapping/-/baseline-browser-mapping-2.9.19.tgz#3e508c43c46d961eb4d7d2e5b8d1dd0f9ee4f488" integrity sha512-ipDqC8FrAl/76p2SSWKSI+H9tFwm7vYqXQrItCuiVPt26Km0jS+NzSsBWAaBusvSbQcfJG+JitdMm+wZAgTYqg== @@ -4220,25 +4447,7 @@ bluebird@^3.1.1: resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== -body-parser@^1.20.3: - version "1.20.3" - resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.3.tgz#1953431221c6fb5cd63c4b36d53fab0928e548c6" - integrity sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g== - dependencies: - bytes "3.1.2" - content-type "~1.0.5" - debug "2.6.9" - depd "2.0.0" - destroy "1.2.0" - http-errors "2.0.0" - iconv-lite "0.4.24" - on-finished "2.4.1" - qs "6.13.0" - raw-body "2.5.2" - type-is "~1.6.18" - unpipe "1.0.0" - -body-parser@~1.20.3: +body-parser@^1.20.3, body-parser@~1.20.3: version "1.20.4" resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.4.tgz#f8e20f4d06ca8a50a71ed329c15dccad1cdc547f" integrity sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA== @@ -4317,18 +4526,7 @@ braces@^3.0.3, braces@~3.0.2: dependencies: fill-range "^7.1.1" -browserslist@^4.24.0, browserslist@^4.25.0, browserslist@^4.28.0: - version "4.28.0" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.28.0.tgz#9cefece0a386a17a3cd3d22ebf67b9deca1b5929" - integrity sha512-tbydkR/CxfMwelN0vwdP/pLkDwyAASZ+VfWm4EOwlB6SWhx1sYnWLqo8N5j0rAzPfzfRaxt0mM/4wPU/Su84RQ== - dependencies: - baseline-browser-mapping "^2.8.25" - caniuse-lite "^1.0.30001754" - electron-to-chromium "^1.5.249" - node-releases "^2.0.27" - update-browserslist-db "^1.1.4" - -browserslist@^4.28.1: +browserslist@^4.24.0, browserslist@^4.25.0, browserslist@^4.28.1: version "4.28.1" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.28.1.tgz#7f534594628c53c63101079e27e40de490456a95" integrity sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA== @@ -4371,6 +4569,11 @@ bundle-name@^4.1.0: dependencies: run-applescript "^7.0.0" +byte-counter@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/byte-counter/-/byte-counter-0.1.0.tgz#c49760b5790e50e942a0d57a57b3fc0e94488dcc" + integrity sha512-jheRLVMeUKrDBjVw2O5+k4EvR4t9wtxHL+bo/LxfkxsVeuGMy3a5SEGgXdAFA4FSzTrU8rQXQIrsZ3oBq5a0pQ== + bytes@3.1.2, bytes@~3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" @@ -4386,6 +4589,24 @@ cacheable-lookup@^5.0.3: resolved "https://registry.yarnpkg.com/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz#5a6b865b2c44357be3d5ebc2a467b032719a7005" integrity sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA== +cacheable-lookup@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/cacheable-lookup/-/cacheable-lookup-7.0.0.tgz#3476a8215d046e5a3202a9209dd13fec1f933a27" + integrity sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w== + +cacheable-request@^13.0.12: + version "13.0.18" + resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-13.0.18.tgz#b256c499df6c266d3c2ed075009f54e6d08fe431" + integrity sha512-rFWadDRKJs3s2eYdXlGggnBZKG7MTblkFBB0YllFds+UYnfogDp2wcR6JN97FhRkHTvq59n2vhNoHNZn29dh/Q== + dependencies: + "@types/http-cache-semantics" "^4.0.4" + get-stream "^9.0.1" + http-cache-semantics "^4.2.0" + keyv "^5.5.5" + mimic-response "^4.0.0" + normalize-url "^8.1.1" + responselike "^4.0.2" + cacheable-request@^7.0.2: version "7.0.4" resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-7.0.4.tgz#7a33ebf08613178b403635be7b899d3e69bbe817" @@ -4448,15 +4669,10 @@ camelcase@^6.2.0: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== -caniuse-lite@^1.0.30001754: - version "1.0.30001755" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001755.tgz#c01cfb1c30f5acf1229391666ec03492f4c332ff" - integrity sha512-44V+Jm6ctPj7R52Na4TLi3Zri4dWUljJd+RDm+j8LtNCc/ihLCT+X1TzoOAkRETEWqjuLnh9581Tl80FvK7jVA== - caniuse-lite@^1.0.30001759: - version "1.0.30001760" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001760.tgz#bdd1960fafedf8d5f04ff16e81460506ff9b798f" - integrity sha512-7AAMPcueWELt1p3mi13HR/LHH0TJLT11cnwDJEs3xA4+CK/PLKeO9Kl1oru24htkyUKtkGCvAx4ohB0Ttry8Dw== + version "1.0.30001769" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001769.tgz#1ad91594fad7dc233777c2781879ab5409f7d9c2" + integrity sha512-BCfFL1sHijQlBGWBMuJyhZUhzo7wer5sVj9hqekB/7xn0Ypy+pER/edCYQm4exbXj4WiySGp40P8UuTh6w1srg== chalk@^2.0.1, chalk@^2.4.2: version "2.4.2" @@ -4532,11 +4748,6 @@ ci-info@^3.2.0, ci-info@^3.3.0, ci-info@^3.7.0: resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.9.0.tgz#4279a62028a7b1f262f3473fc9605f5e218c59b4" integrity sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ== -circular-dependency-plugin@5.2.2: - version "5.2.2" - resolved "https://registry.yarnpkg.com/circular-dependency-plugin/-/circular-dependency-plugin-5.2.2.tgz#39e836079db1d3cf2f988dc48c5188a44058b600" - integrity sha512-g38K9Cm5WRwlaH6g03B9OEz/0qRizI+2I7n+Gz+L5DxXJAPAiWQvwlYNm1V1jkdpUv95bOe/ASm2vfi/G560jQ== - classnames@2.5.1: version "2.5.1" resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.5.1.tgz#ba774c614be0f016da105c858e7159eae8e7687b" @@ -4667,6 +4878,11 @@ commander@^12.0.0, commander@^12.1.0: resolved "https://registry.yarnpkg.com/commander/-/commander-12.1.0.tgz#01423b36f501259fdaac4d0e4d60c96c991585d3" integrity sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA== +commander@^13.1.0: + version "13.1.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-13.1.0.tgz#776167db68c78f38dcce1f9b8d7b8b9a488abf46" + integrity sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw== + commander@^2.20.0: version "2.20.3" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" @@ -4677,11 +4893,6 @@ commander@^4.0.0: resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068" integrity sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA== -commander@^5.0.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-5.1.0.tgz#46abbd1652f8e059bddaef99bbdcb2ad9cf179ae" - integrity sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg== - commander@^6.2.0: version "6.2.1" resolved "https://registry.yarnpkg.com/commander/-/commander-6.2.1.tgz#0792eb682dfbc325999bb2b84fddddba110ac73c" @@ -4707,11 +4918,6 @@ commondir@^1.0.1: resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" integrity sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg== -compare-version@^0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/compare-version/-/compare-version-0.1.2.tgz#0162ec2d9351f5ddd59a9202cba935366a725080" - integrity sha512-pJDh5/4wrEnXX/VWRZvruAGHkzKdr46z11OlTPN+VrATlWWhSKewNCJ1futCO5C7eJB3nPMFZA1LeYtcFboZ2A== - compressible@~2.0.18: version "2.0.18" resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.18.tgz#af53cca6b070d4c3c0750fbd77286a6d7cc46fba" @@ -4779,22 +4985,22 @@ cookie@~0.7.1: resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.7.2.tgz#556369c472a2ba910f2979891b526b3436237ed7" integrity sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w== -core-js-compat@^3.43.0: - version "3.47.0" - resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.47.0.tgz#698224bbdbb6f2e3f39decdda4147b161e3772a3" - integrity sha512-IGfuznZ/n7Kp9+nypamBhvwdwLsW6KC8IOaURw2doAK5e98AG3acVLdh0woOnEqCfUtS+Vu882JE4k/DAm3ItQ== +core-js-compat@^3.43.0, core-js-compat@^3.48.0: + version "3.48.0" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.48.0.tgz#7efbe1fc1cbad44008190462217cc5558adaeaa6" + integrity sha512-OM4cAF3D6VtH/WkLtWvyNC56EZVXsZdU3iqaMG2B4WvYrlqU831pc4UtG5yp0sE9z8Y02wVN7PjW5Zf9Gt0f1Q== dependencies: - browserslist "^4.28.0" + browserslist "^4.28.1" core-js-pure@^3.23.3: - version "3.47.0" - resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.47.0.tgz#1104df8a3b6eb9189fcc559b5a65b90f66e7e887" - integrity sha512-BcxeDbzUrRnXGYIVAGFtcGQVNpFcUhVjr6W7F8XktvQW2iJP9e66GP6xdKotCRFlrxBvNIBrhwKteRXqMV86Nw== + version "3.48.0" + resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.48.0.tgz#7d5a3fe1ec3631b9aa76a81c843ac2ce918e5023" + integrity sha512-1slJgk89tWC51HQ1AEqG+s2VuwpTRr8ocu4n20QUcH1v9lAN0RXen0Q0AABa/DK1I7RrNWLucplOHMx8hfTGTw== -core-js@^3.30.2: - version "3.47.0" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.47.0.tgz#436ef07650e191afeb84c24481b298bd60eb4a17" - integrity sha512-c3Q2VVkGAUyupsjRnaNX6u8Dq2vAdzm9iuPj5FW0fRxzlxgq9Q39MDq10IvmQSpLgHQNyQzQmOo6bgGHmH3NNg== +core-js@^3.48.0: + version "3.48.0" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.48.0.tgz#1f813220a47bbf0e667e3885c36cd6f0593bf14d" + integrity sha512-zpEHTy1fjTMZCKLHUZoVeylt9XrzaIN2rbPXEt0k+q7JE5CkCZdo6bNq55bn24a69CH7ErAVLKijxJja4fw+UQ== core-util-is@~1.0.0: version "1.0.3" @@ -4811,17 +5017,13 @@ cosmiconfig@^9.0.0: js-yaml "^4.1.0" parse-json "^5.2.0" -cross-dirname@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/cross-dirname/-/cross-dirname-0.1.0.tgz#b899599f30a5389f59e78c150e19f957ad16a37c" - integrity sha512-+R08/oI0nl3vfPcqftZRpytksBXDzOUveBq/NBVx0sUp1axwzPQrKinNx5yd5sxPu8j1wIy8AfnVQ+5eFdha6Q== - -cross-env@7.0.3: - version "7.0.3" - resolved "https://registry.yarnpkg.com/cross-env/-/cross-env-7.0.3.tgz#865264b29677dc015ba8418918965dd232fc54cf" - integrity sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw== +cross-env@10.1.0: + version "10.1.0" + resolved "https://registry.yarnpkg.com/cross-env/-/cross-env-10.1.0.tgz#cfd2a6200df9ed75bfb9cb3d7ce609c13ea21783" + integrity sha512-GsYosgnACZTADcmEyJctkJIoqAhHjttw7RsFrVoJNXbsWWqaq6Ym+7kZjq6mS45O0jij6vtiReppKQEtqWy6Dw== dependencies: - cross-spawn "^7.0.1" + "@epic-web/invariant" "^1.0.0" + cross-spawn "^7.0.6" cross-fetch@^3.1.5: version "3.2.0" @@ -4851,19 +5053,19 @@ css-in-js-utils@^3.1.0: dependencies: hyphenate-style-name "^1.0.3" -css-loader@7.1.2: - version "7.1.2" - resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-7.1.2.tgz#64671541c6efe06b0e22e750503106bdd86880f8" - integrity sha512-6WvYYn7l/XEGN8Xu2vWFt9nVzrCn39vKyTEFf/ExEyoksJjjSZV/0/35XPlMbpnr6VGhZIUg5yJrL8tGfes/FA== +css-loader@7.1.3: + version "7.1.3" + resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-7.1.3.tgz#c0de715ceabe39b8531a85fcaf6734a430c4d99a" + integrity sha512-frbERmjT0UC5lMheWpJmMilnt9GEhbZJN/heUb7/zaJYeIzj5St9HvDcfshzzOqbsS+rYpMk++2SD3vGETDSyA== dependencies: icss-utils "^5.1.0" - postcss "^8.4.33" + postcss "^8.4.40" postcss-modules-extract-imports "^3.1.0" postcss-modules-local-by-default "^4.0.5" postcss-modules-scope "^3.2.0" postcss-modules-values "^4.0.0" postcss-value-parser "^4.2.0" - semver "^7.5.4" + semver "^7.6.3" css-select@^4.1.3: version "4.3.0" @@ -4935,7 +5137,7 @@ debug@2.6.9, debug@^2.2.0, debug@^2.6.9: dependencies: ms "2.0.0" -debug@4, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4, debug@^4.3.5, debug@^4.4.0, debug@^4.4.1, debug@^4.4.3: +debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4, debug@^4.3.5, debug@^4.4.0, debug@^4.4.1, debug@^4.4.3: version "4.4.3" resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.3.tgz#c6ae432d9bd9662582fce08709b038c58e9e3d6a" integrity sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA== @@ -4959,6 +5161,13 @@ decode-uri-component@^0.2.2: resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.2.tgz#e69dbe25d37941171dd540e024c444cd5188e1e9" integrity sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ== +decompress-response@^10.0.0: + version "10.0.0" + resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-10.0.0.tgz#d8abd2a4c136c3b99b49a08d1f9a709fe35675a4" + integrity sha512-oj7KWToJuuxlPr7VV0vabvxEIiqNMo+q0NueIiL3XhtwC6FVOX7Hr1c0C4eD0bmf7Zr+S/dSf2xvkH3Ad6sU3Q== + dependencies: + mimic-response "^4.0.0" + decompress-response@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-6.0.0.tgz#ca387612ddb7e104bd16d85aab00d5ecf09c66fc" @@ -4987,9 +5196,9 @@ default-browser-id@^5.0.0: integrity sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q== default-browser@^5.2.1: - version "5.4.0" - resolved "https://registry.yarnpkg.com/default-browser/-/default-browser-5.4.0.tgz#b55cf335bb0b465dd7c961a02cd24246aa434287" - integrity sha512-XDuvSq38Hr1MdN47EDvYtx3U0MTqpCEn+F6ft8z2vYDzMrvQhVp0ui9oQdqW3MvK3vqUETglt1tVGgjLuJ5izg== + version "5.5.0" + resolved "https://registry.yarnpkg.com/default-browser/-/default-browser-5.5.0.tgz#2792e886f2422894545947cc80e1a444496c5976" + integrity sha512-H9LMLr5zwIbSxrmvikGuI/5KGhZ8E2zH3stkMgM5LpOWDutGM2JZaj460Udnf1a+946zc7YBgrqEWwbk7zHvGw== dependencies: bundle-name "^4.1.0" default-browser-id "^5.0.0" @@ -5169,23 +5378,18 @@ electron-positioner@^4.1.0: resolved "https://registry.yarnpkg.com/electron-positioner/-/electron-positioner-4.1.0.tgz#e158f8f6aabd6725a8a9b4f2279b9504bcbea1b0" integrity sha512-726DfbI9ZNoCg+Fcu6XLuTKTnzf+6nFqv7h+K/V6Ug7IbaPMI7s9S8URnGtWFCy5N5PL4HSzRFF2mXuinftDdg== -electron-to-chromium@^1.5.249: - version "1.5.255" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.255.tgz#fe9294ce172241eb50733bc00f2bd00d9c1e4ec7" - integrity sha512-Z9oIp4HrFF/cZkDPMpz2XSuVpc1THDpT4dlmATFlJUIBVCy9Vap5/rIXsASP1CscBacBqhabwh8vLctqBwEerQ== - electron-to-chromium@^1.5.263: - version "1.5.267" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.267.tgz#5d84f2df8cdb6bfe7e873706bb21bd4bfb574dc7" - integrity sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw== + version "1.5.286" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.286.tgz#142be1ab5e1cd5044954db0e5898f60a4960384e" + integrity sha512-9tfDXhJ4RKFNerfjdCcZfufu49vg620741MNs26a9+bhLThdB+plgMeou98CAaHu/WATj2iHOOHTp1hWtABj2A== -electron@39.2.7: - version "39.2.7" - resolved "https://registry.yarnpkg.com/electron/-/electron-39.2.7.tgz#1cf2371304994fe26c564764bd50878fe405fb4b" - integrity sha512-KU0uFS6LSTh4aOIC3miolcbizOFP7N1M46VTYVfqIgFiuA2ilfNaOHLDS9tCMvwwHRowAsvqBrh9NgMXcTOHCQ== +electron@40.4.0: + version "40.4.0" + resolved "https://registry.yarnpkg.com/electron/-/electron-40.4.0.tgz#e2e73d837df141216dcd91fb6c3f754e771387c2" + integrity sha512-31l4V7Ys4oUuXyaN/cCNnyBdDXN9RwOVOG+JhiHCf4zx5tZkHd43PKGY6KLEWpeYCxaphsuGSEjagJLfPqKj8g== dependencies: "@electron/get" "^2.0.0" - "@types/node" "^22.7.7" + "@types/node" "^24.9.0" extract-zip "^2.0.1" emoji-datasource-apple@16.0.0: @@ -5225,13 +5429,13 @@ end-of-stream@^1.1.0: dependencies: once "^1.4.0" -enhanced-resolve@^5.17.4: - version "5.18.4" - resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.18.4.tgz#c22d33055f3952035ce6a144ce092447c525f828" - integrity sha512-LgQMM4WXU3QI+SYgEc2liRgznaD5ojbmY3sb8LxyguVkIg5FxdpTkvk72te2R38/TGKxH634oLxXRGY6d7AP+Q== +enhanced-resolve@^5.19.0: + version "5.19.0" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.19.0.tgz#6687446a15e969eaa63c2fa2694510e17ae6d97c" + integrity sha512-phv3E1Xl4tQOShqSte26C7Fl84EwUdZsyOuSSk9qtAGyyQs2s3jJzComh+Abf4g187lUUAvH+H26omrqia2aGg== dependencies: graceful-fs "^4.2.4" - tapable "^2.2.0" + tapable "^2.3.0" entities@^2.0.0: version "2.2.0" @@ -5248,10 +5452,15 @@ env-paths@^2.2.0, env-paths@^2.2.1: resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.1.tgz#420399d416ce1fbe9bc0a07c62fa68d67fd0f8f2" integrity sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A== +env-paths@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-3.0.0.tgz#2f1e89c2f6dbd3408e1b1711dd82d62e317f58da" + integrity sha512-dtJUTepzMW3Lm/NPxRf3wP4642UWhjL2sQxc+ym2YMj1m/H2zDNQOlezafzkHwn6sMstjHTwG6iQQsctDW/b1A== + envinfo@^7.13.0, envinfo@^7.14.0: - version "7.20.0" - resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.20.0.tgz#3fd9de69fb6af3e777a017dfa033676368d67dd7" - integrity sha512-+zUomDcLXsVkQ37vUqWBvQwLaLlj8eZPSi61llaEFAVBY5mhcXdaSw1pSJVl4yTYD5g/gEfpNl28YYk4IPvrrg== + version "7.21.0" + resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.21.0.tgz#04a251be79f92548541f37d13c8b6f22940c3bae" + integrity sha512-Lw7I8Zp5YKHFCXL7+Dz95g4CcbMEpgvqZNNq3AmlT5XAV6CgAAk6gyAMqn2zjw08K9BHfcNuKrMiCPLByGafow== err-code@^2.0.2: version "2.0.3" @@ -5273,17 +5482,17 @@ error-stack-parser@^2.0.6: stackframe "^1.3.4" errorhandler@^1.5.1: - version "1.5.1" - resolved "https://registry.yarnpkg.com/errorhandler/-/errorhandler-1.5.1.tgz#b9ba5d17cf90744cd1e851357a6e75bf806a9a91" - integrity sha512-rcOwbfvP1WTViVoUjcfZicVzjhjTuhSMntHh6mW3IrEiyE6mJyXvsToJUJGlGlw/2xU9P5whlWNGlIDVeCiT4A== + version "1.5.2" + resolved "https://registry.yarnpkg.com/errorhandler/-/errorhandler-1.5.2.tgz#dd0aa3952eca44aff7c2985e7d246c5932d70444" + integrity sha512-kNAL7hESndBCrWwS72QyV3IVOTrVmj9D062FV5BQswNL5zEdeRmz/WJFyh6Aj/plvvSOrzddkxW57HgkZcR9Fw== dependencies: - accepts "~1.3.7" + accepts "~1.3.8" escape-html "~1.0.3" -es-abstract@^1.17.5, es-abstract@^1.23.2, es-abstract@^1.23.3, es-abstract@^1.23.5, es-abstract@^1.23.6, es-abstract@^1.23.9, es-abstract@^1.24.0: - version "1.24.0" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.24.0.tgz#c44732d2beb0acc1ed60df840869e3106e7af328" - integrity sha512-WSzPgsdLtTcQwm4CROfS5ju2Wa1QQcVeT37jFjYzdFz1r9ahadC8B8/a4qxJxM+09F18iumCdRmlr96ZYkQvEg== +es-abstract@^1.17.5, es-abstract@^1.23.2, es-abstract@^1.23.3, es-abstract@^1.23.5, es-abstract@^1.23.6, es-abstract@^1.23.9, es-abstract@^1.24.0, es-abstract@^1.24.1: + version "1.24.1" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.24.1.tgz#f0c131ed5ea1bb2411134a8dd94def09c46c7899" + integrity sha512-zHXBLhP+QehSSbsS9Pt23Gg964240DPd6QCf8WpkqEXxQ7fhdZzYsocOr5u7apWonsS5EjZDmTF+/slGMyasvw== dependencies: array-buffer-byte-length "^1.0.2" arraybuffer.prototype.slice "^1.0.4" @@ -5356,25 +5565,25 @@ es-errors@^1.3.0: integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== es-iterator-helpers@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/es-iterator-helpers/-/es-iterator-helpers-1.2.1.tgz#d1dd0f58129054c0ad922e6a9a1e65eef435fe75" - integrity sha512-uDn+FE1yrDzyC0pCo961B2IHbdM8y/ACZsKD4dG6WqrjV53BADjwa7D+1aom2rsNVfLyDgU/eigvlJGJ08OQ4w== + version "1.2.2" + resolved "https://registry.yarnpkg.com/es-iterator-helpers/-/es-iterator-helpers-1.2.2.tgz#d979a9f686e2b0b72f88dbead7229924544720bc" + integrity sha512-BrUQ0cPTB/IwXj23HtwHjS9n7O4h9FX94b4xc5zlTHxeLgTAdzYUDyy6KdExAl9lbN5rtfe44xpjpmj9grxs5w== dependencies: call-bind "^1.0.8" - call-bound "^1.0.3" + call-bound "^1.0.4" define-properties "^1.2.1" - es-abstract "^1.23.6" + es-abstract "^1.24.1" es-errors "^1.3.0" - es-set-tostringtag "^2.0.3" + es-set-tostringtag "^2.1.0" function-bind "^1.1.2" - get-intrinsic "^1.2.6" + get-intrinsic "^1.3.0" globalthis "^1.0.4" gopd "^1.2.0" has-property-descriptors "^1.0.2" has-proto "^1.2.0" has-symbols "^1.1.0" internal-slot "^1.1.0" - iterator.prototype "^1.1.4" + iterator.prototype "^1.1.5" safe-array-concat "^1.1.3" es-module-lexer@^2.0.0: @@ -5389,7 +5598,7 @@ es-object-atoms@^1.0.0, es-object-atoms@^1.1.1: dependencies: es-errors "^1.3.0" -es-set-tostringtag@^2.0.3, es-set-tostringtag@^2.1.0: +es-set-tostringtag@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz#f31dbbe0c183b00a6d26eb6325c810c0fd18bd4d" integrity sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA== @@ -5420,7 +5629,7 @@ es6-error@^4.1.1: resolved "https://registry.yarnpkg.com/es6-error/-/es6-error-4.1.1.tgz#9e3af407459deed47e9a91f9b885a84eb05c561d" integrity sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg== -esbuild@^0.25.0, esbuild@~0.25.0: +esbuild@^0.25.0: version "0.25.12" resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.25.12.tgz#97a1d041f4ab00c2fce2f838d2b9969a2d2a97a5" integrity sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg== @@ -5452,6 +5661,38 @@ esbuild@^0.25.0, esbuild@~0.25.0: "@esbuild/win32-ia32" "0.25.12" "@esbuild/win32-x64" "0.25.12" +esbuild@~0.27.0: + version "0.27.3" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.27.3.tgz#5859ca8e70a3af956b26895ce4954d7e73bd27a8" + integrity sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg== + optionalDependencies: + "@esbuild/aix-ppc64" "0.27.3" + "@esbuild/android-arm" "0.27.3" + "@esbuild/android-arm64" "0.27.3" + "@esbuild/android-x64" "0.27.3" + "@esbuild/darwin-arm64" "0.27.3" + "@esbuild/darwin-x64" "0.27.3" + "@esbuild/freebsd-arm64" "0.27.3" + "@esbuild/freebsd-x64" "0.27.3" + "@esbuild/linux-arm" "0.27.3" + "@esbuild/linux-arm64" "0.27.3" + "@esbuild/linux-ia32" "0.27.3" + "@esbuild/linux-loong64" "0.27.3" + "@esbuild/linux-mips64el" "0.27.3" + "@esbuild/linux-ppc64" "0.27.3" + "@esbuild/linux-riscv64" "0.27.3" + "@esbuild/linux-s390x" "0.27.3" + "@esbuild/linux-x64" "0.27.3" + "@esbuild/netbsd-arm64" "0.27.3" + "@esbuild/netbsd-x64" "0.27.3" + "@esbuild/openbsd-arm64" "0.27.3" + "@esbuild/openbsd-x64" "0.27.3" + "@esbuild/openharmony-arm64" "0.27.3" + "@esbuild/sunos-x64" "0.27.3" + "@esbuild/win32-arm64" "0.27.3" + "@esbuild/win32-ia32" "0.27.3" + "@esbuild/win32-x64" "0.27.3" + escalade@^3.1.1, escalade@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.2.0.tgz#011a3f69856ba189dffa7dc8fcce99d2a87903e5" @@ -5462,7 +5703,7 @@ escape-html@~1.0.3: resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow== -escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: +escape-string-regexp@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== @@ -5671,9 +5912,9 @@ esprima@^4.0.0: integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== esquery@^1.5.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.6.0.tgz#91419234f804d852a82dceec3e16cdc22cf9dae7" - integrity sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg== + version "1.7.0" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.7.0.tgz#08d048f261f0ddedb5bae95f46809463d9c9496d" + integrity sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g== dependencies: estraverse "^5.1.0" @@ -5776,15 +6017,7 @@ expo-clipboard@8.0.8: resolved "https://registry.yarnpkg.com/expo-clipboard/-/expo-clipboard-8.0.8.tgz#5e52054a4bbaebef090ec6fe5eaa200072ff94f7" integrity sha512-VKoBkHIpZZDJTB0jRO4/PZskHdMNOEz3P/41tmM6fDuODMpqhvyWK053X0ebspkxiawJX9lX33JXHBCvVsTTOA== -expo-constants@~18.0.12: - version "18.0.12" - resolved "https://registry.yarnpkg.com/expo-constants/-/expo-constants-18.0.12.tgz#3e67f7109ffd03eaa5514c19875a767c39f5a2ca" - integrity sha512-WzcKYMVNRRu4NcSzfIVRD5aUQFnSpTZgXFrlWmm19xJoDa4S3/PQNi6PNTBRc49xz9h8FT7HMxRKaC8lr0gflA== - dependencies: - "@expo/config" "~12.0.12" - "@expo/env" "~2.0.8" - -expo-constants@~18.0.13: +expo-constants@~18.0.12, expo-constants@~18.0.13: version "18.0.13" resolved "https://registry.yarnpkg.com/expo-constants/-/expo-constants-18.0.13.tgz#0117f1f3d43be7b645192c0f4f431fb4efc4803d" integrity sha512-FnZn12E1dRYKDHlAdIyNFhBurKTS3F9CrfrBDJI5m3D7U17KBHMQ6JEfYlSj7LG7t+Ulr+IKaj58L1k5gBwTcQ== @@ -5802,10 +6035,10 @@ expo-file-system@19.0.21, expo-file-system@~19.0.21: resolved "https://registry.yarnpkg.com/expo-file-system/-/expo-file-system-19.0.21.tgz#e96a68107fb629cf0dd1906fe7b46b566ff13e10" integrity sha512-s3DlrDdiscBHtab/6W1osrjGL+C2bvoInPJD7sOwmxfJ5Woynv2oc+Fz1/xVXaE/V7HE/+xrHC/H45tu6lZzzg== -expo-font@~14.0.10: - version "14.0.10" - resolved "https://registry.yarnpkg.com/expo-font/-/expo-font-14.0.10.tgz#33fb9f6dc5661729192a6bc8cd6f08bd1a9097cc" - integrity sha512-UqyNaaLKRpj4pKAP4HZSLnuDQqueaO5tB1c/NWu5vh1/LF9ulItyyg2kF/IpeOp0DeOLk0GY0HrIXaKUMrwB+Q== +expo-font@~14.0.11: + version "14.0.11" + resolved "https://registry.yarnpkg.com/expo-font/-/expo-font-14.0.11.tgz#198743d17332520545107df026d8a261e6b2732f" + integrity sha512-ga0q61ny4s/kr4k8JX9hVH69exVSIfcIc19+qZ7gt71Mqtm7xy2c6kwsPTCyhBW2Ro5yXTT8EaZOpuRi35rHbg== dependencies: fontfaceobserver "^2.1.0" @@ -5886,26 +6119,26 @@ expo-task-manager@14.0.9: dependencies: unimodules-app-loader "~6.0.8" -expo@54.0.31: - version "54.0.31" - resolved "https://registry.yarnpkg.com/expo/-/expo-54.0.31.tgz#c9b88d7154039bb7165abc21d73aec5e5fde6d71" - integrity sha512-kQ3RDqA/a59I7y+oqQGyrPbbYlgPMUdKBOgvFLpoHbD2bCM+F75i4N0mUijy7dG5F/CUCu2qHmGGUCXBbMDkCg== +expo@54.0.33: + version "54.0.33" + resolved "https://registry.yarnpkg.com/expo/-/expo-54.0.33.tgz#f7d572857323f5a8250a9afe245a487d2ee2735f" + integrity sha512-3yOEfAKqo+gqHcV8vKcnq0uA5zxlohnhA3fu4G43likN8ct5ZZ3LjAh9wDdKteEkoad3tFPvwxmXW711S5OHUw== dependencies: "@babel/runtime" "^7.20.0" - "@expo/cli" "54.0.21" + "@expo/cli" "54.0.23" "@expo/config" "~12.0.13" "@expo/config-plugins" "~54.0.4" "@expo/devtools" "0.1.8" "@expo/fingerprint" "0.15.4" "@expo/metro" "~54.2.0" - "@expo/metro-config" "54.0.13" + "@expo/metro-config" "54.0.14" "@expo/vector-icons" "^15.0.3" "@ungap/structured-clone" "^1.3.0" - babel-preset-expo "~54.0.9" + babel-preset-expo "~54.0.10" expo-asset "~12.0.12" expo-constants "~18.0.13" expo-file-system "~19.0.21" - expo-font "~14.0.10" + expo-font "~14.0.11" expo-keep-awake "~15.0.8" expo-modules-autolinking "3.0.24" expo-modules-core "3.0.29" @@ -5955,7 +6188,7 @@ express@^4.22.1: utils-merge "1.0.1" vary "~1.1.2" -extract-zip@^2.0.0, extract-zip@^2.0.1: +extract-zip@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-2.0.1.tgz#663dca56fe46df890d5f131ef4a06d22bb8ba13a" integrity sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg== @@ -6010,9 +6243,9 @@ fastest-levenshtein@^1.0.12: integrity sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg== fastq@^1.6.0: - version "1.19.1" - resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.19.1.tgz#d50eaba803c8846a883c16492821ebcd2cda55f5" - integrity sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ== + version "1.20.1" + resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.20.1.tgz#ca750a10dc925bc8b18839fd203e3ef4b3ced675" + integrity sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw== dependencies: reusify "^1.0.4" @@ -6067,19 +6300,17 @@ file-entry-cache@^8.0.0: dependencies: flat-cache "^4.0.0" -filename-reserved-regex@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/filename-reserved-regex/-/filename-reserved-regex-2.0.0.tgz#abf73dfab735d045440abfea2d91f389ebbfa229" - integrity sha512-lc1bnsSr4L4Bdif8Xb/qrtokGbq5zlsms/CYH8PP+WtCkGNF65DPiQY8vG3SakEdRn8Dlnm+gW/qWKKjS5sZzQ== +filename-reserved-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/filename-reserved-regex/-/filename-reserved-regex-3.0.0.tgz#3d5dd6d4e2d73a3fed2ebc4cd0b3448869a081f7" + integrity sha512-hn4cQfU6GOT/7cFHXBqeBg2TbrMBgdD0kcjLhvSQYYwm3s4B6cjvBfb7nBALJLAXqmU5xajSa7X2NnUud/VCdw== -filenamify@^4.1.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/filenamify/-/filenamify-4.3.0.tgz#62391cb58f02b09971c9d4f9d63b3cf9aba03106" - integrity sha512-hcFKyUG57yWGAzu1CMt/dPzYZuv+jAJUT85bL8mrXvNe6hWj6yEHEc4EdcgiA6Z3oi1/9wXJdZPXF2dZNgwgOg== +filenamify@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/filenamify/-/filenamify-6.0.0.tgz#38def94098c62154c42a41d822650f5f55bcbac2" + integrity sha512-vqIlNogKeyD3yzrm0yhRMQg8hOVwYcYRfjEoODd49iCprMn4HL85gK3HcykQE53EPIpX3HcAbGA5ELQv216dAQ== dependencies: - filename-reserved-regex "^2.0.0" - strip-outer "^1.0.1" - trim-repeated "^1.0.0" + filename-reserved-regex "^3.0.0" fill-range@^7.1.1: version "7.1.1" @@ -6190,13 +6421,12 @@ flatted@^3.2.9: resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.3.tgz#67c8fad95454a7c7abebf74bb78ee74a44023358" integrity sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg== -flora-colossus@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/flora-colossus/-/flora-colossus-2.0.0.tgz#af1e85db0a8256ef05f3fb531c1235236c97220a" - integrity sha512-dz4HxH6pOvbUzZpZ/yXhafjbR2I8cenK5xL0KtBFb7U2ADsR+OwXifnxZjij/pZWF775uSCMzWVd+jDik2H2IA== +flora-colossus@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/flora-colossus/-/flora-colossus-3.0.2.tgz#fb309f9041f309a03728de3306796b1239a3ede5" + integrity sha512-Jk78K/Tzt6saxQPGChlJw69xuFGpWyTSAS8EdU0h/FyXwD2K46yNOXmo6nRHcZ9ooekyBAzMkwmiGNt7wOC5zg== dependencies: - debug "^4.3.4" - fs-extra "^10.1.0" + debug "^4.4.1" flow-enums-runtime@^0.0.6: version "0.0.6" @@ -6220,14 +6450,27 @@ for-each@^0.3.3, for-each@^0.3.5: dependencies: is-callable "^1.2.7" +foreground-child@^3.3.1: + version "3.3.1" + resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-3.3.1.tgz#32e8e9ed1b68a3497befb9ac2b6adf92a638576f" + integrity sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw== + dependencies: + cross-spawn "^7.0.6" + signal-exit "^4.0.1" + +form-data-encoder@^4.0.2: + version "4.1.0" + resolved "https://registry.yarnpkg.com/form-data-encoder/-/form-data-encoder-4.1.0.tgz#497cedc94810bd5d53b99b5d4f6c152d5cbc9db2" + integrity sha512-G6NsmEW15s0Uw9XnCg+33H3ViYRyiM0hMrMhhqQOR8NFc5GhYrI+6I3u7OTw7b91J2g8rtvMBZJDbcGb2YUniw== + forwarded@0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== -framed-msgpack-rpc@keybase/node-framed-msgpack-rpc#nojima/HOTPOT-use-buffer: +framed-msgpack-rpc@keybase/node-framed-msgpack-rpc#nojima/HOTPOT-use-buffer-iserror: version "1.1.24" - resolved "https://codeload.github.com/keybase/node-framed-msgpack-rpc/tar.gz/be19aba75ae0b67e301953efe87933aaaae7dfd8" + resolved "https://codeload.github.com/keybase/node-framed-msgpack-rpc/tar.gz/9c7cf455201444cc6dbe1495e7343df3c5c2e1e5" dependencies: iced-error "0.0.13" iced-lock "2.0.1" @@ -6244,7 +6487,7 @@ freeport-async@^2.0.0: resolved "https://registry.yarnpkg.com/freeport-async/-/freeport-async-2.0.0.tgz#6adf2ec0c629d11abff92836acd04b399135bab4" integrity sha512-K7od3Uw45AJg00XUmy15+Hae2hOcgKcmN3/EF6Y7i01O0gaqiRx8sUSpsb9+BRNL8RPBrhzPsVfy8q9ADlJuWQ== -fresh@0.5.2, fresh@~0.5.2: +fresh@~0.5.2: version "0.5.2" resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q== @@ -6258,7 +6501,7 @@ fs-extra@11.3.3: jsonfile "^6.0.1" universalify "^2.0.0" -fs-extra@^10.0.0, fs-extra@^10.1.0: +fs-extra@^10.0.0: version "10.1.0" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-10.1.0.tgz#02873cfbc4084dde127eaa5f9905eef2325d1abf" integrity sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ== @@ -6267,15 +6510,6 @@ fs-extra@^10.0.0, fs-extra@^10.1.0: jsonfile "^6.0.1" universalify "^2.0.0" -fs-extra@^11.1.0, fs-extra@^11.1.1: - version "11.3.2" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-11.3.2.tgz#c838aeddc6f4a8c74dd15f85e11fe5511bfe02a4" - integrity sha512-Xr9F6z6up6Ws+NjzMCZc6WXg2YFRlrLP9NQDO3VQrWrfiojdhS56TzueT88ze0uBdCTwEIhQ3ptnmKeWGFAe0A== - dependencies: - graceful-fs "^4.2.0" - jsonfile "^6.0.1" - universalify "^2.0.0" - fs-extra@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0" @@ -6285,16 +6519,6 @@ fs-extra@^8.1.0: jsonfile "^4.0.0" universalify "^0.1.0" -fs-extra@^9.0.1: - version "9.1.0" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.1.0.tgz#5954460c764a8da2094ba3554bf839e6b9a7c86d" - integrity sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ== - dependencies: - at-least-node "^1.0.0" - graceful-fs "^4.2.0" - jsonfile "^6.0.1" - universalify "^2.0.0" - fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" @@ -6332,14 +6556,13 @@ functions-have-names@^1.2.3: resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== -galactus@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/galactus/-/galactus-1.0.0.tgz#c2615182afa0c6d0859b92e56ae36d052827db7e" - integrity sha512-R1fam6D4CyKQGNlvJne4dkNF+PvUUl7TAJInvTGa9fti9qAv95quQz29GXapA4d8Ec266mJJxFVh82M4GIIGDQ== +galactus@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/galactus/-/galactus-2.0.2.tgz#04a4e403e28bdae7622ed7b46519953bb0717098" + integrity sha512-HmKyTFGomdAchz4umx8MwBnrnfFmdpwiTyGA4ZOF7rya2Lmgbc9qate4yweInL+0gUBVImhaz12SBGpW3SY4Yg== dependencies: - debug "^4.3.4" - flora-colossus "^2.0.0" - fs-extra "^10.1.0" + debug "^4.4.1" + flora-colossus "^3.0.2" generator-function@^2.0.0: version "2.0.1" @@ -6407,6 +6630,14 @@ get-stream@^6.0.0: resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== +get-stream@^9.0.1: + version "9.0.1" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-9.0.1.tgz#95157d21df8eb90d1647102b63039b1df60ebd27" + integrity sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA== + dependencies: + "@sec-ant/readable-stream" "^0.4.1" + is-stream "^4.0.1" + get-symbol-description@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.1.0.tgz#7bdd54e0befe8ffc9f3b4e203220d9f1e881b6ee" @@ -6417,9 +6648,9 @@ get-symbol-description@^1.1.0: get-intrinsic "^1.2.6" get-tsconfig@^4.7.5: - version "4.13.0" - resolved "https://registry.yarnpkg.com/get-tsconfig/-/get-tsconfig-4.13.0.tgz#fcdd991e6d22ab9a600f00e91c318707a5d9a0d7" - integrity sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ== + version "4.13.6" + resolved "https://registry.yarnpkg.com/get-tsconfig/-/get-tsconfig-4.13.6.tgz#2fbfda558a98a691a798f123afd95915badce876" + integrity sha512-shZT/QMiSHc/YBLxxOkMtgSid5HFoauqCE3/exfsEcwg1WkeqjG+V40yBbBrsD+jW2HDXcs28xOfcbm2jI8Ddw== dependencies: resolve-pkg-maps "^1.0.0" @@ -6442,7 +6673,7 @@ glob-parent@^6.0.2: dependencies: is-glob "^4.0.3" -glob-to-regex.js@^1.0.1: +glob-to-regex.js@^1.0.0, glob-to-regex.js@^1.0.1: version "1.2.0" resolved "https://registry.yarnpkg.com/glob-to-regex.js/-/glob-to-regex.js-1.2.0.tgz#2b323728271d133830850e32311f40766c5f6413" integrity sha512-QMwlOQKU/IzqMUOAZWubUOT8Qft+Y0KQWnX9nK3ch0CJg0tTp4TvGZsTfudYKv2NzoQSyPcnA6TYeIQ3jGichQ== @@ -6452,16 +6683,28 @@ glob-to-regexp@^0.4.1: resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e" integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw== -glob@^13.0.0: - version "13.0.0" - resolved "https://registry.yarnpkg.com/glob/-/glob-13.0.0.tgz#9d9233a4a274fc28ef7adce5508b7ef6237a1be3" - integrity sha512-tvZgpqk6fz4BaNZ66ZsRaZnbHvP/jG3uKJvAZOwEVUL4RTA5nJeeLYfyN9/VA8NX/V3IBG+hkeuGpKjvELkVhA== +glob@^11.0.1: + version "11.1.0" + resolved "https://registry.yarnpkg.com/glob/-/glob-11.1.0.tgz#4f826576e4eb99c7dad383793d2f9f08f67e50a6" + integrity sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw== dependencies: + foreground-child "^3.3.1" + jackspeak "^4.1.1" minimatch "^10.1.1" minipass "^7.1.2" + package-json-from-dist "^1.0.0" + path-scurry "^2.0.0" + +glob@^13.0.0: + version "13.0.2" + resolved "https://registry.yarnpkg.com/glob/-/glob-13.0.2.tgz#74b28859255e319c84d1aed1a0a5b5248bfea227" + integrity sha512-035InabNu/c1lW0tzPhAgapKctblppqsKKG9ZaNzbr+gXwWMjXoiyGSyB9sArzrjG7jY+zntRq5ZSUYemrnWVQ== + dependencies: + minimatch "^10.1.2" + minipass "^7.1.2" path-scurry "^2.0.0" -glob@^7.1.1, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: +glob@^7.1.1, glob@^7.1.3, glob@^7.1.4: version "7.2.3" resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== @@ -6527,10 +6770,10 @@ globby@^11.1.0: merge2 "^1.4.1" slash "^3.0.0" -google-libphonenumber@3.2.43: - version "3.2.43" - resolved "https://registry.yarnpkg.com/google-libphonenumber/-/google-libphonenumber-3.2.43.tgz#c1e5107ab9c6e3848dc2108e380bde08da80931c" - integrity sha512-TbIX/UC3BFRJwCxbBeCPwuRC4Qws9Jz/CECmfTM1t9RFoI3X6eRThurv6AYr9wSrt640IA9KFIHuAD/vlyjqRw== +google-libphonenumber@3.2.44: + version "3.2.44" + resolved "https://registry.yarnpkg.com/google-libphonenumber/-/google-libphonenumber-3.2.44.tgz#694ec9d5581f013b881c4c2937791e973a45f420" + integrity sha512-9p2TghluF2LTChFMLWsDRD5N78SZDsILdUk4gyqYxBXluCyxoPiOq+Fqt7DKM+LUd33+OgRkdrc+cPR93AypCQ== gopd@^1.0.1, gopd@^1.2.0: version "1.2.0" @@ -6554,6 +6797,24 @@ got@^11.8.5: p-cancelable "^2.0.0" responselike "^2.0.0" +got@^14.4.5: + version "14.6.6" + resolved "https://registry.yarnpkg.com/got/-/got-14.6.6.tgz#5adf7f576f89de8e291fd782e8b277f9bcc8e6c0" + integrity sha512-QLV1qeYSo5l13mQzWgP/y0LbMr5Plr5fJilgAIwgnwseproEbtNym8xpLsDzeZ6MWXgNE6kdWGBjdh3zT/Qerg== + dependencies: + "@sindresorhus/is" "^7.0.1" + byte-counter "^0.1.0" + cacheable-lookup "^7.0.0" + cacheable-request "^13.0.12" + decompress-response "^10.0.0" + form-data-encoder "^4.0.2" + http2-wrapper "^2.2.1" + keyv "^5.5.3" + lowercase-keys "^3.0.0" + p-cancelable "^4.0.1" + responselike "^4.0.2" + type-fest "^4.26.1" + graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.3, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.11, graceful-fs@^4.2.4, graceful-fs@^4.2.6, graceful-fs@^4.2.9: version "4.2.11" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" @@ -6712,10 +6973,10 @@ html-minifier-terser@^6.0.2: relateurl "^0.2.7" terser "^5.10.0" -html-webpack-plugin@5.6.5: - version "5.6.5" - resolved "https://registry.yarnpkg.com/html-webpack-plugin/-/html-webpack-plugin-5.6.5.tgz#d57defb83cabbf29bf56b2d4bf10b67b650066be" - integrity sha512-4xynFbKNNk+WlzXeQQ+6YYsH2g7mpfPszQZUi3ovKlj+pDmngQ7vRXjrrmGROabmKwyQkcgcX5hqfOwHbFmK5g== +html-webpack-plugin@5.6.6: + version "5.6.6" + resolved "https://registry.yarnpkg.com/html-webpack-plugin/-/html-webpack-plugin-5.6.6.tgz#5321b9579f4a1949318550ced99c2a4a4e60cbaf" + integrity sha512-bLjW01UTrvoWTJQL5LsMRo1SypHW80FTm12OJRSnr3v6YHNhfe+1r0MYUZJMACxnCHURVnBWRwAsWs2yPU9Ezw== dependencies: "@types/html-minifier-terser" "^6.0.0" html-minifier-terser "^6.0.2" @@ -6733,7 +6994,7 @@ htmlparser2@^6.1.0: domutils "^2.5.2" entities "^2.0.0" -http-cache-semantics@^4.0.0: +http-cache-semantics@^4.0.0, http-cache-semantics@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz#205f4db64f8562b76a4ff9235aa5279839a09dd5" integrity sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ== @@ -6743,27 +7004,17 @@ http-deceiver@^1.2.7: resolved "https://registry.yarnpkg.com/http-deceiver/-/http-deceiver-1.2.7.tgz#fa7168944ab9a519d337cb0bec7284dc3e723d87" integrity sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw== -http-errors@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3" - integrity sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ== +http-errors@~1.8.0: + version "1.8.1" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.8.1.tgz#7c3f28577cbc8a207388455dbd62295ed07bd68c" + integrity sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g== dependencies: - depd "2.0.0" + depd "~1.1.2" inherits "2.0.4" setprototypeof "1.2.0" - statuses "2.0.1" + statuses ">= 1.5.0 < 2" toidentifier "1.0.1" -http-errors@~1.6.2: - version "1.6.3" - resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.3.tgz#8b55680bb4be283a0b5bf4ea2e38580be1d9320d" - integrity sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A== - dependencies: - depd "~1.1.2" - inherits "2.0.3" - setprototypeof "1.1.0" - statuses ">= 1.4.0 < 2" - http-errors@~2.0.0, http-errors@~2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.1.tgz#36d2f65bc909c8790018dd36fb4d93da6caae06b" @@ -6808,6 +7059,14 @@ http2-wrapper@^1.0.0-beta.5.2: quick-lru "^5.1.1" resolve-alpn "^1.0.0" +http2-wrapper@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/http2-wrapper/-/http2-wrapper-2.2.1.tgz#310968153dcdedb160d8b72114363ef5fce1f64a" + integrity sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ== + dependencies: + quick-lru "^5.1.1" + resolve-alpn "^1.2.0" + https-proxy-agent@^7.0.5: version "7.0.6" resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz#da8dfeac7da130b05c2ba4b59c9b6cd66611a6b9" @@ -6848,7 +7107,7 @@ iced-runtime@1.0.4, iced-runtime@^1.0.0: resolved "https://registry.yarnpkg.com/iced-runtime/-/iced-runtime-1.0.4.tgz#e9de26dfe98cd8621201f7f3dfb9f7f09c550990" integrity sha512-rgiJXNF6ZgF2Clh/TKUlBDW3q51YPDJUXmxGQXx1b8tbZpVpTn+1RX9q1sjNkujXIIaVxZByQzPHHORg7KV51g== -iconv-lite@0.4.24, iconv-lite@~0.4.24: +iconv-lite@~0.4.24: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== @@ -6882,10 +7141,10 @@ image-size@^1.0.2: dependencies: queue "6.0.2" -immer@11.1.3: - version "11.1.3" - resolved "https://registry.yarnpkg.com/immer/-/immer-11.1.3.tgz#78681e1deb6cec39753acf04eb16d7576c04f4d6" - integrity sha512-6jQTc5z0KJFtr1UgFpIL3N9XSC3saRaI9PwWtzM2pSqkNGtiNkYY2OSwkOGDK2XcTRcLb1pi/aNkKZz0nxVH4Q== +immer@11.1.4: + version "11.1.4" + resolved "https://registry.yarnpkg.com/immer/-/immer-11.1.4.tgz#37aee86890b134a8f1a2fadd44361fb86c6ae67e" + integrity sha512-XREFCPo6ksxVzP4E0ekD5aMdf8WMwmdNaz6vuvxgI40UaEiu6q3p8X52aU6GdyvLY3XXX/8R7JOTXStz/nBbRw== import-fresh@^3.2.1, import-fresh@^3.3.0: version "3.3.1" @@ -6921,11 +7180,6 @@ inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, i resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== -inherits@2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" - integrity sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw== - ini@^1.3.4, ini@~1.3.0: version "1.3.8" resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" @@ -6965,9 +7219,9 @@ ipaddr.js@1.9.1: integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== ipaddr.js@^2.1.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-2.2.0.tgz#d33fa7bac284f4de7af949638c9d68157c6b92e8" - integrity sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA== + version "2.3.0" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-2.3.0.tgz#71dce70e1398122208996d1c22f2ba46a24b1abc" + integrity sha512-Zv/pA+ciVFbCSBBjGfaKUya/CcGmUHzTydLMaTwrUUEM2DIEO3iZvueGxmacvmN50fGpGVKeTXpb2LcYQxeVdg== is-arguments@^1.0.4: version "1.2.0" @@ -7187,6 +7441,11 @@ is-stream@^2.0.0: resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== +is-stream@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-4.0.1.tgz#375cf891e16d2e4baec250b85926cffc14720d9b" + integrity sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A== + is-string@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.1.1.tgz#92ea3f3d5c5b6e039ca8677e5ac8d07ea773cbb9" @@ -7296,7 +7555,7 @@ istanbul-lib-instrument@^5.0.4: istanbul-lib-coverage "^3.2.0" semver "^6.3.0" -iterator.prototype@^1.1.4: +iterator.prototype@^1.1.5: version "1.1.5" resolved "https://registry.yarnpkg.com/iterator.prototype/-/iterator.prototype-1.1.5.tgz#12c959a29de32de0aa3bbbb801f4d777066dae39" integrity sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g== @@ -7308,6 +7567,13 @@ iterator.prototype@^1.1.4: has-symbols "^1.1.0" set-function-name "^2.0.2" +jackspeak@^4.1.1: + version "4.2.3" + resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-4.2.3.tgz#27ef80f33b93412037c3bea4f8eddf80e1931483" + integrity sha512-ykkVRwrYvFm1nb2AJfKKYPr0emF6IiXDYUaFx4Zn9ZuIH7MrzEZ3sD5RlqGXNRpHtvUHJyOnCEFxOlNDtGo7wg== + dependencies: + "@isaacs/cliui" "^9.0.0" + jest-environment-node@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-29.7.0.tgz#0b93e111dda8ec120bc8300e6d1fb9576e164376" @@ -7445,7 +7711,7 @@ js-yaml@^3.13.1: argparse "^1.0.7" esprima "^4.0.0" -js-yaml@^4.1.0: +js-yaml@^4.1.0, js-yaml@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.1.tgz#854c292467705b699476e1a2decc0c8a3458806b" integrity sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA== @@ -7539,10 +7805,10 @@ jsonify@^0.0.1: object.assign "^4.1.4" object.values "^1.1.6" -junk@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/junk/-/junk-3.1.0.tgz#31499098d902b7e98c5d9b9c80f43457a88abfa1" - integrity sha512-pBxcB3LFc8QVgdggvZWyeys+hnrNWg4OcZIU/1X59k5jQdLBlCsYGRQaz234SqoRLTCgMH00fY0xRJH+F9METQ== +junk@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/junk/-/junk-4.0.1.tgz#7ee31f876388c05177fe36529ee714b07b50fbed" + integrity sha512-Qush0uP+G8ZScpGMZvHUiRfI0YBWuB3gVBYlI0v0vvOJt5FLicco+IkP0a50LqTTQhmts/m6tP5SWE+USyIvcQ== keyv@^4.0.0, keyv@^4.5.4: version "4.5.4" @@ -7551,6 +7817,13 @@ keyv@^4.0.0, keyv@^4.5.4: dependencies: json-buffer "3.0.1" +keyv@^5.5.3, keyv@^5.5.5: + version "5.6.0" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-5.6.0.tgz#03044074c6b4d072d0a62c7b9fa649537baf0105" + integrity sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw== + dependencies: + "@keyv/serialize" "^1.1.1" + kind-of@^6.0.2: version "6.0.3" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" @@ -7607,79 +7880,79 @@ lighthouse-logger@^1.0.0: debug "^2.6.9" marky "^1.2.2" -lightningcss-android-arm64@1.30.2: - version "1.30.2" - resolved "https://registry.yarnpkg.com/lightningcss-android-arm64/-/lightningcss-android-arm64-1.30.2.tgz#6966b7024d39c94994008b548b71ab360eb3a307" - integrity sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A== - -lightningcss-darwin-arm64@1.30.2: - version "1.30.2" - resolved "https://registry.yarnpkg.com/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.30.2.tgz#a5fa946d27c029e48c7ff929e6e724a7de46eb2c" - integrity sha512-ylTcDJBN3Hp21TdhRT5zBOIi73P6/W0qwvlFEk22fkdXchtNTOU4Qc37SkzV+EKYxLouZ6M4LG9NfZ1qkhhBWA== - -lightningcss-darwin-x64@1.30.2: - version "1.30.2" - resolved "https://registry.yarnpkg.com/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.30.2.tgz#5ce87e9cd7c4f2dcc1b713f5e8ee185c88d9b7cd" - integrity sha512-oBZgKchomuDYxr7ilwLcyms6BCyLn0z8J0+ZZmfpjwg9fRVZIR5/GMXd7r9RH94iDhld3UmSjBM6nXWM2TfZTQ== - -lightningcss-freebsd-x64@1.30.2: - version "1.30.2" - resolved "https://registry.yarnpkg.com/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.30.2.tgz#6ae1d5e773c97961df5cff57b851807ef33692a5" - integrity sha512-c2bH6xTrf4BDpK8MoGG4Bd6zAMZDAXS569UxCAGcA7IKbHNMlhGQ89eRmvpIUGfKWNVdbhSbkQaWhEoMGmGslA== - -lightningcss-linux-arm-gnueabihf@1.30.2: - version "1.30.2" - resolved "https://registry.yarnpkg.com/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.30.2.tgz#62c489610c0424151a6121fa99d77731536cdaeb" - integrity sha512-eVdpxh4wYcm0PofJIZVuYuLiqBIakQ9uFZmipf6LF/HRj5Bgm0eb3qL/mr1smyXIS1twwOxNWndd8z0E374hiA== - -lightningcss-linux-arm64-gnu@1.30.2: - version "1.30.2" - resolved "https://registry.yarnpkg.com/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.30.2.tgz#2a3661b56fe95a0cafae90be026fe0590d089298" - integrity sha512-UK65WJAbwIJbiBFXpxrbTNArtfuznvxAJw4Q2ZGlU8kPeDIWEX1dg3rn2veBVUylA2Ezg89ktszWbaQnxD/e3A== - -lightningcss-linux-arm64-musl@1.30.2: - version "1.30.2" - resolved "https://registry.yarnpkg.com/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.30.2.tgz#d7ddd6b26959245e026bc1ad9eb6aa983aa90e6b" - integrity sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA== - -lightningcss-linux-x64-gnu@1.30.2: - version "1.30.2" - resolved "https://registry.yarnpkg.com/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.30.2.tgz#5a89814c8e63213a5965c3d166dff83c36152b1a" - integrity sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w== - -lightningcss-linux-x64-musl@1.30.2: - version "1.30.2" - resolved "https://registry.yarnpkg.com/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.30.2.tgz#808c2e91ce0bf5d0af0e867c6152e5378c049728" - integrity sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA== - -lightningcss-win32-arm64-msvc@1.30.2: - version "1.30.2" - resolved "https://registry.yarnpkg.com/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.30.2.tgz#ab4a8a8a2e6a82a4531e8bbb6bf0ff161ee6625a" - integrity sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ== - -lightningcss-win32-x64-msvc@1.30.2: - version "1.30.2" - resolved "https://registry.yarnpkg.com/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.30.2.tgz#f01f382c8e0a27e1c018b0bee316d210eac43b6e" - integrity sha512-5g1yc73p+iAkid5phb4oVFMB45417DkRevRbt/El/gKXJk4jid+vPFF/AXbxn05Aky8PapwzZrdJShv5C0avjw== +lightningcss-android-arm64@1.31.1: + version "1.31.1" + resolved "https://registry.yarnpkg.com/lightningcss-android-arm64/-/lightningcss-android-arm64-1.31.1.tgz#609ff48332adff452a8157a7c2842fd692a8eac4" + integrity sha512-HXJF3x8w9nQ4jbXRiNppBCqeZPIAfUo8zE/kOEGbW5NZvGc/K7nMxbhIr+YlFlHW5mpbg/YFPdbnCh1wAXCKFg== + +lightningcss-darwin-arm64@1.31.1: + version "1.31.1" + resolved "https://registry.yarnpkg.com/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.31.1.tgz#a13da040a7929582bab3ace9a67bdc146e99fc2d" + integrity sha512-02uTEqf3vIfNMq3h/z2cJfcOXnQ0GRwQrkmPafhueLb2h7mqEidiCzkE4gBMEH65abHRiQvhdcQ+aP0D0g67sg== + +lightningcss-darwin-x64@1.31.1: + version "1.31.1" + resolved "https://registry.yarnpkg.com/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.31.1.tgz#f7482c311273571ec0c2bd8277c1f5f6e90e03a4" + integrity sha512-1ObhyoCY+tGxtsz1lSx5NXCj3nirk0Y0kB/g8B8DT+sSx4G9djitg9ejFnjb3gJNWo7qXH4DIy2SUHvpoFwfTA== + +lightningcss-freebsd-x64@1.31.1: + version "1.31.1" + resolved "https://registry.yarnpkg.com/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.31.1.tgz#91df1bb290f1cb7bb2af832d7d0d8809225e0124" + integrity sha512-1RINmQKAItO6ISxYgPwszQE1BrsVU5aB45ho6O42mu96UiZBxEXsuQ7cJW4zs4CEodPUioj/QrXW1r9pLUM74A== + +lightningcss-linux-arm-gnueabihf@1.31.1: + version "1.31.1" + resolved "https://registry.yarnpkg.com/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.31.1.tgz#c3cad5ae8b70045f21600dc95295ab6166acf57e" + integrity sha512-OOCm2//MZJ87CdDK62rZIu+aw9gBv4azMJuA8/KB74wmfS3lnC4yoPHm0uXZ/dvNNHmnZnB8XLAZzObeG0nS1g== + +lightningcss-linux-arm64-gnu@1.31.1: + version "1.31.1" + resolved "https://registry.yarnpkg.com/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.31.1.tgz#a5c4f6a5ac77447093f61b209c0bd7fef1f0a3e3" + integrity sha512-WKyLWztD71rTnou4xAD5kQT+982wvca7E6QoLpoawZ1gP9JM0GJj4Tp5jMUh9B3AitHbRZ2/H3W5xQmdEOUlLg== + +lightningcss-linux-arm64-musl@1.31.1: + version "1.31.1" + resolved "https://registry.yarnpkg.com/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.31.1.tgz#af26ab8f829b727ada0a200938a6c8796ff36900" + integrity sha512-mVZ7Pg2zIbe3XlNbZJdjs86YViQFoJSpc41CbVmKBPiGmC4YrfeOyz65ms2qpAobVd7WQsbW4PdsSJEMymyIMg== + +lightningcss-linux-x64-gnu@1.31.1: + version "1.31.1" + resolved "https://registry.yarnpkg.com/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.31.1.tgz#a891d44e84b71c0d88959feb9a7522bbf61450ee" + integrity sha512-xGlFWRMl+0KvUhgySdIaReQdB4FNudfUTARn7q0hh/V67PVGCs3ADFjw+6++kG1RNd0zdGRlEKa+T13/tQjPMA== + +lightningcss-linux-x64-musl@1.31.1: + version "1.31.1" + resolved "https://registry.yarnpkg.com/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.31.1.tgz#8c8b21def851f4d477fa897b80cb3db2b650bc6e" + integrity sha512-eowF8PrKHw9LpoZii5tdZwnBcYDxRw2rRCyvAXLi34iyeYfqCQNA9rmUM0ce62NlPhCvof1+9ivRaTY6pSKDaA== + +lightningcss-win32-arm64-msvc@1.31.1: + version "1.31.1" + resolved "https://registry.yarnpkg.com/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.31.1.tgz#79000fb8c57e94a91b8fc643e74d5a54407d7080" + integrity sha512-aJReEbSEQzx1uBlQizAOBSjcmr9dCdL3XuC/6HLXAxmtErsj2ICo5yYggg1qOODQMtnjNQv2UHb9NpOuFtYe4w== + +lightningcss-win32-x64-msvc@1.31.1: + version "1.31.1" + resolved "https://registry.yarnpkg.com/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.31.1.tgz#7f025274c81c7d659829731e09c8b6f442209837" + integrity sha512-I9aiFrbd7oYHwlnQDqr1Roz+fTz61oDDJX7n9tYF9FJymH1cIN1DtKw3iYt6b8WZgEjoNwVSncwF4wx/ZedMhw== lightningcss@^1.30.1: - version "1.30.2" - resolved "https://registry.yarnpkg.com/lightningcss/-/lightningcss-1.30.2.tgz#4ade295f25d140f487d37256f4cd40dc607696d0" - integrity sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ== + version "1.31.1" + resolved "https://registry.yarnpkg.com/lightningcss/-/lightningcss-1.31.1.tgz#1a19dd327b547a7eda1d5c296ebe1e72df5a184b" + integrity sha512-l51N2r93WmGUye3WuFoN5k10zyvrVs0qfKBhyC5ogUQ6Ew6JUSswh78mbSO+IU3nTWsyOArqPCcShdQSadghBQ== dependencies: detect-libc "^2.0.3" optionalDependencies: - lightningcss-android-arm64 "1.30.2" - lightningcss-darwin-arm64 "1.30.2" - lightningcss-darwin-x64 "1.30.2" - lightningcss-freebsd-x64 "1.30.2" - lightningcss-linux-arm-gnueabihf "1.30.2" - lightningcss-linux-arm64-gnu "1.30.2" - lightningcss-linux-arm64-musl "1.30.2" - lightningcss-linux-x64-gnu "1.30.2" - lightningcss-linux-x64-musl "1.30.2" - lightningcss-win32-arm64-msvc "1.30.2" - lightningcss-win32-x64-msvc "1.30.2" + lightningcss-android-arm64 "1.31.1" + lightningcss-darwin-arm64 "1.31.1" + lightningcss-darwin-x64 "1.31.1" + lightningcss-freebsd-x64 "1.31.1" + lightningcss-linux-arm-gnueabihf "1.31.1" + lightningcss-linux-arm64-gnu "1.31.1" + lightningcss-linux-arm64-musl "1.31.1" + lightningcss-linux-x64-gnu "1.31.1" + lightningcss-linux-x64-musl "1.31.1" + lightningcss-win32-arm64-msvc "1.31.1" + lightningcss-win32-x64-msvc "1.31.1" lines-and-columns@^1.1.6: version "1.2.4" @@ -7760,16 +8033,11 @@ lodash.throttle@^4.1.1: resolved "https://registry.yarnpkg.com/lodash.throttle/-/lodash.throttle-4.1.1.tgz#c23e91b710242ac70c37f1e1cda9274cc39bf2f4" integrity sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ== -lodash@4.17.23: +lodash@4.17.23, lodash@^4, lodash@^4.17.20, lodash@^4.17.21: version "4.17.23" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.23.tgz#f113b0378386103be4f6893388c73d0bde7f2c5a" integrity sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w== -lodash@^4, lodash@^4.17.20, lodash@^4.17.21: - version "4.17.21" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" - integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== - log-symbols@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-2.2.0.tgz#5740e1c5d6f0dfda4ad9323b5332107ef6b4c40a" @@ -7801,10 +8069,10 @@ loose-envify@^1.0.0, loose-envify@^1.4.0: dependencies: js-tokens "^3.0.0 || ^4.0.0" -lottie-react-native@7.3.5: - version "7.3.5" - resolved "https://registry.yarnpkg.com/lottie-react-native/-/lottie-react-native-7.3.5.tgz#515c703e8a8845c54dbd54d489467c0c1f21d8bd" - integrity sha512-5VPrHGbEmpNxrcEfmxyFZBvDksMaZ6LhyQZL0S0VIDwMRVrhGwOZQZKVFSEFU5HxNuDjxm/vPSoEhlKKfbYKHw== +lottie-react-native@7.3.6: + version "7.3.6" + resolved "https://registry.yarnpkg.com/lottie-react-native/-/lottie-react-native-7.3.6.tgz#b5ab46bff3ec9e48d5efa8d31166dd53758a1aa9" + integrity sha512-TevFHRvFURh6GlaqLKrSNXuKAxvBvFCiXfS7FXQI1K/ikOStgAwWLFPGjW0i1qB2/VzPACKmRs+535VjHUZZZQ== lottie-web@5.13.0: version "5.13.0" @@ -7823,15 +8091,20 @@ lowercase-keys@^2.0.0: resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479" integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA== +lowercase-keys@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-3.0.0.tgz#c5e7d442e37ead247ae9db117a9d0a467c89d4f2" + integrity sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ== + lru-cache@^10.0.1, lru-cache@^10.2.0: version "10.4.3" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.4.3.tgz#410fc8a17b70e598013df257c2446b7f3383f119" integrity sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ== lru-cache@^11.0.0: - version "11.2.2" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-11.2.2.tgz#40fd37edffcfae4b2940379c0722dc6eeaa75f24" - integrity sha512-F9ODfyqML2coTIsQpSkRHnLSZMtkU8Q+mSfcaIyKwy58u+8k5nvAYeiNhsyMARvzNcXJ9QfWVrcPsC9e9rAxtg== + version "11.2.6" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-11.2.6.tgz#356bf8a29e88a7a2945507b31f6429a65a192c58" + integrity sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ== lru-cache@^5.1.1: version "5.1.1" @@ -7878,10 +8151,18 @@ media-typer@0.3.0: integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ== memfs@^4.43.1: - version "4.51.0" - resolved "https://registry.yarnpkg.com/memfs/-/memfs-4.51.0.tgz#f33b5eff5e2faa01bfacc02aacf23ec7d8c84c94" - integrity sha512-4zngfkVM/GpIhC8YazOsM6E8hoB33NP0BCESPOA6z7qaL6umPJNqkO8CNYaLV2FB2MV6H1O3x2luHHOSqppv+A== - dependencies: + version "4.56.10" + resolved "https://registry.yarnpkg.com/memfs/-/memfs-4.56.10.tgz#eaf2f6556db10f91f1e9ad9f1274fd988c646202" + integrity sha512-eLvzyrwqLHnLYalJP7YZ3wBe79MXktMdfQbvMrVD80K+NhrIukCVBvgP30zTJYEEDh9hZ/ep9z0KOdD7FSHo7w== + dependencies: + "@jsonjoy.com/fs-core" "4.56.10" + "@jsonjoy.com/fs-fsa" "4.56.10" + "@jsonjoy.com/fs-node" "4.56.10" + "@jsonjoy.com/fs-node-builtins" "4.56.10" + "@jsonjoy.com/fs-node-to-fsa" "4.56.10" + "@jsonjoy.com/fs-node-utils" "4.56.10" + "@jsonjoy.com/fs-print" "4.56.10" + "@jsonjoy.com/fs-snapshot" "4.56.10" "@jsonjoy.com/json-pack" "^1.11.0" "@jsonjoy.com/util" "^1.9.0" glob-to-regex.js "^1.0.1" @@ -8137,7 +8418,7 @@ mime-db@1.52.0: resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.54.0.tgz#cddb3ee4f9c64530dff640236661d42cb6a314f5" integrity sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ== -mime-types@^2.1.27, mime-types@~2.1.17, mime-types@~2.1.24, mime-types@~2.1.34: +mime-types@^2.1.27, mime-types@~2.1.24, mime-types@~2.1.34, mime-types@~2.1.35: version "2.1.35" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== @@ -8145,9 +8426,9 @@ mime-types@^2.1.27, mime-types@~2.1.17, mime-types@~2.1.24, mime-types@~2.1.34: mime-db "1.52.0" mime-types@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-3.0.1.tgz#b1d94d6997a9b32fd69ebaed0db73de8acb519ce" - integrity sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA== + version "3.0.2" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-3.0.2.tgz#39002d4182575d5af036ffa118100f2524b2e2ab" + integrity sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A== dependencies: mime-db "^1.54.0" @@ -8181,17 +8462,22 @@ mimic-response@^3.1.0: resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-3.1.0.tgz#2d1d59af9c1b129815accc2c46a022a5ce1fa3c9" integrity sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ== +mimic-response@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-4.0.0.tgz#35468b19e7c75d10f5165ea25e75a5ceea7cf70f" + integrity sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg== + minimalistic-assert@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== -minimatch@^10.1.1: - version "10.1.1" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-10.1.1.tgz#e6e61b9b0c1dcab116b5a7d1458e8b6ae9e73a55" - integrity sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ== +minimatch@^10.0.1, minimatch@^10.1.1, minimatch@^10.1.2: + version "10.1.2" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-10.1.2.tgz#6c3f289f9de66d628fa3feb1842804396a43d81c" + integrity sha512-fu656aJ0n2kcXwsnwnv9g24tkU5uSmOlTjd6WyyaKm2Z+h1qmY6bAjrcaIxF/BslFqbZ8UBtbJi7KgQOZD2PTw== dependencies: - "@isaacs/brace-expansion" "^5.0.0" + "@isaacs/brace-expansion" "^5.0.1" minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: version "3.1.2" @@ -8214,7 +8500,7 @@ minimatch@^9.0.0, minimatch@^9.0.3, minimatch@^9.0.4, minimatch@^9.0.5: dependencies: brace-expansion "^2.0.1" -minimist@^1.2.0, minimist@^1.2.6, minimist@^1.2.8: +minimist@^1.2.0, minimist@^1.2.6: version "1.2.8" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== @@ -8371,6 +8657,11 @@ normalize-url@^6.0.1: resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-6.1.0.tgz#40d0885b535deffe3f3147bec877d05fe4c5668a" integrity sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A== +normalize-url@^8.1.1: + version "8.1.1" + resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-8.1.1.tgz#751a20c8520e5725404c06015fea21d7567f25ef" + integrity sha512-JYc0DPlpGWB40kH5g07gGTrYuMqV653k3uBKY6uITPWds3M0ov3GaWGp9lbE3Bzngx8+XkfzgvASb9vk9JDFXQ== + npm-package-arg@^11.0.0: version "11.0.3" resolved "https://registry.yarnpkg.com/npm-package-arg/-/npm-package-arg-11.0.3.tgz#dae0c21199a99feca39ee4bfb074df3adac87e2d" @@ -8463,17 +8754,17 @@ object.fromentries@^2.0.8: es-object-atoms "^1.0.0" object.getownpropertydescriptors@^2.0.3: - version "2.1.8" - resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.8.tgz#2f1fe0606ec1a7658154ccd4f728504f69667923" - integrity sha512-qkHIGe4q0lSYMv0XI4SsBTJz3WaURhLvd0lKSgtVuOsJ2krg4SgMw3PIRQFMp07yi++UR3se2mkcLqsBNpBb/A== + version "2.1.9" + resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.9.tgz#bf9e7520f14d50de88dee2b9c9eca841166322dc" + integrity sha512-mt8YM6XwsTTovI+kdZdHSxoyF2DI59up034orlC9NfweclcWOt7CVascNNLp6U+bjFVCVCIh9PwS76tDM/rH8g== dependencies: - array.prototype.reduce "^1.0.6" - call-bind "^1.0.7" + array.prototype.reduce "^1.0.8" + call-bind "^1.0.8" define-properties "^1.2.1" - es-abstract "^1.23.2" - es-object-atoms "^1.0.0" - gopd "^1.0.1" - safe-array-concat "^1.1.2" + es-abstract "^1.24.0" + es-object-atoms "^1.1.1" + gopd "^1.2.0" + safe-array-concat "^1.1.3" object.values@^1.1.6, object.values@^1.2.1: version "1.2.1" @@ -8490,7 +8781,7 @@ obuf@^1.0.0, obuf@^1.1.2: resolved "https://registry.yarnpkg.com/obuf/-/obuf-1.1.2.tgz#09bea3343d41859ebd446292d11c9d4db619084e" integrity sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg== -on-finished@2.4.1, on-finished@^2.4.1, on-finished@~2.4.1: +on-finished@^2.4.1, on-finished@~2.4.1: version "2.4.1" resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f" integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg== @@ -8617,6 +8908,11 @@ p-cancelable@^2.0.0: resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-2.1.1.tgz#aab7fbd416582fa32a3db49859c122487c5ed2cf" integrity sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg== +p-cancelable@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-4.0.1.tgz#2d1edf1ab8616b72c73db41c4bc9ecdd10af640e" + integrity sha512-wBowNApzd45EIKdO1LaU+LrMBwAcjfPaYtVzV3lmfM3gf8Z4CHZsiIqlM8TZZ8okYvh5A1cP6gTfCRQtwUpaUg== + p-limit@^1.1.0: version "1.3.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8" @@ -8685,7 +8981,7 @@ p-try@^2.0.0: resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== -package-json-from-dist@^1.0.1: +package-json-from-dist@^1.0.0, package-json-from-dist@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz#4f1471a010827a86f94cfd9b0727e36d267de505" integrity sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw== @@ -8746,7 +9042,7 @@ parse-png@^2.1.0: dependencies: pngjs "^3.3.0" -parseurl@~1.3.2, parseurl@~1.3.3: +parseurl@~1.3.3: version "1.3.3" resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== @@ -8915,21 +9211,21 @@ pkijs@^3.3.3: pvutils "^1.1.3" tslib "^2.8.1" -playwright-core@1.56.1: - version "1.56.1" - resolved "https://registry.yarnpkg.com/playwright-core/-/playwright-core-1.56.1.tgz#24a66481e5cd33a045632230aa2c4f0cb6b1db3d" - integrity sha512-hutraynyn31F+Bifme+Ps9Vq59hKuUCz7H1kDOcBs+2oGguKkWTU50bBWrtz34OUWmIwpBTWDxaRPXrIXkgvmQ== +playwright-core@1.58.2: + version "1.58.2" + resolved "https://registry.yarnpkg.com/playwright-core/-/playwright-core-1.58.2.tgz#ac5f5b4b10d29bcf934415f0b8d133b34b0dcb13" + integrity sha512-yZkEtftgwS8CsfYo7nm0KE8jsvm6i/PTgVtB8DL726wNf6H2IMsDuxCpJj59KDaxCtSnrWan2AeDqM7JBaultg== playwright@^1.49.0: - version "1.56.1" - resolved "https://registry.yarnpkg.com/playwright/-/playwright-1.56.1.tgz#62e3b99ddebed0d475e5936a152c88e68be55fbf" - integrity sha512-aFi5B0WovBHTEvpM3DzXTUaeN6eN0qWnTkKx4NQaH4Wvcmc153PdaY2UBdSYKaGYw+UyWXSVyxDUg5DoPEttjw== + version "1.58.2" + resolved "https://registry.yarnpkg.com/playwright/-/playwright-1.58.2.tgz#afe547164539b0bcfcb79957394a7a3fa8683cfd" + integrity sha512-vA30H8Nvkq/cPBnNw4Q8TWz1EJyqgpuinBcHET0YVJVFldr8JDNiU9LaWAE1KqSkRYazuaBhTpB5ZzShOezQ6A== dependencies: - playwright-core "1.56.1" + playwright-core "1.58.2" optionalDependencies: fsevents "2.3.2" -plist@^3.0.0, plist@^3.0.5, plist@^3.1.0: +plist@^3.0.5, plist@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/plist/-/plist-3.1.0.tgz#797a516a93e62f5bde55e0b9cc9c967f860893c9" integrity sha512-uysumyrvkUX0rX/dEVqt8gC3sTBzd4zoWfLeS29nb53imdaXVvLINYXTI2GNqzaMuvacNx4uJQ8+b3zXR0pkgQ== @@ -8977,9 +9273,9 @@ postcss-modules-values@^4.0.0: icss-utils "^5.0.0" postcss-selector-parser@^7.0.0: - version "7.1.0" - resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz#4d6af97eba65d73bc4d84bcb343e865d7dd16262" - integrity sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA== + version "7.1.1" + resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz#e75d2e0d843f620e5df69076166f4e16f891cb9f" + integrity sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg== dependencies: cssesc "^3.0.0" util-deprecate "^1.0.2" @@ -8989,7 +9285,7 @@ postcss-value-parser@^4.1.0, postcss-value-parser@^4.2.0: resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514" integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ== -postcss@^8.4.33: +postcss@^8.4.40: version "8.5.6" resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.5.6.tgz#2825006615a619b4f62a9e7426cc120b349a8f3c" integrity sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg== @@ -9024,15 +9320,10 @@ prelude-ls@^1.2.1: resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== -prettier@3.8.0: - version "3.8.0" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.8.0.tgz#f72cf71505133f40cfa2ef77a2668cdc558fcd69" - integrity sha512-yEPsovQfpxYfgWNhCfECjG5AQaO+K3dp6XERmOepyPDVqcJm+bjyCVO3pmU+nAPe0N5dDvekfGezt/EIiRe1TA== - -prettier@^3.4.2: - version "3.6.2" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.6.2.tgz#ccda02a1003ebbb2bfda6f83a074978f608b9393" - integrity sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ== +prettier@3.8.1: + version "3.8.1" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.8.1.tgz#edf48977cf991558f4fcbd8a3ba6015ba2a3a173" + integrity sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg== pretty-bytes@^5.6.0: version "5.6.0" @@ -9147,27 +9438,15 @@ pvutils@^1.1.3: resolved "https://registry.yarnpkg.com/pvutils/-/pvutils-1.1.5.tgz#84b0dea4a5d670249aa9800511804ee0b7c2809c" integrity sha512-KTqnxsgGiQ6ZAzZCVlJH5eOjSnvlyEgx1m8bkRJfOhmGRqfo5KLvmAlACQkrjEtOQ4B7wF9TdSLIs9O90MX9xA== -qrcode-generator@1.4.4: - version "1.4.4" - resolved "https://registry.yarnpkg.com/qrcode-generator/-/qrcode-generator-1.4.4.tgz#63f771224854759329a99048806a53ed278740e7" - integrity sha512-HM7yY8O2ilqhmULxGMpcHSF1EhJJ9yBj8gvDEuZ6M+KGJ0YY2hKpnXvRD+hZPLrDVck3ExIGhmPtSdcjC+guuw== - qrcode-terminal@0.11.0: version "0.11.0" resolved "https://registry.yarnpkg.com/qrcode-terminal/-/qrcode-terminal-0.11.0.tgz#ffc6c28a2fc0bfb47052b47e23f4f446a5fbdb9e" integrity sha512-Uu7ii+FQy4Qf82G4xu7ShHhjhGahEpCWc3x8UavY3CTcWV+ufmmCtwkr7ZKsX42jdL0kr1B5FKUeqJvAn51jzQ== -qs@6.13.0: - version "6.13.0" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.13.0.tgz#6ca3bd58439f7e245655798997787b0d88a51906" - integrity sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg== - dependencies: - side-channel "^1.0.6" - qs@~6.14.0: - version "6.14.1" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.14.1.tgz#a41d85b9d3902f31d27861790506294881871159" - integrity sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ== + version "6.14.2" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.14.2.tgz#b5634cf9d9ad9898e31fba3504e866e8efb6798c" + integrity sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q== dependencies: side-channel "^1.1.0" @@ -9215,16 +9494,6 @@ range-parser@^1.2.1, range-parser@~1.2.1: resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== -raw-body@2.5.2: - version "2.5.2" - resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.2.tgz#99febd83b90e08975087e8f1f9419a149366b68a" - integrity sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA== - dependencies: - bytes "3.1.2" - http-errors "2.0.0" - iconv-lite "0.4.24" - unpipe "1.0.0" - raw-body@~2.5.3: version "2.5.3" resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.3.tgz#11c6650ee770a7de1b494f197927de0c923822e2" @@ -9288,9 +9557,9 @@ react-is@^18.0.0: integrity sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg== react-is@^19.1.0: - version "19.2.0" - resolved "https://registry.yarnpkg.com/react-is/-/react-is-19.2.0.tgz#ddc3b4a4e0f3336c3847f18b806506388d7b9973" - integrity sha512-x3Ax3kNSMIIkyVYhWPyO09bu0uttcAIoecO/um/rKGQ4EltYWVYtyiGkS/3xMynrbVQdS69Jhlv8FXUEZehlzA== + version "19.2.4" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-19.2.4.tgz#a080758243c572ccd4a63386537654298c99d135" + integrity sha512-W+EWGn2v0ApPKgKKCy/7s7WHXkboGcsrXE+2joLyVxkbyVQfO3MUEaUQDHoSmb8TFFrSKYa9mw64WZHNHSDzYA== react-list@0.8.18: version "0.8.18" @@ -9314,10 +9583,10 @@ react-native-is-edge-to-edge@1.2.1, react-native-is-edge-to-edge@^1.2.1: "react-native-kb@file:../rnmodules/react-native-kb": version "0.1.1" -react-native-keyboard-controller@1.20.6: - version "1.20.6" - resolved "https://registry.yarnpkg.com/react-native-keyboard-controller/-/react-native-keyboard-controller-1.20.6.tgz#139a66f36734a69648822bf2e7ec3c90e7b3dc8e" - integrity sha512-RS6FjIjTFtAMQGdcXp3m6jUs1XgDa8qkpO5c4ix1S5HS0z3L2E1LUOY5rD73YUADOO3MfQN1z3JkHdBtzKucbg== +react-native-keyboard-controller@1.20.7: + version "1.20.7" + resolved "https://registry.yarnpkg.com/react-native-keyboard-controller/-/react-native-keyboard-controller-1.20.7.tgz#e1be1c15a5eb10b96a40a0812d8472e6e4bd8f29" + integrity sha512-G8S5jz1FufPrcL1vPtReATx+jJhT/j+sTqxMIb30b1z7cYEfMlkIzOCyaHgf6IMB2KA9uBmnA5M6ve2A9Ou4kw== dependencies: react-native-is-edge-to-edge "^1.2.1" @@ -9334,10 +9603,10 @@ react-native-safe-area-context@5.6.2: resolved "https://registry.yarnpkg.com/react-native-safe-area-context/-/react-native-safe-area-context-5.6.2.tgz#283e006f5b434fb247fcb4be0971ad7473d5c560" integrity sha512-4XGqMNj5qjUTYywJqpdWZ9IG8jgkS3h06sfVjfw5yZQZfWnRFXczi0GnYyFyCc2EBps/qFmoCH8fez//WumdVg== -react-native-screens@4.18.0: - version "4.18.0" - resolved "https://registry.yarnpkg.com/react-native-screens/-/react-native-screens-4.18.0.tgz#ba5a951b3f145e3b773d201143c19e1b1c1337ff" - integrity sha512-mRTLWL7Uc1p/RFNveEIIrhP22oxHduC2ZnLr/2iHwBeYpGXR0rJZ7Bgc0ktxQSHRjWTPT70qc/7yd4r9960PBQ== +react-native-screens@4.23.0: + version "4.23.0" + resolved "https://registry.yarnpkg.com/react-native-screens/-/react-native-screens-4.23.0.tgz#81574b1b0cc4ac6c9ed63e46eca7126f37affe86" + integrity sha512-XhO3aK0UeLpBn4kLecd+J+EDeRRJlI/Ro9Fze06vo1q163VeYtzfU9QS09/VyDFMWR1qxDC1iazCArTPSFFiPw== dependencies: react-freeze "^1.0.0" warn-once "^0.1.0" @@ -9364,10 +9633,10 @@ react-native-webview@13.16.0: escape-string-regexp "^4.0.0" invariant "2.2.4" -react-native-worklets@0.7.1: - version "0.7.1" - resolved "https://registry.yarnpkg.com/react-native-worklets/-/react-native-worklets-0.7.1.tgz#263da5216b0b5342b9f1b36e0ab897c5ca5c863b" - integrity sha512-KNsvR48ULg73QhTlmwPbdJLPsWcyBotrGPsrDRDswb5FYpQaJEThUKc2ncXE4UM5dn/ewLoQHjSjLaKUVPxPhA== +react-native-worklets@0.7.3: + version "0.7.3" + resolved "https://registry.yarnpkg.com/react-native-worklets/-/react-native-worklets-0.7.3.tgz#e561c006b576019feb21888c4acf60261b55ca24" + integrity sha512-m/CIUCHvLQulboBn0BtgpsesXjOTeubU7t+V0lCPpBj0t2ExigwqDHoKj3ck7OeErnjgkD27wdAtQCubYATe3g== dependencies: "@babel/plugin-transform-arrow-functions" "7.27.1" "@babel/plugin-transform-class-properties" "7.27.1" @@ -9469,10 +9738,10 @@ react-test-renderer@19.1.0: react-is "^19.1.0" scheduler "^0.26.0" -react-window@2.2.5: - version "2.2.5" - resolved "https://registry.yarnpkg.com/react-window/-/react-window-2.2.5.tgz#425a29609980083aafd5a48a1711a2af9319c1d2" - integrity sha512-6viWvPSZvVuMIe9hrl4IIZoVfO/npiqOb03m4Z9w+VihmVzBbiudUrtUqDpsWdKvd/Ai31TCR25CBcFFAUm28w== +react-window@2.2.6: + version "2.2.6" + resolved "https://registry.yarnpkg.com/react-window/-/react-window-2.2.6.tgz#00ca174346b5146d3c33a752d888181250c71d9f" + integrity sha512-v89O08xRdpCaEuf380B39D1C/0KgUDZA59xft6SVAjzjz/xQxSyXrgDWHymIsYI6TMrqE8WO+G0/PB9AGE8VNA== react@19.1.0: version "19.1.0" @@ -9654,7 +9923,7 @@ requires-port@^1.0.0: resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" integrity sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ== -resedit@^2.0.0: +resedit@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/resedit/-/resedit-2.0.3.tgz#5145a9faabca44b917d5636dbe8e67ec7f62c6f2" integrity sha512-oTeemxwoMuxxTYxXUwjkrOPfngTQehlv0/HoYFNkB4uzsP1Un1A9nI8JQKGOFkxpqkC7qkMs0lUsGrvUlbLNUA== @@ -9666,7 +9935,7 @@ reselect@^4.1.7: resolved "https://registry.yarnpkg.com/reselect/-/reselect-4.1.8.tgz#3f5dc671ea168dccdeb3e141236f69f02eaec524" integrity sha512-ab9EmR80F/zQTMNeneUr4cv+jSwPJgIlvEmVwLerwrWVbpLlBuls9XHzIeTFy4cegU2NHBp3va0LKOzU5qFEYQ== -resolve-alpn@^1.0.0: +resolve-alpn@^1.0.0, resolve-alpn@^1.2.0: version "1.2.1" resolved "https://registry.yarnpkg.com/resolve-alpn/-/resolve-alpn-1.2.1.tgz#b7adbdac3546aaaec20b45e7d8265927072726f9" integrity sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g== @@ -9701,16 +9970,16 @@ resolve-pkg-maps@^1.0.0: integrity sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw== resolve-workspace-root@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/resolve-workspace-root/-/resolve-workspace-root-2.0.0.tgz#a0098daa0067cd0efa6eb525c57c8fb4a61e78f8" - integrity sha512-IsaBUZETJD5WsI11Wt8PKHwaIe45or6pwNc8yflvLJ4DWtImK9kuLoH5kUva/2Mmx/RdIyr4aONNSa2v9LTJsw== + version "2.0.1" + resolved "https://registry.yarnpkg.com/resolve-workspace-root/-/resolve-workspace-root-2.0.1.tgz#9cbbf8321ebccaaf0e4ffea5274aa26b611ccd62" + integrity sha512-nR23LHAvaI6aHtMg6RWoaHpdR4D881Nydkzi2CixINyg9T00KgaJdJI6Vwty+Ps8WLxZHuxsS0BseWjxSA4C+w== resolve.exports@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-2.0.3.tgz#41955e6f1b4013b7586f873749a635dea07ebe3f" integrity sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A== -resolve@^1.1.6, resolve@^1.10.0, resolve@^1.20.0, resolve@^1.22.10, resolve@^1.22.2, resolve@^1.22.8: +resolve@^1.10.0, resolve@^1.20.0, resolve@^1.22.10, resolve@^1.22.11, resolve@^1.22.2, resolve@^1.22.8: version "1.22.11" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.11.tgz#aad857ce1ffb8bfa9b0b1ac29f1156383f68c262" integrity sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ== @@ -9742,6 +10011,13 @@ responselike@^2.0.0: dependencies: lowercase-keys "^2.0.0" +responselike@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/responselike/-/responselike-4.0.2.tgz#d99a1105aeca5909c1e93156a839c7f3173e26c2" + integrity sha512-cGk8IbWEAnaCpdAt1BHzJ3Ahz5ewDJa0KseTsE3qIRMJ3C698W8psM7byCeWVpd/Ha7FUYzuRVzXoKoM6nRUbA== + dependencies: + lowercase-keys "^3.0.0" + restore-cursor@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf" @@ -9812,7 +10088,7 @@ run-parallel@^1.1.9: dependencies: queue-microtask "^1.2.2" -safe-array-concat@^1.1.2, safe-array-concat@^1.1.3: +safe-array-concat@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/safe-array-concat/-/safe-array-concat-1.1.3.tgz#c9e54ec4f603b0bbb8e7e5007a5ee7aecd1538c3" integrity sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q== @@ -9856,9 +10132,9 @@ safe-regex-test@^1.1.0: integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== sax@>=0.6.0: - version "1.4.3" - resolved "https://registry.yarnpkg.com/sax/-/sax-1.4.3.tgz#fcebae3b756cdc8428321805f4b70f16ec0ab5db" - integrity sha512-yqYn1JhPczigF94DMS+shiDMjDowYO6y9+wB/4WgO0Y19jWYk0lQ4tuG5KI7kj4FTp1wxPj5IFfcrz/s1c3jjQ== + version "1.4.4" + resolved "https://registry.yarnpkg.com/sax/-/sax-1.4.4.tgz#f29c2bba80ce5b86f4343b4c2be9f2b96627cf8b" + integrity sha512-1n3r/tGXO6b6VXMdFT54SHzT9ytu9yr7TaELowdYpMqY/Ao7EnlQGmAQ1+RatX7Tkkdm6hONI2owqNx2aZj5Sw== scheduler@0.26.0, scheduler@^0.26.0: version "0.26.0" @@ -9907,7 +10183,7 @@ semver-compare@^1.0.0: resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== -semver@7.7.3, semver@^7.1.3, semver@^7.3.2, semver@^7.3.5, semver@^7.3.7, semver@^7.5.2, semver@^7.5.3, semver@^7.5.4, semver@^7.6.0, semver@^7.7.3: +semver@7.7.3: version "7.7.3" resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.3.tgz#4b5f4143d007633a8dc671cd0a6ef9147b8bb946" integrity sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q== @@ -9917,45 +10193,12 @@ semver@^6.2.0, semver@^6.3.0, semver@^6.3.1: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== -send@0.19.0: - version "0.19.0" - resolved "https://registry.yarnpkg.com/send/-/send-0.19.0.tgz#bbc5a388c8ea6c048967049dbeac0e4a3f09d7f8" - integrity sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw== - dependencies: - debug "2.6.9" - depd "2.0.0" - destroy "1.2.0" - encodeurl "~1.0.2" - escape-html "~1.0.3" - etag "~1.8.1" - fresh "0.5.2" - http-errors "2.0.0" - mime "1.6.0" - ms "2.1.3" - on-finished "2.4.1" - range-parser "~1.2.1" - statuses "2.0.1" - -send@^0.19.0: - version "0.19.1" - resolved "https://registry.yarnpkg.com/send/-/send-0.19.1.tgz#1c2563b2ee4fe510b806b21ec46f355005a369f9" - integrity sha512-p4rRk4f23ynFEfcD9LA0xRYngj+IyGiEYyqqOak8kaN0TvNmuxC2dcVeBn62GpCeR2CpWqyHCNScTP91QbAVFg== - dependencies: - debug "2.6.9" - depd "2.0.0" - destroy "1.2.0" - encodeurl "~2.0.0" - escape-html "~1.0.3" - etag "~1.8.1" - fresh "0.5.2" - http-errors "2.0.0" - mime "1.6.0" - ms "2.1.3" - on-finished "2.4.1" - range-parser "~1.2.1" - statuses "2.0.1" +semver@^7.1.3, semver@^7.3.2, semver@^7.3.5, semver@^7.3.7, semver@^7.5.2, semver@^7.5.3, semver@^7.5.4, semver@^7.6.0, semver@^7.6.3, semver@^7.7.1, semver@^7.7.2, semver@^7.7.3: + version "7.7.4" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.4.tgz#28464e36060e991fa7a11d0279d2d3f3b57a7e8a" + integrity sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA== -send@~0.19.0, send@~0.19.1: +send@^0.19.0, send@~0.19.0, send@~0.19.1: version "0.19.2" resolved "https://registry.yarnpkg.com/send/-/send-0.19.2.tgz#59bc0da1b4ea7ad42736fd642b1c4294e114ff29" integrity sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg== @@ -9994,29 +10237,19 @@ serialize-javascript@^6.0.2: randombytes "^2.1.0" serve-index@^1.9.1: - version "1.9.1" - resolved "https://registry.yarnpkg.com/serve-index/-/serve-index-1.9.1.tgz#d3768d69b1e7d82e5ce050fff5b453bea12a9239" - integrity sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw== + version "1.9.2" + resolved "https://registry.yarnpkg.com/serve-index/-/serve-index-1.9.2.tgz#2988e3612106d78a5e4849ddff552ce7bd3d9bcb" + integrity sha512-KDj11HScOaLmrPxl70KYNW1PksP4Nb/CLL2yvC+Qd2kHMPEEpfc4Re2e4FOay+bC/+XQl/7zAcWON3JVo5v3KQ== dependencies: - accepts "~1.3.4" + accepts "~1.3.8" batch "0.6.1" debug "2.6.9" escape-html "~1.0.3" - http-errors "~1.6.2" - mime-types "~2.1.17" - parseurl "~1.3.2" - -serve-static@^1.13.1, serve-static@^1.16.2: - version "1.16.2" - resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.16.2.tgz#b6a5343da47f6bdd2673848bf45754941e803296" - integrity sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw== - dependencies: - encodeurl "~2.0.0" - escape-html "~1.0.3" + http-errors "~1.8.0" + mime-types "~2.1.35" parseurl "~1.3.3" - send "0.19.0" -serve-static@~1.16.2: +serve-static@^1.13.1, serve-static@^1.16.2, serve-static@~1.16.2: version "1.16.3" resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.16.3.tgz#a97b74d955778583f3862a4f0b841eb4d5d78cf9" integrity sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA== @@ -10067,20 +10300,15 @@ setimmediate@^1.0.5: resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" integrity sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA== -setprototypeof@1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656" - integrity sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ== - setprototypeof@1.2.0, setprototypeof@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== sf-symbols-typescript@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/sf-symbols-typescript/-/sf-symbols-typescript-2.1.0.tgz#50a2d7b36edd6809606f0b0a36322fc1fdd7cfde" - integrity sha512-ezT7gu/SHTPIOEEoG6TF+O0m5eewl0ZDAO4AtdBi5HjsrUI6JdCG17+Q8+aKp0heM06wZKApRCn5olNbs0Wb/A== + version "2.2.0" + resolved "https://registry.yarnpkg.com/sf-symbols-typescript/-/sf-symbols-typescript-2.2.0.tgz#926d6e0715e3d8784cadf7658431e36581254208" + integrity sha512-TPbeg0b7ylrswdGCji8FRGFAKuqbpQlLbL8SOle3j1iHSs5Ob5mhvMAxWN2UItOjgALAB5Zp3fmMfj8mbWvXKw== shallow-clone@^3.0.0: version "3.0.1" @@ -10140,7 +10368,7 @@ side-channel-weakmap@^1.0.2: object-inspect "^1.13.3" side-channel-map "^1.0.1" -side-channel@^1.0.6, side-channel@^1.1.0: +side-channel@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.1.0.tgz#c3fcff9c4da932784873335ec9765fa94ff66bc9" integrity sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw== @@ -10156,6 +10384,11 @@ signal-exit@^3.0.2, signal-exit@^3.0.3, signal-exit@^3.0.7: resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== +signal-exit@^4.0.1: + version "4.1.0" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" + integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== + simple-plist@^1.1.0: version "1.3.1" resolved "https://registry.yarnpkg.com/simple-plist/-/simple-plist-1.3.1.tgz#16e1d8f62c6c9b691b8383127663d834112fb017" @@ -10321,12 +10554,7 @@ stacktrace-parser@^0.1.10: dependencies: type-fest "^0.7.1" -statuses@2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" - integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== - -"statuses@>= 1.4.0 < 2", statuses@~1.5.0: +"statuses@>= 1.5.0 < 2", statuses@~1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" integrity sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA== @@ -10480,13 +10708,6 @@ strip-json-comments@~2.0.1: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" integrity sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ== -strip-outer@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/strip-outer/-/strip-outer-1.0.1.tgz#b2fd2abf6604b9d1e6013057195df836b8a9d631" - integrity sha512-k55yxKHwaXnpYGsOzg4Vl8+tDrWylxDEpknGjhTiZB8dFRU5rTo9CAzeycivxV3s+zlTKwrs6WxMxR95n26kwg== - dependencies: - escape-string-regexp "^1.0.2" - strnum@^1.1.1: version "1.1.2" resolved "https://registry.yarnpkg.com/strnum/-/strnum-1.1.2.tgz#57bca4fbaa6f271081715dbc9ed7cee5493e28e4" @@ -10561,7 +10782,7 @@ supports-preserve-symlinks-flag@^1.0.0: resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== -tapable@^2.0.0, tapable@^2.2.0, tapable@^2.3.0: +tapable@^2.0.0, tapable@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.3.0.tgz#7e3ea6d5ca31ba8e078b560f0d83ce9a14aa8be6" integrity sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg== @@ -10602,9 +10823,9 @@ terser-webpack-plugin@5.3.16, terser-webpack-plugin@^5.3.16: terser "^5.31.1" terser@^5.10.0, terser@^5.15.0, terser@^5.31.1: - version "5.44.1" - resolved "https://registry.yarnpkg.com/terser/-/terser-5.44.1.tgz#e391e92175c299b8c284ad6ded609e37303b0a9c" - integrity sha512-t/R3R/n0MSwnnazuPpPNVO60LX0SKL45pyl9YlvxIdkH0Of7D5qM2EVe+yASRIlY5pZ73nclYJfNANGWPwFDZw== + version "5.46.0" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.46.0.tgz#1b81e560d584bbdd74a8ede87b4d9477b0ff9695" + integrity sha512-jTwoImyr/QbOWFFso3YoU3ik0jBBDJ6JTOQiy/J2YxVJdZCc+5u7skhNwiOR3FQIygFqVUPHl7qbbxtjW2K3Qg== dependencies: "@jridgewell/source-map" "^0.3.3" acorn "^8.15.0" @@ -10689,13 +10910,6 @@ tree-dump@^1.0.3, tree-dump@^1.1.0: resolved "https://registry.yarnpkg.com/tree-dump/-/tree-dump-1.1.0.tgz#ab29129169dc46004414f5a9d4a3c6e89f13e8a4" integrity sha512-rMuvhU4MCDbcbnleZTFezWsaZXRFemSqAM+7jPnzUl1fo9w3YEKOxAeui0fz3OI4EU4hf23iyA7uQRVko+UaBA== -trim-repeated@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/trim-repeated/-/trim-repeated-1.0.0.tgz#e3646a2ea4e891312bf7eace6cfb05380bc01c21" - integrity sha512-pkonvlKk8/ZuR0D5tLW8ljt5I8kmxp2XKymhepUeOdCEfKpZaktSArkLHZt76OB1ZvO9bssUsDty4SWhLvZpLg== - dependencies: - escape-string-regexp "^1.0.2" - ts-api-utils@^1.3.0: version "1.4.3" resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.4.3.tgz#bfc2215fe6528fecab2b0fba570a2e8a4263b064" @@ -10729,11 +10943,11 @@ tsutils@^3.21.0: tslib "^1.8.1" tsx@^4.19.3: - version "4.20.6" - resolved "https://registry.yarnpkg.com/tsx/-/tsx-4.20.6.tgz#8fb803fd9c1f70e8ccc93b5d7c5e03c3979ccb2e" - integrity sha512-ytQKuwgmrrkDTFP4LjR0ToE2nqgy886GpvRSpU0JAnrdBYppuY5rLkRUYPU1yCryb24SsKBTL/hlDQAEFVwtZg== + version "4.21.0" + resolved "https://registry.yarnpkg.com/tsx/-/tsx-4.21.0.tgz#32aa6cf17481e336f756195e6fe04dae3e6308b1" + integrity sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw== dependencies: - esbuild "~0.25.0" + esbuild "~0.27.0" get-tsconfig "^4.7.5" optionalDependencies: fsevents "~2.3.3" @@ -10772,6 +10986,11 @@ type-fest@^0.7.1: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.7.1.tgz#8dda65feaf03ed78f0a3f9678f1869147f7c5c48" integrity sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg== +type-fest@^4.26.1: + version "4.41.0" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-4.41.0.tgz#6ae1c8e5731273c2bf1f58ad39cbae2c91a46c58" + integrity sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA== + type-is@~1.6.18: version "1.6.18" resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" @@ -10830,15 +11049,15 @@ typedarray-to-buffer@4.0.0: resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-4.0.0.tgz#cdd2933c61dd3f5f02eda5d012d441f95bfeb50a" integrity sha512-6dOYeZfS3O9RtRD1caom0sMxgK59b27+IwoNy8RDPsmslSGOyU+mpTamlaIW7aNKi90ZQZ9DFaZL3YRoiSCULQ== -typescript-eslint@8.53.0: - version "8.53.0" - resolved "https://registry.yarnpkg.com/typescript-eslint/-/typescript-eslint-8.53.0.tgz#c35ca6403cd381753aee325f67e10d6101d55f04" - integrity sha512-xHURCQNxZ1dsWn0sdOaOfCSQG0HKeqSj9OexIxrz6ypU6wHYOdX2I3D2b8s8wFSsSOYJb+6q283cLiLlkEsBYw== +typescript-eslint@8.55.0: + version "8.55.0" + resolved "https://registry.yarnpkg.com/typescript-eslint/-/typescript-eslint-8.55.0.tgz#abae8295c5f0f82f816218113a46e89bc30c3de2" + integrity sha512-HE4wj+r5lmDVS9gdaN0/+iqNvPZwGfnJ5lZuz7s5vLlg9ODw0bIiiETaios9LvFI1U94/VBXGm3CB2Y5cNFMpw== dependencies: - "@typescript-eslint/eslint-plugin" "8.53.0" - "@typescript-eslint/parser" "8.53.0" - "@typescript-eslint/typescript-estree" "8.53.0" - "@typescript-eslint/utils" "8.53.0" + "@typescript-eslint/eslint-plugin" "8.55.0" + "@typescript-eslint/parser" "8.55.0" + "@typescript-eslint/typescript-estree" "8.55.0" + "@typescript-eslint/utils" "8.55.0" typescript@5.9.3: version "5.9.3" @@ -10925,7 +11144,7 @@ universalify@^2.0.0: resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.1.tgz#168efc2180964e6386d061e094df61afe239b18d" integrity sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw== -unpipe@1.0.0, unpipe@~1.0.0: +unpipe@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== @@ -10938,18 +11157,10 @@ unplugin@2.1.0: acorn "^8.14.0" webpack-virtual-modules "^0.6.2" -update-browserslist-db@^1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.1.4.tgz#7802aa2ae91477f255b86e0e46dbc787a206ad4a" - integrity sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A== - dependencies: - escalade "^3.2.0" - picocolors "^1.1.1" - update-browserslist-db@^1.2.0: - version "1.2.2" - resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.2.2.tgz#cfb4358afa08b3d5731a2ecd95eebf4ddef8033e" - integrity sha512-E85pfNzMQ9jpKkA7+TJAi4TJN+tBCuWh5rUcS/sv6cFi+1q9LYDwDI5dpUL0u/73EElyQ8d3TEaeW4sPedBqYA== + version "1.2.3" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz#64d76db58713136acbeb4c49114366cc6cc2e80d" + integrity sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w== dependencies: escalade "^3.2.0" picocolors "^1.1.1" @@ -11062,10 +11273,10 @@ warn-once@^0.1.0, warn-once@^0.1.1: resolved "https://registry.yarnpkg.com/warn-once/-/warn-once-0.1.1.tgz#952088f4fb56896e73fd4e6a3767272a3fccce43" integrity sha512-VkQZJbO8zVImzYFteBXvBOZEl1qL175WH8VmZcxF2fZAoudNhNDvHi+doCaAEdU2l2vtcIwa2zn0QK5+I1HQ3Q== -watchpack@^2.4.4: - version "2.4.4" - resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.4.4.tgz#473bda72f0850453da6425081ea46fc0d7602947" - integrity sha512-c5EGNOiyxxV5qmTtAB7rbiXxi1ooX1pQKMLX/MIabJjRA0SJBQOjKF+KSVfHkr9U1cADPon0mRiVe/riyaiDUA== +watchpack@^2.5.1: + version "2.5.1" + resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.5.1.tgz#dd38b601f669e0cbf567cb802e75cead82cde102" + integrity sha512-Zn5uXdcFNIA1+1Ei5McRd+iRzfhENPCe7LeABkJtNulSxjma+l7ltNx55BWZkRlwRnpOgHqxnjyaDgJnNXnqzg== dependencies: glob-to-regexp "^0.4.1" graceful-fs "^4.1.2" @@ -11178,10 +11389,10 @@ webpack-virtual-modules@^0.6.2: resolved "https://registry.yarnpkg.com/webpack-virtual-modules/-/webpack-virtual-modules-0.6.2.tgz#057faa9065c8acf48f24cb57ac0e77739ab9a7e8" integrity sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ== -webpack@5.104.1: - version "5.104.1" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.104.1.tgz#94bd41eb5dbf06e93be165ba8be41b8260d4fb1a" - integrity sha512-Qphch25abbMNtekmEGJmeRUhLDbe+QfiWTiqpKYkpCOWY64v9eyl+KRRLmqOFA2AvKPpc9DC6+u2n76tQLBoaA== +webpack@5.105.2: + version "5.105.2" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.105.2.tgz#f3b76f9fc36f1152e156e63ffda3bbb82e6739ea" + integrity sha512-dRXm0a2qcHPUBEzVk8uph0xWSjV/xZxenQQbLwnwP7caQCYpqG1qddwlyEkIDkYn0K8tvmcrZ+bOrzoQ3HxCDw== dependencies: "@types/eslint-scope" "^3.7.7" "@types/estree" "^1.0.8" @@ -11193,7 +11404,7 @@ webpack@5.104.1: acorn-import-phases "^1.0.3" browserslist "^4.28.1" chrome-trace-event "^1.0.2" - enhanced-resolve "^5.17.4" + enhanced-resolve "^5.19.0" es-module-lexer "^2.0.0" eslint-scope "5.1.1" events "^3.2.0" @@ -11206,7 +11417,7 @@ webpack@5.104.1: schema-utils "^4.3.3" tapable "^2.3.0" terser-webpack-plugin "^5.3.16" - watchpack "^2.4.4" + watchpack "^2.5.1" webpack-sources "^3.3.3" websocket-driver@>=0.5.1, websocket-driver@^0.7.4: @@ -11291,9 +11502,9 @@ which-module@^2.0.0: integrity sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ== which-typed-array@^1.1.16, which-typed-array@^1.1.19, which-typed-array@^1.1.2: - version "1.1.19" - resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.19.tgz#df03842e870b6b88e117524a4b364b6fc689f956" - integrity sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw== + version "1.1.20" + resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.20.tgz#3fdb7adfafe0ea69157b1509f3a1cd892bd1d122" + integrity sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg== dependencies: available-typed-arrays "^1.0.7" call-bind "^1.0.8" @@ -11369,9 +11580,9 @@ ws@^7, ws@^7.5.10: integrity sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ== ws@^8.12.1, ws@^8.18.0: - version "8.18.3" - resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.3.tgz#b56b88abffde62791c639170400c93dcb0c95472" - integrity sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg== + version "8.19.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.19.0.tgz#ddc2bdfa5b9ad860204f5a72a4863a8895fd8c8b" + integrity sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg== wsl-utils@^0.1.0: version "0.1.0" @@ -11427,9 +11638,9 @@ yallist@^5.0.0: integrity sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw== yaml@^2.2.1, yaml@^2.2.2, yaml@^2.6.1: - version "2.8.1" - resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.8.1.tgz#1870aa02b631f7e8328b93f8bc574fac5d6c4d79" - integrity sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw== + version "2.8.2" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.8.2.tgz#5694f25eca0ce9c3e7a9d9e00ce0ddabbd9e35c5" + integrity sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A== yargs-parser@^18.1.2: version "18.1.3" @@ -11444,6 +11655,11 @@ yargs-parser@^21.1.1: resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== +yargs-parser@^22.0.0: + version "22.0.0" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-22.0.0.tgz#87b82094051b0567717346ecd00fd14804b357c8" + integrity sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw== + yargs@^15.1.0: version "15.4.1" resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.4.1.tgz#0d87a16de01aee9d8bec2bfbf74f67851730f4f8" @@ -11503,11 +11719,11 @@ zod@^3.22.4: integrity sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ== "zod@^3.25.0 || ^4.0.0": - version "4.1.12" - resolved "https://registry.yarnpkg.com/zod/-/zod-4.1.12.tgz#64f1ea53d00eab91853195653b5af9eee68970f0" - integrity sha512-JInaHOamG8pt5+Ey8kGmdcAcg3OL9reK8ltczgHTAwNhMys/6ThXHityHxVV2p3fkw/c+MAvBHFVYHFZDmjMCQ== - -zustand@5.0.10: - version "5.0.10" - resolved "https://registry.yarnpkg.com/zustand/-/zustand-5.0.10.tgz#4db510c0c4c25a5f1ae43227b307ddf1641a3210" - integrity sha512-U1AiltS1O9hSy3rul+Ub82ut2fqIAefiSuwECWt6jlMVUGejvf+5omLcRBSzqbRagSM3hQZbtzdeRc6QVScXTg== + version "4.3.6" + resolved "https://registry.yarnpkg.com/zod/-/zod-4.3.6.tgz#89c56e0aa7d2b05107d894412227087885ab112a" + integrity sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg== + +zustand@5.0.11: + version "5.0.11" + resolved "https://registry.yarnpkg.com/zustand/-/zustand-5.0.11.tgz#99f912e590de1ca9ce6c6d1cab6cdb1f034ab494" + integrity sha512-fdZY+dk7zn/vbWNCYmzZULHRrss0jx5pPFiOuMZ/5HJN6Yv3u+1Wswy/4MpZEkEGhtNH+pwxZB8OKgUBPzYAGg== From 3a4b3c2f5a474737182e411666a2b5c9a1c1d760 Mon Sep 17 00:00:00 2001 From: chrisnojima Date: Fri, 13 Feb 2026 16:15:23 -0500 Subject: [PATCH 002/112] simplify small chat (#28901) --- shared/chat/inbox/container.tsx | 194 +++--- shared/chat/inbox/defer-loading.tsx | 8 +- shared/chat/inbox/index.desktop.tsx | 18 - shared/chat/inbox/index.native.tsx | 5 - shared/chat/inbox/new-chat-button.tsx | 64 +- shared/chat/inbox/row/big-team-channel.tsx | 61 +- shared/chat/inbox/row/big-team-header.tsx | 45 +- shared/chat/inbox/row/big-teams-divider.tsx | 11 +- shared/chat/inbox/row/big-teams-label.tsx | 19 +- .../chat/inbox/row/small-team/bottom-line.tsx | 339 ---------- shared/chat/inbox/row/small-team/contexts.tsx | 8 - shared/chat/inbox/row/small-team/index.tsx | 623 ++++++++++++++---- shared/chat/inbox/row/small-team/top-line.tsx | 195 ------ shared/chat/inbox/search-row.tsx | 76 +-- shared/chat/selectable-big-team-channel.tsx | 12 +- shared/chat/selectable-small-team.tsx | 12 +- 16 files changed, 703 insertions(+), 987 deletions(-) delete mode 100644 shared/chat/inbox/row/small-team/bottom-line.tsx delete mode 100644 shared/chat/inbox/row/small-team/contexts.tsx delete mode 100644 shared/chat/inbox/row/small-team/top-line.tsx diff --git a/shared/chat/inbox/container.tsx b/shared/chat/inbox/container.tsx index 1bd795aef1d2..09cc8d1e3397 100644 --- a/shared/chat/inbox/container.tsx +++ b/shared/chat/inbox/container.tsx @@ -2,7 +2,7 @@ import * as C from '@/constants' import * as Chat from '@/stores/chat2' import * as React from 'react' import * as T from '@/constants/types' -import Inbox, {type Props} from '.' +import Inbox from '.' import {useIsFocused} from '@react-navigation/core' import type { ChatInboxRowItemBig, @@ -64,19 +64,14 @@ const makeSmallRows = ( }) } -type WrapperProps = Pick< - Props, - | 'isSearching' - | 'navKey' - | 'neverLoaded' - | 'rows' - | 'smallTeamsExpanded' - | 'unreadIndices' - | 'unreadTotal' - | 'selectedConversationIDKey' -> - -const InboxWrapper = React.memo(function InboxWrapper(props: WrapperProps) { +const noSmallTeams = new Array() +const noBigTeams = new Array() +const emptyMap = new Map() + +const Connected = (ownProps: OwnProps) => { + const {navKey, conversationIDKey} = ownProps + const isFocused = useIsFocused() + const chatState = Chat.useChatState( C.useShallow(s => { const inboxNumSmallRows = s.inboxNumSmallRows ?? 5 @@ -87,38 +82,38 @@ const InboxWrapper = React.memo(function InboxWrapper(props: WrapperProps) { return { allowShowFloatingButton, inboxHasLoaded: s.inboxHasLoaded, + inboxLayout, inboxNumSmallRows, inboxRefresh: s.dispatch.inboxRefresh, + isSearching: !!s.inboxSearch, queueMetaToRequest: s.dispatch.queueMetaToRequest, setInboxNumSmallRows: s.dispatch.setInboxNumSmallRows, + smallTeamsExpanded: s.smallTeamsExpanded, toggleSmallTeamsExpanded: s.dispatch.toggleSmallTeamsExpanded, } }) ) - const {allowShowFloatingButton, inboxHasLoaded, inboxNumSmallRows, inboxRefresh} = chatState - const {queueMetaToRequest, setInboxNumSmallRows, toggleSmallTeamsExpanded} = chatState - const isFocused = useIsFocused() + const { + allowShowFloatingButton, inboxHasLoaded, inboxLayout, inboxNumSmallRows, + inboxRefresh, isSearching, queueMetaToRequest, setInboxNumSmallRows, + smallTeamsExpanded, toggleSmallTeamsExpanded, + } = chatState const appendNewChatBuilder = C.useRouterState(s => s.appendNewChatBuilder) - // a hack to have it check for marked as read when we mount as the focus events don't fire always - const onNewChat = appendNewChatBuilder - const onUntrustedInboxVisible = queueMetaToRequest - const [lastIsFocused, setLastIsFocused] = React.useState(isFocused) - - if (lastIsFocused !== isFocused) { - setLastIsFocused(isFocused) - if (C.isMobile) { - if (isFocused && Chat.isSplit) { - setTimeout(() => { - Chat.getConvoState(Chat.getSelectedConversation()).dispatch.tabSelected() - }, 0) - } + const selectedConversationIDKey = conversationIDKey ?? Chat.noConversationIDKey + + // Handle focus changes on mobile + const prevIsFocusedRef = React.useRef(isFocused) + React.useEffect(() => { + if (prevIsFocusedRef.current === isFocused) return + prevIsFocusedRef.current = isFocused + if (C.isMobile && isFocused && Chat.isSplit) { + Chat.getConvoState(Chat.getSelectedConversation()).dispatch.tabSelected() } - } + }, [isFocused]) C.useOnMountOnce(() => { if (!C.isMobile) { - // On mobile this is taken care of by NavigationEvents. Chat.getConvoState(Chat.getSelectedConversation()).dispatch.tabSelected() } if (!inboxHasLoaded) { @@ -134,89 +129,45 @@ const InboxWrapper = React.memo(function InboxWrapper(props: WrapperProps) { }, [inboxHasLoaded, inboxRefresh]) ) - return ( - - ) -}) - -const noSmallTeams = new Array() -const noBigTeams = new Array() -const Connected = (ownProps: OwnProps) => { - const chatState = Chat.useChatState( - C.useShallow(s => ({ - inboxHasLoaded: s.inboxHasLoaded, - inboxLayout: s.inboxLayout, - inboxNumSmallRows: s.inboxNumSmallRows ?? 5, - isSearching: !!s.inboxSearch, - smallTeamsExpanded: s.smallTeamsExpanded, - })) - ) - const {inboxHasLoaded, inboxLayout: _inboxLayout, inboxNumSmallRows} = chatState - const {isSearching, smallTeamsExpanded} = chatState - const {conversationIDKey} = ownProps - const neverLoaded = !inboxHasLoaded - const selectedConversationIDKey = conversationIDKey ?? Chat.noConversationIDKey - const {navKey} = ownProps - const bigTeams = _inboxLayout ? _inboxLayout.bigTeams || noBigTeams : noBigTeams + // Compute rows + const bigTeams = inboxLayout?.bigTeams || noBigTeams const showAllSmallRows = smallTeamsExpanded || !bigTeams.length - const allSmallTeams = _inboxLayout ? _inboxLayout.smallTeams || noSmallTeams : noSmallTeams + const allSmallTeams = inboxLayout?.smallTeams || noSmallTeams const smallTeamsBelowTheFold = !showAllSmallRows && allSmallTeams.length > inboxNumSmallRows - const smallTeams = React.useMemo(() => { - if (!showAllSmallRows) { - return allSmallTeams.slice(0, inboxNumSmallRows) - } else { - return allSmallTeams - } - }, [showAllSmallRows, inboxNumSmallRows, allSmallTeams]) - const smallRows = React.useMemo(() => { - return makeSmallRows(smallTeams) - }, [smallTeams]) - - const bigRows = React.useMemo(() => { - return makeBigRows(bigTeams) - }, [bigTeams]) + const smallTeams = showAllSmallRows ? allSmallTeams : allSmallTeams.slice(0, inboxNumSmallRows) + const smallRows = makeSmallRows(smallTeams) + const bigRows = makeBigRows(bigTeams) const hasAllSmallTeamConvs = - (_inboxLayout?.smallTeams?.length ?? 0) === (_inboxLayout?.totalSmallTeams ?? 0) - const divider: Array = React.useMemo(() => { - return bigRows.length !== 0 || !hasAllSmallTeamConvs + (inboxLayout?.smallTeams?.length ?? 0) === (inboxLayout?.totalSmallTeams ?? 0) + const divider: Array = + bigRows.length !== 0 || !hasAllSmallTeamConvs ? [{showButton: !hasAllSmallTeamConvs || smallTeamsBelowTheFold, type: 'divider'}] : [] - }, [bigRows.length, hasAllSmallTeamConvs, smallTeamsBelowTheFold]) - - const rows: Array = React.useMemo(() => { - const builderAfterSmall = new Array() - const builderAfterDivider = new Array() - const builderAfterBig = new Array() - const teamBuilder: ChatInboxRowItemTeamBuilder = {type: 'teamBuilder'} - if (smallRows.length !== 0) { - if (bigRows.length === 0) { - if (divider.length !== 0) { - builderAfterDivider.push(teamBuilder) - } else { - builderAfterSmall.push(teamBuilder) - } + + const teamBuilder: ChatInboxRowItemTeamBuilder = {type: 'teamBuilder'} + const builderAfterSmall: Array = [] + const builderAfterDivider: Array = [] + const builderAfterBig: Array = [] + if (smallRows.length !== 0) { + if (bigRows.length === 0) { + if (divider.length !== 0) { + builderAfterDivider.push(teamBuilder) } else { - builderAfterBig.push(teamBuilder) + builderAfterSmall.push(teamBuilder) } + } else { + builderAfterBig.push(teamBuilder) } - return [ - ...smallRows, - ...builderAfterSmall, - ...divider, - ...builderAfterDivider, - ...bigRows, - ...builderAfterBig, - ] - }, [bigRows, smallRows, divider]) + } + const rows: Array = [ + ...smallRows, + ...builderAfterSmall, + ...divider, + ...builderAfterDivider, + ...bigRows, + ...builderAfterBig, + ] const unreadIndices = Chat.useChatState( C.useShallow(s => @@ -233,19 +184,24 @@ const Connected = (ownProps: OwnProps) => { const unreadTotal = Array.from(unreadIndices.values()).reduce((acc, val) => acc + val, 0) - const props = { - isSearching, - navKey, - neverLoaded, - rows, - selectedConversationIDKey, - smallTeamsExpanded: smallTeamsExpanded || bigTeams.length === 0, - unreadIndices: unreadIndices.size ? unreadIndices : emptyMap, - unreadTotal, - } - return + return ( + + ) } -const emptyMap = new Map() - export default Connected diff --git a/shared/chat/inbox/defer-loading.tsx b/shared/chat/inbox/defer-loading.tsx index 7da11da3fbbd..2bf52ccb9668 100644 --- a/shared/chat/inbox/defer-loading.tsx +++ b/shared/chat/inbox/defer-loading.tsx @@ -5,7 +5,7 @@ import {useIsFocused, useNavigationState} from '@react-navigation/core' // keep track of this even on unmount, else if you background / foreground you'll lose it let _everFocused = false -const Deferred = React.memo(function Deferred() { +export default function Deferred() { const [visible, setVisible] = React.useState(_everFocused) const isFocused = useIsFocused() const navKey = useNavigationState(state => state.key) @@ -27,10 +27,4 @@ const Deferred = React.memo(function Deferred() { }, [isFocused, visible]) return visible ? : null -}) - -const DeferredOuter = () => { - return } - -export default DeferredOuter diff --git a/shared/chat/inbox/index.desktop.tsx b/shared/chat/inbox/index.desktop.tsx index c40b4875c1ea..2cc0d2efd57e 100644 --- a/shared/chat/inbox/index.desktop.tsx +++ b/shared/chat/inbox/index.desktop.tsx @@ -462,10 +462,6 @@ const styles = Kb.Styles.styleSheetCreate( position: 'relative', }, }), - divider: { - backgroundColor: 'purple', - overflow: 'hidden', - }, fakeAvatar: Kb.Styles.platformStyles({ isElectron: { backgroundColor: Kb.Styles.globalColors.black_10, @@ -482,11 +478,6 @@ const styles = Kb.Styles.styleSheetCreate( width: '100%', }, }), - fakeRemovingRowDivider: { - position: 'absolute', - top: 0, - width: '100%', - }, fakeRow: Kb.Styles.platformStyles({ isElectron: { backgroundColor: Kb.Styles.globalColors.blueGrey, @@ -502,11 +493,6 @@ const styles = Kb.Styles.styleSheetCreate( right: 0, zIndex: 9999, }, - fakeRowDivider: { - bottom: 0, - position: 'absolute', - width: '100%', - }, fakeText: { flexGrow: 1, height: '100%', @@ -554,14 +540,10 @@ const styles = Kb.Styles.styleSheetCreate( paddingTop: 1, width: Kb.Styles.globalMargins.small, }, - hover: {backgroundColor: Kb.Styles.globalColors.blueGreyDark}, list: { flex: 1, height: '100%', }, - rowWithDragger: { - height: 68, - }, spacer: { backgroundColor: Kb.Styles.globalColors.blueGrey, bottom: 0, diff --git a/shared/chat/inbox/index.native.tsx b/shared/chat/inbox/index.native.tsx index e1aa6aaf9ceb..42bf1c36d096 100644 --- a/shared/chat/inbox/index.native.tsx +++ b/shared/chat/inbox/index.native.tsx @@ -400,11 +400,6 @@ const styles = Kb.Styles.styleSheetCreate( () => ({ button: {width: '100%'}, - buttonBar: { - alignItems: 'flex-end', - alignSelf: 'flex-end', - justifyContent: 'flex-end', - }, container: Kb.Styles.platformStyles({ common: { ...Kb.Styles.globalStyles.flexBoxColumn, diff --git a/shared/chat/inbox/new-chat-button.tsx b/shared/chat/inbox/new-chat-button.tsx index bb086547ac38..62445e915d25 100644 --- a/shared/chat/inbox/new-chat-button.tsx +++ b/shared/chat/inbox/new-chat-button.tsx @@ -1,6 +1,5 @@ import * as C from '@/constants' import * as Chat from '@/stores/chat2' -import * as React from 'react' import * as Kb from '@/common-adapters' const HeaderNewChatButton = () => { @@ -12,45 +11,38 @@ const HeaderNewChatButton = () => { (s.inboxLayout.bigTeams || []).length === 0 ) - const appendNewChatBuilder = C.useRouterState(s => s.appendNewChatBuilder) - const onNewChat = React.useCallback(() => { - appendNewChatBuilder() - }, [appendNewChatBuilder]) - const content = React.useMemo(() => { - return ( - - - - - - - - + const onNewChat = C.useRouterState(s => s.appendNewChatBuilder) + + if (hide) return null + + return ( + + + + + + - ) - }, [onNewChat]) - return hide ? null : content + + + ) } const styles = Kb.Styles.styleSheetCreate( () => ({ - button: { - marginLeft: Kb.Styles.globalMargins.small, - marginRight: Kb.Styles.globalMargins.small, - }, gradientContainer: Kb.Styles.platformStyles({ isElectron: { height: '100%', @@ -83,10 +75,6 @@ const styles = Kb.Styles.styleSheetCreate( }, }), gradientYellow: {backgroundColor: '#FFF75A', flex: 1}, - newMeta: { - alignSelf: 'center', - marginRight: Kb.Styles.globalMargins.tiny, - }, rainbowButton: Kb.Styles.platformStyles({ common: { margin: 2, diff --git a/shared/chat/inbox/row/big-team-channel.tsx b/shared/chat/inbox/row/big-team-channel.tsx index e4089507b3b3..f80b156a94fa 100644 --- a/shared/chat/inbox/row/big-team-channel.tsx +++ b/shared/chat/inbox/row/big-team-channel.tsx @@ -1,6 +1,7 @@ +import * as C from '@/constants' import * as Chat from '@/stores/chat2' +import type * as React from 'react' import * as Kb from '@/common-adapters' -import * as React from 'react' import * as RowSizes from './sizes' import * as T from '@/constants/types' @@ -12,36 +13,43 @@ type Props = { layoutSnippetDecoration?: T.RPCChat.SnippetDecoration } -const BigTeamChannel = React.memo(function BigTeamChannel(props: Props) { +const BigTeamChannel = (props: Props) => { return ( - + ) -}) -const BigTeamChannelImpl = (props: Props) => { +} +const BigTeamChannelInner = (props: Props) => { const {selected, layoutChannelname, layoutSnippetDecoration} = props - const channelname = Chat.useChatContext(s => s.meta.channelname || layoutChannelname) - const isError = Chat.useChatContext(s => s.meta.trustedState === 'error') - const snippetDecoration = Chat.useChatContext(s => { - const d = - s.meta.conversationIDKey === Chat.noConversationIDKey - ? (layoutSnippetDecoration ?? T.RPCChat.SnippetDecoration.none) - : s.meta.snippetDecoration - - switch (d) { - case T.RPCChat.SnippetDecoration.pendingMessage: - case T.RPCChat.SnippetDecoration.failedPendingMessage: - return d - default: - return 0 - } - }) - const hasBadge = Chat.useChatContext(s => s.badge > 0) - const hasDraft = Chat.useChatContext(s => !!s.meta.draft) - const hasUnread = Chat.useChatContext(s => s.unread > 0) - const isMuted = Chat.useChatContext(s => s.meta.isMuted) - const navigateToThread = Chat.useChatContext(s => s.dispatch.navigateToThread) + const {channelname, isError, snippetDecoration, hasBadge, hasDraft, hasUnread, isMuted, navigateToThread} = + Chat.useChatContext( + C.useShallow(s => { + const d = + s.meta.conversationIDKey === Chat.noConversationIDKey + ? (layoutSnippetDecoration ?? T.RPCChat.SnippetDecoration.none) + : s.meta.snippetDecoration + let snippetDecoration: number + switch (d) { + case T.RPCChat.SnippetDecoration.pendingMessage: + case T.RPCChat.SnippetDecoration.failedPendingMessage: + snippetDecoration = d + break + default: + snippetDecoration = 0 + } + return { + channelname: s.meta.channelname || layoutChannelname, + hasBadge: s.badge > 0, + hasDraft: !!s.meta.draft, + hasUnread: s.unread > 0, + isError: s.meta.trustedState === 'error', + isMuted: s.meta.isMuted, + navigateToThread: s.dispatch.navigateToThread, + snippetDecoration, + } + }) + ) const onSelectConversation = () => navigateToThread('inboxBig') let outboxTooltip: string | undefined @@ -155,7 +163,6 @@ const styles = Kb.Styles.styleSheetCreate( ({ channelBackground: Kb.Styles.platformStyles({ common: { - ...Kb.Styles.globalStyles.flexBoxRow, alignItems: 'center', marginLeft: Kb.Styles.globalMargins.large, paddingRight: Kb.Styles.globalMargins.xsmall, diff --git a/shared/chat/inbox/row/big-team-header.tsx b/shared/chat/inbox/row/big-team-header.tsx index 0fc4172f6497..d5359603db77 100644 --- a/shared/chat/inbox/row/big-team-header.tsx +++ b/shared/chat/inbox/row/big-team-header.tsx @@ -2,7 +2,6 @@ import * as C from '@/constants' import * as Chat from '@/stores/chat2' import * as Kb from '@/common-adapters' import * as Teams from '@/stores/teams' -import * as React from 'react' import * as RowSizes from './sizes' import type * as T from '@/constants/types' import TeamMenu from '@/chat/conversation/info-panel/menu' @@ -13,37 +12,34 @@ type Props = { teamID: T.Teams.TeamID } -const BigTeamHeader = React.memo(function BigTeamHeader(props: Props) { +const BigTeamHeader = (props: Props) => { return ( - + ) -}) -const BigTeamHeaderImpl = (props: Props) => { +} +const BigTeamHeaderInner = (props: Props) => { const {teamID, teamname} = props const badgeSubscribe = Teams.useTeamsState(s => !Teams.isTeamWithChosenChannels(s, teamname)) const navigateAppend = C.useRouterState(s => s.dispatch.navigateAppend) const onClick = () => navigateAppend({props: {teamID}, selected: 'team'}) - const makePopup = React.useCallback( - (p: Kb.Popup2Parms) => { - const {attachTo, hidePopup} = p - return ( - - - - ) - }, - [teamID] - ) + const makePopup = (p: Kb.Popup2Parms) => { + const {attachTo, hidePopup} = p + return ( + + + + ) + } const {showPopup, popup, popupAnchor} = Kb.usePopup2(makePopup) return ( @@ -97,9 +93,6 @@ const styles = Kb.Styles.styleSheetCreate( borderWidth: 2, }, showMenu: Kb.Styles.platformStyles({ - common: { - ...Kb.Styles.globalStyles.flexBoxRow, - }, isElectron: { alignSelf: 'center', position: 'relative', diff --git a/shared/chat/inbox/row/big-teams-divider.tsx b/shared/chat/inbox/row/big-teams-divider.tsx index 547f2e267670..944193379468 100644 --- a/shared/chat/inbox/row/big-teams-divider.tsx +++ b/shared/chat/inbox/row/big-teams-divider.tsx @@ -1,5 +1,4 @@ import * as Chat from '@/stores/chat2' -import * as React from 'react' import * as Kb from '@/common-adapters' import * as RowSizes from './sizes' import * as T from '@/constants/types' @@ -10,7 +9,7 @@ type Props = { onEdit?: () => void } -const BigTeamsDivider = React.memo(function BigTeamsDivider(props: Props) { +const BigTeamsDivider = (props: Props) => { const {toggle, onEdit} = props const badgeCount = Chat.useChatState(s => s.bigTeamBadgeCount) return ( @@ -29,9 +28,9 @@ const BigTeamsDivider = React.memo(function BigTeamsDivider(props: Props) { > {badgeCount > 0 && } - + - + {onEdit ? ( @@ -42,7 +41,7 @@ const BigTeamsDivider = React.memo(function BigTeamsDivider(props: Props) { ) -}) +} const styles = Kb.Styles.styleSheetCreate( () => @@ -94,8 +93,6 @@ const styles = Kb.Styles.styleSheetCreate( edit: {justifyContent: 'center'}, icon: { ...Kb.Styles.globalStyles.fillAbsolute, - ...Kb.Styles.globalStyles.flexBoxRow, - alignItems: 'flex-start', justifyContent: 'center', marginTop: Kb.Styles.isMobile ? Kb.Styles.globalMargins.tiny : Kb.Styles.globalMargins.xtiny, }, diff --git a/shared/chat/inbox/row/big-teams-label.tsx b/shared/chat/inbox/row/big-teams-label.tsx index 01346f052d5b..ff4d7ceec6ad 100644 --- a/shared/chat/inbox/row/big-teams-label.tsx +++ b/shared/chat/inbox/row/big-teams-label.tsx @@ -1,27 +1,28 @@ import * as Kb from '@/common-adapters' const BigTeamsLabel = () => ( - - + + Big teams - - + + ) const styles = Kb.Styles.styleSheetCreate(() => ({ bigTeamsLabelBox: { - ...Kb.Styles.globalStyles.flexBoxRow, - alignItems: 'center', minHeight: 24, }, container: { - ...Kb.Styles.globalStyles.flexBoxRow, - alignItems: 'center', height: Kb.Styles.isMobile ? 32 : 24, marginLeft: Kb.Styles.globalMargins.tiny, - } as const, + }, text: {backgroundColor: Kb.Styles.globalColors.fastBlank}, })) diff --git a/shared/chat/inbox/row/small-team/bottom-line.tsx b/shared/chat/inbox/row/small-team/bottom-line.tsx deleted file mode 100644 index 1dfbc213d35e..000000000000 --- a/shared/chat/inbox/row/small-team/bottom-line.tsx +++ /dev/null @@ -1,339 +0,0 @@ -import * as C from '@/constants' -import * as Chat from '@/stores/chat2' -import * as React from 'react' -import * as Kb from '@/common-adapters' -import * as T from '@/constants/types' -import {SnippetContext, SnippetDecorationContext} from './contexts' -import {useCurrentUserState} from '@/stores/current-user' - -type Props = { - layoutSnippet?: string - backgroundColor?: string - isSelected?: boolean - isInWidget?: boolean - allowBold?: boolean -} - -const SnippetDecoration = (p: {type: Kb.IconType; color: string}) => { - const {type, color} = p - return ( - - ) -} - -const Snippet = React.memo(function Snippet(p: {isSelected?: boolean; style: Kb.Styles.StylesCrossPlatform}) { - const snippet = React.useContext(SnippetContext) - const {isSelected, style} = p - - const decoration = React.useContext(SnippetDecorationContext) - let snippetDecoration: React.ReactNode - let exploded = false - const defaultIconColor = isSelected ? Kb.Styles.globalColors.white : Kb.Styles.globalColors.black_20 - let tooltip: string | undefined - - switch (decoration) { - case T.RPCChat.SnippetDecoration.pendingMessage: - tooltip = 'Sending…' - snippetDecoration = - break - case T.RPCChat.SnippetDecoration.failedPendingMessage: - tooltip = 'Failed to send' - snippetDecoration = ( - - ) - break - case T.RPCChat.SnippetDecoration.explodingMessage: - snippetDecoration = - break - case T.RPCChat.SnippetDecoration.explodedMessage: - snippetDecoration = ( - - Message exploded. - - ) - exploded = true - break - case T.RPCChat.SnippetDecoration.audioAttachment: - snippetDecoration = - break - case T.RPCChat.SnippetDecoration.videoAttachment: - snippetDecoration = - break - case T.RPCChat.SnippetDecoration.photoAttachment: - snippetDecoration = - break - case T.RPCChat.SnippetDecoration.fileAttachment: - snippetDecoration = - break - case T.RPCChat.SnippetDecoration.stellarReceived: - snippetDecoration = - break - case T.RPCChat.SnippetDecoration.stellarSent: - snippetDecoration = - break - case T.RPCChat.SnippetDecoration.pinnedMessage: - snippetDecoration = - break - default: - snippetDecoration = null - } - return ( - <> - {!!snippetDecoration && ( - - {snippetDecoration} - - )} - {!exploded && !!snippet && ( - - {snippet} - - )} - - ) -}) - -const BottomLine = React.memo(function BottomLine(p: Props) { - const {allowBold, isSelected, backgroundColor, isInWidget, layoutSnippet} = p - - const isTypingSnippet = Chat.useChatContext(s => { - const typers = !isInWidget ? s.typing : undefined - return !!typers?.size - }) - - const you = useCurrentUserState(s => s.username) - const hasUnread = Chat.useChatContext(s => s.unread > 0) - const _draft = Chat.useChatContext(s => s.meta.draft) - const {hasResetUsers, isDecryptingSnippet, participantNeedToRekey, youAreReset, youNeedToRekey} = - Chat.useChatContext( - C.useShallow(s => { - const { - membershipType, - rekeyers, - resetParticipants, - trustedState, - conversationIDKey, - snippetDecorated, - } = s.meta - const youAreReset = membershipType === 'youAreReset' - const participantNeedToRekey = rekeyers.size > 0 - const youNeedToRekey = rekeyers.has(you) - const hasResetUsers = resetParticipants.size > 0 - - // only use layout if we don't have the meta at all - const typers = !isInWidget ? s.typing : undefined - const typingSnippet = (typers?.size ?? 0) > 0 ? 't' : undefined - const maybeLayoutSnippet = - conversationIDKey === Chat.noConversationIDKey ? layoutSnippet : undefined - - const snippet = typingSnippet ?? snippetDecorated ?? maybeLayoutSnippet ?? '' - const isDecryptingSnippet = - s.id && !snippet ? trustedState === 'requesting' || trustedState === 'untrusted' : false - - return {hasResetUsers, isDecryptingSnippet, participantNeedToRekey, youAreReset, youNeedToRekey} - }) - ) - const draft = (!isSelected && !hasUnread && _draft) || '' - - const props = { - allowBold, - backgroundColor, - draft, - hasResetUsers, - hasUnread, - isDecryptingSnippet, - isSelected, - isTypingSnippet, - participantNeedToRekey, - youAreReset, - youNeedToRekey, - } - - return -}) - -type IProps = { - allowBold?: boolean - backgroundColor?: string - draft: string - hasResetUsers: boolean - hasUnread: boolean - isDecryptingSnippet: boolean - isTypingSnippet: boolean - participantNeedToRekey: boolean - youAreReset: boolean - youNeedToRekey: boolean - isSelected?: boolean -} -const BottomLineImpl = React.memo(function BottomLineImpl(p: IProps) { - const {isDecryptingSnippet, draft, youAreReset, youNeedToRekey, isSelected, allowBold = true} = p - const {isTypingSnippet, hasResetUsers, hasUnread, participantNeedToRekey, backgroundColor} = p - - const subColor = isSelected - ? Kb.Styles.globalColors.white - : hasUnread - ? Kb.Styles.globalColors.black - : Kb.Styles.globalColors.black_50 - const showBold = allowBold && !isSelected && hasUnread - - let content: React.ReactNode - const style = React.useMemo( - () => - Kb.Styles.collapseStyles([ - styles.bottomLine, - { - color: subColor, - ...(showBold ? Kb.Styles.globalStyles.fontBold : {}), - }, - isTypingSnippet ? styles.typingSnippet : null, - ]), - [isTypingSnippet, showBold, subColor] - ) - if (youNeedToRekey) { - content = null - } else if (youAreReset) { - content = ( - - You are locked out. - - ) - } else if (participantNeedToRekey) { - content = ( - - ) - } else if (draft) { - content = ( - - - Draft: - - - {draft} - - - ) - } else if (isDecryptingSnippet) { - content = ( - - ) - } else { - content = ( - - - - ) - } - return ( - - - {hasResetUsers && ( - - )} - {youNeedToRekey && ( - - )} - {content} - - - ) -}) - -const styles = Kb.Styles.styleSheetCreate( - () => - ({ - alertMeta: Kb.Styles.platformStyles({ - common: { - alignSelf: 'center', - marginRight: 6, - }, - isMobile: {marginTop: 2}, - }), - bottom: {justifyContent: 'flex-start'}, - bottomLine: Kb.Styles.platformStyles({ - isElectron: { - color: Kb.Styles.globalColors.black_50, - display: 'block', - minHeight: 16, - overflow: 'hidden', - paddingRight: 10, - textOverflow: 'ellipsis', - whiteSpace: 'nowrap', - width: '100%', - }, - isMobile: { - color: Kb.Styles.globalColors.black_50, - flex: 1, - lineHeight: 19, - paddingRight: 40, - }, - }), - contentBox: { - ...Kb.Styles.globalStyles.fillAbsolute, - alignItems: 'center', - width: '100%', - }, - draftLabel: {color: Kb.Styles.globalColors.orange}, - innerBox: Kb.Styles.platformStyles({ - common: { - ...Kb.Styles.globalStyles.flexBoxRow, - alignItems: 'center', - flexGrow: 1, - height: 17, - position: 'relative', - }, - isMobile: {height: 21}, - }), - outerBox: { - ...Kb.Styles.globalStyles.flexBoxRow, - }, - snippetDecoration: {alignSelf: 'flex-start'}, - typingSnippet: {}, - youAreResetText: Kb.Styles.platformStyles({ - isElectron: { - fontSize: 12, - lineHeight: 13, - }, - isMobile: { - fontSize: 14, - lineHeight: 19, - }, - }), - }) as const -) -export {BottomLine} diff --git a/shared/chat/inbox/row/small-team/contexts.tsx b/shared/chat/inbox/row/small-team/contexts.tsx deleted file mode 100644 index 4773e7437c6d..000000000000 --- a/shared/chat/inbox/row/small-team/contexts.tsx +++ /dev/null @@ -1,8 +0,0 @@ -import * as React from 'react' -import * as T from '@/constants/types' -// so popups will work in both places -export const SnippetContext = React.createContext('') -export const SnippetDecorationContext = React.createContext(T.RPCChat.SnippetDecoration.none) -export const ParticipantsContext = React.createContext | string>('') -export const IsTeamContext = React.createContext(false) -export const TimeContext = React.createContext(0) diff --git a/shared/chat/inbox/row/small-team/index.tsx b/shared/chat/inbox/row/small-team/index.tsx index f0289b126b34..a37186eeab84 100644 --- a/shared/chat/inbox/row/small-team/index.tsx +++ b/shared/chat/inbox/row/small-team/index.tsx @@ -1,23 +1,16 @@ import * as C from '@/constants' import * as Chat from '@/stores/chat2' -import * as React from 'react' +import type * as React from 'react' import * as Kb from '@/common-adapters' -import {SimpleTopLine} from './top-line' -import {BottomLine} from './bottom-line' -import {Avatars, TeamAvatar} from '@/chat/avatars' import * as RowSizes from '../sizes' import * as T from '@/constants/types' import SwipeConvActions from './swipe-conv-actions' import './small-team.css' +import {Avatars, TeamAvatar} from '@/chat/avatars' +import {formatTimeForConversationList} from '@/util/timestamp' import {useCurrentUserState} from '@/stores/current-user' -import { - IsTeamContext, - ParticipantsContext, - TimeContext, - SnippetContext, - SnippetDecorationContext, -} from './contexts' import {useOpenedRowState} from '../opened-row-state' +import TeamMenu from '@/chat/conversation/info-panel/menu' export type Props = { conversationIDKey: T.Chat.ConversationIDKey @@ -31,62 +24,70 @@ export type Props = { onSelectConversation?: () => void } -const SmallTeam = React.memo(function SmallTeam(p: Props) { +const SmallTeam = (p: Props) => { return ( - + ) -}) +} -const SmallTeamImpl = (p: Props) => { - const {layoutName, layoutIsTeam, layoutSnippet, isSelected, layoutTime, layoutSnippetDecoration} = p - const {isInWidget} = p +const SmallTeamInner = (p: Props) => { + const {layoutName, layoutIsTeam, layoutSnippet, isSelected, layoutTime, layoutSnippetDecoration, isInWidget} = p - const {snippet, snippetDecoration} = Chat.useChatContext( - C.useShallow(s => { - const typingSnippet = (() => { - const typers = !isInWidget ? s.typing : undefined - if (!typers?.size) return undefined - if (typers.size === 1) { - const [t] = typers - return `${t} is typing...` - } else { - return 'Multiple people typing...' - } - })() - - const {meta} = s - // only use layout if we don't have the meta at all - const maybeLayoutSnippet = - meta.conversationIDKey === Chat.noConversationIDKey ? layoutSnippet : undefined - const snippet = typingSnippet ?? meta.snippetDecorated ?? maybeLayoutSnippet ?? '' - const snippetDecoration = - meta.conversationIDKey === Chat.noConversationIDKey - ? (layoutSnippetDecoration ?? T.RPCChat.SnippetDecoration.none) - : meta.snippetDecoration - return {snippet, snippetDecoration} - }) - ) const you = useCurrentUserState(s => s.username) - const navigateToThread = Chat.useChatContext(s => s.dispatch.navigateToThread) + + const {snippet, snippetDecoration, isMuted, isLocked, hasUnread, hasBadge, timestamp, navigateToThread, teamDisplayName} = + Chat.useChatContext( + C.useShallow(s => { + const typingSnippet = (() => { + const typers = !isInWidget ? s.typing : undefined + if (!typers?.size) return undefined + if (typers.size === 1) { + const [t] = typers + return `${t} is typing...` + } else { + return 'Multiple people typing...' + } + })() + const {meta} = s + const maybeLayoutSnippet = + meta.conversationIDKey === Chat.noConversationIDKey ? layoutSnippet : undefined + const snippet = typingSnippet ?? meta.snippetDecorated ?? maybeLayoutSnippet ?? '' + const snippetDecoration = + meta.conversationIDKey === Chat.noConversationIDKey + ? (layoutSnippetDecoration ?? T.RPCChat.SnippetDecoration.none) + : meta.snippetDecoration + const metaTeamname = (meta.teamname || (layoutIsTeam ? layoutName : '')) || '' + const channelname = isInWidget ? meta.channelname : '' + const teamDisplayName = metaTeamname + ? channelname ? `${metaTeamname}#${channelname}` : metaTeamname + : '' + + return { + hasBadge: s.badge > 0, + hasUnread: s.unread > 0, + isLocked: meta.rekeyers.has(you) || meta.rekeyers.size > 0 || !!meta.wasFinalizedBy, + isMuted: meta.isMuted, + navigateToThread: s.dispatch.navigateToThread, + snippet, + snippetDecoration, + teamDisplayName, + timestamp: meta.timestamp || layoutTime || 0, + } + }) + ) + const participants = Chat.useChatContext( C.useShallow(s => { - const {meta} = s const participantInfo = s.participants - const teamname = (meta.teamname || layoutIsTeam ? layoutName : '') || '' - const channelname = isInWidget ? meta.channelname : '' - if (teamname && channelname) { - return `${teamname}#${channelname}` - } if (participantInfo.name.length) { - // Filter out ourselves unless it's our 1:1 conversation return participantInfo.name.filter((participant, _, list) => list.length === 1 ? true : participant !== you ) } - if (layoutIsTeam && layoutName) { - return [layoutName] + if (layoutIsTeam) { + return [] } return ( layoutName @@ -97,13 +98,13 @@ const SmallTeamImpl = (p: Props) => { ) const setOpenedRow = useOpenedRowState(s => s.dispatch.setOpenRow) - - const _onSelectConversation = React.useCallback(() => { - setOpenedRow(Chat.noConversationIDKey) - navigateToThread('inboxSmall') - }, [navigateToThread, setOpenedRow]) - - const onSelectConversation = isSelected ? undefined : (p.onSelectConversation ?? _onSelectConversation) + const onSelectConversation = isSelected + ? undefined + : (p.onSelectConversation ?? + (() => { + setOpenedRow(Chat.noConversationIDKey) + navigateToThread('inboxSmall') + })) const backgroundColor = isInWidget ? Kb.Styles.globalColors.white @@ -113,117 +114,440 @@ const SmallTeamImpl = (p: Props) => { ? Kb.Styles.globalColors.fastBlank : Kb.Styles.globalColors.blueGrey - const children = React.useMemo(() => { - return ( - - - - - - - - - + + + {teamname ? ( + + ) : ( + + )} + + + - - - - + + + + + + + ) +} + +type TopLineProps = { + isSelected: boolean + isInWidget: boolean + hasUnread: boolean + hasBadge: boolean + backgroundColor: string + teamDisplayName: string + participants: Array + timestamp: number +} + +const TopLine = (p: TopLineProps) => { + const {isSelected, isInWidget, hasUnread, hasBadge, backgroundColor, teamDisplayName, participants, timestamp} = p + const showGear = !isInWidget + const showBold = !isSelected && hasUnread + const subColor = isSelected + ? Kb.Styles.globalColors.white + : hasUnread + ? Kb.Styles.globalColors.black + : Kb.Styles.globalColors.black_50 + const iconHoverColor = isSelected ? Kb.Styles.globalColors.white_75 : Kb.Styles.globalColors.black + + const makePopup = (p: Kb.Popup2Parms) => { + const {attachTo, hidePopup} = p + return ( + ) - }, [backgroundColor, isInWidget, isSelected, onSelectConversation, layoutSnippet]) + } + const {showingPopup, showPopup, popup, popupAnchor} = Kb.usePopup2(makePopup) + + const tssubColor = (!hasBadge || isSelected) && subColor + const timestampStyle = Kb.Styles.collapseStyles([ + showBold && styles.bold, + styles.timestamp, + tssubColor !== false && ({color: tssubColor} as Kb.Styles.StylesCrossPlatform), + ]) + const timestampText = timestamp ? formatTimeForConversationList(timestamp) : '' + + const usernameColor = isSelected ? Kb.Styles.globalColors.white : Kb.Styles.globalColors.black + const nameContainerStyle = Kb.Styles.collapseStyles([ + styles.name, + showBold && styles.bold, + {color: usernameColor}, + Kb.Styles.isMobile && {backgroundColor}, + ]) + const teamContainerStyle = Kb.Styles.collapseStyles([ + styles.teamTextStyle, + showBold && styles.bold, + {color: usernameColor}, + ]) return ( - - - - - - {children} - - - - - + + {showGear && showingPopup && popup} + + + {teamDisplayName ? ( + + + {teamDisplayName} + + + ) : ( + + )} + + + + {timestampText} + + {!Kb.Styles.isMobile && showGear && ( + + )} + {hasBadge ? : null} + ) } -type RowAvatarProps = { +type BottomLineProps = { + snippet?: string + snippetDecoration?: T.RPCChat.SnippetDecoration backgroundColor?: string - isSelected: boolean + isSelected?: boolean + allowBold?: boolean } -const RowAvatars = React.memo(function RowAvatars(p: RowAvatarProps) { - const {backgroundColor, isSelected} = p - const layoutIsTeam = React.useContext(IsTeamContext) - const participants = React.useContext(ParticipantsContext) - const isMuted = Chat.useChatContext(s => s.meta.isMuted) + +const BottomLine = (p: BottomLineProps) => { + const {allowBold = true, isSelected, backgroundColor} = p + const snippet = p.snippet ?? '' + const snippetDecoration = p.snippetDecoration ?? T.RPCChat.SnippetDecoration.none + const you = useCurrentUserState(s => s.username) - const isLocked = Chat.useChatContext(s => { - const {meta} = s - const isLocked = meta.rekeyers.has(you) || meta.rekeyers.size > 0 || !!meta.wasFinalizedBy - return isLocked - }) - - let participantOne = '' - let participantTwo = '' - let teamname = '' - - if (typeof participants === 'string') { - teamname = participants.split('#')[0] ?? '' - } else if (layoutIsTeam) { - teamname = participants[0] ?? '' + const { + hasUnread, + draft: _draft, + hasResetUsers, + participantNeedToRekey, + youAreReset, + youNeedToRekey, + trustedState, + hasId, + } = Chat.useChatContext( + C.useShallow(s => { + const {membershipType, rekeyers, resetParticipants, trustedState} = s.meta + return { + draft: s.meta.draft, + hasId: !!s.id, + hasResetUsers: resetParticipants.size > 0, + hasUnread: s.unread > 0, + participantNeedToRekey: rekeyers.size > 0, + trustedState, + youAreReset: membershipType === 'youAreReset', + youNeedToRekey: rekeyers.has(you), + } + }) + ) + + const isDecryptingSnippet = + hasId && !snippet ? trustedState === 'requesting' || trustedState === 'untrusted' : false + const draft = (!isSelected && !hasUnread && _draft) || '' + + const subColor = isSelected + ? Kb.Styles.globalColors.white + : hasUnread + ? Kb.Styles.globalColors.black + : Kb.Styles.globalColors.black_50 + const showBold = allowBold && !isSelected && hasUnread + const style = Kb.Styles.collapseStyles([ + styles.bottomLine, + {color: subColor, ...(showBold ? Kb.Styles.globalStyles.fontBold : {})}, + ]) + + let content: React.ReactNode + if (youNeedToRekey) { + content = null + } else if (youAreReset) { + content = ( + + You are locked out. + + ) + } else if (participantNeedToRekey) { + content = ( + + ) + } else if (draft) { + content = ( + + + Draft: + + + {draft} + + + ) + } else if (isDecryptingSnippet) { + content = ( + + ) } else { - participantOne = participants[0] ?? '' - participantTwo = participants[1] ?? '' + content = ( + + + + ) } - return teamname ? ( - - ) : ( - + + return ( + + + {hasResetUsers && ( + + )} + {youNeedToRekey && ( + + )} + {content} + + ) -}) +} + +const SnippetContent = (p: { + snippet: string + snippetDecoration: T.RPCChat.SnippetDecoration + isSelected?: boolean + style: Kb.Styles.StylesCrossPlatform +}) => { + const {snippet, snippetDecoration: decoration, isSelected, style} = p + const defaultIconColor = isSelected ? Kb.Styles.globalColors.white : Kb.Styles.globalColors.black_20 + + let decorationNode: React.ReactNode + let exploded = false + let tooltip: string | undefined + + switch (decoration) { + case T.RPCChat.SnippetDecoration.pendingMessage: + tooltip = 'Sending\u2026' + decorationNode = + break + case T.RPCChat.SnippetDecoration.failedPendingMessage: + tooltip = 'Failed to send' + decorationNode = ( + + ) + break + case T.RPCChat.SnippetDecoration.explodingMessage: + decorationNode = + break + case T.RPCChat.SnippetDecoration.explodedMessage: + decorationNode = ( + + Message exploded. + + ) + exploded = true + break + case T.RPCChat.SnippetDecoration.audioAttachment: + decorationNode = + break + case T.RPCChat.SnippetDecoration.videoAttachment: + decorationNode = + break + case T.RPCChat.SnippetDecoration.photoAttachment: + decorationNode = + break + case T.RPCChat.SnippetDecoration.fileAttachment: + decorationNode = + break + case T.RPCChat.SnippetDecoration.stellarReceived: + decorationNode = + break + case T.RPCChat.SnippetDecoration.stellarSent: + decorationNode = + break + case T.RPCChat.SnippetDecoration.pinnedMessage: + decorationNode = + break + default: + decorationNode = null + } + + return ( + <> + {!!decorationNode && ( + + {decorationNode} + + )} + {!exploded && !!snippet && ( + + {snippet} + + )} + + ) +} + +const SnippetDecorationIcon = (p: {type: Kb.IconType; color: string}) => ( + +) const styles = Kb.Styles.styleSheetCreate( () => ({ + alertMeta: Kb.Styles.platformStyles({ + common: {alignSelf: 'center', marginRight: 6}, + isMobile: {marginTop: 2}, + }), + bold: {...Kb.Styles.globalStyles.fontBold}, + bottom: {justifyContent: 'flex-start'}, + bottomLine: Kb.Styles.platformStyles({ + isElectron: { + color: Kb.Styles.globalColors.black_50, + display: 'block', + minHeight: 16, + overflow: 'hidden', + paddingRight: 10, + textOverflow: 'ellipsis', + whiteSpace: 'nowrap', + width: '100%', + }, + isMobile: { + color: Kb.Styles.globalColors.black_50, + flex: 1, + lineHeight: 19, + paddingRight: 40, + }, + }), container: { flexShrink: 0, height: RowSizes.smallRowHeight, }, + contentBox: { + ...Kb.Styles.globalStyles.fillAbsolute, + alignItems: 'center', + width: '100%', + }, conversationRow: { - ...Kb.Styles.globalStyles.flexBoxColumn, flexGrow: 1, height: '100%', justifyContent: 'center', paddingLeft: Kb.Styles.globalMargins.tiny, }, + draftLabel: {color: Kb.Styles.globalColors.orange}, fastBlank: Kb.Styles.platformStyles({ isPhone: {backgroundColor: Kb.Styles.globalColors.fastBlank}, isTablet: {backgroundColor: undefined}, }), - flexOne: {flex: 1}, + icon: {position: 'relative'} as const, + innerBox: Kb.Styles.platformStyles({ + common: { + flexGrow: 1, + height: 17, + position: 'relative', + }, + isMobile: {height: 21}, + }), + insideContainer: { + flexGrow: 1, + height: Kb.Styles.isMobile ? 21 : 17, + position: 'relative', + }, + name: {paddingRight: Kb.Styles.globalMargins.tiny}, + nameContainer: { + ...Kb.Styles.globalStyles.fillAbsolute, + }, rowContainer: Kb.Styles.platformStyles({ common: { - ...Kb.Styles.globalStyles.flexBoxRow, - alignItems: 'center', height: '100%', paddingLeft: Kb.Styles.globalMargins.xsmall, paddingRight: Kb.Styles.globalMargins.xsmall, @@ -234,12 +558,37 @@ const styles = Kb.Styles.styleSheetCreate( paddingRight: Kb.Styles.globalMargins.small, }, }), + snippetDecoration: {alignSelf: 'flex-start'} as const, + teamTextStyle: Kb.Styles.platformStyles({ + isElectron: { + overflow: 'hidden', + textOverflow: 'ellipsis', + whiteSpace: 'nowrap', + }, + }), + timestamp: Kb.Styles.platformStyles({ + common: { + backgroundColor: Kb.Styles.globalColors.fastBlank, + color: Kb.Styles.globalColors.blueDark, + }, + isTablet: {backgroundColor: undefined}, + }), + unreadDotStyle: { + backgroundColor: Kb.Styles.globalColors.orange, + borderRadius: 6, + height: 8, + marginLeft: 4, + width: 8, + }, withBottomLine: { justifyContent: 'flex-end', paddingBottom: Kb.Styles.globalMargins.xxtiny, }, - withoutBottomLine: {justifyContent: 'center'}, + youAreResetText: Kb.Styles.platformStyles({ + isElectron: {fontSize: 12, lineHeight: 13}, + isMobile: {fontSize: 14, lineHeight: 19}, + }), }) as const ) -export {SmallTeam} +export {SmallTeam, BottomLine} diff --git a/shared/chat/inbox/row/small-team/top-line.tsx b/shared/chat/inbox/row/small-team/top-line.tsx deleted file mode 100644 index 3e45c439e679..000000000000 --- a/shared/chat/inbox/row/small-team/top-line.tsx +++ /dev/null @@ -1,195 +0,0 @@ -import * as Chat from '@/stores/chat2' -import * as React from 'react' -import * as Kb from '@/common-adapters' -import TeamMenu from '@/chat/conversation/info-panel/menu' -import {formatTimeForConversationList} from '@/util/timestamp' -import {TimeContext, ParticipantsContext} from './contexts' - -type Props = { - isSelected: boolean - layoutName?: string - layoutIsTeam?: boolean - isInWidget: boolean - layoutTime?: number -} - -const Timestamp = React.memo(function Timestamp() { - const layoutTime = React.useContext(TimeContext) - const timeNum = Chat.useChatContext(s => s.meta.timestamp || layoutTime) - const timestamp = timeNum ? formatTimeForConversationList(timeNum) : '' - return <>{timestamp} -}) - -const Names = React.memo(function Names(p: {isSelected?: boolean; showBold: boolean; isInWidget: boolean}) { - const participants = React.useContext(ParticipantsContext) - const {isSelected, isInWidget, showBold} = p - const usernameColor = isSelected ? Kb.Styles.globalColors.white : Kb.Styles.globalColors.black - const backgroundColor = isInWidget - ? Kb.Styles.globalColors.white - : isSelected - ? Kb.Styles.globalColors.blue - : Kb.Styles.isPhone - ? Kb.Styles.globalColors.fastBlank - : Kb.Styles.globalColors.blueGrey - const nameContainerStyle = React.useMemo( - () => - Kb.Styles.collapseStyles([ - styles.name, - showBold && styles.bold, - {color: usernameColor}, - Kb.Styles.isMobile && {backgroundColor}, - ]), - [showBold, usernameColor, backgroundColor] - ) - - const teamContainerStyle = React.useMemo( - () => Kb.Styles.collapseStyles([styles.teamTextStyle, showBold && styles.bold, {color: usernameColor}]), - [showBold, usernameColor] - ) - return typeof participants === 'string' ? ( - - - {participants} - - - ) : ( - - ) -}) - -const SimpleTopLine = React.memo(function SimpleTopLine(p: Props) { - const {isSelected, isInWidget} = p - const hasUnread = Chat.useChatContext(s => s.unread > 0) - const hasBadge = Chat.useChatContext(s => s.badge > 0) - const props = { - hasBadge, - hasUnread, - isInWidget, - isSelected, - } - return -}) - -type IProps = { - isSelected?: boolean - isInWidget: boolean - hasBadge: boolean - hasUnread: boolean -} -const SimpleTopLineImpl = React.memo(function SimpleTopLineImpl(p: IProps) { - const {isSelected, isInWidget, hasBadge, hasUnread} = p - const showGear = !isInWidget - const showBold = !isSelected && hasUnread - const subColor = isSelected - ? Kb.Styles.globalColors.white - : hasUnread - ? Kb.Styles.globalColors.black - : Kb.Styles.globalColors.black_50 - - const iconHoverColor = isSelected ? Kb.Styles.globalColors.white_75 : Kb.Styles.globalColors.black - - const makePopup = React.useCallback((p: Kb.Popup2Parms) => { - const {attachTo, hidePopup} = p - return ( - - ) - }, []) - const {showingPopup, showPopup, popup, popupAnchor} = Kb.usePopup2(makePopup) - - const tssubColor = (!hasBadge || isSelected) && subColor - const timestampStyle = React.useMemo( - () => - Kb.Styles.collapseStyles([ - showBold && styles.bold, - styles.timestamp, - tssubColor !== false && ({color: tssubColor} as Kb.Styles.StylesCrossPlatform), - ]), - [showBold, tssubColor] - ) - - return ( - - {showGear && showingPopup && popup} - - - - - - - - - {!Kb.Styles.isMobile && showGear && ( - - )} - {hasBadge ? : null} - - ) -}) - -const styles = Kb.Styles.styleSheetCreate( - () => - ({ - bold: {...Kb.Styles.globalStyles.fontBold}, - container: { - ...Kb.Styles.globalStyles.flexBoxRow, - alignItems: 'center', - }, - icon: {position: 'relative'}, - insideContainer: { - ...Kb.Styles.globalStyles.flexBoxRow, - flexGrow: 1, - height: Kb.Styles.isMobile ? 21 : 17, - position: 'relative', - }, - name: {paddingRight: Kb.Styles.globalMargins.tiny}, - nameContainer: { - ...Kb.Styles.globalStyles.flexBoxRow, - ...Kb.Styles.globalStyles.fillAbsolute, - alignItems: 'center', - }, - teamTextStyle: Kb.Styles.platformStyles({ - isElectron: { - overflow: 'hidden', - textOverflow: 'ellipsis', - whiteSpace: 'nowrap', - }, - }), - timestamp: Kb.Styles.platformStyles({ - common: { - backgroundColor: Kb.Styles.globalColors.fastBlank, - color: Kb.Styles.globalColors.blueDark, - }, - isTablet: {backgroundColor: undefined}, - }), - unreadDotStyle: { - backgroundColor: Kb.Styles.globalColors.orange, - borderRadius: 6, - height: 8, - marginLeft: 4, - width: 8, - }, - }) as const -) - -export {SimpleTopLine} diff --git a/shared/chat/inbox/search-row.tsx b/shared/chat/inbox/search-row.tsx index ceedb67615a9..0ae7328faf2f 100644 --- a/shared/chat/inbox/search-row.tsx +++ b/shared/chat/inbox/search-row.tsx @@ -6,50 +6,40 @@ import StartNewChat from './row/start-new-chat' type OwnProps = {headerContext: 'chat-header' | 'inbox-header'} -export default React.memo(function InboxSearchRow(ownProps: OwnProps) { +export default function InboxSearchRow(ownProps: OwnProps) { const {headerContext} = ownProps - const hasLoadedEmptyInbox = Chat.useChatState( - s => - s.inboxHasLoaded && - !!s.inboxLayout && - (s.inboxLayout.smallTeams || []).length === 0 && - (s.inboxLayout.bigTeams || []).length === 0 + const chatState = Chat.useChatState( + C.useShallow(s => { + const hasLoadedEmptyInbox = + s.inboxHasLoaded && + !!s.inboxLayout && + (s.inboxLayout.smallTeams || []).length === 0 && + (s.inboxLayout.bigTeams || []).length === 0 + return { + inboxSearch: s.dispatch.inboxSearch, + inboxSearchMoveSelectedIndex: s.dispatch.inboxSearchMoveSelectedIndex, + inboxSearchSelect: s.dispatch.inboxSearchSelect, + isSearching: !!s.inboxSearch, + showEmptyInbox: !s.inboxSearch && hasLoadedEmptyInbox, + } + }) ) - const showEmptyInbox = Chat.useChatState(s => !s.inboxSearch && hasLoadedEmptyInbox) + const {inboxSearch, inboxSearchMoveSelectedIndex, inboxSearchSelect, isSearching, showEmptyInbox} = chatState const showStartNewChat = !C.isMobile && showEmptyInbox - const isSearching = Chat.useChatState(s => !!s.inboxSearch) const showFilter = !showEmptyInbox - const navigateUp = C.useRouterState(s => s.dispatch.navigateUp) - const onBack = React.useCallback(() => { - navigateUp() - }, [navigateUp]) + const routerState = C.useRouterState( + C.useShallow(s => ({ + appendNewChatBuilder: s.appendNewChatBuilder, + navigateUp: s.dispatch.navigateUp, + })) + ) const [query, setQuery] = React.useState('') - const inboxSearchSelect = Chat.useChatState(s => s.dispatch.inboxSearchSelect) - const inboxSearch = Chat.useChatState(s => s.dispatch.inboxSearch) - const inboxSearchMoveSelectedIndex = Chat.useChatState(s => s.dispatch.inboxSearchMoveSelectedIndex) - const onEnsureSelection = React.useCallback(() => { - inboxSearchSelect() - }, [inboxSearchSelect]) - - const appendNewChatBuilder = C.useRouterState(s => s.appendNewChatBuilder) - const onNewChat = React.useCallback(() => { - appendNewChatBuilder() - }, [appendNewChatBuilder]) - const onQueryChanged = React.useCallback( - (q: string) => { - setQuery(q) - inboxSearch(q) - }, - [inboxSearch] - ) - const onSelectDown = React.useCallback(() => { - inboxSearchMoveSelectedIndex(true) - }, [inboxSearchMoveSelectedIndex]) - const onSelectUp = React.useCallback(() => { - inboxSearchMoveSelectedIndex(false) - }, [inboxSearchMoveSelectedIndex]) + const onQueryChanged = (q: string) => { + setQuery(q) + inboxSearch(q) + } const [lastSearching, setLastSearching] = React.useState(isSearching) if (lastSearching !== isSearching) { @@ -64,12 +54,14 @@ export default React.memo(function InboxSearchRow(ownProps: OwnProps) { return ( <> - {!!showStartNewChat && } + {!!showStartNewChat && ( + + )} {!!showFilter && ( inboxSearchMoveSelectedIndex(false)} + onSelectDown={() => inboxSearchMoveSelectedIndex(true)} + onEnsureSelection={inboxSearchSelect} onQueryChanged={onQueryChanged} query={query} showNewChat={showNewChat} @@ -78,4 +70,4 @@ export default React.memo(function InboxSearchRow(ownProps: OwnProps) { )} ) -}) +} diff --git a/shared/chat/selectable-big-team-channel.tsx b/shared/chat/selectable-big-team-channel.tsx index bc8758fcb6c5..c644d8ea5c8e 100644 --- a/shared/chat/selectable-big-team-channel.tsx +++ b/shared/chat/selectable-big-team-channel.tsx @@ -2,9 +2,8 @@ import * as React from 'react' import * as Kb from '@/common-adapters' import {TeamAvatar} from './avatars' import {pluralize} from '@/util/string' -import {BottomLine} from './inbox/row/small-team/bottom-line' +import {BottomLine} from './inbox/row/small-team' import type * as T from '@/constants/types' -import {SnippetContext} from './inbox/row/small-team/contexts' type Props = { isSelected: boolean @@ -68,9 +67,12 @@ const SelectableBigTeamChannel = (props: Props) => { {!props.numSearchHits && ( - - - + )} {!!props.numSearchHits && ( { usernameColor={props.usernameColor} /> {!props.numSearchHits && ( - - - + )} {props.showBadge && } From 53f4180071071a0125f33ff0b409b6ace5e59f58 Mon Sep 17 00:00:00 2001 From: chrisnojima Date: Fri, 13 Feb 2026 16:48:34 -0500 Subject: [PATCH 003/112] inbox desktop cleanup (#28904) --- shared/chat/inbox/index.desktop.tsx | 250 +++++++++++----------------- 1 file changed, 98 insertions(+), 152 deletions(-) diff --git a/shared/chat/inbox/index.desktop.tsx b/shared/chat/inbox/index.desktop.tsx index 2cc0d2efd57e..ac2d433ec883 100644 --- a/shared/chat/inbox/index.desktop.tsx +++ b/shared/chat/inbox/index.desktop.tsx @@ -53,12 +53,12 @@ const DragLine = (p: { const {inboxNumSmallRows, showButton, style, scrollDiv} = p const {smallTeamsExpanded, toggleSmallTeamsExpanded, rows, setInboxNumSmallRows} = p const [dragY, setDragY] = React.useState(-1) - const deltaNewSmallRows = React.useCallback(() => { + const deltaNewSmallRows = () => { if (dragY === -1) { return 0 } return Math.max(0, Math.floor(dragY / smallRowHeight)) - inboxNumSmallRows - }, [dragY, inboxNumSmallRows]) + } const newSmallRows = deltaNewSmallRows() let expandingRows: Array = [] @@ -72,8 +72,14 @@ const DragLine = (p: { const throttledDragY = C.useThrottledCallback(setDragY, 100) - const onDragOver = React.useCallback( - (e: DragEvent) => { + const goodDropRef = React.useRef(false) + + React.useEffect(() => { + const d = scrollDiv.current + if (!d) { + return + } + const onDragOver = (e: DragEvent) => { e.preventDefault() if ( scrollDiv.current && @@ -86,21 +92,10 @@ const DragLine = (p: { throttledDragY(dy) } } - }, - [scrollDiv, throttledDragY] - ) - - const goodDropRef = React.useRef(false) - - const onDrop = React.useCallback((e: DragEvent) => { - e.preventDefault() - goodDropRef.current = true - }, []) - - React.useEffect(() => { - const d = scrollDiv.current - if (!d) { - return + } + const onDrop = (e: DragEvent) => { + e.preventDefault() + goodDropRef.current = true } d.addEventListener('dragover', onDragOver) d.addEventListener('drop', onDrop) @@ -108,13 +103,13 @@ const DragLine = (p: { d.removeEventListener('dragover', onDragOver) d.removeEventListener('drop', onDrop) } - }, [scrollDiv, onDragOver, onDrop]) + }, [scrollDiv, throttledDragY]) - const onDragStart = React.useCallback((e: React.DragEvent) => { + const onDragStart = (e: React.DragEvent) => { e.dataTransfer.setData(dragKey, dragKey) goodDropRef.current = false - }, []) - const onDragEnd = React.useCallback(() => { + } + const onDragEnd = () => { if (goodDropRef.current) { const delta = deltaNewSmallRows() if (delta !== 0) { @@ -123,7 +118,7 @@ const DragLine = (p: { goodDropRef.current = false } setDragY(-1) - }, [setInboxNumSmallRows, inboxNumSmallRows, deltaNewSmallRows]) + } return (
void } -const InboxRow = React.memo(function InboxRow(p: RowComponentProps) { +function InboxRow(p: RowComponentProps) { const {index, style, rows} = p const {scrollDiv, inboxNumSmallRows, smallTeamsExpanded, toggleSmallTeamsExpanded} = p const {setInboxNumSmallRows, navKey, selectedConversationIDKey} = p @@ -234,9 +229,30 @@ const InboxRow = React.memo(function InboxRow(p: RowComponentProps {makeRow(row, navKey, selectedConversationIDKey === row.conversationIDKey)}
) -}) as (props: RowComponentProps) => React.ReactElement +} + +const shouldShowFloating = (rows: ChatInboxRowItem[], visibleIdx: number) => + visibleIdx >= 0 && rows[visibleIdx]?.type === 'small' + +const calcUnreadShortcut = (unreadIndices: Map, visibleIdx: number) => { + if (!unreadIndices.size || visibleIdx < 0) { + return {firstOffscreen: -1, showUnread: false, unreadCount: 0} + } + let unreadCount = 0 + let foid = 0 + unreadIndices.forEach((count, idx) => { + if (idx > visibleIdx) { + if (foid <= 0) foid = idx + unreadCount += count + } + }) + if (foid) { + return {firstOffscreen: foid, showUnread: true, unreadCount} + } + return {firstOffscreen: -1, showUnread: false, unreadCount: 0} +} -const Inbox = React.memo(function Inbox(props: TInbox.Props) { +function Inbox(props: TInbox.Props) { const {smallTeamsExpanded, rows, unreadIndices, unreadTotal, inboxNumSmallRows} = props const {toggleSmallTeamsExpanded, navKey, selectedConversationIDKey, onUntrustedInboxVisible} = props const {setInboxNumSmallRows, allowShowFloatingButton} = props @@ -251,113 +267,56 @@ const Inbox = React.memo(function Inbox(props: TInbox.Props) { const firstOffscreenIdx = React.useRef(-1) const lastVisibleIdx = React.useRef(-1) - const isMounted = C.useIsMounted() - - const lastSmallTeamsExpanded = React.useRef(smallTeamsExpanded) - const lastRowsLength = React.useRef(rows.length) const lastUnreadIndices = React.useRef(unreadIndices) const lastUnreadTotal = React.useRef(unreadTotal) - const itemSizeGetter = React.useCallback( - (index: number) => { - const row = rows[index] - if (!row) { - return 0 - } + const itemSizeGetter = (index: number) => { + const row = rows[index] + if (!row) { + return 0 + } - return getRowHeight(row.type, row.type === 'divider' && row.showButton) - }, - [rows] - ) + return getRowHeight(row.type, row.type === 'divider' && row.showButton) + } - const scrollToUnread = React.useCallback(() => { + const scrollToUnread = () => { if (firstOffscreenIdx.current <= 0) { return } listRef.current?.scrollToRow({index: firstOffscreenIdx.current}) - }, [listRef]) - - const calculateShowFloating = React.useCallback(() => { - if (lastVisibleIdx.current < 0) { - return - } - let show = true - const row = rows[lastVisibleIdx.current] - if (row?.type !== 'small') { - show = false - } - setShowFloating(show) - }, [rows]) + } const onItemsRenderedDebounced = C.useDebouncedCallback( - React.useCallback( - (p: {startIndex: number; stopIndex: number}) => { - if (!isMounted()) { - return - } - const {startIndex, stopIndex} = p - const toUnbox = rows - .slice(startIndex, stopIndex + 1) - .reduce>((arr, r) => { - if ((r.type === 'small' || r.type === 'big') && r.conversationIDKey) { - arr.push(r.conversationIDKey) - } - return arr - }, []) - calculateShowFloating() - onUntrustedInboxVisible(toUnbox) - }, - [calculateShowFloating, onUntrustedInboxVisible, rows, isMounted] - ), + (p: {startIndex: number; stopIndex: number}) => { + const {startIndex, stopIndex} = p + const toUnbox = rows + .slice(startIndex, stopIndex + 1) + .reduce>((arr, r) => { + if ((r.type === 'small' || r.type === 'big') && r.conversationIDKey) { + arr.push(r.conversationIDKey) + } + return arr + }, []) + setShowFloating(shouldShowFloating(rows, lastVisibleIdx.current)) + onUntrustedInboxVisible(toUnbox) + }, 200 ) - const rowsLength = rows.length - - const calculateShowUnreadShortcut = React.useCallback(() => { - if (!isMounted()) { - return - } - if (!unreadIndices.size || lastVisibleIdx.current < 0) { - if (showUnread) { - setShowUnread(false) - } - return - } - - let unreadCount = 0 - let foid = 0 - unreadIndices.forEach((count, idx) => { - if (idx > lastVisibleIdx.current) { - if (foid <= 0) { - foid = idx - } - unreadCount += count - } - }) - if (foid) { - setShowUnread(true) - setUnreadCount(unreadCount) - firstOffscreenIdx.current = foid - } else { - setShowUnread(false) - setUnreadCount(0) - firstOffscreenIdx.current = -1 - } - }, [showUnread, unreadIndices, isMounted]) - - const calculateShowUnreadShortcutThrottled = C.useThrottledCallback(calculateShowUnreadShortcut, 100) - - const onItemsRendered = React.useCallback( - ({startIndex, stopIndex}: {startIndex: number; stopIndex: number}) => { - lastVisibleIdx.current = stopIndex - calculateShowUnreadShortcutThrottled() - onItemsRenderedDebounced({startIndex, stopIndex}) - }, - [calculateShowUnreadShortcutThrottled, onItemsRenderedDebounced] - ) + const calculateShowUnreadShortcutThrottled = C.useThrottledCallback(() => { + const result = calcUnreadShortcut(unreadIndices, lastVisibleIdx.current) + setShowUnread(result.showUnread) + setUnreadCount(result.unreadCount) + firstOffscreenIdx.current = result.firstOffscreen + }, 100) + + const onItemsRendered = ({startIndex, stopIndex}: {startIndex: number; stopIndex: number}) => { + lastVisibleIdx.current = stopIndex + calculateShowUnreadShortcutThrottled() + onItemsRenderedDebounced({startIndex, stopIndex}) + } - const scrollToBigTeams = React.useCallback(() => { + const scrollToBigTeams = () => { if (!scrollDiv.current) return if (smallTeamsExpanded) { @@ -375,56 +334,43 @@ const Inbox = React.memo(function Inbox(props: TInbox.Props) { if (boundingHeight + currentScrollTop < top + dragHeight) { scrollableDiv.scrollBy({behavior: 'smooth', top}) } - }, [inboxNumSmallRows, smallTeamsExpanded, toggleSmallTeamsExpanded]) + } React.useEffect(() => { - if (rowsLength !== lastRowsLength.current) { - calculateShowFloating() - } - }, [calculateShowFloating, rowsLength]) + setShowFloating(shouldShowFloating(rows, lastVisibleIdx.current)) + }, [rows]) React.useEffect(() => { if ( !C.shallowEqual(lastUnreadIndices.current, unreadIndices) || lastUnreadTotal.current !== unreadTotal ) { - calculateShowUnreadShortcut() + const result = calcUnreadShortcut(unreadIndices, lastVisibleIdx.current) + setShowUnread(result.showUnread) + setUnreadCount(result.unreadCount) + firstOffscreenIdx.current = result.firstOffscreen } - }, [calculateShowUnreadShortcut, unreadIndices, unreadTotal]) + }, [unreadIndices, unreadTotal]) React.useEffect(() => { - lastSmallTeamsExpanded.current = smallTeamsExpanded - lastRowsLength.current = rowsLength lastUnreadIndices.current = unreadIndices lastUnreadTotal.current = unreadTotal - }, [unreadTotal, unreadIndices, rowsLength, smallTeamsExpanded]) + }, [unreadTotal, unreadIndices]) const floatingDivider = showFloating && allowShowFloatingButton && ( ) - const itemData = React.useMemo( - () => ({ - inboxNumSmallRows, - navKey, - rows, - scrollDiv, - selectedConversationIDKey, - setInboxNumSmallRows, - smallTeamsExpanded, - toggleSmallTeamsExpanded, - }), - [ - inboxNumSmallRows, - navKey, - rows, - scrollDiv, - selectedConversationIDKey, - setInboxNumSmallRows, - smallTeamsExpanded, - toggleSmallTeamsExpanded, - ] - ) + const itemData = { + inboxNumSmallRows, + navKey, + rows, + scrollDiv, + selectedConversationIDKey, + setInboxNumSmallRows, + smallTeamsExpanded, + toggleSmallTeamsExpanded, + } return ( @@ -446,7 +392,7 @@ const Inbox = React.memo(function Inbox(props: TInbox.Props) { ) -}) +} const styles = Kb.Styles.styleSheetCreate( () => From a336843ea407d547f8f9929317cdadf16bf6284c Mon Sep 17 00:00:00 2001 From: chrisnojima Date: Fri, 13 Feb 2026 16:49:16 -0500 Subject: [PATCH 004/112] search cleanup (#28903) --- shared/chat/inbox-search/index.tsx | 183 ++++++++++++----------------- 1 file changed, 76 insertions(+), 107 deletions(-) diff --git a/shared/chat/inbox-search/index.tsx b/shared/chat/inbox-search/index.tsx index 823e2b970a33..e54b3e914a32 100644 --- a/shared/chat/inbox-search/index.tsx +++ b/shared/chat/inbox-search/index.tsx @@ -44,26 +44,28 @@ type Item = NameResult | TextResult | BotResult | OpenTeamResult const emptySearch = Chat.makeInboxSearchInfo() -export default React.memo(function InboxSearchContainer(ownProps: OwnProps) { - const _inboxSearch = Chat.useChatState(s => s.inboxSearch ?? emptySearch) - const toggleInboxSearch = Chat.useChatState(s => s.dispatch.toggleInboxSearch) - const inboxSearchSelect = Chat.useChatState(s => s.dispatch.inboxSearchSelect) - const onCancel = React.useCallback(() => { +export default function InboxSearchContainer(ownProps: OwnProps) { + const {_inboxSearch, toggleInboxSearch, inboxSearchSelect} = Chat.useChatState( + C.useShallow(s => ({ + _inboxSearch: s.inboxSearch ?? emptySearch, + inboxSearchSelect: s.dispatch.inboxSearchSelect, + toggleInboxSearch: s.dispatch.toggleInboxSearch, + })) + ) + const onCancel = () => { toggleInboxSearch(false) - }, [toggleInboxSearch]) + } const navigateAppend = C.useRouterState(s => s.dispatch.navigateAppend) - const onInstallBot = React.useCallback( - (username: string) => { - navigateAppend({props: {botUsername: username}, selected: 'chatInstallBotPick'}) - }, - [navigateAppend] - ) - const onSelectConversation = React.useCallback( - (conversationIDKey: T.Chat.ConversationIDKey, selectedIndex: number, query: string) => { - inboxSearchSelect(conversationIDKey, query.length > 0 ? query : undefined, selectedIndex) - }, - [inboxSearchSelect] - ) + const onInstallBot = (username: string) => { + navigateAppend({props: {botUsername: username}, selected: 'chatInstallBotPick'}) + } + const onSelectConversation = ( + conversationIDKey: T.Chat.ConversationIDKey, + selectedIndex: number, + query: string + ) => { + inboxSearchSelect(conversationIDKey, query.length > 0 ? query : undefined, selectedIndex) + } const {header} = ownProps const {indexPercent, nameResults: _nameResults, nameResultsUnread, nameStatus, textStatus} = _inboxSearch const {botsResults: _botsResults, botsResultsSuggested, botsStatus} = _inboxSearch @@ -76,26 +78,14 @@ export default React.memo(function InboxSearchContainer(ownProps: OwnProps) { const [openTeamsAll, setOpenTeamsAll] = React.useState(false) const [openTeamsCollapsed, setOpenTeamsCollapsed] = React.useState(false) const [textCollapsed, setTextCollapsed] = React.useState(false) - const toggleCollapseName = React.useCallback(() => { - setNameCollapsed(s => !s) - }, []) - const toggleCollapseText = React.useCallback(() => { - setTextCollapsed(s => !s) - }, []) - const toggleCollapseOpenTeams = React.useCallback(() => { - setOpenTeamsCollapsed(s => !s) - }, []) - const toggleOpenTeamsAll = React.useCallback(() => { - setOpenTeamsAll(s => !s) - }, []) - const toggleCollapseBots = React.useCallback(() => { - setBotsCollapsed(s => !s) - }, []) - const toggleBotsAll = React.useCallback(() => { - setBotsAll(s => !s) - }, []) - - const renderOpenTeams = (h: {item: Item; index: number}) => { + const toggleCollapseName = () => setNameCollapsed(s => !s) + const toggleCollapseText = () => setTextCollapsed(s => !s) + const toggleCollapseOpenTeams = () => setOpenTeamsCollapsed(s => !s) + const toggleOpenTeamsAll = () => setOpenTeamsAll(s => !s) + const toggleCollapseBots = () => setBotsCollapsed(s => !s) + const toggleBotsAll = () => setBotsAll(s => !s) + + const renderOpenTeams = (h: {item: Item}) => { const {item} = h if (item.type !== 'openTeam') return null const {hit} = item @@ -143,8 +133,14 @@ export default React.memo(function InboxSearchContainer(ownProps: OwnProps) { ) } - const renderTeamHeader = (section: Section) => { - const showMore = _openTeamsResults.length > 3 && !openTeamsCollapsed + const renderHeaderWithMore = ( + section: Section, + resultsLength: number, + collapsed: boolean, + showAll: boolean, + toggleAll: () => void + ) => { + const showMore = resultsLength > 3 && !collapsed const label = ( {section.title} @@ -152,11 +148,11 @@ export default React.memo(function InboxSearchContainer(ownProps: OwnProps) { { e.stopPropagation() - toggleOpenTeamsAll() + toggleAll() }} type="BodySmallSecondaryLink" > - {!openTeamsAll ? '(more)' : '(less)'} + {!showAll ? '(more)' : '(less)'} )} @@ -171,33 +167,11 @@ export default React.memo(function InboxSearchContainer(ownProps: OwnProps) { ) } - const renderBotsHeader = (section: Section) => { - const showMore = _botsResults.length > 3 && !botsCollapsed - const label = ( - - {section.title} - {showMore && ( - { - e.stopPropagation() - toggleBotsAll() - }} - type="BodySmallSecondaryLink" - > - {!botsAll ? '(more)' : '(less)'} - - )} - - ) - return ( - - ) - } + const renderTeamHeader = (section: Section) => + renderHeaderWithMore(section, _openTeamsResults.length, openTeamsCollapsed, openTeamsAll, toggleOpenTeamsAll) + + const renderBotsHeader = (section: Section) => + renderHeaderWithMore(section, _botsResults.length, botsCollapsed, botsAll, toggleBotsAll) const renderTextHeader = (section: Section) => { const ratio = indexPercent / 100.0 @@ -246,8 +220,7 @@ export default React.memo(function InboxSearchContainer(ownProps: OwnProps) { if (h.item.type !== 'text' && h.item.type !== 'name') return null - const {item: _item, section, index} = h - const item = _item + const {item, section, index} = h const numHits = item.type === 'text' ? item.numHits : undefined const realIndex = index + section.indexOffset return item.sizeType === 'big' ? ( @@ -381,7 +354,7 @@ export default React.memo(function InboxSearchContainer(ownProps: OwnProps) { />
) -}) +} type SectionExtra = { indexOffset: number @@ -408,36 +381,37 @@ const OpenTeamRow = (p: OpenTeamProps) => { const [hovering, setHovering] = React.useState(false) const {name, description, memberCount, publicAdmins, inTeam, isSelected} = p const showingDueToSelect = React.useRef(false) - const joinTeam = useTeamsState(s => s.dispatch.joinTeam) - const showTeamByName = useTeamsState(s => s.dispatch.showTeamByName) + const {joinTeam, showTeamByName} = useTeamsState( + C.useShallow(s => ({ + joinTeam: s.dispatch.joinTeam, + showTeamByName: s.dispatch.showTeamByName, + })) + ) const clearModals = C.useRouterState(s => s.dispatch.clearModals) - const makePopup = React.useCallback( - (p: Kb.Popup2Parms) => { - const {attachTo, hidePopup} = p - return ( - joinTeam(name)} - onViewTeam={() => { - clearModals() - showTeamByName(name) - }} - publicAdmins={publicAdmins} - visible={true} - /> - ) - }, - [showTeamByName, joinTeam, description, inTeam, memberCount, name, publicAdmins, clearModals] - ) + const makePopup = (p: Kb.Popup2Parms) => { + const {attachTo, hidePopup} = p + return ( + joinTeam(name)} + onViewTeam={() => { + clearModals() + showTeamByName(name) + }} + publicAdmins={publicAdmins} + visible={true} + /> + ) + } const {hidePopup, showingPopup, popup, popupAnchor, showPopup} = Kb.usePopup2(makePopup) React.useEffect(() => { @@ -472,9 +446,7 @@ const OpenTeamRow = (p: OpenTeamProps) => { { Date: Tue, 17 Feb 2026 15:26:48 -0500 Subject: [PATCH 005/112] style cleanup (#28907) --- shared/chat/audio/audio-player.tsx | 9 ---- shared/chat/audio/audio-recorder.native.tsx | 4 -- shared/chat/blocking/block-modal.tsx | 1 - .../attachment-fullscreen/index.desktop.tsx | 36 --------------- .../attachment-fullscreen/index.native.tsx | 17 ------- .../conversation/attachment-get-titles.tsx | 27 ------------ .../chat/conversation/bot/channel-picker.tsx | 4 -- .../conversation/info-panel/attachments.tsx | 16 ------- shared/chat/conversation/info-panel/bot.tsx | 21 --------- .../chat/conversation/info-panel/header.tsx | 12 ----- shared/chat/conversation/info-panel/menu.tsx | 31 ------------- .../normal2/platform-input.desktop.tsx | 16 ------- .../normal2/platform-input.native.tsx | 10 ----- .../input-area/normal2/typing.tsx | 13 ------ .../input-area/suggestors/shared.tsx | 18 -------- .../suggestors/suggestion-list.desktop.tsx | 1 - .../messages/account-payment/container.tsx | 8 ---- .../attachment/image2/imageimpl.desktop.tsx | 12 ----- .../attachment/video/videoimpl.desktop.tsx | 19 -------- .../conversation/messages/cards/hello-bot.tsx | 7 --- .../messages/cards/team-journey/container.tsx | 35 --------------- .../messages/message-popup/header.tsx | 6 --- .../messages/placeholder/wrapper.tsx | 5 --- .../conversation/messages/react-button.tsx | 12 ----- .../messages/reaction-tooltip.tsx | 1 - .../conversation/messages/reactions-rows.tsx | 1 - .../messages/system-joined/container.tsx | 4 -- .../messages/text/coinflip/index.tsx | 16 ------- .../messages/text/coinflip/participants.tsx | 5 --- .../chat/conversation/messages/text/reply.tsx | 12 ----- .../text/unfurl/unfurl-list/giphy.tsx | 6 --- .../messages/wrapper/exploding-meta.tsx | 12 ----- .../wrapper/long-pressable/index.native.tsx | 4 -- .../messages/wrapper/send-indicator.tsx | 4 -- .../conversation/messages/wrapper/wrapper.tsx | 25 ----------- .../chat/conversation/normal/index.native.tsx | 11 ----- shared/chat/conversation/pinned-message.tsx | 4 -- shared/chat/emoji-picker/container.tsx | 7 --- shared/chat/inbox/row/start-new-chat.tsx | 14 ------ shared/chat/location-map.desktop.tsx | 3 -- shared/chat/location-map.native.tsx | 8 ---- .../common-adapters/avatar/index.desktop.tsx | 5 --- .../common-adapters/avatar/index.native.tsx | 11 ----- shared/common-adapters/checkbox.desktop.tsx | 1 - .../common-adapters/drag-and-drop.desktop.tsx | 10 ----- .../menu-layout/index.native.tsx | 1 - .../floating-picker.native.tsx | 13 ------ .../header-hoc/index.native.tsx | 2 - shared/common-adapters/labeled-input.tsx | 3 -- .../markdown/maybe-mention/unknown.tsx | 3 -- shared/common-adapters/markdown/spoiler.tsx | 6 --- shared/common-adapters/modal2.tsx | 29 ------------ .../common-adapters/popup-dialog.desktop.tsx | 20 --------- shared/common-adapters/reload.tsx | 6 --- shared/common-adapters/search-filter.tsx | 1 - shared/common-adapters/video.desktop.tsx | 8 ---- shared/crypto/input.tsx | 3 -- shared/crypto/output.tsx | 3 -- shared/crypto/sub-nav/nav-row.tsx | 10 ----- .../remote/component-loader.desktop.tsx | 1 - shared/devices/paper-key.tsx | 1 - .../kext-permission-popup.tsx | 8 ---- shared/fs/common/path-item-action/confirm.tsx | 10 ----- shared/fs/filepreview/normal-preview.tsx | 12 ----- shared/fs/filepreview/text-view.native.tsx | 4 -- shared/fs/nav-header/title.tsx | 6 --- shared/fs/simple-screens/oops.tsx | 5 --- shared/login/join-or-login.tsx | 12 ----- .../recover-password/prompt-reset-shared.tsx | 1 - shared/login/reset/password-enter.tsx | 6 --- shared/login/reset/password-known.tsx | 5 --- shared/login/signup/common.tsx | 13 ------ shared/menubar/chat-container.desktop.tsx | 16 ------- shared/menubar/files.desktop.tsx | 19 -------- shared/menubar/index.desktop.tsx | 3 -- shared/people/index.desktop.tsx | 3 -- shared/people/todo.tsx | 6 --- shared/profile/add-to-team.tsx | 5 --- shared/profile/edit-avatar/index.native.tsx | 14 ------ shared/profile/edit-profile.tsx | 1 - shared/profile/generic/proofs-list.tsx | 14 ------ shared/profile/generic/result.tsx | 4 -- shared/profile/user/actions/index.tsx | 1 - shared/profile/user/index.tsx | 16 ------- shared/provision/paper-key.tsx | 10 ----- shared/provision/set-public-name.tsx | 10 ----- shared/router-v2/account-switcher/index.tsx | 4 -- shared/router-v2/header/index.desktop.tsx | 2 - shared/router-v2/tab-bar.desktop.tsx | 5 --- shared/settings/advanced.tsx | 13 ------ shared/settings/archive/index.tsx | 1 - shared/settings/feedback/index.tsx | 5 --- shared/settings/notifications/render.tsx | 1 - shared/settings/password.tsx | 4 -- shared/settings/proxy.tsx | 16 ------- shared/team-building/contact-restricted.tsx | 4 -- shared/team-building/continue-button.tsx | 3 -- shared/team-building/email-search.tsx | 7 --- shared/team-building/go-button.tsx | 15 ------- shared/team-building/index.tsx | 24 ---------- shared/team-building/phone-search.tsx | 1 - .../search-result/common-result.tsx | 6 --- .../search-result/people-result.tsx | 6 --- .../search-result/you-result.tsx | 14 ------ .../team-building/service-tab-bar.desktop.tsx | 1 - .../team-building/service-tab-bar.native.tsx | 6 --- shared/team-building/team-box.tsx | 26 ----------- shared/team-building/user-bubble.tsx | 18 -------- shared/teams/add-members-wizard/add-email.tsx | 8 ---- shared/teams/add-members-wizard/add-phone.tsx | 8 ---- shared/teams/channel/index.tsx | 14 ------ .../really-leave-team/index.tsx | 8 ---- shared/teams/edit-team-description.tsx | 5 --- shared/teams/main/banner.tsx | 3 -- shared/teams/main/index.tsx | 9 ---- shared/teams/main/team-row.tsx | 3 -- .../teams/new-team/wizard/new-team-info.tsx | 8 ---- shared/teams/new-team/wizard/team-purpose.tsx | 8 ---- shared/teams/rename-team.tsx | 6 --- shared/teams/role-picker.tsx | 8 ---- shared/teams/team/index.tsx | 15 ------- shared/teams/team/member/index.new.tsx | 18 -------- shared/teams/team/rows/bot-row/bot.tsx | 13 ------ shared/teams/team/rows/emoji-row/add.tsx | 1 - shared/teams/team/rows/emoji-row/header.tsx | 1 - shared/teams/team/rows/invite-row/request.tsx | 44 ------------------- shared/teams/team/rows/subteam-row/add.tsx | 13 ------ shared/teams/team/settings-tab/index.tsx | 18 -------- shared/teams/team/tabs.tsx | 7 --- shared/teams/team/team-info.tsx | 8 ---- shared/tracker2/bio.tsx | 7 --- shared/unlock-folders/device-list.desktop.tsx | 1 - shared/wallets/wallet-popup.tsx | 5 --- shared/whats-new/index.tsx | 5 --- shared/whats-new/versions.tsx | 11 ----- 135 files changed, 1255 deletions(-) diff --git a/shared/chat/audio/audio-player.tsx b/shared/chat/audio/audio-player.tsx index 4e5e5ff66b14..f27541891713 100644 --- a/shared/chat/audio/audio-player.tsx +++ b/shared/chat/audio/audio-player.tsx @@ -116,12 +116,6 @@ const AudioPlayer = (props: Props) => { } const styles = Kb.Styles.styleSheetCreate(() => ({ - button: { - borderRadius: 15, - height: 30, - position: 'relative', - width: 30, - }, container: { ...Kb.Styles.padding(Kb.Styles.globalMargins.xxtiny, Kb.Styles.globalMargins.tiny), backgroundColor: Kb.Styles.globalColors.white, @@ -130,9 +124,6 @@ const styles = Kb.Styles.styleSheetCreate(() => ({ borderStyle: 'solid', borderWidth: 1, }, - vis: { - alignSelf: 'flex-start', - }, visContainer: { alignItems: 'flex-start', justifyContent: 'flex-end', diff --git a/shared/chat/audio/audio-recorder.native.tsx b/shared/chat/audio/audio-recorder.native.tsx index 8c3a2d197c98..564e029ee800 100644 --- a/shared/chat/audio/audio-recorder.native.tsx +++ b/shared/chat/audio/audio-recorder.native.tsx @@ -821,10 +821,6 @@ const styles = Kb.Styles.styleSheetCreate(() => ({ bigBackgroundStyle: { ...circleAroundIcon(Kb.Styles.isTablet ? 2000 : 750), }, - cancelHintIcon: { - left: 0, - position: 'absolute', - }, cancelHintStyle: { ...Kb.Styles.globalStyles.flexBoxRow, alignItems: 'center', diff --git a/shared/chat/blocking/block-modal.tsx b/shared/chat/blocking/block-modal.tsx index 0a9dfdd64d03..c29fa87e9fa5 100644 --- a/shared/chat/blocking/block-modal.tsx +++ b/shared/chat/blocking/block-modal.tsx @@ -541,6 +541,5 @@ const styles = Kb.Styles.styleSheetCreate(() => ({ popupStyleContainer: {height: 450}, radioButton: {marginLeft: Kb.Styles.globalMargins.large}, - scroll: Kb.Styles.platformStyles({isMobile: {height: '100%'}}), shrink: {flexShrink: 1}, })) diff --git a/shared/chat/conversation/attachment-fullscreen/index.desktop.tsx b/shared/chat/conversation/attachment-fullscreen/index.desktop.tsx index 0c122da1e608..6f80c5700830 100644 --- a/shared/chat/conversation/attachment-fullscreen/index.desktop.tsx +++ b/shared/chat/conversation/attachment-fullscreen/index.desktop.tsx @@ -199,21 +199,6 @@ const styles = Kb.Styles.styleSheetCreate( paddingRight: Kb.Styles.globalMargins.tiny, width: '100%', }, - imgOrig: Kb.Styles.platformStyles({ - isElectron: { - display: 'flex', - margin: 'auto', - maxHeight: '100%', - maxWidth: '100%', - transform: '', - }, - }), - imgZoomed: Kb.Styles.platformStyles({ - isElectron: { - position: 'absolute', - transformOrigin: 'top left', - }, - }), link: Kb.Styles.platformStyles({ isElectron: {color: Kb.Styles.globalColors.black_50, cursor: 'pointer'}, }), @@ -221,27 +206,6 @@ const styles = Kb.Styles.styleSheetCreate( color: Kb.Styles.globalColors.redDark, textDecorationLine: 'underline', }, - scrollAttachOrig: Kb.Styles.platformStyles({ - isElectron: { - alignItems: 'center', - cursor: 'zoom-in', - display: 'flex', - height: '100%', - justifyContent: 'center', - overflow: 'hidden', - position: 'relative', - width: '100%', - }, - }), - scrollAttachZoomed: Kb.Styles.platformStyles({ - isElectron: { - cursor: 'zoom-out', - height: '100%', - overflow: 'hidden', - position: 'relative', - width: '100%', - }, - }), videoFit: Kb.Styles.platformStyles({ isElectron: { cursor: 'normal', diff --git a/shared/chat/conversation/attachment-fullscreen/index.native.tsx b/shared/chat/conversation/attachment-fullscreen/index.native.tsx index 97fa89f2a1b4..a8a9ffe8dd25 100644 --- a/shared/chat/conversation/attachment-fullscreen/index.native.tsx +++ b/shared/chat/conversation/attachment-fullscreen/index.native.tsx @@ -196,18 +196,10 @@ const styles = Kb.Styles.styleSheetCreate( right: 0, top: 0, }, - assetWrapper: { - ...Kb.Styles.globalStyles.flexBoxCenter, - flex: 1, - }, close: { color: Kb.Styles.globalColors.blueDark, padding: Kb.Styles.globalMargins.small, }, - fastImage: { - height: Kb.Styles.dimensionHeight, - width: Kb.Styles.dimensionWidth, - }, headerFooter: { ...Kb.Styles.globalStyles.flexBoxRow, alignItems: 'center', @@ -223,11 +215,6 @@ const styles = Kb.Styles.styleSheetCreate( headerWrapper: {backgroundColor: Kb.Styles.globalColors.blackOrBlack}, progressIndicator: {width: 48}, progressWrapper: {position: 'absolute'}, - safeAreaTop: { - ...Kb.Styles.globalStyles.flexBoxColumn, - ...Kb.Styles.globalStyles.fillAbsolute, - backgroundColor: Kb.Styles.globalColors.blackOrBlack, - }, videoWrapper: { alignItems: 'center', height: '100%', @@ -241,10 +228,6 @@ const styles = Kb.Styles.styleSheetCreate( position: 'relative', width: '100%', }, - zoomableBoxContainer: { - flex: 1, - position: 'relative', - }, }) as const ) diff --git a/shared/chat/conversation/attachment-get-titles.tsx b/shared/chat/conversation/attachment-get-titles.tsx index 609cbee8610b..bbc712628acf 100644 --- a/shared/chat/conversation/attachment-get-titles.tsx +++ b/shared/chat/conversation/attachment-get-titles.tsx @@ -239,7 +239,6 @@ const styles = Kb.Styles.styleSheetCreate( }, isMobile: Kb.Styles.padding(Kb.Styles.globalMargins.xsmall, Kb.Styles.globalMargins.small, 0), }), - cancelButton: {marginRight: Kb.Styles.globalMargins.tiny}, container: Kb.Styles.platformStyles({ common: { alignItems: 'center', @@ -277,20 +276,6 @@ const styles = Kb.Styles.styleSheetCreate( maxWidth: '100%', width: '100%', }, - imageContainer: Kb.Styles.platformStyles({ - common: {justifyContent: 'center'}, - isElectron: { - flex: 1, - height: 325, - paddingBottom: Kb.Styles.globalMargins.medium, - paddingTop: Kb.Styles.globalMargins.medium, - width: 325, - }, - isMobile: { - height: '100%', - width: '100%', - }, - }), input: Kb.Styles.platformStyles({ common: { borderColor: Kb.Styles.globalColors.blue, @@ -312,18 +297,6 @@ const styles = Kb.Styles.styleSheetCreate( paddingRight: Kb.Styles.globalMargins.small, }, }), - nonImage: { - alignSelf: 'center', - justifyContentSelf: 'center', - }, - scrollView: Kb.Styles.platformStyles({ - common: { - backgroundColor: Kb.Styles.globalColors.blueGrey, - height: '100%', - width: '100%', - }, - isElectron: {borderRadius: Kb.Styles.borderRadius}, - }), }) as const ) export default Container diff --git a/shared/chat/conversation/bot/channel-picker.tsx b/shared/chat/conversation/bot/channel-picker.tsx index db8b4ecdc028..ac0da027b912 100644 --- a/shared/chat/conversation/bot/channel-picker.tsx +++ b/shared/chat/conversation/bot/channel-picker.tsx @@ -145,10 +145,6 @@ const ChannelPicker = (props: Props) => { const styles = Kb.Styles.styleSheetCreate( () => ({ - channelCheckbox: { - marginRight: Kb.Styles.globalMargins.tiny, - paddingTop: 0, - }, channelHash: { alignSelf: 'center', color: Kb.Styles.globalColors.black_50, diff --git a/shared/chat/conversation/info-panel/attachments.tsx b/shared/chat/conversation/info-panel/attachments.tsx index 61b7fb7608a9..5cc7a6a05d48 100644 --- a/shared/chat/conversation/info-panel/attachments.tsx +++ b/shared/chat/conversation/info-panel/attachments.tsx @@ -316,10 +316,6 @@ const styles = Kb.Styles.styleSheetCreate( }, }), avatar: {marginRight: Kb.Styles.globalMargins.tiny}, - container: { - flex: 1, - height: '100%', - }, docBottom: {padding: Kb.Styles.globalMargins.tiny}, docIcon: {height: 32}, docProgress: {alignSelf: 'center'}, @@ -359,18 +355,6 @@ const styles = Kb.Styles.styleSheetCreate( alignSelf: 'center', marginTop: Kb.Styles.globalMargins.tiny, }, - loading: { - bottom: '50%', - left: '50%', - marginBottom: -12, - marginLeft: -12, - marginRight: -12, - marginTop: -12, - position: 'absolute', - right: '50%', - top: '50%', - width: 24, - }, selectorContainer: { maxWidth: 460, padding: Kb.Styles.globalMargins.small, diff --git a/shared/chat/conversation/info-panel/bot.tsx b/shared/chat/conversation/info-panel/bot.tsx index 1152170c6d5d..c3d7cee00aad 100644 --- a/shared/chat/conversation/info-panel/bot.tsx +++ b/shared/chat/conversation/info-panel/bot.tsx @@ -150,7 +150,6 @@ const styles = Kb.Styles.styleSheetCreate( marginRight: Kb.Styles.globalMargins.small, marginTop: Kb.Styles.globalMargins.small, }, - addButton: {marginLeft: Kb.Styles.globalMargins.tiny}, botHeaders: { marginBottom: Kb.Styles.globalMargins.tiny, marginLeft: Kb.Styles.globalMargins.small, @@ -165,27 +164,7 @@ const styles = Kb.Styles.styleSheetCreate( marginRight: Kb.Styles.globalMargins.tiny, }, }), - divider: Kb.Styles.platformStyles({ - common: {marginTop: Kb.Styles.globalMargins.tiny}, - isElectron: {marginLeft: 56}, - isMobile: {marginLeft: 81}, - }), listItemContainer: {paddingRight: Kb.Styles.globalMargins.tiny}, - row: { - alignItems: 'center', - flex: 1, - marginRight: Kb.Styles.globalMargins.tiny, - }, - rowContainer: Kb.Styles.platformStyles({ - common: { - minHeight: 48, - paddingLeft: Kb.Styles.globalMargins.small, - paddingRight: Kb.Styles.globalMargins.small, - }, - isElectron: { - ...Kb.Styles.desktopStyles.clickable, - }, - }), }) as const ) diff --git a/shared/chat/conversation/info-panel/header.tsx b/shared/chat/conversation/info-panel/header.tsx index 6d45cc0ca533..64a003077ceb 100644 --- a/shared/chat/conversation/info-panel/header.tsx +++ b/shared/chat/conversation/info-panel/header.tsx @@ -172,11 +172,6 @@ const styles = Kb.Styles.styleSheetCreate( marginLeft: Kb.Styles.globalMargins.small, marginRight: Kb.Styles.globalMargins.small, }, - adhocPartContainer: {padding: Kb.Styles.globalMargins.tiny}, - adhocScrollContainer: Kb.Styles.platformStyles({ - isElectron: {maxHeight: 230}, - isMobile: {maxHeight: 220}, - }), channelName: Kb.Styles.platformStyles({ isElectron: {wordBreak: 'break-all'}, }), @@ -185,13 +180,6 @@ const styles = Kb.Styles.styleSheetCreate( paddingLeft: Kb.Styles.globalMargins.small, paddingRight: Kb.Styles.globalMargins.small, }, - editBox: { - ...Kb.Styles.globalStyles.flexBoxRow, - position: 'absolute', - right: -50, - top: Kb.Styles.isMobile ? 2 : 1, - }, - editIcon: {marginRight: Kb.Styles.globalMargins.xtiny}, flexOne: {flex: 1}, floatingMenuContainerStyle: Kb.Styles.platformStyles({ isElectron: { diff --git a/shared/chat/conversation/info-panel/menu.tsx b/shared/chat/conversation/info-panel/menu.tsx index 0d1bd8e20c50..f605acb68839 100644 --- a/shared/chat/conversation/info-panel/menu.tsx +++ b/shared/chat/conversation/info-panel/menu.tsx @@ -474,21 +474,6 @@ const TeamHeader = (props: TeamHeaderProps) => { const styles = Kb.Styles.styleSheetCreate( () => ({ - badge: Kb.Styles.platformStyles({ - common: { - backgroundColor: Kb.Styles.globalColors.blue, - borderRadius: 6, - height: 8, - margin: 6, - width: 8, - }, - isElectron: { - margin: 4, - marginTop: 5, - position: 'absolute', - right: Kb.Styles.globalMargins.tiny, - }, - }), channelHeader: Kb.Styles.platformStyles({ common: { backgroundColor: Kb.Styles.globalColors.blueGreyLight, @@ -529,28 +514,12 @@ const styles = Kb.Styles.styleSheetCreate( wordBreak: 'break-word', } as const, }), - muteAction: { - ...Kb.Styles.globalStyles.flexBoxRow, - alignItems: 'center', - }, - noTopborder: { - borderTopWidth: 0, - }, teamHeader: { borderStyle: 'solid', borderTopColor: Kb.Styles.globalColors.black_10, borderTopWidth: 1, marginTop: Kb.Styles.globalMargins.tiny, }, - teamText: { - flex: 1, - justifyContent: 'space-between', - }, - text: Kb.Styles.platformStyles({ - isMobile: { - color: Kb.Styles.globalColors.blueDark, - }, - }), }) as const ) diff --git a/shared/chat/conversation/input-area/normal2/platform-input.desktop.tsx b/shared/chat/conversation/input-area/normal2/platform-input.desktop.tsx index 5533d8515bec..03ff601a671b 100644 --- a/shared/chat/conversation/input-area/normal2/platform-input.desktop.tsx +++ b/shared/chat/conversation/input-area/normal2/platform-input.desktop.tsx @@ -475,17 +475,6 @@ const styles = Kb.Styles.styleSheetCreate( backgroundColor: Kb.Styles.globalColors.white, width: '100%', }, - emojiPickerContainer: Kb.Styles.platformStyles({ - common: { - borderRadius: 4, - bottom: 32, - position: 'absolute', - right: -64, - }, - isElectron: {...Kb.Styles.desktopStyles.boxShadow}, - }), - emojiPickerContainerWrapper: {...Kb.Styles.globalStyles.fillAbsolute}, - emojiPickerRelative: {position: 'relative'}, explodingIconContainer: Kb.Styles.platformStyles({ common: { ...Kb.Styles.globalStyles.flexBoxColumn, @@ -564,11 +553,6 @@ const styles = Kb.Styles.styleSheetCreate( position: 'absolute', right: Kb.Styles.globalMargins.medium, }, - walletsIcon: { - alignSelf: 'flex-end', - marginBottom: 2, - marginRight: Kb.Styles.globalMargins.xtiny, - }, }) as const ) diff --git a/shared/chat/conversation/input-area/normal2/platform-input.native.tsx b/shared/chat/conversation/input-area/normal2/platform-input.native.tsx index 496f7ebb8c5a..b986cc6cbc7e 100644 --- a/shared/chat/conversation/input-area/normal2/platform-input.native.tsx +++ b/shared/chat/conversation/input-area/normal2/platform-input.native.tsx @@ -550,15 +550,6 @@ const styles = Kb.Styles.styleSheetCreate( marginLeft: Kb.Styles.globalMargins.tiny, marginRight: Kb.Styles.globalMargins.tiny, }, - editingTabStyle: { - ...Kb.Styles.globalStyles.flexBoxColumn, - alignItems: 'flex-start', - backgroundColor: Kb.Styles.globalColors.yellowOrYellowAlt, - flexShrink: 0, - height: '100%', - minWidth: 32, - padding: Kb.Styles.globalMargins.xtiny, - }, exploding: { backgroundColor: Kb.Styles.globalColors.black, borderRadius: Kb.Styles.globalMargins.mediumLarge / 2, @@ -583,7 +574,6 @@ const styles = Kb.Styles.styleSheetCreate( justifyContent: 'center', width: 36, }, - hidden: {height: 0, maxHeight: 0, maxWidth: 0, width: 0}, iconBottom: { bottom: 0, left: 1, diff --git a/shared/chat/conversation/input-area/normal2/typing.tsx b/shared/chat/conversation/input-area/normal2/typing.tsx index c274f4396aae..adfc08867ee1 100644 --- a/shared/chat/conversation/input-area/normal2/typing.tsx +++ b/shared/chat/conversation/input-area/normal2/typing.tsx @@ -113,19 +113,6 @@ const styles = Kb.Styles.styleSheetCreate( marginRight: Kb.Styles.globalMargins.tiny, }, }), - typingIcon: Kb.Styles.platformStyles({ - common: { - position: 'absolute', - width: 24, - }, - isElectron: { - bottom: 7, - left: 21, - }, - isMobile: { - bottom: 0, - }, - }), typingIconContainer: Kb.Styles.platformStyles({ isMobile: { alignItems: 'center', diff --git a/shared/chat/conversation/input-area/suggestors/shared.tsx b/shared/chat/conversation/input-area/suggestors/shared.tsx index df6c845679f6..c5a0d4c7207d 100644 --- a/shared/chat/conversation/input-area/suggestors/shared.tsx +++ b/shared/chat/conversation/input-area/suggestors/shared.tsx @@ -36,22 +36,4 @@ const styles = Kb.Styles.styleSheetCreate(() => ({ paddingLeft: Kb.Styles.globalMargins.tiny, }, }), - timeBadge: Kb.Styles.platformStyles({ - common: { - borderColor: Kb.Styles.globalColors.white, - borderRadius: 3, - borderStyle: 'solid', - }, - isElectron: { - borderWidth: 1, - cursor: 'pointer', - marginLeft: -11, - marginTop: -6, - }, - isMobile: { - borderWidth: 2, - marginLeft: -5, - marginTop: -1, - }, - }), })) diff --git a/shared/chat/conversation/input-area/suggestors/suggestion-list.desktop.tsx b/shared/chat/conversation/input-area/suggestors/suggestion-list.desktop.tsx index 9a25e5bfa147..dac5d95447dd 100644 --- a/shared/chat/conversation/input-area/suggestors/suggestion-list.desktop.tsx +++ b/shared/chat/conversation/input-area/suggestors/suggestion-list.desktop.tsx @@ -60,7 +60,6 @@ const styles = Kb.Styles.styleSheetCreate( justifyContent: 'center', ...Kb.Styles.padding(Kb.Styles.globalMargins.xxtiny, 0), }, - fullHeight: {height: '100%'}, listContainer: { backgroundColor: Kb.Styles.globalColors.white, borderRadius: 4, diff --git a/shared/chat/conversation/messages/account-payment/container.tsx b/shared/chat/conversation/messages/account-payment/container.tsx index e728ab2606eb..8682e953227f 100644 --- a/shared/chat/conversation/messages/account-payment/container.tsx +++ b/shared/chat/conversation/messages/account-payment/container.tsx @@ -229,11 +229,6 @@ const styles = Kb.Styles.styleSheetCreate( }, isMobile: {justifyContent: 'space-between'}, }), - button: { - alignSelf: 'flex-start', - marginTop: Kb.Styles.globalMargins.xtiny, - }, - buttonText: {color: Kb.Styles.globalColors.white}, flexWrap: {flexWrap: 'wrap'}, lineThrough: {textDecorationLine: 'line-through'}, memo: Kb.Styles.platformStyles({ @@ -252,9 +247,6 @@ const styles = Kb.Styles.styleSheetCreate( }), purple: {color: Kb.Styles.globalColors.purpleDark}, purpleOrWhite: {color: Kb.Styles.globalColors.purpleDarkOrWhite}, - tooltipText: Kb.Styles.platformStyles({ - isElectron: {wordBreak: 'normal'}, - }), }) as const ) diff --git a/shared/chat/conversation/messages/attachment/image2/imageimpl.desktop.tsx b/shared/chat/conversation/messages/attachment/image2/imageimpl.desktop.tsx index 1b49e75ba02e..782eae11e1a9 100644 --- a/shared/chat/conversation/messages/attachment/image2/imageimpl.desktop.tsx +++ b/shared/chat/conversation/messages/attachment/image2/imageimpl.desktop.tsx @@ -19,18 +19,6 @@ const Image2Impl = () => { const styles = Kb.Styles.styleSheetCreate( () => ({ - actionContainer: { - alignSelf: 'flex-end', - backgroundColor: Kb.Styles.globalColors.black_50, - borderRadius: 2, - overflow: 'hidden', - padding: 1, - paddingLeft: 4, - paddingRight: 4, - position: 'absolute', - right: Kb.Styles.globalMargins.tiny, - top: Kb.Styles.globalMargins.tiny, - }, image: Kb.Styles.platformStyles({ isElectron: { ...Kb.Styles.globalStyles.rounded, diff --git a/shared/chat/conversation/messages/attachment/video/videoimpl.desktop.tsx b/shared/chat/conversation/messages/attachment/video/videoimpl.desktop.tsx index 28b6c650569b..67429b5aaded 100644 --- a/shared/chat/conversation/messages/attachment/video/videoimpl.desktop.tsx +++ b/shared/chat/conversation/messages/attachment/video/videoimpl.desktop.tsx @@ -59,13 +59,6 @@ const VideoImpl = (p: Props) => { const styles = Kb.Styles.styleSheetCreate( () => ({ - downloadIcon: Kb.Styles.platformStyles({ - isElectron: { - display: 'inline-flex', - opacity: 0.75, - paddingTop: 2, - }, - }), durationContainer: { alignSelf: 'flex-end', backgroundColor: Kb.Styles.globalColors.black_50, @@ -81,17 +74,6 @@ const styles = Kb.Styles.styleSheetCreate( paddingLeft: 3, paddingRight: 3, }, - infoIcon: Kb.Styles.platformStyles({ - isElectron: { - display: 'inline-flex', - opacity: 0.75, - paddingTop: 2, - }, - }), - link: { - color: Kb.Styles.globalColors.black_50, - flexGrow: 1, - }, playButton: { left: '50%', marginLeft: -32, @@ -104,7 +86,6 @@ const styles = Kb.Styles.styleSheetCreate( flexShrink: 1, position: 'relative', }, - tipText: {color: Kb.Styles.globalColors.white_75}, video: Kb.Styles.platformStyles({ isElectron: { ...Kb.Styles.globalStyles.rounded, diff --git a/shared/chat/conversation/messages/cards/hello-bot.tsx b/shared/chat/conversation/messages/cards/hello-bot.tsx index 8c20e9a3ac7c..efeaf57f7a61 100644 --- a/shared/chat/conversation/messages/cards/hello-bot.tsx +++ b/shared/chat/conversation/messages/cards/hello-bot.tsx @@ -37,12 +37,6 @@ const styles = Kb.Styles.styleSheetCreate( header: { maxWidth: Kb.Styles.isMobile ? 126 : undefined, }, - icon: Kb.Styles.platformStyles({ - isElectron: { - display: 'block', - marginTop: 4, - }, - }), image: Kb.Styles.platformStyles({ common: { marginLeft: Kb.Styles.globalMargins.medium, @@ -55,7 +49,6 @@ const styles = Kb.Styles.styleSheetCreate( marginTop: Kb.Styles.globalMargins.tiny, }, }), - link: {color: Kb.Styles.isMobile ? Kb.Styles.globalColors.blueLighter : undefined}, textContainer: {padding: Kb.Styles.globalMargins.medium}, }) as const ) diff --git a/shared/chat/conversation/messages/cards/team-journey/container.tsx b/shared/chat/conversation/messages/cards/team-journey/container.tsx index 19d47a2be434..00f24f9eac04 100644 --- a/shared/chat/conversation/messages/cards/team-journey/container.tsx +++ b/shared/chat/conversation/messages/cards/team-journey/container.tsx @@ -241,13 +241,6 @@ const styles = Kb.Styles.styleSheetCreate( }, isMobile: {marginLeft: Kb.Styles.globalMargins.tiny}, }), - avatarTeamSettings: Kb.Styles.platformStyles({ - isElectron: { - marginLeft: Kb.Styles.globalMargins.tiny, - marginTop: 0, - }, - isMobile: {marginLeft: Kb.Styles.globalMargins.xtiny}, - }), bottomLine: { ...Kb.Styles.globalStyles.flexGrow, alignItems: 'baseline', @@ -274,38 +267,10 @@ const styles = Kb.Styles.styleSheetCreate( Kb.Styles.globalMargins.mediumLarge, // avatar }, }), - contentHorizontalPadTeamSettings: Kb.Styles.platformStyles({ - isElectron: { - paddingLeft: - // Space for below the avatar - Kb.Styles.globalMargins.tiny + // right margin - Kb.Styles.globalMargins.tiny + // left margin - Kb.Styles.globalMargins.mediumLarge, // avatar - paddingRight: Kb.Styles.globalMargins.tiny, - }, - isMobile: { - paddingLeft: - // Space for below the avatar - Kb.Styles.globalMargins.tiny + // right margin - Kb.Styles.globalMargins.tiny + // left margin - Kb.Styles.globalMargins.mediumLarge, // avatar - }, - }), contentWithImage: {minHeight: 70}, image: Kb.Styles.platformStyles({ isElectron: {marginTop: -33}, }), - imageSettingsTab: Kb.Styles.platformStyles({ - common: { - position: 'absolute', - top: 0, - }, - isElectron: { - left: '50%', - marginLeft: 15, - }, - isMobile: {right: 25}, - }), teamnameText: {color: Kb.Styles.globalColors.black}, text: {maxWidth: Kb.Styles.isMobile ? '70%' : 320}, }) as const diff --git a/shared/chat/conversation/messages/message-popup/header.tsx b/shared/chat/conversation/messages/message-popup/header.tsx index d57e76ad84f5..7c0c2d864558 100644 --- a/shared/chat/conversation/messages/message-popup/header.tsx +++ b/shared/chat/conversation/messages/message-popup/header.tsx @@ -142,7 +142,6 @@ const styles = Kb.Styles.styleSheetCreate( () => ({ alignItemsCenter: {alignItems: 'center'}, - colorBlack40: {color: Kb.Styles.globalColors.black_50}, headerContainer: Kb.Styles.platformStyles({ common: { ...Kb.Styles.globalStyles.flexBoxColumn, @@ -155,11 +154,6 @@ const styles = Kb.Styles.styleSheetCreate( minWidth: 200, }, }), - headerDetailsContainer: { - ...Kb.Styles.globalStyles.flexBoxRow, - paddingLeft: Kb.Styles.globalMargins.small, - paddingRight: Kb.Styles.globalMargins.small, - }, headerIcon: Kb.Styles.platformStyles({ common: { height: headerIconHeight, diff --git a/shared/chat/conversation/messages/placeholder/wrapper.tsx b/shared/chat/conversation/messages/placeholder/wrapper.tsx index 45c196e3be7a..b2a278feb43b 100644 --- a/shared/chat/conversation/messages/placeholder/wrapper.tsx +++ b/shared/chat/conversation/messages/placeholder/wrapper.tsx @@ -53,11 +53,6 @@ const styles = Kb.Styles.styleSheetCreate( height: Kb.Styles.isMobile ? 22 : 17, // to match a line of text width: '100%', }, - spinner: { - height: 16, - marginLeft: 0, - width: 16, - }, }) as const ) diff --git a/shared/chat/conversation/messages/react-button.tsx b/shared/chat/conversation/messages/react-button.tsx index 4ce79b009d4b..a9dbf69bdaf1 100644 --- a/shared/chat/conversation/messages/react-button.tsx +++ b/shared/chat/conversation/messages/react-button.tsx @@ -152,23 +152,11 @@ const styles = Kb.Styles.styleSheetCreate( }, isElectron: {...Kb.Styles.transition('border-color', 'background-color', 'box-shadow')}, }), - containerInner: { - alignItems: 'center', - height: 24, - }, count: { color: Kb.Styles.globalColors.black_50, position: 'relative', }, countActive: {color: Kb.Styles.globalColors.blueDark}, - emoji: {height: 25}, - emojiContainer: Kb.Styles.platformStyles({ - isElectron: { - ...Kb.Styles.desktopStyles.boxShadow, - borderRadius: 4, - marginRight: Kb.Styles.globalMargins.small, - }, - }), emojiIconWrapper: Kb.Styles.platformStyles({ isElectron: {position: 'absolute'}, isMobile: {marginTop: 2}, diff --git a/shared/chat/conversation/messages/reaction-tooltip.tsx b/shared/chat/conversation/messages/reaction-tooltip.tsx index d196fa1180f1..70dbeaf79bcf 100644 --- a/shared/chat/conversation/messages/reaction-tooltip.tsx +++ b/shared/chat/conversation/messages/reaction-tooltip.tsx @@ -192,7 +192,6 @@ const styles = Kb.Styles.styleSheetCreate( paddingTop: Kb.Styles.globalMargins.small, }, addReactionButtonIcon: {marginRight: Kb.Styles.globalMargins.tiny}, - addReactionButtonText: {color: Kb.Styles.globalColors.black_50}, buttonContainer: { alignItems: 'center', backgroundColor: Kb.Styles.globalColors.white, diff --git a/shared/chat/conversation/messages/reactions-rows.tsx b/shared/chat/conversation/messages/reactions-rows.tsx index 21d1eef42325..e5bff9213792 100644 --- a/shared/chat/conversation/messages/reactions-rows.tsx +++ b/shared/chat/conversation/messages/reactions-rows.tsx @@ -128,7 +128,6 @@ const styles = Kb.Styles.styleSheetCreate( marginBottom: Kb.Styles.globalMargins.tiny, paddingRight: Kb.Styles.globalMargins.xtiny, }, - visibilityHidden: Kb.Styles.platformStyles({isElectron: {visibility: 'hidden'}}), }) as const ) diff --git a/shared/chat/conversation/messages/system-joined/container.tsx b/shared/chat/conversation/messages/system-joined/container.tsx index a9dc7563c9d2..970147b86a1d 100644 --- a/shared/chat/conversation/messages/system-joined/container.tsx +++ b/shared/chat/conversation/messages/system-joined/container.tsx @@ -71,10 +71,6 @@ const MultiUserJoinedNotice = (p: { const styles = Kb.Styles.styleSheetCreate( () => ({ - avatarLine: Kb.Styles.platformStyles({ - isElectron: {marginLeft: -2 + 48}, - isMobile: {marginLeft: -Kb.Styles.globalMargins.xsmall}, - }), container: {marginLeft: -40, paddingBottom: 4}, timestamp: Kb.Styles.platformStyles({isElectron: {lineHeight: 19}}), }) as const diff --git a/shared/chat/conversation/messages/text/coinflip/index.tsx b/shared/chat/conversation/messages/text/coinflip/index.tsx index 45f53d2d088d..35d32126979f 100644 --- a/shared/chat/conversation/messages/text/coinflip/index.tsx +++ b/shared/chat/conversation/messages/text/coinflip/index.tsx @@ -168,26 +168,10 @@ const styles = Kb.Styles.styleSheetCreate( flipAgainContainer: {paddingTop: Kb.Styles.globalMargins.tiny}, flipAgainContainerHidden: {opacity: 0, paddingTop: Kb.Styles.globalMargins.tiny}, placeholder: {backgroundColor: Kb.Styles.globalColors.grey}, - progress: Kb.Styles.platformStyles({ - isElectron: { - cursor: 'text', - userSelect: 'text', - wordBreak: 'break-all', - }, - }), progressVis: { height: 40, width: 64, }, - result: Kb.Styles.platformStyles({ - common: {fontWeight: '600'}, - isElectron: { - cursor: 'text', - userSelect: 'text', - wordBreak: 'break-all', - }, - }), - statusContainer: {paddingTop: Kb.Styles.globalMargins.tiny}, }) as const ) diff --git a/shared/chat/conversation/messages/text/coinflip/participants.tsx b/shared/chat/conversation/messages/text/coinflip/participants.tsx index a5d36fd592a5..6c6d5df3d924 100644 --- a/shared/chat/conversation/messages/text/coinflip/participants.tsx +++ b/shared/chat/conversation/messages/text/coinflip/participants.tsx @@ -74,11 +74,6 @@ const styles = Kb.Styles.styleSheetCreate( marginBottom: Kb.Styles.globalMargins.tiny, marginTop: Kb.Styles.globalMargins.tiny, }, - title: Kb.Styles.platformStyles({ - isElectron: { - paddingTop: Kb.Styles.globalMargins.xtiny, - }, - }), }) as const ) diff --git a/shared/chat/conversation/messages/text/reply.tsx b/shared/chat/conversation/messages/text/reply.tsx index bc0d9c745128..652f53798e58 100644 --- a/shared/chat/conversation/messages/text/reply.tsx +++ b/shared/chat/conversation/messages/text/reply.tsx @@ -168,18 +168,6 @@ const styles = Kb.Styles.styleSheetCreate( overflow: 'hidden', position: 'relative', }, - replyProgress: { - bottom: '50%', - left: '50%', - marginBottom: -12, - marginLeft: -12, - marginRight: -12, - marginTop: -12, - position: 'absolute', - right: '50%', - top: '50%', - width: 24, - }, replyTextContainer: { alignSelf: 'flex-start', flex: 1, diff --git a/shared/chat/conversation/messages/text/unfurl/unfurl-list/giphy.tsx b/shared/chat/conversation/messages/text/unfurl/unfurl-list/giphy.tsx index 238515cda7c3..6546e4e912ed 100644 --- a/shared/chat/conversation/messages/text/unfurl/unfurl-list/giphy.tsx +++ b/shared/chat/conversation/messages/text/unfurl/unfurl-list/giphy.tsx @@ -109,12 +109,6 @@ const styles = Kb.Styles.styleSheetCreate( height: 16, width: 16, }, - imageContainer: Kb.Styles.platformStyles({ - isMobile: { - alignSelf: 'flex-start', - padding: Kb.Styles.globalMargins.xxtiny, - }, - }), innerContainer: Kb.Styles.platformStyles({ common: { alignSelf: 'flex-start', diff --git a/shared/chat/conversation/messages/wrapper/exploding-meta.tsx b/shared/chat/conversation/messages/wrapper/exploding-meta.tsx index fa796f6b0354..33e8700f3e29 100644 --- a/shared/chat/conversation/messages/wrapper/exploding-meta.tsx +++ b/shared/chat/conversation/messages/wrapper/exploding-meta.tsx @@ -282,19 +282,7 @@ const styles = Kb.Styles.styleSheetCreate( }, countdownContainerHighlighted: {backgroundColor: Kb.Styles.globalColors.blackOrBlack}, countdownHighlighted: {color: Kb.Styles.globalColors.whiteOrWhite}, - explodingTooltip: {marginRight: -Kb.Styles.globalMargins.xxtiny}, hidden: {opacity: 0}, - progressContainer: Kb.Styles.platformStyles({ - common: { - alignItems: 'center', - justifyContent: 'center', - }, - isElectron: {width: 28}, - isMobile: { - height: 15, - width: 32, - }, - }), }) as const ) diff --git a/shared/chat/conversation/messages/wrapper/long-pressable/index.native.tsx b/shared/chat/conversation/messages/wrapper/long-pressable/index.native.tsx index 72f39ea87942..8c097952754d 100644 --- a/shared/chat/conversation/messages/wrapper/long-pressable/index.native.tsx +++ b/shared/chat/conversation/messages/wrapper/long-pressable/index.native.tsx @@ -111,10 +111,6 @@ const styles = Kb.Styles.styleSheetCreate( justifyContent: 'flex-end', }, replyIcon: {paddingRight: Kb.Styles.globalMargins.small}, - view: { - ...Kb.Styles.globalStyles.flexBoxColumn, - position: 'relative', - }, }) as const ) diff --git a/shared/chat/conversation/messages/wrapper/send-indicator.tsx b/shared/chat/conversation/messages/wrapper/send-indicator.tsx index 6f197da1ed5a..919c3e08cde5 100644 --- a/shared/chat/conversation/messages/wrapper/send-indicator.tsx +++ b/shared/chat/conversation/messages/wrapper/send-indicator.tsx @@ -142,10 +142,6 @@ const SendIndicatorContainer = React.memo(function SendIndicatorContainer() { const styles = Kb.Styles.styleSheetCreate( () => ({ - animationInvisible: Kb.Styles.platformStyles({ - common: {height: 20, opacity: 0, width: 20}, - isMobile: {backgroundColor: Kb.Styles.globalColors.white}, - }), animationVisible: Kb.Styles.platformStyles({ common: {height: 20, opacity: 1, width: 20}, isMobile: { diff --git a/shared/chat/conversation/messages/wrapper/wrapper.tsx b/shared/chat/conversation/messages/wrapper/wrapper.tsx index e4f0a9609e36..5dd333456f15 100644 --- a/shared/chat/conversation/messages/wrapper/wrapper.tsx +++ b/shared/chat/conversation/messages/wrapper/wrapper.tsx @@ -617,19 +617,6 @@ const styles = Kb.Styles.styleSheetCreate( }, }), failUnderline: {color: Kb.Styles.globalColors.redDark, textDecorationLine: 'underline'}, - highlighted: { - backgroundColor: Kb.Styles.globalColors.yellowOrYellowAlt, - }, - menuButtons: Kb.Styles.platformStyles({ - common: { - alignSelf: 'flex-start', - flexShrink: 0, - justifyContent: 'flex-end', - overflow: 'hidden', - }, - isElectron: {height: 20}, - isMobile: {height: 24}, - }), messagePopupContainer: {marginRight: Kb.Styles.globalMargins.small}, middle: { flexGrow: 1, @@ -638,8 +625,6 @@ const styles = Kb.Styles.styleSheetCreate( paddingRight: 4, position: 'relative', }, - moreActionsTooltip: {marginRight: -Kb.Styles.globalMargins.xxtiny}, - paddingLeftTiny: {paddingLeft: Kb.Styles.globalMargins.tiny}, rightSide: Kb.Styles.platformStyles({ common: { borderRadius: Kb.Styles.borderRadius, @@ -663,15 +648,5 @@ const styles = Kb.Styles.styleSheetCreate( }, isElectron: {minHeight: 14}, }), - sendIndicatorPlaceholder: { - height: 20, - width: 20, - }, - timestamp: Kb.Styles.platformStyles({ - isElectron: { - flexShrink: 0, - lineHeight: 19, - }, - }), }) as const ) diff --git a/shared/chat/conversation/normal/index.native.tsx b/shared/chat/conversation/normal/index.native.tsx index bfb4263d9019..2e44b8a11b2d 100644 --- a/shared/chat/conversation/normal/index.native.tsx +++ b/shared/chat/conversation/normal/index.native.tsx @@ -114,17 +114,6 @@ const styles = Kb.Styles.styleSheetCreate( position: 'relative', }, offline: {padding: Kb.Styles.globalMargins.xxtiny}, - outerContainer: Kb.Styles.platformStyles({ - isTablet: { - flex: 1, - position: 'relative', - }, - }), - sav: { - flexGrow: 1, - maxHeight: '100%', - position: 'relative', - }, }) as const ) diff --git a/shared/chat/conversation/pinned-message.tsx b/shared/chat/conversation/pinned-message.tsx index 9e18af274b21..f1be26373338 100644 --- a/shared/chat/conversation/pinned-message.tsx +++ b/shared/chat/conversation/pinned-message.tsx @@ -187,10 +187,6 @@ const styles = Kb.Styles.styleSheetCreate( }, isElectron: {maxWidth: 200}, }), - styleOverride: Kb.Styles.platformStyles({ - common: {color: Kb.Styles.globalColors.black_50}, - isElectron: {transition: 'color 0.25s ease-in-out'}, - }), text: Kb.Styles.platformStyles({ common: {color: Kb.Styles.globalColors.black_50}, isElectron: { diff --git a/shared/chat/emoji-picker/container.tsx b/shared/chat/emoji-picker/container.tsx index 34376fe70b98..7dfa47c45929 100644 --- a/shared/chat/emoji-picker/container.tsx +++ b/shared/chat/emoji-picker/container.tsx @@ -321,13 +321,6 @@ const styles = Kb.Styles.styleSheetCreate( height: Kb.Styles.globalMargins.mediumLarge + Kb.Styles.globalMargins.small, }, }), - input: { - borderBottomWidth: 1, - borderColor: Kb.Styles.globalColors.black_10, - borderRadius: 0, - borderWidth: 0, - padding: Kb.Styles.globalMargins.small, - }, searchFilter: Kb.Styles.platformStyles({ isMobile: { flexGrow: 1, diff --git a/shared/chat/inbox/row/start-new-chat.tsx b/shared/chat/inbox/row/start-new-chat.tsx index f94c8120943d..e6d5784c51aa 100644 --- a/shared/chat/inbox/row/start-new-chat.tsx +++ b/shared/chat/inbox/row/start-new-chat.tsx @@ -28,11 +28,6 @@ const StartNewChat = (props: Props) => { const styles = Kb.Styles.styleSheetCreate( () => ({ - backButton: { - left: 0, - position: 'absolute', - top: Kb.Styles.globalMargins.xxtiny, - }, button: Kb.Styles.platformStyles({ common: { flexGrow: 1, @@ -41,9 +36,6 @@ const styles = Kb.Styles.styleSheetCreate( }, isElectron: Kb.Styles.desktopStyles.windowDraggingClickable, }), - buttonIcon: { - marginRight: Kb.Styles.globalMargins.tiny, - }, clickableBox: { alignItems: 'center', flexDirection: 'row', @@ -72,12 +64,6 @@ const styles = Kb.Styles.styleSheetCreate( padding: Kb.Styles.globalMargins.xtiny, }, }), - rabbitEmoji: { - marginLeft: Kb.Styles.globalMargins.xtiny, - }, - startNewChatText: { - color: Kb.Styles.globalColors.white, - }, }) as const ) diff --git a/shared/chat/location-map.desktop.tsx b/shared/chat/location-map.desktop.tsx index 5ddcd7c071ed..5ddf7036b307 100644 --- a/shared/chat/location-map.desktop.tsx +++ b/shared/chat/location-map.desktop.tsx @@ -53,9 +53,6 @@ const styles = Kb.Styles.styleSheetCreate( alignItems: 'center', justifyContent: 'center', }, - learn: { - color: Kb.Styles.globalColors.blueDark, - }, loading: { bottom: '50%', left: '50%', diff --git a/shared/chat/location-map.native.tsx b/shared/chat/location-map.native.tsx index 051f2b115a23..d30f61c27abd 100644 --- a/shared/chat/location-map.native.tsx +++ b/shared/chat/location-map.native.tsx @@ -50,9 +50,6 @@ const styles = Kb.Styles.styleSheetCreate( ...Kb.Styles.globalStyles.fillAbsolute, justifyContent: 'center', }, - learn: { - color: Kb.Styles.globalColors.blueDark, - }, loading: { bottom: '50%', left: '50%', @@ -65,10 +62,5 @@ const styles = Kb.Styles.styleSheetCreate( top: '50%', width: 24, }, - mapImage: Kb.Styles.platformStyles({ - isTablet: { - resizeMode: 'cover', - }, - }), }) as const ) diff --git a/shared/common-adapters/avatar/index.desktop.tsx b/shared/common-adapters/avatar/index.desktop.tsx index cf95470db3b2..af482e68c9a3 100644 --- a/shared/common-adapters/avatar/index.desktop.tsx +++ b/shared/common-adapters/avatar/index.desktop.tsx @@ -156,11 +156,6 @@ const styles = Styles.styleSheetCreate( right: -6, }, }), - editTeamOld: { - bottom: -2, - position: 'absolute', - right: -18, - }, poopContainer: { alignItems: 'center', display: 'flex', diff --git a/shared/common-adapters/avatar/index.native.tsx b/shared/common-adapters/avatar/index.native.tsx index 397ec5d1e294..d3e183163e8f 100644 --- a/shared/common-adapters/avatar/index.native.tsx +++ b/shared/common-adapters/avatar/index.native.tsx @@ -24,8 +24,6 @@ const sizeToTeamBorderRadius = new Map([ ]) const backgroundOffset = 1 -const borderOffset = -1 -const borderSize = 1 // Layer on top to extend outside of the image const Avatar = React.memo(function Avatar(p: Props) { @@ -136,15 +134,6 @@ const styles = Styles.styleSheetCreate( right: backgroundOffset, top: backgroundOffset, }, - borderBase: { - borderWidth: borderSize, - bottom: borderOffset, - left: borderOffset, - margin: borderSize / 2, - position: 'absolute', - right: borderOffset, - top: borderOffset, - }, edit: { bottom: 0, position: 'absolute', diff --git a/shared/common-adapters/checkbox.desktop.tsx b/shared/common-adapters/checkbox.desktop.tsx index e42f1b45e587..a04afc136e7a 100644 --- a/shared/common-adapters/checkbox.desktop.tsx +++ b/shared/common-adapters/checkbox.desktop.tsx @@ -92,7 +92,6 @@ const styles = Kb.Styles.styleSheetCreate(() => ({ alignSelf: 'center', }, }), - opaque: {opacity: 1}, semiTransparent: {opacity: 0.4}, transparent: {opacity: 0}, })) diff --git a/shared/common-adapters/drag-and-drop.desktop.tsx b/shared/common-adapters/drag-and-drop.desktop.tsx index 2054f0fa9e9f..6750a3f5fa22 100644 --- a/shared/common-adapters/drag-and-drop.desktop.tsx +++ b/shared/common-adapters/drag-and-drop.desktop.tsx @@ -110,16 +110,6 @@ const styles = Styles.styleSheetCreate( padding: Styles.globalMargins.large, }, }), - icon: { - position: 'relative', - top: 2, - }, - iconContainer: { - backgroundColor: Styles.globalColors.blue, - borderRadius: 100, - height: 48, - width: 48, - }, }) as const ) diff --git a/shared/common-adapters/floating-menu/menu-layout/index.native.tsx b/shared/common-adapters/floating-menu/menu-layout/index.native.tsx index 79eea0facf21..43e8783fe4ea 100644 --- a/shared/common-adapters/floating-menu/menu-layout/index.native.tsx +++ b/shared/common-adapters/floating-menu/menu-layout/index.native.tsx @@ -262,7 +262,6 @@ const styles = Styles.styleSheetCreate( marginTop: Styles.globalMargins.tiny, }, firstIsUnWrapped: {paddingTop: 0}, - flexOne: {flex: 1}, iconBadge: { backgroundColor: Styles.globalColors.blue, height: Styles.globalMargins.tiny, diff --git a/shared/common-adapters/floating-picker.native.tsx b/shared/common-adapters/floating-picker.native.tsx index 4e9327b1b09c..3ab3b4157374 100644 --- a/shared/common-adapters/floating-picker.native.tsx +++ b/shared/common-adapters/floating-picker.native.tsx @@ -124,19 +124,6 @@ const styles = Styles.styleSheetCreate( backgroundColor: Styles.globalColors.white, justifyContent: 'flex-end', }, - overlay: { - ...Styles.globalStyles.flexBoxColumn, - alignItems: 'stretch', - backgroundColor: Styles.globalColors.black_50, - justifyContent: 'flex-end', - }, - overlayContainer: { - bottom: 0, - left: 0, - position: 'absolute', - right: 0, - top: 0, - }, picker: Styles.platformStyles({ isAndroid: { color: Styles.globalColors.black, diff --git a/shared/common-adapters/header-hoc/index.native.tsx b/shared/common-adapters/header-hoc/index.native.tsx index a982412a2968..37a56f896a82 100644 --- a/shared/common-adapters/header-hoc/index.native.tsx +++ b/shared/common-adapters/header-hoc/index.native.tsx @@ -180,7 +180,6 @@ const styles = Styles.styleSheetCreate( paddingLeft: Styles.globalMargins.tiny, }, }), - actionPressable: {opacity: 0.3}, borderless: {borderBottomWidth: 0}, container: { ...Styles.globalStyles.flexBoxColumn, @@ -206,7 +205,6 @@ const styles = Styles.styleSheetCreate( height: 40 + Styles.headerExtraHeight, }, }), - innerWrapper: {...Styles.globalStyles.fillAbsolute}, leftAction: Styles.platformStyles({ common: { ...Styles.globalStyles.flexBoxColumn, diff --git a/shared/common-adapters/labeled-input.tsx b/shared/common-adapters/labeled-input.tsx index 53116fea9bf9..478bf7e902ef 100644 --- a/shared/common-adapters/labeled-input.tsx +++ b/shared/common-adapters/labeled-input.tsx @@ -131,9 +131,6 @@ const styles = Styles.styleSheetCreate( }), containerError: {borderColor: Styles.globalColors.red}, containerFocused: {borderColor: Styles.globalColors.blue}, - displayFlex: {display: 'flex'}, - hideBorder: {borderWidth: 0}, - icon: {marginRight: Styles.globalMargins.xtiny}, input: { backgroundColor: Styles.globalColors.transparent, flexGrow: 1, diff --git a/shared/common-adapters/markdown/maybe-mention/unknown.tsx b/shared/common-adapters/markdown/maybe-mention/unknown.tsx index 219405c73acb..e2bb0e8fc35c 100644 --- a/shared/common-adapters/markdown/maybe-mention/unknown.tsx +++ b/shared/common-adapters/markdown/maybe-mention/unknown.tsx @@ -137,9 +137,6 @@ const styles = Kb.Styles.styleSheetCreate( display: 'inline-block', }, }), - warning: { - color: Kb.Styles.globalColors.redDark, - }, }) as const ) diff --git a/shared/common-adapters/markdown/spoiler.tsx b/shared/common-adapters/markdown/spoiler.tsx index 335e899717ad..388077928118 100644 --- a/shared/common-adapters/markdown/spoiler.tsx +++ b/shared/common-adapters/markdown/spoiler.tsx @@ -78,12 +78,6 @@ const styles = Styles.styleSheetCreate(() => { paddingRight: 2, }, }), - tip: Styles.platformStyles({ - isElectron: { - alignItems: 'flex-start', - display: 'inline-flex', - }, - }), } as const }) diff --git a/shared/common-adapters/modal2.tsx b/shared/common-adapters/modal2.tsx index b02547ea20e6..e6f6a35bcedc 100644 --- a/shared/common-adapters/modal2.tsx +++ b/shared/common-adapters/modal2.tsx @@ -176,15 +176,6 @@ const styles = Styles.styleSheetCreate(() => { borderTopColor: Styles.globalColors.black_10, borderTopWidth: 1, }, - footerFullscreen: Styles.platformStyles({ - isElectron: { - ...Styles.padding( - Styles.globalMargins.xsmall, - Styles.globalMargins.small, - Styles.globalMargins.xlarge - ), - }, - }), footerWide: Styles.platformStyles({ isElectron: { ...Styles.padding(Styles.globalMargins.xsmall, Styles.globalMargins.medium), @@ -211,26 +202,6 @@ const styles = Styles.styleSheetCreate(() => { ...headerCommon, minHeight: 64, }, - measured: { - alignItems: 'center', - flex: 1, - justifyContent: 'center', - }, - overflowVisible: {overflow: 'visible'}, - scroll: Styles.platformStyles({ - isElectron: {...Styles.globalStyles.flexBoxColumn, flex: 1, position: 'relative'}, - }), - scrollContentContainer: Styles.platformStyles({ - common: { - ...Styles.globalStyles.flexBoxColumn, - flexGrow: 1, - width: '100%', - }, - isTablet: { - alignSelf: 'center', - maxWidth: 600, - }, - }), } }) diff --git a/shared/common-adapters/popup-dialog.desktop.tsx b/shared/common-adapters/popup-dialog.desktop.tsx index a5f92b6a645f..2a6bdf200f53 100644 --- a/shared/common-adapters/popup-dialog.desktop.tsx +++ b/shared/common-adapters/popup-dialog.desktop.tsx @@ -69,15 +69,6 @@ function PopupDialog(p: Props) { const styles = Styles.styleSheetCreate(() => ({ get clipContainer() { return Styles.platformStyles({ - isElectron: { - ...Styles.desktopStyles.boxShadow, - ...Styles.globalStyles.flexBoxColumn, - backgroundColor: Styles.globalColors.white, - borderRadius: Styles.borderRadius, - flex: 1, - maxWidth: '100%', - position: 'relative', - }, }) }, close: Styles.platformStyles({ @@ -111,17 +102,6 @@ const styles = Styles.styleSheetCreate(() => ({ paddingRight: Styles.globalMargins.large, paddingTop: Styles.globalMargins.large, }, - coverTabBarShim: { - ...Styles.globalStyles.flexBoxRow, - ...Styles.globalStyles.fillAbsolute, - alignItems: 'center', - backgroundColor: Styles.globalColors.black_50OrBlack_60, - justifyContent: 'center', - paddingBottom: Styles.globalMargins.small, - paddingLeft: 0, - paddingRight: 0, - paddingTop: Styles.globalMargins.large, - }, })) export default PopupDialog diff --git a/shared/common-adapters/reload.tsx b/shared/common-adapters/reload.tsx index e93d4d91c339..e488e0a6b4c6 100644 --- a/shared/common-adapters/reload.tsx +++ b/shared/common-adapters/reload.tsx @@ -128,12 +128,6 @@ const styles = Styles.styleSheetCreate( maxWidth: '100%', padding: Styles.globalMargins.small, }, - scrollInside: { - height: '100%', - maxHeight: '100%', - maxWidth: '100%', - width: '100%', - }, }) as const ) diff --git a/shared/common-adapters/search-filter.tsx b/shared/common-adapters/search-filter.tsx index 0590aedf9182..522262ded330 100644 --- a/shared/common-adapters/search-filter.tsx +++ b/shared/common-adapters/search-filter.tsx @@ -384,7 +384,6 @@ const styles = Styles.styleSheetCreate(() => ({ paddingLeft: 0, paddingRight: 0, }, - inputNoGrow: {flexGrow: 0}, leftIconTiny: {marginRight: Styles.globalMargins.tiny}, leftIconXTiny: {marginRight: Styles.globalMargins.xtiny}, light: {backgroundColor: Styles.globalColors.black_05}, diff --git a/shared/common-adapters/video.desktop.tsx b/shared/common-adapters/video.desktop.tsx index 4c6d2654bc82..b7f4faff289d 100644 --- a/shared/common-adapters/video.desktop.tsx +++ b/shared/common-adapters/video.desktop.tsx @@ -52,14 +52,6 @@ const styles = Styles.styleSheetCreate(() => ({ overflow: 'hidden', width: '100%', }, - video: Styles.platformStyles({ - isElectron: { - maxHeight: '100%', - maxWidth: '100%', - objectFit: 'contain', - position: 'absolute', - }, - }), })) export default Video diff --git a/shared/crypto/input.tsx b/shared/crypto/input.tsx index 273288164961..392908539ed1 100644 --- a/shared/crypto/input.tsx +++ b/shared/crypto/input.tsx @@ -397,9 +397,6 @@ const styles = Kb.Styles.styleSheetCreate( alignSelf: 'flex-start', ...Kb.Styles.padding(Kb.Styles.globalMargins.small), }, - hidden: { - display: 'none', - }, input: Kb.Styles.platformStyles({ common: { color: Kb.Styles.globalColors.black, diff --git a/shared/crypto/output.tsx b/shared/crypto/output.tsx index 6510f136f84b..1555b82232c0 100644 --- a/shared/crypto/output.tsx +++ b/shared/crypto/output.tsx @@ -495,8 +495,6 @@ const styles = Kb.Styles.styleSheetCreate( }, }), outputPlaceholder: {backgroundColor: Kb.Styles.globalColors.blueGreyLight}, - outputVerifiedContainer: {marginBottom: Kb.Styles.globalMargins.xlarge}, - placeholder: {color: Kb.Styles.globalColors.black_50}, progressBar: { width: 200, }, @@ -533,7 +531,6 @@ const styles = Kb.Styles.styleSheetCreate( ...Kb.Styles.padding(Kb.Styles.globalMargins.tiny), }, }), - signedIcon: {color: Kb.Styles.globalColors.green}, signedSender: { ...Kb.Styles.globalStyles.flexGrow, }, diff --git a/shared/crypto/sub-nav/nav-row.tsx b/shared/crypto/sub-nav/nav-row.tsx index 9e2b14f2d13e..62bcb8b6c6ed 100644 --- a/shared/crypto/sub-nav/nav-row.tsx +++ b/shared/crypto/sub-nav/nav-row.tsx @@ -67,17 +67,7 @@ const NavRow = (props: Props) => { return Kb.Styles.isMobile ? mobileRow : desktopRow } -const rowHeight = 50 - const styles = Kb.Styles.styleSheetCreate(() => ({ - clickableContainer: Kb.Styles.platformStyles({ - isElectron: { - ...Kb.Styles.desktopStyles.clickable, - flexShrink: 0, - height: rowHeight, - width: '100%', - }, - }), textContainer: { justifyContent: 'center', marginLeft: Kb.Styles.globalMargins.tiny, diff --git a/shared/desktop/remote/component-loader.desktop.tsx b/shared/desktop/remote/component-loader.desktop.tsx index 520a3629bddd..5f358975f713 100644 --- a/shared/desktop/remote/component-loader.desktop.tsx +++ b/shared/desktop/remote/component-loader.desktop.tsx @@ -78,7 +78,6 @@ const styles = Kb.Styles.styleSheetCreate( }, }), errorFallback: {backgroundColor: Kb.Styles.globalColors.white}, - loading: {backgroundColor: Kb.Styles.globalColors.greyDark}, }) as const ) diff --git a/shared/devices/paper-key.tsx b/shared/devices/paper-key.tsx index 5a0290c84f1d..3c00af238a8b 100644 --- a/shared/devices/paper-key.tsx +++ b/shared/devices/paper-key.tsx @@ -86,7 +86,6 @@ const styles = Kb.Styles.styleSheetCreate( maxWidth: Kb.Styles.isMobile ? undefined : 560, padding: Kb.Styles.globalMargins.medium, }, - header: {position: 'absolute'}, intro: {textAlign: 'center'}, keyBox: { backgroundColor: Kb.Styles.globalColors.white, diff --git a/shared/fs/banner/system-file-manager-integration-banner/kext-permission-popup.tsx b/shared/fs/banner/system-file-manager-integration-banner/kext-permission-popup.tsx index c294c7b776ea..68bfe35eb805 100644 --- a/shared/fs/banner/system-file-manager-integration-banner/kext-permission-popup.tsx +++ b/shared/fs/banner/system-file-manager-integration-banner/kext-permission-popup.tsx @@ -90,14 +90,6 @@ const styles = Kb.Styles.styleSheetCreate( right: 0, top: 0, }, - highlight: { - backgroundColor: Kb.Styles.globalColors.black_05, - borderColor: Kb.Styles.globalColors.blue, - borderRadius: Kb.Styles.borderRadius, - borderStyle: 'solid', - borderWidth: 2, - position: 'absolute', - }, illustrationContainer: { position: 'relative', }, diff --git a/shared/fs/common/path-item-action/confirm.tsx b/shared/fs/common/path-item-action/confirm.tsx index c3fb1a363ea6..2044dcba1a90 100644 --- a/shared/fs/common/path-item-action/confirm.tsx +++ b/shared/fs/common/path-item-action/confirm.tsx @@ -86,16 +86,6 @@ const styles = Kb.Styles.styleSheetCreate( confirmTextBox: { padding: Kb.Styles.globalMargins.medium, }, - menuRowText: { - color: Kb.Styles.globalColors.blueDark, - }, - menuRowTextDisabled: { - color: Kb.Styles.globalColors.blueDark, - opacity: 0.6, - }, - progressIndicator: { - marginRight: Kb.Styles.globalMargins.xtiny, - }, }) as const ) diff --git a/shared/fs/filepreview/normal-preview.tsx b/shared/fs/filepreview/normal-preview.tsx index 68fb8711cb4c..378bea6c5042 100644 --- a/shared/fs/filepreview/normal-preview.tsx +++ b/shared/fs/filepreview/normal-preview.tsx @@ -24,18 +24,6 @@ export default NormalPreview const styles = Kb.Styles.styleSheetCreate( () => ({ - contentContainer: Kb.Styles.platformStyles({ - common: { - ...Kb.Styles.globalStyles.flexBoxColumn, - ...Kb.Styles.globalStyles.flexGrow, - height: '100%', - width: '100%', - }, - isElectron: { - paddingLeft: Kb.Styles.globalMargins.medium, - paddingRight: Kb.Styles.globalMargins.medium, - }, - }), greyContainer: { backgroundColor: Kb.Styles.globalColors.blueLighter3, flexGrow: 1, diff --git a/shared/fs/filepreview/text-view.native.tsx b/shared/fs/filepreview/text-view.native.tsx index 1e0fbf5c0f65..33a4768af75d 100644 --- a/shared/fs/filepreview/text-view.native.tsx +++ b/shared/fs/filepreview/text-view.native.tsx @@ -18,10 +18,6 @@ const TextView = (props: Props) => ( const styles = Kb.Styles.styleSheetCreate( () => ({ - progressContainer: { - justifyContent: 'center', - position: 'absolute', - }, webview: { backgroundColor: Kb.Styles.globalColors.white, height: '100%', diff --git a/shared/fs/nav-header/title.tsx b/shared/fs/nav-header/title.tsx index 1bcb3e156a77..b728339399b8 100644 --- a/shared/fs/nav-header/title.tsx +++ b/shared/fs/nav-header/title.tsx @@ -134,17 +134,11 @@ const styles = Kb.Styles.styleSheetCreate( }, isElectron: Kb.Styles.desktopStyles.windowDraggingClickable, }), - dropdown: { - marginLeft: -Kb.Styles.globalMargins.tiny, // the icon has padding, so offset it to align with the name below - }, floating: Kb.Styles.platformStyles({ isElectron: { width: 196, }, }), - icon: { - padding: Kb.Styles.globalMargins.tiny, - }, mainTitleText: Kb.Styles.platformStyles({isElectron: Kb.Styles.desktopStyles.windowDraggingClickable}), rootTitle: { alignSelf: 'center', diff --git a/shared/fs/simple-screens/oops.tsx b/shared/fs/simple-screens/oops.tsx index b753e866178f..2a395ab03417 100644 --- a/shared/fs/simple-screens/oops.tsx +++ b/shared/fs/simple-screens/oops.tsx @@ -129,11 +129,6 @@ const styles = Kb.Styles.styleSheetCreate( marginLeft: Kb.Styles.globalMargins.xtiny, marginRight: Kb.Styles.globalMargins.xtiny, }, - footer: {paddingBottom: Kb.Styles.globalMargins.large}, - header: { - backgroundColor: Kb.Styles.globalColors.red, - height: 40, - }, main: {...Kb.Styles.globalStyles.flexGrow}, textYouDontHave: Kb.Styles.platformStyles({ isElectron: {marginTop: Kb.Styles.globalMargins.medium}, diff --git a/shared/login/join-or-login.tsx b/shared/login/join-or-login.tsx index 0e4b0c267713..d2afd8b210ac 100644 --- a/shared/login/join-or-login.tsx +++ b/shared/login/join-or-login.tsx @@ -87,18 +87,6 @@ const Intro = () => { const styles = Kb.Styles.styleSheetCreate( () => ({ - banner: { - backgroundColor: Kb.Styles.globalColors.blue, - justifyContent: 'center', - minHeight: 40, - paddingBottom: Kb.Styles.globalMargins.tiny, - paddingLeft: Kb.Styles.isMobile ? Kb.Styles.globalMargins.small : Kb.Styles.globalMargins.xlarge, - paddingRight: Kb.Styles.isMobile ? Kb.Styles.globalMargins.small : Kb.Styles.globalMargins.xlarge, - paddingTop: Kb.Styles.globalMargins.tiny, - position: 'absolute', - top: 50, - }, - bannerMessage: {color: Kb.Styles.globalColors.white}, buttonBar: Kb.Styles.platformStyles({ isElectron: { paddingBottom: Kb.Styles.globalMargins.xlarge - Kb.Styles.globalMargins.tiny, // tiny added inside buttonbar diff --git a/shared/login/recover-password/prompt-reset-shared.tsx b/shared/login/recover-password/prompt-reset-shared.tsx index dff66e082417..f7786cdb3726 100644 --- a/shared/login/recover-password/prompt-reset-shared.tsx +++ b/shared/login/recover-password/prompt-reset-shared.tsx @@ -111,7 +111,6 @@ const styles = Kb.Styles.styleSheetCreate(() => ({ ...Kb.Styles.padding(0, Kb.Styles.globalMargins.medium, Kb.Styles.globalMargins.small), maxWidth: 500, }, - questionBox: Kb.Styles.padding(Kb.Styles.globalMargins.tiny, Kb.Styles.globalMargins.tiny, 0), topGap: Kb.Styles.platformStyles({ isMobile: { justifyContent: 'flex-start', diff --git a/shared/login/reset/password-enter.tsx b/shared/login/reset/password-enter.tsx index 010a4cdaf290..5b4ec07dec91 100644 --- a/shared/login/reset/password-enter.tsx +++ b/shared/login/reset/password-enter.tsx @@ -55,11 +55,5 @@ const styles = Kb.Styles.styleSheetCreate(() => ({ width: 368, }, }), - topGap: Kb.Styles.platformStyles({ - isMobile: { - justifyContent: 'flex-start', - marginTop: '20%', - }, - }), })) export default EnterPassword diff --git a/shared/login/reset/password-known.tsx b/shared/login/reset/password-known.tsx index f955e6b7d183..f89f25bc17da 100644 --- a/shared/login/reset/password-known.tsx +++ b/shared/login/reset/password-known.tsx @@ -54,11 +54,6 @@ const KnowPassword = () => { } const styles = Kb.Styles.styleSheetCreate(() => ({ - input: Kb.Styles.platformStyles({ - isElectron: { - width: 368, - }, - }), topGap: Kb.Styles.platformStyles({ isMobile: { justifyContent: 'flex-start', diff --git a/shared/login/signup/common.tsx b/shared/login/signup/common.tsx index 08a8d4917b06..e08faa9a7f7c 100644 --- a/shared/login/signup/common.tsx +++ b/shared/login/signup/common.tsx @@ -45,20 +45,7 @@ export const ContinueButton = ({ const styles = Kb.Styles.styleSheetCreate( () => ({ - avatar: {marginBottom: Kb.Styles.isMobile ? Kb.Styles.globalMargins.xtiny : 0}, buttonBar: {maxWidth: 460, padding: 0, paddingTop: Kb.Styles.globalMargins.medium}, - header: { - backgroundColor: Kb.Styles.globalColors.transparent, - borderBottomWidth: 0, - left: 0, - position: 'absolute', - right: 0, - top: 0, - }, - input: {maxWidth: 460, width: '100%'}, - inputContainer: {alignItems: 'center', alignSelf: 'stretch'}, - inputErrorStyle: {minHeight: 0}, - inputInnerStyle: {width: '100%'}, wrapper: {paddingLeft: Kb.Styles.globalMargins.medium, paddingRight: Kb.Styles.globalMargins.medium}, }) as const ) diff --git a/shared/menubar/chat-container.desktop.tsx b/shared/menubar/chat-container.desktop.tsx index 6bcff1bb9438..fd8eed2d3694 100644 --- a/shared/menubar/chat-container.desktop.tsx +++ b/shared/menubar/chat-container.desktop.tsx @@ -66,22 +66,6 @@ const styles = Kb.Styles.styleSheetCreate(() => ({ backgroundColor: Kb.Styles.globalColors.white, color: Kb.Styles.globalColors.black, }, - toggleButton: Kb.Styles.platformStyles({ - common: { - backgroundColor: Kb.Styles.globalColors.black_05, - borderRadius: Kb.Styles.borderRadius, - marginBottom: Kb.Styles.globalMargins.xtiny, - marginTop: Kb.Styles.globalMargins.xtiny, - paddingBottom: Kb.Styles.globalMargins.xtiny, - paddingTop: Kb.Styles.globalMargins.xtiny, - }, - isElectron: { - marginLeft: Kb.Styles.globalMargins.tiny, - marginRight: Kb.Styles.globalMargins.tiny, - paddingLeft: Kb.Styles.globalMargins.tiny, - paddingRight: Kb.Styles.globalMargins.tiny, - }, - }), })) export default ChatPreview diff --git a/shared/menubar/files.desktop.tsx b/shared/menubar/files.desktop.tsx index f297be40fa95..1182ceb25a41 100644 --- a/shared/menubar/files.desktop.tsx +++ b/shared/menubar/files.desktop.tsx @@ -214,24 +214,5 @@ const styles = Kb.Styles.styleSheetCreate( tlfTopLine: { justifyContent: 'space-between', }, - toggleButton: Kb.Styles.platformStyles({ - common: { - backgroundColor: Kb.Styles.globalColors.black_05, - borderRadius: Kb.Styles.borderRadius, - marginTop: Kb.Styles.globalMargins.xtiny, - paddingBottom: Kb.Styles.globalMargins.xtiny, - paddingTop: Kb.Styles.globalMargins.xtiny, - }, - isElectron: { - marginRight: Kb.Styles.globalMargins.tiny, - paddingLeft: Kb.Styles.globalMargins.tiny, - paddingRight: Kb.Styles.globalMargins.tiny, - }, - }), - wordWrapFilename: Kb.Styles.platformStyles({ - isElectron: { - wordBreak: 'break-all', - }, - }), }) as const ) diff --git a/shared/menubar/index.desktop.tsx b/shared/menubar/index.desktop.tsx index 314d47f7a184..e2961e3a28b4 100644 --- a/shared/menubar/index.desktop.tsx +++ b/shared/menubar/index.desktop.tsx @@ -431,9 +431,6 @@ const styles = Kb.Styles.styleSheetCreate(() => ({ }, flexOne: {flexGrow: 1}, footer: {width: 360}, - hamburgerIcon: { - marginRight: Kb.Styles.globalMargins.tiny, - }, headerBadgesContainer: { ...Kb.Styles.globalStyles.flexBoxRow, alignItems: 'center', diff --git a/shared/people/index.desktop.tsx b/shared/people/index.desktop.tsx index db0abafcc73d..3dc49e729f4a 100644 --- a/shared/people/index.desktop.tsx +++ b/shared/people/index.desktop.tsx @@ -17,7 +17,6 @@ const styles = Kb.Styles.styleSheetCreate(() => ({ container: { ...Kb.Styles.globalStyles.fullHeight, }, - header: {flexGrow: 1}, progress: { height: 24, left: 40, @@ -25,8 +24,6 @@ const styles = Kb.Styles.styleSheetCreate(() => ({ top: -72, width: 24, }, - searchContainer: {paddingBottom: Kb.Styles.globalMargins.xsmall}, - sectionTitle: {flexGrow: 1}, })) export default People diff --git a/shared/people/todo.tsx b/shared/people/todo.tsx index 016bc55b3d41..a4f016c66daf 100644 --- a/shared/people/todo.tsx +++ b/shared/people/todo.tsx @@ -402,12 +402,6 @@ const Task = (props: Props) => ( const styles = Kb.Styles.styleSheetCreate(() => ({ instructions: {marginTop: 2}, - search: { - alignSelf: undefined, - flexGrow: 0, - marginBottom: Kb.Styles.globalMargins.xsmall, - marginTop: Kb.Styles.globalMargins.xsmall, - }, })) export default TaskChooser diff --git a/shared/profile/add-to-team.tsx b/shared/profile/add-to-team.tsx index a4581d07b18b..ed8b9a86a394 100644 --- a/shared/profile/add-to-team.tsx +++ b/shared/profile/add-to-team.tsx @@ -377,11 +377,6 @@ const styles = Kb.Styles.styleSheetCreate( paddingRight: Kb.Styles.globalMargins.tiny, }, }), - wrapper: Kb.Styles.platformStyles({ - common: {}, - isElectron: {maxHeight: '80%'}, - isMobile: {flexGrow: 1}, - }), }) as const ) diff --git a/shared/profile/edit-avatar/index.native.tsx b/shared/profile/edit-avatar/index.native.tsx index 2e889d405d85..30ef727a7bc2 100644 --- a/shared/profile/edit-avatar/index.native.tsx +++ b/shared/profile/edit-avatar/index.native.tsx @@ -272,10 +272,6 @@ const styles = Kb.Styles.styleSheetCreate( flexReallyGrow: { flexGrow: 1000, }, - image: { - overflow: 'hidden', - position: 'relative', - }, placeholder: { alignItems: 'center', backgroundColor: Kb.Styles.globalColors.black_05, @@ -285,20 +281,10 @@ const styles = Kb.Styles.styleSheetCreate( display: 'flex', justifyContent: 'center', }, - standardScreen: {...Kb.Styles.padding(0), flexGrow: 1}, wizardContainer: { ...Kb.Styles.padding(64, Kb.Styles.globalMargins.large), backgroundColor: Kb.Styles.globalColors.blueGrey, }, - zoomContainer: Kb.Styles.platformStyles({ - common: { - backgroundColor: Kb.Styles.globalColors.grey, - marginBottom: Kb.Styles.globalMargins.tiny, - overflow: 'hidden', - position: 'relative', - }, - isTablet: {alignSelf: 'center'}, - }), }) as const ) diff --git a/shared/profile/edit-profile.tsx b/shared/profile/edit-profile.tsx index c153a4866cf4..70cdfb9fff1c 100644 --- a/shared/profile/edit-profile.tsx +++ b/shared/profile/edit-profile.tsx @@ -92,7 +92,6 @@ const Container = () => { const maxBio = 255 const styles = Kb.Styles.styleSheetCreate(() => ({ - bio: {maxHeight: undefined}, container: Kb.Styles.platformStyles({ common: {padding: Kb.Styles.globalMargins.small}, isElectron: { diff --git a/shared/profile/generic/proofs-list.tsx b/shared/profile/generic/proofs-list.tsx index ffbfa6fe9f16..92608611f7d4 100644 --- a/shared/profile/generic/proofs-list.tsx +++ b/shared/profile/generic/proofs-list.tsx @@ -196,20 +196,6 @@ const styles = Kb.Styles.styleSheetCreate( justifyContent: 'flex-start', }, description: {...rightColumnStyle}, - flexOne: {flex: 1}, - footer: { - alignItems: 'center', - backgroundColor: Kb.Styles.globalColors.blueGrey, - display: 'flex', - flexDirection: 'row', - justifyContent: 'center', - padding: Kb.Styles.globalMargins.xsmall, - }, - footerText: { - ...rightColumnStyle, - color: Kb.Styles.globalColors.black_50, - marginLeft: Kb.Styles.globalMargins.tiny, - }, header: { color: Kb.Styles.globalColors.black, marginTop: Kb.Styles.globalMargins.tiny, diff --git a/shared/profile/generic/result.tsx b/shared/profile/generic/result.tsx index 765700b8b568..2fd44b0d2535 100644 --- a/shared/profile/generic/result.tsx +++ b/shared/profile/generic/result.tsx @@ -76,10 +76,6 @@ const styles = Kb.Styles.styleSheetCreate( position: 'absolute', right: -5, }, - serviceIcon: { - height: 64, - width: 64, - }, serviceIconContainer: Kb.Styles.platformStyles({ common: { marginBottom: Kb.Styles.globalMargins.tiny, diff --git a/shared/profile/user/actions/index.tsx b/shared/profile/user/actions/index.tsx index 2dcf058334ef..27f11190cd70 100644 --- a/shared/profile/user/actions/index.tsx +++ b/shared/profile/user/actions/index.tsx @@ -240,7 +240,6 @@ const DropdownButton = (p: DropdownProps) => { } const styles = Kb.Styles.styleSheetCreate(() => ({ - chatIcon: {marginRight: Kb.Styles.globalMargins.tiny}, dropdownButton: {minWidth: undefined}, })) diff --git a/shared/profile/user/index.tsx b/shared/profile/user/index.tsx index 22a81fcd424a..438a484745cc 100644 --- a/shared/profile/user/index.tsx +++ b/shared/profile/user/index.tsx @@ -543,8 +543,6 @@ const styles = Kb.Styles.styleSheetCreate(() => ({ height: '100%', width: '100%', }, - noGrow: {flexGrow: 0}, - profileSearch: {marginTop: Kb.Styles.globalMargins.xtiny}, progress: {position: 'absolute'}, proofs: Kb.Styles.platformStyles({ isElectron: { @@ -566,20 +564,6 @@ const styles = Kb.Styles.styleSheetCreate(() => ({ isMobile: {padding: Kb.Styles.globalMargins.tiny}, }), reloadable: {paddingTop: 60}, - search: Kb.Styles.platformStyles({ - common: { - backgroundColor: Kb.Styles.globalColors.black_10, - borderRadius: Kb.Styles.borderRadius, - }, - isElectron: { - minHeight: 24, - minWidth: 240, - }, - isMobile: { - minHeight: 32, - minWidth: 200, - }, - }), sectionList: Kb.Styles.platformStyles({ common: {width: '100%'}, isElectron: { diff --git a/shared/provision/paper-key.tsx b/shared/provision/paper-key.tsx index 0ae84be9dc0d..79355119119a 100644 --- a/shared/provision/paper-key.tsx +++ b/shared/provision/paper-key.tsx @@ -85,16 +85,6 @@ export const PaperKey = (props: Props) => { const styles = Kb.Styles.styleSheetCreate( () => ({ - backButton: Kb.Styles.platformStyles({ - isElectron: { - marginLeft: Kb.Styles.globalMargins.medium, - marginTop: Kb.Styles.globalMargins.medium, - }, - isMobile: { - marginLeft: 0, - marginTop: 0, - }, - }), contents: Kb.Styles.platformStyles({ common: { flexGrow: 1, diff --git a/shared/provision/set-public-name.tsx b/shared/provision/set-public-name.tsx index 3b5d7ba52058..231b4f6ef457 100644 --- a/shared/provision/set-public-name.tsx +++ b/shared/provision/set-public-name.tsx @@ -102,16 +102,6 @@ const SetPublicName = () => { } const styles = Kb.Styles.styleSheetCreate(() => ({ - backButton: Kb.Styles.platformStyles({ - isElectron: { - marginLeft: Kb.Styles.globalMargins.medium, - marginTop: Kb.Styles.globalMargins.medium, - }, - isMobile: { - marginLeft: 0, - marginTop: 0, - }, - }), contents: Kb.Styles.platformStyles({ common: {width: '100%'}, isTablet: {width: undefined}, diff --git a/shared/router-v2/account-switcher/index.tsx b/shared/router-v2/account-switcher/index.tsx index f4a67159ff4d..16585f893c47 100644 --- a/shared/router-v2/account-switcher/index.tsx +++ b/shared/router-v2/account-switcher/index.tsx @@ -213,10 +213,6 @@ const styles = Kb.Styles.styleSheetCreate(() => ({ isElectron: {wordBreak: 'break-all'}, }), progressIndicator: {bottom: 0, position: 'absolute', right: 0}, - row: { - paddingBottom: -Kb.Styles.globalMargins.small, - paddingTop: -Kb.Styles.globalMargins.small, - }, text2: {flexShrink: 0}, userBox: { paddingLeft: Kb.Styles.globalMargins.small, diff --git a/shared/router-v2/header/index.desktop.tsx b/shared/router-v2/header/index.desktop.tsx index 892c4fbde2a4..5107b454d820 100644 --- a/shared/router-v2/header/index.desktop.tsx +++ b/shared/router-v2/header/index.desktop.tsx @@ -250,9 +250,7 @@ const styles = Kb.Styles.styleSheetCreate( }, }), bottom: {height: 40 - 1, maxHeight: 40 - 1}, // for border - bottomExpandable: {minHeight: 40 - 1}, bottomTitle: {flexGrow: 1, height: '100%', maxHeight: '100%', overflow: 'hidden'}, - flexOne: {flex: 1}, headerBack: Kb.Styles.platformStyles({ isElectron: { alignItems: 'center', diff --git a/shared/router-v2/tab-bar.desktop.tsx b/shared/router-v2/tab-bar.desktop.tsx index 67baf715faf2..08876ddda968 100644 --- a/shared/router-v2/tab-bar.desktop.tsx +++ b/shared/router-v2/tab-bar.desktop.tsx @@ -348,11 +348,6 @@ const styles = Kb.Styles.styleSheetCreate( () => ({ avatar: {marginLeft: 14}, - badgeIcon: { - bottom: -4, - position: 'absolute', - right: 8, - }, badgeIconUpload: { bottom: -Kb.Styles.globalMargins.xxtiny, height: Kb.Styles.globalMargins.xsmall, diff --git a/shared/settings/advanced.tsx b/shared/settings/advanced.tsx index 7f926fdc196a..1f7414a76e96 100644 --- a/shared/settings/advanced.tsx +++ b/shared/settings/advanced.tsx @@ -317,13 +317,6 @@ const Developer = () => { const styles = Kb.Styles.styleSheetCreate( () => ({ - checkboxContainer: { - ...Kb.Styles.globalStyles.flexBoxRow, - alignItems: 'center', - paddingBottom: Kb.Styles.globalMargins.tiny, - paddingTop: Kb.Styles.globalMargins.tiny, - width: '100%', - }, developerButtons: { marginTop: Kb.Styles.globalMargins.small, }, @@ -344,12 +337,6 @@ const styles = Kb.Styles.styleSheetCreate( filler: { flex: 1, }, - progressContainer: { - ...Kb.Styles.globalStyles.flexBoxRow, - alignItems: 'center', - justifyContent: 'center', - minHeight: 32, - }, proxyDivider: { marginBottom: Kb.Styles.globalMargins.small, marginTop: Kb.Styles.globalMargins.small, diff --git a/shared/settings/archive/index.tsx b/shared/settings/archive/index.tsx index ab07df9512c9..36eebb035e7e 100644 --- a/shared/settings/archive/index.tsx +++ b/shared/settings/archive/index.tsx @@ -424,7 +424,6 @@ const styles = Kb.Styles.styleSheetCreate(() => ({ container: {padding: Kb.Styles.isMobile ? 8 : 16}, errorTip: {justifyContent: 'center'}, jobLeft: {flexGrow: 1, flexShrink: 1}, - jobSub: {height: 22}, jobs: { flexGrow: 1, flexShrink: 1, diff --git a/shared/settings/feedback/index.tsx b/shared/settings/feedback/index.tsx index ca87475f0b49..49b32fa99f91 100644 --- a/shared/settings/feedback/index.tsx +++ b/shared/settings/feedback/index.tsx @@ -143,9 +143,6 @@ export default Feedback const styles = Kb.Styles.styleSheetCreate( () => ({ - container: Kb.Styles.platformStyles({ - common: {flex: 1}, - }), includeLogs: { ...Kb.Styles.globalStyles.fullWidth, }, @@ -166,7 +163,5 @@ const styles = Kb.Styles.styleSheetCreate( width: Kb.Styles.globalStyles.largeWidthPercent, }, }), - outerStyle: {backgroundColor: Kb.Styles.globalColors.white}, - smallLabel: {color: Kb.Styles.globalColors.black}, }) as const ) diff --git a/shared/settings/notifications/render.tsx b/shared/settings/notifications/render.tsx index fd67e846c9e0..cecb718a4115 100644 --- a/shared/settings/notifications/render.tsx +++ b/shared/settings/notifications/render.tsx @@ -67,7 +67,6 @@ const Notifications = () => { const styles = Kb.Styles.styleSheetCreate( () => ({ - checkbox: {marginRight: 0, marginTop: Kb.Styles.globalMargins.xtiny}, divider: { marginBottom: Kb.Styles.globalMargins.small, marginLeft: -Kb.Styles.globalMargins.small, diff --git a/shared/settings/password.tsx b/shared/settings/password.tsx index f9c157c2641b..4088ea228c93 100644 --- a/shared/settings/password.tsx +++ b/shared/settings/password.tsx @@ -195,10 +195,6 @@ const styles = Kb.Styles.styleSheetCreate( flexGrow: 1, padding: Kb.Styles.globalMargins.small, }, - headerText: { - paddingBottom: Kb.Styles.globalMargins.small, - paddingTop: Kb.Styles.globalMargins.small, - }, passwordBackground: Kb.Styles.platformStyles({ isTablet: { backgroundColor: Kb.Styles.globalColors.blueGrey, diff --git a/shared/settings/proxy.tsx b/shared/settings/proxy.tsx index 3239af09aba9..28ee5dba5c54 100644 --- a/shared/settings/proxy.tsx +++ b/shared/settings/proxy.tsx @@ -191,26 +191,10 @@ const ProxySettingsPopup = (props: Props) => { } const styles = Kb.Styles.styleSheetCreate(() => ({ - divider: { - marginTop: Kb.Styles.globalMargins.xsmall, - width: '100%', - }, - flexButtons: { - display: 'flex', - flexShrink: 0, - flexWrap: 'wrap', - marginTop: Kb.Styles.globalMargins.tiny, - }, popupBox: { minHeight: '40%', padding: Kb.Styles.globalMargins.small, }, - proxyContainer: { - ...Kb.Styles.globalStyles.flexBoxColumn, - alignItems: 'flex-start', - paddingBottom: Kb.Styles.globalMargins.medium, - paddingTop: Kb.Styles.globalMargins.medium, - }, proxySetting: {marginBottom: Kb.Styles.globalMargins.small}, proxySettingPopupBox: {padding: Kb.Styles.globalMargins.xlarge}, radioButton: {marginRight: Kb.Styles.globalMargins.medium}, diff --git a/shared/team-building/contact-restricted.tsx b/shared/team-building/contact-restricted.tsx index c17e0994981f..38697f5bf7bf 100644 --- a/shared/team-building/contact-restricted.tsx +++ b/shared/team-building/contact-restricted.tsx @@ -113,10 +113,6 @@ const styles = Kb.Styles.styleSheetCreate(() => ({ flex: 1, }, }), - icon: { - marginBottom: Kb.Styles.globalMargins.medium, - marginTop: Kb.Styles.globalMargins.xlarge, - }, text: { margin: Kb.Styles.globalMargins.small, }, diff --git a/shared/team-building/continue-button.tsx b/shared/team-building/continue-button.tsx index dfeeca9a7ed8..afdcffb300f1 100644 --- a/shared/team-building/continue-button.tsx +++ b/shared/team-building/continue-button.tsx @@ -19,9 +19,6 @@ const styles = Kb.Styles.styleSheetCreate(() => ({ continueText: { color: Kb.Styles.globalColors.white, }, - rabbitEmoji: { - marginLeft: Kb.Styles.globalMargins.xtiny, - }, })) export default ContinueButton diff --git a/shared/team-building/email-search.tsx b/shared/team-building/email-search.tsx index 2f5f8f17930d..636d935be9aa 100644 --- a/shared/team-building/email-search.tsx +++ b/shared/team-building/email-search.tsx @@ -112,9 +112,6 @@ const styles = Kb.Styles.styleSheetCreate( zIndex: -1, }, }), - bottomContainer: { - flexGrow: 1, - }, emptyContainer: Kb.Styles.platformStyles({ common: {flex: 1}, isElectron: { @@ -144,10 +141,6 @@ const styles = Kb.Styles.styleSheetCreate( height: 48, }, }), - userMatchMention: { - alignSelf: 'flex-start', - marginLeft: Kb.Styles.globalMargins.small, - }, }) as const ) diff --git a/shared/team-building/go-button.tsx b/shared/team-building/go-button.tsx index 46e850067f8c..b2d827d75ceb 100644 --- a/shared/team-building/go-button.tsx +++ b/shared/team-building/go-button.tsx @@ -43,15 +43,6 @@ const styles = Kb.Styles.styleSheetCreate(() => ({ marginBottom: Kb.Styles.globalMargins.tiny, marginTop: Kb.Styles.globalMargins.tiny, }, - go: Kb.Styles.platformStyles({ - common: {color: Kb.Styles.globalColors.white}, - isElectron: {lineHeight: 40}, - }), - goIcon: Kb.Styles.platformStyles({ - isElectron: { - lineHeight: 40, - }, - }), goTooltipIcon: Kb.Styles.platformStyles({ isElectron: { marginRight: Kb.Styles.globalMargins.xtiny, @@ -63,12 +54,6 @@ const styles = Kb.Styles.styleSheetCreate(() => ({ ...Kb.Styles.globalStyles.fullHeight, }, }), - hoverContainerStyle: Kb.Styles.platformStyles({ - isElectron: { - justifyContent: 'center', - width: '100%', - }, - }), })) export default GoButton diff --git a/shared/team-building/index.tsx b/shared/team-building/index.tsx index 7b94ab175862..634d5d246ea4 100644 --- a/shared/team-building/index.tsx +++ b/shared/team-building/index.tsx @@ -300,30 +300,6 @@ const TeamBuilding = (p: OwnProps) => { const styles = Kb.Styles.styleSheetCreate( () => ({ - container: Kb.Styles.platformStyles({ - common: {position: 'relative'}, - }), - headerContainer: Kb.Styles.platformStyles({ - isElectron: { - marginBottom: Kb.Styles.globalMargins.xtiny, - marginTop: Kb.Styles.globalMargins.small + 2, - }, - }), - mobileFlex: Kb.Styles.platformStyles({ - isMobile: {flex: 1}, - }), - newChatHeader: Kb.Styles.platformStyles({ - isElectron: {margin: Kb.Styles.globalMargins.xsmall}, - }), - peoplePopupStyleClose: Kb.Styles.platformStyles({isElectron: {display: 'none'}}), - shrinkingGap: {flexShrink: 1, height: Kb.Styles.globalMargins.xtiny}, - teamAvatar: Kb.Styles.platformStyles({ - isElectron: { - alignSelf: 'center', - position: 'absolute', - top: -16, - }, - }), waiting: { ...Kb.Styles.globalStyles.fillAbsolute, backgroundColor: Kb.Styles.globalColors.black_20, diff --git a/shared/team-building/phone-search.tsx b/shared/team-building/phone-search.tsx index 7c3a04a330bd..dcad2d6fa6b9 100644 --- a/shared/team-building/phone-search.tsx +++ b/shared/team-building/phone-search.tsx @@ -124,7 +124,6 @@ export const UserMatchMention = ({username}: UserMatchMentionProps) => ( const styles = Kb.Styles.styleSheetCreate( () => ({ - button: {flexGrow: 0}, containerStyle: Kb.Styles.platformStyles({ common: { backgroundColor: Kb.Styles.globalColors.blueGrey, diff --git a/shared/team-building/search-result/common-result.tsx b/shared/team-building/search-result/common-result.tsx index 4d4c2ce0df09..5c0214b1a92c 100644 --- a/shared/team-building/search-result/common-result.tsx +++ b/shared/team-building/search-result/common-result.tsx @@ -343,18 +343,12 @@ const styles = Kb.Styles.styleSheetCreate(() => ({ alignItems: 'baseline', display: 'flex', }, - contactName: { - lineHeight: 22, - }, highlighted: Kb.Styles.platformStyles({ isElectron: { backgroundColor: Kb.Styles.globalColors.blueLighter2, borderRadius: Kb.Styles.borderRadius, }, }), - keybaseServiceIcon: { - marginRight: Kb.Styles.globalMargins.xtiny, - }, // Default padding to people search vlaues: // top/bottom: 8, left/right: 12 // diff --git a/shared/team-building/search-result/people-result.tsx b/shared/team-building/search-result/people-result.tsx index 7925b8767ac3..22fd97cda5c3 100644 --- a/shared/team-building/search-result/people-result.tsx +++ b/shared/team-building/search-result/people-result.tsx @@ -166,12 +166,6 @@ const DropdownButton = (p: DropdownProps) => { const styles = Kb.Styles.styleSheetCreate(() => ({ chatIcon: {marginRight: Kb.Styles.globalMargins.tiny}, dropdownButton: {minWidth: undefined}, - highlighted: Kb.Styles.platformStyles({ - isElectron: { - backgroundColor: Kb.Styles.globalColors.blueLighter2, - borderRadius: Kb.Styles.borderRadius, - }, - }), rowContainer: { ...Kb.Styles.padding(Kb.Styles.globalMargins.tiny, Kb.Styles.globalMargins.xsmall), }, diff --git a/shared/team-building/search-result/you-result.tsx b/shared/team-building/search-result/you-result.tsx index 0c482c089fc9..275e48425a90 100644 --- a/shared/team-building/search-result/you-result.tsx +++ b/shared/team-building/search-result/you-result.tsx @@ -37,20 +37,6 @@ const YouResult = React.memo(function YouResult(props: ResultProps) { }) const styles = Kb.Styles.styleSheetCreate(() => ({ - actionButton: Kb.Styles.platformStyles({ - common: { - marginLeft: Kb.Styles.globalMargins.tiny, - }, - isElectron: { - height: Kb.Styles.globalMargins.small, - width: Kb.Styles.globalMargins.small, - }, - isMobile: { - height: Kb.Styles.globalMargins.large, - marginRight: Kb.Styles.globalMargins.tiny, - width: Kb.Styles.globalMargins.large, - }, - }), rowContainer: { ...Kb.Styles.padding( Kb.Styles.globalMargins.tiny, diff --git a/shared/team-building/service-tab-bar.desktop.tsx b/shared/team-building/service-tab-bar.desktop.tsx index 2d6ba60680d6..7675fd6691c2 100644 --- a/shared/team-building/service-tab-bar.desktop.tsx +++ b/shared/team-building/service-tab-bar.desktop.tsx @@ -228,7 +228,6 @@ const styles = Kb.Styles.styleSheetCreate( width: '100%', }, moreText: {color: Kb.Styles.globalColors.black_50}, - pendingAnimation: {height: 10, width: 10}, serviceIconBox: {marginTop: 14}, serviceIconContainer: { flex: 1, diff --git a/shared/team-building/service-tab-bar.native.tsx b/shared/team-building/service-tab-bar.native.tsx index 77b9aa4e55a0..d53777be4f41 100644 --- a/shared/team-building/service-tab-bar.native.tsx +++ b/shared/team-building/service-tab-bar.native.tsx @@ -249,7 +249,6 @@ const styles = Kb.Styles.styleSheetCreate( marginTop: Kb.Styles.globalMargins.xtiny, overflow: 'hidden', }, - pendingAnimation: {height: 17, width: 17}, scroll: { flexGrow: 0, flexShrink: 0, @@ -261,11 +260,6 @@ const styles = Kb.Styles.styleSheetCreate( paddingTop: Kb.Styles.globalMargins.tiny, position: 'relative', }, - tabBarContainer: { - backgroundColor: Kb.Styles.globalColors.white, - shadowOffset: {height: 3, width: 0}, - shadowRadius: 2, - }, }) as const ) diff --git a/shared/team-building/team-box.tsx b/shared/team-building/team-box.tsx index e94c7a1a9cd8..29838e27bf10 100644 --- a/shared/team-building/team-box.tsx +++ b/shared/team-building/team-box.tsx @@ -154,32 +154,6 @@ const styles = Kb.Styles.styleSheetCreate( paddingTop: Kb.Styles.globalMargins.tiny, }, }), - search: Kb.Styles.platformStyles({ - common: { - flex: 1, - flexWrap: 'wrap', - }, - isElectron: { - ...Kb.Styles.globalStyles.rounded, - backgroundColor: Kb.Styles.globalColors.white, - borderColor: Kb.Styles.globalColors.black_20, - borderStyle: 'solid', - borderWidth: 1, - maxHeight: 170, - minHeight: 40, - overflowY: 'scroll', - }, - isMobile: { - borderBottomColor: Kb.Styles.globalColors.black_10, - borderBottomWidth: 1, - borderStyle: 'solid', - minHeight: 48, - }, - }), - searchIcon: { - alignSelf: 'center', - marginLeft: 10, - }, }) as const ) diff --git a/shared/team-building/user-bubble.tsx b/shared/team-building/user-bubble.tsx index a433539a43e6..8da84290b2b9 100644 --- a/shared/team-building/user-bubble.tsx +++ b/shared/team-building/user-bubble.tsx @@ -74,25 +74,7 @@ const styles = Kb.Styles.styleSheetCreate( }, }), bubbleContainer: Kb.Styles.platformStyles({common: {position: 'relative'}, isMobile: {width: 91}}), - container: Kb.Styles.platformStyles({ - common: { - marginBottom: Kb.Styles.globalMargins.xtiny, - marginLeft: Kb.Styles.globalMargins.tiny, - marginTop: Kb.Styles.globalMargins.xtiny, - }, - }), - generalService: Kb.Styles.platformStyles({ - isElectron: { - lineHeight: '35px', - }, - }), // TODO: the service icons are too high without this - are they right? - iconBox: Kb.Styles.platformStyles({ - isElectron: { - marginBottom: -3, - marginTop: 3, - }, - }), remove: Kb.Styles.platformStyles({ common: { alignItems: 'center', diff --git a/shared/teams/add-members-wizard/add-email.tsx b/shared/teams/add-members-wizard/add-email.tsx index c19e84f68547..3e97be7b9ee6 100644 --- a/shared/teams/add-members-wizard/add-email.tsx +++ b/shared/teams/add-members-wizard/add-email.tsx @@ -116,15 +116,7 @@ const styles = Kb.Styles.styleSheetCreate(() => ({ }, isMobile: {...Kb.Styles.globalStyles.flexOne}, }), - container: { - padding: Kb.Styles.globalMargins.small, - }, errorText: {color: Kb.Styles.globalColors.redDark}, - wordBreak: Kb.Styles.platformStyles({ - isElectron: { - wordBreak: 'break-all', - }, - }), })) export default AddEmail diff --git a/shared/teams/add-members-wizard/add-phone.tsx b/shared/teams/add-members-wizard/add-phone.tsx index 5ed2a3a15ae2..2e8b7e2dfd3c 100644 --- a/shared/teams/add-members-wizard/add-phone.tsx +++ b/shared/teams/add-members-wizard/add-phone.tsx @@ -111,14 +111,6 @@ const styles = Kb.Styles.styleSheetCreate(() => ({ }, isMobile: {...Kb.Styles.globalStyles.flexOne}, }), - container: { - padding: Kb.Styles.globalMargins.small, - }, - wordBreak: Kb.Styles.platformStyles({ - isElectron: { - wordBreak: 'break-all', - }, - }), })) export default AddPhone diff --git a/shared/teams/channel/index.tsx b/shared/teams/channel/index.tsx index 0af5d5e9274d..d2168f70d831 100644 --- a/shared/teams/channel/index.tsx +++ b/shared/teams/channel/index.tsx @@ -273,12 +273,6 @@ const Channel = (props: OwnProps) => { const styles = Kb.Styles.styleSheetCreate( () => ({ - backButton: { - bottom: 0, - left: 0, - position: 'absolute', - top: 0, - }, container: { ...Kb.Styles.globalStyles.flexBoxColumn, alignItems: 'stretch', @@ -287,11 +281,6 @@ const styles = Kb.Styles.styleSheetCreate( position: 'relative', width: '100%', }, - endAnchor: { - flex: 1, - height: 0, - }, - header: {height: 40, left: 0, position: 'absolute', right: 0, top: 0}, list: {}, listContentContainer: Kb.Styles.platformStyles({ isElectron: { @@ -304,9 +293,6 @@ const styles = Kb.Styles.styleSheetCreate( flexGrow: 1, }, }), - smallHeader: { - ...Kb.Styles.padding(0, Kb.Styles.globalMargins.xlarge), - }, }) as const ) diff --git a/shared/teams/confirm-modals/really-leave-team/index.tsx b/shared/teams/confirm-modals/really-leave-team/index.tsx index 9d4340be3b68..1a1a68499dac 100644 --- a/shared/teams/confirm-modals/really-leave-team/index.tsx +++ b/shared/teams/confirm-modals/really-leave-team/index.tsx @@ -107,14 +107,6 @@ const styles = Kb.Styles.styleSheetCreate( zIndex: 1, }, prompt: Kb.Styles.padding(0, Kb.Styles.globalMargins.small), - spinnerContainer: { - alignItems: 'center', - flex: 1, - padding: Kb.Styles.globalMargins.xlarge, - }, - spinnerProgressIndicator: { - width: Kb.Styles.globalMargins.medium, - }, }) as const ) diff --git a/shared/teams/edit-team-description.tsx b/shared/teams/edit-team-description.tsx index 71e0934031c3..6f98efb5ae57 100644 --- a/shared/teams/edit-team-description.tsx +++ b/shared/teams/edit-team-description.tsx @@ -88,11 +88,6 @@ const styles = Kb.Styles.styleSheetCreate(() => ({ ...Kb.Styles.padding(Kb.Styles.globalMargins.small), width: '100%', }, - headerIcon: Kb.Styles.padding(Kb.Styles.globalMargins.tiny, 0, 0), - title: { - paddingBottom: Kb.Styles.globalMargins.medium, - paddingTop: Kb.Styles.globalMargins.xtiny, - }, })) export default EditTeamDescription diff --git a/shared/teams/main/banner.tsx b/shared/teams/main/banner.tsx index 42acc7e9014e..8825b1dbe05c 100644 --- a/shared/teams/main/banner.tsx +++ b/shared/teams/main/banner.tsx @@ -34,9 +34,6 @@ const Banner = ({onReadMore, onHideChatBanner}: Props) => ( ) const styles = Kb.Styles.styleSheetCreate(() => ({ - closeIcon: { - padding: Kb.Styles.globalMargins.xtiny, - }, closeIconContainer: Kb.Styles.platformStyles({ common: { position: 'absolute', diff --git a/shared/teams/main/index.tsx b/shared/teams/main/index.tsx index 7ef9fd136b08..a98031f54722 100644 --- a/shared/teams/main/index.tsx +++ b/shared/teams/main/index.tsx @@ -182,12 +182,6 @@ const Teams = React.memo(function Teams(p: Props) { const styles = Kb.Styles.styleSheetCreate( () => ({ - avatarContainer: {position: 'relative'}, - badge: { - position: 'absolute', - right: -5, - top: -5, - }, bigButton: Kb.Styles.platformStyles({ common: { borderColor: Kb.Styles.globalColors.black_10, @@ -204,9 +198,6 @@ const styles = Kb.Styles.styleSheetCreate( }), container: {backgroundColor: Kb.Styles.globalColors.blueGrey}, emptyNote: Kb.Styles.padding(60, 42, Kb.Styles.globalMargins.medium, Kb.Styles.globalMargins.medium), - kerning: {letterSpacing: 0.2}, - maxWidth: {maxWidth: '100%'}, - openMeta: {alignSelf: 'center'}, relative: {position: 'relative'}, sortHeader: Kb.Styles.platformStyles({ common: {backgroundColor: Kb.Styles.globalColors.blueGrey}, diff --git a/shared/teams/main/team-row.tsx b/shared/teams/main/team-row.tsx index 524f5e399dca..934eb6063aea 100644 --- a/shared/teams/main/team-row.tsx +++ b/shared/teams/main/team-row.tsx @@ -198,9 +198,6 @@ const styles = Kb.Styles.styleSheetCreate(() => ({ isMobile: {bottom: 4, right: -5}, }), darkerAdminIcon: {color: Kb.Styles.globalColors.greyDark}, - openMeta: { - alignSelf: 'center', - }, white: {backgroundColor: Kb.Styles.globalColors.white}, })) diff --git a/shared/teams/new-team/wizard/new-team-info.tsx b/shared/teams/new-team/wizard/new-team-info.tsx index 0bfb4183f529..73a9304b0568 100644 --- a/shared/teams/new-team/wizard/new-team-info.tsx +++ b/shared/teams/new-team/wizard/new-team-info.tsx @@ -252,9 +252,6 @@ const styles = Kb.Styles.styleSheetCreate(() => ({ }, isMobile: {...Kb.Styles.globalStyles.flexOne}, }), - container: { - padding: Kb.Styles.globalMargins.small, - }, extraLineText: { height: 36, }, @@ -265,11 +262,6 @@ const styles = Kb.Styles.styleSheetCreate(() => ({ }, }), subteamNameInput: Kb.Styles.padding(Kb.Styles.globalMargins.tiny), - wordBreak: Kb.Styles.platformStyles({ - isElectron: { - wordBreak: 'break-all', - }, - }), })) export default NewTeamInfo diff --git a/shared/teams/new-team/wizard/team-purpose.tsx b/shared/teams/new-team/wizard/team-purpose.tsx index 4fa97617bb83..cf94977472fa 100644 --- a/shared/teams/new-team/wizard/team-purpose.tsx +++ b/shared/teams/new-team/wizard/team-purpose.tsx @@ -76,14 +76,6 @@ const styles = Kb.Styles.styleSheetCreate(() => ({ }, isMobile: {...Kb.Styles.globalStyles.flexOne}, }), - container: { - padding: Kb.Styles.globalMargins.small, - }, - wordBreak: Kb.Styles.platformStyles({ - isElectron: { - wordBreak: 'break-all', - }, - }), })) export default TeamPurpose diff --git a/shared/teams/rename-team.tsx b/shared/teams/rename-team.tsx index bdcb775f658d..156886ec3323 100644 --- a/shared/teams/rename-team.tsx +++ b/shared/teams/rename-team.tsx @@ -180,12 +180,6 @@ const styles = Kb.Styles.styleSheetCreate( padding: Kb.Styles.globalMargins.tiny, }, inputContainerError: {borderColor: Kb.Styles.globalColors.red}, - prefix: Kb.Styles.platformStyles({ - isMobile: { - position: 'relative', - top: 1, - }, - }), teamnameHeader: Kb.Styles.platformStyles({ isElectron: {wordBreak: 'break-word'} as const, }), diff --git a/shared/teams/role-picker.tsx b/shared/teams/role-picker.tsx index 4850792971ad..d9a5caaa21db 100644 --- a/shared/teams/role-picker.tsx +++ b/shared/teams/role-picker.tsx @@ -326,11 +326,6 @@ const styles = Kb.Styles.styleSheetCreate( isMobile: {paddingRight: Kb.Styles.globalMargins.tiny, paddingTop: 4}, }), canText: {color: Kb.Styles.globalColors.black}, - checkIcon: { - left: -24, - paddingTop: 2, - position: 'absolute', - }, checkbox: { ...Kb.Styles.padding(Kb.Styles.globalMargins.tiny, Kb.Styles.globalMargins.small), flexGrow: 0, @@ -405,9 +400,6 @@ const styles = Kb.Styles.styleSheetCreate( rowSelected: { position: 'relative', }, - scroll: { - backgroundColor: Kb.Styles.globalColors.white, - }, text: { textAlign: 'left', }, diff --git a/shared/teams/team/index.tsx b/shared/teams/team/index.tsx index 9c2ad37aa0a9..d7064f6f079e 100644 --- a/shared/teams/team/index.tsx +++ b/shared/teams/team/index.tsx @@ -189,12 +189,6 @@ const Team = (props: Props) => { } const styles = Kb.Styles.styleSheetCreate(() => ({ - backButton: { - bottom: 0, - left: 0, - position: 'absolute', - top: 0, - }, container: { ...Kb.Styles.globalStyles.flexBoxColumn, alignItems: 'stretch', @@ -204,14 +198,6 @@ const styles = Kb.Styles.styleSheetCreate(() => ({ position: 'relative', width: '100%', }, - header: { - backgroundColor: Kb.Styles.globalColors.white, - height: 40, - left: 0, - position: 'absolute', - right: 0, - top: 0, - }, list: Kb.Styles.platformStyles({}), listContentContainer: Kb.Styles.platformStyles({ isMobile: { @@ -219,7 +205,6 @@ const styles = Kb.Styles.styleSheetCreate(() => ({ flexGrow: 1, }, }), - smallHeader: {...Kb.Styles.padding(0, Kb.Styles.globalMargins.xlarge)}, })) export default Team diff --git a/shared/teams/team/member/index.new.tsx b/shared/teams/team/member/index.new.tsx index c5bcd10e5aa9..dbcc40f7c479 100644 --- a/shared/teams/team/member/index.new.tsx +++ b/shared/teams/team/member/index.new.tsx @@ -732,12 +732,6 @@ const BlockDropdown = (props: {username: string}) => { } const styles = Kb.Styles.styleSheetCreate(() => ({ - backButton: { - bottom: 0, - left: 0, - position: 'absolute', - top: 0, - }, container: { ...Kb.Styles.globalStyles.flexBoxColumn, flex: 1, @@ -814,20 +808,9 @@ const styles = Kb.Styles.styleSheetCreate(() => ({ membershipTeamTextExpanded: Kb.Styles.platformStyles({ isMobile: {paddingTop: Kb.Styles.globalMargins.tiny}, }), - mobileHeader: { - backgroundColor: Kb.Styles.globalColors.white, - height: 40, - position: 'absolute', - right: 0, - top: 0, - }, paddingBottomMobile: Kb.Styles.platformStyles({ isPhone: {paddingBottom: Kb.Styles.globalMargins.small}, }), - reloadButton: { - marginTop: Kb.Styles.globalMargins.tiny, - minWidth: 56, - }, roleButton: {paddingRight: 0}, roleButtonExpanded: Kb.Styles.platformStyles({ isElectron: { @@ -845,7 +828,6 @@ const styles = Kb.Styles.styleSheetCreate(() => ({ height: 65, }, }), - smallHeader: {...Kb.Styles.padding(0, Kb.Styles.globalMargins.xlarge)}, teamNameLink: {color: Kb.Styles.globalColors.black}, })) diff --git a/shared/teams/team/rows/bot-row/bot.tsx b/shared/teams/team/rows/bot-row/bot.tsx index 4724d6429e39..8806f0744ddc 100644 --- a/shared/teams/team/rows/bot-row/bot.tsx +++ b/shared/teams/team/rows/bot-row/bot.tsx @@ -113,7 +113,6 @@ export const TeamBotRow = (props: Props) => { } const styles = Kb.Styles.styleSheetCreate(() => ({ - buttonBarContainer: {...Kb.Styles.globalStyles.flexBoxRow, flexShrink: 1}, clickable: { ...Kb.Styles.globalStyles.flexBoxRow, alignItems: 'center', @@ -131,11 +130,7 @@ const styles = Kb.Styles.styleSheetCreate(() => ({ containerReset: { backgroundColor: Kb.Styles.globalColors.blueLighter2, }, - crownIcon: { - marginRight: Kb.Styles.globalMargins.xtiny, - }, fullNameLabel: {marginRight: Kb.Styles.globalMargins.xtiny}, - innerContainerBottom: {...Kb.Styles.globalStyles.flexBoxRow, flexShrink: 1}, innerContainerTop: { ...Kb.Styles.globalStyles.flexBoxRow, ...Kb.Styles.padding(Kb.Styles.globalMargins.xsmall, Kb.Styles.globalMargins.small), @@ -144,14 +139,6 @@ const styles = Kb.Styles.styleSheetCreate(() => ({ height: Kb.Styles.isMobile ? 56 : 48, width: '100%', }, - lockedOutOrDeleted: { - ...Kb.Styles.globalStyles.fontBold, - backgroundColor: Kb.Styles.globalColors.red, - color: Kb.Styles.globalColors.white, - marginRight: Kb.Styles.globalMargins.xtiny, - paddingLeft: Kb.Styles.globalMargins.xtiny, - paddingRight: Kb.Styles.globalMargins.xtiny, - }, menuButtonDesktop: { marginLeft: Kb.Styles.globalMargins.small, marginRight: Kb.Styles.globalMargins.tiny, diff --git a/shared/teams/team/rows/emoji-row/add.tsx b/shared/teams/team/rows/emoji-row/add.tsx index b48b58e984e4..83103b6c0f27 100644 --- a/shared/teams/team/rows/emoji-row/add.tsx +++ b/shared/teams/team/rows/emoji-row/add.tsx @@ -72,7 +72,6 @@ const styles = Kb.Styles.styleSheetCreate(() => ({ flexGrow: 1, }, }), - text: {padding: Kb.Styles.globalMargins.xtiny}, })) export default AddEmoji diff --git a/shared/teams/team/rows/emoji-row/header.tsx b/shared/teams/team/rows/emoji-row/header.tsx index 5608890988d5..2ff306654f0a 100644 --- a/shared/teams/team/rows/emoji-row/header.tsx +++ b/shared/teams/team/rows/emoji-row/header.tsx @@ -38,7 +38,6 @@ const styles = Kb.Styles.styleSheetCreate(() => ({ marginRight: Kb.Styles.globalMargins.medium + Kb.Styles.globalMargins.xtiny, width: 72 - Kb.Styles.globalMargins.small, }, - text: {padding: Kb.Styles.globalMargins.xtiny}, username: { marginRight: Kb.Styles.globalMargins.large + Kb.Styles.globalMargins.small, // width of icon button + gap + padding width: 210, diff --git a/shared/teams/team/rows/invite-row/request.tsx b/shared/teams/team/rows/invite-row/request.tsx index 36be8c8e829c..006796457a98 100644 --- a/shared/teams/team/rows/invite-row/request.tsx +++ b/shared/teams/team/rows/invite-row/request.tsx @@ -160,33 +160,6 @@ const styleCharm = { const styles = Kb.Styles.styleSheetCreate(() => ({ bg: {backgroundColor: Kb.Styles.globalColors.white}, - clickContainer: Kb.Styles.platformStyles({ - common: { - ...Kb.Styles.globalStyles.flexBoxRow, - alignItems: 'center', - flexGrow: 0, - flexShrink: 1, - }, - isElectron: {width: 'initial'}, - }), - container: Kb.Styles.platformStyles({ - common: { - ...Kb.Styles.globalStyles.flexBoxRow, - ...Kb.Styles.padding(Kb.Styles.globalMargins.tiny, Kb.Styles.globalMargins.small), - alignItems: 'center', - flexDirection: 'row', - flexGrow: 0, - flexShrink: 1, - height: 48, - justifyContent: 'space-between', - width: '100%', - }, - isPhone: { - flexDirection: 'column', - height: 112, - }, - isTablet: {height: 56}, - }), disabled: {backgroundColor: Kb.Styles.globalColors.white, opacity: 0.4}, floatingRolePicker: Kb.Styles.platformStyles({ isElectron: { @@ -194,18 +167,6 @@ const styles = Kb.Styles.styleSheetCreate(() => ({ top: -32, }, }), - floatingRolePickerContainer: Kb.Styles.platformStyles({ - common: { - ...Kb.Styles.globalStyles.flexBoxRow, - alignItems: 'center', - marginTop: 0, - }, - isMobile: {marginTop: Kb.Styles.globalMargins.tiny}, - }), - icon: { - marginLeft: Kb.Styles.globalMargins.small, - marginRight: Kb.Styles.globalMargins.tiny, - }, ignoreButton: {marginLeft: Kb.Styles.globalMargins.xtiny}, letInButton: { backgroundColor: Kb.Styles.globalColors.green, @@ -215,11 +176,6 @@ const styles = Kb.Styles.styleSheetCreate(() => ({ ...Kb.Styles.globalStyles.flexOne, paddingRight: Kb.Styles.globalMargins.xtiny, }, - userDetails: { - ...Kb.Styles.globalStyles.flexBoxColumn, - flexGrow: 1, - marginLeft: Kb.Styles.globalMargins.small, - }, })) type OwnProps = { diff --git a/shared/teams/team/rows/subteam-row/add.tsx b/shared/teams/team/rows/subteam-row/add.tsx index 58102c169fa9..6c72cf9ba243 100644 --- a/shared/teams/team/rows/subteam-row/add.tsx +++ b/shared/teams/team/rows/subteam-row/add.tsx @@ -35,25 +35,12 @@ const AddSubteam = ({teamID}: {teamID: T.Teams.TeamID}) => { } const styles = Kb.Styles.styleSheetCreate(() => ({ - container: Kb.Styles.platformStyles({ - common: { - ...Kb.Styles.globalStyles.flexBoxRow, - ...Kb.Styles.padding(Kb.Styles.globalMargins.tiny, 0), - alignItems: 'center', - justifyContent: 'center', - width: '100%', - }, - isMobile: { - paddingTop: Kb.Styles.globalMargins.small, - }, - }), containerNew: { ...Kb.Styles.padding(6, Kb.Styles.globalMargins.small), backgroundColor: Kb.Styles.globalColors.blueGrey, justifyContent: 'space-between', }, filterInput: {maxWidth: 148}, - text: {padding: Kb.Styles.globalMargins.xtiny}, })) export default AddSubteam diff --git a/shared/teams/team/settings-tab/index.tsx b/shared/teams/team/settings-tab/index.tsx index 5b83a2071295..ec377d75f5bd 100644 --- a/shared/teams/team/settings-tab/index.tsx +++ b/shared/teams/team/settings-tab/index.tsx @@ -301,11 +301,6 @@ export const Settings = (p: Props) => { } const styles = Kb.Styles.styleSheetCreate(() => ({ - button: { - justifyContent: 'center', - paddingBottom: C.isMobile ? Kb.Styles.globalMargins.tiny : Kb.Styles.globalMargins.small, - paddingTop: C.isMobile ? Kb.Styles.globalMargins.tiny : Kb.Styles.globalMargins.small, - }, floatingRolePicker: Kb.Styles.platformStyles({ isElectron: { position: 'relative', @@ -313,10 +308,6 @@ const styles = Kb.Styles.styleSheetCreate(() => ({ }, }), grey: {color: Kb.Styles.globalColors.black_50}, - header: { - ...Kb.Styles.globalStyles.flexBoxRow, - marginBottom: Kb.Styles.globalMargins.tiny, - }, joinAs: Kb.Styles.platformStyles({ isElectron: {paddingRight: Kb.Styles.globalMargins.xtiny}, }), @@ -342,16 +333,7 @@ const styles = Kb.Styles.styleSheetCreate(() => ({ paddingTop: Kb.Styles.globalMargins.small, }, shrink: {flex: 1}, - spinner: {paddingLeft: Kb.Styles.globalMargins.xtiny}, teamPadding: {paddingTop: Kb.Styles.globalMargins.small}, - welcomeMessage: {paddingRight: Kb.Styles.globalMargins.small}, - welcomeMessageBorder: { - alignSelf: 'stretch', - backgroundColor: Kb.Styles.globalColors.grey, - paddingLeft: Kb.Styles.globalMargins.xtiny, - }, - welcomeMessageCard: {paddingBottom: Kb.Styles.globalMargins.tiny}, - welcomeMessageContainer: {position: 'relative'}, })) import {useSettingsTabState} from './use-settings' diff --git a/shared/teams/team/tabs.tsx b/shared/teams/team/tabs.tsx index a2fcf15ecd24..762f5eb58207 100644 --- a/shared/teams/team/tabs.tsx +++ b/shared/teams/team/tabs.tsx @@ -71,13 +71,6 @@ const styles = Kb.Styles.styleSheetCreate(() => ({ }, }), container: {backgroundColor: Kb.Styles.globalColors.white}, - inlineProgressIndicator: { - height: 17, - position: 'absolute', - right: Kb.Styles.globalMargins.small, - top: Kb.Styles.globalMargins.small, - width: 17, - }, tab: Kb.Styles.platformStyles({ isElectron: {flexGrow: 1}, isMobile: { diff --git a/shared/teams/team/team-info.tsx b/shared/teams/team/team-info.tsx index 4eb6002f45b9..e120e0acf17f 100644 --- a/shared/teams/team/team-info.tsx +++ b/shared/teams/team/team-info.tsx @@ -143,16 +143,8 @@ const styles = Kb.Styles.styleSheetCreate(() => ({ }, isMobile: {...Kb.Styles.globalStyles.flexOne}, }), - container: { - padding: Kb.Styles.globalMargins.small, - }, faded: {opacity: 0.5}, subteamNameInput: Kb.Styles.padding(Kb.Styles.globalMargins.tiny), - wordBreak: Kb.Styles.platformStyles({ - isElectron: { - wordBreak: 'break-all', - }, - }), })) export default TeamInfo diff --git a/shared/tracker2/bio.tsx b/shared/tracker2/bio.tsx index e26d67af9457..0e4cf0e656ac 100644 --- a/shared/tracker2/bio.tsx +++ b/shared/tracker2/bio.tsx @@ -160,17 +160,10 @@ const styles = Kb.Styles.styleSheetCreate( }, bold: {...Kb.Styles.globalStyles.fontBold}, container: {backgroundColor: Kb.Styles.globalColors.white, flexShrink: 0}, - floatingContainer: Kb.Styles.platformStyles({ - common: {backgroundColor: Kb.Styles.globalColors.purple}, - isElectron: {maxWidth: 200}, - }), - fullName: Kb.Styles.platformStyles({isElectron: {wordBreak: 'break-all'} as const}), fullNameContainer: { paddingLeft: Kb.Styles.globalMargins.mediumLarge, paddingRight: Kb.Styles.globalMargins.mediumLarge, }, - learnButton: {alignSelf: 'center', marginTop: Kb.Styles.globalMargins.tiny}, - star: {alignSelf: 'center', marginBottom: Kb.Styles.globalMargins.tiny}, text: Kb.Styles.platformStyles({ common: { paddingLeft: Kb.Styles.globalMargins.mediumLarge, diff --git a/shared/unlock-folders/device-list.desktop.tsx b/shared/unlock-folders/device-list.desktop.tsx index ab64aeb86072..5aec553bb53e 100644 --- a/shared/unlock-folders/device-list.desktop.tsx +++ b/shared/unlock-folders/device-list.desktop.tsx @@ -53,7 +53,6 @@ const DeviceList = (props: Props) => ( const styles = Kb.Styles.styleSheetCreate( () => ({ - accessFolders: {marginRight: 0}, buttonsContainer: { ...Kb.Styles.globalStyles.flexBoxRow, alignSelf: 'center', diff --git a/shared/wallets/wallet-popup.tsx b/shared/wallets/wallet-popup.tsx index b49b0a53523b..291977439f8e 100644 --- a/shared/wallets/wallet-popup.tsx +++ b/shared/wallets/wallet-popup.tsx @@ -109,11 +109,6 @@ const styles = Kb.Styles.styleSheetCreate(() => ({ }, isMobile: {}, }), - header: Kb.Styles.platformStyles({ - isElectron: { - borderRadius: 4, - }, - }), headerStyle: {backgroundColor: Kb.Styles.globalColors.transparent}, outerContainer: Kb.Styles.platformStyles({ isElectron: { diff --git a/shared/whats-new/index.tsx b/shared/whats-new/index.tsx index f15609db9263..a09034b61822 100644 --- a/shared/whats-new/index.tsx +++ b/shared/whats-new/index.tsx @@ -119,11 +119,6 @@ const styles = Kb.Styles.styleSheetCreate(() => ({ marginTop: Kb.Styles.globalMargins.small, }, }), - versionTitle: { - color: Kb.Styles.globalColors.black_50, - marginBottom: Kb.Styles.globalMargins.tiny, - marginTop: Kb.Styles.globalMargins.xsmall, - }, })) export default WhatsNew diff --git a/shared/whats-new/versions.tsx b/shared/whats-new/versions.tsx index ddf79961c28c..f536f985f1fa 100644 --- a/shared/whats-new/versions.tsx +++ b/shared/whats-new/versions.tsx @@ -190,17 +190,6 @@ const styles = Kb.Styles.styleSheetCreate(() => ({ justifyContent: 'center', }, ponyIcon: {marginLeft: Kb.Styles.globalMargins.tiny}, - roundedImage: Kb.Styles.platformStyles({ - common: { - borderColor: Kb.Styles.globalColors.grey, - borderWidth: Kb.Styles.globalMargins.xxtiny, - }, - isElectron: { - // Pass borderRadius as a number to the image on mobile using collapseStyles - borderRadius: '100%', - borderStyle: 'solid', - }, - }), versionTitle: { color: Kb.Styles.globalColors.black_50, marginBottom: Kb.Styles.globalMargins.tiny, From da74ce0bd0b6e8a145c979fa01ba3e0b0220224a Mon Sep 17 00:00:00 2001 From: chrisnojima Date: Wed, 18 Feb 2026 09:18:03 -0500 Subject: [PATCH 006/112] add remote debug for playwright (#28915) --- shared/desktop/yarn-helper/electron.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/shared/desktop/yarn-helper/electron.tsx b/shared/desktop/yarn-helper/electron.tsx index 8253f5d384da..58ab0cc603a1 100644 --- a/shared/desktop/yarn-helper/electron.tsx +++ b/shared/desktop/yarn-helper/electron.tsx @@ -5,6 +5,7 @@ import {spawn} from 'child_process' const isLinux = process.platform === 'linux' const debugInNode = (false as boolean) ? '--inspect-brk' : '' +const remoteDebug = process.env['KB_ENABLE_REMOTE_DEBUG'] === '1' ? '--remote-debugging-port=9222' : '' const commands = { 'inject-code-prod': { @@ -56,7 +57,11 @@ function startHot() { const req = http.get('http://localhost:4000/dist/node.dev.bundle.js', () => { // require in case we're trying to yarn install electron! const electron = require('electron') as unknown as string - spawn(electron, [...params, ...(isLinux ? ['--disable-gpu'] : [])], {stdio: 'inherit'}) + spawn( + electron, + [...params, ...(remoteDebug ? [remoteDebug] : []), ...(isLinux ? ['--disable-gpu'] : [])], + {stdio: 'inherit'} + ) }) req.on('error', e => { console.log('Error: ', e) From c44a3902ef0cedbe250cae34edff2449df6732b3 Mon Sep 17 00:00:00 2001 From: chrisnojima Date: Wed, 18 Feb 2026 09:50:11 -0500 Subject: [PATCH 007/112] modernize some css (#28913) --- .../normal2/platform-input.desktop.tsx | 1 - .../index.desktop.tsx | 25 +-------- shared/common-adapters/avatar/avatar.css | 23 ++------ shared/common-adapters/button.css | 13 +---- shared/common-adapters/choice-list.css | 8 +-- shared/common-adapters/icon.desktop.tsx | 29 ++++++----- shared/common-adapters/input2.desktop.tsx | 43 +++------------ shared/common-adapters/list2.desktop.tsx | 9 ++-- .../common-adapters/plain-input.desktop.tsx | 46 +++------------- shared/common-adapters/text.css | 16 +----- shared/desktop/renderer/style.css | 52 ++++--------------- shared/fs/filepreview/text-view.desktop.tsx | 4 +- shared/fs/footer/upload.desktop.tsx | 21 +------- shared/router-v2/router.css | 5 +- shared/styles/css.d.ts | 3 ++ shared/styles/index.d.ts | 5 +- shared/styles/index.desktop.tsx | 36 +++++++++---- shared/styles/shared.tsx | 2 +- shared/unlock-folders/device-list.desktop.tsx | 3 +- 19 files changed, 95 insertions(+), 249 deletions(-) diff --git a/shared/chat/conversation/input-area/normal2/platform-input.desktop.tsx b/shared/chat/conversation/input-area/normal2/platform-input.desktop.tsx index 03ff601a671b..88026a913e7e 100644 --- a/shared/chat/conversation/input-area/normal2/platform-input.desktop.tsx +++ b/shared/chat/conversation/input-area/normal2/platform-input.desktop.tsx @@ -515,7 +515,6 @@ const styles = Kb.Styles.styleSheetCreate( input: Kb.Styles.platformStyles({ isElectron: { backgroundColor: Kb.Styles.globalColors.transparent, - height: 22, // Line height change is so that emojis (unicode characters inside // textarea) are not clipped at the top. This change is accompanied by // a change in padding to offset the increased line height diff --git a/shared/chat/conversation/messages/wrapper/exploding-height-retainer/index.desktop.tsx b/shared/chat/conversation/messages/wrapper/exploding-height-retainer/index.desktop.tsx index 2c0931cb80f8..6cfcf84d3f74 100644 --- a/shared/chat/conversation/messages/wrapper/exploding-height-retainer/index.desktop.tsx +++ b/shared/chat/conversation/messages/wrapper/exploding-height-retainer/index.desktop.tsx @@ -1,8 +1,6 @@ import * as React from 'react' import * as Kb from '@/common-adapters' -import {urlsToImgSet} from '@/common-adapters/icon.desktop' import type {Props} from '.' -import {getAssetPath} from '@/constants/platform.desktop' import {useColorScheme} from 'react-native' export const animationDuration = 2000 @@ -59,7 +57,6 @@ const ExplodingHeightRetainer = (p: Props) => { const Ashes = (props: {doneExploding: boolean; exploded: boolean; explodedBy?: string; height: number}) => { const {doneExploding, explodedBy, exploded, height} = props - const isDarkMode = useColorScheme() === 'dark' let explodedTag: React.ReactNode = null if (doneExploding) { explodedTag = explodedBy ? ( @@ -87,26 +84,8 @@ const Ashes = (props: {doneExploding: boolean; exploded: boolean; explodedBy?: s return (
{exploded && explodedTag} diff --git a/shared/common-adapters/avatar/avatar.css b/shared/common-adapters/avatar/avatar.css index 1154f56411c5..4557afa2c9e3 100644 --- a/shared/common-adapters/avatar/avatar.css +++ b/shared/common-adapters/avatar/avatar.css @@ -127,29 +127,17 @@ /* Background layer - no border-radius needed, parent clips via overflow */ .avatar-background { - background-color: var(--color-greyLight); - bottom: 0; - left: 0; + background-color: light-dark(var(--color-greyLight), #0f0f0f); position: absolute; - right: 0; - top: 0; -} - -@media (prefers-color-scheme: dark) { - .avatar-background { - background-color: #0f0f0f; - } + inset: 0; } /* Image layer - no border-radius needed, parent clips via overflow */ .avatar-user-image { flex-shrink: 0; background-size: cover; - bottom: 0; - left: 0; position: absolute; - right: 0; - top: 0; + inset: 0; } /* For img tag avatars - better caching and performance */ @@ -172,12 +160,9 @@ img.avatar-user-image { .avatar-border-team { border-radius: 8px; background: rgba(0, 0, 0, 0); - bottom: 0; flex-shrink: 0; - left: 0; position: absolute; - right: 0; - top: 0; + inset: 0; } @media (prefers-color-scheme: dark) { diff --git a/shared/common-adapters/button.css b/shared/common-adapters/button.css index 2c628e4f7644..8c243da185fc 100644 --- a/shared/common-adapters/button.css +++ b/shared/common-adapters/button.css @@ -21,10 +21,7 @@ .button__underlay { position: absolute; - bottom: 0; - left: 0; - right: 0; - top: 0; + inset: 0; border-radius: 4px; transition: background-color 0.2s ease-out; } @@ -44,13 +41,7 @@ background-color: rgba(112, 78, 186, 0.05); } .button__underlay_black:hover { - background-color: rgba(0, 0, 0, 0.05); -} - -@media (prefers-color-scheme: dark) { - .button__underlay_black:hover { - background-color: rgba(255, 255, 255, 0.05); - } + background-color: light-dark(rgba(0, 0, 0, 0.05), rgba(255, 255, 255, 0.05)); } .button__underlay_yellow:hover { diff --git a/shared/common-adapters/choice-list.css b/shared/common-adapters/choice-list.css index f4caf09970b0..40aa8203137c 100644 --- a/shared/common-adapters/choice-list.css +++ b/shared/common-adapters/choice-list.css @@ -1,19 +1,13 @@ .cl-entry { background-color: transparent; &:hover { - background-color: var(--color-blueLighter2); + background-color: light-dark(var(--color-blueLighter2), #140d03); .cl-icon-container { background: transparent; } } } -@media (prefers-color-scheme: dark) { - .cl-entry:hover { - background-color: #140d03; - } -} - .cl-icon { transform-origin: center center; diff --git a/shared/common-adapters/icon.desktop.tsx b/shared/common-adapters/icon.desktop.tsx index efd796e624f7..fb644d2d4f54 100644 --- a/shared/common-adapters/icon.desktop.tsx +++ b/shared/common-adapters/icon.desktop.tsx @@ -1,17 +1,17 @@ import * as Shared from './icon.shared' import * as Styles from '@/styles' -import {colors, darkColors} from '@/styles/colors' import * as React from 'react' import logger from '@/logger' import {iconMeta} from './icon.constants-gen' -import invert from 'lodash/invert' import {getAssetPath} from '@/constants/platform.desktop' import type {Props, IconType} from './icon' import type {MeasureRef} from './measure-ref' -import {useColorScheme} from 'react-native' -const invertedLight = invert(colors) -const invertedDark = invert(darkColors) +// Extract color name from CSS variable string: "var(--color-black_50)" → "black_50" +const cssVarToColorName = (cssVar: string): string | undefined => { + const match = /^var\(--color-(.+)\)$/.exec(cssVar) + return match?.[1] +} const Icon = React.memo( React.forwardRef(function Icon(props, ref) { @@ -19,8 +19,6 @@ const Icon = React.memo( const {className, hint, colorOverride, padding, boxStyle, allowLazy = true} = props const iconType = type const hasDarkVariant = !!iconMeta[iconType].nameDark - const scheme = useColorScheme() - const isDarkMode = scheme === 'dark' && hasDarkVariant if (!Shared.isValidIconType(iconType)) { logger.warn('Unknown icontype passed', iconType) @@ -86,7 +84,7 @@ const Icon = React.memo( ...(props.color ? {color: color} : {}), } as React.CSSProperties - iconElement = ( + const img = ( ( title={hint} style={imgStyle} onClick={onClick || undefined} - srcSet={iconTypeToSrcSet(iconType, isDarkMode)} + srcSet={iconTypeToSrcSet(iconType, false)} /> ) + iconElement = hasDarkVariant ? ( + + + {img} + + ) : ( + img + ) } if (hasContainer) { @@ -112,10 +118,9 @@ const Icon = React.memo( hoverColor: 'inherit', } } else { - const invertedColors = isDarkMode ? invertedDark : invertedLight - const hoverColorName = onClick ? invertedColors[hoverColor] : null + const hoverColorName = onClick ? cssVarToColorName(hoverColor) : null hoverStyleName = hoverColorName ? `hover_color_${hoverColorName}` : '' - const colorName = invertedColors[color] + const colorName = cssVarToColorName(color) if (colorName) { colorStyleName = `color_${colorName}` } diff --git a/shared/common-adapters/input2.desktop.tsx b/shared/common-adapters/input2.desktop.tsx index 25a68504b9c4..97255ba9e5dd 100644 --- a/shared/common-adapters/input2.desktop.tsx +++ b/shared/common-adapters/input2.desktop.tsx @@ -21,41 +21,13 @@ export const Input2 = React.memo( const inputSingleRef = React.useRef(null) const inputMultiRef = React.useRef(null) - const autoResizeLastRef = React.useRef('') - const autoResize = React.useCallback(() => { - if (!multiline) { - // no resizing height on single-line inputs - return - } - - // Allow textarea to layout automatically - // if (this.props.growAndScroll) { - // return - // } - - const n = inputMultiRef.current - if (!n) { - return - } - - // ignore if value hasn't changed - if (n.value === autoResizeLastRef.current) { - return - } - autoResizeLastRef.current = n.value - - n.style.height = '1px' - n.style.height = `${n.scrollHeight}px` - }, [multiline]) - const onChange = React.useCallback( (e: {target: HTMLInputElement | HTMLTextAreaElement}) => { const s = e.target.value setValue(s) _onChangeText?.(s) - autoResize() }, - [_onChangeText, autoResize] + [_onChangeText] ) const onSelect = React.useCallback((e: {currentTarget: HTMLInputElement | HTMLTextAreaElement}) => { selectionRef.current = { @@ -103,7 +75,6 @@ export const Input2 = React.memo( } } }, 10) - autoResize() if (reflectChange) { setTimeout(() => { if (!i) return @@ -114,13 +85,13 @@ export const Input2 = React.memo( }, value, } - }, [value, multiline, autoResize, onChange]) + }, [value, multiline, onChange]) const rows = multiline ? rowsMin || Math.min(2, rowsMax || 2) : 0 const style = React.useMemo(() => { const textStyle = getTextStyle(textType, isDarkMode) if (multiline) { - const heightStyles: {minHeight: number; maxHeight?: number; overflowY?: 'hidden'} = { + const heightStyles: {minHeight: number; maxHeight?: number} = { minHeight: rows * (textStyle.lineHeight === undefined ? 20 : maybeParseInt(textStyle.lineHeight, 10) || 20) + (padding ? Styles.globalMargins[padding] * 2 : 0), @@ -130,8 +101,6 @@ export const Input2 = React.memo( heightStyles.maxHeight = rowsMax * (textStyle.lineHeight === undefined ? 20 : maybeParseInt(textStyle.lineHeight, 10) || 20) - } else { - heightStyles.overflowY = 'hidden' } const paddingStyles = padding ? Styles.padding(Styles.globalMargins[padding]) : {} @@ -219,13 +188,15 @@ const styles = Styles.styleSheetCreate(() => ({ }, growAndScroll: Styles.platformStyles({ isElectron: { + fieldSizing: 'fixed', maxHeight: '100%', - overflowY: 'scroll', + overflowY: 'auto', + scrollbarGutter: 'stable', }, }), multiline: Styles.platformStyles({ isElectron: { - height: 'initial', + fieldSizing: 'content', paddingBottom: 0, paddingTop: 0, resize: 'none', diff --git a/shared/common-adapters/list2.desktop.tsx b/shared/common-adapters/list2.desktop.tsx index adeb8003a470..385faf885abd 100644 --- a/shared/common-adapters/list2.desktop.tsx +++ b/shared/common-adapters/list2.desktop.tsx @@ -32,7 +32,8 @@ function List2(props: Props) { style={ { height: '100%', - overflowY: 'scroll', + overflowY: 'auto', + scrollbarGutter: 'stable', width: '100%', ...Styles.castStyleDesktop(style), } as const @@ -68,7 +69,8 @@ function List2(props: Props) { style={ { height: '100%', - overflowY: 'scroll', + overflowY: 'auto', + scrollbarGutter: 'stable', width: '100%', ...Styles.castStyleDesktop(style), } as const @@ -89,7 +91,8 @@ function List2(props: Props) { style={ { height: '100%', - overflowY: 'scroll', + overflowY: 'auto', + scrollbarGutter: 'stable', width: '100%', ...Styles.castStyleDesktop(style), } as const diff --git a/shared/common-adapters/plain-input.desktop.tsx b/shared/common-adapters/plain-input.desktop.tsx index acbffba61cbc..94ab9c6b66ae 100644 --- a/shared/common-adapters/plain-input.desktop.tsx +++ b/shared/common-adapters/plain-input.desktop.tsx @@ -28,8 +28,6 @@ type NativeTextRef = { focus: () => void blur: () => void value: string - style: {height: string} - scrollHeight: number selectionStart: number | null selectionEnd: number | null } @@ -44,7 +42,6 @@ const PlainInput = React.memo( const inputRef = React.useRef(null) const isComposingIMERef = React.useRef(false) const mountedRef = React.useRef(true) - const autoResizeLastRef = React.useRef('') const isDarkMode = useColorScheme() === 'dark' const focus = React.useCallback(() => { @@ -82,32 +79,6 @@ const PlainInput = React.memo( [_onKeyUp] ) - const autoResize = React.useCallback(() => { - if (!multiline) { - // no resizing height on single-line inputs - return - } - - // Allow textarea to layout automatically - if (growAndScroll) { - return - } - - const n = inputRef.current - if (!n) { - return - } - - // ignore if value hasn't changed - if (n.value === autoResizeLastRef.current) { - return - } - autoResizeLastRef.current = n.value - - n.style.height = '1px' - n.style.height = `${n.scrollHeight}px` - }, [multiline, growAndScroll]) - // This is controlled if a value prop is passed const isControlled = typeof p.value === 'string' @@ -151,9 +122,8 @@ const PlainInput = React.memo( } onChangeText?.(value) - autoResize() }, - [maxBytes, onChangeText, autoResize] + [maxBytes, onChangeText] ) const globalKeyDownHandler = React.useCallback( @@ -227,7 +197,7 @@ const PlainInput = React.memo( const getMultilineProps = () => { const rows = rowsMin || Math.min(2, rowsMax || 2) const textStyle = getTextStyle(textType ?? 'Body', isDarkMode) - const heightStyles: {minHeight: number; maxHeight?: number; overflowY?: 'hidden'} = { + const heightStyles: {minHeight: number; maxHeight?: number} = { minHeight: rows * (textStyle.lineHeight === undefined ? 20 : maybeParseInt(textStyle.lineHeight, 10) || 20) + (padding ? Styles.globalMargins[padding] * 2 : 0), @@ -235,8 +205,6 @@ const PlainInput = React.memo( if (rowsMax) { heightStyles.maxHeight = rowsMax * (textStyle.lineHeight === undefined ? 20 : maybeParseInt(textStyle.lineHeight, 10) || 20) - } else { - heightStyles.overflowY = 'hidden' } const paddingStyles = padding ? Styles.padding(Styles.globalMargins[padding]) : {} @@ -301,11 +269,9 @@ const PlainInput = React.memo( if (reflectChange) { onChange({target: inputRef.current ?? {value: ''}}) } - - autoResize() } }, - [autoResize, isControlled, onChange] + [isControlled, onChange] ) React.useImperativeHandle(ref, () => { @@ -363,13 +329,15 @@ const styles = Styles.styleSheetCreate(() => ({ }, growAndScroll: Styles.platformStyles({ isElectron: { + fieldSizing: 'fixed', maxHeight: '100%', - overflowY: 'scroll', + overflowY: 'auto', + scrollbarGutter: 'stable', }, }), multiline: Styles.platformStyles({ isElectron: { - height: 'initial', + fieldSizing: 'content', paddingBottom: 0, paddingTop: 0, resize: 'none', diff --git a/shared/common-adapters/text.css b/shared/common-adapters/text.css index 0cbbf81c10dd..be009814a316 100644 --- a/shared/common-adapters/text.css +++ b/shared/common-adapters/text.css @@ -248,13 +248,7 @@ } .text_Terminal { - color: #a8ccff; -} - -@media (prefers-color-scheme: dark) { - .text_Terminal { - color: #4c8eff; - } + color: light-dark(#a8ccff, #4c8eff); } .text_TerminalComment { @@ -268,7 +262,7 @@ .text_TerminalInline { line-height: 16px; - color: #2645a3; + color: light-dark(#2645a3, #f00); background-color: var(--color-blueLighter2); border-radius: 2px; height: 17px; @@ -276,12 +270,6 @@ word-wrap: break-word; } -@media (prefers-color-scheme: dark) { - .text_TerminalInline { - color: #f00; - } -} - .text_center { display: inline-block; text-align: center; diff --git a/shared/desktop/renderer/style.css b/shared/desktop/renderer/style.css index 52b806ac3357..94f8893d8760 100644 --- a/shared/desktop/renderer/style.css +++ b/shared/desktop/renderer/style.css @@ -211,6 +211,7 @@ table { /* End Keybase */ :root { + color-scheme: light dark; --size-xxtiny: 2px; --size-xtiny: 4px; --size-tiny: 8px; @@ -243,6 +244,7 @@ html { body { height: 100%; width: 100%; + scrollbar-gutter: stable; } :root { @@ -250,17 +252,9 @@ body { --scrollbar-minlength: 0.5rem; --scrollbar-ff-width: thin; --scrollbar-track-color: transparent; - --scrollbar-color: rgba(0, 0, 0, 0.2); - --scrollbar-color-hover: rgba(0, 0, 0, 0.3); - --scrollbar-color-active: rgb(0, 0, 0); -} - -@media (prefers-color-scheme: dark) { - :root { - --scrollbar-color: rgba(255, 255, 255, 0.2); - --scrollbar-color-hover: rgba(255, 255, 255, 0.3); - --scrollbar-color-active: rgb(255, 255, 255, 0.5); - } + --scrollbar-color: light-dark(rgba(0, 0, 0, 0.2), rgba(255, 255, 255, 0.2)); + --scrollbar-color-hover: light-dark(rgba(0, 0, 0, 0.3), rgba(255, 255, 255, 0.3)); + --scrollbar-color-active: light-dark(rgb(0, 0, 0), rgb(255, 255, 255, 0.5)); } /* Used to customize scrollbars and enable testing the auto hide behavior on os x */ @@ -274,8 +268,6 @@ body { .scrollbar-test { overscroll-behavior: contain; overflow-y: auto; - -webkit-overflow-scrolling: touch; - -ms-overflow-style: -ms-autohiding-scrollbar; scrollbar-width: var(--scrollbar-ff-width); } @@ -308,10 +300,7 @@ body { #root { position: absolute; display: flex; - top: 0; - bottom: 0; - left: 0; - right: 0; + inset: 0; overflow: hidden; } @@ -329,13 +318,7 @@ body { .emoji-picker-emoji-box { border-radius: 2px; &:hover { - background-color: rgba(0, 0, 0, 0.05); - } -} - -@media (prefers-color-scheme: dark) { - .emoji-picker-emoji-box:hover { - background-color: rgba(255, 255, 255, 0.15); + background-color: light-dark(rgba(0, 0, 0, 0.05), rgba(255, 255, 255, 0.15)); } } @@ -483,35 +466,18 @@ body { } body { - background-color: #ffffff; + background-color: light-dark(#ffffff, #191919); &.isWidget { background-color: transparent; } } -@media (prefers-color-scheme: dark) { - body { - background-color: #191919; - &.isWidget { - background-color: transparent; - } - } -} - .addInviteAndLinkBox { transition: box-shadow 0.35s; } .headerTitle:hover { .addInviteAndLinkBox { - box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2); - } -} - -@media (prefers-color-scheme: dark) { - .headerTitle:hover { - .addInviteAndLinkBox { - box-shadow: 0 2px 5px rgba(0, 0, 0, 0.8); - } + box-shadow: light-dark(0 2px 5px rgba(0, 0, 0, 0.2), 0 2px 5px rgba(0, 0, 0, 0.8)); } } diff --git a/shared/fs/filepreview/text-view.desktop.tsx b/shared/fs/filepreview/text-view.desktop.tsx index 63e4ec770d38..bb27fd5ca528 100644 --- a/shared/fs/filepreview/text-view.desktop.tsx +++ b/shared/fs/filepreview/text-view.desktop.tsx @@ -40,7 +40,7 @@ const styles = Kb.Styles.styleSheetCreate( backgroundColor: Kb.Styles.globalColors.blueLighter3, padding: Kb.Styles.globalMargins.small, }, - isElectron: {overflow: 'scroll'} as const, + isElectron: {overflow: 'auto', scrollbarGutter: 'stable'} as const, }), innerContainer: { ...Kb.Styles.globalStyles.flexGrow, @@ -56,7 +56,7 @@ const styles = Kb.Styles.styleSheetCreate( text: Kb.Styles.platformStyles({ isElectron: { color: Kb.Styles.globalColors.black_on_white, - overflow: 'scroll', + overflow: 'auto', whiteSpace: 'pre', }, }), diff --git a/shared/fs/footer/upload.desktop.tsx b/shared/fs/footer/upload.desktop.tsx index fa464af2c342..c6191ab41533 100644 --- a/shared/fs/footer/upload.desktop.tsx +++ b/shared/fs/footer/upload.desktop.tsx @@ -3,20 +3,7 @@ import * as React from 'react' import * as Kb from '@/common-adapters' import type {UploadProps} from './upload' import capitalize from 'lodash/capitalize' -import {getAssetPath} from '@/constants/platform.desktop' -import * as Path from '@/util/path' import './upload.css' -import {useColorScheme} from 'react-native' - -const backgroundURL = (url: string, isDarkMode: boolean) => { - const ext = Path.extname(url) - const goodPath = Path.basename(url, ext) ?? '' - const guiModePath = `${isDarkMode ? 'dark-' : ''}${goodPath}` - const images = [1, 2, 3].map( - mult => `url('${getAssetPath('images', guiModePath)}${mult === 1 ? '' : `@${mult}x`}${ext}') ${mult}x` - ) - return `-webkit-image-set(${images.join(', ')})` -} type DrawState = 'showing' | 'hiding' | 'hidden' const Upload = React.memo(function Upload(props: UploadProps) { @@ -43,7 +30,6 @@ const Upload = React.memo(function Upload(props: UploadProps) { // this is due to the fact that the parent container has a marginTop of -13 on darwin const offset = smallMode && C.isDarwin ? 13 : 0 - const isDarkMode = useColorScheme() === 'dark' return ( <> {!!debugToggleShow && ( @@ -57,15 +43,10 @@ const Upload = React.memo(function Upload(props: UploadProps) { diff --git a/shared/router-v2/router.css b/shared/router-v2/router.css index f6c6a6b3f7b4..6664eddbd307 100644 --- a/shared/router-v2/router.css +++ b/shared/router-v2/router.css @@ -1,9 +1,6 @@ .modal-backdrop { position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; + inset: 0; pointer-events: none; transition: background-color 0.3s ease; } diff --git a/shared/styles/css.d.ts b/shared/styles/css.d.ts index 8bdfdf5cfe65..398830bd51c2 100644 --- a/shared/styles/css.d.ts +++ b/shared/styles/css.d.ts @@ -6,10 +6,12 @@ export type DimensionValue = number | 'auto' | `${number}%` export type Color = undefined | string type _StylesDesktopOverride = { backgroundImage?: string + fieldSizing?: 'content' | 'fixed' lineHeight?: `${number}px` | number | 'inherit' | 'unset' objectFit?: 'contain' | 'cover' | 'fill' | 'none' | 'scale-down' overflowX?: 'auto' | 'clip' | 'hidden' | 'scroll' | 'visible' overflowY?: 'auto' | 'clip' | 'hidden' | 'scroll' | 'visible' + scrollbarGutter?: 'auto' | 'stable' | 'stable both-edges' wordBreak?: 'normal' | 'break-all' | 'keep-all' | 'inherit' | 'initial' | 'unset' | 'break-word' WebkitAppRegion?: 'drag' | 'no-drag' WebkitBackgroundClip?: 'text' @@ -65,6 +67,7 @@ type StyleKeys = | 'fontVariant' | 'fontWeight' | 'height' + | 'inset' | 'justifyContent' | 'left' | 'letterSpacing' diff --git a/shared/styles/index.d.ts b/shared/styles/index.d.ts index 8449c0f166fe..14189938c2f6 100644 --- a/shared/styles/index.d.ts +++ b/shared/styles/index.d.ts @@ -14,11 +14,8 @@ type _fakeFontDefSeeCommentsOnThisStyle = { export declare const globalStyles: { fillAbsolute: { - bottom: 0 - left: 0 + inset: 0 position: 'absolute' - right: 0 - top: 0 } flexBoxCenter: { alignItems: 'center' diff --git a/shared/styles/index.desktop.tsx b/shared/styles/index.desktop.tsx index f0b35b8114cb..89ee60ff8823 100644 --- a/shared/styles/index.desktop.tsx +++ b/shared/styles/index.desktop.tsx @@ -2,6 +2,7 @@ import * as Shared from './shared' import styleSheetCreateProxy from './style-sheet-proxy' import type * as CSS from './css' import {themed, colors, darkColors} from './colors' +import {getAssetPath} from '@/constants/platform.desktop' const fontCommon = { WebkitFontSmoothing: 'antialiased', @@ -119,18 +120,12 @@ export const initDesktopStyles = () => { const colorVars = ` :root { ${colorNames .reduce((s, name) => { - s.push(`--color-${name}: ${colors[name]};`) + const light = colors[name] as string + const dark = darkColors[name] as string + s.push(`--color-${name}: ${light === dark ? light : `light-dark(${light}, ${dark})`};`) return s }, new Array()) .join(' ')} } - @media (prefers-color-scheme: dark) { - :root { ${colorNames - .reduce((s, name) => { - s.push(`--color-${name}: ${darkColors[name]};`) - return s - }, new Array()) - .join(' ')} } - } ` const helpers = colorNames.reduce((s, name) => { return ( @@ -150,6 +145,29 @@ export const initDesktopStyles = () => { const helperStyle = document.createElement('style') helperStyle.appendChild(document.createTextNode(helpers)) head.appendChild(helperStyle) + + // Generate background-image CSS classes with dark mode variants + const makeImgSet = (dir: string, name: string) => { + const url = getAssetPath('images', dir, name) + return `-webkit-image-set(url('${url}') 1x)` + } + const makeMultiResImgSet = (baseName: string) => { + const ext = baseName.slice(baseName.lastIndexOf('.')) + const base = baseName.slice(0, baseName.lastIndexOf('.')) + const images = [1, 2, 3].map( + mult => `url('${getAssetPath('images', base)}${mult === 1 ? '' : `@${mult}x`}${ext}') ${mult}x` + ) + return `-webkit-image-set(${images.join(', ')})` + } + const imageCss = + `.ashes-bg { background-image: ${makeImgSet('icons', 'pattern-ashes-desktop-400-68.png')}; }\n` + + `@media (prefers-color-scheme: dark) { .ashes-bg { background-image: ${makeImgSet('icons', 'dark-pattern-ashes-desktop-400-68.png')}; } }\n` + + `.upload-bg { background-image: ${makeMultiResImgSet('upload-pattern-80.png')}; }\n` + + `@media (prefers-color-scheme: dark) { .upload-bg { background-image: ${makeMultiResImgSet('dark-upload-pattern-80.png')}; } }\n` + const imageStyle = document.createElement('style') + imageStyle.appendChild(document.createTextNode(imageCss)) + head.appendChild(imageStyle) + fixScrollbars() } diff --git a/shared/styles/shared.tsx b/shared/styles/shared.tsx index 31258346654c..ac9239e32290 100644 --- a/shared/styles/shared.tsx +++ b/shared/styles/shared.tsx @@ -56,7 +56,7 @@ export const backgroundModeToTextColor = (backgroundMode: Background) => { const flexCommon = isMobile ? {} : ({display: 'flex'} as const) export const util = { - fillAbsolute: {bottom: 0, left: 0, position: 'absolute', right: 0, top: 0}, + fillAbsolute: {inset: 0, position: 'absolute'}, flexBoxCenter: {...flexCommon, alignItems: 'center', justifyContent: 'center'}, flexBoxColumn: {...flexCommon, flexDirection: 'column'}, flexBoxColumnReverse: {...flexCommon, flexDirection: 'column-reverse'}, diff --git a/shared/unlock-folders/device-list.desktop.tsx b/shared/unlock-folders/device-list.desktop.tsx index 5aec553bb53e..7388a3f10f38 100644 --- a/shared/unlock-folders/device-list.desktop.tsx +++ b/shared/unlock-folders/device-list.desktop.tsx @@ -64,9 +64,10 @@ const styles = Kb.Styles.styleSheetCreate( alignSelf: 'center', backgroundColor: Kb.Styles.globalColors.greyLight, height: 162, - overflowY: 'scroll', + overflowY: 'auto', paddingBottom: Kb.Styles.globalMargins.small, paddingTop: Kb.Styles.globalMargins.small, + scrollbarGutter: 'stable', width: 440, }, }), From 22587f88150318ba080ec02e490e6a946f84a9d5 Mon Sep 17 00:00:00 2001 From: chrisnojima Date: Wed, 18 Feb 2026 10:20:31 -0500 Subject: [PATCH 008/112] tsc go (#28916) --- .../conversation/messages/wrapper/index.tsx | 2 +- .../markdown/maybe-mention/team.tsx | 2 +- shared/constants/chat2/message.tsx | 2 +- shared/package.json | 3 +- shared/tsconfig.json | 12 +---- shared/yarn.lock | 48 +++++++++++++++++++ 6 files changed, 55 insertions(+), 14 deletions(-) diff --git a/shared/chat/conversation/messages/wrapper/index.tsx b/shared/chat/conversation/messages/wrapper/index.tsx index 50971bee4c96..26a0fd2e03c5 100644 --- a/shared/chat/conversation/messages/wrapper/index.tsx +++ b/shared/chat/conversation/messages/wrapper/index.tsx @@ -53,7 +53,7 @@ const typeMap = { systemText: SystemText, systemUsersAddedToConversation: SystemUsersAddedToConv, text: Text, -} satisfies Partial>> as Record< +} satisfies Partial>> as unknown as Record< T.Chat.RenderMessageType, React.NamedExoticComponent | undefined > diff --git a/shared/common-adapters/markdown/maybe-mention/team.tsx b/shared/common-adapters/markdown/maybe-mention/team.tsx index e9b7f234f06b..a2b81144cb63 100644 --- a/shared/common-adapters/markdown/maybe-mention/team.tsx +++ b/shared/common-adapters/markdown/maybe-mention/team.tsx @@ -7,7 +7,7 @@ import Text, {type StylesTextCrossPlatform} from '@/common-adapters/text' import {Box2} from '@/common-adapters/box' import * as Styles from '@/styles' import TeamInfo from '@/profile/user/teams/teaminfo' -import type {MeasureRef} from 'common-adapters/measure-ref' +import type {MeasureRef} from '@/common-adapters/measure-ref' const Kb = {Box2, Styles, Text} diff --git a/shared/constants/chat2/message.tsx b/shared/constants/chat2/message.tsx index b0cdf7684d5b..42bbaa7aa796 100644 --- a/shared/constants/chat2/message.tsx +++ b/shared/constants/chat2/message.tsx @@ -5,7 +5,7 @@ import type * as ConvoConstants from '@/stores/convostate' import HiddenString from '@/util/hidden-string' import logger from '@/logger' import type * as MessageTypes from '@/constants/types/chat2/message' -import type {ServiceId} from 'util/platforms' +import type {ServiceId} from '@/util/platforms' import {noConversationIDKey} from '@/constants/types/chat2/common' import invert from 'lodash/invert' import {isIOS, isMobile} from '@/constants/platform' diff --git a/shared/package.json b/shared/package.json index 688526747830..f9a28d172ca9 100644 --- a/shared/package.json +++ b/shared/package.json @@ -49,7 +49,7 @@ "rn-test-ios": "xcodebuild test -project ./ios/Keybase.xcodeproj -scheme 'Keybase For Test' -destination 'platform=iOS Simulator,name=iPhone 6s,OS=9.3'", "rn-jsbuild-ios": "mkdir -p ios/dist && react-native bundle --platform ios --dev false --entry-file index.ios.js --bundle-output ios/dist/main.jsbundle --sourcemap-output ios/dist/main.jsbundle.sourcemap", "rn-jsbuild-android": "mkdir -p android/dist && react-native bundle --platform android --dev false --entry-file index.android.js --bundle-output android/dist/main.jsbundle --sourcemap-output android/dist/main.jsbundle.sourcemap", - "tsc": "node --max-old-space-size=4096 ./node_modules/.bin/tsc --project ./tsconfig.json ", + "tsc": "./node_modules/.bin/tsgo --project ./tsconfig.json", "modules": "yarn install --pure-lockfile --ignore-optional --ignore-scripts && yarn postinstall && cd node_modules/electron && yarn postinstall", "log-to-trace": "yarn _node desktop/yarn-helper/log-to-trace", "pod-clean": "pod cache clean --all ; rm -rf ios/build; rm -rf ios/Pods; rm -f ios/.xcode.env.local; rm -rf ../rnmodules/react-native-kb/node_modules", @@ -155,6 +155,7 @@ "@types/react-measure": "2.0.12", "@types/shallowequal": "1.1.5", "@types/webpack-env": "1.18.8", + "@typescript/native-preview": "^7.0.0-dev.20260218.1", "@welldone-software/why-did-you-render": "10.0.1", "babel-loader": "10.0.0", "babel-plugin-module-resolver": "5.0.2", diff --git a/shared/tsconfig.json b/shared/tsconfig.json index fec1880c1d14..299b0877ddf3 100644 --- a/shared/tsconfig.json +++ b/shared/tsconfig.json @@ -1,26 +1,18 @@ { "compilerOptions": { "allowJs": false, - "allowSyntheticDefaultImports": true, "allowUnreachableCode": false, "allowUnusedLabels": false, - "alwaysStrict": true, - "assumeChangesOnlyAffectDirectDependencies": false, - "baseUrl": ".", "checkJs": false, - "declaration": true, - "exactOptionalPropertyTypes": false, - "forceConsistentCasingInFileNames": false, "incremental": true, "isolatedModules": true, "jsx": "preserve", "lib": ["ESNext", "dom"], - "module": "commonjs", - "noErrorTruncation": true, + "module": "preserve", + "moduleResolution": "bundler", "noEmit": true, "noFallthroughCasesInSwitch": true, "noImplicitAny": true, - "noImplicitOverride": false, "noImplicitReturns": true, "noImplicitThis": true, "noPropertyAccessFromIndexSignature": true, diff --git a/shared/yarn.lock b/shared/yarn.lock index 0aab599e89da..7b7f49a9bef1 100644 --- a/shared/yarn.lock +++ b/shared/yarn.lock @@ -3716,6 +3716,54 @@ "@typescript-eslint/types" "8.55.0" eslint-visitor-keys "^4.2.1" +"@typescript/native-preview-darwin-arm64@7.0.0-dev.20260218.1": + version "7.0.0-dev.20260218.1" + resolved "https://registry.yarnpkg.com/@typescript/native-preview-darwin-arm64/-/native-preview-darwin-arm64-7.0.0-dev.20260218.1.tgz#7557ca06b54e9bf1c2b33f747c203c2f72175fff" + integrity sha512-ybxez4ClJU12TUvX/IxGPIQfS26+Zia7kbB1L4RH+G8yzYg90RPt4njfJkU2WxP70Hp59zS2copPkaBz5gUJkQ== + +"@typescript/native-preview-darwin-x64@7.0.0-dev.20260218.1": + version "7.0.0-dev.20260218.1" + resolved "https://registry.yarnpkg.com/@typescript/native-preview-darwin-x64/-/native-preview-darwin-x64-7.0.0-dev.20260218.1.tgz#446f82d01e7ca7d62f6a4e4c32e705f4a782a6b5" + integrity sha512-n9Ki8WTW82w6PlBTlrAQAjEUQB2V7C2oXrkN5U7ElwUH4FOostSFzZHuAdnPMbdzMx76P0pEw9FteYrLDA4m9g== + +"@typescript/native-preview-linux-arm64@7.0.0-dev.20260218.1": + version "7.0.0-dev.20260218.1" + resolved "https://registry.yarnpkg.com/@typescript/native-preview-linux-arm64/-/native-preview-linux-arm64-7.0.0-dev.20260218.1.tgz#1756a2b7b985ca9628e6cfb8f620815e4d253889" + integrity sha512-Osus82LSlwi1l3LoxLWKDuxh5E8JyWwkseBjr2n+TMaTuDPcRSzT8Jr4ywIp3NJpCUUV/LzR84i64jA6g8iVIw== + +"@typescript/native-preview-linux-arm@7.0.0-dev.20260218.1": + version "7.0.0-dev.20260218.1" + resolved "https://registry.yarnpkg.com/@typescript/native-preview-linux-arm/-/native-preview-linux-arm-7.0.0-dev.20260218.1.tgz#56f59f0089e7a8caa0d687a4d3a5a37ecdfeb2d6" + integrity sha512-WRPMvTztPatQ91UzYWSp82NT45JmjMgo/pVgZjXYEWdF2rwS4ejzR6DnHq30jXhEPnMah1bTeOzSWFF2kvXUmg== + +"@typescript/native-preview-linux-x64@7.0.0-dev.20260218.1": + version "7.0.0-dev.20260218.1" + resolved "https://registry.yarnpkg.com/@typescript/native-preview-linux-x64/-/native-preview-linux-x64-7.0.0-dev.20260218.1.tgz#213087e4ae85772956f90ba720083b1c73fa4eac" + integrity sha512-jcDhKCvhWQyMbra4MiqSgyUoSdM9mAiSkIdc80qScpk03aZOU+BZEmHz51S+fEn+8KRWuMuIHXM3sG3oX/EJZA== + +"@typescript/native-preview-win32-arm64@7.0.0-dev.20260218.1": + version "7.0.0-dev.20260218.1" + resolved "https://registry.yarnpkg.com/@typescript/native-preview-win32-arm64/-/native-preview-win32-arm64-7.0.0-dev.20260218.1.tgz#eaa7d8a3144f70fd7423eb34f0715387a9ab76ff" + integrity sha512-VmWvJ+TEuTPmZrhWe+buvvUvHbMyiD4ZLgxYPdYcJ3kRQlk2mD5lOq63ZISx1pDB8kYz5/R5xYKy/8gSIU5MgQ== + +"@typescript/native-preview-win32-x64@7.0.0-dev.20260218.1": + version "7.0.0-dev.20260218.1" + resolved "https://registry.yarnpkg.com/@typescript/native-preview-win32-x64/-/native-preview-win32-x64-7.0.0-dev.20260218.1.tgz#9d1bfe60693b3ed2e5f937f2f0fc878859b40955" + integrity sha512-9zfUrKV3xBog2tpIR9NZOags+QJZSj7v9Ek7KdSkVu978IJqF9RX7oa2xftX+eiHySfV5ZQ8r2fdhdbYBk+kMw== + +"@typescript/native-preview@^7.0.0-dev.20260218.1": + version "7.0.0-dev.20260218.1" + resolved "https://registry.yarnpkg.com/@typescript/native-preview/-/native-preview-7.0.0-dev.20260218.1.tgz#819df4977ba5badce94d6728b70c9b76a22bfe7e" + integrity sha512-hbGRXBk7abFvOQJk/7mc8K9q1kPkiyziyUsS8r8Hc1sLxrDFUbGgsW9p8qg67Xe1K6NUv/9UU2cdeIitUDexIQ== + optionalDependencies: + "@typescript/native-preview-darwin-arm64" "7.0.0-dev.20260218.1" + "@typescript/native-preview-darwin-x64" "7.0.0-dev.20260218.1" + "@typescript/native-preview-linux-arm" "7.0.0-dev.20260218.1" + "@typescript/native-preview-linux-arm64" "7.0.0-dev.20260218.1" + "@typescript/native-preview-linux-x64" "7.0.0-dev.20260218.1" + "@typescript/native-preview-win32-arm64" "7.0.0-dev.20260218.1" + "@typescript/native-preview-win32-x64" "7.0.0-dev.20260218.1" + "@ungap/structured-clone@^1.3.0": version "1.3.0" resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.3.0.tgz#d06bbb384ebcf6c505fde1c3d0ed4ddffe0aaff8" From beeb5403e340b2ab6053c60b5dbe61b92a037b4d Mon Sep 17 00:00:00 2001 From: chrisnojima Date: Wed, 18 Feb 2026 12:44:04 -0500 Subject: [PATCH 009/112] hmr fixes for stores (#28918) --- shared/constants/init/index.desktop.tsx | 110 +++++++++++++--------- shared/constants/init/shared.tsx | 35 ++++--- shared/desktop/renderer/main2.desktop.tsx | 5 +- shared/stores/convostate.tsx | 9 +- shared/stores/team-building.tsx | 4 +- 5 files changed, 101 insertions(+), 62 deletions(-) diff --git a/shared/constants/init/index.desktop.tsx b/shared/constants/init/index.desktop.tsx index 08d952d63514..cc2b8dae260c 100644 --- a/shared/constants/init/index.desktop.tsx +++ b/shared/constants/init/index.desktop.tsx @@ -252,7 +252,19 @@ const onInstallCachedDokan = async () => { } } +const _platformUnsubs: Array<() => void> = __DEV__ + ? ((globalThis as any).__hmr_platformUnsubs ??= []) + : [] + +let _oneTimeInitDone: boolean = __DEV__ + ? ((globalThis as any).__hmr_oneTimeInitDone ?? false) + : false + export const initPlatformListener = () => { + // HMR cleanup: unsubscribe old store subscriptions before re-subscribing + for (const unsub of _platformUnsubs) unsub() + _platformUnsubs.length = 0 + useConfigState.setState(s => { s.dispatch.defer.dumpLogsNative = dumpLogs s.dispatch.defer.showMainNative = wrapErrors(() => showMainWindow?.()) @@ -266,12 +278,12 @@ export const initPlatformListener = () => { }) }) - useConfigState.subscribe((s, old) => { + _platformUnsubs.push(useConfigState.subscribe((s, old) => { if (s.appFocused === old.appFocused) return useFSState.getState().dispatch.onChangedFocus(s.appFocused) - }) + })) - useConfigState.subscribe((s, old) => { + _platformUnsubs.push(useConfigState.subscribe((s, old) => { if (s.loggedIn !== old.loggedIn) { s.dispatch.osNetworkStatusChanged(navigator.onLine, 'notavailable', true) } @@ -310,32 +322,60 @@ export const initPlatformListener = () => { } ignorePromise(f()) } - }) + })) - const handleWindowFocusEvents = () => { - const handle = (appFocused: boolean) => { - if (skipAppFocusActions) { - console.log('Skipping app focus actions!') - } else { - useConfigState.getState().dispatch.changedFocus(appFocused) + // One-time setup: window event listeners and input monitor (skip on HMR to avoid duplicates) + if (!_oneTimeInitDone) { + _oneTimeInitDone = true + if (__DEV__) (globalThis as any).__hmr_oneTimeInitDone = true + + const handleWindowFocusEvents = () => { + const handle = (appFocused: boolean) => { + if (skipAppFocusActions) { + console.log('Skipping app focus actions!') + } else { + useConfigState.getState().dispatch.changedFocus(appFocused) + } } + window.addEventListener('focus', () => handle(true)) + window.addEventListener('blur', () => handle(false)) } - window.addEventListener('focus', () => handle(true)) - window.addEventListener('blur', () => handle(false)) - } - handleWindowFocusEvents() + handleWindowFocusEvents() + + const setupReachabilityWatcher = () => { + window.addEventListener('online', () => + useConfigState.getState().dispatch.osNetworkStatusChanged(true, 'notavailable') + ) + window.addEventListener('offline', () => + useConfigState.getState().dispatch.osNetworkStatusChanged(false, 'notavailable') + ) + } + setupReachabilityWatcher() - const setupReachabilityWatcher = () => { - window.addEventListener('online', () => - useConfigState.getState().dispatch.osNetworkStatusChanged(true, 'notavailable') - ) - window.addEventListener('offline', () => - useConfigState.getState().dispatch.osNetworkStatusChanged(false, 'notavailable') - ) + if (isLinux) { + useConfigState.getState().dispatch.initUseNativeFrame() + } + useConfigState.getState().dispatch.initNotifySound() + useConfigState.getState().dispatch.initForceSmallNav() + useConfigState.getState().dispatch.initOpenAtLogin() + useConfigState.getState().dispatch.initAppUpdateLoop() + + const initializeInputMonitor = () => { + const inputMonitor = new InputMonitor() + inputMonitor.notifyActive = (userActive: boolean) => { + if (skipAppFocusActions) { + console.log('Skipping app focus actions!') + } else { + useConfigState.getState().dispatch.setActive(userActive) + // let node thread save file + activeChanged?.(Date.now(), userActive) + } + } + } + initializeInputMonitor() } - setupReachabilityWatcher() - useDaemonState.subscribe((s, old) => { + _platformUnsubs.push(useDaemonState.subscribe((s, old) => { if (s.handshakeVersion !== old.handshakeVersion) { if (!isWindows) return @@ -368,15 +408,7 @@ export const initPlatformListener = () => { tab: undefined, }) } - }) - - if (isLinux) { - useConfigState.getState().dispatch.initUseNativeFrame() - } - useConfigState.getState().dispatch.initNotifySound() - useConfigState.getState().dispatch.initForceSmallNav() - useConfigState.getState().dispatch.initOpenAtLogin() - useConfigState.getState().dispatch.initAppUpdateLoop() + })) useProfileState.setState(s => { s.dispatch.editAvatar = () => { @@ -386,20 +418,6 @@ export const initPlatformListener = () => { } }) - const initializeInputMonitor = () => { - const inputMonitor = new InputMonitor() - inputMonitor.notifyActive = (userActive: boolean) => { - if (skipAppFocusActions) { - console.log('Skipping app focus actions!') - } else { - useConfigState.getState().dispatch.setActive(userActive) - // let node thread save file - activeChanged?.(Date.now(), userActive) - } - } - } - initializeInputMonitor() - useDaemonState.setState(s => { s.dispatch.onRestartHandshakeNative = () => { const {handshakeFailedReason} = useDaemonState.getState() diff --git a/shared/constants/init/shared.tsx b/shared/constants/init/shared.tsx index 830d47dd9019..8865e381c0a4 100644 --- a/shared/constants/init/shared.tsx +++ b/shared/constants/init/shared.tsx @@ -53,9 +53,15 @@ import {useRouterState} from '@/stores/router2' import * as Util from '@/constants/router2' import {setConvoDefer} from '@/stores/convostate' -let _emitStartupOnLoadDaemonConnectedOnce = false -let _devicesLoaded = false -let _gitLoaded = false +let _emitStartupOnLoadDaemonConnectedOnce: boolean = __DEV__ + ? ((globalThis as any).__hmr_startupOnce ?? false) + : false +let _devicesLoaded: boolean = __DEV__ ? ((globalThis as any).__hmr_devicesLoaded ?? false) : false +let _gitLoaded: boolean = __DEV__ ? ((globalThis as any).__hmr_gitLoaded ?? false) : false + +const _sharedUnsubs: Array<() => void> = __DEV__ + ? ((globalThis as any).__hmr_sharedUnsubs ??= []) + : [] export const onEngineConnected = () => { { @@ -397,6 +403,10 @@ export const initSettingsCallbacks = () => { } export const initSharedSubscriptions = () => { + // HMR cleanup: unsubscribe old store subscriptions before re-subscribing + for (const unsub of _sharedUnsubs) unsub() + _sharedUnsubs.length = 0 + setConvoDefer({ chatBlockButtonsMapHas: teamID => storeRegistry.getState('chat').blockButtonsMap.has(teamID), @@ -423,7 +433,7 @@ export const initSharedSubscriptions = () => { usersGetBio: username => storeRegistry.getState('users').dispatch.getBio(username), }) - useConfigState.subscribe((s, old) => { + _sharedUnsubs.push(useConfigState.subscribe((s, old) => { if (s.loadOnStartPhase !== old.loadOnStartPhase) { if (s.loadOnStartPhase === 'startupOrReloginButNotInARush') { const getFollowerInfo = () => { @@ -546,9 +556,9 @@ export const initSharedSubscriptions = () => { const cs = storeRegistry.getConvoState(getSelectedConversation()) cs.dispatch.markThreadAsRead() } - }) + })) - useDaemonState.subscribe((s, old) => { + _sharedUnsubs.push(useDaemonState.subscribe((s, old) => { if (s.handshakeVersion !== old.handshakeVersion) { useDarkModeState.getState().dispatch.loadDarkPrefs() storeRegistry.getState('chat').dispatch.loadStaticConfig() @@ -587,13 +597,14 @@ export const initSharedSubscriptions = () => { if (s.handshakeState === 'done') { if (!_emitStartupOnLoadDaemonConnectedOnce) { _emitStartupOnLoadDaemonConnectedOnce = true + if (__DEV__) (globalThis as any).__hmr_startupOnce = true useConfigState.getState().dispatch.loadOnStart('connectedToDaemonForFirstTime') } } } - }) + })) - useProvisionState.subscribe((s, old) => { + _sharedUnsubs.push(useProvisionState.subscribe((s, old) => { if (s.startProvisionTrigger !== old.startProvisionTrigger) { useConfigState.getState().dispatch.setLoginError() useConfigState.getState().dispatch.resetRevokedSelf() @@ -605,9 +616,9 @@ export const initSharedSubscriptions = () => { } ignorePromise(f()) } - }) + })) - useRouterState.subscribe((s, old) => { + _sharedUnsubs.push(useRouterState.subscribe((s, old) => { const next = s.navState as Util.NavState const prev = old.navState as Util.NavState if (prev === next) return @@ -684,7 +695,7 @@ export const initSharedSubscriptions = () => { } storeRegistry.getState('chat').dispatch.onRouteChanged(prev, next) - }) + })) initAutoResetCallbacks() initChat2Callbacks() @@ -721,6 +732,7 @@ export const _onEngineIncoming = (action: EngineGen.Actions) => { const hasValue = (newDevices?.length ?? 0) + (revokedDevices?.length ?? 0) > 0 if (_devicesLoaded || hasValue) { _devicesLoaded = true + if (__DEV__) (globalThis as any).__hmr_devicesLoaded = true const {useDevicesState} = require('@/stores/devices') as typeof UseDevicesStateType useDevicesState.getState().dispatch.onEngineIncomingImpl(action) } @@ -728,6 +740,7 @@ export const _onEngineIncoming = (action: EngineGen.Actions) => { const badges = new Set(badgeState.newGitRepoGlobalUniqueIDs) if (_gitLoaded || badges.size) { _gitLoaded = true + if (__DEV__) (globalThis as any).__hmr_gitLoaded = true const {useGitState} = require('@/stores/git') as typeof UseGitStateType useGitState.getState().dispatch.onEngineIncomingImpl(action) } diff --git a/shared/desktop/renderer/main2.desktop.tsx b/shared/desktop/renderer/main2.desktop.tsx index e2d29cd73822..c42d21a86e53 100644 --- a/shared/desktop/renderer/main2.desktop.tsx +++ b/shared/desktop/renderer/main2.desktop.tsx @@ -347,8 +347,9 @@ const setupHMR = () => { const load = () => { if (global.DEBUGLoaded) { - // only load once - console.log('Bail on load() on HMR') + // HMR detected — reinit subscriptions on new store instances + console.log('HMR: reinitializing store subscriptions') + initPlatformListener() return } global.DEBUGLoaded = true diff --git a/shared/stores/convostate.tsx b/shared/stores/convostate.tsx index 372a1e9cb9f1..7c114a5ff3e5 100644 --- a/shared/stores/convostate.tsx +++ b/shared/stores/convostate.tsx @@ -431,10 +431,13 @@ const stubDefer: ConvoState['dispatch']['defer'] = { }, } -let convoDeferImpl: ConvoState['dispatch']['defer'] | undefined +let convoDeferImpl: ConvoState['dispatch']['defer'] | undefined = __DEV__ + ? (globalThis as any).__hmr_convoDeferImpl + : undefined export const setConvoDefer = (impl: ConvoState['dispatch']['defer']) => { convoDeferImpl = impl + if (__DEV__) (globalThis as any).__hmr_convoDeferImpl = impl for (const store of chatStores.values()) { const s = store.getState() store.setState({ @@ -3313,7 +3316,9 @@ const createSlice = (): Z.ImmerStateCreator => (set, get) => { } type MadeStore = UseBoundStore> -export const chatStores = new Map() +export const chatStores: Map = __DEV__ + ? ((globalThis as any).__hmr_chatStores ??= new Map()) + : new Map() export const clearChatStores = () => { chatStores.clear() diff --git a/shared/stores/team-building.tsx b/shared/stores/team-building.tsx index fb68799d16c0..cab98fa5d93d 100644 --- a/shared/stores/team-building.tsx +++ b/shared/stores/team-building.tsx @@ -497,7 +497,9 @@ const createSlice: Z.ImmerStateCreator = (set, get) => { } type MadeStore = UseBoundStore> -export const TBstores = new Map() +export const TBstores: Map = __DEV__ + ? ((globalThis as any).__hmr_TBstores ??= new Map()) + : new Map() registerDebugClear(() => { TBstores.clear() From a33a9c1d093e262e5131a0d2d5c7d1d1092333c4 Mon Sep 17 00:00:00 2001 From: chrisnojima Date: Wed, 18 Feb 2026 16:31:51 -0500 Subject: [PATCH 010/112] move from box to box2 (#28919) * move from box to box2 --- shared/chat/audio/audio-send.native.tsx | 6 +- shared/chat/avatars.tsx | 16 ++-- shared/chat/blocking/block-modal.tsx | 12 +-- .../attachment-fullscreen/index.desktop.tsx | 20 ++--- shared/chat/conversation/command-markdown.tsx | 4 +- shared/chat/conversation/command-status.tsx | 4 +- shared/chat/conversation/error.tsx | 11 +-- .../chat/conversation/giphy/index.desktop.tsx | 8 +- .../normal2/platform-input.desktop.tsx | 29 +++--- .../input-area/normal2/typing.tsx | 9 +- .../chat/conversation/input-area/preview.tsx | 6 +- .../conversation/list-area/index.native.tsx | 4 +- .../conversation/messages/attachment/file.tsx | 31 +++---- .../chat/conversation/messages/emoji-row.tsx | 12 +-- .../messages/message-popup/header.tsx | 10 +-- .../chat/conversation/messages/reset-user.tsx | 5 +- .../messages/retention-notice.tsx | 11 +-- .../messages/special-top-message.tsx | 25 +++--- .../system-added-to-team/container.tsx | 4 +- .../chat/conversation/messages/text/reply.tsx | 4 +- .../unfurl-list/image/video.desktop.tsx | 4 +- .../unfurl/unfurl-list/image/video.native.tsx | 4 +- .../index.desktop.tsx | 10 +-- .../index.native.tsx | 15 ++-- .../conversation/messages/wrapper/wrapper.tsx | 4 +- shared/chat/conversation/pinned-message.tsx | 4 +- .../rekey/participant-rekey.desktop.tsx | 55 ++++++------ .../rekey/participant-rekey.native.tsx | 32 +++---- .../conversation/rekey/you-rekey.desktop.tsx | 13 ++- .../conversation/rekey/you-rekey.native.tsx | 14 ++- shared/chat/conversation/reply-preview.tsx | 8 +- shared/chat/conversation/you-are-reset.tsx | 24 +++-- shared/chat/create-channel/index.desktop.tsx | 19 ++-- shared/chat/create-channel/index.native.tsx | 8 +- shared/chat/delete-history-warning.tsx | 16 ++-- shared/chat/emoji-picker/container.tsx | 3 +- shared/chat/emoji-picker/index.tsx | 7 +- shared/chat/emoji-picker/skin-tone-picker.tsx | 17 ++-- shared/chat/inbox/index.desktop.tsx | 7 +- shared/chat/inbox/index.native.tsx | 9 +- shared/chat/inbox/new-chat-button.tsx | 8 +- shared/chat/inbox/row/big-team-channel.tsx | 2 +- shared/chat/inbox/row/big-team-header.tsx | 2 +- shared/chat/inbox/row/start-new-chat.tsx | 6 +- shared/chat/maybe-popup.tsx | 18 +++- shared/common-adapters/animation.desktop.tsx | 6 +- shared/common-adapters/animation.native.tsx | 6 +- .../common-adapters/avatar/index.native.tsx | 17 ++-- shared/common-adapters/back-button.native.tsx | 10 +-- shared/common-adapters/badge.tsx | 3 +- shared/common-adapters/banner.tsx | 6 +- shared/common-adapters/box-grow.tsx | 10 ++- shared/common-adapters/box.d.ts | 25 +----- shared/common-adapters/box.desktop.tsx | 15 +--- shared/common-adapters/box.native.tsx | 3 - shared/common-adapters/button-bar.tsx | 31 ++++--- shared/common-adapters/button.tsx | 19 ++-- shared/common-adapters/checkbox.desktop.tsx | 9 +- .../common-adapters/choice-list.desktop.tsx | 55 ++++++------ shared/common-adapters/choice-list.native.tsx | 27 +++--- .../common-adapters/clickable-box.native.tsx | 5 +- .../common-adapters/copyable-text.native.tsx | 74 ++++++++------- shared/common-adapters/divider.tsx | 5 +- shared/common-adapters/dropdown.tsx | 13 ++- .../floating-box/index.native.tsx | 9 +- .../relative-floating-box.desktop.tsx | 8 +- .../menu-layout/index.desktop.tsx | 36 ++++---- .../menu-layout/index.native.tsx | 17 ++-- .../header-hoc/index.desktop.tsx | 23 ++--- .../header-hoc/index.native.tsx | 50 +++++------ shared/common-adapters/index.d.ts | 2 +- shared/common-adapters/info-note.tsx | 6 +- shared/common-adapters/list-item.desktop.tsx | 34 +++---- shared/common-adapters/list-item.native.tsx | 34 ++++--- .../common-adapters/loading-line.desktop.tsx | 10 +-- shared/common-adapters/markdown/react.tsx | 10 +-- shared/common-adapters/modal.tsx | 9 +- shared/common-adapters/modal2.tsx | 7 +- shared/common-adapters/name-with-icon.tsx | 87 +++++++++--------- shared/common-adapters/new-input.tsx | 6 +- .../common-adapters/overlay/index.native.tsx | 8 +- shared/common-adapters/placeholder.tsx | 5 +- shared/common-adapters/platform-icon.tsx | 6 +- .../common-adapters/popup-dialog.desktop.tsx | 9 ++ .../common-adapters/popup-dialog.native.tsx | 46 +++++----- shared/common-adapters/profile-card.tsx | 3 +- shared/common-adapters/progress-bar.tsx | 9 +- shared/common-adapters/save-indicator.tsx | 9 +- shared/common-adapters/search-filter.tsx | 3 +- shared/common-adapters/switch.tsx | 9 +- shared/common-adapters/tabs.tsx | 7 +- shared/common-adapters/timeline-marker.tsx | 14 +-- shared/common-adapters/toast.native.tsx | 8 +- shared/common-adapters/video.native.tsx | 11 +-- shared/common-adapters/wave-button.tsx | 7 +- .../common-adapters/with-tooltip.native.tsx | 9 +- shared/crypto/input.tsx | 4 +- shared/devices/device-page.tsx | 16 ++-- .../container.tsx | 6 +- .../kext-permission-popup.tsx | 12 +-- shared/fs/browser/rows/common.tsx | 7 +- shared/fs/browser/rows/editing.tsx | 14 ++- shared/fs/browser/rows/placeholder.tsx | 6 +- shared/fs/browser/rows/rows.tsx | 7 +- shared/fs/browser/rows/tlf.tsx | 6 +- shared/fs/common/item-icon.tsx | 28 +++--- shared/fs/common/kbfs-path.tsx | 5 +- shared/fs/common/path-item-action/header.tsx | 4 +- shared/fs/common/path-item-info.tsx | 3 +- shared/fs/common/path-status-icon.tsx | 2 +- shared/fs/common/pie-slice.tsx | 16 ++-- shared/fs/common/sfmi-popup.tsx | 4 +- shared/fs/filepreview/bare-preview.tsx | 21 ++--- shared/fs/filepreview/view.tsx | 5 +- shared/fs/footer/download.tsx | 9 +- shared/fs/footer/downloads.tsx | 2 +- shared/fs/footer/upload.native.tsx | 12 +-- shared/fs/nav-header/mobile-header.tsx | 2 +- shared/fs/top-bar/index.tsx | 2 +- shared/git/delete-repo.tsx | 16 ++-- shared/git/new-repo.tsx | 22 ++--- shared/git/row.tsx | 90 +++++++++---------- shared/git/select-channel.tsx | 5 +- shared/login/forms/container.native.tsx | 3 +- shared/login/relogin/dropdown.native.tsx | 16 ++-- shared/login/relogin/index.native.tsx | 16 ++-- shared/login/user-card/index.native.tsx | 22 ++--- shared/menubar/files.desktop.tsx | 4 +- shared/menubar/index.desktop.tsx | 36 ++++---- shared/package.json | 2 +- shared/people/follow-suggestions.tsx | 5 +- shared/people/index.shared.tsx | 4 +- shared/people/item.tsx | 21 +++-- shared/pinentry/index.desktop.tsx | 9 +- shared/profile/edit-avatar/index.desktop.tsx | 8 +- shared/profile/edit-avatar/index.native.tsx | 8 +- shared/profile/generic/proofs-list.tsx | 10 +-- shared/profile/generic/shared.tsx | 3 +- shared/profile/pgp/import/index.desktop.tsx | 6 +- shared/profile/post-proof.tsx | 4 +- shared/profile/revoke.tsx | 17 ++-- shared/profile/showcase-team-offer.tsx | 4 +- shared/provision/code-page/qr-scan/lines.tsx | 24 +++-- shared/router-v2/header/index.desktop.tsx | 4 +- shared/settings/account/email-phone-row.tsx | 3 +- shared/settings/advanced.tsx | 8 +- shared/settings/archive/index.tsx | 4 +- shared/settings/chat.tsx | 10 +-- shared/settings/common.desktop.tsx | 4 +- shared/settings/contacts-joined.tsx | 4 +- shared/settings/db-nuke.confirm.tsx | 15 ++-- shared/settings/display.tsx | 6 +- shared/settings/files/index.desktop.tsx | 12 +-- .../invite-generated/index.desktop.tsx | 21 ++--- shared/settings/invites/index.desktop.tsx | 48 +++++----- .../settings/notifications/index.native.tsx | 13 +-- shared/settings/notifications/render.tsx | 6 +- shared/settings/proxy.tsx | 16 ++-- shared/settings/subheading.desktop.tsx | 4 +- shared/team-building/go-button.tsx | 4 +- shared/teams/channel/index.tsx | 8 +- shared/teams/channel/rows.tsx | 9 +- shared/teams/channel/tabs.tsx | 5 +- shared/teams/common/selection-popup.tsx | 4 +- .../teams/confirm-modals/confirm-kick-out.tsx | 4 +- .../confirm-remove-from-channel.tsx | 4 +- shared/teams/emojis/add-emoji.tsx | 4 +- .../teams/invite-by-contact/index.native.tsx | 38 ++++---- shared/teams/invite-by-email.tsx | 39 ++++---- shared/teams/join-team/container.tsx | 4 +- shared/teams/join-team/join-from-invite.tsx | 6 +- shared/teams/main/banner.tsx | 19 ++-- shared/teams/main/footer.tsx | 10 +-- shared/teams/main/index.tsx | 4 +- shared/teams/team/index.tsx | 8 +- shared/teams/team/member/add-to-channels.tsx | 6 +- shared/teams/team/new-header.tsx | 4 +- shared/teams/team/rows/bot-row/bot.tsx | 33 +++---- shared/teams/team/rows/emoji-row/item.tsx | 4 +- shared/teams/team/rows/member-row.tsx | 6 +- .../team/settings-tab/open-team-warning.tsx | 6 +- .../team/settings-tab/retention/index.tsx | 18 ++-- .../team/settings-tab/retention/warning.tsx | 10 +-- shared/teams/team/tabs.tsx | 4 +- shared/tracker2/assertion.tsx | 2 +- shared/unlock-folders/success.desktop.tsx | 4 +- shared/whats-new/icon/index.tsx | 4 +- 187 files changed, 1105 insertions(+), 1282 deletions(-) diff --git a/shared/chat/audio/audio-send.native.tsx b/shared/chat/audio/audio-send.native.tsx index ba6ca44a70e1..24038957c46f 100644 --- a/shared/chat/audio/audio-send.native.tsx +++ b/shared/chat/audio/audio-send.native.tsx @@ -41,9 +41,9 @@ const AudioSend = (props: Props) => { - + - + {player} @@ -63,9 +63,7 @@ const styles = Kb.Styles.styleSheetCreate(() => ({ paddingRight: Kb.Styles.globalMargins.tiny, }, icon: { - alignItems: 'center', height: 32, - justifyContent: 'center', marginRight: Kb.Styles.globalMargins.tiny, width: 32, }, diff --git a/shared/chat/avatars.tsx b/shared/chat/avatars.tsx index 2a156da743bb..57b367599cbf 100644 --- a/shared/chat/avatars.tsx +++ b/shared/chat/avatars.tsx @@ -28,7 +28,7 @@ const OverlayIcon = React.memo(function OverlayIcon(p: { if (!type) return null return ( - + - + ) }) @@ -102,12 +102,12 @@ const Avatars = React.memo(function Avatars(p: Props) { if (!participantTwo) { return ( - - + + - - + + ) } @@ -144,10 +144,10 @@ const TeamAvatar = React.memo(function TeamAvatar(p: { }) { const {teamname, size, isSelected, isMuted, isHovered} = p return ( - + - + ) }) diff --git a/shared/chat/blocking/block-modal.tsx b/shared/chat/blocking/block-modal.tsx index c29fa87e9fa5..0bb637314769 100644 --- a/shared/chat/blocking/block-modal.tsx +++ b/shared/chat/blocking/block-modal.tsx @@ -52,7 +52,7 @@ const CheckboxRow = (props: CheckboxRowProps) => ( onClick={() => props.onCheck(!props.checked)} style={styles.shrink} /> - + {props.info && ( { style={styles.radioButton} /> ))} - { onChangeText={props.setExtraNotes} value={props.extraNotes} /> - + {showIncludeTranscript && ( - + - + ) } diff --git a/shared/chat/conversation/attachment-fullscreen/index.desktop.tsx b/shared/chat/conversation/attachment-fullscreen/index.desktop.tsx index 6f80c5700830..7ee2b45988c0 100644 --- a/shared/chat/conversation/attachment-fullscreen/index.desktop.tsx +++ b/shared/chat/conversation/attachment-fullscreen/index.desktop.tsx @@ -13,7 +13,7 @@ type ArrowProps = { const Arrow = (props: ArrowProps) => { const {left, onClick} = props return ( - { e.stopPropagation() @@ -26,7 +26,7 @@ const Arrow = (props: ArrowProps) => { color={Kb.Styles.globalColors.white} style={Kb.Styles.collapseStyles([styles.arrow, left && styles.arrowLeft, !left && styles.arrowRight])} /> - + ) } @@ -80,8 +80,8 @@ const Fullscreen = React.memo(function Fullscreen(p: Props) { return ( - - + + {title} @@ -96,7 +96,7 @@ const Fullscreen = React.memo(function Fullscreen(p: Props) { onClick={showPopup} /> {popup} - + {path && ( @@ -126,7 +126,7 @@ const Fullscreen = React.memo(function Fullscreen(p: Props) { )} - + {!!progressLabel && ( )} - - + + ) }) @@ -183,7 +183,6 @@ const styles = Kb.Styles.styleSheetCreate( width: 36, }, }), - container: {...Kb.Styles.globalStyles.flexBoxColumn, height: '100%', width: '100%'}, contentsFit: { ...Kb.Styles.globalStyles.flexBoxRow, flex: 1, @@ -192,12 +191,9 @@ const styles = Kb.Styles.styleSheetCreate( }, error: {color: Kb.Styles.globalColors.redDark}, headerFooter: { - ...Kb.Styles.globalStyles.flexBoxRow, - alignItems: 'center', height: 32, paddingLeft: Kb.Styles.globalMargins.tiny, paddingRight: Kb.Styles.globalMargins.tiny, - width: '100%', }, link: Kb.Styles.platformStyles({ isElectron: {color: Kb.Styles.globalColors.black_50, cursor: 'pointer'}, diff --git a/shared/chat/conversation/command-markdown.tsx b/shared/chat/conversation/command-markdown.tsx index 10c20f1c5509..f40010babebf 100644 --- a/shared/chat/conversation/command-markdown.tsx +++ b/shared/chat/conversation/command-markdown.tsx @@ -6,7 +6,7 @@ const CommandMarkdown = () => { const body = md?.body ?? '' const title = md?.title ?? undefined return ( - + {!!title && ( {title} @@ -17,7 +17,7 @@ const CommandMarkdown = () => { {body} - + ) } diff --git a/shared/chat/conversation/command-status.tsx b/shared/chat/conversation/command-status.tsx index dc67888d461c..4181640af8b6 100644 --- a/shared/chat/conversation/command-status.tsx +++ b/shared/chat/conversation/command-status.tsx @@ -39,7 +39,7 @@ const Container = () => { } return ( - + { })} - + ) } diff --git a/shared/chat/conversation/error.tsx b/shared/chat/conversation/error.tsx index 0c9266bb235c..e098c723b111 100644 --- a/shared/chat/conversation/error.tsx +++ b/shared/chat/conversation/error.tsx @@ -4,15 +4,15 @@ import * as Kb from '@/common-adapters' const ConversationError = () => { const text = Chat.useChatContext(s => s.meta.snippet ?? '') return ( - + There was an error loading this conversation. The error is: - + - - + + ) } @@ -21,12 +21,9 @@ const styles = Kb.Styles.styleSheetCreate( ({ body: {marginTop: Kb.Styles.globalMargins.small}, container: { - ...Kb.Styles.globalStyles.flexBoxColumn, padding: Kb.Styles.globalMargins.medium, - width: '100%', }, errorBox: { - ...Kb.Styles.globalStyles.flexBoxRow, marginTop: Kb.Styles.globalMargins.small, }, errorText: {flexGrow: 1}, diff --git a/shared/chat/conversation/giphy/index.desktop.tsx b/shared/chat/conversation/giphy/index.desktop.tsx index 4581d7dcb51b..6e235c6b048a 100644 --- a/shared/chat/conversation/giphy/index.desktop.tsx +++ b/shared/chat/conversation/giphy/index.desktop.tsx @@ -28,7 +28,7 @@ const GiphySearch = () => { ) } return ( - + { const margin = -margins[index]! / 2 - 1 return p.targetUrl ? ( - + { url={p.previewUrl} width={scaledWidth(p.previewWidth)} /> - + ) : null })} @@ -84,7 +84,7 @@ const GiphySearch = () => { ))} - + ) } diff --git a/shared/chat/conversation/input-area/normal2/platform-input.desktop.tsx b/shared/chat/conversation/input-area/normal2/platform-input.desktop.tsx index 88026a913e7e..ae52c4c6f365 100644 --- a/shared/chat/conversation/input-area/normal2/platform-input.desktop.tsx +++ b/shared/chat/conversation/input-area/normal2/platform-input.desktop.tsx @@ -137,9 +137,9 @@ const GiphyButton = React.memo(function GiphyButton() { const onGiphyToggle = toggleGiphyPrefill return ( - + - + ) }) @@ -184,21 +184,21 @@ const FileButton = React.memo(function FileButton(p: { ) return ( - + - + ) }) const Footer = () => { return ( - + {`*bold*, _italics_, \`code\`, >quote, !>spoiler - + ) } @@ -418,8 +418,10 @@ const PlatformInput = React.memo(function PlatformInput(p: Props) { <> {popup} - - + - +
- + ) @@ -471,9 +473,7 @@ const styles = Kb.Styles.styleSheetCreate( ({ cancelEditingBtn: {margin: Kb.Styles.globalMargins.xtiny}, container: { - ...Kb.Styles.globalStyles.flexBoxColumn, backgroundColor: Kb.Styles.globalColors.white, - width: '100%', }, explodingIconContainer: Kb.Styles.platformStyles({ common: { @@ -501,8 +501,6 @@ const styles = Kb.Styles.styleSheetCreate( textAlign: 'right', }, footerContainer: { - ...Kb.Styles.globalStyles.flexBoxRow, - alignItems: 'flex-start', justifyContent: 'space-between', }, hidden: {display: 'none'}, @@ -533,8 +531,7 @@ const styles = Kb.Styles.styleSheetCreate( }, inputEditing: {color: Kb.Styles.globalColors.blackOrBlack}, inputWrapper: { - ...Kb.Styles.globalStyles.flexBoxRow, - alignItems: 'flex-end', + alignSelf: 'stretch', backgroundColor: Kb.Styles.globalColors.white, borderColor: Kb.Styles.globalColors.black_20, borderRadius: 4, diff --git a/shared/chat/conversation/input-area/normal2/typing.tsx b/shared/chat/conversation/input-area/normal2/typing.tsx index adfc08867ee1..9b80f6647870 100644 --- a/shared/chat/conversation/input-area/normal2/typing.tsx +++ b/shared/chat/conversation/input-area/normal2/typing.tsx @@ -56,18 +56,18 @@ const Typing = React.memo(function Typing() { }) ) return ( - + {names.size > 0 && ( - + - + )} {names.size > 0 && ( )} - + ) }) @@ -91,7 +91,6 @@ const styles = Kb.Styles.styleSheetCreate( opacity: 1, }, isMobile: { - ...Kb.Styles.globalStyles.flexBoxRow, alignItems: 'flex-end', backgroundColor: Kb.Styles.globalColors.white, height: mobileTypingContainerHeight, diff --git a/shared/chat/conversation/input-area/preview.tsx b/shared/chat/conversation/input-area/preview.tsx index 64e25419c5f8..41f2eef83300 100644 --- a/shared/chat/conversation/input-area/preview.tsx +++ b/shared/chat/conversation/input-area/preview.tsx @@ -19,7 +19,7 @@ const Preview = () => { } return ( - + Would you like to join #{channelname}? @@ -38,7 +38,7 @@ const Preview = () => { {clicked === 'join' ? 'Joining...' : 'Leaving...'} )} - + ) } @@ -46,8 +46,6 @@ const styles = Kb.Styles.styleSheetCreate( () => ({ container: { - ...Kb.Styles.globalStyles.flexBoxColumn, - alignItems: 'center', backgroundColor: Kb.Styles.globalColors.blue, paddingBottom: Kb.Styles.globalMargins.tiny, paddingTop: Kb.Styles.globalMargins.tiny, diff --git a/shared/chat/conversation/list-area/index.native.tsx b/shared/chat/conversation/list-area/index.native.tsx index 4a4ea8308087..b932d1950dfe 100644 --- a/shared/chat/conversation/list-area/index.native.tsx +++ b/shared/chat/conversation/list-area/index.native.tsx @@ -272,7 +272,7 @@ const ConversationList = React.memo(function ConversationList() { - + {jumpToRecent} {debugWhichList} - + diff --git a/shared/chat/conversation/messages/attachment/file.tsx b/shared/chat/conversation/messages/attachment/file.tsx index bfcbe3bbc11f..70b30f78aeb1 100644 --- a/shared/chat/conversation/messages/attachment/file.tsx +++ b/shared/chat/conversation/messages/attachment/file.tsx @@ -127,7 +127,9 @@ const FileContainer = React.memo(function FileContainer(p: OwnProps) { return ( - @@ -171,7 +173,7 @@ const FileContainer = React.memo(function FileContainer(p: OwnProps) { {!Kb.Styles.isMobile && isSaltpackFile && operation && ( - + onSaltpackFileOpen(fileName, operation)} /> - + )} {!!arrowColor && ( - + - + )} {!!progressLabel && ( - + {progressLabel} {hasProgress && } - + )} {!!errorMsg && ( - + Failed to download.{' '} Retry - + )} {onShowInFinder && ( Show in {Kb.Styles.fileUIName} )} - + ) }) @@ -218,10 +220,6 @@ const styles = Kb.Styles.styleSheetCreate( () => ({ containerStyle: Kb.Styles.platformStyles({ - common: { - ...Kb.Styles.globalStyles.flexBoxColumn, - width: '100%', - }, isElectron: {...Kb.Styles.desktopStyles.clickable}, }), downloadedIcon: { @@ -230,7 +228,6 @@ const styles = Kb.Styles.styleSheetCreate( top: 1, }, downloadedIconWrapperStyle: { - ...Kb.Styles.globalStyles.flexBoxCenter, ...Kb.Styles.padding(3, 0, 3, 3), borderRadius: 20, bottom: 0, @@ -251,10 +248,6 @@ const styles = Kb.Styles.styleSheetCreate( }, }), linkStyle: {color: Kb.Styles.globalColors.black_50}, - progressContainerStyle: { - ...Kb.Styles.globalStyles.flexBoxRow, - alignItems: 'center', - }, progressLabelStyle: { color: Kb.Styles.globalColors.black_50, marginRight: Kb.Styles.globalMargins.tiny, diff --git a/shared/chat/conversation/messages/emoji-row.tsx b/shared/chat/conversation/messages/emoji-row.tsx index ffbb461d8082..813264d650fd 100644 --- a/shared/chat/conversation/messages/emoji-row.tsx +++ b/shared/chat/conversation/messages/emoji-row.tsx @@ -69,28 +69,28 @@ const EmojiRowContainer = React.memo(function EmojiRowContainer(p: OwnProps) { - - + {!!onReply && ( - + - + )} {!!onForward && ( - - + )} {showingPicker && ( diff --git a/shared/chat/conversation/messages/message-popup/header.tsx b/shared/chat/conversation/messages/message-popup/header.tsx index 7c0c2d864558..49d1c9e4cba2 100644 --- a/shared/chat/conversation/messages/message-popup/header.tsx +++ b/shared/chat/conversation/messages/message-popup/header.tsx @@ -64,10 +64,10 @@ const MessagePopupHeader = (props: Props) => { ) return ( - + {Kb.Styles.isMobile ? null : } {Kb.Styles.isMobile ? null : ( - + { > ENCRYPTED & SIGNED - + )} @@ -134,7 +134,7 @@ const MessagePopupHeader = (props: Props) => { width: '100%', }} /> - + ) } @@ -144,8 +144,6 @@ const styles = Kb.Styles.styleSheetCreate( alignItemsCenter: {alignItems: 'center'}, headerContainer: Kb.Styles.platformStyles({ common: { - ...Kb.Styles.globalStyles.flexBoxColumn, - alignItems: 'center', paddingTop: Kb.Styles.globalMargins.tiny, width: '100%', }, diff --git a/shared/chat/conversation/messages/reset-user.tsx b/shared/chat/conversation/messages/reset-user.tsx index 5ef6bde4ba85..23e0f1923e9c 100644 --- a/shared/chat/conversation/messages/reset-user.tsx +++ b/shared/chat/conversation/messages/reset-user.tsx @@ -33,14 +33,14 @@ const ResetUser = () => { } - + 1. Be satisfied with their new proofs, or 2. Know them outside Keybase and have gotten a thumbs up from them. - + Don't let them in until one of the above is true. @@ -78,7 +78,6 @@ const styles = Kb.Styles.styleSheetCreate( () => ({ bullet: { - ...Kb.Styles.globalStyles.flexBoxColumn, maxWidth: 320, }, buttonContainer: { diff --git a/shared/chat/conversation/messages/retention-notice.tsx b/shared/chat/conversation/messages/retention-notice.tsx index 77327c4f58ed..2ec9b07ecdca 100644 --- a/shared/chat/conversation/messages/retention-notice.tsx +++ b/shared/chat/conversation/messages/retention-notice.tsx @@ -56,10 +56,10 @@ const RetentionNoticeContainer = React.memo(function RetentionNoticeContainer() : 'iconfont-timer-solid' return ( - - + + - + {!!explanation && ( {explanation} @@ -74,7 +74,7 @@ const RetentionNoticeContainer = React.memo(function RetentionNoticeContainer() Change this )} - + ) }) @@ -82,14 +82,11 @@ const styles = Kb.Styles.styleSheetCreate( () => ({ container: { - ...Kb.Styles.globalStyles.flexBoxColumn, - alignItems: 'center', backgroundColor: Kb.Styles.globalColors.blueLighter3, paddingBottom: Kb.Styles.globalMargins.small, paddingLeft: Kb.Styles.globalMargins.medium, paddingRight: Kb.Styles.globalMargins.medium, paddingTop: Kb.Styles.globalMargins.small, - width: '100%', }, iconBox: {marginBottom: Kb.Styles.globalMargins.xtiny}, }) as const diff --git a/shared/chat/conversation/messages/special-top-message.tsx b/shared/chat/conversation/messages/special-top-message.tsx index e726d608a19c..beb4eeb662b5 100644 --- a/shared/chat/conversation/messages/special-top-message.tsx +++ b/shared/chat/conversation/messages/special-top-message.tsx @@ -182,43 +182,43 @@ const SpecialTopMessage = React.memo(function SpecialTopMessage() { }, [username]) return ( - + {loadMoreType === 'noMoreToLoad' && showRetentionNotice && } - + {hasOlderResetConversation && } {pendingState === 'waiting' && ( - + Loading... - + )} {pendingState === 'error' && } {loadMoreType === 'noMoreToLoad' && !showRetentionNotice && pendingState === 'done' && ( - + {isHelloBotConversation ? ( ) : ( )} - + )} {showTeamOffer && ( - + - + )} {allowDigging && loadMoreType === 'moreToLoad' && pendingState === 'done' && ( - + Digging ancient messages... - + )} {!Kb.Styles.isMobile || usingFlashList ? null : ( // special case here with the sep. The flatlist and flashlist invert the leading-trailing, see useStateFast )} - + ) }) @@ -228,10 +228,7 @@ const styles = Kb.Styles.styleSheetCreate( buttonBar: {padding: Kb.Styles.globalMargins.small}, errorText: {padding: Kb.Styles.globalMargins.small}, more: { - ...Kb.Styles.globalStyles.flexBoxColumn, - alignItems: 'center', paddingBottom: Kb.Styles.globalMargins.medium, - width: '100%', }, spacer: {height: Kb.Styles.globalMargins.small}, }) as const diff --git a/shared/chat/conversation/messages/system-added-to-team/container.tsx b/shared/chat/conversation/messages/system-added-to-team/container.tsx index 10999c12dc9f..4c684a85fd06 100644 --- a/shared/chat/conversation/messages/system-added-to-team/container.tsx +++ b/shared/chat/conversation/messages/system-added-to-team/container.tsx @@ -120,11 +120,11 @@ const ManageComponent = (props: Props) => { } if (addee === you) { return ( - + Manage phone and computer notifications - + ) } else if (bot) { return ( diff --git a/shared/chat/conversation/messages/text/reply.tsx b/shared/chat/conversation/messages/text/reply.tsx index 652f53798e58..0a3d40cfd5be 100644 --- a/shared/chat/conversation/messages/text/reply.tsx +++ b/shared/chat/conversation/messages/text/reply.tsx @@ -48,9 +48,9 @@ const ReplyImage = () => { const sizing = imageWidth && imageHeight ? Chat.zoomImage(imageWidth, imageHeight, 80) : undefined return ( - + - + ) } diff --git a/shared/chat/conversation/messages/text/unfurl/unfurl-list/image/video.desktop.tsx b/shared/chat/conversation/messages/text/unfurl/unfurl-list/image/video.desktop.tsx index d17dc1106a61..4a2b76e79989 100644 --- a/shared/chat/conversation/messages/text/unfurl/unfurl-list/image/video.desktop.tsx +++ b/shared/chat/conversation/messages/text/unfurl/unfurl-list/image/video.desktop.tsx @@ -36,9 +36,9 @@ export const Video = (p: Props) => { return ( - + {!playing && } - +
- + )} {dragY !== -1 && ( @@ -374,7 +374,7 @@ function Inbox(props: TInbox.Props) { return ( - +
{rows.length ? ( {floatingDivider || (rows.length === 0 && )} {showUnread && !showFloating && } - + ) } @@ -399,7 +399,6 @@ const styles = Kb.Styles.styleSheetCreate( ({ container: Kb.Styles.platformStyles({ isElectron: { - ...Kb.Styles.globalStyles.flexBoxColumn, backgroundColor: Kb.Styles.globalColors.blueGrey, contain: 'strict', height: '100%', diff --git a/shared/chat/inbox/index.native.tsx b/shared/chat/inbox/index.native.tsx index 42bf1c36d096..c5843f51b039 100644 --- a/shared/chat/inbox/index.native.tsx +++ b/shared/chat/inbox/index.native.tsx @@ -341,7 +341,7 @@ const Inbox = React.memo(function Inbox(p: TInbox.Props) { return ( - + {isSearching ? ( @@ -374,7 +374,7 @@ const Inbox = React.memo(function Inbox(p: TInbox.Props) { )} {debugWhichList} - + ) }) @@ -390,9 +390,9 @@ const LoadingLine = () => { C.waitingKeyChatInboxSyncStarted, ]) return isLoading ? ( - + - + ) : null } @@ -402,7 +402,6 @@ const styles = Kb.Styles.styleSheetCreate( button: {width: '100%'}, container: Kb.Styles.platformStyles({ common: { - ...Kb.Styles.globalStyles.flexBoxColumn, backgroundColor: Kb.Styles.globalColors.fastBlank, flexGrow: 1, position: 'relative', diff --git a/shared/chat/inbox/new-chat-button.tsx b/shared/chat/inbox/new-chat-button.tsx index 62445e915d25..dc17141c8e42 100644 --- a/shared/chat/inbox/new-chat-button.tsx +++ b/shared/chat/inbox/new-chat-button.tsx @@ -23,10 +23,10 @@ const HeaderNewChatButton = () => { className="tooltip-right" > - - - - + + + + { > {draftIcon} {outboxIcon} - {hasBadge && } + {hasBadge && } diff --git a/shared/chat/inbox/row/big-team-header.tsx b/shared/chat/inbox/row/big-team-header.tsx index d5359603db77..8c70593b401c 100644 --- a/shared/chat/inbox/row/big-team-header.tsx +++ b/shared/chat/inbox/row/big-team-header.tsx @@ -69,7 +69,7 @@ const BigTeamHeaderInner = (props: Props) => { color={Kb.Styles.globalColors.black_35} type="iconfont-gear" /> - + ) diff --git a/shared/chat/inbox/row/start-new-chat.tsx b/shared/chat/inbox/row/start-new-chat.tsx index e6d5784c51aa..f2f540fc2805 100644 --- a/shared/chat/inbox/row/start-new-chat.tsx +++ b/shared/chat/inbox/row/start-new-chat.tsx @@ -8,14 +8,14 @@ type Props = { const StartNewChat = (props: Props) => { if (Kb.Styles.isMobile) { return ( - + Start a new chat - + ) } return ( @@ -41,8 +41,6 @@ const styles = Kb.Styles.styleSheetCreate( flexDirection: 'row', }, container: { - ...Kb.Styles.globalStyles.flexBoxRow, - alignItems: 'center', backgroundColor: Kb.Styles.isMobile ? Kb.Styles.globalColors.fastBlank : Kb.Styles.globalColors.blueGrey, diff --git a/shared/chat/maybe-popup.tsx b/shared/chat/maybe-popup.tsx index 9ba965103fd8..8e938e30d65b 100644 --- a/shared/chat/maybe-popup.tsx +++ b/shared/chat/maybe-popup.tsx @@ -1,5 +1,5 @@ import type * as React from 'react' -import Box from '../common-adapters/box' +import {Box2} from '../common-adapters/box' import PopupDialog from '../common-adapters/popup-dialog' import * as Styles from '@/styles' @@ -16,7 +16,11 @@ type Props = { } const MaybePopup = Styles.isMobile - ? (props: Props) => + ? (props: Props) => ( + + {props.children} + + ) : (props: Props) => ( ) +const styles = Styles.styleSheetCreate( + () => + ({ + mobileContainer: { + height: '100%', + width: '100%', + }, + }) as const +) + const _styleCover = { alignItems: 'stretch', backgroundColor: Styles.globalColors.black, diff --git a/shared/common-adapters/animation.desktop.tsx b/shared/common-adapters/animation.desktop.tsx index ab8780e953b7..27160db1067a 100644 --- a/shared/common-adapters/animation.desktop.tsx +++ b/shared/common-adapters/animation.desktop.tsx @@ -1,5 +1,5 @@ import * as React from 'react' -import Box from './box' +import {Box2} from './box' import lottie from 'lottie-web' import type {Props, AnimationType} from './animation' @@ -26,7 +26,7 @@ const Animation = React.memo(function Animation(props: Props) { } }, [animationType]) return ( - +
- + ) }) diff --git a/shared/common-adapters/animation.native.tsx b/shared/common-adapters/animation.native.tsx index 72c3f0f80e92..c9e8cf31acc6 100644 --- a/shared/common-adapters/animation.native.tsx +++ b/shared/common-adapters/animation.native.tsx @@ -1,5 +1,5 @@ import * as React from 'react' -import Box from './box' +import {Box2} from './box' import LottieView from 'lottie-react-native' import type {Props, AnimationType} from './animation' // prettier-ignore @@ -16,9 +16,9 @@ const Animation = React.memo(function Animation(props: Props) { const source = data[animationType] return ( - + - + ) }) diff --git a/shared/common-adapters/avatar/index.native.tsx b/shared/common-adapters/avatar/index.native.tsx index d3e183163e8f..db137e0f0788 100644 --- a/shared/common-adapters/avatar/index.native.tsx +++ b/shared/common-adapters/avatar/index.native.tsx @@ -3,12 +3,12 @@ import * as React from 'react' import * as Styles from '@/styles' import ClickableBox from '../clickable-box' import Image2 from '../image2' -import Box from '../box' +import {Box2} from '../box' import type {Props, AvatarSize} from '.' import useHook from './hooks' const Kb = { - Box, + Box2, ClickableBox, Icon, Image2, @@ -34,14 +34,17 @@ const Avatar = React.memo(function Avatar(p: Props) { return ( - + {!props.skipBackground && ( - + )} {!!props.blocked && ( - + - + )} {!!props.url && ( )} {props.children} - + ) }) diff --git a/shared/common-adapters/back-button.native.tsx b/shared/common-adapters/back-button.native.tsx index e9bedbaf2daf..13218fb405c4 100644 --- a/shared/common-adapters/back-button.native.tsx +++ b/shared/common-adapters/back-button.native.tsx @@ -2,7 +2,7 @@ import * as React from 'react' import * as C from '@/constants' import {Pressable, Keyboard} from 'react-native' import Badge from './badge' -import Box from './box' +import {Box2} from './box' import Icon from './icon' import * as Styles from '@/styles' import type {Props} from './back-button' @@ -10,7 +10,7 @@ import noop from 'lodash/noop' const Kb = { Badge, - Box, + Box2, Icon, } @@ -25,7 +25,7 @@ const BackButton = React.memo(function BackButton(props: Props) { const onBack = props.disabled ? noop : (props.onClick ?? onNavUp) return ( - + {!!props.badgeNumber && } - + ) }) @@ -44,8 +44,6 @@ const styles = Styles.styleSheetCreate(() => ({ marginTop: 2, }, container: { - ...Styles.globalStyles.flexBoxRow, - alignItems: 'center', marginRight: 8, minWidth: 32, padding: Styles.globalMargins.tiny, diff --git a/shared/common-adapters/badge.tsx b/shared/common-adapters/badge.tsx index de33010fdd47..8a1d41fff7ca 100644 --- a/shared/common-adapters/badge.tsx +++ b/shared/common-adapters/badge.tsx @@ -1,10 +1,9 @@ import * as React from 'react' -import {Box, Box2} from './box' +import {Box2} from './box' import Text from './text' import * as Styles from '@/styles' const Kb = { - Box, Box2, Text, } diff --git a/shared/common-adapters/banner.tsx b/shared/common-adapters/banner.tsx index 316dfa4288da..1ff3acfd132e 100644 --- a/shared/common-adapters/banner.tsx +++ b/shared/common-adapters/banner.tsx @@ -1,5 +1,5 @@ import * as React from 'react' -import Box, {Box2} from './box' +import {Box2} from './box' import Icon from './icon' import Text from './text' import * as Styles from '@/styles' @@ -111,7 +111,7 @@ export const Banner = (props: BannerProps) => ( )} {!!props.onClose && ( - + ( hoverColor={colorToIconHoverColor()[props.color]} onClick={props.onClose} /> - + )} ) diff --git a/shared/common-adapters/box-grow.tsx b/shared/common-adapters/box-grow.tsx index c04b12fe8f93..409d21a02aae 100644 --- a/shared/common-adapters/box-grow.tsx +++ b/shared/common-adapters/box-grow.tsx @@ -1,6 +1,6 @@ // A box that flex grows but constrains children import * as Styles from '@/styles' -import Box, {Box2, type LayoutEvent} from './box' +import {Box2, type LayoutEvent} from './box' type Props = { children?: React.ReactNode @@ -10,9 +10,11 @@ type Props = { const BoxGrow = (p: Props) => { return ( - - {p.children} - + + + {p.children} + + ) } diff --git a/shared/common-adapters/box.d.ts b/shared/common-adapters/box.d.ts index 767e2ff655ee..1f2b70290b4f 100644 --- a/shared/common-adapters/box.d.ts +++ b/shared/common-adapters/box.d.ts @@ -3,24 +3,6 @@ import type {StylesCrossPlatform, globalMargins} from '@/styles' import type {MeasureRef} from './measure-ref' import type {View, NativeSyntheticEvent} from 'react-native' -export type Props = { - onMoveShouldSetResponder?: () => boolean - onMouseDown?: (syntheticEvent: React.MouseEvent) => void // desktop only - onMouseLeave?: (syntheticEvent: React.MouseEvent) => void // desktop only - onMouseUp?: (syntheticEvent: React.MouseEvent) => void // desktop only - onMouseOver?: (syntheticEvent: React.MouseEvent) => void // desktop only - onStartShouldSetResponder?: () => boolean - pointerEvents?: 'none' | 'box-none' - onLayout?: (evt: LayoutEvent) => void // mobile only - onClick?: (event: React.BaseSyntheticEvent) => void - children?: React.ReactNode - collapsable?: boolean - className?: string - style?: StylesCrossPlatform - ref?: never - tooltip?: string -} - export type LayoutEvent = NativeSyntheticEvent<{ layout: { x: number @@ -62,17 +44,12 @@ export type Box2Props = { tooltip?: string } -/** - * Box is deprecated, use Box2 instead - **/ -export declare const Box: (p: Props) => React.ReactNode export declare const Box2: (p: Box2Props) => React.ReactNode // wrapped by reanimated -export declare const Box2Animated: ReturnType, Box2Props>> +export declare const Box2Animated: ReturnType, Box2Props>> // Box2 but with a special ref for targetting popups, split in case there's overhead we barely need export declare const Box2Measure: ReturnType> // desktop only export declare const Box2Div: ReturnType> // mobile only export declare const Box2View: ReturnType> -export default Box diff --git a/shared/common-adapters/box.desktop.tsx b/shared/common-adapters/box.desktop.tsx index d430de59684f..1158b71472ab 100644 --- a/shared/common-adapters/box.desktop.tsx +++ b/shared/common-adapters/box.desktop.tsx @@ -1,21 +1,9 @@ import * as React from 'react' import * as Styles from '@/styles' -import type {Box2Props, Props} from './box' +import type {Box2Props} from './box' import type {MeasureRef} from './measure-ref' import './box.css' -export const Box = (p: Props) => { - const {style, onLayout, tooltip, className, ...rest} = p - return ( -
- ) -} - const getProps = (p: Box2Props) => { const {direction, fullHeight, fullWidth, centerChildren, alignSelf, alignItems, noShrink} = p const {onMouseMove, onMouseDown, onMouseLeave, onMouseUp, onMouseOver, onCopyCapture, children} = p @@ -102,4 +90,3 @@ export const Box2Measure = React.forwardRef(function Box2 return
}) -export default Box diff --git a/shared/common-adapters/box.native.tsx b/shared/common-adapters/box.native.tsx index d7b7886bd33a..0a7a1ac974d2 100644 --- a/shared/common-adapters/box.native.tsx +++ b/shared/common-adapters/box.native.tsx @@ -5,8 +5,6 @@ import type {Box2Props} from './box' import type {MeasureRef} from './measure-ref' import Reanimated from 'react-native-reanimated' -export const Box = View - type Margins = keyof typeof Styles.globalMargins const marginKeys = Object.keys(Styles.globalMargins) as Array @@ -173,4 +171,3 @@ const styles = { }, } as const -export default Box diff --git a/shared/common-adapters/button-bar.tsx b/shared/common-adapters/button-bar.tsx index 63c816d3e8b6..dd8d8946dbb5 100644 --- a/shared/common-adapters/button-bar.tsx +++ b/shared/common-adapters/button-bar.tsx @@ -1,8 +1,8 @@ import * as React from 'react' import * as Styles from '@/styles' -import Box from './box' +import {Box2} from './box' -const Kb = {Box} +const Kb = {Box2} type Props = { direction?: 'row' | 'column' @@ -43,33 +43,40 @@ const ButtonBar = (props: Props) => { minHeight: Styles.isMobile ? (props.small ? 64 : 72) : props.small ? 44 : 64, } + const isColumn = (props.direction ?? 'row') === 'column' const style = Styles.collapseStyles([ { - alignItems: (props.fullWidth ?? false) ? 'stretch' : 'center', - width: '100%', ...(Styles.isTablet ? {maxWidth: 460} : {}), - ...((props.direction ?? 'row') === 'column' - ? {...Styles.globalStyles.flexBoxColumn} - : { - ...Styles.globalStyles.flexBoxRow, + ...(!isColumn + ? { justifyContent: props.align ?? 'center', ...minHeight, - }), + } + : undefined), }, props.style, ]) - return {childrenWithSpacing} + return ( + + {childrenWithSpacing} + + ) } // Note explicitly not using globalMargins here. We don't necessarily want this spacing to change ever -const BigSpacer = () => +const BigSpacer = () => const bigSpacerStyle = { flexShrink: 0, height: 8, width: 8, } -const SmallSpacer = () => +const SmallSpacer = () => const smallSpacerStyle = { flexShrink: 0, height: Styles.isMobile ? 8 : 4, diff --git a/shared/common-adapters/button.tsx b/shared/common-adapters/button.tsx index 8350b5028f42..924019528045 100644 --- a/shared/common-adapters/button.tsx +++ b/shared/common-adapters/button.tsx @@ -7,13 +7,12 @@ import Icon, {type SizeType} from './icon' import Text, {type StylesTextCrossPlatform} from './text' import WithTooltip from './with-tooltip' import type {IconType} from './icon.constants-gen' -import {Box, Box2} from './box' +import {Box2} from './box' import type {MeasureRef} from './measure-ref' import type AnimationType from './animation' const Kb = { Badge, - Box, Box2, ClickableBox, Icon, @@ -76,12 +75,12 @@ export type Props = DefaultProps & WithIconProps const Progress = ({small, white}: {small?: boolean; white: boolean}) => { const {default: Animation} = require('./animation') as {default: typeof AnimationType} return ( - + - + ) } @@ -190,7 +189,7 @@ const Button = React.forwardRef(function ButtonInner( } const underlay = !Styles.isMobile && underlayClassNames.length ? ( - + ) : null if (props.className) classNames.push(props.className) @@ -208,10 +207,10 @@ const Button = React.forwardRef(function ButtonInner( tooltip={props.tooltip} > {underlay} - (function ButtonInner( {!!props.badgeNumber && } {!!props.waiting && } - + ) if (props.disabled && props.tooltip && Styles.isMobile) { @@ -335,7 +334,7 @@ const styles = Styles.styleSheetCreate(() => ({ }), opacity0: {opacity: 0}, opacity30: {opacity: 0.3}, - progressContainer: {...Styles.globalStyles.fillAbsolute, ...Styles.globalStyles.flexBoxCenter}, + progressContainer: {...Styles.globalStyles.fillAbsolute}, progressNormal: {height: Styles.isMobile ? 32 : 24, width: Styles.isMobile ? 32 : 24}, progressSmall: {height: Styles.isMobile ? 28 : 20, width: Styles.isMobile ? 28 : 20}, small: { diff --git a/shared/common-adapters/checkbox.desktop.tsx b/shared/common-adapters/checkbox.desktop.tsx index a04afc136e7a..63eaa4fe90d2 100644 --- a/shared/common-adapters/checkbox.desktop.tsx +++ b/shared/common-adapters/checkbox.desktop.tsx @@ -1,12 +1,13 @@ -import Box, {Box2} from './box' +import {Box2} from './box' +import ClickableBox from './clickable-box' import Icon from './icon' import Text from './text' import type {Props} from './checkbox' import * as Styles from '@/styles' const Kb = { - Box, Box2, + ClickableBox, Icon, Styles, Text, @@ -17,7 +18,7 @@ const CHECKBOX_MARGIN = 8 const Checkbox = (props: Props) => { return ( - // If something in labelComponent needs to catch a click without calling this, use @@ -47,7 +48,7 @@ const Checkbox = (props: Props) => { ))} {!!props.labelSubtitle && {props.labelSubtitle}} - + ) } diff --git a/shared/common-adapters/choice-list.desktop.tsx b/shared/common-adapters/choice-list.desktop.tsx index e08386265466..7dd5ba5c4593 100644 --- a/shared/common-adapters/choice-list.desktop.tsx +++ b/shared/common-adapters/choice-list.desktop.tsx @@ -1,4 +1,5 @@ -import {Box} from './box' +import {Box2} from './box' +import ClickableBox from './clickable-box' import Text from './text' import Icon from './icon' import * as Styles from '@/styles' @@ -6,48 +7,51 @@ import type {Props} from './choice-list' import './choice-list.css' const Kb = { - Box, + Box2, + ClickableBox, Icon, Text, } const ChoiceList = ({options}: Props) => { return ( - + {options.map((op, idx) => { const iconType = op.icon return ( - op.onClick()}> - - {typeof op.icon === 'string' ? ( - - ) : ( - - {op.icon} - - )} - - - {op.title} - {op.description} - - + op.onClick()}> + + + {typeof op.icon === 'string' ? ( + + ) : ( + + {op.icon} + + )} + + + {op.title} + {op.description} + + + ) })} - + ) } const styles = Styles.styleSheetCreate(() => ({ entry: Styles.platformStyles({ isElectron: { - ...Styles.globalStyles.flexBoxRow, ...Styles.desktopStyles.clickable, ...Styles.padding(Styles.globalMargins.tiny, Styles.globalMargins.small), - width: '100%', }, }), icon: { @@ -55,14 +59,11 @@ const styles = Styles.styleSheetCreate(() => ({ width: 48, }, iconContainer: { - alignItems: 'center', background: Styles.globalColors.greyLight, height: 80, - justifyContent: 'center', width: 80, }, infoContainer: { - alignItems: 'flex-start', flex: 1, justifyContent: 'center', marginLeft: Styles.globalMargins.small, diff --git a/shared/common-adapters/choice-list.native.tsx b/shared/common-adapters/choice-list.native.tsx index 03e8a86ce3b0..1b68a058fee9 100644 --- a/shared/common-adapters/choice-list.native.tsx +++ b/shared/common-adapters/choice-list.native.tsx @@ -1,4 +1,4 @@ -import Box from './box' +import {Box2} from './box' import ClickableBox from './clickable-box' import Icon from './icon' import Text from './text' @@ -6,7 +6,7 @@ import * as React from 'react' import * as Styles from '@/styles' import type {Props} from './choice-list' -const Kb = {Box, ClickableBox, Icon, Text} +const Kb = {Box2, ClickableBox, Icon, Text} const ChoiceList = (props: Props) => { const [activeIndex, setActiveIndex] = React.useState(undefined) @@ -17,7 +17,7 @@ const ChoiceList = (props: Props) => { }, [options]) return ( - + {options.map((op, idx) => { const iconType = op.icon return ( @@ -28,30 +28,29 @@ const ChoiceList = (props: Props) => { onPressIn={() => setActiveIndex(idx)} onPressOut={() => setActiveIndex(undefined)} > - - + + {typeof op.icon === 'string' ? ( ) : ( - {op.icon} + {op.icon} )} - - + + {op.title} {op.description} - - + + ) })} - + ) } const styleEntry = { - ...Styles.globalStyles.flexBoxRow, paddingBottom: Styles.globalMargins.tiny, paddingLeft: Styles.globalMargins.small, paddingRight: Styles.globalMargins.small, @@ -60,12 +59,9 @@ const styleEntry = { const styleIconContainer = (active: boolean) => ({ - ...Styles.globalStyles.flexBoxColumn, - alignItems: 'center', alignSelf: 'center', borderRadius: (Styles.globalMargins.large + Styles.globalMargins.medium) / 2, height: Styles.globalMargins.large + Styles.globalMargins.medium, - justifyContent: 'center', ...(active ? {} : {backgroundColor: Styles.globalColors.greyLight}), width: Styles.globalMargins.large + Styles.globalMargins.medium, }) as const @@ -76,7 +72,6 @@ const styleIcon = { } const styleInfoContainer = { - ...Styles.globalStyles.flexBoxColumn, flex: 1, justifyContent: 'center', marginLeft: Styles.globalMargins.small, diff --git a/shared/common-adapters/clickable-box.native.tsx b/shared/common-adapters/clickable-box.native.tsx index bf0a632c4a90..828fa28bed5b 100644 --- a/shared/common-adapters/clickable-box.native.tsx +++ b/shared/common-adapters/clickable-box.native.tsx @@ -1,12 +1,10 @@ import * as React from 'react' import * as Styles from '@/styles' -import Box from './box' import {Pressable, View, TouchableOpacity, TouchableWithoutFeedback} from 'react-native' import type {Props, Props2} from './clickable-box' import type {MeasureRef} from './measure-ref' const Kb = { - Box, Styles, } @@ -42,11 +40,10 @@ const ClickableBox = React.forwardRef(function ClickableBoxIn - {children} + {children} ) } diff --git a/shared/common-adapters/copyable-text.native.tsx b/shared/common-adapters/copyable-text.native.tsx index 2f4874b29b5f..f16ea2a92d7d 100644 --- a/shared/common-adapters/copyable-text.native.tsx +++ b/shared/common-adapters/copyable-text.native.tsx @@ -2,7 +2,7 @@ import * as React from 'react' import type {Props as PropsCommon} from './copyable-text' import {useTimeout} from './use-timers' import Text from './text' -import Box from './box' +import {Box2} from './box' import {TouchableHighlight} from 'react-native' import * as Styles from '@/styles' import * as Clipboard from 'expo-clipboard' @@ -28,59 +28,57 @@ const CopyableText = (props: Props) => { onPress={() => handleCopy()} style={props.style} > - + {props.value} - - + + {hasCopied ? 'Copied!' : 'Tap to copy'} - - - + + + ) } -const styleBase = { - ...Styles.globalStyles.flexBoxColumn, - alignItems: 'flex-start', - backgroundColor: Styles.globalColors.greyLight, - borderColor: Styles.globalColors.black_10, - borderRadius: 3, - borderWidth: 1, - // Guarantee that the first line of text is shown above the 'Tap to Copy' toast - minHeight: Styles.globalMargins.medium + Styles.globalMargins.tiny + 2 * Styles.globalMargins.small + 24, - padding: 10, - position: 'relative', -} as const +const styles = Styles.styleSheetCreate( + () => + ({ + base: { + backgroundColor: Styles.globalColors.greyLight, + borderColor: Styles.globalColors.black_10, + borderRadius: 3, + borderWidth: 1, + // Guarantee that the first line of text is shown above the 'Tap to Copy' toast + minHeight: + Styles.globalMargins.medium + Styles.globalMargins.tiny + 2 * Styles.globalMargins.small + 24, + padding: 10, + position: 'relative', + }, + copyToast: { + backgroundColor: Styles.globalColors.black_50, + borderRadius: Styles.globalMargins.large, + height: Styles.globalMargins.medium + Styles.globalMargins.tiny, + paddingLeft: Styles.globalMargins.medium, + paddingRight: Styles.globalMargins.medium, + }, + copyToastContainer: { + bottom: Styles.globalMargins.small, + left: 0, + position: 'absolute', + right: 0, + }, + }) as const +) const styleText = { ...Styles.globalStyles.fontTerminal, color: Styles.globalColors.black, } -const styleCopyToastContainer = { - ...Styles.globalStyles.flexBoxColumn, - alignItems: 'center', - bottom: Styles.globalMargins.small, - left: 0, - position: 'absolute', - right: 0, -} as const - -const styleCopyToast = { - ...Styles.globalStyles.flexBoxRow, - alignItems: 'center', - backgroundColor: Styles.globalColors.black_50, - borderRadius: Styles.globalMargins.large, - height: Styles.globalMargins.medium + Styles.globalMargins.tiny, - paddingLeft: Styles.globalMargins.medium, - paddingRight: Styles.globalMargins.medium, -} as const - const styleCopyToastText = { color: Styles.globalColors.white, } diff --git a/shared/common-adapters/divider.tsx b/shared/common-adapters/divider.tsx index 917d9768b966..b8af2e5ec1ee 100644 --- a/shared/common-adapters/divider.tsx +++ b/shared/common-adapters/divider.tsx @@ -1,10 +1,11 @@ -import Box from './box' +import {Box2} from './box' import * as Styles from '@/styles' import type {Props} from './divider.d' const Divider = (props: Props) => ( - { {width: inline ? undefined : '100%'}, ])} > - + {loading ? : selected} - + (p: Props) { ) return ( - + (p: Props) { toggleOpen={toggleOpen} /> {popup} - + ) } @@ -288,9 +287,7 @@ const styles = Styles.styleSheetCreate( }, }), selectedBox: { - ...Styles.globalStyles.flexBoxCenter, minHeight: regularHeight, - width: '100%', }, }) as const ) diff --git a/shared/common-adapters/floating-box/index.native.tsx b/shared/common-adapters/floating-box/index.native.tsx index 05a830764347..295a97f8e24b 100644 --- a/shared/common-adapters/floating-box/index.native.tsx +++ b/shared/common-adapters/floating-box/index.native.tsx @@ -1,12 +1,12 @@ import * as React from 'react' -import Box from '@/common-adapters/box' +import {Box2} from '@/common-adapters/box' import * as Styles from '@/styles' import {Keyboard} from 'react-native' import {Portal} from '../portal.native' import type {Props} from '.' const Kb = { - Box, + Box2, Portal, } @@ -22,12 +22,13 @@ const FloatingBox = (p: Props) => { return ( - {children} - + ) } diff --git a/shared/common-adapters/floating-box/relative-floating-box.desktop.tsx b/shared/common-adapters/floating-box/relative-floating-box.desktop.tsx index f6c327f5d8cf..386431a878c5 100644 --- a/shared/common-adapters/floating-box/relative-floating-box.desktop.tsx +++ b/shared/common-adapters/floating-box/relative-floating-box.desktop.tsx @@ -1,12 +1,12 @@ import * as React from 'react' import * as Styles from '@/styles' import includes from 'lodash/includes' -import Box from '@/common-adapters/box' +import {Box2} from '@/common-adapters/box' import ReactDOM from 'react-dom' import {EscapeHandler} from '../key-event-handler.desktop' import type {MeasureDesktop} from '@/common-adapters/measure-ref' -const Kb = {Box} +const Kb = {Box2} type ComputedStyle = { position: Styles._StylesCrossPlatform['position'] @@ -301,10 +301,10 @@ export const RelativeFloatingBox = (props: ModalPositionRelativeProps) => { ? ReactDOM.createPortal(
{disableEscapeKey ? ( - {children} + {children} ) : ( - {children} + {children} )}
, diff --git a/shared/common-adapters/floating-menu/menu-layout/index.desktop.tsx b/shared/common-adapters/floating-menu/menu-layout/index.desktop.tsx index bb51ccbac531..f0bf8a7d1814 100644 --- a/shared/common-adapters/floating-menu/menu-layout/index.desktop.tsx +++ b/shared/common-adapters/floating-menu/menu-layout/index.desktop.tsx @@ -1,6 +1,7 @@ import type * as React from 'react' import type {MenuLayoutProps, MenuItem} from '.' -import Box from '@/common-adapters/box' +import {Box2} from '@/common-adapters/box' +import ClickableBox from '@/common-adapters/clickable-box' import Divider from '@/common-adapters/divider' import Icon from '@/common-adapters/icon' import Text from '@/common-adapters/text' @@ -29,7 +30,7 @@ const MenuLayout = (props: MenuLayoutProps) => { return item.unWrapped ? ( item.view ) : ( - { > {item.view} {!item.view && ( - + { style={{paddingLeft: Styles.globalMargins.tiny}} /> )} - + )} {!item.view && item.subTitle && ( { )} {!!item.progressIndicator && } - + ) } @@ -96,26 +97,35 @@ const MenuLayout = (props: MenuLayoutProps) => { }, []) return ( - { // never allow a click to go through event.stopPropagation() }} > - + {/* Display header if there is one */} {props.header} {/* Display menu items */} {items.some(item => item !== 'Divider') && ( - + {items.map( (item, index): React.ReactNode => item === 'Divider' ? renderDivider(index) : renderMenuItem(item, index) )} - + )} - - + + ) } @@ -133,7 +143,6 @@ const styles = Styles.styleSheetCreate( dividerFirst: { marginBottom: 8, }, - horizBox: {...Styles.globalStyles.flexBoxRow}, icon: {marginLeft: Styles.globalMargins.xtiny}, iconBadge: { backgroundColor: Styles.globalColors.blue, @@ -154,8 +163,6 @@ const styles = Styles.styleSheetCreate( menuContainer: Styles.platformStyles({ isElectron: { ...Styles.desktopStyles.boxShadow, - ...Styles.globalStyles.flexBoxColumn, - alignItems: 'stretch', backgroundColor: Styles.globalColors.white, borderRadius: Styles.borderRadius, justifyContent: 'flex-start', @@ -165,7 +172,6 @@ const styles = Styles.styleSheetCreate( }, }), menuItemList: { - ...Styles.globalStyles.flexBoxColumn, flexShrink: 0, paddingBottom: Styles.globalMargins.tiny, paddingTop: Styles.globalMargins.tiny, diff --git a/shared/common-adapters/floating-menu/menu-layout/index.native.tsx b/shared/common-adapters/floating-menu/menu-layout/index.native.tsx index 43e8783fe4ea..1737e6365189 100644 --- a/shared/common-adapters/floating-menu/menu-layout/index.native.tsx +++ b/shared/common-adapters/floating-menu/menu-layout/index.native.tsx @@ -1,7 +1,7 @@ import * as Styles from '@/styles' import {TouchableOpacity, Keyboard} from 'react-native' import Badge from '@/common-adapters/badge' -import Box, {Box2} from '@/common-adapters/box' +import {Box2} from '@/common-adapters/box' import Icon from '@/common-adapters/icon' import Text from '@/common-adapters/text' import Meta from '@/common-adapters/meta' @@ -17,7 +17,6 @@ import noop from 'lodash/noop' const Kb = { Badge, - Box, Box2, Divider, Icon, @@ -158,7 +157,7 @@ const MenuLayout = (props: MenuLayoutProps) => { const close = ( <> - + { textColor={props.textColor} backgroundColor={props.backgroundColor} /> - + ) @@ -207,7 +206,9 @@ const MenuLayout = (props: MenuLayoutProps) => { props.backgroundColor && {backgroundColor: props.backgroundColor}, ])} > - { {items} {close} - + ) @@ -289,16 +290,12 @@ const styles = Styles.styleSheetCreate( paddingRight: Styles.globalMargins.small, }, menuBox: { - ...Styles.globalStyles.flexBoxColumn, - alignItems: 'stretch', backgroundColor: Styles.globalColors.white, justifyContent: 'flex-end', paddingBottom: Styles.globalMargins.tiny, paddingTop: Styles.globalMargins.xsmall, }, menuGroup: { - ...Styles.globalStyles.flexBoxColumn, - alignItems: 'stretch', justifyContent: 'flex-end', }, progressIndicator: { diff --git a/shared/common-adapters/header-hoc/index.desktop.tsx b/shared/common-adapters/header-hoc/index.desktop.tsx index ebb3f15bc325..8d658ca8394e 100644 --- a/shared/common-adapters/header-hoc/index.desktop.tsx +++ b/shared/common-adapters/header-hoc/index.desktop.tsx @@ -2,13 +2,13 @@ import * as C from '@/constants' import type * as React from 'react' import * as Styles from '@/styles' import BackButton from '../back-button' -import Box from '@/common-adapters/box' +import {Box2} from '@/common-adapters/box' import Icon from '@/common-adapters/icon' import Text from '@/common-adapters/text' import {useNavigation} from '@react-navigation/native' import type {Props, LeftActionProps} from '.' -const Kb = {BackButton, Box, Icon, Text} +const Kb = {BackButton, Box2, Icon, Text} export const HeaderHocHeader = ({ headerStyle, @@ -18,7 +18,7 @@ export const HeaderHocHeader = ({ onCancel, theme = 'light', }: Props) => ( - + {customComponent} {onCancel && ( )} {title && ( - + {title} - + )} {titleComponent} - + ) // TODO use LeftAction above @@ -48,7 +48,7 @@ const LeftAction = ({ onLeftAction, theme, }: LeftActionProps) => ( - + {onLeftAction && leftAction === 'cancel' ? ( {leftActionText || customCancelText || 'Cancel'} @@ -69,7 +69,7 @@ const LeftAction = ({ onClick={disabled ? undefined : onLeftAction} /> ) : null} - + ) export const HeaderHocWrapper = (props: Props & {children: React.ReactNode}): React.ReactNode => { @@ -77,8 +77,6 @@ export const HeaderHocWrapper = (props: Props & {children: React.ReactNode}): Re } const _headerStyle = { - ...Styles.globalStyles.flexBoxRow, - alignItems: 'center', flexShrink: 0, justifyContent: 'flex-start', minHeight: undefined, @@ -111,11 +109,8 @@ const _styleCloseThemed = { } const _titleStyle = { - ...Styles.globalStyles.flexBoxRow, - alignItems: 'center', bottom: 0, flex: 1, - justifyContent: 'center', left: 0, position: 'absolute', // This is always centered so we never worry about items to the left/right. If you have overlap or other issues you likely have to fix the content right: 0, @@ -142,8 +137,6 @@ const styles = Styles.styleSheetCreate(() => ({ }, leftAction: Styles.platformStyles({ common: { - ...Styles.globalStyles.flexBoxColumn, - alignItems: 'flex-start', flexShrink: 1, justifyContent: 'flex-start', }, diff --git a/shared/common-adapters/header-hoc/index.native.tsx b/shared/common-adapters/header-hoc/index.native.tsx index 37a56f896a82..9ef6113c858c 100644 --- a/shared/common-adapters/header-hoc/index.native.tsx +++ b/shared/common-adapters/header-hoc/index.native.tsx @@ -1,7 +1,7 @@ import * as React from 'react' import * as Styles from '@/styles' import BackButton from '../back-button' -import Box from '@/common-adapters/box' +import {Box2} from '@/common-adapters/box' import BoxGrow from '@/common-adapters/box-grow' import FloatingMenu from '@/common-adapters/floating-menu' import Icon, {type IconType} from '@/common-adapters/icon' @@ -10,7 +10,7 @@ import Text from '@/common-adapters/text' import {useNavigation} from '@react-navigation/native' import type {Props, LeftActionProps} from '.' -const Kb = {BackButton, Box, BoxGrow, FloatingMenu, Icon, Text} +const Kb = {BackButton, Box2, BoxGrow, FloatingMenu, Icon, Text} type RightAction = { label?: string @@ -38,12 +38,17 @@ export const HeaderHocHeader = (props: Props) => { const hasTextTitle = !!props.title && !props.titleComponent const header = ( - {props.customComponent} {hasTextTitle && ( - { {props.title} - + )} { theme={props.theme} /> {props.titleComponent && ( - { ] as const)} > {props.titleComponent} - + )} {rightAction && } - + ) return header @@ -95,7 +102,7 @@ export const LeftAction = (p: LeftActionProps): React.ReactElement => { const {badgeNumber, disabled, customCancelText, hasTextTitle, hideBackLabel, leftAction} = p const {leftActionText, onLeftAction, theme, customIconColor, style} = p return ( - + {onLeftAction && leftAction === 'cancel' ? ( {leftActionText || customCancelText || 'Cancel'} @@ -118,16 +125,16 @@ export const LeftAction = (p: LeftActionProps): React.ReactElement => { /> ) )} - + ) } const RightActions = (p: {hasTextTitle: boolean; rightAction: RightAction}) => { const {hasTextTitle, rightAction} = p return ( - - {renderAction(rightAction, 0)} - + + {renderAction(rightAction, 0)} + ) } @@ -149,12 +156,12 @@ const renderAction = (action: RightAction, index: number): React.ReactNode => export const HeaderHocWrapper = (props: Props & {children: React.ReactNode; skipHeader?: boolean}) => { const {customSafeAreaTopStyle, children, customSafeAreaBottomStyle, skipHeader} = props return ( - + {!!customSafeAreaTopStyle && } {!skipHeader && } {children} {!!customSafeAreaBottomStyle && } - + ) } @@ -182,15 +189,11 @@ const styles = Styles.styleSheetCreate( }), borderless: {borderBottomWidth: 0}, container: { - ...Styles.globalStyles.flexBoxColumn, - height: '100%', position: 'relative', }, grow: {flexGrow: 1}, header: Styles.platformStyles({ common: { - ...Styles.globalStyles.flexBoxRow, - alignItems: 'center', borderBottomColor: Styles.globalColors.black_10, borderBottomWidth: 1, borderStyle: 'solid', @@ -207,27 +210,20 @@ const styles = Styles.styleSheetCreate( }), leftAction: Styles.platformStyles({ common: { - ...Styles.globalStyles.flexBoxColumn, - alignItems: 'flex-start', flexShrink: 1, justifyContent: 'flex-start', }, }), rightActions: Styles.platformStyles({ common: { - ...Styles.globalStyles.flexBoxColumn, - alignItems: 'flex-end', flexShrink: 1, justifyContent: 'flex-end', }, isIOS: {paddingRight: Styles.globalMargins.tiny}, }), - rightActionsWrapper: {...Styles.globalStyles.flexBoxRow}, title: {color: Styles.globalColors.black}, titleContainer: Styles.platformStyles({ common: { - ...Styles.globalStyles.flexBoxColumn, - alignItems: 'center', flexGrow: 1, flexShrink: 2, justifyContent: 'center', @@ -243,7 +239,7 @@ const styles = Styles.styleSheetCreate( titleContainerRightPadding: Styles.platformStyles({ isAndroid: {paddingRight: Styles.globalMargins.small}, }), - titleTextContainer: {...Styles.globalStyles.fillAbsolute}, + titleTextContainer: Styles.globalStyles.fillAbsolute, }) as const ) diff --git a/shared/common-adapters/index.d.ts b/shared/common-adapters/index.d.ts index ce7742902487..500578a72a99 100644 --- a/shared/common-adapters/index.d.ts +++ b/shared/common-adapters/index.d.ts @@ -18,7 +18,7 @@ export {default as AvatarLine} from './avatar/avatar-line' export {default as BackButton} from './back-button' export {default as Badge} from './badge' export {Banner, BannerParagraph} from './banner' -export {default as Box, Box2, Box2Measure, Box2Animated, Box2Div, Box2View, type LayoutEvent} from './box' +export {Box2, Box2Measure, Box2Animated, Box2Div, Box2View, type LayoutEvent} from './box' export type {MeasureDesktop, MeasureNative, MeasureRef} from './measure-ref' export {default as ButtonBar} from './button-bar' export {default as Button} from './button' diff --git a/shared/common-adapters/info-note.tsx b/shared/common-adapters/info-note.tsx index 5937df28d2b4..89d70583c397 100644 --- a/shared/common-adapters/info-note.tsx +++ b/shared/common-adapters/info-note.tsx @@ -1,5 +1,5 @@ import type * as React from 'react' -import Box, {Box2} from './box' +import {Box2} from './box' import Icon from './icon' import * as Styles from '@/styles' @@ -16,13 +16,13 @@ const InfoNote = (props: Props) => ( style={Styles.collapseStyles([styles.alignCenter, props.containerStyle])} > - + - + {props.children} diff --git a/shared/common-adapters/list-item.desktop.tsx b/shared/common-adapters/list-item.desktop.tsx index 3024f83df36a..2d900d222598 100644 --- a/shared/common-adapters/list-item.desktop.tsx +++ b/shared/common-adapters/list-item.desktop.tsx @@ -1,43 +1,45 @@ -import Box from './box' -import {globalStyles, desktopStyles} from '@/styles' +import {Box2} from './box' +import {desktopStyles} from '@/styles' import type {Props} from './list-item' const ListItem = (p: Props) => { const clickable = !!p.onClick const minHeight = {Large: 56, Small: 40}[p.type] return ( - - - + {p.icon} - - - {p.body} - + + + {p.body} + + {p.action} -
- + + ) } diff --git a/shared/common-adapters/list-item.native.tsx b/shared/common-adapters/list-item.native.tsx index b9cde118939f..8902e3ae6f03 100644 --- a/shared/common-adapters/list-item.native.tsx +++ b/shared/common-adapters/list-item.native.tsx @@ -1,47 +1,45 @@ import type {Props} from './list-item' -import Box from './box' +import {Box2} from './box' import ClickableBox from './clickable-box' -import {globalStyles} from '@/styles' const ListItem = (p: Props) => { const height = {Large: 64, Small: 48}[p.type] // minimum height const listItem = ( - - - - + + + {p.icon} - - - + + {p.body} - + {!p.swipeToAction && ( - {p.action} - + )} - + ) return {listItem} } diff --git a/shared/common-adapters/loading-line.desktop.tsx b/shared/common-adapters/loading-line.desktop.tsx index 32924e927525..46bb23b0addf 100644 --- a/shared/common-adapters/loading-line.desktop.tsx +++ b/shared/common-adapters/loading-line.desktop.tsx @@ -1,15 +1,15 @@ -import Box from './box' +import {Box2} from './box' import * as React from 'react' import * as Styles from '@/styles' import './loading-line.css' -const Kb = {Box} +const Kb = {Box2} const LoadingLine = React.memo(function LoadingLine() { return ( - - - + + + ) }) diff --git a/shared/common-adapters/markdown/react.tsx b/shared/common-adapters/markdown/react.tsx index c48a4dd516fc..ef91e4d73183 100644 --- a/shared/common-adapters/markdown/react.tsx +++ b/shared/common-adapters/markdown/react.tsx @@ -3,7 +3,7 @@ import * as SM from '@khanacademy/simple-markdown' import type * as T from '@/constants/types' import * as Styles from '@/styles' import Text, {type StylesTextCrossPlatform} from '@/common-adapters/text' -import Box from '@/common-adapters/box' +import {Box2} from '@/common-adapters/box' import Spoiler from './spoiler' import NativeEmoji from '@/common-adapters/emoji/native-emoji' import type {StyleOverride} from '.' @@ -180,7 +180,7 @@ const InlineCode = (p: {children: React.ReactNode; state: State}) => { const Fence = (p: {children: React.ReactNode; state: State}) => { const {children, state} = p return Styles.isMobile ? ( - + { > {children} - + ) : ( + {output(node['content'], state)} - + ) state.inBlockQuote = oldInBlockQuote return ret diff --git a/shared/common-adapters/modal.tsx b/shared/common-adapters/modal.tsx index c094c5aa9ce8..8a8f50f78556 100644 --- a/shared/common-adapters/modal.tsx +++ b/shared/common-adapters/modal.tsx @@ -2,13 +2,12 @@ import * as React from 'react' import * as Styles from '@/styles' import PopupDialog from './popup-dialog' import ScrollView, {type ScrollViewRef} from '@/common-adapters/scroll-view' -import {Box2, Box, type LayoutEvent} from '@/common-adapters/box' +import {Box2, type LayoutEvent} from '@/common-adapters/box' import BoxGrow from './box-grow' import Text from '@/common-adapters/text' import {useTimeout} from './use-timers' const Kb = { - Box, Box2, BoxGrow, ScrollView, @@ -175,7 +174,7 @@ const Header = (props: HeaderProps) => { {showTitle && ( - + {!!subTitle && props.subTitleAbove && subTitle} {typeof props.title === 'string' ? ( @@ -185,7 +184,7 @@ const Header = (props: HeaderProps) => { props.title )} {!!subTitle && !props.subTitleAbove && subTitle} - + )} { minHeight: 64, }, measured: { - alignItems: 'center', flex: 1, - justifyContent: 'center', }, modeDefault: Styles.platformStyles({ isElectron: { diff --git a/shared/common-adapters/modal2.tsx b/shared/common-adapters/modal2.tsx index e6f6a35bcedc..e532ed66eea5 100644 --- a/shared/common-adapters/modal2.tsx +++ b/shared/common-adapters/modal2.tsx @@ -1,13 +1,12 @@ import * as React from 'react' import * as Styles from '@/styles' import ScrollView from './scroll-view' -import {Box2, Box, type LayoutEvent} from './box' +import {Box2, type LayoutEvent} from './box' import BoxGrow from './box-grow' import Text from './text' import {useTimeout} from './use-timers' const Kb = { - Box, Box2, BoxGrow, ScrollView, @@ -107,7 +106,7 @@ const Header2 = (props: HeaderProps) => { { - + {!!subTitle && props.subTitleAbove && subTitle} {typeof props.title === 'string' ? ( @@ -117,7 +116,7 @@ const Header2 = (props: HeaderProps) => { props.title )} {!!subTitle && !props.subTitleAbove && subTitle} - + } diff --git a/shared/common-adapters/name-with-icon.tsx b/shared/common-adapters/name-with-icon.tsx index 07c7f2c4f3c7..da7810497fad 100644 --- a/shared/common-adapters/name-with-icon.tsx +++ b/shared/common-adapters/name-with-icon.tsx @@ -3,7 +3,7 @@ import * as Styles from '@/styles' import * as C from '@/constants' import {useTeamsState} from '@/stores/teams' import Avatar, {type AvatarSize} from './avatar' -import {Box} from './box' +import {Box2} from './box' import ClickableBox from './clickable-box' import Icon, {type IconType} from './icon' import Text, { @@ -71,7 +71,6 @@ const NameWithIcon = (props: NameWithIconProps) => { const isAvatar = !!(username || teamname) && !props.icon const commonHeight = size === 'big' ? 64 : Styles.isMobile ? 48 : 32 - const BoxComponent = onClick ? ClickableBox : Box const adapterProps = getAdapterProps(size || 'default') let avatarOrIcon: React.ReactNode @@ -158,11 +157,11 @@ const NameWithIcon = (props: NameWithIconProps) => { /> ) const metas = props.horizontal ? ( - + {metaOne} {!!(props.metaTwo && props.metaOne) &&  · } {metaTwo} - + ) : ( <> {metaOne} @@ -170,43 +169,49 @@ const NameWithIcon = (props: NameWithIconProps) => { ) - return ( - + const containerStyle = Styles.collapseStyles([ + props.horizontal + ? size === 'big' + ? styles.hbContainerStyle + : styles.hContainerStyle + : styles.vContainerStyle, + props.containerStyle, + ]) + + const metaContainerStyle = props.horizontal + ? Styles.collapseStyles([size === 'big' && styles.textContainer, props.metaStyle]) + : Styles.collapseStyles([ + styles.metaStyle, + size === 'smaller' && styles.smallerWidthTextContainer, + size !== 'smaller' && styles.fullWidthTextContainer, + {marginTop: adapterProps.metaMargin}, + props.metaStyle, + size === 'smaller' ? styles.smallerWidthTextContainer : {}, + ] as const) + + const children = ( + <> {avatarOrIcon} - + {botAlias} {usernameOrTitle} {metas} - - + + + ) + + return _onClickWrapper ? ( + + {children} + + ) : ( + + {children} + ) } @@ -242,7 +247,7 @@ const styles = Styles.styleSheetCreate(() => ({ }), hContainerStyle: { ...Styles.globalStyles.flexBoxRow, - alignItems: 'center', + alignItems: 'center' as const, }, hIconStyle: Styles.platformStyles({ isElectron: { @@ -278,14 +283,10 @@ const styles = Styles.styleSheetCreate(() => ({ }, }), metaStyle: { - ...Styles.globalStyles.flexBoxColumn, - ...Styles.globalStyles.flexBoxCenter, marginTop: Styles.globalMargins.tiny, }, metasBox: { - ...Styles.globalStyles.flexBoxRow, maxWidth: '100%', - width: '100%', }, smallerWidthTextContainer: Styles.platformStyles({ isElectron: { @@ -300,7 +301,7 @@ const styles = Styles.styleSheetCreate(() => ({ }, vContainerStyle: { ...Styles.globalStyles.flexBoxColumn, - alignItems: 'center', + alignItems: 'center' as const, }, vUsernameContainerStyle: Styles.platformStyles({ isElectron: { diff --git a/shared/common-adapters/new-input.tsx b/shared/common-adapters/new-input.tsx index bc820ff714da..6f2d43e05b3c 100644 --- a/shared/common-adapters/new-input.tsx +++ b/shared/common-adapters/new-input.tsx @@ -1,6 +1,6 @@ import * as React from 'react' import PlainInput, {type PropsWithInput, type PlainInputRef} from './plain-input' -import Box, {Box2} from './box' +import {Box2} from './box' import Icon, {type IconType} from './icon' import Text, {getTextStyle} from './text' import * as Styles from '@/styles' @@ -52,14 +52,14 @@ const NewInput = React.forwardRef(function NewInputInner(p ])} > {!!props.icon && ( - + - + )} {!!prefix && ( diff --git a/shared/common-adapters/overlay/index.native.tsx b/shared/common-adapters/overlay/index.native.tsx index 09e30f5be372..72dd196e4161 100644 --- a/shared/common-adapters/overlay/index.native.tsx +++ b/shared/common-adapters/overlay/index.native.tsx @@ -1,11 +1,10 @@ -import {TouchableWithoutFeedback} from 'react-native' -import {Box, Box2} from '@/common-adapters/box' +import {TouchableWithoutFeedback, View} from 'react-native' +import {Box2} from '@/common-adapters/box' import FloatingBox from '../floating-box' import type {Props} from '.' import * as Styles from '@/styles' const Kb = { - Box, Box2, FloatingBox, } @@ -22,8 +21,7 @@ const Overlay = (props: Props) => { style={Styles.collapseStyles([styles.container, !!props.color && {color: props.color}])} > - {/* This has to be a `Box` so `TouchableWithoutFeedback`'s touch responders get piped through to the `View` */} - + {props.children} diff --git a/shared/common-adapters/placeholder.tsx b/shared/common-adapters/placeholder.tsx index a664d7bfc8e7..b6697c2152df 100644 --- a/shared/common-adapters/placeholder.tsx +++ b/shared/common-adapters/placeholder.tsx @@ -1,5 +1,5 @@ import * as Styles from '@/styles' -import Box from './box' +import {Box2} from './box' type PlaceholderProps = { style?: Styles.StylesCrossPlatform @@ -7,7 +7,8 @@ type PlaceholderProps = { } const Placeholder = (props: PlaceholderProps) => ( - const Render = ({platform, overlay, style}: Props) => { const iconSpec = getSpecForPlatform(platform) return ( - + { right: iconSpec.offsetRight, }} /> - + ) } diff --git a/shared/common-adapters/popup-dialog.desktop.tsx b/shared/common-adapters/popup-dialog.desktop.tsx index 2a6bdf200f53..7e284accbf22 100644 --- a/shared/common-adapters/popup-dialog.desktop.tsx +++ b/shared/common-adapters/popup-dialog.desktop.tsx @@ -69,6 +69,15 @@ function PopupDialog(p: Props) { const styles = Styles.styleSheetCreate(() => ({ get clipContainer() { return Styles.platformStyles({ + isElectron: { + ...Styles.desktopStyles.boxShadow, + ...Styles.globalStyles.flexBoxColumn, + backgroundColor: Styles.globalColors.white, + borderRadius: Styles.borderRadius, + flex: 1, + maxWidth: '100%', + position: 'relative', + }, }) }, close: Styles.platformStyles({ diff --git a/shared/common-adapters/popup-dialog.native.tsx b/shared/common-adapters/popup-dialog.native.tsx index e07c45eb2331..964ea3386c2d 100644 --- a/shared/common-adapters/popup-dialog.native.tsx +++ b/shared/common-adapters/popup-dialog.native.tsx @@ -1,4 +1,4 @@ -import Box from './box' +import {Box2} from './box' import {TouchableWithoutFeedback} from 'react-native' import * as Styles from '@/styles' @@ -7,31 +7,35 @@ import type {Props} from './popup-dialog' export function PopupDialog({children, onClose, styleCover, styleContainer}: Props) { return ( - + - {children} + + {children} + - + ) } -const coverStyle = { - ...Styles.globalStyles.flexBoxCenter, - ...Styles.globalStyles.fillAbsolute, - backgroundColor: Styles.globalColors.black, - paddingBottom: Styles.globalMargins.small, - paddingLeft: Styles.globalMargins.large, - paddingRight: Styles.globalMargins.large, - paddingTop: Styles.globalMargins.small, -} as const - -const containerStyle = { - ...Styles.globalStyles.flexBoxColumn, - backgroundColor: Styles.globalColors.white, - borderRadius: 4, - flexGrow: 1, - position: 'relative', -} as const +const styles = Styles.styleSheetCreate( + () => + ({ + container: { + backgroundColor: Styles.globalColors.white, + borderRadius: 4, + flexGrow: 1, + position: 'relative', + }, + cover: { + ...Styles.globalStyles.fillAbsolute, + backgroundColor: Styles.globalColors.black, + paddingBottom: Styles.globalMargins.small, + paddingLeft: Styles.globalMargins.large, + paddingRight: Styles.globalMargins.large, + paddingTop: Styles.globalMargins.small, + }, + }) as const +) export default PopupDialog diff --git a/shared/common-adapters/profile-card.tsx b/shared/common-adapters/profile-card.tsx index a053acccd437..4296b59566c1 100644 --- a/shared/common-adapters/profile-card.tsx +++ b/shared/common-adapters/profile-card.tsx @@ -5,7 +5,7 @@ import * as Platforms from '@/util/platforms' import * as Tracker from '@/stores/tracker2' import type * as T from '@/constants/types' import capitalize from 'lodash/capitalize' -import Box, {Box2, Box2Measure} from './box' +import {Box2, Box2Measure} from './box' import ClickableBox from './clickable-box' import ConnectedNameWithIcon from './name-with-icon' import {_setWithProfileCardPopup} from './usernames' @@ -27,7 +27,6 @@ import type {MeasureRef} from './measure-ref' const positionFallbacks = ['top center', 'bottom center'] as const const Kb = { - Box, Box2, Box2Measure, ClickableBox, diff --git a/shared/common-adapters/progress-bar.tsx b/shared/common-adapters/progress-bar.tsx index 62372e201ac3..f405c80001a7 100644 --- a/shared/common-adapters/progress-bar.tsx +++ b/shared/common-adapters/progress-bar.tsx @@ -1,4 +1,4 @@ -import Box from './box' +import {Box2} from './box' import * as Styles from '@/styles' type Props = { @@ -18,7 +18,8 @@ const ProgressBar = ({ratio, style, fillStyle, flatLeft, flatRight}: Props) => { width: `${Math.max(0, Math.min(1, ratio)) * 100}%`, } as const return ( - { flatRight ? styles.flatRight : {}, ])} > - - + + ) } diff --git a/shared/common-adapters/save-indicator.tsx b/shared/common-adapters/save-indicator.tsx index f571a46c408a..9245f8ccc98e 100644 --- a/shared/common-adapters/save-indicator.tsx +++ b/shared/common-adapters/save-indicator.tsx @@ -1,12 +1,12 @@ import * as React from 'react' import * as Styles from '@/styles' -import Box from './box' +import {Box2} from './box' import Icon from './icon' import ProgressIndicator from './progress-indicator' import Text from './text' const Kb = { - Box, + Box2, Icon, ProgressIndicator, Text, @@ -20,10 +20,7 @@ export type Props = { } const defaultStyle = { - ...Styles.globalStyles.flexBoxRow, - alignItems: 'center', height: Styles.globalMargins.medium, - justifyContent: 'center', } as const const SaveIndicator = (props: Props) => { @@ -71,7 +68,7 @@ const SaveIndicator = (props: Props) => { break } - return {content} + return {content} } export default SaveIndicator diff --git a/shared/common-adapters/search-filter.tsx b/shared/common-adapters/search-filter.tsx index 522262ded330..68aa929a90e6 100644 --- a/shared/common-adapters/search-filter.tsx +++ b/shared/common-adapters/search-filter.tsx @@ -1,6 +1,6 @@ import * as React from 'react' import Animation from './animation' -import Box, {Box2, Box2Measure} from './box' +import {Box2, Box2Measure} from './box' import ClickableBox, {ClickableBox2} from './clickable-box' import NewInput from './new-input' import PlainInput, {type PlainInputRef} from './plain-input' @@ -15,7 +15,6 @@ import type {MeasureRef} from './measure-ref' const Kb = { Animation, - Box, Box2, Box2Measure, ClickableBox, diff --git a/shared/common-adapters/switch.tsx b/shared/common-adapters/switch.tsx index 2331728591aa..c16bc640eecc 100644 --- a/shared/common-adapters/switch.tsx +++ b/shared/common-adapters/switch.tsx @@ -1,7 +1,7 @@ import * as React from 'react' import * as Styles from '@/styles' import ClickableBox from './clickable-box' -import Box, {Box2} from './box' +import {Box2} from './box' import ProgressIndicator from './progress-indicator' import Text from './text' import SwitchToggle from './switch-toggle' @@ -10,7 +10,6 @@ import type {MeasureRef} from './measure-ref' import type {TextType} from './text.shared' const Kb = { - Box, Box2, ClickableBox, ProgressIndicator, @@ -75,8 +74,8 @@ const Switch = React.forwardRef(function Switch(props: Props, ] as const)} /> - {!!props.gapInBetween && } - {!!props.gapSize && } + {!!props.gapInBetween && } + {!!props.gapSize && } {typeof props.label === 'string' ? ( {props.label} @@ -94,7 +93,7 @@ const Switch = React.forwardRef(function Switch(props: Props, ) return Styles.isMobile || !props.labelTooltip ? ( - {content} + {content} ) : ( (props: Props) => ( style={props.clickableBoxStyle} > - + {tab.icon ? ( ) : ( )} {!!tab.badgeNumber && } - + diff --git a/shared/common-adapters/timeline-marker.tsx b/shared/common-adapters/timeline-marker.tsx index 321c692a29f9..f7428ffab013 100644 --- a/shared/common-adapters/timeline-marker.tsx +++ b/shared/common-adapters/timeline-marker.tsx @@ -1,9 +1,9 @@ -import Box from './box' +import {Box2} from './box' import * as Styles from '@/styles' import {timeline_grey} from './timeline-marker.meta' const Kb = { - Box, + Box2, } export type Props = { @@ -14,11 +14,11 @@ export type Props = { } const TimelineMarker = ({idx, max, type, style}: Props) => ( - - - {type === 'closed' ? : } - - + + + {type === 'closed' ? : } + + ) const circleSize = 8 diff --git a/shared/common-adapters/toast.native.tsx b/shared/common-adapters/toast.native.tsx index cf58d4503172..a5e8577b87e1 100644 --- a/shared/common-adapters/toast.native.tsx +++ b/shared/common-adapters/toast.native.tsx @@ -2,7 +2,7 @@ import * as C from '@/constants' import * as React from 'react' import * as Styles from '@/styles' import FloatingBox from './floating-box' -import Box from './box' +import {Box2} from './box' import {KeyboardAvoidingView2} from './keyboard-avoiding-view' import {useTimeout} from './use-timers' import {Animated as NativeAnimated, Easing as NativeEasing} from 'react-native' @@ -12,7 +12,7 @@ import noop from 'lodash/noop' import {useColorScheme} from 'react-native' const Kb = { - Box, + Box2, FloatingBox, KeyboardAvoidingView2, } @@ -74,7 +74,7 @@ const Toast = (props: Props) => { return shouldRender ? ( - + { > {props.children} - + ) : null diff --git a/shared/common-adapters/video.native.tsx b/shared/common-adapters/video.native.tsx index a2235363d576..6eeb1517f7a0 100644 --- a/shared/common-adapters/video.native.tsx +++ b/shared/common-adapters/video.native.tsx @@ -1,12 +1,12 @@ import * as React from 'react' import * as Styles from '@/styles' -import Box from './box' +import {Box2} from './box' import type {Props} from './video' import {StatusBar} from 'react-native' import {Video as AVVideo, VideoFullscreenUpdate} from 'expo-av' import {useCheckURL} from './video.shared' -const Kb = {Box} +const Kb = {Box2} // There seems to be a race between navigation animation and the measurement stuff // here that causes stuff to be rendered off-screen. So delay mounting to avoid @@ -32,7 +32,7 @@ const Video = (props: Props) => { const content = ( - + { }} style={styles.video} /> - + ) @@ -58,10 +58,7 @@ export default Video const styles = Styles.styleSheetCreate(() => ({ container: { - ...Styles.globalStyles.flexBoxRow, - alignItems: 'center', height: '100%', - justifyContent: 'center', width: '100%', }, video: { diff --git a/shared/common-adapters/wave-button.tsx b/shared/common-adapters/wave-button.tsx index 63a8ab05699a..5a8de12f9f3f 100644 --- a/shared/common-adapters/wave-button.tsx +++ b/shared/common-adapters/wave-button.tsx @@ -1,7 +1,7 @@ import * as C from '@/constants' import * as Chat from '@/stores/chat2' import * as React from 'react' -import {Box2, Box} from './box' +import {Box2} from './box' import Icon from './icon' import Text from './text' import Button from './button' @@ -11,7 +11,6 @@ import type * as T from '@/constants/types' import logger from '@/logger' const Kb = { - Box, Box2, Button, Icon, @@ -77,7 +76,7 @@ const WaveButtonImpl = (props: Props) => { const hideButton = waved && !waving return ( - + {hideButton && ( @@ -97,7 +96,7 @@ const WaveButtonImpl = (props: Props) => { - + ) } diff --git a/shared/common-adapters/with-tooltip.native.tsx b/shared/common-adapters/with-tooltip.native.tsx index c5d5af2eb987..71d97dba4736 100644 --- a/shared/common-adapters/with-tooltip.native.tsx +++ b/shared/common-adapters/with-tooltip.native.tsx @@ -2,7 +2,7 @@ import * as C from '@/constants' import * as React from 'react' import {Portal} from './portal.native' import {useTimeout} from './use-timers' -import Box from './box' +import {Box2} from './box' import ClickableBox from './clickable-box' import Text from './text' import * as Styles from '@/styles' @@ -17,7 +17,7 @@ import {useSafeAreaFrame} from 'react-native-safe-area-context' // "top center" for now. const Kb = { - Box, + Box2, ClickableBox, Portal, Text, @@ -32,12 +32,13 @@ type Dims = { const FloatingBox = (props: {children: React.ReactNode; style: Styles.StylesCrossPlatform}) => ( - {props.children} - + ) diff --git a/shared/crypto/input.tsx b/shared/crypto/input.tsx index 392908539ed1..61cd4614aa01 100644 --- a/shared/crypto/input.tsx +++ b/shared/crypto/input.tsx @@ -134,7 +134,7 @@ const TextInput = (props: TextProps) => { ) : null return ( - + { {!Kb.Styles.isMobile && clearButton} - + ) } diff --git a/shared/devices/device-page.tsx b/shared/devices/device-page.tsx index f270c62a1b27..adfe805f391c 100644 --- a/shared/devices/device-page.tsx +++ b/shared/devices/device-page.tsx @@ -10,11 +10,11 @@ type OwnProps = {deviceID: string} const TimelineMarker = (p: {first: boolean; last: boolean; closedCircle: boolean}) => { const {first, last, closedCircle} = p return ( - - - - - + + + + + ) } @@ -37,7 +37,7 @@ const TimelineLabel = (p: { )} {!!subDesc && !subDescIsName && {subDesc}} - {spacerOnBottom && } + {spacerOnBottom && } ) } @@ -175,10 +175,6 @@ const styles = Kb.Styles.styleSheetCreate( width: 8, }, invisible: {opacity: 0}, - marker: { - ...Kb.Styles.globalStyles.flexBoxColumn, - alignItems: 'center', - }, meta: { alignSelf: 'center', marginTop: 4, diff --git a/shared/fs/banner/system-file-manager-integration-banner/container.tsx b/shared/fs/banner/system-file-manager-integration-banner/container.tsx index 232e32b74c5d..3d921bf38001 100644 --- a/shared/fs/banner/system-file-manager-integration-banner/container.tsx +++ b/shared/fs/banner/system-file-manager-integration-banner/container.tsx @@ -135,11 +135,11 @@ const Banner = (props: BannerProps) => ( {props.title} {props.body && ( - + {props.body} - + )} {props.bodyExtraComponent ?? false} @@ -166,7 +166,7 @@ const Banner = (props: BannerProps) => ( )} - + {!!props.onDismiss && ( { Open your macOS Security & Privacy Settings and follow these steps. - + - + - + {'Change "Allow applications downloaded from" to "App Store and identified developers"'} - + @@ -50,7 +50,7 @@ const InstallSecurityPrefs = () => { {driverStatus.type === T.FS.DriverStatusType.Disabled && driverStatus.isEnabling && ( - + { Checking ... - + )} diff --git a/shared/fs/browser/rows/common.tsx b/shared/fs/browser/rows/common.tsx index da8ea3946b01..9a89ae06beec 100644 --- a/shared/fs/browser/rows/common.tsx +++ b/shared/fs/browser/rows/common.tsx @@ -38,7 +38,9 @@ export const StillCommon = ( onClick={props.onOpen} body={ props.body || ( - {props.status || null} - + ) } onlyShowActionOnHover="fade" @@ -74,7 +76,6 @@ export const rowStyles = Kb.Styles.styleSheetCreate( () => ({ itemBox: { - ...Kb.Styles.globalStyles.flexBoxColumn, flex: 1, justifyContent: 'center', minWidth: 0, diff --git a/shared/fs/browser/rows/editing.tsx b/shared/fs/browser/rows/editing.tsx index 99ac037113a7..1d2fba13a49f 100644 --- a/shared/fs/browser/rows/editing.tsx +++ b/shared/fs/browser/rows/editing.tsx @@ -42,12 +42,12 @@ const Editing = React.memo(function Editing({editID}: Props) { /> } icon={ - + - + } body={ - + - + } action={ - + {!!edit.error && ( @@ -82,7 +82,7 @@ const Editing = React.memo(function Editing({editID}: Props) { hoverColor={Kb.Styles.globalColors.black} style={styles.iconCancel} /> - + } /> ) @@ -104,8 +104,6 @@ const styles = Kb.Styles.styleSheetCreate( }, }), rightBox: { - ...Kb.Styles.globalStyles.flexBoxRow, - alignItems: 'center', flexShrink: 1, justifyContent: 'flex-end', }, diff --git a/shared/fs/browser/rows/placeholder.tsx b/shared/fs/browser/rows/placeholder.tsx index f21847aef538..b8acb0e2e0ab 100644 --- a/shared/fs/browser/rows/placeholder.tsx +++ b/shared/fs/browser/rows/placeholder.tsx @@ -10,7 +10,7 @@ const PlaceholderRow = ({type}: PlaceholderProps) => ( } + statusIcon={} icon={ ( /> } body={ - + - + } /> ) diff --git a/shared/fs/browser/rows/rows.tsx b/shared/fs/browser/rows/rows.tsx index 788835eeeecc..f6b5b9c25383 100644 --- a/shared/fs/browser/rows/rows.tsx +++ b/shared/fs/browser/rows/rows.tsx @@ -18,13 +18,13 @@ export type Props = { } export const WrapRow = ({children}: {children: React.ReactNode}) => ( - + {children} - + ) -const EmptyRow = () => +const EmptyRow = () => const Rows = React.memo(function Rows(props: Props & {listKey: string}) { const {items, emptyMode, destinationPickerIndex, listKey} = props @@ -187,7 +187,6 @@ const styles = Kb.Styles.styleSheetCreate( ...Kb.Styles.globalStyles.flexGrow, }, rowContainer: { - ...Kb.Styles.globalStyles.flexBoxColumn, flexShrink: 0, height: normalRowHeight, }, diff --git a/shared/fs/browser/rows/tlf.tsx b/shared/fs/browser/rows/tlf.tsx index 1092deddbaff..63728625d969 100644 --- a/shared/fs/browser/rows/tlf.tsx +++ b/shared/fs/browser/rows/tlf.tsx @@ -53,13 +53,13 @@ const TLFContainer = (p: OwnProps) => { ) const avatar = ( - + {FS.isTeamPath(path) ? ( ) : ( )} - + ) return ( @@ -70,7 +70,7 @@ const TLFContainer = (p: OwnProps) => { onOpen={disabled ? undefined : onOpen} mixedMode={mixedMode} writingToJournal={false} - body={Kb.Styles.isMobile ? {content} : undefined} + body={Kb.Styles.isMobile ? {content} : undefined} content={ !Kb.Styles.isMobile ? ( <> diff --git a/shared/fs/common/item-icon.tsx b/shared/fs/common/item-icon.tsx index 8b0d42789caf..c163b7045d15 100644 --- a/shared/fs/common/item-icon.tsx +++ b/shared/fs/common/item-icon.tsx @@ -78,21 +78,21 @@ const TlfTypeIcon = (props: TlfTypeIconProps) => { const badgeCount = FS.computeBadgeNumberForTlfList(tlfList) const badgeStyle = badgeStyles[getIconSizeString(props.size)] return ( - + {getTlfTypeIcon(props.size, props.tlfType)} {props.badgeOverride ? ( - + color={Kb.Styles.globalColors.greyDarker} - + ) : ( !!badgeCount && ( - + - + ) )} - + ) } @@ -104,23 +104,23 @@ type TlfIconProps = { } const TlfIcon = (props: TlfIconProps) => ( - + {props.tlfTypeForFolderIconOverride ? ( getTlfTypeIcon(props.size, props.tlfTypeForFolderIconOverride) ) : ( )} {!!props.badgeOverride && ( - + - + )} - + ) type InTlfItemIconProps = { @@ -139,7 +139,7 @@ const InTlfIcon = (props: InTlfItemIconProps) => { const badgeStyle = badgeStyles[getIconSizeString(props.size)] const badgeIcon = props.badgeOverride || (downloadIntent && 'icon-addon-file-downloading') return ( - + {pathItem.type === T.FS.PathType.Folder ? ( props.tlfTypeForFolderIconOverride ? ( getTlfTypeIcon(props.size, props.tlfTypeForFolderIconOverride) @@ -150,15 +150,15 @@ const InTlfIcon = (props: InTlfItemIconProps) => { )} {badgeIcon ? ( - + - + ) : null} - + ) } diff --git a/shared/fs/common/kbfs-path.tsx b/shared/fs/common/kbfs-path.tsx index c49cd7cd96e7..4fa72cf7dbec 100644 --- a/shared/fs/common/kbfs-path.tsx +++ b/shared/fs/common/kbfs-path.tsx @@ -87,14 +87,15 @@ const KbfsPath = (props: Props) => { {popup} ) : ( - setShowing(true)} onMouseLeave={() => setShowing(false)} > {text} {popup} - + ) } diff --git a/shared/fs/common/path-item-action/header.tsx b/shared/fs/common/path-item-action/header.tsx index bad49b1b3836..62a5b18adc17 100644 --- a/shared/fs/common/path-item-action/header.tsx +++ b/shared/fs/common/path-item-action/header.tsx @@ -6,7 +6,7 @@ import PathInfo from '../path-info' export type Props = {path: T.FS.Path} const Header = (props: Props) => ( - ( - + ) export default Header diff --git a/shared/fs/common/path-item-info.tsx b/shared/fs/common/path-item-info.tsx index bd9d22d388f1..c02b1c3d2440 100644 --- a/shared/fs/common/path-item-info.tsx +++ b/shared/fs/common/path-item-info.tsx @@ -93,7 +93,7 @@ const PathItemInfo = (props: Props) => { - {name} + {name} {pathItem.type === T.FS.PathType.File && ( {FS.humanReadableFileSize(pathItem.size)} )} @@ -111,7 +111,6 @@ const styles = Kb.Styles.styleSheetCreate( ({ nameTextBox: Kb.Styles.platformStyles({ common: { - ...Kb.Styles.globalStyles.flexBoxRow, flexWrap: 'wrap', justifyContent: 'center', }, diff --git a/shared/fs/common/path-status-icon.tsx b/shared/fs/common/path-status-icon.tsx index e2094df33885..429e843b0d84 100644 --- a/shared/fs/common/path-status-icon.tsx +++ b/shared/fs/common/path-status-icon.tsx @@ -98,7 +98,7 @@ const PathStatusIcon = React.memo(function PathStatusIcon(props: Props) { ) : props.isTlfType ? ( ) : ( - + ) }) diff --git a/shared/fs/common/pie-slice.tsx b/shared/fs/common/pie-slice.tsx index 8490f2dad3d8..da52293a3d7b 100644 --- a/shared/fs/common/pie-slice.tsx +++ b/shared/fs/common/pie-slice.tsx @@ -11,9 +11,10 @@ const Slice = (props: Props) => { const styleFilled = props.negative ? styles.filledNegative : styles.filledPositive const styleUnfilled = props.negative ? styles.unfilledNegative : styles.unfilledPositive return ( - - - + + { }), ])} > - - - + + - + ) } diff --git a/shared/fs/common/sfmi-popup.tsx b/shared/fs/common/sfmi-popup.tsx index 33aa3bbe952b..db25ac7e9cef 100644 --- a/shared/fs/common/sfmi-popup.tsx +++ b/shared/fs/common/sfmi-popup.tsx @@ -29,7 +29,7 @@ const SFMIPopup = (props: Props) => { return ( - { e.stopPropagation() @@ -56,7 +56,7 @@ const SFMIPopup = (props: Props) => { onClick={enableDriver} /> - + ) }, diff --git a/shared/fs/filepreview/bare-preview.tsx b/shared/fs/filepreview/bare-preview.tsx index f9d95b402178..cc4a6f083ec3 100644 --- a/shared/fs/filepreview/bare-preview.tsx +++ b/shared/fs/filepreview/bare-preview.tsx @@ -15,27 +15,27 @@ const ConnectedBarePreview = (ownProps: OwnProps) => { const onUrlError = Kbfs.useFsFileContext(path) return ( - - + + Close - - + + - - + + - +
- + ) } @@ -49,7 +49,6 @@ const styles = Kb.Styles.styleSheetCreate( }, container: Kb.Styles.platformStyles({ common: { - ...Kb.Styles.globalStyles.flexBoxColumn, ...Kb.Styles.globalStyles.flexGrow, backgroundColor: Kb.Styles.globalColors.blackOrBlack, }, @@ -58,14 +57,10 @@ const styles = Kb.Styles.styleSheetCreate( ...Kb.Styles.globalStyles.flexGrow, }, footer: { - ...Kb.Styles.globalStyles.flexBoxRow, - alignItems: 'center', height: 48, paddingLeft: Kb.Styles.globalMargins.tiny, }, header: { - ...Kb.Styles.globalStyles.flexBoxRow, - alignItems: 'center', paddingLeft: Kb.Styles.globalMargins.tiny, }, text: { diff --git a/shared/fs/filepreview/view.tsx b/shared/fs/filepreview/view.tsx index aad5f4757683..3a5fef1d5ba8 100644 --- a/shared/fs/filepreview/view.tsx +++ b/shared/fs/filepreview/view.tsx @@ -57,14 +57,14 @@ const FilePreviewViewContent = ({path, onUrlError}: Props) => { } const reloadBanner = loadedLastModifiedTimestamp !== pathItem.lastModifiedTimestamp && ( - + - + ) // Electron caches aggressively and doesn't really probe server to @@ -141,7 +141,6 @@ const styles = Kb.Styles.styleSheetCreate( }, bannerContainer: { position: 'relative', - width: '100%', zIndex: 200, // needed for mobile }, container: { diff --git a/shared/fs/footer/download.tsx b/shared/fs/footer/download.tsx index 9cf91a45cefb..37210707c49c 100644 --- a/shared/fs/footer/download.tsx +++ b/shared/fs/footer/download.tsx @@ -14,16 +14,17 @@ export type Props = { const getProgress = (dlState: T.FS.DownloadState) => ( - - - + + - + {formatDurationFromNowTo(dlState.endEstimate)} diff --git a/shared/fs/footer/downloads.tsx b/shared/fs/footer/downloads.tsx index f00942abd8b0..1274ba5ccc38 100644 --- a/shared/fs/footer/downloads.tsx +++ b/shared/fs/footer/downloads.tsx @@ -64,7 +64,7 @@ const Desktop = () => { /> )} - + { {!!debugToggleShow && } {showing && ( - + - - + + {files ? `Encrypting and uploading ${files} files...` @@ -130,7 +130,7 @@ const Upload = (props: UploadProps) => { {!!timeLeft.length && ( {`${timeLeft} left`} )} - + )} @@ -144,7 +144,6 @@ const styles = Kb.Styles.styleSheetCreate( common: { height: 48, overflow: 'hidden', - width: '100%', }, }), backgroundImage: { @@ -152,10 +151,7 @@ const styles = Kb.Styles.styleSheetCreate( width: '100%', }, box: { - ...Kb.Styles.globalStyles.flexBoxColumn, - alignItems: 'center', height: 48, - justifyContent: 'center', marginTop: -48, }, text: { diff --git a/shared/fs/nav-header/mobile-header.tsx b/shared/fs/nav-header/mobile-header.tsx index 3868d0e25ba9..8c34bb4c5f94 100644 --- a/shared/fs/nav-header/mobile-header.tsx +++ b/shared/fs/nav-header/mobile-header.tsx @@ -65,7 +65,7 @@ const NavMobileHeader = (props: Props) => { {pop ? ( ) : null} - + )} diff --git a/shared/fs/top-bar/index.tsx b/shared/fs/top-bar/index.tsx index 9f5f55ee48cb..317916708293 100644 --- a/shared/fs/top-bar/index.tsx +++ b/shared/fs/top-bar/index.tsx @@ -22,7 +22,7 @@ const TopBar = (props: Props) => ( > {!Kb.Styles.isMobile && } - + {T.FS.getPathLevel(props.path) === 3 && } ) diff --git a/shared/git/delete-repo.tsx b/shared/git/delete-repo.tsx index 1be08287171f..efdb94fa110a 100644 --- a/shared/git/delete-repo.tsx +++ b/shared/git/delete-repo.tsx @@ -56,20 +56,20 @@ const Container = (ownProps: OwnProps) => { return ( - + {!!error && ( - + {error.message} - + )} Are you sure you want to delete this {teamname ? 'team ' : ''} repository? - + {!!teamname && ( { > {teamname ? `${teamname}/${_name}` : _name} - + {teamname ? 'This will permanently delete your remote files and history, and all members of the team will be notified. This action cannot be undone.' @@ -125,7 +125,7 @@ const Container = (ownProps: OwnProps) => { waitingKey={waitingKey} /> - + ) @@ -133,8 +133,6 @@ const Container = (ownProps: OwnProps) => { const styles = Kb.Styles.styleSheetCreate(() => ({ avatarBox: { - ...Kb.Styles.globalStyles.flexBoxRow, - alignItems: 'center', marginBottom: Kb.Styles.globalMargins.medium, }, buttonBar: {alignItems: 'center'}, @@ -149,8 +147,6 @@ const styles = Kb.Styles.styleSheetCreate(() => ({ }, container: Kb.Styles.platformStyles({ common: { - ...Kb.Styles.globalStyles.flexBoxColumn, - alignItems: 'center', flex: 1, height: '100%', }, diff --git a/shared/git/new-repo.tsx b/shared/git/new-repo.tsx index 2672731dc6a7..e771eccd7320 100644 --- a/shared/git/new-repo.tsx +++ b/shared/git/new-repo.tsx @@ -57,21 +57,21 @@ const Container = (ownProps: OwnProps) => { if (item === NewTeamSentry) { return ( - New team... - + ) } return ( - + { return ( - + {!!error && ( - + {error.message} - + )} New {isTeam ? 'team' : 'personal'} git repository @@ -175,7 +175,7 @@ const Container = (ownProps: OwnProps) => { waitingKey={waitingKey} /> - + ) @@ -186,8 +186,6 @@ const styles = Kb.Styles.styleSheetCreate( ({ addIcon: {marginBottom: 27}, avatarBox: { - ...Kb.Styles.globalStyles.flexBoxRow, - alignItems: 'center', paddingLeft: Kb.Styles.globalMargins.xsmall, paddingRight: Kb.Styles.globalMargins.small, width: '100%', @@ -199,8 +197,6 @@ const styles = Kb.Styles.styleSheetCreate( }, container: Kb.Styles.platformStyles({ common: { - ...Kb.Styles.globalStyles.flexBoxColumn, - alignItems: 'center', flex: 1, height: '100%', padding: Kb.Styles.isMobile ? Kb.Styles.globalMargins.tiny : Kb.Styles.globalMargins.large, diff --git a/shared/git/row.tsx b/shared/git/row.tsx index af0167090a53..ae42d3340397 100644 --- a/shared/git/row.tsx +++ b/shared/git/row.tsx @@ -93,17 +93,16 @@ const ConnectedRow = React.memo(function ConnectedRow(ownProps: OwnProps) { const url = gitURL // TODO use ListItem2 return ( - - - + + - + )} - + {expanded && ( - - + - - + )} {!!teamname && !!lastEditUser && ( - + openUserTracker(lastEditUser)} /> - + )} {Kb.Styles.isMobile && . } @@ -190,9 +191,9 @@ const ConnectedRow = React.memo(function ConnectedRow(ownProps: OwnProps) { . - + {!!teamname && ( - + {canEdit && ( )} - + )} )} - + )} - - - + + - + ) }) const styles = Kb.Styles.styleSheetCreate( () => ({ - container: {width: '100%'}, containerMobile: Kb.Styles.platformStyles({ - common: {width: '100%'}, isMobile: { paddingLeft: Kb.Styles.globalMargins.small, paddingRight: Kb.Styles.globalMargins.small, @@ -305,6 +299,10 @@ const styles = Kb.Styles.styleSheetCreate( ...Kb.Styles.globalStyles.italic, color: Kb.Styles.globalColors.black_50, }, + expandedSpacer: { + backgroundColor: Kb.Styles.globalColors.blueLighter3, + height: 6, + }, iconCaret: Kb.Styles.platformStyles({ common: { marginBottom: 2, @@ -319,10 +317,8 @@ const styles = Kb.Styles.styleSheetCreate( marginLeft: 6, }, rowBottom: { - ...Kb.Styles.globalStyles.flexBoxColumn, paddingBottom: Kb.Styles.globalMargins.tiny, paddingLeft: Kb.Styles.globalMargins.medium, - width: '100%', }, rowClick: { ...Kb.Styles.globalStyles.flexBoxColumn, @@ -338,19 +334,13 @@ const styles = Kb.Styles.styleSheetCreate( }, rowStyle: { - ...Kb.Styles.globalStyles.flexBoxColumn, - alignItems: 'flex-start', flexShrink: 0, minHeight: Kb.Styles.globalMargins.large, paddingLeft: 0, - width: '100%', }, rowTop: Kb.Styles.platformStyles({ common: { - ...Kb.Styles.globalStyles.flexBoxRow, - alignItems: 'center', marginBottom: Kb.Styles.globalMargins.xtiny, - width: '100%', }, isElectron: {paddingLeft: Kb.Styles.globalMargins.tiny}, }), diff --git a/shared/git/select-channel.tsx b/shared/git/select-channel.tsx index a4b40c3af1af..2b1da5b21a7a 100644 --- a/shared/git/select-channel.tsx +++ b/shared/git/select-channel.tsx @@ -38,14 +38,14 @@ const SelectChannel = (ownProps: OwnProps) => { Select a channel {channelNames.map(name => ( - + selected && setSelected(name)} /> - + ))} @@ -71,7 +71,6 @@ const styles = Kb.Styles.styleSheetCreate(() => ({ marginLeft: Kb.Styles.globalMargins.tiny, }, row: { - ...Kb.Styles.globalStyles.flexBoxRow, paddingLeft: Kb.Styles.globalMargins.tiny, paddingRight: Kb.Styles.globalMargins.tiny, }, diff --git a/shared/login/forms/container.native.tsx b/shared/login/forms/container.native.tsx index 386e00905714..ecc88699501f 100644 --- a/shared/login/forms/container.native.tsx +++ b/shared/login/forms/container.native.tsx @@ -5,7 +5,7 @@ import NativeScrollView from '@/common-adapters/scroll-view.native' const Container = ({children, style, outerStyle}: Props) => { return ( - {children} + {children} ) } @@ -20,7 +20,6 @@ const styles = Kb.Styles.styleSheetCreate( paddingRight: Kb.Styles.globalMargins.medium, }, innerContainer: { - ...Kb.Styles.globalStyles.flexBoxColumn, flexGrow: 1, marginTop: Kb.Styles.globalMargins.medium, }, diff --git a/shared/login/relogin/dropdown.native.tsx b/shared/login/relogin/dropdown.native.tsx index 4ba86b5e3776..38cc33b62ad1 100644 --- a/shared/login/relogin/dropdown.native.tsx +++ b/shared/login/relogin/dropdown.native.tsx @@ -99,30 +99,30 @@ const Dropdown = (props: Props) => { if (Kb.Styles.isIOS) { return ( showModal(true)}> - + showModal(false)} > - + showModal(false)}> - + {picker} - + {labelAndCaret} - + ) } else { return ( - + {labelAndCaret} {picker} - + ) } } @@ -131,8 +131,6 @@ const styles = Kb.Styles.styleSheetCreate( () => ({ container: { - ...Kb.Styles.globalStyles.flexBoxRow, - alignItems: 'center', backgroundColor: Kb.Styles.globalColors.white, borderColor: Kb.Styles.globalColors.black_10, borderRadius: Kb.Styles.borderRadius, diff --git a/shared/login/relogin/index.native.tsx b/shared/login/relogin/index.native.tsx index 58f5598f4a8d..75bcc4c729cb 100644 --- a/shared/login/relogin/index.native.tsx +++ b/shared/login/relogin/index.native.tsx @@ -21,18 +21,20 @@ const LoginRender = (props: Props) => { } return ( - setScrollViewHeight(evt.nativeEvent.layout.height)} style={Kb.Styles.globalStyles.flexOne} > - + {C.isAndroid && !C.isDeviceSecureAndroid && !C.isAndroidNewerThanM && ( - + {"Since you don't have a lock screen, you'll have to type your password everytime."} - + )} {!!props.error && {props.error}} @@ -90,9 +92,9 @@ const LoginRender = (props: Props) => { style={{flexGrow: 0}} /> - + - + ) } @@ -107,8 +109,6 @@ const styles = Kb.Styles.styleSheetCreate( isTablet: {paddingBottom: 0}, }), container: { - ...Kb.Styles.globalStyles.flexBoxColumn, - alignItems: 'center', backgroundColor: Kb.Styles.globalColors.blueGrey, flex: 1, }, diff --git a/shared/login/user-card/index.native.tsx b/shared/login/user-card/index.native.tsx index 979959a8d36e..8cdfcebcd51b 100644 --- a/shared/login/user-card/index.native.tsx +++ b/shared/login/user-card/index.native.tsx @@ -5,9 +5,10 @@ const UserCard = (p: Props) => { const {avatarBackgroundStyle, avatarSize = 96, outerStyle, onAvatarClicked} = p const {username, style, children, lighterPlaceholders} = p return ( - - - + + { username={username} lighterPlaceholders={lighterPlaceholders} /> - - {children} - + + {children} + ) } const styles = Kb.Styles.styleSheetCreate(() => ({ avatar: { - ...Kb.Styles.globalStyles.flexBoxColumn, - alignItems: 'center', - alignSelf: 'stretch', marginTop: 0, }, avatarBackground: { @@ -43,20 +41,14 @@ const styles = Kb.Styles.styleSheetCreate(() => ({ }, container: Kb.Styles.platformStyles({ common: { - ...Kb.Styles.globalStyles.flexBoxColumn, - alignItems: 'stretch', - width: '100%', }, isTablet: { maxWidth: 410, }, }), inside: { - ...Kb.Styles.globalStyles.flexBoxColumn, - alignItems: 'stretch', justifyContent: 'flex-start', padding: 16, - width: '100%', }, })) diff --git a/shared/menubar/files.desktop.tsx b/shared/menubar/files.desktop.tsx index 1182ceb25a41..ea320164a0b2 100644 --- a/shared/menubar/files.desktop.tsx +++ b/shared/menubar/files.desktop.tsx @@ -37,9 +37,9 @@ export const FileUpdate = (props: FileUpdateProps) => ( {props.uploading && ( - + - + )} diff --git a/shared/menubar/index.desktop.tsx b/shared/menubar/index.desktop.tsx index e2961e3a28b4..c81b75b075ee 100644 --- a/shared/menubar/index.desktop.tsx +++ b/shared/menubar/index.desktop.tsx @@ -41,7 +41,8 @@ export type Props = Pick { const isDarkMode = useColorScheme() === 'dark' return ( - { const badgeCountInMenu = badgesInMenu.reduce((acc, val) => navBadges.get(val) ?? 0 + acc, 0) const isDarkMode = useColorScheme() === 'dark' return ( - - + {showBadges ? badgeTypesInHeader.map(tab => ( )) : null} - - + { ref={popupAnchor} /> {!!badgeCountInMenu && } - + {popup} - + ) } @@ -269,7 +274,7 @@ const LoggedIn = (p: Props) => { )} - + { totalSyncingBytes={totalSyncingBytes} smallMode={true} /> - + ) } @@ -355,10 +360,10 @@ const TabView = (p: {title: string; iconType: Kb.IconType; count?: number}) => { const {count, iconType, title} = p return ( - + {!!count && } - + {title} @@ -389,7 +394,8 @@ const BadgeIcon = (p: {tab: Tabs; countMap: ReadonlyMap; openApp } return ( - ; openApp type={iconType} /> {!!count && } - + ) } @@ -432,8 +438,6 @@ const styles = Kb.Styles.styleSheetCreate(() => ({ flexOne: {flexGrow: 1}, footer: {width: 360}, headerBadgesContainer: { - ...Kb.Styles.globalStyles.flexBoxRow, - alignItems: 'center', flex: 1, justifyContent: 'center', marginLeft: 24 + 8, @@ -444,8 +448,6 @@ const styles = Kb.Styles.styleSheetCreate(() => ({ }, navIcons: {paddingLeft: Kb.Styles.globalMargins.xtiny, paddingRight: Kb.Styles.globalMargins.xtiny}, topRow: { - ...Kb.Styles.globalStyles.flexBoxRow, - alignItems: 'center', borderTopLeftRadius: Kb.Styles.globalMargins.xtiny, borderTopRightRadius: Kb.Styles.globalMargins.xtiny, flex: 1, diff --git a/shared/package.json b/shared/package.json index f9a28d172ca9..d6aa96a9d201 100644 --- a/shared/package.json +++ b/shared/package.json @@ -50,7 +50,7 @@ "rn-jsbuild-ios": "mkdir -p ios/dist && react-native bundle --platform ios --dev false --entry-file index.ios.js --bundle-output ios/dist/main.jsbundle --sourcemap-output ios/dist/main.jsbundle.sourcemap", "rn-jsbuild-android": "mkdir -p android/dist && react-native bundle --platform android --dev false --entry-file index.android.js --bundle-output android/dist/main.jsbundle --sourcemap-output android/dist/main.jsbundle.sourcemap", "tsc": "./node_modules/.bin/tsgo --project ./tsconfig.json", - "modules": "yarn install --pure-lockfile --ignore-optional --ignore-scripts && yarn postinstall && cd node_modules/electron && yarn postinstall", + "modules": "yarn install --pure-lockfile --ignore-optional --ignore-scripts && yarn postinstall && cd node_modules/electron && yarn postinstall && cd ../.. && cd node_modules/@typescript/native-preview && yarn install", "log-to-trace": "yarn _node desktop/yarn-helper/log-to-trace", "pod-clean": "pod cache clean --all ; rm -rf ios/build; rm -rf ios/Pods; rm -f ios/.xcode.env.local; rm -rf ../rnmodules/react-native-kb/node_modules", "pod-install": "cd ios; pod install --repo-update", diff --git a/shared/people/follow-suggestions.tsx b/shared/people/follow-suggestions.tsx index fc52381711c9..018ce1512e86 100644 --- a/shared/people/follow-suggestions.tsx +++ b/shared/people/follow-suggestions.tsx @@ -8,7 +8,7 @@ export type Props = { } const FollowSuggestions = (props: Props) => ( - + Consider following... @@ -29,13 +29,12 @@ const FollowSuggestions = (props: Props) => ( /> ))} - + ) export default FollowSuggestions const styles = Kb.Styles.styleSheetCreate(() => ({ container: { - ...Kb.Styles.globalStyles.flexBoxColumn, backgroundColor: Kb.Styles.globalColors.fastBlank, paddingTop: Kb.Styles.globalMargins.tiny, position: 'relative', diff --git a/shared/people/index.shared.tsx b/shared/people/index.shared.tsx index e51585aa6339..94e6e091a21c 100644 --- a/shared/people/index.shared.tsx +++ b/shared/people/index.shared.tsx @@ -114,7 +114,7 @@ const ResentEmailVerificationBanner = React.memo(function ResentEmailVerificatio export const PeoplePageList = React.memo(function PeoplePageList(props: Props) { return ( - + {props.newItems @@ -132,6 +132,6 @@ export const PeoplePageList = React.memo(function PeoplePageList(props: Props) { {props.oldItems.map((item): React.ReactNode => itemToComponent(item, props))} - + ) }) diff --git a/shared/people/item.tsx b/shared/people/item.tsx index 1706067f3f0c..19c99a2bf86e 100644 --- a/shared/people/item.tsx +++ b/shared/people/item.tsx @@ -25,11 +25,11 @@ export type Props = { } const PeopleItem = (props: Props) => ( - + {!!props.icon && ( - + {props.icon} - + )} ( props.buttons.length > 0 && props.buttons.map((b, idx) => React.isValidElement(b) ? ( - + {b} - + ) : ( ) )} - ( {props.badged && ( )} - - + + ) export default PeopleItem @@ -84,7 +86,6 @@ const styles = Kb.Styles.styleSheetCreate(() => ({ }, container: Kb.Styles.platformStyles({ common: { - ...Kb.Styles.globalStyles.flexBoxRow, backgroundColor: Kb.Styles.globalColors.white, borderBottomColor: Kb.Styles.globalColors.black_10, borderBottomWidth: 1, @@ -105,8 +106,6 @@ const styles = Kb.Styles.styleSheetCreate(() => ({ }, timestampContainer: Kb.Styles.platformStyles({ common: { - ...Kb.Styles.globalStyles.flexBoxRow, - alignItems: 'center', alignSelf: 'center', marginLeft: 'auto', marginRight: Kb.Styles.globalMargins.small, diff --git a/shared/pinentry/index.desktop.tsx b/shared/pinentry/index.desktop.tsx index 9fb6d42d9168..561e6871f524 100644 --- a/shared/pinentry/index.desktop.tsx +++ b/shared/pinentry/index.desktop.tsx @@ -38,9 +38,9 @@ const Pinentry = (props: Props) => { const isPaperKey = props.type === T.RPCGen.PassphraseType.paperKey return ( - + - + {props.prompt} @@ -83,15 +83,14 @@ const Pinentry = (props: Props) => { onClick={handleSubmit} disabled={!password} /> - - + + ) } const styles = Kb.Styles.styleSheetCreate(() => ({ alignment: {marginLeft: Kb.Styles.globalMargins.xsmall}, container: { - ...Kb.Styles.globalStyles.flexBoxColumn, backgroundColor: Kb.Styles.globalColors.white, paddingBottom: Kb.Styles.globalMargins.medium, }, diff --git a/shared/profile/edit-avatar/index.desktop.tsx b/shared/profile/edit-avatar/index.desktop.tsx index 883b30795fc9..ba1a5a76ac12 100644 --- a/shared/profile/edit-avatar/index.desktop.tsx +++ b/shared/profile/edit-avatar/index.desktop.tsx @@ -166,11 +166,11 @@ const EditAvatar = (_p: Props) => { ])} > {type === 'team' && createdTeam && !wizard && ( - + Hoorah! Your team {teamname} was created. - + )} Drag and drop a {type} avatar or{' '} @@ -179,7 +179,7 @@ const EditAvatar = (_p: Props) => { {' '} for one. - { type="iconfont-camera" /> )} - + {loading === 'loaded' ? Click to select. Scroll to zoom. : null}
diff --git a/shared/profile/edit-avatar/index.native.tsx b/shared/profile/edit-avatar/index.native.tsx index 30ef727a7bc2..e91766d8fa3a 100644 --- a/shared/profile/edit-avatar/index.native.tsx +++ b/shared/profile/edit-avatar/index.native.tsx @@ -147,7 +147,7 @@ const AvatarUploadWrapper = (p: Props) => { > {renderImageZoomer()} - + { {error} ) : null} - - {renderImageZoomer()} + + {renderImageZoomer()} { waitingKey={waitingKey} /> - + ) } diff --git a/shared/profile/generic/proofs-list.tsx b/shared/profile/generic/proofs-list.tsx index 92608611f7d4..28be4cc9ec4e 100644 --- a/shared/profile/generic/proofs-list.tsx +++ b/shared/profile/generic/proofs-list.tsx @@ -32,14 +32,14 @@ const Container = () => { return ( - + {!Kb.Styles.isMobile && ( Prove your... )} - + { style={styles.text} value={filter} /> - + { - + ) } @@ -208,8 +208,6 @@ const styles = Kb.Styles.styleSheetCreate( }, iconArrow: {marginRight: Kb.Styles.globalMargins.small}, inputContainer: { - ...Kb.Styles.globalStyles.flexBoxRow, - alignItems: 'center', backgroundColor: Kb.Styles.globalColors.black_10, borderRadius: Kb.Styles.borderRadius, marginBottom: Kb.Styles.globalMargins.xsmall, diff --git a/shared/profile/generic/shared.tsx b/shared/profile/generic/shared.tsx index 8d75636ce461..4dcd2da9008e 100644 --- a/shared/profile/generic/shared.tsx +++ b/shared/profile/generic/shared.tsx @@ -24,7 +24,8 @@ export const SiteIcon = (props: SiteIconProps) => { style={Kb.Styles.collapseStyles([style, props.style])} /> ) : ( - - + {"# import a key from gpg's key chain"} keybase pgp select # for more options keybase pgp help - + ) } @@ -38,8 +38,6 @@ const styleBody = { } const styleTerminal = { - ...Kb.Styles.globalStyles.flexBoxColumn, - alignItems: 'stretch', backgroundColor: Kb.Styles.globalColors.blueDarker2, borderRadius: 4, boxSizing: 'content-box', diff --git a/shared/profile/post-proof.tsx b/shared/profile/post-proof.tsx index 74f1cc9fd63c..f58be7fe5e74 100644 --- a/shared/profile/post-proof.tsx +++ b/shared/profile/post-proof.tsx @@ -157,7 +157,7 @@ const WebDescription = ({platformUserName}: {platformUserName: string}) => { const root = `${platformUserName}/keybase.txt` const wellKnown = `${platformUserName}/.well-known/keybase.txt` return ( - + Please serve the text below exactly as it appears {" at one of these URL's."} @@ -178,7 +178,7 @@ const WebDescription = ({platformUserName}: {platformUserName: string}) => { > {wellKnown} - + ) } diff --git a/shared/profile/revoke.tsx b/shared/profile/revoke.tsx index aa34864f41ae..9b28c1f324a1 100644 --- a/shared/profile/revoke.tsx +++ b/shared/profile/revoke.tsx @@ -32,17 +32,17 @@ const RevokeProof = (ownProps: OwnProps) => { return ( {!!errorMessage && ( - + {errorMessage} - + )} - - + + - + {platformHandle} @@ -66,7 +66,7 @@ const RevokeProof = (ownProps: OwnProps) => { waitingKey={C.waitingKeyProfile} /> - + ) } @@ -75,18 +75,13 @@ const styles = Kb.Styles.styleSheetCreate( () => ({ contentContainer: { - ...Kb.Styles.globalStyles.flexBoxColumn, - alignItems: 'center', flexGrow: 1, - justifyContent: 'center', margin: Kb.Styles.isMobile ? Kb.Styles.globalMargins.tiny : Kb.Styles.globalMargins.large, maxWidth: 512, textAlign: Kb.Styles.isMobile ? undefined : 'center', }, descriptionText: {marginTop: Kb.Styles.globalMargins.medium}, errorBanner: { - ...Kb.Styles.globalStyles.flexBoxColumn, - alignItems: 'center', backgroundColor: Kb.Styles.globalColors.red, justifyContent: 'center', minHeight: Kb.Styles.globalMargins.large, diff --git a/shared/profile/showcase-team-offer.tsx b/shared/profile/showcase-team-offer.tsx index 12c43db82598..d23cd99028f4 100644 --- a/shared/profile/showcase-team-offer.tsx +++ b/shared/profile/showcase-team-offer.tsx @@ -113,7 +113,7 @@ const TeamRow = (p: RowProps) => { } const ShowcaseTeamOfferHeader = () => ( - + {!Kb.Styles.isMobile && ( {"Feature the teams you're in"} @@ -125,7 +125,7 @@ const ShowcaseTeamOfferHeader = () => ( members will be public. - + ) const styles = Kb.Styles.styleSheetCreate( diff --git a/shared/provision/code-page/qr-scan/lines.tsx b/shared/provision/code-page/qr-scan/lines.tsx index 31b8b9b01305..f1db23d7fa5f 100644 --- a/shared/provision/code-page/qr-scan/lines.tsx +++ b/shared/provision/code-page/qr-scan/lines.tsx @@ -8,31 +8,39 @@ const QRScanLines = ({canScan, color}: {canScan: boolean; color?: Kb.Styles.Colo const s = [styles.common, {backgroundColor: color}] return canScan ? ( <> - - - - - - - - {/* TODO have headerLeft be the back button */} {headerLeft !== null && ( - - + )} { } const Badge = (p: {backgroundColor: string; menuItem?: boolean}) => ( - { const onClearLogs = useSettingsState(s => s.dispatch.clearLogs) return ( - + {"Please don't do anything below here unless instructed to by a developer."} @@ -309,8 +309,8 @@ const Developer = () => { )} - - + + ) } @@ -321,8 +321,6 @@ const styles = Kb.Styles.styleSheetCreate( marginTop: Kb.Styles.globalMargins.small, }, developerContainer: { - ...Kb.Styles.globalStyles.flexBoxColumn, - alignItems: 'center', flex: 1, paddingBottom: Kb.Styles.globalMargins.medium, }, diff --git a/shared/settings/archive/index.tsx b/shared/settings/archive/index.tsx index 36eebb035e7e..b107ce6c1297 100644 --- a/shared/settings/archive/index.tsx +++ b/shared/settings/archive/index.tsx @@ -233,7 +233,7 @@ const KBFSJob = React.memo(function KBFSJob(p: {index: number; id: string}) { {job.gitRepo ?? job.kbfsPath} - {C.isMobile ? null : } + {C.isMobile ? null : } {C.isMobile ? null : job.bytesTotal ? ( {FS.humanReadableFileSize(job.bytesTotal)} ) : null} @@ -252,7 +252,7 @@ const KBFSJob = React.memo(function KBFSJob(p: {index: number; id: string}) { > {Math.round(progress * 100) + '%'} - + {errorStr && ( diff --git a/shared/settings/chat.tsx b/shared/settings/chat.tsx index 1759ee2dbaa1..ff558b5b8f2c 100644 --- a/shared/settings/chat.tsx +++ b/shared/settings/chat.tsx @@ -292,8 +292,8 @@ const Links = () => { {getUnfurlWhitelist(false).map((w, idx) => { const wlremoved = unfurlWhitelistRemoved[w] return ( - - {idx === 0 && } + + {idx === 0 && } { Restore ) : ( - + toggleUnfurlWhitelist(w)} @@ -320,10 +320,10 @@ const Links = () => { type="iconfont-trash" /> - + )} - + ) })} diff --git a/shared/settings/common.desktop.tsx b/shared/settings/common.desktop.tsx index 526004e2fb7e..2dd0210bbdda 100644 --- a/shared/settings/common.desktop.tsx +++ b/shared/settings/common.desktop.tsx @@ -3,11 +3,11 @@ import range from 'lodash/range' function Stars({count}: {count: number}) { return ( - + {range(count).map(i => ( ))} - + ) } diff --git a/shared/settings/contacts-joined.tsx b/shared/settings/contacts-joined.tsx index 994dcd5a5a85..7c63bb4161cf 100644 --- a/shared/settings/contacts-joined.tsx +++ b/shared/settings/contacts-joined.tsx @@ -61,9 +61,9 @@ const Item = ({item}: {item: T.RPCGen.ProcessedContact}) => { } return ( - + - + {label} diff --git a/shared/settings/db-nuke.confirm.tsx b/shared/settings/db-nuke.confirm.tsx index d286b5367ddd..f06fdf93649b 100644 --- a/shared/settings/db-nuke.confirm.tsx +++ b/shared/settings/db-nuke.confirm.tsx @@ -14,14 +14,11 @@ const DbNukeConfirm = () => { } return ( - Are you sure you want to blast away your local database? @@ -30,7 +27,7 @@ const DbNukeConfirm = () => { - + ) } diff --git a/shared/settings/display.tsx b/shared/settings/display.tsx index f7ab52aafed4..10b6d64a0a2a 100644 --- a/shared/settings/display.tsx +++ b/shared/settings/display.tsx @@ -29,7 +29,7 @@ const Display = () => { } return ( - + Appearance @@ -70,17 +70,15 @@ const Display = () => { )} - + ) } const styles = Kb.Styles.styleSheetCreate(() => ({ container: { - ...Kb.Styles.globalStyles.flexBoxColumn, flex: 1, padding: Kb.Styles.globalMargins.small, - width: '100%', }, scrollview: { width: '100%', diff --git a/shared/settings/files/index.desktop.tsx b/shared/settings/files/index.desktop.tsx index f62f7811fba2..884b736f8312 100644 --- a/shared/settings/files/index.desktop.tsx +++ b/shared/settings/files/index.desktop.tsx @@ -80,8 +80,8 @@ const FinderIntegration = () => { return Platform.isDarwin || Platform.isWindows ? ( <> - - + + {Platform.fileUIName} integration {isPending && } {driverStatus.type === T.FS.DriverStatusType.Disabled && driverStatus.kextPermissionError && ( @@ -124,7 +124,7 @@ const FinderIntegration = () => { )} - + @@ -141,8 +141,8 @@ const FilesSettings = () => { - - + + File sync { disabled={props.areSettingsLoading} style={styles.syncNotificationCheckbox} /> - + diff --git a/shared/settings/invite-generated/index.desktop.tsx b/shared/settings/invite-generated/index.desktop.tsx index 61c4f07e4851..fb4f70956367 100644 --- a/shared/settings/invite-generated/index.desktop.tsx +++ b/shared/settings/invite-generated/index.desktop.tsx @@ -6,14 +6,11 @@ const InviteGeneratedRender = (props: Props) => { const {link, email} = props const onClose = C.useRouterState(s => s.dispatch.navigateUp) return ( - @@ -27,7 +24,7 @@ const InviteGeneratedRender = (props: Props) => { Yay! Please share the below link with your friend. It contains signup & install instructions. )} - + { {link} - + - + ) } @@ -52,8 +49,6 @@ const styles = Kb.Styles.styleSheetCreate(() => ({ }, }), linkContainer: { - ...Kb.Styles.globalStyles.flexBoxRow, - alignItems: 'center', backgroundColor: Kb.Styles.globalColors.greenLighter, borderRadius: Kb.Styles.borderRadius, height: 32, diff --git a/shared/settings/invites/index.desktop.tsx b/shared/settings/invites/index.desktop.tsx index c9dd76394d51..9389385972b9 100644 --- a/shared/settings/invites/index.desktop.tsx +++ b/shared/settings/invites/index.desktop.tsx @@ -75,16 +75,17 @@ const Invites = () => { } return ( - + {!!error && ( )} - { /> {pendingInvites.length > 0 && ( - + Pending invites ({pendingInvites.length}) {intersperseDividers( pendingInvites.map(invite => ( @@ -126,9 +127,9 @@ const Invites = () => { /> )) )} - + )} - + Accepted invites ({acceptedInvites.length}) {intersperseDividers( acceptedInvites.map(invite => ( @@ -139,9 +140,9 @@ const Invites = () => { /> )) )} - - - + + + ) } @@ -159,13 +160,13 @@ function PendingInviteItem({ onSelectPendingInvite: (invite: PendingInvite) => void }) { return ( - + {invite.email ? ( ) : ( )} - + onReclaimInvitation(invite.id)} @@ -173,7 +174,7 @@ function PendingInviteItem({ > Reclaim - + ) } @@ -185,23 +186,23 @@ function PendingEmailContent({ onSelectPendingInvite: (invite: PendingInvite) => void }) { return ( - + - + onSelectPendingInvite(invite)}> {invite.email} Invited {dateFns.format(dateFns.fromUnixTime(invite.created), 'MMM d, yyyy')} - - + + ) } function PendingURLContent({invite}: {invite: PendingInvite}) { return ( - + {invite.url} - + ) } function AcceptedInviteItem(p: {invite: AcceptedInvite; onClick: () => void}) { const {invite, onClick} = p return ( - - + - - + + ) } const styles = Kb.Styles.styleSheetCreate(() => ({ container: { + alignSelf: 'flex-start' as const, marginTop: Kb.Styles.globalMargins.small, minHeight: 269, width: 400, }, inviteItem: { ...Kb.Styles.globalStyles.flexBoxRow, - alignItems: 'center', + alignItems: 'center' as const, flexShrink: 0, height: 40, marginLeft: Kb.Styles.globalMargins.tiny, diff --git a/shared/settings/notifications/index.native.tsx b/shared/settings/notifications/index.native.tsx index d7cf7fe2d81a..d9c8a4c1ae00 100644 --- a/shared/settings/notifications/index.native.tsx +++ b/shared/settings/notifications/index.native.tsx @@ -35,17 +35,18 @@ const TurnOnNotifications = () => { if (mobileHasPermissions) return null const onEnable = requestPermissions return ( - - { }} > - + { Enable notifications - + ) } diff --git a/shared/settings/notifications/render.tsx b/shared/settings/notifications/render.tsx index cecb718a4115..1a7c68d2d392 100644 --- a/shared/settings/notifications/render.tsx +++ b/shared/settings/notifications/render.tsx @@ -39,7 +39,7 @@ const Notifications = () => { ) : ( - + {props.showEmailSection ? ( ) : ( @@ -60,7 +60,7 @@ const Notifications = () => { ) : null} - + ) } @@ -74,7 +74,7 @@ const styles = Kb.Styles.styleSheetCreate( }, loading: {alignItems: 'center', flex: 1, justifyContent: 'center'}, main: Kb.Styles.platformStyles({ - common: {flex: 1, padding: Kb.Styles.globalMargins.small, paddingRight: 0, width: '100%'}, + common: {flex: 1, padding: Kb.Styles.globalMargins.small, paddingRight: 0}, isElectron: Kb.Styles.desktopStyles.scrollable, }), }) as const diff --git a/shared/settings/proxy.tsx b/shared/settings/proxy.tsx index 28ee5dba5c54..4a4071dd8894 100644 --- a/shared/settings/proxy.tsx +++ b/shared/settings/proxy.tsx @@ -170,22 +170,22 @@ const ProxySettingsPopup = (props: Props) => { if (Kb.Styles.isMobile) { return ( - - + + - - + + ) } return ( - + - + - - + + ) } diff --git a/shared/settings/subheading.desktop.tsx b/shared/settings/subheading.desktop.tsx index d751df255603..2bdcef6e0aa7 100644 --- a/shared/settings/subheading.desktop.tsx +++ b/shared/settings/subheading.desktop.tsx @@ -4,14 +4,14 @@ import type {Props} from './subheading' function SubHeading({children}: Props) { return ( - + {children} - + ) } diff --git a/shared/team-building/go-button.tsx b/shared/team-building/go-button.tsx index b2d827d75ceb..6a39c0c96422 100644 --- a/shared/team-building/go-button.tsx +++ b/shared/team-building/go-button.tsx @@ -8,7 +8,7 @@ export type Props = { } const GoButton = (props: Props) => ( - + @@ -32,7 +32,7 @@ const GoButton = (props: Props) => ( waitingKey={props.waitingKey} /> - + ) const styles = Kb.Styles.styleSheetCreate(() => ({ diff --git a/shared/teams/channel/index.tsx b/shared/teams/channel/index.tsx index d2168f70d831..5a62a4d944f2 100644 --- a/shared/teams/channel/index.tsx +++ b/shared/teams/channel/index.tsx @@ -255,7 +255,7 @@ const Channel = (props: OwnProps) => { } return ( - + section.title ? : null @@ -266,7 +266,7 @@ const Channel = (props: OwnProps) => { style={styles.list} /> - + ) } @@ -274,12 +274,8 @@ const styles = Kb.Styles.styleSheetCreate( () => ({ container: { - ...Kb.Styles.globalStyles.flexBoxColumn, - alignItems: 'stretch', flex: 1, - height: '100%', position: 'relative', - width: '100%', }, list: {}, listContentContainer: Kb.Styles.platformStyles({ diff --git a/shared/teams/channel/rows.tsx b/shared/teams/channel/rows.tsx index 5c77b0f6a518..b7f1edbb8c6b 100644 --- a/shared/teams/channel/rows.tsx +++ b/shared/teams/channel/rows.tsx @@ -106,11 +106,11 @@ const ChannelMemberRow = (props: Props) => { - + - + - + {fullNameLabel} {crown} {!active && ( @@ -123,7 +123,7 @@ const ChannelMemberRow = (props: Props) => { {!!active && Teams.typeToLabel[teamMemberInfo.type]} {resetLabel} - + ) @@ -294,7 +294,6 @@ const styles = Kb.Styles.styleSheetCreate( marginRight: {marginRight: Kb.Styles.globalMargins.xtiny}, mobileMarginsHack: Kb.Styles.platformStyles({isMobile: {marginRight: 48}}), // ListItem2 is malfunctioning because the checkbox width is unusual nameContainer: {flex: 1, marginLeft: Kb.Styles.globalMargins.small}, - nameContainerInner: {...Kb.Styles.globalStyles.flexBoxRow, alignItems: 'center'}, selected: {backgroundColor: Kb.Styles.globalColors.blueLighterOrBlueDarker}, widenClickableArea: {margin: -5, padding: 5}, }) as const diff --git a/shared/teams/channel/tabs.tsx b/shared/teams/channel/tabs.tsx index bc8d5526ea3a..fd3517a080eb 100644 --- a/shared/teams/channel/tabs.tsx +++ b/shared/teams/channel/tabs.tsx @@ -25,7 +25,7 @@ const ChannelTabs = (props: Props) => { return ( - + { style={styles.tabContainer} tabStyle={styles.tab} /> - + {!!error && {error}} ) @@ -46,7 +46,6 @@ const styles = Kb.Styles.styleSheetCreate(() => ({ }, container: { backgroundColor: Kb.Styles.globalColors.white, - width: '100%', }, tab: Kb.Styles.platformStyles({ isMobile: { diff --git a/shared/teams/common/selection-popup.tsx b/shared/teams/common/selection-popup.tsx index 531efe9d707a..c1d6a3d810c8 100644 --- a/shared/teams/common/selection-popup.tsx +++ b/shared/teams/common/selection-popup.tsx @@ -113,12 +113,12 @@ const JointSelectionPopup = (props: JointSelectionPopupProps) => { {!Kb.Styles.isPhone && } {children} {/* bottom safe area */} - {Kb.Styles.isPhone && } + {Kb.Styles.isPhone && } ) return Kb.Styles.isMobile ? ( <> - { 48 ? height - 48 - bottom : -bottom}} />} + { 48 ? height - 48 - bottom : -bottom}} />} {popup} ) : ( diff --git a/shared/teams/confirm-modals/confirm-kick-out.tsx b/shared/teams/confirm-modals/confirm-kick-out.tsx index 4c9d358d20a3..f8d209f0181a 100644 --- a/shared/teams/confirm-modals/confirm-kick-out.tsx +++ b/shared/teams/confirm-modals/confirm-kick-out.tsx @@ -65,7 +65,7 @@ const ConfirmKickOut = (props: Props) => { ) const header = ( - + { style={styles.headerIcon} /> - + ) return ( { const prompt = `Remove ${Teams.stringifyPeople(members)} from #${channelname}?` const header = ( - + { style={styles.headerIcon} sizeType="Small" /> - + ) return ( item.emojiToAdd.error && styles.emojiToAddRowWithError, ])} > - + - + { const hasThumbnail = !!props.pictureUri return ( - - - + + + {!!hasThumbnail && !!props.pictureUri && ( )} {!hasThumbnail && } - - + + {props.name} - - + + {props.valueFormatted || props.value} - - - - + + + + { onClick={props.onClick} style={styles.inviteButton} /> - - - + + + ) } @@ -99,7 +99,7 @@ export const InviteByContact = (props: InviteByContactProps) => { )} {hasItems && ( - + { renderItem={contactRow} style={styles.contactList} /> - + )} ) @@ -148,15 +148,10 @@ const styles = Kb.Styles.styleSheetCreate( alignSelf: 'stretch', }, contactRowBox: { - ...Kb.Styles.globalStyles.flexBoxRow, - alignItems: 'center', height: 56, padding: Kb.Styles.globalMargins.small, - width: '100%', }, contactRowInnerBox: { - ...Kb.Styles.globalStyles.flexBoxRow, - alignItems: 'center', flex: 1, }, errorMessageContainer: { @@ -179,7 +174,6 @@ const styles = Kb.Styles.styleSheetCreate( width: 100, }, listContainer: { - ...Kb.Styles.globalStyles.flexBoxColumn, flex: 1, paddingBottom: Kb.Styles.globalMargins.xtiny, }, diff --git a/shared/teams/invite-by-email.tsx b/shared/teams/invite-by-email.tsx index f0201964e4d5..c6d30557a184 100644 --- a/shared/teams/invite-by-email.tsx +++ b/shared/teams/invite-by-email.tsx @@ -59,23 +59,20 @@ const Container = (ownProps: OwnProps) => { return ( - - + Invite by email - Add these team members to {name} as: @@ -95,8 +92,8 @@ const Container = (ownProps: OwnProps) => { style={{width: 100}} /> - - + + { - - + + ) } const _makeDropdownItem = (item: string) => ( - {capitalize(item)} - + ) const styles = Kb.Styles.styleSheetCreate(() => ({ diff --git a/shared/teams/join-team/container.tsx b/shared/teams/join-team/container.tsx index 7a1e2e4df322..a745a5655594 100644 --- a/shared/teams/join-team/container.tsx +++ b/shared/teams/join-team/container.tsx @@ -73,13 +73,13 @@ const Container = (ownProps: OwnProps) => { - + Your request was sent to the admins of{' '} {successTeamName ? {successTeamName} : 'the team'}. {"Hang tight, you'll get notified as soon as you're let in."} - + )} diff --git a/shared/teams/join-team/join-from-invite.tsx b/shared/teams/join-team/join-from-invite.tsx index f87440ceac61..1f6f7d8b5fde 100644 --- a/shared/teams/join-team/join-from-invite.tsx +++ b/shared/teams/join-team/join-from-invite.tsx @@ -104,7 +104,7 @@ const JoinFromInvite = () => { gap="xtiny" style={styles.body} > - + { )} - + Join {teamname} {details.teamNumMembers.toLocaleString()} members @@ -144,7 +144,7 @@ const JoinFromInvite = () => { {!!error && {error}} - + ( - + - + Create a team on Keybase @@ -22,15 +22,15 @@ const Banner = ({onReadMore, onHideChatBanner}: Props) => ( Read more - - + + - - + + ) const styles = Kb.Styles.styleSheetCreate(() => ({ @@ -51,28 +51,21 @@ const styles = Kb.Styles.styleSheetCreate(() => ({ }), containerBanner: Kb.Styles.platformStyles({ common: { - alignItems: 'center', backgroundColor: Kb.Styles.globalColors.blue, flexShrink: 0, position: 'relative', - width: '100%', }, isElectron: { - ...Kb.Styles.globalStyles.flexBoxRow, height: 212, justifyContent: 'flex-start', paddingRight: Kb.Styles.globalMargins.large, }, isMobile: { - ...Kb.Styles.globalStyles.flexBoxColumn, justifyContent: 'center', padding: 24, }, }), containerHeader: Kb.Styles.platformStyles({ - common: { - ...Kb.Styles.globalStyles.flexBoxColumn, - }, isElectron: { maxWidth: 360, }, diff --git a/shared/teams/main/footer.tsx b/shared/teams/main/footer.tsx index fd9f2f2f4645..4d4937726349 100644 --- a/shared/teams/main/footer.tsx +++ b/shared/teams/main/footer.tsx @@ -11,15 +11,15 @@ const TeamsFooter = (props: {empty: boolean}) => { <> {props.empty && ( - - + + - + You are not a part of any team, lone wolf. - + )} - + {(Kb.Styles.isMobile || !props.empty) && ( Keybase team chats are encrypted – unlike Slack – and work for any size group, from casual diff --git a/shared/teams/main/index.tsx b/shared/teams/main/index.tsx index a98031f54722..abd48e43a7dd 100644 --- a/shared/teams/main/index.tsx +++ b/shared/teams/main/index.tsx @@ -40,10 +40,10 @@ const TeamBigButtons = (props: HeaderProps & {empty: boolean}) => ( > Create a team - + - + { return ( - + { } teamID={teamID} /> - + ) } const styles = Kb.Styles.styleSheetCreate(() => ({ container: { - ...Kb.Styles.globalStyles.flexBoxColumn, - alignItems: 'stretch', backgroundColor: Kb.Styles.globalColors.blueGrey, flex: 1, - height: '100%', position: 'relative', - width: '100%', }, list: Kb.Styles.platformStyles({}), listContentContainer: Kb.Styles.platformStyles({ diff --git a/shared/teams/team/member/add-to-channels.tsx b/shared/teams/team/member/add-to-channels.tsx index 3e3035cb798e..c6543d903f06 100644 --- a/shared/teams/team/member/add-to-channels.tsx +++ b/shared/teams/team/member/add-to-channels.tsx @@ -232,9 +232,9 @@ const AddToChannels = React.memo(function AddToChannels(props: Props) { onClose={onCancel} > {loadingChannels && !channelMetas.size ? ( - + - + ) : ( @@ -293,7 +293,7 @@ const HeaderRow = React.memo(function HeaderRow(p: { icon="iconfont-new" /> {mode === 'self' || (!onSelectAll && !onSelectNone) ? ( - // box so that the other item aligns to the left + // box so that the other item aligns to the left ) : ( {onSelectAll ? 'Select all' : 'Clear'} diff --git a/shared/teams/team/new-header.tsx b/shared/teams/team/new-header.tsx index b5f05946e03b..47210405cf1c 100644 --- a/shared/teams/team/new-header.tsx +++ b/shared/teams/team/new-header.tsx @@ -50,9 +50,9 @@ const FeatureTeamCard = ({teamID}: FeatureTeamCardProps) => { alignSelf="flex-end" fullWidth={true} > - + - + Feature team on your profile? {"So your friends or coworkers know of your team's existence."} diff --git a/shared/teams/team/rows/bot-row/bot.tsx b/shared/teams/team/rows/bot-row/bot.tsx index 8806f0744ddc..71dba895f68b 100644 --- a/shared/teams/team/rows/bot-row/bot.tsx +++ b/shared/teams/team/rows/bot-row/bot.tsx @@ -70,19 +70,19 @@ export const TeamBotRow = (props: Props) => { // TODO: switch this to a ListItem2 so that we get dividers, free styling, etc return ( - - - + + + - - {usernameDisplay} - {descriptionLabel} - - + + {usernameDisplay} + {descriptionLabel} + + {(active || C.isLargeScreen) && ( // Desktop & mobile large screen - display on the far right of the first row @@ -107,37 +107,29 @@ export const TeamBotRow = (props: Props) => { onHidden={_onHideMenu} /> - - + + ) } const styles = Kb.Styles.styleSheetCreate(() => ({ clickable: { - ...Kb.Styles.globalStyles.flexBoxRow, - alignItems: 'center', flexGrow: 1, }, container: { - ...Kb.Styles.globalStyles.flexBoxColumn, - alignItems: 'center', backgroundColor: Kb.Styles.globalColors.white, flex: 1, height: '100%', position: 'relative', - width: '100%', }, containerReset: { backgroundColor: Kb.Styles.globalColors.blueLighter2, }, fullNameLabel: {marginRight: Kb.Styles.globalMargins.xtiny}, innerContainerTop: { - ...Kb.Styles.globalStyles.flexBoxRow, ...Kb.Styles.padding(Kb.Styles.globalMargins.xsmall, Kb.Styles.globalMargins.small), - alignItems: 'center', flexShrink: 0, height: Kb.Styles.isMobile ? 56 : 48, - width: '100%', }, menuButtonDesktop: { marginLeft: Kb.Styles.globalMargins.small, @@ -153,13 +145,10 @@ const styles = Kb.Styles.styleSheetCreate(() => ({ top: 12, }, menuIconContainer: { - ...Kb.Styles.globalStyles.flexBoxRow, - alignItems: 'center', flexShrink: 1, height: '100%', }, - nameContainer: {...Kb.Styles.globalStyles.flexBoxColumn, marginLeft: Kb.Styles.globalMargins.small}, - nameContainerInner: {...Kb.Styles.globalStyles.flexBoxRow, alignItems: 'center'}, + nameContainer: {marginLeft: Kb.Styles.globalMargins.small}, })) type OwnProps = { diff --git a/shared/teams/team/rows/emoji-row/item.tsx b/shared/teams/team/rows/emoji-row/item.tsx index 41c72d14f68b..613e44d672e6 100644 --- a/shared/teams/team/rows/emoji-row/item.tsx +++ b/shared/teams/team/rows/emoji-row/item.tsx @@ -74,7 +74,7 @@ const ItemRow = ({conversationIDKey, emoji, firstItem, teamID}: OwnProps) => { const {showPopup, popup, popupAnchor} = Kb.usePopup2(makePopup) return ( - + { fullDivider={true} height={Kb.Styles.isMobile ? 48 : 42} /> - + ) } diff --git a/shared/teams/team/rows/member-row.tsx b/shared/teams/team/rows/member-row.tsx index 7fc8920b2a01..9285675978f2 100644 --- a/shared/teams/team/rows/member-row.tsx +++ b/shared/teams/team/rows/member-row.tsx @@ -118,14 +118,14 @@ export const TeamMemberRow = (props: Props) => { - + - + {fullNameLabel} @@ -293,8 +293,6 @@ const styles = Kb.Styles.styleSheetCreate(() => ({ lockedOutMeta: {marginRight: Kb.Styles.globalMargins.xtiny}, mobileMarginsHack: Kb.Styles.platformStyles({isMobile: {marginRight: 48}}), // ListItem2 is malfunctioning because the checkbox width is unusual nameContainer: { - ...Kb.Styles.globalStyles.flexBoxColumn, - alignSelf: undefined, flex: 1, justifyContent: 'center', marginLeft: Kb.Styles.globalMargins.small, diff --git a/shared/teams/team/settings-tab/open-team-warning.tsx b/shared/teams/team/settings-tab/open-team-warning.tsx index cf765f0eabb2..172beab25bfd 100644 --- a/shared/teams/team/settings-tab/open-team-warning.tsx +++ b/shared/teams/team/settings-tab/open-team-warning.tsx @@ -35,7 +35,7 @@ const OpenTeamWarning = (props: Props) => { return ( - + Make {teamname} into {isOpenTeam ? 'an open' : 'a closed'} team? @@ -70,7 +70,7 @@ const OpenTeamWarning = (props: Props) => { disabled={!enabled} /> - + ) } @@ -87,8 +87,6 @@ const styles = Kb.Styles.styleSheetCreate(() => ({ }), container: Kb.Styles.platformStyles({ common: { - ...Kb.Styles.globalStyles.flexBoxColumn, - alignItems: 'center', paddingBottom: Kb.Styles.globalMargins.large, }, isElectron: { diff --git a/shared/teams/team/settings-tab/retention/index.tsx b/shared/teams/team/settings-tab/retention/index.tsx index 0c539384b43c..1a0b23bd09e7 100644 --- a/shared/teams/team/settings-tab/retention/index.tsx +++ b/shared/teams/team/settings-tab/retention/index.tsx @@ -214,11 +214,11 @@ const RetentionPicker = (p: Props) => { const {showPopup, popup, popupAnchor} = Kb.usePopup2(makePopup) return ( - + {popup} - + Message deletion - + { )} {showOverrideNotice && Individual channels can override this.} {showSaveIndicator && } - + ) } @@ -263,12 +263,12 @@ const RetentionDisplay = ( } const text = policyToExplanation(convType, props.policy, props.teamPolicy) return ( - - + + Message deletion - + {text} - + ) } @@ -279,8 +279,6 @@ const styles = Kb.Styles.styleSheetCreate( marginBottom: 2, }, heading: { - ...Kb.Styles.globalStyles.flexBoxRow, - alignItems: 'center', marginBottom: Kb.Styles.globalMargins.tiny, }, label: { diff --git a/shared/teams/team/settings-tab/retention/warning.tsx b/shared/teams/team/settings-tab/retention/warning.tsx index 8bd576602f48..c45ebcd1c02d 100644 --- a/shared/teams/team/settings-tab/retention/warning.tsx +++ b/shared/teams/team/settings-tab/retention/warning.tsx @@ -34,14 +34,14 @@ const RetentionWarning = (props: Props) => { const convType: string = getConvType(props.entityType) return ( - - + + - + {props.exploding ? 'Explode' : 'Auto-delete'} chat messages after {props.timePeriod}? @@ -78,7 +78,7 @@ const RetentionWarning = (props: Props) => { disabled={!enabled} /> - + ) } @@ -117,8 +117,6 @@ const styles = Kb.Styles.styleSheetCreate(() => ({ }), container: Kb.Styles.platformStyles({ common: { - ...Kb.Styles.globalStyles.flexBoxColumn, - alignItems: 'center', paddingBottom: Kb.Styles.globalMargins.large, }, isElectron: { diff --git a/shared/teams/team/tabs.tsx b/shared/teams/team/tabs.tsx index 762f5eb58207..f96ecc8d6608 100644 --- a/shared/teams/team/tabs.tsx +++ b/shared/teams/team/tabs.tsx @@ -45,7 +45,7 @@ const TeamTabs = (props: TeamTabsProps) => { ) return ( - + {Kb.Styles.isMobile ? ( { ) : ( tabContent )} - + {!!props.error && {props.error}} ) diff --git a/shared/tracker2/assertion.tsx b/shared/tracker2/assertion.tsx index 1cf078bf5ece..2c59572e7e20 100644 --- a/shared/tracker2/assertion.tsx +++ b/shared/tracker2/assertion.tsx @@ -526,7 +526,7 @@ const Value = (p: { } const HoverOpacity = (p: {children: React.ReactNode}) => ( - {p.children} + {p.children} ) type SIProps = { diff --git a/shared/unlock-folders/success.desktop.tsx b/shared/unlock-folders/success.desktop.tsx index af9a6ee189e0..d4b0bfa0e28d 100644 --- a/shared/unlock-folders/success.desktop.tsx +++ b/shared/unlock-folders/success.desktop.tsx @@ -3,7 +3,7 @@ import * as Kb from '@/common-adapters' const PaperKeyInput = ({onClose}: {onClose: () => void}) => (
- + Success! @@ -11,7 +11,7 @@ const PaperKeyInput = ({onClose}: {onClose: () => void}) => ( Your paper key is now rekeying folders for this computer. It takes just a couple minutes but lasts forever, like the decision to have a child - + diff --git a/shared/whats-new/icon/index.tsx b/shared/whats-new/icon/index.tsx index 08ee61d17832..be60d008b669 100644 --- a/shared/whats-new/icon/index.tsx +++ b/shared/whats-new/icon/index.tsx @@ -46,7 +46,7 @@ export const IconWithPopupDesktop = (p: PopupOwnProps) => { const popupVisibleColor = color || Kb.Styles.globalColors.black return ( <> - + { newRelease={newRelease} /> - + {popup} ) From beeb7e927f7de626ce87c5ab0701b9222a53941c Mon Sep 17 00:00:00 2001 From: chrisnojima Date: Wed, 18 Feb 2026 17:51:11 -0500 Subject: [PATCH 011/112] list to list2 cleanup --- shared/chat/blocking/block-modal.tsx | 83 ++++++---- shared/common-adapters/index-impl.js | 3 - shared/common-adapters/index.d.ts | 1 - shared/common-adapters/list.d.ts | 29 ---- shared/common-adapters/list.desktop.tsx | 122 -------------- shared/common-adapters/list.native.tsx | 82 ---------- .../common-adapters/popup-dialog.native.tsx | 12 +- shared/common-adapters/safe-react-list.tsx | 57 ------- shared/crypto/sub-nav/left-nav.desktop.tsx | 8 +- shared/devices/device-revoke.tsx | 7 +- shared/login/relogin/dropdown.native.tsx | 12 +- shared/package.json | 2 - shared/patches/react-list+0.8.18.patch | 12 -- shared/provision/select-other-device.tsx | 152 +++++++++++------- shared/settings/contacts-joined.tsx | 10 +- shared/team-building/list-body.tsx | 65 ++++---- .../teams/invite-by-contact/index.native.tsx | 18 +-- shared/teams/main/index.tsx | 36 ++++- shared/yarn.lock | 12 -- 19 files changed, 256 insertions(+), 467 deletions(-) delete mode 100644 shared/common-adapters/list.d.ts delete mode 100644 shared/common-adapters/list.desktop.tsx delete mode 100644 shared/common-adapters/list.native.tsx delete mode 100644 shared/common-adapters/safe-react-list.tsx delete mode 100644 shared/patches/react-list+0.8.18.patch diff --git a/shared/chat/blocking/block-modal.tsx b/shared/chat/blocking/block-modal.tsx index 0bb637314769..955b68d67704 100644 --- a/shared/chat/blocking/block-modal.tsx +++ b/shared/chat/blocking/block-modal.tsx @@ -123,9 +123,6 @@ const ReportOptions = (props: ReportOptionsProps) => { ) } -// In order to have this play nicely with scrolling and keyboards, put all the stuff in a List. -type Item = 'topStuff' | {username: string} - const Container = React.memo(function BlockModal(ownProps: OwnProps) { const {context, conversationIDKey, blockUserByDefault = false, filterUserByDefault = false} = ownProps const {flagUserByDefault = false, reportsUserByDefault = false, team: teamname} = ownProps @@ -433,30 +430,58 @@ const Container = React.memo(function BlockModal(ownProps: OwnProps) { const teamCheckboxDisabled = !!teamname && !otherUsernames?.length && !adderUsername const teamLabel = context === 'message-popup' - const topStuff = ( - - {(!!teamname || !adderUsername) && ( + type Item = 'topStuff' | {username: string} + const items: Array = ['topStuff'] + otherUsernames?.forEach(username => items.push({username})) + + const topStuffHeight = + 120 + + (!!adderUsername && getShouldReport(adderUsername) + ? reasons.length * 18 + 54 + 40 + : 0) + + (otherUsernames?.length ? 41 : 0) + // Each username row is 2 checkboxes (40px each) + 1px divider = 81px + const usernameRowHeight = 81 + + const itemHeight = { + getItemLayout: (index: number, item?: Item) => { + const length = item === 'topStuff' ? topStuffHeight : usernameRowHeight + let offset = 0 + for (let i = 0; i < index; i++) { + offset += items[i] === 'topStuff' ? topStuffHeight : usernameRowHeight + } + return {index, length, offset} + }, + type: 'variable' as const, + } + + const renderItem = (_: number, item: Item) => { + if (item === 'topStuff') { + return ( <> - - + {(!!teamname || !adderUsername) && ( + <> + + + + )} + {!!adderUsername && renderRowsForUsername(adderUsername, true, teamLabel)} + {!!otherUsernames?.length && ( + + Also block {adderUsername ? 'others' : 'individuals'}? + + )} - )} - {!!adderUsername && renderRowsForUsername(adderUsername, true, teamLabel)} - {!!otherUsernames?.length && ( - - Also block {adderUsername ? 'others' : 'individuals'}? - - )} - - ) + ) + } + return renderRowsForUsername(item.username, item === items[items.length - 1]) + } - const items: Array = ['topStuff'] - otherUsernames?.forEach(username => items.push({username})) return ( - - item === 'topStuff' - ? topStuff - : renderRowsForUsername(item.username, idx === otherUsernames?.length) - } + renderItem={renderItem} indexAsKey={true} + itemHeight={itemHeight} style={ Kb.Styles.isMobile ? styles.grow diff --git a/shared/common-adapters/index-impl.js b/shared/common-adapters/index-impl.js index aa7fcc8731ad..875e19166f30 100644 --- a/shared/common-adapters/index-impl.js +++ b/shared/common-adapters/index-impl.js @@ -165,9 +165,6 @@ module.exports = { get LabeledInput() { return require('./labeled-input').default }, - get List() { - return require('./list').default - }, get List2() { return require('./list2').default }, diff --git a/shared/common-adapters/index.d.ts b/shared/common-adapters/index.d.ts index 500578a72a99..7c31102a3fad 100644 --- a/shared/common-adapters/index.d.ts +++ b/shared/common-adapters/index.d.ts @@ -50,7 +50,6 @@ export {default as InfoNote} from './info-note' export {Input2} from './input2' export {KeyboardAvoidingView2} from './keyboard-avoiding-view' export {default as LabeledInput} from './labeled-input' -export {default as List} from './list' export {default as List2} from './list2' export {default as LoadingLine} from './loading-line' export {default as ListItem} from './list-item' diff --git a/shared/common-adapters/list.d.ts b/shared/common-adapters/list.d.ts deleted file mode 100644 index 48253d05af9c..000000000000 --- a/shared/common-adapters/list.d.ts +++ /dev/null @@ -1,29 +0,0 @@ -import type * as React from 'react' -import type {StylesCrossPlatform} from '@/styles' - -export type Props = { - bounces?: boolean // mobile only - indexAsKey?: boolean - items: ReadonlyArray - style?: StylesCrossPlatform - contentContainerStyle?: StylesCrossPlatform - fixedHeight?: number - renderItem: (index: number, item: Item) => React.ReactElement - keyProperty?: string // if passed uses item[keyProperty] for the item keys - selectedIndex?: number // TODO work on mobile - itemSizeEstimator?: (index: number, cache: {[K in number]: number}) => number // Desktop only - keyboardShouldPersistTaps?: 'never' | 'always' | 'handled' // mobile only - windowSize?: number // Mobile only, has a non-RN default - onEndReached?: () => void - ListHeaderComponent?: React.ComponentType | React.ReactElement - onEndReachedThreshold?: number // mobile only - onScroll?: (e: unknown) => void // mobile only - reAnimated?: boolean // mobile only, make list animated - keyboardDismissMode?: 'none' | 'interactive' | 'on-drag' // mobile only -} - -/** - * Semi deprecated. Use list2 if your items are a fixed height - */ -declare function List(p: Props): React.ReactNode -export default List diff --git a/shared/common-adapters/list.desktop.tsx b/shared/common-adapters/list.desktop.tsx deleted file mode 100644 index 02882c68ad5c..000000000000 --- a/shared/common-adapters/list.desktop.tsx +++ /dev/null @@ -1,122 +0,0 @@ -import * as Styles from '@/styles' -import * as React from 'react' -import SafeReactList from './safe-react-list' -import logger from '@/logger' -import type RL from 'react-list' -import type {Props} from './list' -import {renderElementOrComponentOrNot} from '@/util/util' -import {useThrottledCallback} from 'use-debounce' - -const List = React.memo(function List(p: Props) { - const { - items, - renderItem, - indexAsKey, - keyProperty, - fixedHeight, - onEndReached: _onEndReached, - selectedIndex, - } = p - const listRef = React.useRef(null) - - const itemRender = React.useCallback( - (index: number, _: number | string): React.JSX.Element => { - // ReactList has an issue where it caches the list length into its own state so can ask - // for indices outside of the items... - if (index >= items.length) { - return <> - } - const item = items[index] - const children = item ? renderItem(index, item) : <> - - if (indexAsKey) { - // if indexAsKey is set, just use index. - return {children} - } - const keyProp = keyProperty || 'key' - const i = item as {[key: string]: unknown} | undefined - if (i?.[keyProp]) { - const key: unknown = i[keyProp] - // otherwise, see if key is set on item directly. - return {children} - } - // We still don't have a key. So hopefully renderItem will provide the key. - logger.info( - 'Setting key from renderItem does not work on native. Please set it directly on items or use indexAsKey.' - ) - return children - }, - [items, renderItem, indexAsKey, keyProperty] - ) - - const lastSelectedIndexRef = React.useRef(selectedIndex) - React.useEffect(() => { - if (selectedIndex !== -1 && selectedIndex !== lastSelectedIndexRef.current) { - lastSelectedIndexRef.current = selectedIndex - if (selectedIndex !== undefined) { - listRef.current?.scrollAround(selectedIndex) - } - } - }, [selectedIndex]) - - const type = fixedHeight ? 'uniform' : 'simple' - const didOnEndReadchedRef = React.useRef(false) - - React.useEffect(() => { - didOnEndReadchedRef.current = false - }, [items]) - - const onScroll = useThrottledCallback( - (e: React.BaseSyntheticEvent) => { - if (didOnEndReadchedRef.current) return - const target = e.currentTarget - if (!target) return - const diff = target.scrollHeight - (target.scrollTop + target.clientHeight) - if (diff < 5) { - didOnEndReadchedRef.current = true - _onEndReached?.() - } - }, - 100 - ) - - return ( -
-
-
- {renderElementOrComponentOrNot(p.ListHeaderComponent)} - -
-
-
- ) -}) - -const styles = Styles.styleSheetCreate( - () => - ({ - innerDiv: Styles.platformStyles({ - isElectron: { - height: '100%', - overflowY: 'auto', - width: '100%', - }, - }), - outerDiv: { - flexGrow: 1, - position: 'relative', - }, - }) as const -) - -export default List diff --git a/shared/common-adapters/list.native.tsx b/shared/common-adapters/list.native.tsx deleted file mode 100644 index ff78be646c6e..000000000000 --- a/shared/common-adapters/list.native.tsx +++ /dev/null @@ -1,82 +0,0 @@ -import * as React from 'react' -import {FlatList, View} from 'react-native' -import * as Styles from '@/styles' -import type {Props} from './list' -import ReAnimated from './reanimated' -import noop from 'lodash/noop' - -const AnimatedFlatList = ReAnimated.FlatList - -const List = (props: Props) => { - const {renderItem, fixedHeight, indexAsKey, keyProperty} = props - const _itemRender = React.useCallback( - ({item, index}: {item: Item; index: number}) => { - return renderItem(index, item) - }, - [renderItem] - ) - - const _getItemLayout = React.useCallback( - (_: unknown, index: number) => ({ - index, - length: fixedHeight || 0, - offset: (fixedHeight || 0) * index, - }), - [fixedHeight] - ) - - const _keyExtractor = React.useCallback( - (item: Item, index: number) => { - if (indexAsKey || !item) { - return String(index) - } - - const keyProp = keyProperty || 'key' - const i = item as {[key: string]: string} - return i[keyProp] ?? String(index) - }, - [indexAsKey, keyProperty] - ) - - const ListComponent = props.reAnimated ? AnimatedFlatList : FlatList - - return ( - - {/* need windowSize so iphone 6 doesn't have OOM issues */} - {/* We can use initialScrollIndex={this.props.fixedHeight ? this.props.selectedIndex : undefined} .. - in FlatList below to pass through selectedIndex. However, it has undesirable behavior when the - selectedIndex is near the end of the list, as it'll then put that index in the center, adding gray - rows below, and a touch will cause it to 'snap back' so that the end of the list is at the bottom. */} - - - - - - ) -} - -const styles = Styles.styleSheetCreate(() => ({ - outerView: { - flexGrow: 1, - position: 'relative', - }, -})) - -export default List diff --git a/shared/common-adapters/popup-dialog.native.tsx b/shared/common-adapters/popup-dialog.native.tsx index 964ea3386c2d..f17cbceacfd8 100644 --- a/shared/common-adapters/popup-dialog.native.tsx +++ b/shared/common-adapters/popup-dialog.native.tsx @@ -1,5 +1,4 @@ -import {Box2} from './box' -import {TouchableWithoutFeedback} from 'react-native' +import {View, TouchableWithoutFeedback} from 'react-native' import * as Styles from '@/styles' import type {Props} from './popup-dialog' @@ -7,13 +6,13 @@ import type {Props} from './popup-dialog' export function PopupDialog({children, onClose, styleCover, styleContainer}: Props) { return ( - + - + {children} - + - + ) } @@ -29,6 +28,7 @@ const styles = Styles.styleSheetCreate( }, cover: { ...Styles.globalStyles.fillAbsolute, + ...Styles.globalStyles.flexBoxCenter, backgroundColor: Styles.globalColors.black, paddingBottom: Styles.globalMargins.small, paddingLeft: Styles.globalMargins.large, diff --git a/shared/common-adapters/safe-react-list.tsx b/shared/common-adapters/safe-react-list.tsx deleted file mode 100644 index d7be1d05c55e..000000000000 --- a/shared/common-adapters/safe-react-list.tsx +++ /dev/null @@ -1,57 +0,0 @@ -import * as C from '@/constants' -import * as React from 'react' -import ReactList from 'react-list' - -// Default ReactList will get into a bad state if it redraws while in a hidden parent (like in a stack) -// to fix we force a redraw when we're back visible - -type ItemRenderer = (index: number, key: number | string) => React.JSX.Element -type ItemsRenderer = (items: React.JSX.Element[], ref: string) => React.JSX.Element -type ItemSizeEstimator = (index: number, cache: object) => number -type ItemSizeGetter = (index: number) => number -type ScrollParentGetter = () => React.JSX.Element -type ReactListProps = { - children?: React.ReactNode - ref?: React.LegacyRef | undefined - axis?: 'x' | 'y' | undefined - initialIndex?: number | undefined - itemRenderer?: ItemRenderer | undefined - itemSizeEstimator?: ItemSizeEstimator | undefined - itemSizeGetter?: ItemSizeGetter | undefined - itemsRenderer?: ItemsRenderer | undefined - length?: number | undefined - minSize?: number | undefined - pageSize?: number | undefined - scrollParentGetter?: ScrollParentGetter | undefined - threshold?: number | undefined - type?: string | undefined - useStaticSize?: boolean | undefined - useTranslate3d?: boolean | undefined - extraData?: unknown -} - -const SafeReactList = React.forwardRef(function SafeReactList(p, ref) { - const [force, setForce] = React.useState(0) - const isMounted = C.useIsMounted() - - C.Router2.useSafeFocusEffect( - React.useCallback(() => { - setTimeout(() => { - if (isMounted()) { - setForce(i => i + 1) - } - }, 1) - }, [isMounted]) - ) - - return ( - - ) -}) - -export default SafeReactList diff --git a/shared/crypto/sub-nav/left-nav.desktop.tsx b/shared/crypto/sub-nav/left-nav.desktop.tsx index 5dc1b78e412a..8b0f7b87065a 100644 --- a/shared/crypto/sub-nav/left-nav.desktop.tsx +++ b/shared/crypto/sub-nav/left-nav.desktop.tsx @@ -43,7 +43,13 @@ const SubNav = (props: Props) => { - + {props.children} diff --git a/shared/devices/device-revoke.tsx b/shared/devices/device-revoke.tsx index 90e0850a35fe..cb95e6fd9abd 100644 --- a/shared/devices/device-revoke.tsx +++ b/shared/devices/device-revoke.tsx @@ -25,7 +25,12 @@ const EndangeredTLFList = (props: {endangeredTLFs: Array}) => { You may lose access to these folders forever: - + ) diff --git a/shared/login/relogin/dropdown.native.tsx b/shared/login/relogin/dropdown.native.tsx index 38cc33b62ad1..15c5aece3420 100644 --- a/shared/login/relogin/dropdown.native.tsx +++ b/shared/login/relogin/dropdown.native.tsx @@ -2,7 +2,7 @@ import * as Kb from '@/common-adapters' import * as React from 'react' import type * as T from '@/constants/types' import {Picker} from '@react-native-picker/picker' -import {TouchableWithoutFeedback, Modal} from 'react-native' +import {View, TouchableWithoutFeedback, Modal} from 'react-native' /* * A dropdown on iOS and Android. @@ -99,7 +99,7 @@ const Dropdown = (props: Props) => { if (Kb.Styles.isIOS) { return ( showModal(true)}> - + { > showModal(false)}> - + {picker} {labelAndCaret} - + ) } else { @@ -131,13 +131,17 @@ const styles = Kb.Styles.styleSheetCreate( () => ({ container: { + ...Kb.Styles.globalStyles.flexBoxRow, + alignItems: 'center', backgroundColor: Kb.Styles.globalColors.white, borderColor: Kb.Styles.globalColors.black_10, borderRadius: Kb.Styles.borderRadius, borderWidth: 1, height: 48, + maxWidth: '100%', paddingLeft: Kb.Styles.globalMargins.small, paddingRight: Kb.Styles.globalMargins.small, + width: '100%', }, icon: {width: 10}, item: {color: Kb.Styles.globalColors.black}, diff --git a/shared/package.json b/shared/package.json index d6aa96a9d201..15966957fcc7 100644 --- a/shared/package.json +++ b/shared/package.json @@ -111,7 +111,6 @@ "react-dom": "19.1.0", "react-error-boundary": "5.0.0", "react-is": "19.1.0", - "react-list": "0.8.18", "react-native": "0.81.5", "react-native-gesture-handler": "2.30.0", "react-native-kb": "file:../rnmodules/react-native-kb", @@ -151,7 +150,6 @@ "@types/react": "19.2.14", "@types/react-dom": "19.2.3", "@types/react-is": "19.2.0", - "@types/react-list": "0.8.12", "@types/react-measure": "2.0.12", "@types/shallowequal": "1.1.5", "@types/webpack-env": "1.18.8", diff --git a/shared/patches/react-list+0.8.18.patch b/shared/patches/react-list+0.8.18.patch deleted file mode 100644 index d594bb10ee9d..000000000000 --- a/shared/patches/react-list+0.8.18.patch +++ /dev/null @@ -1,12 +0,0 @@ -diff --git a/node_modules/react-list/react-list.js b/node_modules/react-list/react-list.js -index ac8447d..8b249a7 100644 ---- a/node_modules/react-list/react-list.js -+++ b/node_modules/react-list/react-list.js -@@ -196,6 +196,7 @@ - window.removeEventListener('resize', this.updateFrameAndClearCache); - this.scrollParent.removeEventListener('scroll', this.updateFrameAndClearCache, PASSIVE); - this.scrollParent.removeEventListener('mousewheel', NOOP, PASSIVE); -+ this.scrollParent = null; - } - }, { - key: "getOffset", diff --git a/shared/provision/select-other-device.tsx b/shared/provision/select-other-device.tsx index 3ac49ebaba69..01362bf15181 100644 --- a/shared/provision/select-other-device.tsx +++ b/shared/provision/select-other-device.tsx @@ -12,58 +12,112 @@ type Props = { onResetAccount: () => void } -const resetSignal = 'reset' -type DeviceOrReset = Device | 'reset' +type Item = {type: 'header'} | {device: Device; type: 'device'} | {type: 'reset'} + +const deviceSmallHeight = Kb.Styles.isMobile ? 56 : 48 +// "or" text with padding (~36) + ListItem2 Small +const resetHeight = 36 + deviceSmallHeight +// Header text with padding +const headerHeight = Kb.Styles.isMobile ? 80 : 60 + +const getItemHeight = (item: Item | undefined): number => { + switch (item?.type) { + case 'header': + return headerHeight + case 'device': + return deviceSmallHeight + case 'reset': + return resetHeight + default: + return deviceSmallHeight + } +} const SelectOtherDevice = (props: Props) => { const {passwordRecovery, devices, onBack, onSelect, onResetAccount} = props - const items: DeviceOrReset[] = React.useMemo(() => [...devices, resetSignal], [devices]) - const renderItem = (index: number, item: DeviceOrReset) => { - if (item === resetSignal) { - return ( - - - or - + const items: Item[] = React.useMemo( + () => [ + {type: 'header'}, + ...devices.map(device => ({device, type: 'device'}) as const), + {type: 'reset'}, + ], + [devices] + ) + + const itemHeight = React.useMemo( + () => ({ + getItemLayout: (index: number, item?: Item) => { + const length = getItemHeight(item) + let offset = 0 + for (let i = 0; i < index; i++) { + offset += getItemHeight(items[i]) + } + return {index, length, offset} + }, + type: 'variable' as const, + }), + [items] + ) + + const renderItem = (index: number, item: Item) => { + switch (item.type) { + case 'header': + return ( + + {!passwordRecovery && ( + + For security reasons, you need to authorize this{' '} + {Kb.Styles.isMobile ? 'phone' : 'computer'} with another device or a paper key. + + )} + + Which do you have handy? + + + ) + case 'reset': + return ( + + + or + + } + body={ + + I lost all my devices/paper keys + Reset your account + + } + /> + + ) + case 'device': { + const descriptions = { + backup: 'Paper key', + desktop: 'Computer', + mobile: 'Phone', + } + return ( } + firstItem={index === 1} + onClick={() => onSelect(item.device.name)} + icon={} body={ - I lost all my devices/paper keys - Reset your account + {item.device.name} + {descriptions[item.device.type]} } /> - - ) - } - - const descriptions = { - backup: 'Paper key', - desktop: 'Computer', - mobile: 'Phone', + ) + } } - - return ( - onSelect(item.name)} - icon={} - body={ - - {item.name} - {descriptions[item.type]} - - } - /> - ) } return ( @@ -76,24 +130,12 @@ const SelectOtherDevice = (props: Props) => { contentContainerStyle={Kb.Styles.padding(0)} > - - {!passwordRecovery && ( - - For security reasons, you need to authorize this {Kb.Styles.isMobile ? 'phone' : 'computer'}{' '} - with another device or a paper key. - - )} - - Which do you have handy? - - - } + indexAsKey={true} + itemHeight={itemHeight} /> diff --git a/shared/settings/contacts-joined.tsx b/shared/settings/contacts-joined.tsx index 7c63bb4161cf..6ec596844d6e 100644 --- a/shared/settings/contacts-joined.tsx +++ b/shared/settings/contacts-joined.tsx @@ -9,6 +9,8 @@ import {useTrackerState} from '@/stores/tracker2' import {useFollowerState} from '@/stores/followers' const renderItem = (_: number, item: T.RPCGen.ProcessedContact) => +type ItemHeight = React.ComponentProps>['itemHeight'] +const itemHeight: ItemHeight = {height: 96, type: 'fixed'} type FollowProps = { username: string @@ -55,10 +57,6 @@ const Item = ({item}: {item: T.RPCGen.ProcessedContact}) => { const username = item.username const label = item.contactName || item.component.phoneNumber || item.component.email || '' - const followThem = useFollowerState(s => s.following.has(username)) - if (followThem) { - return null - } return ( @@ -79,6 +77,8 @@ const Item = ({item}: {item: T.RPCGen.ProcessedContact}) => { const ContactsJoinedModal = () => { const people = useSettingsContactsState(s => s.alreadyOnKeybase) + const following = useFollowerState(s => s.following) + const filteredPeople = people.filter(p => !following.has(p.username)) const nav = useSafeNavigation() const onClose = () => nav.safeNavigateUp() return ( @@ -95,7 +95,7 @@ const ContactsJoinedModal = () => { Woot! Some of your contacts are already on Keybase. - +
) } diff --git a/shared/team-building/list-body.tsx b/shared/team-building/list-body.tsx index 20b0bcd9dac5..0e2b6ba00075 100644 --- a/shared/team-building/list-body.tsx +++ b/shared/team-building/list-body.tsx @@ -351,37 +351,38 @@ export const ListBody = ( return ( <> {searchResults?.length ? ( - ( - - )} - /> + + ( + + )} + /> + ) : ( Sorry, no results were found. @@ -410,8 +411,6 @@ const styles = Kb.Styles.styleSheetCreate( }), list: Kb.Styles.platformStyles({ common: {paddingBottom: Kb.Styles.globalMargins.small}, - }), - listContentContainer: Kb.Styles.platformStyles({ isMobile: {paddingTop: Kb.Styles.globalMargins.xtiny}, }), loadingAnimation: Kb.Styles.platformStyles({ diff --git a/shared/teams/invite-by-contact/index.native.tsx b/shared/teams/invite-by-contact/index.native.tsx index e5eb0193546e..9a24393c15f4 100644 --- a/shared/teams/invite-by-contact/index.native.tsx +++ b/shared/teams/invite-by-contact/index.native.tsx @@ -120,18 +120,16 @@ export const InviteByContact = (props: InviteByContactProps) => { position="bottom center" disabledRoles={{owner: 'Cannot invite an owner via email.'}} /> - controlRolePicker(true)} style={styles.rolePickerBox}> + + Users will be invited to {props.teamName} as + {' ' + props.selectedRole + 's'}. + + + controlRolePicker(true)} style={styles.rolePickerBox}> - - Users will be invited to {props.teamName} as - {' ' + props.selectedRole + 's'}. - - - } + itemHeight={{height: 56, type: 'fixed'}} renderItem={contactRow} style={styles.contactList} /> diff --git a/shared/teams/main/index.tsx b/shared/teams/main/index.tsx index abd48e43a7dd..f592747afb2d 100644 --- a/shared/teams/main/index.tsx +++ b/shared/teams/main/index.tsx @@ -122,6 +122,23 @@ type Row = {key: React.Key} & ( | {team: T.Teams.TeamMeta; type: 'team'} ) +const getRowHeight = (item: Row | undefined): number => { + switch (item?.type) { + case '_buttons': + return Kb.Styles.isMobile ? 180 : 160 + case '_sortHeader': + return Kb.Styles.isMobile ? 44 : 36 + case 'deletedTeam': + return 50 + case 'team': + return Kb.Styles.isMobile ? 72 : 48 + case '_footer': + return 56 + default: + return 48 + } +} + const Teams = React.memo(function Teams(p: Props) { const {deletedTeams, teams, onReadMore, onCreateTeam, onHideChatBanner, onJoinTeam} = p @@ -139,6 +156,21 @@ const Teams = React.memo(function Teams(p: Props) { [deletedTeams, teams] ) + const itemHeight = React.useMemo( + () => ({ + getItemLayout: (index: number, item?: Row) => { + const length = getRowHeight(item) + let offset = 0 + for (let i = 0; i < index; i++) { + offset += getRowHeight(items[i]) + } + return {index, length, offset} + }, + type: 'variable' as const, + }), + [items] + ) + const renderItem = React.useCallback( (index: number, item: Row) => { switch (item.type) { @@ -174,7 +206,9 @@ const Teams = React.memo(function Teams(p: Props) { return ( - + + + ) }) diff --git a/shared/yarn.lock b/shared/yarn.lock index 7b7f49a9bef1..b7ceb9da31da 100644 --- a/shared/yarn.lock +++ b/shared/yarn.lock @@ -3361,13 +3361,6 @@ dependencies: "@types/react" "*" -"@types/react-list@0.8.12": - version "0.8.12" - resolved "https://registry.yarnpkg.com/@types/react-list/-/react-list-0.8.12.tgz#ead6a7bdd24d237ca2d2adc9ecbe895e0c86f5f2" - integrity sha512-X09dFxEJUeqiLe2KNuYl4JyIcvuOzmz3Im2jBH3X3VTH46Mo10wnAYWEsUbDNDIdQ2mGbWgmToqCofXPNGlWXA== - dependencies: - "@types/react" "*" - "@types/react-measure@2.0.12": version "2.0.12" resolved "https://registry.yarnpkg.com/@types/react-measure/-/react-measure-2.0.12.tgz#e8ba05057357b9529aa4115064fe7ea77549f54c" @@ -9609,11 +9602,6 @@ react-is@^19.1.0: resolved "https://registry.yarnpkg.com/react-is/-/react-is-19.2.4.tgz#a080758243c572ccd4a63386537654298c99d135" integrity sha512-W+EWGn2v0ApPKgKKCy/7s7WHXkboGcsrXE+2joLyVxkbyVQfO3MUEaUQDHoSmb8TFFrSKYa9mw64WZHNHSDzYA== -react-list@0.8.18: - version "0.8.18" - resolved "https://registry.yarnpkg.com/react-list/-/react-list-0.8.18.tgz#682b6957ad680db496a376e75cd37b1d38409857" - integrity sha512-1OSdDvzuKuwDJvQNuhXxxL+jTmmdtKg1i6KtYgxI9XR98kbOql1FcSGP+Lcvo91fk3cYng+Z6YkC6X9HRJwxfw== - react-native-gesture-handler@2.30.0: version "2.30.0" resolved "https://registry.yarnpkg.com/react-native-gesture-handler/-/react-native-gesture-handler-2.30.0.tgz#990c621fbeeefde853ececdcab7cbe1b621dbb8b" From a0f864130f2d7a14d1454ea5b5874435ad78b237 Mon Sep 17 00:00:00 2001 From: chrisnojima Date: Wed, 18 Feb 2026 18:30:15 -0500 Subject: [PATCH 012/112] more gap (#28921) --- shared/chat/conversation/error.tsx | 10 +++----- .../chat/conversation/messages/reset-user.tsx | 6 ++--- shared/common-adapters/box.css | 24 +++++++++---------- shared/fs/banner/reset-banner.tsx | 7 +++--- shared/teams/join-team/container.tsx | 5 ++-- shared/teams/team/team-info.tsx | 1 - 6 files changed, 23 insertions(+), 30 deletions(-) diff --git a/shared/chat/conversation/error.tsx b/shared/chat/conversation/error.tsx index e098c723b111..4bb646a822d1 100644 --- a/shared/chat/conversation/error.tsx +++ b/shared/chat/conversation/error.tsx @@ -4,12 +4,12 @@ import * as Kb from '@/common-adapters' const ConversationError = () => { const text = Chat.useChatContext(s => s.meta.snippet ?? '') return ( - + There was an error loading this conversation. - + The error is: - + @@ -19,13 +19,9 @@ const ConversationError = () => { const styles = Kb.Styles.styleSheetCreate( () => ({ - body: {marginTop: Kb.Styles.globalMargins.small}, container: { padding: Kb.Styles.globalMargins.medium, }, - errorBox: { - marginTop: Kb.Styles.globalMargins.small, - }, errorText: {flexGrow: 1}, }) as const ) diff --git a/shared/chat/conversation/messages/reset-user.tsx b/shared/chat/conversation/messages/reset-user.tsx index 23e0f1923e9c..5f1b93dffc40 100644 --- a/shared/chat/conversation/messages/reset-user.tsx +++ b/shared/chat/conversation/messages/reset-user.tsx @@ -33,11 +33,11 @@ const ResetUser = () => { } - - + + 1. Be satisfied with their new proofs, or - + 2. Know them outside Keybase and have gotten a thumbs up from them. diff --git a/shared/common-adapters/box.css b/shared/common-adapters/box.css index 0720dff82b0c..950dc857b930 100644 --- a/shared/common-adapters/box.css +++ b/shared/common-adapters/box.css @@ -26,8 +26,8 @@ &.box2_gap_medium { row-gap: var(--size-medium); } - &.box2_gap_mediumlarge { - row-gap: var(--size-mediumlarge); + &.box2_gap_mediumLarge { + row-gap: var(--size-mediumLarge); } &.box2_gap_large { row-gap: var(--size-large); @@ -54,8 +54,8 @@ &.box2_gapStart_medium { padding-top: var(--size-medium); } - &.box2_gapStart_mediumlarge { - padding-top: var(--size-mediumlarge); + &.box2_gapStart_mediumLarge { + padding-top: var(--size-mediumLarge); } &.box2_gapStart_large { padding-top: var(--size-large); @@ -82,8 +82,8 @@ &.box2_gapEnd_medium { padding-bottom: var(--size-medium); } - &.box2_gapEnd_mediumlarge { - padding-bottom: var(--size-mediumlarge); + &.box2_gapEnd_mediumLarge { + padding-bottom: var(--size-mediumLarge); } &.box2_gapEnd_large { padding-bottom: var(--size-large); @@ -120,8 +120,8 @@ &.box2_gap_medium { column-gap: var(--size-medium); } - &.box2_gap_mediumlarge { - column-gap: var(--size-mediumlarge); + &.box2_gap_mediumLarge { + column-gap: var(--size-mediumLarge); } &.box2_gap_large { column-gap: var(--size-large); @@ -148,8 +148,8 @@ &.box2_gapStart_medium { padding-left: var(--size-medium); } - &.box2_gapStart_mediumlarge { - padding-left: var(--size-mediumlarge); + &.box2_gapStart_mediumLarge { + padding-left: var(--size-mediumLarge); } &.box2_gapStart_large { padding-left: var(--size-large); @@ -176,8 +176,8 @@ &.box2_gapEnd_medium { padding-right: var(--size-medium); } - &.box2_gapEnd_mediumlarge { - padding-right: var(--size-mediumlarge); + &.box2_gapEnd_mediumLarge { + padding-right: var(--size-mediumLarge); } &.box2_gapEnd_large { padding-right: var(--size-large); diff --git a/shared/fs/banner/reset-banner.tsx b/shared/fs/banner/reset-banner.tsx index 3606d16b8f2c..4cc95c49acc4 100644 --- a/shared/fs/banner/reset-banner.tsx +++ b/shared/fs/banner/reset-banner.tsx @@ -92,11 +92,11 @@ const ConnectedBanner = (ownProps: OwnProps) => { If you want to let them into this folder and the matching chat, you should either: - - + + 1. Be satisfied with their new proofs, or - + 2. Know them outside Keybase and have gotten a thumbs up from them. @@ -188,7 +188,6 @@ const styles = Kb.Styles.styleSheetCreate( justifyContent: 'center', maxWidth: C.isMobile ? 280 : 400, }, - listTextContent: {marginTop: Kb.Styles.globalMargins.tiny}, textDontLetThemIn: { ...fixedHeight(Kb.Styles.globalMargins.mediumLarge), marginBottom: Kb.Styles.globalMargins.tiny, diff --git a/shared/teams/join-team/container.tsx b/shared/teams/join-team/container.tsx index a745a5655594..1c04a8822663 100644 --- a/shared/teams/join-team/container.tsx +++ b/shared/teams/join-team/container.tsx @@ -84,8 +84,8 @@ const Container = (ownProps: OwnProps) => { )} ) : ( - - + + { const styles = Kb.Styles.styleSheetCreate(() => ({ avatar: { alignSelf: 'center', - marginBottom: Kb.Styles.globalMargins.tiny, marginRight: Kb.Styles.globalMargins.tiny, }, bg: {backgroundColor: Kb.Styles.globalColors.blueGrey}, From ecaa0311cf5422366fec44eb0e6db937d1caaa1b Mon Sep 17 00:00:00 2001 From: chrisnojima Date: Thu, 19 Feb 2026 16:34:53 -0500 Subject: [PATCH 013/112] refactor text to simpler impl (#28922) --- shared/chat/conversation/bot/install.tsx | 4 +- shared/chat/conversation/bottom-banner.tsx | 21 +- .../chat/conversation/giphy/index.desktop.tsx | 3 +- .../conversation/header-area/index.native.tsx | 2 +- .../conversation/info-panel/attachments.tsx | 29 +- .../chat/conversation/messages/separator.tsx | 6 +- .../messages/text/coinflip/errors.tsx | 37 +- .../messages/text/coinflip/participants.tsx | 3 +- .../text/unfurl/unfurl-list/generic.tsx | 4 +- .../conversation/messages/wrapper/edited.tsx | 1 - .../index.native.tsx | 7 +- shared/chat/inbox/row/big-team-channel.tsx | 8 +- shared/chat/inbox/row/small-team/index.tsx | 9 +- .../chat/selectable-small-team-container.tsx | 2 +- shared/common-adapters/button.tsx | 3 +- shared/common-adapters/copy-text.tsx | 8 +- shared/common-adapters/index-impl.js | 6 +- shared/common-adapters/index.d.ts | 2 +- shared/common-adapters/input2.d.ts | 2 +- shared/common-adapters/input2.desktop.tsx | 2 +- shared/common-adapters/input2.native.tsx | 2 +- shared/common-adapters/labeled-input.tsx | 5 +- shared/common-adapters/markdown/channel.tsx | 3 +- shared/common-adapters/markdown/index.tsx | 2 +- .../markdown/maybe-mention/index.tsx | 5 +- .../markdown/maybe-mention/team.tsx | 3 +- shared/common-adapters/markdown/react.tsx | 6 +- .../markdown/service-decoration.tsx | 44 +- shared/common-adapters/mention-container.tsx | 2 + shared/common-adapters/name-with-icon.tsx | 8 +- shared/common-adapters/new-input.tsx | 3 +- shared/common-adapters/plain-input.d.ts | 2 +- .../common-adapters/plain-input.desktop.tsx | 2 +- shared/common-adapters/plain-input.native.tsx | 2 +- shared/common-adapters/search-filter.tsx | 3 +- shared/common-adapters/team-with-popup.tsx | 3 +- shared/common-adapters/text-url.d.ts | 5 + shared/common-adapters/text-url.desktop.tsx | 17 + shared/common-adapters/text-url.native.tsx | 19 + shared/common-adapters/text.css | 201 +++--- shared/common-adapters/text.d.ts | 108 +-- shared/common-adapters/text.desktop.tsx | 169 ++--- shared/common-adapters/text.meta.native.tsx | 677 ++++++++---------- shared/common-adapters/text.native.tsx | 153 +--- shared/common-adapters/text.shared.tsx | 78 +- shared/common-adapters/text.styles.d.ts | 2 + ...ta.desktop.tsx => text.styles.desktop.tsx} | 34 +- shared/common-adapters/text.styles.native.tsx | 376 ++++++++++ shared/common-adapters/text2.d.ts | 22 - shared/common-adapters/text2.desktop.tsx | 49 -- shared/common-adapters/text2.native.tsx | 64 -- shared/common-adapters/usernames.tsx | 15 +- .../zoomable-image.desktop.tsx | 2 +- shared/devices/paper-key.tsx | 8 +- shared/fs/browser/rows/tlf-type.tsx | 1 - shared/fs/common/comma-separated-name.tsx | 2 +- shared/fs/common/filename.tsx | 8 +- shared/fs/common/last-modified-line.tsx | 8 +- shared/fs/common/tlf-info-line.tsx | 3 - shared/git/nav-header.tsx | 43 +- shared/profile/post-proof.tsx | 6 +- shared/profile/user/friend.tsx | 4 +- shared/profile/user/index.tsx | 3 +- shared/settings/account/index.tsx | 3 +- shared/settings/advanced.tsx | 3 +- shared/settings/sub-nav/settings-item.tsx | 6 +- shared/signup/username.tsx | 3 +- shared/styles/shared.tsx | 2 +- shared/teams/main/team-row.tsx | 6 +- shared/teams/team/settings-tab/index.tsx | 3 +- shared/whats-new/versions.tsx | 2 +- 71 files changed, 1250 insertions(+), 1109 deletions(-) create mode 100644 shared/common-adapters/text-url.d.ts create mode 100644 shared/common-adapters/text-url.desktop.tsx create mode 100644 shared/common-adapters/text-url.native.tsx create mode 100644 shared/common-adapters/text.styles.d.ts rename shared/common-adapters/{text.meta.desktop.tsx => text.styles.desktop.tsx} (92%) create mode 100644 shared/common-adapters/text.styles.native.tsx delete mode 100644 shared/common-adapters/text2.d.ts delete mode 100644 shared/common-adapters/text2.desktop.tsx delete mode 100644 shared/common-adapters/text2.native.tsx diff --git a/shared/chat/conversation/bot/install.tsx b/shared/chat/conversation/bot/install.tsx index 88c1f3a90369..5ef47dd9de6c 100644 --- a/shared/chat/conversation/bot/install.tsx +++ b/shared/chat/conversation/bot/install.tsx @@ -304,8 +304,8 @@ const InstallBotPopup = (props: Props) => { setInstallWithRestrict(true)}> Install as a restricted bot - {' '} - if you’d like to customize which messages are encrypted for this bot. + + {" if you’d like to customize which messages are encrypted for this bot."} )} diff --git a/shared/chat/conversation/bottom-banner.tsx b/shared/chat/conversation/bottom-banner.tsx index 878d44341538..a3e4390de7d6 100644 --- a/shared/chat/conversation/bottom-banner.tsx +++ b/shared/chat/conversation/bottom-banner.tsx @@ -4,7 +4,6 @@ import * as Kb from '@/common-adapters' import * as React from 'react' import _openSMS from '@/util/sms' import {assertionToDisplay} from '@/common-adapters/usernames' -import type {Props as TextProps} from '@/common-adapters/text' import {useUsersState} from '@/stores/users' import {useFollowerState} from '@/stores/followers' import {showShareActionSheet} from '@/util/platform-specific' @@ -12,6 +11,7 @@ import {showShareActionSheet} from '@/util/platform-specific' const installMessage = `I sent you encrypted messages on Keybase. You can install it here: https://keybase.io/phone-app` const Invite = () => { + const linkUrlProps = Kb.useClickURL('https://keybase.io/app') const participantInfo = Chat.useChatContext(s => s.participants) const participantInfoAll = participantInfo.all const users = participantInfoAll.filter(p => p.includes('@')) @@ -48,7 +48,7 @@ const Invite = () => { if (C.isMobile) { return ( - {caption} + {caption} { return ( - {caption} - + {caption} + Send them this link: - https://keybase.io/app - - + + ) } @@ -148,10 +149,6 @@ const BannerBox = (props: { ) -const BannerText = (props: Partial) => ( - -) - const styles = Kb.Styles.styleSheetCreate( () => ({ diff --git a/shared/chat/conversation/giphy/index.desktop.tsx b/shared/chat/conversation/giphy/index.desktop.tsx index 6e235c6b048a..9838dae00dd9 100644 --- a/shared/chat/conversation/giphy/index.desktop.tsx +++ b/shared/chat/conversation/giphy/index.desktop.tsx @@ -10,6 +10,7 @@ const GiphySearch = () => { const props = useHooks() const [width, setWidth] = React.useState(undefined) const divRef = React.useRef(null) + const learnMoreUrlProps = Kb.useClickURL('https://keybase.io/docs/chat/linkpreviews') React.useEffect(() => { if (!divRef.current) return @@ -44,7 +45,7 @@ const GiphySearch = () => { Learn more about GIFs & encryption diff --git a/shared/chat/conversation/header-area/index.native.tsx b/shared/chat/conversation/header-area/index.native.tsx index ceaaff316fdd..810761d4e8dc 100644 --- a/shared/chat/conversation/header-area/index.native.tsx +++ b/shared/chat/conversation/header-area/index.native.tsx @@ -238,7 +238,7 @@ const UsernameHeader = () => { style={Kb.Styles.collapseStyles([styles.usernameHeaderContainer, maxWidthStyle])} > {!!theirFullname && ( - + {theirFullname} )} diff --git a/shared/chat/conversation/info-panel/attachments.tsx b/shared/chat/conversation/info-panel/attachments.tsx index 5cc7a6a05d48..53ea4dd2432e 100644 --- a/shared/chat/conversation/info-panel/attachments.tsx +++ b/shared/chat/conversation/info-panel/attachments.tsx @@ -1,7 +1,7 @@ import * as C from '@/constants' import * as Chat from '@/stores/chat2' import * as Kb from '@/common-adapters' -import type {StylesTextCrossPlatform} from '@/common-adapters/text' +import type {StylesTextCrossPlatform} from '@/common-adapters/text.shared' import * as T from '@/constants/types' import * as React from 'react' import chunk from 'lodash/chunk' @@ -300,6 +300,22 @@ const AttachmentTypeSelector = (props: SelectorProps) => ( ) +const LinkTitle = (p: {title: string; url?: string}) => { + const urlProps = Kb.useClickURL(p.url ?? '') + return ( + + {p.title} + + ) +} + const styles = Kb.Styles.styleSheetCreate( () => ({ @@ -705,16 +721,7 @@ export const useAttachmentSections = ( {!!item.title && ( - - {item.title} - + )} diff --git a/shared/chat/conversation/messages/separator.tsx b/shared/chat/conversation/messages/separator.tsx index 877beff3ad3a..a292536af678 100644 --- a/shared/chat/conversation/messages/separator.tsx +++ b/shared/chat/conversation/messages/separator.tsx @@ -129,7 +129,7 @@ const TopSide = React.memo(function TopSide(p: {ordinal: T.Chat.Ordinal; showUse colorFollowing={true} colorYou={true} onUsernameClicked={onAuthorClick} - fixOverdraw="auto" + type="BodySmallBold" usernames={showUsername} virtualText={true} @@ -162,9 +162,9 @@ const TopSide = React.memo(function TopSide(p: {ordinal: T.Chat.Ordinal; showUse ) const timestampNode = ( - + {formatTimeForChat(timestamp)} - + ) return ( diff --git a/shared/chat/conversation/messages/text/coinflip/errors.tsx b/shared/chat/conversation/messages/text/coinflip/errors.tsx index bfa8e0b51cb2..61f049e4df0a 100644 --- a/shared/chat/conversation/messages/text/coinflip/errors.tsx +++ b/shared/chat/conversation/messages/text/coinflip/errors.tsx @@ -46,26 +46,29 @@ type AbsenteeProps = { error: T.RPCChat.UICoinFlipAbsenteeError } -const CoinFlipAbsenteeError = (props: AbsenteeProps) => ( - - - Uh oh, a participant disappeared: - - - - {(props.error.absentees || []).map(a => `${a.user} (device: ${a.device})`).join(', ')} - - - +const CoinFlipAbsenteeError = (props: AbsenteeProps) => { + const learnMoreUrlProps = Kb.useClickURL('https://keybase.io/coin-flip') + return ( + - It was likely a network problem, but they could be trying to pull a fast one. - - - Learn More + Uh oh, a participant disappeared: + + + {(props.error.absentees || []).map(a => `${a.user} (device: ${a.device})`).join(', ')} + + + + + It was likely a network problem, but they could be trying to pull a fast one. + + + Learn More + + - -) + ) +} const CoinFlipTimeoutError = () => ( diff --git a/shared/chat/conversation/messages/text/coinflip/participants.tsx b/shared/chat/conversation/messages/text/coinflip/participants.tsx index 6c6d5df3d924..986fee957694 100644 --- a/shared/chat/conversation/messages/text/coinflip/participants.tsx +++ b/shared/chat/conversation/messages/text/coinflip/participants.tsx @@ -12,6 +12,7 @@ const items: Kb.MenuItems = [] const CoinFlipParticipants = (props: Props) => { const {attachTo, onHidden, participants, visible} = props + const howThisWorksUrlProps = Kb.useClickURL('https://keybase.io/coin-flip') const header = ( @@ -33,7 +34,7 @@ const CoinFlipParticipants = (props: Props) => { - + How this works diff --git a/shared/chat/conversation/messages/text/unfurl/unfurl-list/generic.tsx b/shared/chat/conversation/messages/text/unfurl/unfurl-list/generic.tsx index 51105368b6e1..450be91670dc 100644 --- a/shared/chat/conversation/messages/text/unfurl/unfurl-list/generic.tsx +++ b/shared/chat/conversation/messages/text/unfurl/unfurl-list/generic.tsx @@ -56,6 +56,8 @@ const UnfurlGeneric = React.memo(function UnfurlGeneric(p: {idx: number}) { ordinal ) + const titleUrlProps = Kb.useClickURL(data?.url ?? '') + if (!data) return null const {description, favicon, height, isCollapsed, isVideo, publishTime} = data @@ -131,7 +133,7 @@ const UnfurlGeneric = React.memo(function UnfurlGeneric(p: {idx: number}) { {!Kb.Styles.isMobile && } {publisher} - + {title} {snippet} diff --git a/shared/chat/conversation/messages/wrapper/edited.tsx b/shared/chat/conversation/messages/wrapper/edited.tsx index a3e2d0df2fbe..eda4089e9a2e 100644 --- a/shared/chat/conversation/messages/wrapper/edited.tsx +++ b/shared/chat/conversation/messages/wrapper/edited.tsx @@ -17,7 +17,6 @@ export const useEdited = () => { diff --git a/shared/chat/conversation/messages/wrapper/exploding-height-retainer/index.native.tsx b/shared/chat/conversation/messages/wrapper/exploding-height-retainer/index.native.tsx index 125feab14cb8..9776924d06a8 100644 --- a/shared/chat/conversation/messages/wrapper/exploding-height-retainer/index.native.tsx +++ b/shared/chat/conversation/messages/wrapper/exploding-height-retainer/index.native.tsx @@ -151,7 +151,7 @@ const EmojiTower = (p: {numImages: number; animatedValue: NativeAnimated.Value}) } } children.push( - + {emoji} ) @@ -179,15 +179,14 @@ const AshTower = (p: {explodedBy?: string; numImages: number; showExploded: bool if (showExploded) { exploded = !explodedBy ? ( - + EXPLODED ) : ( - + EXPLODED BY{' '} { ]) const name = ( - #{' '} - + {channelname} - - + + ) const mutedIcon = isMuted ? ( diff --git a/shared/chat/inbox/row/small-team/index.tsx b/shared/chat/inbox/row/small-team/index.tsx index a37186eeab84..7e66ad5d04c8 100644 --- a/shared/chat/inbox/row/small-team/index.tsx +++ b/shared/chat/inbox/row/small-team/index.tsx @@ -248,9 +248,9 @@ const TopLine = (p: TopLineProps) => { )} - + {timestampText} - + {!Kb.Styles.isMobile && showGear && ( { content = ( { } else if (draft) { content = ( - { ])} > Draft: - + {draft} diff --git a/shared/chat/selectable-small-team-container.tsx b/shared/chat/selectable-small-team-container.tsx index 0a1f5f44b24a..e287b21d4448 100644 --- a/shared/chat/selectable-small-team-container.tsx +++ b/shared/chat/selectable-small-team-container.tsx @@ -1,6 +1,6 @@ import * as Chat from '@/stores/chat2' import * as Kb from '@/common-adapters' -import type {AllowedColors} from '@/common-adapters/text' +import type {AllowedColors} from '@/common-adapters/text.shared' import SelectableSmallTeam from './selectable-small-team' import {useCurrentUserState} from '@/stores/current-user' diff --git a/shared/common-adapters/button.tsx b/shared/common-adapters/button.tsx index 924019528045..86b62d8b5798 100644 --- a/shared/common-adapters/button.tsx +++ b/shared/common-adapters/button.tsx @@ -4,7 +4,8 @@ import * as Styles from '@/styles' import Badge from './badge' import ClickableBox from './clickable-box' import Icon, {type SizeType} from './icon' -import Text, {type StylesTextCrossPlatform} from './text' +import Text from './text' +import type {StylesTextCrossPlatform} from './text.shared' import WithTooltip from './with-tooltip' import type {IconType} from './icon.constants-gen' import {Box2} from './box' diff --git a/shared/common-adapters/copy-text.tsx b/shared/common-adapters/copy-text.tsx index 43a797333631..858d6d20b1ad 100644 --- a/shared/common-adapters/copy-text.tsx +++ b/shared/common-adapters/copy-text.tsx @@ -2,7 +2,8 @@ import * as React from 'react' import {Box2Measure} from './box' import Icon from './icon' import Button, {type Props as ButtonProps} from './button' -import Text, {type TextMeasureRef, type LineClampType, type TextType} from './text' +import Text from './text' +import type {LineClampType, TextType} from './text.shared' import Toast from './toast' import {useTimeout} from './use-timers' import * as Styles from '@/styles' @@ -60,7 +61,6 @@ const CopyText = (props: Props) => { }, [withReveal, text, loadText]) const popupAnchor = React.useRef(null) - const textRef = React.useRef(null) const copyToClipboard = useConfigState(s => s.dispatch.defer.copyToClipboard) const showShareActionSheet = useConfigState(s => s.dispatch.defer.showShareActionSheet) const copy = React.useCallback(() => { @@ -75,7 +75,6 @@ const CopyText = (props: Props) => { showShareActionSheet?.('', text, 'text/plain') } else { setShowingToast(true) - textRef.current?.highlightText() copyToClipboard(text) } onCopy?.() @@ -139,8 +138,7 @@ const CopyText = (props: Props) => { selectable={true} center={true} style={Styles.collapseStyles([styles.text, props.disabled && styles.textDisabled])} - allowHighlightText={true} - textRef={textRef} + > {isRevealed && (props.text || props.placeholderText) ? props.text || props.placeholderText diff --git a/shared/common-adapters/index-impl.js b/shared/common-adapters/index-impl.js index 875e19166f30..49c55c4159df 100644 --- a/shared/common-adapters/index-impl.js +++ b/shared/common-adapters/index-impl.js @@ -291,9 +291,6 @@ module.exports = { get Text() { return require('./text').default }, - get Text2() { - return require('./text2').Text2 - }, get TimelineMarker() { return require('./timeline-marker').default }, @@ -330,6 +327,9 @@ module.exports = { get urlsToImgSet() { return require('./icon').urlsToImgSet }, + get useClickURL() { + return require('./text-url').useClickURL + }, get useHotKey() { return require('./hot-key').useHotKey }, diff --git a/shared/common-adapters/index.d.ts b/shared/common-adapters/index.d.ts index 7c31102a3fad..f7e8f74fc96e 100644 --- a/shared/common-adapters/index.d.ts +++ b/shared/common-adapters/index.d.ts @@ -92,7 +92,7 @@ export {default as Switch} from './switch' export {default as Tabs} from './tabs' export {default as TeamWithPopup} from './team-with-popup' export {default as Text} from './text' -export {Text2} from './text2' +export {useClickURL} from './text-url' export {default as Toast} from './toast' export {default as SimpleToast} from './simple-toast' export {default as TimelineMarker} from './timeline-marker' diff --git a/shared/common-adapters/input2.d.ts b/shared/common-adapters/input2.d.ts index aebfa220ae11..7952342b0494 100644 --- a/shared/common-adapters/input2.d.ts +++ b/shared/common-adapters/input2.d.ts @@ -1,6 +1,6 @@ import type * as React from 'react' import type * as Styles from '@/styles' -import type {TextType} from './text' +import type {TextType} from './text.shared' import type {TextInputProps} from 'react-native' export type RefType = { diff --git a/shared/common-adapters/input2.desktop.tsx b/shared/common-adapters/input2.desktop.tsx index 97255ba9e5dd..921a1b37df61 100644 --- a/shared/common-adapters/input2.desktop.tsx +++ b/shared/common-adapters/input2.desktop.tsx @@ -1,7 +1,7 @@ import * as React from 'react' import * as Styles from '@/styles' import type {Props, TextInfo, RefType} from './input2' -import {getTextStyle} from './text' +import {getTextStyle} from './text.styles' import {useColorScheme} from 'react-native' const maybeParseInt = (input: string | number, radix: number): number => diff --git a/shared/common-adapters/input2.native.tsx b/shared/common-adapters/input2.native.tsx index 5d5f3d095d7a..ed191864d2db 100644 --- a/shared/common-adapters/input2.native.tsx +++ b/shared/common-adapters/input2.native.tsx @@ -2,7 +2,7 @@ import * as React from 'react' import * as Styles from '@/styles' import type {Props, TextInfo, RefType} from './input2' import {isIOS} from '@/constants/platform' -import {getTextStyle} from './text' +import {getTextStyle} from './text.styles' import { TextInput, type NativeSyntheticEvent, diff --git a/shared/common-adapters/labeled-input.tsx b/shared/common-adapters/labeled-input.tsx index 478bf7e902ef..2b93bea05b4f 100644 --- a/shared/common-adapters/labeled-input.tsx +++ b/shared/common-adapters/labeled-input.tsx @@ -1,7 +1,8 @@ import * as React from 'react' import PlainInput, {type PropsWithInput, type PlainInputRef} from './plain-input' import {Box2} from './box' -import Text, {getTextStyle} from './text' +import Text from './text' +import {getTextStyle} from './text.styles' import * as Styles from '@/styles' import {isMobile} from '@/constants/platform' import {useColorScheme} from 'react-native' @@ -52,7 +53,7 @@ const LabeledInputImpl = React.forwardRef(function Labeled const collapsed = focused || populated || multiline // We're using fontSize to derive heights - const fontSize = getTextStyle(props.textType || 'BodySemibold', isDarkMode).fontSize + const fontSize = getTextStyle(props.textType || 'BodySemibold', isDarkMode).fontSize ?? 14 const computedContainerSize = fontSize + (isMobile ? 48 : 38) + (multiline ? fontSize : 0) return ( diff --git a/shared/common-adapters/markdown/channel.tsx b/shared/common-adapters/markdown/channel.tsx index a80546942696..538620210165 100644 --- a/shared/common-adapters/markdown/channel.tsx +++ b/shared/common-adapters/markdown/channel.tsx @@ -1,6 +1,7 @@ import * as Chat from '@/stores/chat2' import type * as T from '@/constants/types' -import Text, {type StylesTextCrossPlatform} from '../text' +import Text from '../text' +import type {StylesTextCrossPlatform} from '../text.shared' import * as React from 'react' type OwnProps = { diff --git a/shared/common-adapters/markdown/index.tsx b/shared/common-adapters/markdown/index.tsx index 35203c8cb7e7..af6eae1f1ef7 100644 --- a/shared/common-adapters/markdown/index.tsx +++ b/shared/common-adapters/markdown/index.tsx @@ -14,7 +14,7 @@ import { serviceOnlyNoWrapOutput, } from './react' import type * as T from '@/constants/types' -import type {StylesTextCrossPlatform, LineClampType} from '@/common-adapters/text' +import type {StylesTextCrossPlatform, LineClampType} from '@/common-adapters/text.shared' import isArray from 'lodash/isArray' import {ErrorBoundary} from 'react-error-boundary' diff --git a/shared/common-adapters/markdown/maybe-mention/index.tsx b/shared/common-adapters/markdown/maybe-mention/index.tsx index 86bbe6a0a396..3858e5d93845 100644 --- a/shared/common-adapters/markdown/maybe-mention/index.tsx +++ b/shared/common-adapters/markdown/maybe-mention/index.tsx @@ -1,6 +1,7 @@ import * as Chat from '@/stores/chat2' import * as T from '@/constants/types' -import Text, {type StylesTextCrossPlatform} from '@/common-adapters/text' +import Text from '@/common-adapters/text' +import type {StylesTextCrossPlatform} from '@/common-adapters/text.shared' import Mention from '../../mention-container' import TeamMention from './team' import UnknownMention from './unknown' @@ -40,7 +41,7 @@ const MaybeMention = (props: Props) => { /> ) case T.RPCChat.UIMaybeMentionStatus.user: - return + return case T.RPCChat.UIMaybeMentionStatus.team: return ( { {children} @@ -265,6 +267,7 @@ const reactComponentsForMarkdownType = { state.insideStrong && markdownStyles.boldStyle, state.styleOverride?.em, ])} + allowFontScaling={state.allowFontScaling} > {output(node['content'], state)} @@ -283,6 +286,7 @@ const reactComponentsForMarkdownType = { size={state.styleOverride?.emojiSize?.size ?? 16} key={state.key} disableSelecting={state.virtualText} + allowFontScaling={state.allowFontScaling} style={state.styleOverride?.emoji} /> ), diff --git a/shared/common-adapters/markdown/service-decoration.tsx b/shared/common-adapters/markdown/service-decoration.tsx index 90879a58e0b2..32f0c040ca6a 100644 --- a/shared/common-adapters/markdown/service-decoration.tsx +++ b/shared/common-adapters/markdown/service-decoration.tsx @@ -8,7 +8,9 @@ import KbfsPath from '@/fs/common/kbfs-path' import MaybeMention from './maybe-mention' import Mention from '../mention-container' import PaymentStatus from '../../chat/payments/status' -import Text, {type StylesTextCrossPlatform} from '@/common-adapters/text' +import Text from '@/common-adapters/text' +import type {StylesTextCrossPlatform} from '@/common-adapters/text.shared' +import {useClickURL} from '@/common-adapters/text-url' import WithTooltip from '../with-tooltip' import type {StyleOverride} from '.' import {RPCToEmojiData, default as Emoji} from '@/common-adapters/emoji' @@ -57,6 +59,7 @@ type WarningLinkProps = { const WarningLink = (props: WarningLinkProps) => { const {display, punycode, url} = props const navigateAppend = C.useRouterState(s => s.dispatch.navigateAppend) + const urlProps = useClickURL(url) if (Styles.isMobile) { return ( { type="BodyPrimaryLink" style={Styles.collapseStyles([props.wrapStyle, linkStyle, props.linkStyle])} title={display} - onClickURL={url} - onLongPressURL={url} + {...urlProps} > { ) } +const URLText = (p: { + url: string + className?: string + type?: string + style?: Styles.StylesCrossPlatform + title?: string + children?: React.ReactNode +}) => { + const urlProps = useClickURL(p.url) + return ( + + {p.children} + + ) +} + export type Props = { json: string allowFontScaling?: boolean @@ -172,32 +196,30 @@ const ServiceDecoration = (p: Props) => { wrapStyle={styles['wrapStyle']} /> ) : ( - {parsed.link.url} - + ) } else if (parsed.typ === T.RPCChat.UITextDecorationTyp.mailto) { const openUrl = parsed.mailto.url.toLowerCase().startsWith('mailto:') ? parsed.mailto.url : 'mailto:' + parsed.mailto.url return ( - {parsed.mailto.url} - + ) } else if (parsed.typ === T.RPCChat.UITextDecorationTyp.channelnamemention) { return ( diff --git a/shared/common-adapters/mention-container.tsx b/shared/common-adapters/mention-container.tsx index f7044658c04c..2f36d05b027b 100644 --- a/shared/common-adapters/mention-container.tsx +++ b/shared/common-adapters/mention-container.tsx @@ -36,7 +36,9 @@ const Container = (ownProps: OwnProps) => { const onClick = Chat.isSpecialMention(username) ? undefined : _onClick const props = { + allowFontScaling: ownProps.allowFontScaling, onClick, + style: ownProps.style, theme, username, } diff --git a/shared/common-adapters/name-with-icon.tsx b/shared/common-adapters/name-with-icon.tsx index da7810497fad..488105de97ac 100644 --- a/shared/common-adapters/name-with-icon.tsx +++ b/shared/common-adapters/name-with-icon.tsx @@ -6,12 +6,8 @@ import Avatar, {type AvatarSize} from './avatar' import {Box2} from './box' import ClickableBox from './clickable-box' import Icon, {type IconType} from './icon' -import Text, { - type TextType, - type StylesTextCrossPlatform, - type AllowedColors, - type TextTypeBold, -} from './text' +import Text from './text' +import type {TextType, StylesTextCrossPlatform, AllowedColors, TextTypeBold} from './text.shared' import ConnectedUsernames from './usernames' import {useTrackerState} from '@/stores/tracker2' import {useProfileState} from '@/stores/profile' diff --git a/shared/common-adapters/new-input.tsx b/shared/common-adapters/new-input.tsx index 6f2d43e05b3c..1b6da029e62a 100644 --- a/shared/common-adapters/new-input.tsx +++ b/shared/common-adapters/new-input.tsx @@ -2,7 +2,8 @@ import * as React from 'react' import PlainInput, {type PropsWithInput, type PlainInputRef} from './plain-input' import {Box2} from './box' import Icon, {type IconType} from './icon' -import Text, {getTextStyle} from './text' +import Text from './text' +import {getTextStyle} from './text.styles' import * as Styles from '@/styles' import './input.css' diff --git a/shared/common-adapters/plain-input.d.ts b/shared/common-adapters/plain-input.d.ts index eb59d90d6bb1..198751c7592e 100644 --- a/shared/common-adapters/plain-input.d.ts +++ b/shared/common-adapters/plain-input.d.ts @@ -1,6 +1,6 @@ import type * as React from 'react' import type {globalMargins, CustomStyles} from '@/styles' -import type {TextType} from './text' +import type {TextType} from './text.shared' import type {NativeSyntheticEvent} from 'react-native' export type KeyboardType = diff --git a/shared/common-adapters/plain-input.desktop.tsx b/shared/common-adapters/plain-input.desktop.tsx index 94ab9c6b66ae..895969a84daa 100644 --- a/shared/common-adapters/plain-input.desktop.tsx +++ b/shared/common-adapters/plain-input.desktop.tsx @@ -1,6 +1,6 @@ import * as React from 'react' import * as Styles from '@/styles' -import {getTextStyle} from './text.desktop' +import {getTextStyle} from './text.styles' import logger from '@/logger' import {checkTextInfo} from './input.shared' import type {InternalProps, TextInfo, Selection} from './plain-input' diff --git a/shared/common-adapters/plain-input.native.tsx b/shared/common-adapters/plain-input.native.tsx index dea0dc6fb17d..5313fb84144d 100644 --- a/shared/common-adapters/plain-input.native.tsx +++ b/shared/common-adapters/plain-input.native.tsx @@ -11,7 +11,7 @@ import { } from 'react-native' import {Box2} from './box' import {checkTextInfo} from './input.shared' -import {getTextStyle} from './text' +import {getTextStyle} from './text.styles' import {isIOS} from '@/constants/platform' import {stringToUint8Array} from 'uint8array-extras' import {useColorScheme} from 'react-native' diff --git a/shared/common-adapters/search-filter.tsx b/shared/common-adapters/search-filter.tsx index 68aa929a90e6..b05917e4ff82 100644 --- a/shared/common-adapters/search-filter.tsx +++ b/shared/common-adapters/search-filter.tsx @@ -4,7 +4,8 @@ import {Box2, Box2Measure} from './box' import ClickableBox, {ClickableBox2} from './clickable-box' import NewInput from './new-input' import PlainInput, {type PlainInputRef} from './plain-input' -import Text, {type AllowedColors} from './text' +import Text from './text' +import type {AllowedColors} from './text.shared' import ProgressIndicator from './progress-indicator' import {useHotKey} from './hot-key' import Icon, {type IconType} from './icon' diff --git a/shared/common-adapters/team-with-popup.tsx b/shared/common-adapters/team-with-popup.tsx index f907e5e1ca80..b38b8534847f 100644 --- a/shared/common-adapters/team-with-popup.tsx +++ b/shared/common-adapters/team-with-popup.tsx @@ -3,7 +3,8 @@ import * as Teams from '@/stores/teams' import * as React from 'react' import {Box2} from './box' import * as Styles from '@/styles' -import Text, {type TextType} from './text' +import Text from './text' +import type {TextType} from './text.shared' import DelayedMounting from './delayed-mounting' import {TeamDetailsSubscriber} from '../teams/subscriber' import type TeamInfoType from '../profile/user/teams/teaminfo' diff --git a/shared/common-adapters/text-url.d.ts b/shared/common-adapters/text-url.d.ts new file mode 100644 index 000000000000..83d7e0dd24d0 --- /dev/null +++ b/shared/common-adapters/text-url.d.ts @@ -0,0 +1,5 @@ +export declare function useClickURL(url: string | undefined): { + readonly onClick?: (e: React.BaseSyntheticEvent) => void + readonly onContextMenu?: (e: React.BaseSyntheticEvent) => void + readonly onLongPress?: () => void +} diff --git a/shared/common-adapters/text-url.desktop.tsx b/shared/common-adapters/text-url.desktop.tsx new file mode 100644 index 000000000000..b97636272d18 --- /dev/null +++ b/shared/common-adapters/text-url.desktop.tsx @@ -0,0 +1,17 @@ +import openURL from '@/util/open-url' +import KB2 from '@/util/electron.desktop' +const {showContextMenu} = KB2.functions + +export function useClickURL(url: string | undefined) { + if (!url) return {} as const + return { + onClick: (e: React.BaseSyntheticEvent) => { + e.stopPropagation() + openURL(url) + }, + onContextMenu: (e: React.BaseSyntheticEvent) => { + e.stopPropagation() + showContextMenu?.(url) + }, + } as const +} diff --git a/shared/common-adapters/text-url.native.tsx b/shared/common-adapters/text-url.native.tsx new file mode 100644 index 000000000000..24c0cd8931d5 --- /dev/null +++ b/shared/common-adapters/text-url.native.tsx @@ -0,0 +1,19 @@ +import openURL from '@/util/open-url' +import * as Clipboard from 'expo-clipboard' +import {Alert} from 'react-native' + +export function useClickURL(url: string | undefined) { + if (!url) return {} as const + return { + onClick: () => { + openURL(url) + }, + onLongPress: () => { + Alert.alert('', url, [ + {onPress: () => openURL(url), text: 'Open'}, + {onPress: () => { void Clipboard.setStringAsync(url) }, text: 'Copy'}, + {text: 'Cancel'}, + ]) + }, + } as const +} diff --git a/shared/common-adapters/text.css b/shared/common-adapters/text.css index be009814a316..33eeb993b200 100644 --- a/shared/common-adapters/text.css +++ b/shared/common-adapters/text.css @@ -1,4 +1,4 @@ -/* Used by text on desktop to speed up perf */ +/* Text desktop styles */ .text_HeaderBig, .text_HeaderBigExtrabold, @@ -48,6 +48,7 @@ font-family: Keybase; } +/* Dark mode: primary-color types flip to white */ @media (prefers-color-scheme: dark) { .text_HeaderBig, .text_HeaderBigExtrabold, @@ -66,6 +67,7 @@ } } +/* Secondary color group */ .text_BodySecondaryLink, .text_BodySmall, .text_BodySmallBold, @@ -85,6 +87,7 @@ color: var(--color-black_50OrWhite_40); } +/* Link color group */ .text_HeaderLink, .text_BodyBigLink, .text_BodySemiboldLink, @@ -94,6 +97,18 @@ color: var(--color-blueDark); } +/* Special color types */ +.text_BodySmallError { + color: var(--color-redDark); +} +.text_BodySmallSuccess { + color: var(--color-greenDark); +} +.text_BodySmallWallet { + color: var(--color-purpleDark); +} + +/* Italic group */ .text_HeaderItalic, .text_BodyItalic, .text_BodySemiboldItalic, @@ -103,6 +118,7 @@ font-style: italic; } +/* Cursor pointer for link types */ .text_HeaderLink, .text_BodyBigLink, .text_BodyPrimaryLink, @@ -116,71 +132,17 @@ cursor: pointer; } +/* Font size / line-height groups */ :is(.text_HeaderBig, .text_HeaderBigExtrabold) { font-size: 24px; line-height: 28px; } -.text_Body, -.text_BodyItalic, -.text_BodyPrimaryLink, -.text_BodySecondaryLink, -.text_BodySmall, -.text_BodySmallItalic, -.text_BodySmallPrimaryLink, -.text_BodySmallSecondaryLink, -.text_BodySmallError, -.text_BodySmallSuccess, -.text_BodySmallWallet, -.text_BodyTiny, -.text_BodyTinyLink { - font-weight: 400; -} - -.text_BodyBig, -.text_BodyBigLink, -.text_BodySemibold, -.text_BodySemiboldItalic, -.text_BodySemiboldLink, -.text_BodySmallSemiboldItalic, -.text_BodySmallSemiboldPrimaryLink, -.text_BodySmallSemiboldSecondaryLink, -.text_BodyTinySemibold, -.text_BodyTinySemiboldItalic, -.text_BodySmallSemibold { - font-weight: 600; -} - -.text_HeaderBig, -.text_Header, -.text_HeaderItalic, -.text_HeaderLink, -.text_BodyBold, -.text_BodySmallBold, -.text_BodyTinyBold { - font-weight: 700; -} - -.text_HeaderBigExtrabold, -.text_HeaderExtrabold, -.text_BodyExtrabold, -.text_BodySmallExtrabold, -.text_BodySmallExtraboldSecondaryLink, -.text_BodyTinyExtrabold { - font-weight: 800; -} - :is(.text_Header, .text_HeaderItalic, .text_HeaderExtrabold, .text_HeaderLink) { font-size: 18px; line-height: 22px; } -@media (prefers-color-scheme: dark) { - .text_HeaderLink { - color: var(--color-blueDarker2_75); - } -} - :is(.text_BodyBig, .text_BodyBigExtrabold, .text_BodyBigLink) { font-size: 15px; line-height: 19px; @@ -211,21 +173,12 @@ .text_BodySmallExtraboldSecondaryLink, .text_BodySmallError, .text_BodySmallSuccess, -.text_BodySmallWallet { +.text_BodySmallWallet, +.text_BodySmallSemibold { font-size: 13px; line-height: 17px; } -.text_BodySmallError { - color: var(--color-redDark); -} -.text_BodySmallSuccess { - color: var(--color-greenDark); -} -.text_BodySmallWallet { - color: var(--color-purpleDark); -} - .text_BodyTiny, .text_BodyTinyLink, .text_BodyTinyBold, @@ -236,11 +189,58 @@ line-height: 16px; } +/* Font weight groups */ +.text_Body, +.text_BodyItalic, +.text_BodyPrimaryLink, +.text_BodySecondaryLink, +.text_BodySmall, +.text_BodySmallItalic, +.text_BodySmallPrimaryLink, +.text_BodySmallSecondaryLink, +.text_BodySmallError, +.text_BodySmallSuccess, +.text_BodySmallWallet, +.text_BodyTiny, +.text_BodyTinyLink { + font-weight: 400; +} + +.text_BodyBig, +.text_BodyBigLink, +.text_BodySemibold, +.text_BodySemiboldItalic, +.text_BodySemiboldLink, +.text_BodySmallSemiboldItalic, +.text_BodySmallSemiboldPrimaryLink, +.text_BodySmallSemiboldSecondaryLink, +.text_BodyTinySemibold, +.text_BodyTinySemiboldItalic, .text_BodySmallSemibold { - font-size: 13px; - line-height: 17px; + font-weight: 600; +} + +.text_HeaderBig, +.text_Header, +.text_HeaderItalic, +.text_HeaderLink, +.text_BodyBold, +.text_BodySmallBold, +.text_BodyTinyBold { + font-weight: 700; } +.text_HeaderBigExtrabold, +.text_HeaderExtrabold, +.text_BodyBigExtrabold, +.text_BodyExtrabold, +.text_BodySmallExtrabold, +.text_BodySmallExtraboldSecondaryLink, +.text_BodyTinyExtrabold { + font-weight: 800; +} + +/* Terminal types */ :is(.text_Terminal, .text_TerminalComment, .text_TerminalEmpty, .text_TerminalInline) { font-size: 13px; line-height: 20px; @@ -270,45 +270,86 @@ word-wrap: break-word; } +/* Dark mode link override */ +@media (prefers-color-scheme: dark) { + .text_HeaderLink { + color: var(--color-blueDarker2_75); + } +} + +/* Negative: white for most types */ +.text_negative { + color: rgba(255, 255, 255, 0.85) !important; +} + +/* Terminal types have non-white negative colors */ +.text_negative.text_Terminal { + color: light-dark(#a8ccff, #4c8eff) !important; +} +.text_negative.text_TerminalComment, +.text_negative.text_TerminalEmpty { + color: var(--color-blueLighter_40) !important; +} +.text_negative.text_TerminalInline { + color: light-dark(#2645a3, #f00) !important; +} + +/* Hover underline for link types */ +.text_hover-underline:hover { + text-decoration: underline; +} + +/* Utility classes */ .text_center { display: inline-block; text-align: center; } -:is(.lineClamp1, .lineClamp2, .lineClamp3, .lineClamp4, .lineClamp5) { +.text_selectable { + user-select: text; + cursor: text; +} + +.text_clickable { + cursor: pointer; +} + +:is(.text_lineClamp1, .text_lineClamp2, .text_lineClamp3, .text_lineClamp4, .text_lineClamp5) { -webkit-box-orient: vertical; display: -webkit-box; - /* in chrome this will complain about main thread hit scroll scroll issues and clip works but*/ - /* not clear its worth trying it */ overflow: hidden; text-overflow: ellipsis; word-break: break-word; } -.lineClamp1 { +.text_lineClamp1 { -webkit-line-clamp: 1; } -.lineClamp2 { +.text_lineClamp2 { -webkit-line-clamp: 2; } -.lineClamp3 { +.text_lineClamp3 { -webkit-line-clamp: 3; } -.lineClamp4 { +.text_lineClamp4 { -webkit-line-clamp: 4; } -.lineClamp5 { +.text_lineClamp5 { -webkit-line-clamp: 5; } -.noLineHeight { - line-height: unset !important; +.text_underline { + text-decoration: underline; +} + +.text_underlineNever { + text-decoration: none !important; } -.virtualText::before { +.text_virtualText::before { content: attr(data-virtual-text); } diff --git a/shared/common-adapters/text.d.ts b/shared/common-adapters/text.d.ts index f4906aef4dff..36e08afca95f 100644 --- a/shared/common-adapters/text.d.ts +++ b/shared/common-adapters/text.d.ts @@ -1,108 +1,28 @@ -import type * as React from 'react' -import type {TextType} from './text.shared' import type * as CSS from '@/styles/css' -import type colors from '@/styles/colors' +import type * as React from 'react' import type {MeasureRef} from './measure-ref' +import type {TextType} from './text.shared' -type Background = - | 'Announcements' - | 'Documentation' - | 'HighRisk' - | 'Information' - | 'Normal' - | 'Success' - | 'Terminal' - -type Colors = typeof colors -type TextTypeBold = 'BodyTinyBold' | 'BodySmallBold' | 'BodyBold' | 'BodyBig' | 'Header' | 'HeaderBig' - -// Talk to design before adding a color here - these should cover all cases. -type AllowedColorNames = - | 'blueDark' - | 'blueLighter' // for terminal background only - | 'greenDark' - | 'greenLight' - | 'redDark' - | 'purpleDark' - | 'black' - | 'black_on_white' - | 'black_50' - | 'black_50_on_white' - | 'black_50OrWhite_40' - | 'black_35' - | 'black_20' - | 'black_20_on_white' - | 'white' - | 'white_75' - | 'white_40' - | 'brown_75' - | 'orange' - | 'transparent' - -export type AllowedColors = Colors[AllowedColorNames] | 'inherit' - -export type _StylesTextCrossPlatform = CSS._CustomStyles<'color', {color?: AllowedColors}> -export type StylesTextCrossPlatform = CSS.CustomStyles<'color', {color?: AllowedColors}> - -export type LineClampType = 1 | 2 | 3 | 4 | 5 - -type Props = { - ref?: never - // TODO could make this ref if we make this a function component - textRef?: React.RefObject +export type Props = { + type?: TextType + children?: React.ReactNode + style?: CSS.StylesCrossPlatform allowFontScaling?: boolean - allowHighlightText?: boolean // if true, highlighttext through refs works,, + onClick?: (e: React.BaseSyntheticEvent) => void + onContextMenu?: (e: React.BaseSyntheticEvent) => void + onLongPress?: () => void center?: boolean - children?: React.ReactNode - className?: string - ellipsizeMode?: 'head' | 'middle' | 'tail' | 'clip' // mobile only, defines how ellipsis will be put in if `lineClamp` is supplied,, - lineClamp?: LineClampType negative?: boolean - onClick?: ((e: React.BaseSyntheticEvent) => void) | null - onClickURL?: string - onLongPress?: () => void - onLongPressURL?: string - onPress?: never - fixOverdraw?: boolean // use fastBlank to fix overdraw issues TODO support auto when this is a function - plainText?: boolean + lineClamp?: number + ellipsizeMode?: 'head' | 'middle' | 'tail' | 'clip' selectable?: boolean - style?: CSS.StylesCrossPlatform //StylesTextCrossPlatform ideally this but its more complex than its worth now - textBreakStrategy?: 'simple' | 'highQuality' | 'balanced' // android only,, title?: string tooltip?: string - type: TextType + textRef?: React.RefObject underline?: boolean underlineNever?: boolean - virtualText?: boolean // desktop only. use css to print text thats uncopyable -} - -type MetaType = { - fontSize: 12 | 13 | 14 | 15 | 18 | 24 | 16 | 17 | 20 | 28 - colorForBackground: { - positive: string - negative: string - } - isLink?: true - styleOverride?: CSS.StylesCrossPlatform - isTerminal?: true + virtualText?: boolean + className?: string } - -export type TextMeasureRef = { - highlightText: () => void -} & MeasureRef - -// if we fix the ref thing -// export declare const Text: ReturnType> export declare const Text: (p: Props) => React.ReactNode - -export type TextStyle = Omit & { - fontSize: number - lineHeight: number - color: string - cursor: string -} - -export declare function getTextStyle(type: TextType, isDarkMode: boolean): TextStyle - -export type {Background, MetaType, Props, TextType, TextTypeBold} export default Text diff --git a/shared/common-adapters/text.desktop.tsx b/shared/common-adapters/text.desktop.tsx index ca037b209b61..62635b72a394 100644 --- a/shared/common-adapters/text.desktop.tsx +++ b/shared/common-adapters/text.desktop.tsx @@ -1,133 +1,64 @@ import * as Styles from '@/styles' -import * as React from 'react' -import openURL from '@/util/open-url' -import {fontSizeToSizeStyle, metaData} from './text.meta.desktop' -import type {Props, TextType, _StylesTextCrossPlatform, TextStyle} from './text' +import {linkTypes} from './text.shared' +import type {Props} from './text' import './text.css' -import KB2 from '@/util/electron.desktop' -const {showContextMenu} = KB2.functions -const Text = React.memo(function Text(p: Props) { - const {onClickURL, allowHighlightText, textRef, className: _className} = p - const {type, onClick, negative, underlineNever, lineClamp, selectable} = p - const {center, tooltip, virtualText, underline, title, style, children} = p - const spanRef = React.useRef(null) - const highlightText = React.useCallback(() => { - const el = spanRef.current - if (!el) { - return - } - const range = document.createRange() - range.selectNodeContents(el) - - const sel = window.getSelection() - if (sel) { - sel.removeAllRanges() - sel.addRange(range) - } - }, []) - - const setRef = React.useCallback( - (r: HTMLSpanElement | null) => { - if (allowHighlightText) { - spanRef.current = r +export function Text(p: Props) { + const type = p.type ?? 'BodySmall' + const {textRef} = p + const setRef = textRef + ? (r: HTMLSpanElement | null) => { + textRef.current = r + ? {divRef: {current: null}, measure: () => r.getBoundingClientRect()} + : null } - - if (textRef) { - // outer type isn't writable due to class components - const writeRef = textRef - writeRef.current = { - divRef: {current: null}, - highlightText: () => { - allowHighlightText && highlightText() - }, - measure: () => { - return r?.getBoundingClientRect() - }, + : undefined + const cn = Styles.classNames(`text_${type}`, p.className, { + text_center: p.center, + text_clickable: !!p.onClick, + 'text_hover-underline': linkTypes.has(type), + text_lineClamp1: p.lineClamp === 1, + text_lineClamp2: p.lineClamp === 2, + text_lineClamp3: p.lineClamp === 3, + text_lineClamp4: p.lineClamp === 4, + text_lineClamp5: p.lineClamp === 5, + text_negative: p.negative, + text_selectable: p.selectable, + text_underline: p.underline, + text_underlineNever: p.underlineNever, + text_virtualText: p.virtualText, + tooltip: !!p.tooltip, + }) + const lcStyle: React.CSSProperties | undefined = + p.lineClamp && p.lineClamp > 5 + ? { + WebkitBoxOrient: 'vertical', + WebkitLineClamp: p.lineClamp, + display: '-webkit-box', + overflow: 'hidden', + textOverflow: 'ellipsis', + wordBreak: 'break-word', } - } - }, - [allowHighlightText, highlightText, textRef] - ) - - const className = (() => { - const isLink = metaData(true)[type].isLink - return Styles.classNames(`text_${type}`, _className, { - clickable: !!onClick, - color_white_important: negative, - underline: underline || (isLink && negative), - 'underline-never': underlineNever, - // eslint-disable-next-line sort-keys - 'hover-underline': isLink && !negative, - lineClamp1: lineClamp === 1, - lineClamp2: lineClamp === 2, - lineClamp3: lineClamp === 3, - lineClamp4: lineClamp === 4, - lineClamp5: lineClamp === 5, - selectable: selectable, - text_center: center, - tooltip: tooltip, - virtualText: virtualText, - }) - })() - - const onContextMenu = React.useCallback( - (event: React.SyntheticEvent) => { - const url = onClickURL - if (!url) { - return - } - event.stopPropagation() - showContextMenu?.(url) - }, - [onClickURL] - ) - - const urlClick = React.useCallback( - (e: React.MouseEvent) => { - if (!onClickURL) { - return - } - e.stopPropagation() - openURL(onClickURL) - }, - [onClickURL] - ) + : undefined + const style = lcStyle + ? {...lcStyle, ...(p.style as React.CSSProperties)} + : p.style + ? Styles.castStyleDesktop(p.style) + : undefined return ( - {virtualText ? null : children} + {p.virtualText ? null : p.children} ) -}) - -// Only used by external components -export function getTextStyle(type: TextType, isDarkMode: boolean): TextStyle { - const meta = metaData(isDarkMode)[type] - const sizeStyle = fontSizeToSizeStyle(meta.fontSize) - // pipe positive color through because caller probably isn't using class - const colorStyle = {color: meta.colorForBackground['positive']} - const cursorStyle = meta.isLink ? {cursor: 'pointer'} : null - - return Styles.platformStyles({ - common: { - ...meta.styleOverride, - }, - isElectron: { - ...sizeStyle, - ...colorStyle, - ...cursorStyle, - }, - }) } - export default Text diff --git a/shared/common-adapters/text.meta.native.tsx b/shared/common-adapters/text.meta.native.tsx index 669bfe704111..820fc3b95fdc 100644 --- a/shared/common-adapters/text.meta.native.tsx +++ b/shared/common-adapters/text.meta.native.tsx @@ -1,372 +1,313 @@ import * as Styles from '@/styles' -import type {MetaType, TextType} from './text' +import type {TextType} from './text.shared' -// need to be `undefined` instead of `null` since `null` doesn't ellipsize at -// all. -export function lineClamp(lines: number | undefined, mode: string | undefined): object { - return { - ...(lines ? {ellipsizeMode: mode, numberOfLines: lines} : null), - } +export const negativeColors: {[K in TextType]: string} = { + Body: Styles.globalColors.white, + BodyBig: Styles.globalColors.white, + BodyBigExtrabold: Styles.globalColors.white, + BodyBigLink: Styles.globalColors.white, + BodyBold: Styles.globalColors.white, + BodyExtrabold: Styles.globalColors.white, + BodyItalic: Styles.globalColors.white, + BodyPrimaryLink: Styles.globalColors.white, + BodySecondaryLink: Styles.globalColors.white, + BodySemibold: Styles.globalColors.white, + BodySemiboldItalic: Styles.globalColors.white, + BodySemiboldLink: Styles.globalColors.white, + BodySmall: Styles.globalColors.white, + BodySmallBold: Styles.globalColors.white, + BodySmallError: Styles.globalColors.white, + BodySmallExtrabold: Styles.globalColors.white, + BodySmallExtraboldSecondaryLink: Styles.globalColors.white, + BodySmallItalic: Styles.globalColors.white, + BodySmallPrimaryLink: Styles.globalColors.white, + BodySmallSecondaryLink: Styles.globalColors.white, + BodySmallSemibold: Styles.globalColors.white, + BodySmallSemiboldItalic: Styles.globalColors.white, + BodySmallSemiboldPrimaryLink: Styles.globalColors.white, + BodySmallSemiboldSecondaryLink: Styles.globalColors.white, + BodySmallSuccess: Styles.globalColors.white, + BodySmallWallet: Styles.globalColors.white, + BodyTiny: Styles.globalColors.white, + BodyTinyBold: Styles.globalColors.white, + BodyTinyExtrabold: Styles.globalColors.white, + BodyTinyLink: Styles.globalColors.white, + BodyTinySemibold: Styles.globalColors.white, + BodyTinySemiboldItalic: Styles.globalColors.white, + Header: Styles.globalColors.white, + HeaderBig: Styles.globalColors.white, + HeaderBigExtrabold: Styles.globalColors.white, + HeaderExtrabold: Styles.globalColors.white, + HeaderItalic: Styles.globalColors.white, + HeaderLink: Styles.globalColors.white, + Terminal: Styles.globalColors.blueDarker, + TerminalComment: Styles.globalColors.blueLighter_40, + TerminalEmpty: Styles.globalColors.blueLighter_40, + TerminalInline: Styles.globalColors.blueDarker, } -export function fontSizeToSizeStyle(fontSize: number): {fontSize: number; lineHeight: number} { - const lineHeight = { - '13': 17, - '15': 19, - '16': 20, - '17': 21, - '20': 24, - '28': 32, - }[String(fontSize)] as number - - return { - fontSize, - lineHeight, - } -} - -const _metaData = (): {[K in TextType]: MetaType} => { - // you CANNOT spread these else the getters dissappear and your colors will be wrong - const whiteNegative = { - get negative() { - return Styles.globalColors.white - }, - get positive() { - return Styles.globalColors.black - }, - } - const whiteNegative_50 = { - get negative() { - return Styles.globalColors.white - }, - get positive() { - return Styles.globalColors.black_50 - }, - } - - const _blueLink = { - get negative() { - return Styles.globalColors.white - }, - get positive() { - return Styles.globalColors.blueDark - }, - } - return { - Body: { - colorForBackground: whiteNegative, - fontSize: 16, - styleOverride: Styles.globalStyles.fontRegular, - }, - BodyBig: { - colorForBackground: whiteNegative, - fontSize: 17, - styleOverride: Styles.globalStyles.fontSemibold, - }, - BodyBigExtrabold: { - colorForBackground: whiteNegative, - fontSize: 17, - styleOverride: Styles.globalStyles.fontExtrabold, - }, - BodyBigLink: { - colorForBackground: _blueLink, - fontSize: 17, - isLink: true, - styleOverride: Styles.globalStyles.fontSemibold, - }, - BodyBold: { - colorForBackground: whiteNegative, - fontSize: 16, - styleOverride: Styles.globalStyles.fontBold, - }, - BodyExtrabold: { - colorForBackground: whiteNegative, - fontSize: 16, - styleOverride: Styles.globalStyles.fontExtrabold, - }, - BodyItalic: { - colorForBackground: whiteNegative, - fontSize: 16, - styleOverride: { - ...Styles.globalStyles.fontRegular, - fontStyle: 'italic', - }, - }, - BodyPrimaryLink: { - colorForBackground: _blueLink, - fontSize: 16, - isLink: true, - styleOverride: Styles.globalStyles.fontRegular, - }, - BodySecondaryLink: { - colorForBackground: whiteNegative_50, - fontSize: 16, - isLink: true, - styleOverride: Styles.globalStyles.fontRegular, - }, - BodySemibold: { - colorForBackground: whiteNegative, - fontSize: 16, - styleOverride: Styles.globalStyles.fontSemibold, - }, - BodySemiboldItalic: { - colorForBackground: whiteNegative, - fontSize: 16, - styleOverride: { - ...Styles.globalStyles.fontSemibold, - fontStyle: 'italic', - }, - }, - BodySemiboldLink: { - colorForBackground: _blueLink, - fontSize: 16, - isLink: true, - styleOverride: Styles.globalStyles.fontSemibold, - }, - BodySmall: { - colorForBackground: whiteNegative_50, - fontSize: 15, - styleOverride: Styles.globalStyles.fontRegular, - }, - BodySmallBold: { - colorForBackground: whiteNegative_50, - fontSize: 15, - styleOverride: Styles.globalStyles.fontBold, - }, - BodySmallError: { - colorForBackground: { - get negative() { - return Styles.globalColors.white - }, - get positive() { - return Styles.globalColors.redDark - }, - }, - fontSize: 15, - styleOverride: Styles.globalStyles.fontRegular, - }, - BodySmallExtrabold: { - colorForBackground: whiteNegative_50, - fontSize: 15, - styleOverride: Styles.globalStyles.fontExtrabold, - }, - BodySmallExtraboldSecondaryLink: { - colorForBackground: whiteNegative_50, - fontSize: 15, - isLink: true, - styleOverride: Styles.globalStyles.fontExtrabold, - }, - BodySmallItalic: { - colorForBackground: whiteNegative_50, - fontSize: 15, - styleOverride: { - ...Styles.globalStyles.fontRegular, - fontStyle: 'italic', - }, - }, - BodySmallPrimaryLink: { - colorForBackground: _blueLink, - fontSize: 15, - isLink: true, - styleOverride: Styles.globalStyles.fontRegular, - }, - BodySmallSecondaryLink: { - colorForBackground: whiteNegative_50, - fontSize: 15, - isLink: true, - styleOverride: Styles.globalStyles.fontRegular, - }, - BodySmallSemibold: { - colorForBackground: whiteNegative_50, - fontSize: 15, - styleOverride: Styles.globalStyles.fontSemibold, - }, - BodySmallSemiboldItalic: { - colorForBackground: whiteNegative_50, - fontSize: 15, - styleOverride: {...Styles.globalStyles.fontSemibold, fontStyle: 'italic'}, - }, - BodySmallSemiboldPrimaryLink: { - colorForBackground: _blueLink, - fontSize: 15, - isLink: true, - styleOverride: Styles.globalStyles.fontSemibold, - }, - BodySmallSemiboldSecondaryLink: { - colorForBackground: _blueLink, - fontSize: 15, - isLink: true, - styleOverride: {...Styles.globalStyles.fontSemibold, textDecorationLine: undefined}, - }, - BodySmallSuccess: { - colorForBackground: { - get negative() { - return Styles.globalColors.white - }, - get positive() { - return Styles.globalColors.greenDark - }, - }, - fontSize: 15, - styleOverride: Styles.globalStyles.fontRegular, - }, - BodySmallWallet: { - colorForBackground: { - get negative() { - return Styles.globalColors.white - }, - get positive() { - return Styles.globalColors.purpleDark - }, - }, - fontSize: 15, - styleOverride: Styles.globalStyles.fontRegular, - }, - BodyTiny: { - colorForBackground: whiteNegative_50, - fontSize: 13, - styleOverride: Styles.globalStyles.fontRegular, - }, - BodyTinyBold: { - colorForBackground: whiteNegative_50, - fontSize: 13, - styleOverride: Styles.globalStyles.fontBold, - }, - BodyTinyExtrabold: { - colorForBackground: whiteNegative_50, - fontSize: 13, - styleOverride: Styles.globalStyles.fontExtrabold, - }, - BodyTinyLink: { - colorForBackground: whiteNegative_50, - fontSize: 13, - isLink: true, - styleOverride: Styles.globalStyles.fontRegular, - }, - BodyTinySemibold: { - colorForBackground: whiteNegative_50, - fontSize: 13, - styleOverride: Styles.globalStyles.fontSemibold, - }, - BodyTinySemiboldItalic: { - colorForBackground: whiteNegative_50, - fontSize: 13, - styleOverride: { - ...Styles.globalStyles.fontSemibold, - fontStyle: 'italic', - }, - }, - Header: { - colorForBackground: whiteNegative, - fontSize: 20, - styleOverride: Styles.globalStyles.fontBold, - }, - HeaderBig: { - colorForBackground: whiteNegative, - fontSize: 28, - styleOverride: Styles.globalStyles.fontBold, - }, - HeaderBigExtrabold: { - colorForBackground: whiteNegative, - fontSize: 28, - styleOverride: Styles.globalStyles.fontExtrabold, - }, - HeaderExtrabold: { - colorForBackground: whiteNegative, - fontSize: 20, - styleOverride: Styles.globalStyles.fontExtrabold, - }, - HeaderItalic: { - colorForBackground: whiteNegative, - fontSize: 20, - styleOverride: { - ...Styles.globalStyles.fontBold, - fontStyle: 'italic', - }, - }, - HeaderLink: { - colorForBackground: _blueLink, - fontSize: 20, - isLink: true, - styleOverride: Styles.globalStyles.fontBold, - }, - Terminal: { - colorForBackground: { - get negative() { - return Styles.globalColors.blueDarker - }, - get positive() { - return Styles.globalColors.blueLighter - }, - }, - fontSize: 15, - styleOverride: { - ...Styles.globalStyles.fontTerminal, - lineHeight: 20, - }, - }, - TerminalComment: { - colorForBackground: { - get negative() { - return Styles.globalColors.blueLighter_40 - }, - get positive() { - return Styles.globalColors.blueLighter_40 - }, - }, - fontSize: 15, - styleOverride: { - ...Styles.globalStyles.fontTerminal, - lineHeight: 20, - }, - }, - TerminalEmpty: { - colorForBackground: { - get negative() { - return Styles.globalColors.blueLighter_40 - }, - get positive() { - return Styles.globalColors.blueLighter_40 - }, - }, - fontSize: 15, - styleOverride: { - ...Styles.globalStyles.fontTerminal, - height: 20, - lineHeight: 20, - }, - }, - TerminalInline: { - colorForBackground: { - get negative() { - return Styles.globalColors.blueDarker - }, - get positive() { - return Styles.globalColors.blueDarker - }, - }, - fontSize: 15, - styleOverride: { - ...Styles.globalStyles.fontTerminal, - backgroundColor: Styles.globalColors.blueLighter2, - borderRadius: 2, - height: 20, - lineHeight: 20, - padding: 2, - }, - }, - } -} - -let _darkMetaData: {[K in TextType]: MetaType} | undefined -let _lightMetaData: {[K in TextType]: MetaType} | undefined - -export const metaData = (isDarkMode: boolean): {[K in TextType]: MetaType} => { - if (isDarkMode) { - _darkMetaData = _darkMetaData || _metaData() - return _darkMetaData - } else { - _lightMetaData = _lightMetaData || _metaData() - return _lightMetaData - } -} +export const typeStyles = Styles.styleSheetCreate(() => ({ + Body: { + ...Styles.globalStyles.fontRegular, + color: Styles.globalColors.black, + fontSize: 16, + lineHeight: 20, + }, + BodyBig: { + ...Styles.globalStyles.fontSemibold, + color: Styles.globalColors.black, + fontSize: 17, + lineHeight: 21, + }, + BodyBigExtrabold: { + ...Styles.globalStyles.fontExtrabold, + color: Styles.globalColors.black, + fontSize: 17, + lineHeight: 21, + }, + BodyBigLink: { + ...Styles.globalStyles.fontSemibold, + color: Styles.globalColors.blueDark, + fontSize: 17, + lineHeight: 21, + }, + BodyBold: { + ...Styles.globalStyles.fontBold, + color: Styles.globalColors.black, + fontSize: 16, + lineHeight: 20, + }, + BodyExtrabold: { + ...Styles.globalStyles.fontExtrabold, + color: Styles.globalColors.black, + fontSize: 16, + lineHeight: 20, + }, + BodyItalic: { + ...Styles.globalStyles.fontRegular, + color: Styles.globalColors.black, + fontSize: 16, + fontStyle: 'italic' as const, + lineHeight: 20, + }, + BodyPrimaryLink: { + ...Styles.globalStyles.fontRegular, + color: Styles.globalColors.blueDark, + fontSize: 16, + lineHeight: 20, + }, + BodySecondaryLink: { + ...Styles.globalStyles.fontRegular, + color: Styles.globalColors.black_50, + fontSize: 16, + lineHeight: 20, + }, + BodySemibold: { + ...Styles.globalStyles.fontSemibold, + color: Styles.globalColors.black, + fontSize: 16, + lineHeight: 20, + }, + BodySemiboldItalic: { + ...Styles.globalStyles.fontSemibold, + color: Styles.globalColors.black, + fontSize: 16, + fontStyle: 'italic' as const, + lineHeight: 20, + }, + BodySemiboldLink: { + ...Styles.globalStyles.fontSemibold, + color: Styles.globalColors.blueDark, + fontSize: 16, + lineHeight: 20, + }, + BodySmall: { + ...Styles.globalStyles.fontRegular, + color: Styles.globalColors.black_50, + fontSize: 15, + lineHeight: 19, + }, + BodySmallBold: { + ...Styles.globalStyles.fontBold, + color: Styles.globalColors.black_50, + fontSize: 15, + lineHeight: 19, + }, + BodySmallError: { + ...Styles.globalStyles.fontRegular, + color: Styles.globalColors.redDark, + fontSize: 15, + lineHeight: 19, + }, + BodySmallExtrabold: { + ...Styles.globalStyles.fontExtrabold, + color: Styles.globalColors.black_50, + fontSize: 15, + lineHeight: 19, + }, + BodySmallExtraboldSecondaryLink: { + ...Styles.globalStyles.fontExtrabold, + color: Styles.globalColors.black_50, + fontSize: 15, + lineHeight: 19, + }, + BodySmallItalic: { + ...Styles.globalStyles.fontRegular, + color: Styles.globalColors.black_50, + fontSize: 15, + fontStyle: 'italic' as const, + lineHeight: 19, + }, + BodySmallPrimaryLink: { + ...Styles.globalStyles.fontRegular, + color: Styles.globalColors.blueDark, + fontSize: 15, + lineHeight: 19, + }, + BodySmallSecondaryLink: { + ...Styles.globalStyles.fontRegular, + color: Styles.globalColors.black_50, + fontSize: 15, + lineHeight: 19, + }, + BodySmallSemibold: { + ...Styles.globalStyles.fontSemibold, + color: Styles.globalColors.black_50, + fontSize: 15, + lineHeight: 19, + }, + BodySmallSemiboldItalic: { + ...Styles.globalStyles.fontSemibold, + color: Styles.globalColors.black_50, + fontSize: 15, + fontStyle: 'italic' as const, + lineHeight: 19, + }, + BodySmallSemiboldPrimaryLink: { + ...Styles.globalStyles.fontSemibold, + color: Styles.globalColors.blueDark, + fontSize: 15, + lineHeight: 19, + }, + BodySmallSemiboldSecondaryLink: { + ...Styles.globalStyles.fontSemibold, + color: Styles.globalColors.blueDark, + fontSize: 15, + lineHeight: 19, + }, + BodySmallSuccess: { + ...Styles.globalStyles.fontRegular, + color: Styles.globalColors.greenDark, + fontSize: 15, + lineHeight: 19, + }, + BodySmallWallet: { + ...Styles.globalStyles.fontRegular, + color: Styles.globalColors.purpleDark, + fontSize: 15, + lineHeight: 19, + }, + BodyTiny: { + ...Styles.globalStyles.fontRegular, + color: Styles.globalColors.black_50, + fontSize: 13, + lineHeight: 17, + }, + BodyTinyBold: { + ...Styles.globalStyles.fontBold, + color: Styles.globalColors.black_50, + fontSize: 13, + lineHeight: 17, + }, + BodyTinyExtrabold: { + ...Styles.globalStyles.fontExtrabold, + color: Styles.globalColors.black_50, + fontSize: 13, + lineHeight: 17, + }, + BodyTinyLink: { + ...Styles.globalStyles.fontRegular, + color: Styles.globalColors.black_50, + fontSize: 13, + lineHeight: 17, + }, + BodyTinySemibold: { + ...Styles.globalStyles.fontSemibold, + color: Styles.globalColors.black_50, + fontSize: 13, + lineHeight: 17, + }, + BodyTinySemiboldItalic: { + ...Styles.globalStyles.fontSemibold, + color: Styles.globalColors.black_50, + fontSize: 13, + fontStyle: 'italic' as const, + lineHeight: 17, + }, + Header: { + ...Styles.globalStyles.fontBold, + color: Styles.globalColors.black, + fontSize: 20, + lineHeight: 24, + }, + HeaderBig: { + ...Styles.globalStyles.fontBold, + color: Styles.globalColors.black, + fontSize: 28, + lineHeight: 32, + }, + HeaderBigExtrabold: { + ...Styles.globalStyles.fontExtrabold, + color: Styles.globalColors.black, + fontSize: 28, + lineHeight: 32, + }, + HeaderExtrabold: { + ...Styles.globalStyles.fontExtrabold, + color: Styles.globalColors.black, + fontSize: 20, + lineHeight: 24, + }, + HeaderItalic: { + ...Styles.globalStyles.fontBold, + color: Styles.globalColors.black, + fontSize: 20, + fontStyle: 'italic' as const, + lineHeight: 24, + }, + HeaderLink: { + ...Styles.globalStyles.fontBold, + color: Styles.globalColors.blueDark, + fontSize: 20, + lineHeight: 24, + }, + Terminal: { + ...Styles.globalStyles.fontTerminal, + color: Styles.globalColors.blueLighter, + fontSize: 15, + lineHeight: 20, + }, + TerminalComment: { + ...Styles.globalStyles.fontTerminal, + color: Styles.globalColors.blueLighter_40, + fontSize: 15, + lineHeight: 20, + }, + TerminalEmpty: { + ...Styles.globalStyles.fontTerminal, + color: Styles.globalColors.blueLighter_40, + fontSize: 15, + height: 20, + lineHeight: 20, + }, + TerminalInline: { + ...Styles.globalStyles.fontTerminal, + backgroundColor: Styles.globalColors.blueLighter2, + borderRadius: 2, + color: Styles.globalColors.blueDarker, + fontSize: 15, + height: 20, + lineHeight: 20, + padding: 2, + }, +}) as const) diff --git a/shared/common-adapters/text.native.tsx b/shared/common-adapters/text.native.tsx index 82d09d3b6691..4ee08f2b929c 100644 --- a/shared/common-adapters/text.native.tsx +++ b/shared/common-adapters/text.native.tsx @@ -1,126 +1,35 @@ -import * as React from 'react' -import openURL from '@/util/open-url' -import {fontSizeToSizeStyle, lineClamp, metaData} from './text.meta.native' -import type {Props, TextType, TextStyle} from './text' import * as Styles from '@/styles' -import {Text as NativeText, Alert} from 'react-native' -import * as Clipboard from 'expo-clipboard' - -const modes = ['positive', 'negative'] as const - -const styles2 = Styles.styleSheetCreate( - () => - ({ - center: {textAlign: 'center'}, - fixOverdraw: {backgroundColor: Styles.globalColors.fastBlank}, - }) as const -) -// TEMP -const styles = Styles.styleSheetCreate(() => - Object.keys(metaData(true)).reduce<{[key: string]: Styles._StylesCrossPlatform}>((map, type) => { - const meta = metaData(true)[type as TextType] - modes.forEach(mode => { - map[`${type}:${mode}`] = { - ...fontSizeToSizeStyle(meta.fontSize), - color: meta.colorForBackground[mode] || Styles.globalColors.black, - ...meta.styleOverride, - } as Styles._StylesCrossPlatform - }) - return map - }, {}) -) - -// Init common styles for perf - -const Text = React.memo( - React.forwardRef(function Text(p, ref) { - const _urlClick = () => { - p.onClickURL && openURL(p.onClickURL) - } - - const _urlCopy = (url?: string) => { - if (!url) return - Clipboard.setStringAsync(url) - .then(() => {}) - .catch(() => {}) - } - - const _urlChooseOption = () => { - const url = p.onLongPressURL - if (!url) return - Alert.alert('', url, [ - {style: 'cancel', text: 'Cancel'}, - {onPress: () => openURL(url), text: 'Open Link'}, - {onPress: () => _urlCopy(url), text: 'Copy Link'}, - ]) - } - - const baseStyle: Styles.StylesCrossPlatform = styles[`${p.type}:${p.negative ? 'negative' : 'positive'}`] - const dynamicStyle: Styles._StylesCrossPlatform = p.negative - ? _getStyle(p.type, p.negative, !!p.underline) - : {} - - let style: Array | Styles.StylesCrossPlatform - if (!Object.keys(dynamicStyle).length) { - style = - p.style || p.center || p.fixOverdraw - ? [baseStyle, p.center && styles2.center, p.fixOverdraw && styles2.fixOverdraw, p.style] - : baseStyle - } else { - style = [baseStyle, dynamicStyle, p.center && styles2.center, p.style] - } - - const onPress = - p.onClick || - (p.onClickURL ? _urlClick : undefined) || - // If selectable and there isn't already an onClick handler, - // make a dummy one so that it shows the selection (on iOS). - (p.selectable ? () => {} : undefined) - - const onLongPress = p.onLongPress || (p.onLongPressURL ? _urlChooseOption : undefined) - - return ( - - {p.children} - - ) - }) -) - -// external things call this so leave the original alone -function _getStyle(type: TextType, isDarkMode: boolean, negative?: boolean, forceUnderline?: boolean) { - if (!negative) { - return forceUnderline ? ({textDecorationLine: 'underline'} as const) : {} - } - // negative === true - const meta = metaData(isDarkMode)[type] - const colorStyle = {color: meta.colorForBackground.negative} - const textDecoration = meta.isLink ? ({textDecorationLine: 'underline'} as const) : {} - - return { - ...colorStyle, - ...textDecoration, - } -} -export function getTextStyle(type: TextType, isDarkMode: boolean): TextStyle { - const meta = metaData(isDarkMode)[type] - const sizeStyle = fontSizeToSizeStyle(meta.fontSize) - const colorStyle = {color: meta.colorForBackground['positive']} - - return { - ...sizeStyle, - ...colorStyle, - ...meta.styleOverride, - } +import {typeStyles, negativeColors} from './text.meta.native' +import type {Props} from './text' +import {Text as RNText} from 'react-native' + +export function Text(p: Props) { + const type = p.type ?? 'BodySmall' + const baseStyle = typeStyles[type] + const negStyle = p.negative ? {color: negativeColors[type]} : undefined + const centerStyle = p.center ? styles.center : undefined + const mergedStyle = + negStyle || centerStyle || p.style + ? Styles.collapseStyles([baseStyle, negStyle, centerStyle, p.style]) + : baseStyle + + return ( + + {p.children} + + ) } export default Text + +const styles = Styles.styleSheetCreate(() => ({ + center: {textAlign: 'center'}, +}) as const) diff --git a/shared/common-adapters/text.shared.tsx b/shared/common-adapters/text.shared.tsx index be10c7a0d8e3..4a37562375d8 100644 --- a/shared/common-adapters/text.shared.tsx +++ b/shared/common-adapters/text.shared.tsx @@ -1,4 +1,6 @@ -import type {Background} from './text' +import type * as CSS from '@/styles/css' +import type colors from '@/styles/colors' + /* eslint-disable sort-keys */ const _allTextTypes = { Body: 'Body', @@ -47,5 +49,79 @@ const _allTextTypes = { type AllTextTypes = typeof _allTextTypes export type TextType = keyof AllTextTypes +export const linkTypes = new Set([ + 'BodyBigLink', + 'BodyPrimaryLink', + 'BodySecondaryLink', + 'BodySemiboldLink', + 'BodySmallPrimaryLink', + 'BodySmallSecondaryLink', + 'BodySmallSemiboldPrimaryLink', + 'BodySmallSemiboldSecondaryLink', + 'BodySmallExtraboldSecondaryLink', + 'BodyTinyLink', + 'HeaderLink', +]) + +export type Background = + | 'Announcements' + | 'Documentation' + | 'HighRisk' + | 'Information' + | 'Normal' + | 'Success' + | 'Terminal' + +type Colors = typeof colors +export type TextTypeBold = 'BodyTinyBold' | 'BodySmallBold' | 'BodyBold' | 'BodyBig' | 'Header' | 'HeaderBig' + +// Talk to design before adding a color here - these should cover all cases. +type AllowedColorNames = + | 'blueDark' + | 'blueLighter' // for terminal background only + | 'greenDark' + | 'greenLight' + | 'redDark' + | 'purpleDark' + | 'black' + | 'black_on_white' + | 'black_50' + | 'black_50_on_white' + | 'black_50OrWhite_40' + | 'black_35' + | 'black_20' + | 'black_20_on_white' + | 'white' + | 'white_75' + | 'white_40' + | 'brown_75' + | 'orange' + | 'transparent' + +export type AllowedColors = Colors[AllowedColorNames] | 'inherit' + +export type _StylesTextCrossPlatform = CSS._CustomStyles<'color', {color?: AllowedColors}> +export type StylesTextCrossPlatform = CSS.CustomStyles<'color', {color?: AllowedColors}> + +export type LineClampType = 1 | 2 | 3 | 4 | 5 + +export type MetaType = { + fontSize: 12 | 13 | 14 | 15 | 18 | 24 | 16 | 17 | 20 | 28 + colorForBackground: { + positive: string + negative: string + } + isLink?: true + styleOverride?: CSS.StylesCrossPlatform + isTerminal?: true +} + +export type TextStyle = CSS._StylesCrossPlatform & { + fontSize?: number + lineHeight?: number + color?: string + cursor?: string +} + export const backgroundModeIsNegative = (bm?: Background): boolean => !!bm && !['Normal', 'Information'].includes(bm) diff --git a/shared/common-adapters/text.styles.d.ts b/shared/common-adapters/text.styles.d.ts new file mode 100644 index 000000000000..229c621f89f7 --- /dev/null +++ b/shared/common-adapters/text.styles.d.ts @@ -0,0 +1,2 @@ +import type {TextType, TextStyle} from './text.shared' +export declare function getTextStyle(type: TextType, isDarkMode: boolean): TextStyle diff --git a/shared/common-adapters/text.meta.desktop.tsx b/shared/common-adapters/text.styles.desktop.tsx similarity index 92% rename from shared/common-adapters/text.meta.desktop.tsx rename to shared/common-adapters/text.styles.desktop.tsx index 9a15b704ddf2..124e4bf7fd00 100644 --- a/shared/common-adapters/text.meta.desktop.tsx +++ b/shared/common-adapters/text.styles.desktop.tsx @@ -1,16 +1,7 @@ import * as Styles from '@/styles' -import type {MetaType, TextType} from './text' +import type {MetaType, TextType, TextStyle} from './text.shared' -export const lineClamp = (lines: number) => ({ - WebkitBoxOrient: 'vertical', - WebkitLineClamp: lines, - display: '-webkit-box', - overflow: 'hidden', - textOverflow: 'ellipsis', - wordBreak: 'break-word', -}) - -export function fontSizeToSizeStyle(fontSize: 12 | 13 | 14 | 15 | 16 | 17 | 18 | 20 | 24 | 28) { +function fontSizeToSizeStyle(fontSize: 12 | 13 | 14 | 15 | 16 | 17 | 18 | 20 | 24 | 28) { const hm = { [12]: 16, [13]: 17, @@ -331,7 +322,7 @@ const _metaData = (): {[K in TextType]: MetaType} => { let _darkMetaData: {[K in TextType]: MetaType} | undefined let _lightMetaData: {[K in TextType]: MetaType} | undefined -export const metaData = (isDarkMode: boolean): {[K in TextType]: MetaType} => { +const metaData = (isDarkMode: boolean): {[K in TextType]: MetaType} => { if (isDarkMode) { _darkMetaData = _darkMetaData || _metaData() return _darkMetaData @@ -340,3 +331,22 @@ export const metaData = (isDarkMode: boolean): {[K in TextType]: MetaType} => { return _lightMetaData } } + +export function getTextStyle(type: TextType, isDarkMode: boolean): TextStyle { + const meta = metaData(isDarkMode)[type] + const sizeStyle = fontSizeToSizeStyle(meta.fontSize) + // pipe positive color through because caller probably isn't using class + const colorStyle = {color: meta.colorForBackground['positive']} + const cursorStyle = meta.isLink ? {cursor: 'pointer'} : null + + return Styles.platformStyles({ + common: { + ...meta.styleOverride, + }, + isElectron: { + ...sizeStyle, + ...colorStyle, + ...cursorStyle, + }, + }) +} diff --git a/shared/common-adapters/text.styles.native.tsx b/shared/common-adapters/text.styles.native.tsx new file mode 100644 index 000000000000..42550dc7ed56 --- /dev/null +++ b/shared/common-adapters/text.styles.native.tsx @@ -0,0 +1,376 @@ +import * as Styles from '@/styles' +import type {MetaType, TextType, TextStyle} from './text.shared' + +function fontSizeToSizeStyle(fontSize: number): {fontSize: number; lineHeight: number} { + const lineHeight = { + '13': 17, + '15': 19, + '16': 20, + '17': 21, + '20': 24, + '28': 32, + }[String(fontSize)] as number + + return { + fontSize, + lineHeight, + } +} + +const _metaData = (): {[K in TextType]: MetaType} => { + // you CANNOT spread these else the getters dissappear and your colors will be wrong + const whiteNegative = { + get negative() { + return Styles.globalColors.white + }, + get positive() { + return Styles.globalColors.black + }, + } + const whiteNegative_50 = { + get negative() { + return Styles.globalColors.white + }, + get positive() { + return Styles.globalColors.black_50 + }, + } + + const _blueLink = { + get negative() { + return Styles.globalColors.white + }, + get positive() { + return Styles.globalColors.blueDark + }, + } + return { + Body: { + colorForBackground: whiteNegative, + fontSize: 16, + styleOverride: Styles.globalStyles.fontRegular, + }, + BodyBig: { + colorForBackground: whiteNegative, + fontSize: 17, + styleOverride: Styles.globalStyles.fontSemibold, + }, + BodyBigExtrabold: { + colorForBackground: whiteNegative, + fontSize: 17, + styleOverride: Styles.globalStyles.fontExtrabold, + }, + BodyBigLink: { + colorForBackground: _blueLink, + fontSize: 17, + isLink: true, + styleOverride: Styles.globalStyles.fontSemibold, + }, + BodyBold: { + colorForBackground: whiteNegative, + fontSize: 16, + styleOverride: Styles.globalStyles.fontBold, + }, + BodyExtrabold: { + colorForBackground: whiteNegative, + fontSize: 16, + styleOverride: Styles.globalStyles.fontExtrabold, + }, + BodyItalic: { + colorForBackground: whiteNegative, + fontSize: 16, + styleOverride: { + ...Styles.globalStyles.fontRegular, + fontStyle: 'italic', + }, + }, + BodyPrimaryLink: { + colorForBackground: _blueLink, + fontSize: 16, + isLink: true, + styleOverride: Styles.globalStyles.fontRegular, + }, + BodySecondaryLink: { + colorForBackground: whiteNegative_50, + fontSize: 16, + isLink: true, + styleOverride: Styles.globalStyles.fontRegular, + }, + BodySemibold: { + colorForBackground: whiteNegative, + fontSize: 16, + styleOverride: Styles.globalStyles.fontSemibold, + }, + BodySemiboldItalic: { + colorForBackground: whiteNegative, + fontSize: 16, + styleOverride: { + ...Styles.globalStyles.fontSemibold, + fontStyle: 'italic', + }, + }, + BodySemiboldLink: { + colorForBackground: _blueLink, + fontSize: 16, + isLink: true, + styleOverride: Styles.globalStyles.fontSemibold, + }, + BodySmall: { + colorForBackground: whiteNegative_50, + fontSize: 15, + styleOverride: Styles.globalStyles.fontRegular, + }, + BodySmallBold: { + colorForBackground: whiteNegative_50, + fontSize: 15, + styleOverride: Styles.globalStyles.fontBold, + }, + BodySmallError: { + colorForBackground: { + get negative() { + return Styles.globalColors.white + }, + get positive() { + return Styles.globalColors.redDark + }, + }, + fontSize: 15, + styleOverride: Styles.globalStyles.fontRegular, + }, + BodySmallExtrabold: { + colorForBackground: whiteNegative_50, + fontSize: 15, + styleOverride: Styles.globalStyles.fontExtrabold, + }, + BodySmallExtraboldSecondaryLink: { + colorForBackground: whiteNegative_50, + fontSize: 15, + isLink: true, + styleOverride: Styles.globalStyles.fontExtrabold, + }, + BodySmallItalic: { + colorForBackground: whiteNegative_50, + fontSize: 15, + styleOverride: { + ...Styles.globalStyles.fontRegular, + fontStyle: 'italic', + }, + }, + BodySmallPrimaryLink: { + colorForBackground: _blueLink, + fontSize: 15, + isLink: true, + styleOverride: Styles.globalStyles.fontRegular, + }, + BodySmallSecondaryLink: { + colorForBackground: whiteNegative_50, + fontSize: 15, + isLink: true, + styleOverride: Styles.globalStyles.fontRegular, + }, + BodySmallSemibold: { + colorForBackground: whiteNegative_50, + fontSize: 15, + styleOverride: Styles.globalStyles.fontSemibold, + }, + BodySmallSemiboldItalic: { + colorForBackground: whiteNegative_50, + fontSize: 15, + styleOverride: {...Styles.globalStyles.fontSemibold, fontStyle: 'italic'}, + }, + BodySmallSemiboldPrimaryLink: { + colorForBackground: _blueLink, + fontSize: 15, + isLink: true, + styleOverride: Styles.globalStyles.fontSemibold, + }, + BodySmallSemiboldSecondaryLink: { + colorForBackground: _blueLink, + fontSize: 15, + isLink: true, + styleOverride: {...Styles.globalStyles.fontSemibold, textDecorationLine: undefined}, + }, + BodySmallSuccess: { + colorForBackground: { + get negative() { + return Styles.globalColors.white + }, + get positive() { + return Styles.globalColors.greenDark + }, + }, + fontSize: 15, + styleOverride: Styles.globalStyles.fontRegular, + }, + BodySmallWallet: { + colorForBackground: { + get negative() { + return Styles.globalColors.white + }, + get positive() { + return Styles.globalColors.purpleDark + }, + }, + fontSize: 15, + styleOverride: Styles.globalStyles.fontRegular, + }, + BodyTiny: { + colorForBackground: whiteNegative_50, + fontSize: 13, + styleOverride: Styles.globalStyles.fontRegular, + }, + BodyTinyBold: { + colorForBackground: whiteNegative_50, + fontSize: 13, + styleOverride: Styles.globalStyles.fontBold, + }, + BodyTinyExtrabold: { + colorForBackground: whiteNegative_50, + fontSize: 13, + styleOverride: Styles.globalStyles.fontExtrabold, + }, + BodyTinyLink: { + colorForBackground: whiteNegative_50, + fontSize: 13, + isLink: true, + styleOverride: Styles.globalStyles.fontRegular, + }, + BodyTinySemibold: { + colorForBackground: whiteNegative_50, + fontSize: 13, + styleOverride: Styles.globalStyles.fontSemibold, + }, + BodyTinySemiboldItalic: { + colorForBackground: whiteNegative_50, + fontSize: 13, + styleOverride: { + ...Styles.globalStyles.fontSemibold, + fontStyle: 'italic', + }, + }, + Header: { + colorForBackground: whiteNegative, + fontSize: 20, + styleOverride: Styles.globalStyles.fontBold, + }, + HeaderBig: { + colorForBackground: whiteNegative, + fontSize: 28, + styleOverride: Styles.globalStyles.fontBold, + }, + HeaderBigExtrabold: { + colorForBackground: whiteNegative, + fontSize: 28, + styleOverride: Styles.globalStyles.fontExtrabold, + }, + HeaderExtrabold: { + colorForBackground: whiteNegative, + fontSize: 20, + styleOverride: Styles.globalStyles.fontExtrabold, + }, + HeaderItalic: { + colorForBackground: whiteNegative, + fontSize: 20, + styleOverride: { + ...Styles.globalStyles.fontBold, + fontStyle: 'italic', + }, + }, + HeaderLink: { + colorForBackground: _blueLink, + fontSize: 20, + isLink: true, + styleOverride: Styles.globalStyles.fontBold, + }, + Terminal: { + colorForBackground: { + get negative() { + return Styles.globalColors.blueDarker + }, + get positive() { + return Styles.globalColors.blueLighter + }, + }, + fontSize: 15, + styleOverride: { + ...Styles.globalStyles.fontTerminal, + lineHeight: 20, + }, + }, + TerminalComment: { + colorForBackground: { + get negative() { + return Styles.globalColors.blueLighter_40 + }, + get positive() { + return Styles.globalColors.blueLighter_40 + }, + }, + fontSize: 15, + styleOverride: { + ...Styles.globalStyles.fontTerminal, + lineHeight: 20, + }, + }, + TerminalEmpty: { + colorForBackground: { + get negative() { + return Styles.globalColors.blueLighter_40 + }, + get positive() { + return Styles.globalColors.blueLighter_40 + }, + }, + fontSize: 15, + styleOverride: { + ...Styles.globalStyles.fontTerminal, + height: 20, + lineHeight: 20, + }, + }, + TerminalInline: { + colorForBackground: { + get negative() { + return Styles.globalColors.blueDarker + }, + get positive() { + return Styles.globalColors.blueDarker + }, + }, + fontSize: 15, + styleOverride: { + ...Styles.globalStyles.fontTerminal, + backgroundColor: Styles.globalColors.blueLighter2, + borderRadius: 2, + height: 20, + lineHeight: 20, + padding: 2, + }, + }, + } +} + +let _darkMetaData: {[K in TextType]: MetaType} | undefined +let _lightMetaData: {[K in TextType]: MetaType} | undefined + +const metaData = (isDarkMode: boolean): {[K in TextType]: MetaType} => { + if (isDarkMode) { + _darkMetaData = _darkMetaData || _metaData() + return _darkMetaData + } else { + _lightMetaData = _lightMetaData || _metaData() + return _lightMetaData + } +} + +export function getTextStyle(type: TextType, isDarkMode: boolean): TextStyle { + const meta = metaData(isDarkMode)[type] + const sizeStyle = fontSizeToSizeStyle(meta.fontSize) + const colorStyle = {color: meta.colorForBackground['positive']} + + return { + ...sizeStyle, + ...colorStyle, + ...meta.styleOverride, + } +} diff --git a/shared/common-adapters/text2.d.ts b/shared/common-adapters/text2.d.ts deleted file mode 100644 index 52f0aad9fc71..000000000000 --- a/shared/common-adapters/text2.d.ts +++ /dev/null @@ -1,22 +0,0 @@ -import type * as CSS from '@/styles/css' -import type * as React from 'react' -import type {TextType} from './text.shared' - -type Props = { - children?: React.ReactNode // ideally just Text2 and string but this isn't easy - title?: string - className?: string - ref?: never - onClick?: never - onClickURL?: never - onLongPress?: never - onLongPressURL?: never - onPress?: never - style?: CSS.StylesCrossPlatform - type?: TextType - lineClamp?: number - selectable?: boolean - ellipsizeMode?: 'head' | 'middle' | 'tail' | 'clip' // mobile only, defines how ellipsis will be put in if `lineClamp` is supplied,, - virtualText?: boolean // desktop only -} -export declare const Text2: (p: Props) => React.ReactNode diff --git a/shared/common-adapters/text2.desktop.tsx b/shared/common-adapters/text2.desktop.tsx deleted file mode 100644 index d29493a74447..000000000000 --- a/shared/common-adapters/text2.desktop.tsx +++ /dev/null @@ -1,49 +0,0 @@ -import * as React from 'react' -import * as Styles from '@/styles' -import {metaData} from './text.meta.desktop' -import type {Props} from './text2' -import {debugWarning} from '@/util/debug-warning' - -const TEMP_MARK_V2 = __DEV__ && (false as boolean) -// const TEMP_SWITCH = __DEV__ && (false as boolean) - -if (TEMP_MARK_V2 /*|| TEMP_SWITCH*/) { - debugWarning('Text2 flags on') -} - -const virtualKey = 'data-virtual-text' -export const Text2 = /*TEMP_SWITCH - ? require('./text').default - : */ React.memo(function Text2(p: Props) { - const {selectable, title, type = 'BodySmall', style, children: _children, lineClamp, virtualText} = p - const isLink = metaData(true)[type].isLink - const className = Styles.classNames(`text_${type}`, p.className, { - lineClamp1: lineClamp === 1, - lineClamp2: lineClamp === 2, - lineClamp3: lineClamp === 3, - lineClamp4: lineClamp === 4, - lineClamp5: lineClamp === 5, - selectable, - virtualText, - // eslint-disable-next-line sort-keys - 'hover-underline': isLink, - }) - - let children = virtualText ? null : _children - const virtTextProps = virtualText ? {[virtualKey]: _children} : undefined - - if (TEMP_MARK_V2) { - if (children) { - children = ['⚠', children] - } - if (virtTextProps) { - // eslint-disable-next-line - virtTextProps[virtualKey] = '⚠' + virtTextProps[virtualKey] - } - } - return ( - - {children} - - ) -}) diff --git a/shared/common-adapters/text2.native.tsx b/shared/common-adapters/text2.native.tsx deleted file mode 100644 index 81d5292e07c9..000000000000 --- a/shared/common-adapters/text2.native.tsx +++ /dev/null @@ -1,64 +0,0 @@ -import * as React from 'react' -import * as Styles from '@/styles' -import {fontSizeToSizeStyle, metaData} from './text.meta.native' -import type {Props} from './text2' -import {Text as RNText} from 'react-native' -import type {TextType} from './text.shared' -import {debugWarning} from '@/util/debug-warning' - -const TEMP_MARK_V2 = __DEV__ && (false as boolean) -const TEMP_SWITCH = __DEV__ && (false as boolean) - -if (TEMP_MARK_V2 || TEMP_SWITCH) { - debugWarning('Text2 flags on') -} -// this is supposed to be faster with some tradeoffs but it doesn't work on text that updates -// the value doesn't show up on the native side -// type RNTextProps = React.ComponentProps -// const RCTText = (props: RNTextProps) => { -// const {onLongPress, onPress, onPressIn, onPressOut, ...p} = props -// return React.createElement('RCTText', p) -// } - -// TODO we're passing in hardcoded metadata calls here which doesn't make sense -const styles = Styles.styleSheetCreate(() => - Object.keys(metaData(true)).reduce<{[key: string]: Styles._StylesCrossPlatform}>((map, type) => { - const meta = metaData(true)[type as TextType] - map[type] = { - ...fontSizeToSizeStyle(meta.fontSize), - color: meta.colorForBackground['positive'] || Styles.globalColors.black, - ...meta.styleOverride, - } as Styles._StylesCrossPlatform - return map - }, {}) -) - -export const Text2 = /*TEMP_SWITCH - ? require('./text').default - : */ React.memo(function Text2(p: Props) { - const {type: _type, style: _style, children: _children, lineClamp, selectable, ellipsizeMode} = p - const type = _type ?? 'BodySmall' - const canFixOverdraw = React.useContext(Styles.CanFixOverdrawContext) - const style = React.useMemo(() => { - const baseStyle: Styles.StylesCrossPlatform = styles[type] - const overdrawStyle = canFixOverdraw ? {backgroundColor: Styles.globalColors.fastBlank} : undefined - return Styles.collapseStyles([baseStyle, overdrawStyle, _style]) - }, [type, _style, canFixOverdraw]) - - const clampProps = React.useMemo(() => { - return lineClamp ? {ellipsizeMode, numberOfLines: lineClamp} : undefined - }, [ellipsizeMode, lineClamp]) - - let children = _children - if (TEMP_MARK_V2) { - if (children) { - children = ['⚠', children] - } - } - - return ( - - {children} - - ) -}) diff --git a/shared/common-adapters/usernames.tsx b/shared/common-adapters/usernames.tsx index 570f91b6abab..a9d13dee81d7 100644 --- a/shared/common-adapters/usernames.tsx +++ b/shared/common-adapters/usernames.tsx @@ -1,15 +1,9 @@ import * as C from '@/constants' import * as React from 'react' import * as Styles from '@/styles' -import Text, { - type TextType, - type Background, - type StylesTextCrossPlatform, - type AllowedColors, - type LineClampType, - type TextTypeBold, -} from './text' +import Text from './text' import {backgroundModeIsNegative} from './text.shared' +import type {TextType, Background, StylesTextCrossPlatform, AllowedColors, LineClampType, TextTypeBold} from './text.shared' import isArray from 'lodash/isArray' import type {e164ToDisplay as e164ToDisplayType} from '@/util/phone-numbers' import {useTrackerState} from '@/stores/tracker2' @@ -50,7 +44,6 @@ export type Props = { underline?: boolean usernames: ReadonlyArray | string withProfileCardPopup?: boolean - fixOverdraw?: boolean | 'auto' virtualText?: boolean // desktop only see text.desktop } & ({colorFollowing?: false; type: TextType} | {colorFollowing: boolean; type: TextTypeBold}) @@ -265,14 +258,13 @@ const Usernames = React.memo( const {backgroundMode, commaColor, inline, containerStyle, className} = p const {joinerStyle, lineClamp, notFollowingColorOverride, onUsernameClicked, prefix, selectable} = p const {showAnd, inlineGrammar, colorYou, skipSelf, style, suffix, suffixType, title} = p - const {usernames, fixOverdraw, virtualText, type} = p + const {usernames, virtualText, type} = p const colorFollowing = p.colorFollowing ?? true const colorBroken = p.colorBroken ?? true const underline = p.underline ?? true const withProfileCardPopup = p.withProfileCardPopup ?? true const you = useCurrentUserState(s => s.username) - const canFixOverdraw = React.useContext(Styles.CanFixOverdrawContext) const containerStyle2: Styles.StylesCrossPlatform = inline ? styles.inlineStyle : styles.nonInlineStyle const bgMode = backgroundMode const isNegative = backgroundModeIsNegative(bgMode) @@ -292,7 +284,6 @@ const Usernames = React.memo( className={className} type={type} negative={isNegative} - fixOverdraw={fixOverdraw === 'auto' ? canFixOverdraw : (fixOverdraw ?? false)} style={Styles.collapseStyles([containerStyle2, containerStyle])} title={title} ellipsizeMode="tail" diff --git a/shared/common-adapters/zoomable-image.desktop.tsx b/shared/common-adapters/zoomable-image.desktop.tsx index c1fe4c0a05a4..ac051a625af6 100644 --- a/shared/common-adapters/zoomable-image.desktop.tsx +++ b/shared/common-adapters/zoomable-image.desktop.tsx @@ -1,7 +1,7 @@ import * as React from 'react' import Toast from './toast.desktop' import {Box2} from './box' -import Text from './text.desktop' +import Text from './text' import type {Props} from './zoomable-image' import type {MeasureRef} from './measure-ref' diff --git a/shared/devices/paper-key.tsx b/shared/devices/paper-key.tsx index 3c00af238a8b..60e86632fdde 100644 --- a/shared/devices/paper-key.tsx +++ b/shared/devices/paper-key.tsx @@ -46,13 +46,7 @@ const PaperKey = () => { {paperkey ? ( - + {paperkey} ) : ( diff --git a/shared/fs/browser/rows/tlf-type.tsx b/shared/fs/browser/rows/tlf-type.tsx index e60d181d6f39..8699b7c179fb 100644 --- a/shared/fs/browser/rows/tlf-type.tsx +++ b/shared/fs/browser/rows/tlf-type.tsx @@ -21,7 +21,6 @@ const TLFTypeContainer = (p: OwnProps) => { writingToJournal={false} content={ { ) return ( - { selectable={props.selectable} > {fileNameWithoutExtension} - + {fileExtension ? ( - {fileExtension} - + ) : null} ) diff --git a/shared/fs/common/last-modified-line.tsx b/shared/fs/common/last-modified-line.tsx index a11c6be3625f..9ee8d46d195f 100644 --- a/shared/fs/common/last-modified-line.tsx +++ b/shared/fs/common/last-modified-line.tsx @@ -41,10 +41,10 @@ const Container = (ownProps: OwnProps) => { case 'menu': return ( - + {time} - + {by} @@ -52,7 +52,7 @@ const Container = (ownProps: OwnProps) => { case 'row': return ( - + {time} {by} @@ -61,7 +61,7 @@ const Container = (ownProps: OwnProps) => { case 'default': return ( - + {time} {by} diff --git a/shared/fs/common/tlf-info-line.tsx b/shared/fs/common/tlf-info-line.tsx index 2e5276175c86..1e212cf5124e 100644 --- a/shared/fs/common/tlf-info-line.tsx +++ b/shared/fs/common/tlf-info-line.tsx @@ -56,7 +56,6 @@ const getPrefixText = (props: Props) => props.mixedMode && props.tlfType ? ( const timeText = (props: Props) => props.tlfMtime ? ( { const prefix = getPrefixText(props) const dot = ( ( - - Git repositories - - All repositories are end-to-end encrypted.{' '} - - Read how it works. +export const HeaderTitle = () => { + const readHowUrlProps = Kb.useClickURL('https://keybase.io/blog/encrypted-git-for-everyone') + return ( + + Git repositories + + All repositories are end-to-end encrypted.{' '} + + Read how it works. + - - -) + + ) +} export const HeaderRightActions = () => { const setError = Git.useGitState(s => s.dispatch.setError) diff --git a/shared/profile/post-proof.tsx b/shared/profile/post-proof.tsx index f58be7fe5e74..7ae448928cea 100644 --- a/shared/profile/post-proof.tsx +++ b/shared/profile/post-proof.tsx @@ -156,6 +156,8 @@ const noteMap = new Map([ const WebDescription = ({platformUserName}: {platformUserName: string}) => { const root = `${platformUserName}/keybase.txt` const wellKnown = `${platformUserName}/.well-known/keybase.txt` + const rootUrlProps = Kb.useClickURL(`https://${root}`) + const wellKnownUrlProps = Kb.useClickURL(`https://${wellKnown}`) return ( @@ -165,7 +167,7 @@ const WebDescription = ({platformUserName}: {platformUserName: string}) => { {root} @@ -173,7 +175,7 @@ const WebDescription = ({platformUserName}: {platformUserName: string}) => { {wellKnown} diff --git a/shared/profile/user/friend.tsx b/shared/profile/user/friend.tsx index 462965aaa940..a48e8f8822dc 100644 --- a/shared/profile/user/friend.tsx +++ b/shared/profile/user/friend.tsx @@ -32,9 +32,9 @@ const Container = (ownProps: OwnProps) => { colorFollowing={true} lineClamp={1} /> - + {fullname} - + ) diff --git a/shared/profile/user/index.tsx b/shared/profile/user/index.tsx index 438a484745cc..5ee2290eb8be 100644 --- a/shared/profile/user/index.tsx +++ b/shared/profile/user/index.tsx @@ -117,6 +117,7 @@ const ProveIt = (p: BioTeamProofsProps) => { break } const url = 'https://keybase.io/install' + const installUrlProps = Kb.useClickURL(url) return ( <> @@ -124,7 +125,7 @@ const ProveIt = (p: BioTeamProofsProps) => { Send them this link:{' '} - + {url} diff --git a/shared/settings/account/index.tsx b/shared/settings/account/index.tsx index c45c1d9725b0..6ebfe4655c87 100644 --- a/shared/settings/account/index.tsx +++ b/shared/settings/account/index.tsx @@ -38,6 +38,7 @@ const EmailPhone = () => { const tooManyEmails = _emails.size >= 10 // If you change this, also change in keybase/config/prod/email.iced const tooManyPhones = !!_phones && _phones.size >= 10 // If you change this, also change in keybase/config/prod/phone_numbers.iced const waiting = C.Waiting.useAnyWaiting(C.waitingKeySettingsLoadSettings) + const readMoreUrlProps = Kb.useClickURL('https://keybase.io/docs/chat/phones-and-emails') const onAddEmail = () => { navigateAppend('settingsAddEmail') } @@ -54,7 +55,7 @@ const EmailPhone = () => { Secures your account by letting us send important notifications, and allows friends and teammates to find you by phone number or email.{' '} - + Read more{' '} - + Read more{' '} ) : null} - {props.text} - - {props.text && props.subText && {props.subText}} + + {props.text && props.subText && {props.subText}} {props.inProgress && } {!!props.badgeNumber && props.badgeNumber > 0 && ( diff --git a/shared/signup/username.tsx b/shared/signup/username.tsx index f3f32f1156b3..07ba084613cc 100644 --- a/shared/signup/username.tsx +++ b/shared/signup/username.tsx @@ -48,6 +48,7 @@ type Props = { const EnterUsername = (props: Props) => { const [username, onChangeUsername] = React.useState(props.initialUsername || '') const [acceptedEULA, setAcceptedEULA] = React.useState(false) + const eulaUrlProps = Kb.useClickURL('https://keybase.io/docs/acceptable-use-policy') const usernameTrimmed = username.trim() const disabled = !usernameTrimmed || usernameTrimmed === props.usernameTaken || !acceptedEULA const onContinue = () => { @@ -62,7 +63,7 @@ const EnterUsername = (props: Props) => { I accept the{' '} Keybase Acceptable Use Policy diff --git a/shared/styles/shared.tsx b/shared/styles/shared.tsx index ac9239e32290..c9b7af7aae87 100644 --- a/shared/styles/shared.tsx +++ b/shared/styles/shared.tsx @@ -1,7 +1,7 @@ import {themed as globalColors} from './colors' import {isMobile, isIOS, isAndroid, isTablet, isPhone, isElectron} from '@/constants/platform' import type {_StylesCrossPlatform, _StylesMobile, _StylesDesktop} from './css' -import type {Background} from '@/common-adapters/text' +import type {Background} from '@/common-adapters/text.shared' /* eslint-disable sort-keys */ export const globalMargins = { diff --git a/shared/teams/main/team-row.tsx b/shared/teams/main/team-row.tsx index 934eb6063aea..da75a29a2576 100644 --- a/shared/teams/main/team-row.tsx +++ b/shared/teams/main/team-row.tsx @@ -94,9 +94,9 @@ const TeamRow = React.memo(function TeamRow(props: Props) { style={styles.bodyLeftText} > - + {teamMeta.teamname} - + {teamMeta.isOpen && ( )} - + {teamMeta.memberCount.toLocaleString()} {pluralize('member', teamMeta.memberCount)} diff --git a/shared/teams/team/settings-tab/index.tsx b/shared/teams/team/settings-tab/index.tsx index ec377d75f5bd..60cdbaa712a5 100644 --- a/shared/teams/team/settings-tab/index.tsx +++ b/shared/teams/team/settings-tab/index.tsx @@ -84,6 +84,7 @@ const PublicityAnyMember = (props: { const teamsLink = 'keybase.io/popular-teams' const PublicityTeam = (props: {newPublicityTeam: boolean; setNewPublicityTeam: (s: boolean) => void}) => { + const teamsLinkUrlProps = Kb.useClickURL(`https://${teamsLink}`) return ( Publicize this team on{' '} - + {teamsLink} diff --git a/shared/whats-new/versions.tsx b/shared/whats-new/versions.tsx index f536f985f1fa..ba6d3b6a94b1 100644 --- a/shared/whats-new/versions.tsx +++ b/shared/whats-new/versions.tsx @@ -158,7 +158,7 @@ export const LastLast = ({seen, onNavigate, onNavigateExternal}: VersionProps) = Chat admins can now pin messages. {` `} - + Listen to From a30c55a9a88a1df4499b3eefe624a2a3be3a7faa Mon Sep 17 00:00:00 2001 From: chrisnojima Date: Fri, 20 Feb 2026 09:40:57 -0500 Subject: [PATCH 014/112] try automated perf harness (#28925) * try automated perf harness --- .../conversation/list-area/index.desktop.tsx | 1 + .../conversation/list-area/index.native.tsx | 1 + shared/chat/inbox/index.desktop.tsx | 2 +- shared/chat/inbox/index.native.tsx | 1 + shared/ios/Keybase.xcodeproj/project.pbxproj | 132 ++++++++++++ .../xcshareddata/xcschemes/Keybase.xcscheme | 10 + shared/ios/Keybase/AppDelegate.swift | 4 + shared/ios/Keybase/PerfFPSMonitor.swift | 103 +++++++++ shared/ios/KeybaseUITests/Info.plist | 22 ++ .../ScrollPerformanceTests.swift | 74 +++++++ shared/perf/.gitignore | 1 + shared/perf/PERF-TESTING.md | 204 ++++++++++++++++++ shared/perf/desktop-perf-inject.js | 140 ++++++++++++ shared/perf/parse-ios-results.js | 82 +++++++ shared/perf/run-desktop-cdp-profile.js | 150 +++++++++++++ shared/perf/run-ios-perf.sh | 125 +++++++++++ shared/router-v2/router.native.tsx | 9 + 17 files changed, 1060 insertions(+), 1 deletion(-) create mode 100644 shared/ios/Keybase/PerfFPSMonitor.swift create mode 100644 shared/ios/KeybaseUITests/Info.plist create mode 100644 shared/ios/KeybaseUITests/ScrollPerformanceTests.swift create mode 100644 shared/perf/.gitignore create mode 100644 shared/perf/PERF-TESTING.md create mode 100644 shared/perf/desktop-perf-inject.js create mode 100755 shared/perf/parse-ios-results.js create mode 100755 shared/perf/run-desktop-cdp-profile.js create mode 100755 shared/perf/run-ios-perf.sh diff --git a/shared/chat/conversation/list-area/index.desktop.tsx b/shared/chat/conversation/list-area/index.desktop.tsx index a7558654025f..ffe7fbb53330 100644 --- a/shared/chat/conversation/list-area/index.desktop.tsx +++ b/shared/chat/conversation/list-area/index.desktop.tsx @@ -587,6 +587,7 @@ const ThreadWrapper = React.memo(function ThreadWrapper() { onCopyCapture={onCopyCapture} >
-
+
{rows.length ? ( ) : ( + + + + 0 { + samples.append(frameCount) + } + writeResults() + NSLog("PerfFPSMonitor: stopped, wrote %d samples to %@", samples.count, PerfFPSMonitor.outputPath) + } + + @objc private func tick(_ link: CADisplayLink) { + if lastTimestamp == 0 { + lastTimestamp = link.timestamp + frameCount = 1 + return + } + + frameCount += 1 + + let elapsed = link.timestamp - lastTimestamp + if elapsed >= 1.0 { + samples.append(frameCount) + frameCount = 0 + lastTimestamp = link.timestamp + } + } + + private func writeResults() { + guard !samples.isEmpty else { return } + let sorted = samples.sorted() + let sum = samples.reduce(0, +) + let avg = Double(sum) / Double(samples.count) + let min = sorted.first ?? 0 + let max = sorted.last ?? 0 + let p5Idx = Swift.max(0, Int(ceil(Double(sorted.count) * 0.05)) - 1) + let p5 = sorted[p5Idx] + + let result: [String: Any] = [ + "fps": [ + "avg": round(avg * 10) / 10, + "min": min, + "max": max, + "p5": p5, + "samples": samples + ], + "durationSeconds": samples.count + ] + + if let data = try? JSONSerialization.data(withJSONObject: result, options: .prettyPrinted) { + try? data.write(to: URL(fileURLWithPath: PerfFPSMonitor.outputPath)) + } + } + + /// Write results on app background so data is available after test + @objc static func appDidEnterBackground() { + if shared.running { + shared.stop() + } + } +} diff --git a/shared/ios/KeybaseUITests/Info.plist b/shared/ios/KeybaseUITests/Info.plist new file mode 100644 index 000000000000..64d65ca49577 --- /dev/null +++ b/shared/ios/KeybaseUITests/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/shared/ios/KeybaseUITests/ScrollPerformanceTests.swift b/shared/ios/KeybaseUITests/ScrollPerformanceTests.swift new file mode 100644 index 000000000000..d86f8e098802 --- /dev/null +++ b/shared/ios/KeybaseUITests/ScrollPerformanceTests.swift @@ -0,0 +1,74 @@ +import XCTest + +final class ScrollPerformanceTests: XCTestCase { + let app = XCUIApplication() + + override func setUp() { + continueAfterFailure = false + app.launchArguments.append("-PERF_FPS_MONITOR") + app.launch() + + let chatTab = app.buttons["Chat"] + if chatTab.waitForExistence(timeout: 60) { + chatTab.tap() + } + + // If we're inside a conversation, go back to inbox + let inbox = app.otherElements["inboxList"].firstMatch + if !inbox.waitForExistence(timeout: 10) { + let backButton = app.buttons["Back"] + if backButton.exists { + backButton.tap() + } + } + } + + override func tearDown() { + // Background the app to trigger PerfFPSMonitor to write results via NSLog + XCUIDevice.shared.press(.home) + sleep(2) + } + + private func openFirstConversation() { + let inbox = app.otherElements["inboxList"].firstMatch + XCTAssertTrue(inbox.waitForExistence(timeout: 15)) + + let firstRow = inbox.coordinate(withNormalizedOffset: CGVector(dx: 0.5, dy: 0.15)) + firstRow.tap() + + let messageList = app.otherElements["messageList"].firstMatch + XCTAssertTrue(messageList.waitForExistence(timeout: 15)) + } + + func testInboxScrollPerformance() throws { + let inbox = app.otherElements["inboxList"].firstMatch + XCTAssertTrue(inbox.waitForExistence(timeout: 15)) + + let start = CFAbsoluteTimeGetCurrent() + for _ in 0..<5 { + inbox.swipeUp(velocity: .fast) + } + for _ in 0..<5 { + inbox.swipeDown(velocity: .fast) + } + let elapsed = CFAbsoluteTimeGetCurrent() - start + NSLog("PERF_RESULT: testInboxScrollPerformance %.3f s", elapsed) + } + + func testMessageListScrollPerformance() throws { + openFirstConversation() + + let messageList = app.otherElements["messageList"].firstMatch + + // Message list is inverted — swipe down to scroll into history, up to come back + let start = CFAbsoluteTimeGetCurrent() + for _ in 0..<5 { + messageList.swipeDown(velocity: .fast) + } + for _ in 0..<5 { + messageList.swipeUp(velocity: .fast) + } + let elapsed = CFAbsoluteTimeGetCurrent() - start + NSLog("PERF_RESULT: testMessageListScrollPerformance %.3f s", elapsed) + } +} diff --git a/shared/perf/.gitignore b/shared/perf/.gitignore new file mode 100644 index 000000000000..ea1472ec1f38 --- /dev/null +++ b/shared/perf/.gitignore @@ -0,0 +1 @@ +output/ diff --git a/shared/perf/PERF-TESTING.md b/shared/perf/PERF-TESTING.md new file mode 100644 index 000000000000..9fe2c69525f2 --- /dev/null +++ b/shared/perf/PERF-TESTING.md @@ -0,0 +1,204 @@ +# Performance Testing + +Tools and workflows for measuring scroll performance in the Keybase app, covering both the desktop Electron app (via Playwright MCP + CDP) and the iOS simulator (via XCUITest + CADisplayLink FPS monitor). + +## Desktop Performance Profiling + +### Prerequisites + +- App running with remote debugging: `KB_ENABLE_REMOTE_DEBUG=1 yarn start-hot` +- Playwright MCP configured to connect via CDP on port 9222 + +### Quick Workflow (Playwright MCP + Claude) + +1. Start the app: `KB_ENABLE_REMOTE_DEBUG=1 yarn start-hot` +2. In Claude, close the DevTools tab (`browser_tabs` action=close index=0) and select the app tab +3. Navigate to the target page via snapshot + click +4. Read and inject the perf measurement script: + ``` + browser_evaluate: (paste contents of desktop-perf-inject.js) + ``` +5. Start measurement: + ``` + browser_evaluate: window.__perf.start() + ``` +6. Scroll using `browser_press_key` (PageDown x20, PageUp x20) or call: + ``` + browser_evaluate: window.__perf.scrollContainer('[data-testid="message-list"]', {distance: 3000}) + ``` +7. Stop and get results: + ``` + browser_evaluate: JSON.stringify(window.__perf.stop()) + ``` + +### Desktop Test IDs + +| Selector | Component | +|----------|-----------| +| `[data-testid="message-list"]` | Chat message list scroll container | +| `[data-testid="inbox-list"]` | Inbox/conversation list (note: testid is on wrapper div; the scrollable element is its first child) | + +### Desktop Metrics Returned + +```js +{ + durationMs: number, // Total measurement time + fps: { + avg: number, // Average frames per second + min: number, // Lowest 1-second FPS sample + max: number, // Highest 1-second FPS sample + p5: number, // 5th percentile (worst-case) + samples: number[] // Per-second FPS values + }, + longTasks: { + count: number, // Tasks >50ms + totalMs: number, // Sum of all long task durations + entries: Array<{duration, startTime}> + }, + memory: { + startHeapMB: number, + endHeapMB: number, + peakHeapMB: number + }, + marks: Array<{name, startTime}> // Any performance.mark() calls +} +``` + +### CPU Profiling (CDP) + +For deeper analysis, capture a CPU profile: + +```bash +node shared/perf/run-desktop-cdp-profile.js --duration 5000 +``` + +This saves a `.cpuprofile` file to `shared/perf/output/` that can be loaded in Chrome DevTools (Performance tab > Load profile). + +Options: +- `--duration ` — Recording duration (default: 5000) +- `--output ` — Output file path + +## iOS Performance Profiling + +### Prerequisites + +- Xcode with the Keybase workspace +- iOS Simulator booted (use a device compatible with your Xcode SDK, e.g. iPhone 17 Pro for Xcode 26) +- Metro dev server running (`yarn react-native start`) — the first bundle takes ~30s; subsequent runs use cache +- The app must be logged in on the simulator (provision manually before first test run) + +### How It Works + +The iOS perf tests use two complementary systems: + +1. **XCUITest (`ScrollPerformanceTests.swift`)** — Automates the UI: launches the app, navigates to chat, scrolls lists up and down, and times the operations with `CFAbsoluteTimeGetCurrent()`. + +2. **PerfFPSMonitor (`PerfFPSMonitor.swift`)** — A native `CADisplayLink`-based FPS monitor that runs inside the app process. Activated by the `-PERF_FPS_MONITOR` launch argument (passed automatically by the tests). Counts frames per second during the entire test session and writes results to the app's tmp directory as JSON when the app backgrounds. + +The flow: +- XCUITest launches the app with `-PERF_FPS_MONITOR` +- `AppDelegate` calls `PerfFPSMonitor.startIfEnabled()` on launch +- The CADisplayLink fires on `.common` run loop mode (works during scroll tracking) +- Per-second frame counts are accumulated as samples +- In `tearDown`, the test presses Home → `applicationDidEnterBackground` triggers `PerfFPSMonitor.stop()` → JSON is written +- The shell script extracts the JSON from the simulator's app container via `xcrun simctl get_app_container` + +### iOS Test IDs + +| testID | Component | +|--------|-----------| +| `messageList` | Chat message list (FlatList) | +| `inboxList` | Inbox conversation list (FlatList) | + +These are set via `testID` prop on the React Native FlatList components, which maps to `accessibilityIdentifier` for XCUITest discovery. + +### Running Tests + +```bash +# Run all scroll performance tests +./shared/perf/run-ios-perf.sh --simulator "iPhone 17 Pro" + +# Run a specific test +./shared/perf/run-ios-perf.sh --simulator "iPhone 17 Pro" --test testInboxScrollPerformance + +# Run with xctrace recording +./shared/perf/run-ios-perf.sh --simulator "iPhone 17 Pro" --trace +``` + +### Available Tests + +| Test | What it measures | +|------|-----------------| +| `testInboxScrollPerformance` | 5 fast swipes up + 5 fast swipes down on inbox list | +| `testMessageListScrollPerformance` | Opens first conversation, 5 fast swipes down (into history, list is inverted) + 5 back up | + +### iOS FPS Metrics + +The FPS JSON written by `PerfFPSMonitor` and extracted by the shell script: + +```json +{ + "durationSeconds": 43, + "fps": { + "avg": 51.7, + "min": 6, + "max": 60, + "p5": 32, + "samples": [46, 12, 59, 60, ...] + } +} +``` + +- **avg** — Average FPS across all 1-second samples (includes app launch/navigation, not just scrolling) +- **min** — Lowest single-second sample (often during transitions, not scroll jank) +- **max** — Highest single-second sample (60 = vsync ceiling on standard displays) +- **p5** — 5th percentile: the FPS at the worst 5% of seconds +- **samples** — Raw per-second frame counts for the entire test duration + +### Output Files + +All output goes to `shared/perf/output/` (gitignored): + +| File | Content | +|------|---------| +| `ios-result.xcresult` | Xcode result bundle | +| `ios-summary.json` | Test pass/fail summary from xcresulttool | +| `ios-metrics.json` | Test metrics from xcresulttool | +| `ios-fps.json` | FPS data from PerfFPSMonitor (extracted from app container) | +| `ios-test.log` | Full xcodebuild console output | +| `ios-trace.trace` | xctrace recording (if `--trace` used) | + +### Parsing Results + +```bash +node shared/perf/parse-ios-results.js +``` + +### Gotchas + +- **First run after Metro restart**: The initial JS bundle takes ~30-60s. The test setUp has a 60-second timeout for the Chat tab to appear, but if bundling is very slow it can still time out. Run once manually to warm the cache. +- **App state persistence**: The app saves navigation state between launches. The test setUp handles this by checking for the inbox and pressing Back if inside a conversation. +- **Inverted message list**: The chat message list is inverted (newest at bottom). Swipe **down** scrolls into history, swipe **up** returns to recent messages. +- **Tab bar accessibility**: On phones (not tablets), tab bar items show only icons. The `tabBarAccessibilityLabel` prop in `router.native.tsx` provides the "Chat" label for XCUITest discovery. +- **FlatList rows aren't UITableViewCells**: React Native FlatList items aren't native cells, so `app.cells` won't find them. Use coordinate-based taps instead (e.g. `inbox.coordinate(withNormalizedOffset:)`). +- **NSLog from test methods**: `NSLog` calls in the XCUITest Swift code don't appear in xcodebuild stdout — they go to the xcresult activity log. Use file-based extraction (like PerfFPSMonitor) for reliable data retrieval. + +## Interpreting Results + +### Before/After Comparison + +When measuring the impact of a change: + +1. Run the test on the base branch, note metrics +2. Apply your change +3. Run the same test again +4. Compare: + - **FPS**: Higher is better. p5 (5th percentile) is the most useful — it captures worst-case jank + - **Long tasks** (desktop): Fewer and shorter is better. Any task >100ms causes visible jank + - **Memory** (desktop): Check for leaks (endHeapMB >> startHeapMB after repeated scroll cycles) + +### What's "Good"? + +- **Desktop FPS**: 55+ avg is good, <30 p5 indicates jank +- **Desktop long tasks**: 0 is ideal; >5 during a scroll is concerning +- **iOS FPS**: 50+ avg is good for simulator (real devices perform differently). p5 > 30 means scrolling is smooth. Dips during app launch and navigation transitions are normal and expected. diff --git a/shared/perf/desktop-perf-inject.js b/shared/perf/desktop-perf-inject.js new file mode 100644 index 000000000000..44066591a5a0 --- /dev/null +++ b/shared/perf/desktop-perf-inject.js @@ -0,0 +1,140 @@ +// Desktop performance measurement script — inject via browser_evaluate in Electron. +// Creates window.__perf with start(), scrollContainer(), and stop() methods. +;(function () { + if (window.__perf) return + + let running = false + let startTime = 0 + let fpsSamples = [] + let longTaskEntries = [] + let memoryStart = null + let memoryPeak = 0 + let marks = [] + let frameCount = 0 + let lastSecond = 0 + let rafId = null + let longTaskObserver = null + + function sampleMemory() { + // Chromium-only API, available in Electron + if (performance.memory) { + return performance.memory.usedJSHeapSize / (1024 * 1024) + } + return 0 + } + + function countFrames(timestamp) { + if (!running) return + frameCount++ + const second = Math.floor(timestamp / 1000) + if (lastSecond && second !== lastSecond) { + fpsSamples.push(frameCount) + const mem = sampleMemory() + if (mem > memoryPeak) memoryPeak = mem + frameCount = 0 + } + lastSecond = second + rafId = requestAnimationFrame(countFrames) + } + + function percentile(arr, p) { + if (!arr.length) return 0 + const sorted = [...arr].sort((a, b) => a - b) + const idx = Math.max(0, Math.ceil(sorted.length * (p / 100)) - 1) + return sorted[idx] + } + + window.__perf = { + scrollContainer(selector, options = {}) { + const {distance = 2000, direction = 'down', stepMs = 16, stepPx = 50} = options + return new Promise((resolve, reject) => { + const el = document.querySelector(selector) + if (!el) { + reject(new Error('Element not found: ' + selector)) + return + } + let scrolled = 0 + const dir = direction === 'up' ? -1 : 1 + const interval = setInterval(() => { + el.scrollTop += dir * stepPx + scrolled += stepPx + if (scrolled >= distance) { + clearInterval(interval) + resolve() + } + }, stepMs) + }) + }, + start() { + running = true + startTime = performance.now() + fpsSamples = [] + longTaskEntries = [] + marks = [] + frameCount = 0 + lastSecond = 0 + memoryStart = sampleMemory() + memoryPeak = memoryStart + + // Long task observer + if (typeof PerformanceObserver !== 'undefined') { + try { + longTaskObserver = new PerformanceObserver(list => { + for (const entry of list.getEntries()) { + longTaskEntries.push({duration: entry.duration, startTime: entry.startTime}) + } + }) + longTaskObserver.observe({entryTypes: ['longtask']}) + } catch (_e) { + // longtask not supported + } + } + + rafId = requestAnimationFrame(countFrames) + }, + + stop() { + running = false + if (rafId) cancelAnimationFrame(rafId) + if (longTaskObserver) longTaskObserver.disconnect() + + const durationMs = performance.now() - startTime + // Push the final partial-second frame count + if (frameCount > 0) { + fpsSamples.push(frameCount) + } + + const memoryEnd = sampleMemory() + const samples = fpsSamples.length ? fpsSamples : [0] + const avg = samples.reduce((a, b) => a + b, 0) / samples.length + const min = Math.min(...samples) + const max = Math.max(...samples) + const p5 = percentile(samples, 5) + + const totalLongTaskMs = longTaskEntries.reduce((sum, e) => sum + e.duration, 0) + + // Collect any custom marks + try { + marks = performance.getEntriesByType('mark').map(m => ({name: m.name, startTime: m.startTime})) + } catch (_e) { + // ignore + } + + return { + durationMs: Math.round(durationMs), + fps: {avg: Math.round(avg * 10) / 10, max, min, p5, samples}, + longTasks: { + count: longTaskEntries.length, + entries: longTaskEntries, + totalMs: Math.round(totalLongTaskMs), + }, + marks, + memory: { + endHeapMB: Math.round(memoryEnd * 10) / 10, + peakHeapMB: Math.round(memoryPeak * 10) / 10, + startHeapMB: Math.round(memoryStart * 10) / 10, + }, + } + }, + } +})() diff --git a/shared/perf/parse-ios-results.js b/shared/perf/parse-ios-results.js new file mode 100755 index 000000000000..356056594313 --- /dev/null +++ b/shared/perf/parse-ios-results.js @@ -0,0 +1,82 @@ +#!/usr/bin/env node +// Parse XCUITest xcresult performance metrics and print a readable summary. +// +// Usage: node parse-ios-results.js [path-to-ios-metrics.json] + +const fs = require('fs') +const path = require('path') + +const outputDir = path.join(__dirname, 'output') +const metricsPath = process.argv[2] || path.join(outputDir, 'ios-metrics.json') +const summaryPath = path.join(outputDir, 'ios-summary.json') + +// Try to load summary +let summary +if (fs.existsSync(summaryPath)) { + try { + summary = JSON.parse(fs.readFileSync(summaryPath, 'utf8')) + } catch (_) {} +} + +// Load metrics +if (!fs.existsSync(metricsPath)) { + console.error(`File not found: ${metricsPath}`) + console.error('Run the iOS perf tests first: ./run-ios-perf.sh') + process.exit(1) +} + +let metrics +try { + metrics = JSON.parse(fs.readFileSync(metricsPath, 'utf8')) +} catch (e) { + console.error('Failed to parse JSON:', e.message) + process.exit(1) +} + +console.log('=== iOS Performance Test Results ===\n') + +// Print summary info +if (summary) { + console.log(`Result: ${summary.result}`) + console.log(`Tests: ${summary.passedTests} passed, ${summary.failedTests} failed, ${summary.totalTestCount} total`) + if (summary.devicesAndConfigurations?.[0]?.device) { + const d = summary.devicesAndConfigurations[0].device + console.log(`Device: ${d.deviceName} (${d.platform} ${d.osVersion})`) + } + console.log('') +} + +// Print performance metrics +if (!Array.isArray(metrics) || metrics.length === 0) { + console.log('No performance metrics found.') + process.exit(0) +} + +for (const test of metrics) { + console.log(`Test: ${test.testIdentifier}`) + + for (const run of test.testRuns || []) { + if (run.device) { + console.log(` Device: ${run.device.deviceName}`) + } + + for (const m of run.metrics || []) { + const values = m.measurements || [] + console.log(` ${m.displayName} (${m.unitOfMeasurement}):`) + + if (values.length) { + const avg = values.reduce((a, b) => a + b, 0) / values.length + const min = Math.min(...values) + const max = Math.max(...values) + const stddev = Math.sqrt(values.reduce((sum, v) => sum + (v - avg) ** 2, 0) / values.length) + const relStddev = (stddev / avg * 100).toFixed(1) + + console.log(` avg: ${avg.toFixed(3)}, min: ${min.toFixed(3)}, max: ${max.toFixed(3)}`) + console.log(` stddev: ${stddev.toFixed(3)} (${relStddev}%)`) + console.log(` samples: [${values.map(n => n.toFixed(3)).join(', ')}]`) + console.log(` polarity: ${m.polarity}`) + } + console.log('') + } + } +} diff --git a/shared/perf/run-desktop-cdp-profile.js b/shared/perf/run-desktop-cdp-profile.js new file mode 100755 index 000000000000..3c10e9a07a51 --- /dev/null +++ b/shared/perf/run-desktop-cdp-profile.js @@ -0,0 +1,150 @@ +#!/usr/bin/env node +// CDP CPU profiler for the Keybase Electron app. +// Connects to the Chrome DevTools Protocol on localhost:9222, +// records a CPU profile, and saves a .cpuprofile file. +// +// Usage: node run-desktop-cdp-profile.js [--duration 5000] [--output path] +// +// No external dependencies — uses built-in http and ws (from node_modules). + +const http = require('http') +const path = require('path') +const fs = require('fs') + +const args = process.argv.slice(2) +function getArg(name, fallback) { + const idx = args.indexOf(name) + if (idx !== -1 && args[idx + 1]) return args[idx + 1] + return fallback +} + +const duration = parseInt(getArg('--duration', '5000'), 10) +const outputDir = path.resolve(__dirname, 'output') + +function getTimestamp() { + return new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19) +} + +const outputPath = getArg('--output', path.join(outputDir, `cpu-profile-${getTimestamp()}.cpuprofile`)) + +function fetchJSON(url) { + return new Promise((resolve, reject) => { + http + .get(url, res => { + let data = '' + res.on('data', chunk => (data += chunk)) + res.on('end', () => { + try { + resolve(JSON.parse(data)) + } catch (e) { + reject(e) + } + }) + }) + .on('error', reject) + }) +} + +async function main() { + // 1. Discover debugger targets + console.log('Connecting to CDP on localhost:9222...') + let targets + try { + targets = await fetchJSON('http://localhost:9222/json') + } catch (e) { + console.error('Failed to connect. Is the app running with KB_ENABLE_REMOTE_DEBUG=1?') + console.error(e.message) + process.exit(1) + } + + // Find the main page (not DevTools, not service worker) + const page = targets.find(t => t.type === 'page' && !t.url.includes('devtools://')) + if (!page) { + console.error( + 'No suitable page target found. Targets:', + targets.map(t => ({title: t.title, type: t.type})) + ) + process.exit(1) + } + + console.log(`Target: "${page.title}" (${page.url})`) + const wsUrl = page.webSocketDebuggerUrl + if (!wsUrl) { + console.error('No webSocketDebuggerUrl available for the target.') + process.exit(1) + } + + // 2. Connect via WebSocket + const WebSocket = require('ws') + const ws = new WebSocket(wsUrl) + + let msgId = 0 + const pending = new Map() + + function send(method, params = {}) { + return new Promise((resolve, reject) => { + const id = ++msgId + pending.set(id, {reject, resolve}) + ws.send(JSON.stringify({id, method, params})) + }) + } + + ws.on('message', raw => { + const msg = JSON.parse(raw) + if (msg.id && pending.has(msg.id)) { + const {resolve, reject} = pending.get(msg.id) + pending.delete(msg.id) + if (msg.error) { + reject(new Error(msg.error.message)) + } else { + resolve(msg.result) + } + } + }) + + await new Promise((resolve, reject) => { + ws.on('open', resolve) + ws.on('error', reject) + }) + + console.log('Connected to CDP WebSocket.') + + try { + // 3. Enable profiler and start + await send('Profiler.enable') + await send('Profiler.setSamplingInterval', {interval: 100}) + await send('Profiler.start') + console.log(`Profiling for ${duration}ms...`) + + await new Promise(resolve => setTimeout(resvolve, duration)) + + // 4. Stop and save + const result = await send('Profiler.stop') + const profile = result.profile + + // Ensure output directory exists + fs.mkdirSync(path.dirname(outputPath), {recursive: true}) + fs.writeFileSync(outputPath, JSON.stringify(profile, null, 2)) + console.log(`CPU profile saved to: ${outputPath}`) + console.log('Load this file in Chrome DevTools → Performance → Load profile') + + // 5. Optional: heap usage summary + try { + const heap = await send('Runtime.getHeapUsage') + console.log( + `Heap: used=${(heap.usedSize / 1024 / 1024).toFixed(1)}MB, total=${(heap.totalSize / 1024 / 1024).toFixed(1)}MB` + ) + } catch (_e) { + // Runtime.getHeapUsage may not be available + } + + await send('Profiler.disable') + } finally { + ws.close() + } +} + +main().catch(e => { + console.error(e) + process.exit(1) +}) diff --git a/shared/perf/run-ios-perf.sh b/shared/perf/run-ios-perf.sh new file mode 100755 index 000000000000..c2be25cb6790 --- /dev/null +++ b/shared/perf/run-ios-perf.sh @@ -0,0 +1,125 @@ +#!/bin/bash +# iOS performance test runner using XCUITest. +# +# Usage: ./run-ios-perf.sh [--trace] [--test testName] [--simulator "name"] +# +# Options: +# --trace Also record an xctrace alongside XCUITest +# --test NAME Run specific test method (default: all in ScrollPerformanceTests) +# --simulator Simulator name (default: "iPhone 16") + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +IOS_DIR="$(cd "$SCRIPT_DIR/../ios" && pwd)" +OUTPUT_DIR="$SCRIPT_DIR/output" +mkdir -p "$OUTPUT_DIR" + +TRACE=false +TEST_NAME="" +SIMULATOR="iPhone 16" + +while [[ $# -gt 0 ]]; do + case $1 in + --trace) TRACE=true; shift ;; + --test) TEST_NAME="$2"; shift 2 ;; + --simulator) SIMULATOR="$2"; shift 2 ;; + *) echo "Unknown option: $1"; exit 1 ;; + esac +done + +ONLY_TESTING="" +if [ -n "$TEST_NAME" ]; then + ONLY_TESTING="-only-testing:KeybaseUITests/ScrollPerformanceTests/$TEST_NAME" +fi + +RESULT_BUNDLE="$OUTPUT_DIR/ios-result.xcresult" +LOG_FILE="$OUTPUT_DIR/ios-test.log" + +# Clean previous result bundle +rm -rf "$RESULT_BUNDLE" + +TRACE_PID="" +TRACE_FILE="$OUTPUT_DIR/ios-trace.trace" + +if [ "$TRACE" = true ]; then + echo "Starting xctrace recording..." + rm -rf "$TRACE_FILE" + xctrace record \ + --device-name "$SIMULATOR" \ + --template 'Time Profiler' \ + --output "$TRACE_FILE" \ + --time-limit 120s & + TRACE_PID=$! + sleep 2 +fi + +echo "Building and running XCUITests..." +echo "Simulator: $SIMULATOR" + +xcodebuild test \ + -workspace "$IOS_DIR/Keybase.xcworkspace" \ + -scheme Keybase \ + -destination "platform=iOS Simulator,name=$SIMULATOR" \ + $ONLY_TESTING \ + -resultBundlePath "$RESULT_BUNDLE" \ + 2>&1 | tee "$LOG_FILE" || true + +if [ -n "$TRACE_PID" ] && kill -0 "$TRACE_PID" 2>/dev/null; then + echo "Stopping xctrace..." + kill "$TRACE_PID" 2>/dev/null || true + wait "$TRACE_PID" 2>/dev/null || true +fi + +# Extract results from xcresult bundle +if [ -d "$RESULT_BUNDLE" ]; then + echo "" + echo "=== Extracting results ===" + xcrun xcresulttool get test-results summary \ + --path "$RESULT_BUNDLE" \ + > "$OUTPUT_DIR/ios-summary.json" 2>/dev/null || true + + xcrun xcresulttool get test-results metrics \ + --path "$RESULT_BUNDLE" \ + > "$OUTPUT_DIR/ios-metrics.json" 2>/dev/null || true + + echo "Results saved to:" + echo " xcresult: $RESULT_BUNDLE" + echo " Summary: $OUTPUT_DIR/ios-summary.json" + echo " Metrics: $OUTPUT_DIR/ios-metrics.json" + echo " Log: $LOG_FILE" + + if [ "$TRACE" = true ] && [ -d "$TRACE_FILE" ]; then + echo " Trace: $TRACE_FILE" + fi + + # Extract PERF_RESULT lines from the test log + echo "" + echo "=== Performance Results ===" + grep "PERF_RESULT:" "$LOG_FILE" 2>/dev/null || echo "(no PERF_RESULT lines found)" + + # Extract FPS data from the simulator's app container + echo "" + echo "=== FPS Data ===" + APP_CONTAINER=$(xcrun simctl get_app_container booted keybase.ios data 2>/dev/null || true) + if [ -n "$APP_CONTAINER" ]; then + FPS_FILE="$APP_CONTAINER/tmp/perf-fps.json" + if [ -f "$FPS_FILE" ]; then + cp "$FPS_FILE" "$OUTPUT_DIR/ios-fps.json" + echo "FPS data saved to: $OUTPUT_DIR/ios-fps.json" + cat "$OUTPUT_DIR/ios-fps.json" + else + echo "(no FPS data file found at $FPS_FILE)" + # Try to find it via NSLog in the test output + grep "PerfFPSMonitor:" "$LOG_FILE" 2>/dev/null || echo "(no PerfFPSMonitor log lines found)" + fi + else + echo "(could not find app container — is the simulator running?)" + fi + + echo "" + echo "Parse results with: node $SCRIPT_DIR/parse-ios-results.js" +else + echo "" + echo "No xcresult bundle produced. Check $LOG_FILE for errors." +fi diff --git a/shared/router-v2/router.native.tsx b/shared/router-v2/router.native.tsx index 53099d8043d7..3b53027f6d02 100644 --- a/shared/router-v2/router.native.tsx +++ b/shared/router-v2/router.native.tsx @@ -26,6 +26,14 @@ if (module.hot) { module.hot.accept('', () => {}) } +const tabToLabel = new Map([ + [Tabs.chatTab, 'Chat'], + [Tabs.fsTab, 'Files'], + [Tabs.teamsTab, 'Teams'], + [Tabs.peopleTab, 'People'], + [Tabs.settingsTab, 'More'], +]) + // just to get badge rollups const tabs = C.isTablet ? Tabs.tabletTabs : Tabs.phoneTabs @@ -81,6 +89,7 @@ const appTabsScreenOptions = ({route}: {route: {name: string}}) => { return { ...Common.defaultNavigationOptions, headerShown: false, + tabBarAccessibilityLabel: tabToLabel.get(route.name as Tabs.Tab) ?? route.name, tabBarActiveBackgroundColor: Kb.Styles.globalColors.transparent, tabBarButton: (p: BottomTabBarButtonProps) => ( From 9782925b86c7461140d078fcd597c2337e117c75 Mon Sep 17 00:00:00 2001 From: chrisnojima Date: Fri, 20 Feb 2026 09:42:55 -0500 Subject: [PATCH 015/112] fix avatar layout (#28926) --- shared/common-adapters/avatar/index.native.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shared/common-adapters/avatar/index.native.tsx b/shared/common-adapters/avatar/index.native.tsx index db137e0f0788..f959e17bda17 100644 --- a/shared/common-adapters/avatar/index.native.tsx +++ b/shared/common-adapters/avatar/index.native.tsx @@ -34,7 +34,7 @@ const Avatar = React.memo(function Avatar(p: Props) { return ( - + {!props.skipBackground && ( Date: Fri, 20 Feb 2026 10:36:31 -0500 Subject: [PATCH 016/112] name cleanup (#28927) * rename numbered things back to root --- shared/chat/audio/amptracker.tsx | 2 +- shared/chat/audio/audio-recorder.native.tsx | 2 +- shared/chat/blocking/block-modal.tsx | 4 +- shared/chat/blocking/invitation-to-block.tsx | 2 +- shared/chat/chat-button.tsx | 2 +- .../attachment-fullscreen/hooks.tsx | 2 +- .../conversation/attachment-get-titles.tsx | 2 +- .../chat/conversation/bot/channel-picker.tsx | 4 +- shared/chat/conversation/bot/confirm.tsx | 2 +- shared/chat/conversation/bot/install.tsx | 2 +- shared/chat/conversation/bot/search.tsx | 2 +- shared/chat/conversation/bot/team-picker.tsx | 4 +- shared/chat/conversation/bottom-banner.tsx | 2 +- shared/chat/conversation/command-markdown.tsx | 2 +- shared/chat/conversation/command-status.tsx | 2 +- shared/chat/conversation/container.tsx | 2 +- shared/chat/conversation/error.tsx | 2 +- shared/chat/conversation/fwd-msg.tsx | 6 +- shared/chat/conversation/giphy/hooks.tsx | 2 +- .../chat/conversation/header-area/index.d.ts | 2 +- .../conversation/header-area/index.native.tsx | 4 +- .../conversation/info-panel/add-people.tsx | 2 +- .../info-panel/add-to-channel.tsx | 8 +-- .../conversation/info-panel/attachments.tsx | 4 +- shared/chat/conversation/info-panel/bot.tsx | 4 +- .../chat/conversation/info-panel/common.tsx | 2 +- .../chat/conversation/info-panel/header.tsx | 2 +- shared/chat/conversation/info-panel/index.tsx | 2 +- .../chat/conversation/info-panel/members.tsx | 2 +- shared/chat/conversation/info-panel/menu.tsx | 2 +- .../conversation/info-panel/participant.tsx | 2 +- .../info-panel/settings/index.tsx | 2 +- .../info-panel/settings/min-writer-role.tsx | 2 +- .../info-panel/settings/notifications.tsx | 2 +- .../conversation/input-area/container.tsx | 4 +- .../input-area/location-popup.native.tsx | 2 +- .../input-area/{normal2 => normal}/index.tsx | 10 +-- .../max-input-area-context.tsx | 0 .../moremenu-popup.native.tsx | 2 +- .../{normal2 => normal}/platform-input.d.ts | 4 +- .../platform-input.desktop.tsx | 20 +++--- .../platform-input.native.tsx | 22 +++--- .../set-explode-popup/hooks.tsx | 2 +- .../set-explode-popup/index.d.ts | 0 .../set-explode-popup/index.desktop.tsx | 0 .../set-explode-popup/index.native.tsx | 0 .../input-area/{normal2 => normal}/typing.tsx | 2 +- .../chat/conversation/input-area/preview.tsx | 2 +- .../input-area/suggestors/channels.tsx | 2 +- .../input-area/suggestors/commands.tsx | 8 +-- .../input-area/suggestors/emoji.tsx | 2 +- .../input-area/suggestors/index.tsx | 12 ++-- .../suggestors/suggestion-list.desktop.tsx | 2 +- .../input-area/suggestors/users.tsx | 2 +- shared/chat/conversation/list-area/hooks.tsx | 2 +- .../conversation/list-area/index.desktop.tsx | 4 +- .../conversation/list-area/index.native.tsx | 8 +-- shared/chat/conversation/load-status.tsx | 2 +- .../messages/account-payment/container.tsx | 2 +- .../messages/account-payment/wrapper.tsx | 2 +- .../messages/attachment/audio.tsx | 2 +- .../conversation/messages/attachment/file.tsx | 2 +- .../messages/attachment/image/imageimpl.d.ts | 3 + .../{image2 => image}/imageimpl.desktop.tsx | 4 +- .../{image2 => image}/imageimpl.native.tsx | 6 +- .../attachment/{image2 => image}/index.tsx | 4 +- .../{image2 => image}/use-state.tsx | 2 +- .../messages/attachment/image2/imageimpl.d.ts | 3 - .../messages/attachment/shared.tsx | 2 +- .../messages/attachment/video/use-state.tsx | 2 +- .../attachment/video/videoimpl.desktop.tsx | 2 +- .../attachment/video/videoimpl.native.tsx | 2 +- .../messages/attachment/wrapper.tsx | 4 +- .../conversation/messages/cards/make-team.tsx | 2 +- .../messages/cards/team-journey/container.tsx | 2 +- .../chat/conversation/messages/emoji-row.tsx | 2 +- .../messages/message-popup/attachment.tsx | 2 +- .../messages/message-popup/hooks.tsx | 2 +- .../messages/message-popup/index.tsx | 2 +- .../messages/message-popup/journeycard.tsx | 2 +- .../messages/message-popup/reactionitem.tsx | 2 +- .../messages/message-popup/text.tsx | 2 +- .../chat/conversation/messages/pin/index.tsx | 2 +- .../conversation/messages/pin/wrapper.tsx | 2 +- .../messages/placeholder/wrapper.tsx | 2 +- .../conversation/messages/react-button.tsx | 2 +- .../messages/reaction-tooltip.tsx | 2 +- .../conversation/messages/reactions-rows.tsx | 2 +- .../chat/conversation/messages/reset-user.tsx | 2 +- .../messages/retention-notice.tsx | 2 +- .../chat/conversation/messages/separator.tsx | 6 +- .../messages/set-channelname/wrapper.tsx | 2 +- .../messages/set-description/wrapper.tsx | 2 +- .../messages/special-bottom-message.tsx | 2 +- .../messages/special-top-message.tsx | 4 +- .../system-added-to-team/container.tsx | 2 +- .../messages/system-added-to-team/wrapper.tsx | 2 +- .../messages/system-change-avatar/wrapper.tsx | 2 +- .../system-change-retention/container.tsx | 2 +- .../system-change-retention/wrapper.tsx | 2 +- .../messages/system-create-team/container.tsx | 2 +- .../messages/system-create-team/wrapper.tsx | 2 +- .../messages/system-git-push/container.tsx | 2 +- .../messages/system-git-push/wrapper.tsx | 2 +- .../system-invite-accepted/container.tsx | 2 +- .../system-invite-accepted/wrapper.tsx | 2 +- .../messages/system-joined/container.tsx | 2 +- .../messages/system-joined/wrapper.tsx | 2 +- .../messages/system-left/container.tsx | 2 +- .../messages/system-left/wrapper.tsx | 2 +- .../messages/system-new-channel/container.tsx | 2 +- .../messages/system-new-channel/wrapper.tsx | 2 +- .../container.tsx | 2 +- .../messages/system-profile-reset-notice.tsx | 2 +- .../messages/system-sbs-resolve/wrapper.tsx | 2 +- .../system-simple-to-complex/container.tsx | 2 +- .../system-simple-to-complex/wrapper.tsx | 2 +- .../messages/system-text/wrapper.tsx | 2 +- .../system-users-added-to-conv/container.tsx | 2 +- .../system-users-added-to-conv/wrapper.tsx | 2 +- .../conversation/messages/text/bottom.tsx | 2 +- .../messages/text/coinflip/index.tsx | 6 +- .../chat/conversation/messages/text/reply.tsx | 4 +- .../text/unfurl/prompt-list/container.tsx | 2 +- .../text/unfurl/unfurl-list/generic.tsx | 6 +- .../text/unfurl/unfurl-list/giphy.tsx | 4 +- .../text/unfurl/unfurl-list/image/index.tsx | 4 +- .../text/unfurl/unfurl-list/index.tsx | 2 +- .../text/unfurl/unfurl-list/map-popup.tsx | 2 +- .../messages/text/unfurl/unfurl-list/map.tsx | 2 +- .../text/unfurl/unfurl-list/use-state.tsx | 2 +- .../conversation/messages/text/wrapper.tsx | 2 +- .../conversation/messages/wrapper/edited.tsx | 2 +- .../exploding-height-retainer/container.tsx | 2 +- .../index.native.tsx | 2 +- .../messages/wrapper/exploding-meta.tsx | 2 +- .../wrapper/long-pressable/index.native.tsx | 4 +- .../messages/wrapper/send-indicator.tsx | 2 +- .../conversation/messages/wrapper/wrapper.tsx | 2 +- shared/chat/conversation/normal/container.tsx | 2 +- .../conversation/normal/index.desktop.tsx | 2 +- .../chat/conversation/normal/index.native.tsx | 4 +- shared/chat/conversation/pinned-message.tsx | 4 +- shared/chat/conversation/rekey/container.tsx | 2 +- shared/chat/conversation/reply-preview.tsx | 4 +- shared/chat/conversation/search.tsx | 4 +- shared/chat/delete-history-warning.tsx | 2 +- shared/chat/emoji-picker/container.tsx | 2 +- ...=> inbox-and-conversation-get-options.tsx} | 0 shared/chat/inbox-and-conversation-header.tsx | 2 +- ...ation-2.tsx => inbox-and-conversation.tsx} | 2 +- shared/chat/inbox-search/index.tsx | 2 +- shared/chat/inbox/container.tsx | 2 +- shared/chat/inbox/filter-row.tsx | 2 +- shared/chat/inbox/index.native.tsx | 2 +- shared/chat/inbox/new-chat-button.tsx | 2 +- shared/chat/inbox/row/big-team-channel.tsx | 2 +- shared/chat/inbox/row/big-team-header.tsx | 2 +- shared/chat/inbox/row/big-teams-divider.tsx | 2 +- shared/chat/inbox/row/opened-row-state.tsx | 2 +- shared/chat/inbox/row/small-team/index.tsx | 2 +- .../swipe-conv-actions/index.native.tsx | 2 +- shared/chat/inbox/row/teams-divider.tsx | 2 +- shared/chat/inbox/rowitem.tsx | 2 +- shared/chat/inbox/search-row.tsx | 2 +- shared/chat/location-map.desktop.tsx | 2 +- shared/chat/location-map.native.tsx | 2 +- shared/chat/new-team-dialog-container.tsx | 2 +- shared/chat/payments/status/index.tsx | 2 +- shared/chat/pdf/index.desktop.tsx | 2 +- shared/chat/pdf/index.native.tsx | 2 +- shared/chat/routes.tsx | 6 +- .../selectable-big-team-channel-container.tsx | 2 +- .../chat/selectable-small-team-container.tsx | 2 +- .../conversation-list/conversation-list.tsx | 2 +- shared/chat/send-to-chat/index.tsx | 2 +- shared/common-adapters/avatar/hooks.tsx | 2 +- .../common-adapters/avatar/index.native.tsx | 6 +- .../emoji/custom-emoji.desktop.tsx | 6 +- .../emoji/custom-emoji.native.tsx | 4 +- .../{image2.d.ts => image.d.ts} | 4 +- .../{image2.desktop.tsx => image.desktop.tsx} | 6 +- .../{image2.native.tsx => image.native.tsx} | 12 ++-- shared/common-adapters/index-impl.js | 25 +++---- shared/common-adapters/index.d.ts | 15 ++-- .../{input2.d.ts => input.d.ts} | 2 +- .../{input2.desktop.tsx => input.desktop.tsx} | 6 +- .../{input2.native.tsx => input.native.tsx} | 6 +- .../{list-item2.css => list-item.css} | 0 shared/common-adapters/list-item.d.ts | 22 ------ shared/common-adapters/list-item.desktop.tsx | 64 ----------------- shared/common-adapters/list-item.native.tsx | 71 ------------------- .../{list-item2.tsx => list-item.tsx} | 2 +- .../common-adapters/{list2.d.ts => list.d.ts} | 14 ++-- .../{list2.desktop.tsx => list.desktop.tsx} | 18 ++--- .../{list2.native.tsx => list.native.tsx} | 10 +-- shared/common-adapters/markdown/channel.tsx | 2 +- .../markdown/maybe-mention/index.tsx | 2 +- .../markdown/maybe-mention/team.tsx | 2 +- shared/common-adapters/mention-container.tsx | 4 +- shared/common-adapters/mention.tsx | 2 +- shared/common-adapters/name-with-icon.tsx | 2 +- .../common-adapters/plain-input.desktop.tsx | 2 +- shared/common-adapters/plain-input.native.tsx | 2 +- ...nput.shared.tsx => plain-input.shared.tsx} | 0 shared/common-adapters/profile-card.tsx | 4 +- .../common-adapters/proof-broken-banner.tsx | 2 +- shared/common-adapters/usernames.tsx | 2 +- shared/common-adapters/wave-button.tsx | 2 +- .../common-adapters/zoomable-image.native.tsx | 4 +- shared/constants/{chat2 => chat}/common.tsx | 2 +- shared/constants/{chat2 => chat}/debug.tsx | 2 +- shared/constants/{chat2 => chat}/index.tsx | 0 shared/constants/{chat2 => chat}/message.tsx | 4 +- shared/constants/{chat2 => chat}/meta.tsx | 0 shared/constants/deeplinks.tsx | 4 +- shared/constants/fs.tsx | 2 +- shared/constants/index.tsx | 4 +- shared/constants/init/index.desktop.tsx | 6 +- shared/constants/init/index.native.tsx | 10 +-- shared/constants/init/shared.tsx | 30 ++++---- shared/constants/{router2.tsx => router.tsx} | 4 +- shared/constants/strings.tsx | 6 +- .../types/{chat2 => chat}/common.tsx | 0 .../constants/types/{chat2 => chat}/index.tsx | 0 .../types/{chat2 => chat}/message.tsx | 2 +- .../constants/types/{chat2 => chat}/meta.tsx | 0 shared/constants/types/index.tsx | 4 +- shared/constants/types/push.tsx | 2 +- .../types/{router2.tsx => router.tsx} | 0 shared/constants/types/team-building.tsx | 2 +- shared/constants/types/teams.tsx | 2 +- .../types/{tracker2.tsx => tracker.tsx} | 0 shared/crypto/output.tsx | 2 +- shared/crypto/sub-nav/left-nav.desktop.tsx | 4 +- shared/crypto/sub-nav/nav-row.tsx | 2 +- shared/desktop/package.desktop.tsx | 2 +- .../remote/component-loader.desktop.tsx | 2 +- shared/desktop/remote/proxies.desktop.tsx | 2 +- shared/desktop/renderer/main2.desktop.tsx | 10 +-- shared/desktop/webpack.config.babel.js | 2 +- shared/devices/device-revoke.tsx | 2 +- shared/devices/index.tsx | 2 +- shared/devices/row.tsx | 2 +- shared/fs/banner/reset-banner.tsx | 4 +- shared/fs/browser/rows/common.tsx | 4 +- shared/fs/browser/rows/editing.tsx | 2 +- shared/fs/browser/rows/placeholder.tsx | 2 +- shared/fs/browser/rows/rows.tsx | 4 +- shared/fs/common/path-item-action/header.tsx | 2 +- .../path-item-action/menu-container.tsx | 2 +- shared/fs/filepreview/view.tsx | 2 +- shared/git/row.tsx | 4 +- shared/incoming-share/index.tsx | 4 +- shared/menubar/chat-container.desktop.tsx | 2 +- shared/menubar/remote-container.desktop.tsx | 2 +- shared/menubar/remote-proxy.desktop.tsx | 2 +- shared/people/announcement.tsx | 4 +- shared/people/todo.tsx | 2 +- shared/profile/edit-avatar/index.native.tsx | 2 +- shared/profile/edit-profile.tsx | 2 +- shared/profile/generic/proofs-list.tsx | 4 +- shared/profile/generic/shared.tsx | 2 +- shared/profile/showcase-team-offer.tsx | 2 +- shared/profile/user/actions/index.tsx | 2 +- shared/profile/user/hooks.tsx | 2 +- shared/profile/user/index.tsx | 4 +- shared/profile/user/teams/index.tsx | 2 +- shared/provision/code-page/qr-image.tsx | 6 +- shared/provision/select-other-device.tsx | 8 +-- shared/router-v2/account-switcher/index.tsx | 4 +- shared/router-v2/common.d.ts | 2 +- shared/router-v2/common.native.tsx | 2 +- shared/router-v2/hooks.native.tsx | 2 +- .../router-v2/left-tab-navigator.desktop.tsx | 4 +- shared/router-v2/router.native.tsx | 2 +- shared/router-v2/routes.tsx | 2 +- shared/router-v2/screen-layout.desktop.tsx | 2 +- shared/router-v2/screen-layout.native.tsx | 2 +- shared/router-v2/shim.d.ts | 2 +- shared/router-v2/shim.desktop.tsx | 2 +- shared/router-v2/shim.native.tsx | 2 +- shared/router-v2/tab-bar.desktop.tsx | 2 +- shared/settings/account/index.tsx | 2 +- shared/settings/archive/index.tsx | 8 +-- shared/settings/contacts-joined.tsx | 6 +- shared/stores/autoreset.tsx | 2 +- shared/stores/{chat2.tsx => chat.tsx} | 20 +++--- shared/stores/config.tsx | 4 +- shared/stores/convostate.tsx | 12 ++-- shared/stores/crypto.tsx | 2 +- shared/stores/fs.tsx | 2 +- shared/stores/git.tsx | 2 +- shared/stores/logout.tsx | 2 +- shared/stores/profile.tsx | 6 +- shared/stores/provision.tsx | 2 +- shared/stores/push.native.tsx | 2 +- shared/stores/recover-password.tsx | 2 +- shared/stores/{router2.tsx => router.tsx} | 6 +- shared/stores/settings-contacts.native.tsx | 2 +- shared/stores/settings-invites.tsx | 2 +- shared/stores/settings-password.tsx | 2 +- shared/stores/settings.tsx | 2 +- shared/stores/signup.tsx | 2 +- shared/stores/store-registry.tsx | 16 ++--- shared/stores/team-building.tsx | 8 +-- shared/stores/teams.tsx | 6 +- shared/stores/{tracker2.tsx => tracker.tsx} | 4 +- shared/team-building/contact-restricted.tsx | 2 +- shared/team-building/email-search.tsx | 2 +- shared/team-building/index.tsx | 4 +- shared/team-building/list-body.tsx | 2 +- shared/team-building/modal-header-props.tsx | 2 +- shared/team-building/phone-search.tsx | 2 +- .../search-result/hellobot-result.tsx | 2 +- .../search-result/people-result.tsx | 2 +- .../search-result/user-result.tsx | 2 +- .../search-result/you-result.tsx | 4 +- shared/teams/add-members-wizard/confirm.tsx | 2 +- shared/teams/channel/header.tsx | 2 +- shared/teams/channel/index.tsx | 2 +- shared/teams/channel/rows.tsx | 6 +- shared/teams/common/channel-hooks.tsx | 2 +- shared/teams/common/contacts-list.native.tsx | 4 +- shared/teams/common/selection-popup.tsx | 2 +- shared/teams/emojis/add-alias.tsx | 2 +- shared/teams/emojis/add-emoji.tsx | 6 +- shared/teams/external-team.tsx | 4 +- .../teams/invite-by-contact/index.native.tsx | 4 +- shared/teams/main/index.tsx | 2 +- shared/teams/main/team-row.tsx | 4 +- .../new-team/wizard/add-subteam-members.tsx | 6 +- shared/teams/routes.tsx | 2 +- shared/teams/team/member/add-to-channels.tsx | 6 +- shared/teams/team/member/index.new.tsx | 2 +- shared/teams/team/new-header.tsx | 2 +- shared/teams/team/rows/bot-row/bot.tsx | 4 +- .../teams/team/rows/channel-row/channel.tsx | 4 +- shared/teams/team/rows/emoji-row/item.tsx | 2 +- shared/teams/team/rows/empty-row.tsx | 2 +- shared/teams/team/rows/index.tsx | 2 +- shared/teams/team/rows/invite-row/invite.tsx | 2 +- shared/teams/team/rows/invite-row/request.tsx | 4 +- shared/teams/team/rows/member-row.tsx | 8 +-- .../teams/team/settings-tab/channel-popup.tsx | 2 +- shared/teams/team/settings-tab/index.tsx | 2 +- .../team/settings-tab/retention/index.tsx | 2 +- shared/teams/team/tabs.tsx | 2 +- shared/{tracker2 => tracker}/assertion.tsx | 4 +- shared/{tracker2 => tracker}/bio.tsx | 2 +- .../{tracker2 => tracker}/index.desktop.tsx | 0 shared/{tracker2 => tracker}/main.desktop.tsx | 0 .../{tracker2 => tracker}/main2.desktop.tsx | 6 +- .../remote-container.desktop.tsx | 10 +-- .../remote-proxy.desktop.tsx | 4 +- .../remote-serializer.desktop.tsx | 8 +-- shared/whats-new/versions.tsx | 2 +- 357 files changed, 578 insertions(+), 739 deletions(-) rename shared/chat/conversation/input-area/{normal2 => normal}/index.tsx (97%) rename shared/chat/conversation/input-area/{normal2 => normal}/max-input-area-context.tsx (100%) rename shared/chat/conversation/input-area/{normal2 => normal}/moremenu-popup.native.tsx (97%) rename shared/chat/conversation/input-area/{normal2 => normal}/platform-input.d.ts (85%) rename shared/chat/conversation/input-area/{normal2 => normal}/platform-input.desktop.tsx (97%) rename shared/chat/conversation/input-area/{normal2 => normal}/platform-input.native.tsx (96%) rename shared/chat/conversation/input-area/{normal2 => normal}/set-explode-popup/hooks.tsx (97%) rename shared/chat/conversation/input-area/{normal2 => normal}/set-explode-popup/index.d.ts (100%) rename shared/chat/conversation/input-area/{normal2 => normal}/set-explode-popup/index.desktop.tsx (100%) rename shared/chat/conversation/input-area/{normal2 => normal}/set-explode-popup/index.native.tsx (100%) rename shared/chat/conversation/input-area/{normal2 => normal}/typing.tsx (98%) create mode 100644 shared/chat/conversation/messages/attachment/image/imageimpl.d.ts rename shared/chat/conversation/messages/attachment/{image2 => image}/imageimpl.desktop.tsx (92%) rename shared/chat/conversation/messages/attachment/{image2 => image}/imageimpl.native.tsx (67%) rename shared/chat/conversation/messages/attachment/{image2 => image}/index.tsx (97%) rename shared/chat/conversation/messages/attachment/{image2 => image}/use-state.tsx (96%) delete mode 100644 shared/chat/conversation/messages/attachment/image2/imageimpl.d.ts rename shared/chat/{inbox-and-conversation-2-get-options.tsx => inbox-and-conversation-get-options.tsx} (100%) rename shared/chat/{inbox-and-conversation-2.tsx => inbox-and-conversation.tsx} (98%) rename shared/common-adapters/{image2.d.ts => image.d.ts} (87%) rename shared/common-adapters/{image2.desktop.tsx => image.desktop.tsx} (92%) rename shared/common-adapters/{image2.native.tsx => image.native.tsx} (81%) rename shared/common-adapters/{input2.d.ts => input.d.ts} (96%) rename shared/common-adapters/{input2.desktop.tsx => input.desktop.tsx} (97%) rename shared/common-adapters/{input2.native.tsx => input.native.tsx} (96%) rename shared/common-adapters/{list-item2.css => list-item.css} (100%) delete mode 100644 shared/common-adapters/list-item.d.ts delete mode 100644 shared/common-adapters/list-item.desktop.tsx delete mode 100644 shared/common-adapters/list-item.native.tsx rename shared/common-adapters/{list-item2.tsx => list-item.tsx} (99%) rename shared/common-adapters/{list2.d.ts => list.d.ts} (81%) rename shared/common-adapters/{list2.desktop.tsx => list.desktop.tsx} (90%) rename shared/common-adapters/{list2.native.tsx => list.native.tsx} (91%) rename shared/common-adapters/{input.shared.tsx => plain-input.shared.tsx} (100%) rename shared/constants/{chat2 => chat}/common.tsx (97%) rename shared/constants/{chat2 => chat}/debug.tsx (98%) rename shared/constants/{chat2 => chat}/index.tsx (100%) rename shared/constants/{chat2 => chat}/message.tsx (99%) rename shared/constants/{chat2 => chat}/meta.tsx (100%) rename shared/constants/{router2.tsx => router.tsx} (98%) rename shared/constants/types/{chat2 => chat}/common.tsx (100%) rename shared/constants/types/{chat2 => chat}/index.tsx (100%) rename shared/constants/types/{chat2 => chat}/message.tsx (99%) rename shared/constants/types/{chat2 => chat}/meta.tsx (100%) rename shared/constants/types/{router2.tsx => router.tsx} (100%) rename shared/constants/types/{tracker2.tsx => tracker.tsx} (100%) rename shared/stores/{chat2.tsx => chat.tsx} (99%) rename shared/stores/{router2.tsx => router.tsx} (94%) rename shared/stores/{tracker2.tsx => tracker.tsx} (99%) rename shared/{tracker2 => tracker}/assertion.tsx (99%) rename shared/{tracker2 => tracker}/bio.tsx (99%) rename shared/{tracker2 => tracker}/index.desktop.tsx (100%) rename shared/{tracker2 => tracker}/main.desktop.tsx (100%) rename shared/{tracker2 => tracker}/main2.desktop.tsx (82%) rename shared/{tracker2 => tracker}/remote-container.desktop.tsx (95%) rename shared/{tracker2 => tracker}/remote-proxy.desktop.tsx (97%) rename shared/{tracker2 => tracker}/remote-serializer.desktop.tsx (95%) diff --git a/shared/chat/audio/amptracker.tsx b/shared/chat/audio/amptracker.tsx index 7fb5db6aeec0..a33776667c31 100644 --- a/shared/chat/audio/amptracker.tsx +++ b/shared/chat/audio/amptracker.tsx @@ -1,4 +1,4 @@ -import {maxAmpsLength} from '@/constants/chat2/message' +import {maxAmpsLength} from '@/constants/chat/message' const minBars = 20 const maxBars = maxAmpsLength const snap0 = 1000 // anything under this seconds takes minBars diff --git a/shared/chat/audio/audio-recorder.native.tsx b/shared/chat/audio/audio-recorder.native.tsx index 564e029ee800..06e871558816 100644 --- a/shared/chat/audio/audio-recorder.native.tsx +++ b/shared/chat/audio/audio-recorder.native.tsx @@ -1,4 +1,4 @@ -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import * as T from '@/constants/types' import * as Kb from '@/common-adapters' import {Portal} from '@/common-adapters/portal.native' diff --git a/shared/chat/blocking/block-modal.tsx b/shared/chat/blocking/block-modal.tsx index 955b68d67704..345c956e3656 100644 --- a/shared/chat/blocking/block-modal.tsx +++ b/shared/chat/blocking/block-modal.tsx @@ -1,7 +1,7 @@ import * as C from '@/constants' import * as React from 'react' import * as Kb from '@/common-adapters' -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import {useTeamsState} from '@/stores/teams' import {useUsersState} from '@/stores/users' @@ -500,7 +500,7 @@ const Container = React.memo(function BlockModal(ownProps: OwnProps) { }} noScrollView={true} > - ( - { - All channels} diff --git a/shared/chat/conversation/bot/confirm.tsx b/shared/chat/conversation/bot/confirm.tsx index 6d7c001fb176..64178c703933 100644 --- a/shared/chat/conversation/bot/confirm.tsx +++ b/shared/chat/conversation/bot/confirm.tsx @@ -1,7 +1,7 @@ import * as React from 'react' import * as Kb from '@/common-adapters' import * as C from '@/constants' -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import type * as T from '@/constants/types' import {useBotConversationIDKey} from './install' diff --git a/shared/chat/conversation/bot/install.tsx b/shared/chat/conversation/bot/install.tsx index 5ef47dd9de6c..f1a3f2f480ed 100644 --- a/shared/chat/conversation/bot/install.tsx +++ b/shared/chat/conversation/bot/install.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import * as Kb from '@/common-adapters' import * as Teams from '@/stores/teams' import * as React from 'react' diff --git a/shared/chat/conversation/bot/search.tsx b/shared/chat/conversation/bot/search.tsx index ec1309ad25f6..5a91f0862217 100644 --- a/shared/chat/conversation/bot/search.tsx +++ b/shared/chat/conversation/bot/search.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import * as Kb from '@/common-adapters' import * as React from 'react' import debounce from 'lodash/debounce' diff --git a/shared/chat/conversation/bot/team-picker.tsx b/shared/chat/conversation/bot/team-picker.tsx index b62b38be91c9..e94fc85e8d0b 100644 --- a/shared/chat/conversation/bot/team-picker.tsx +++ b/shared/chat/conversation/bot/team-picker.tsx @@ -100,10 +100,10 @@ const BotTeamPicker = (props: Props) => { {error} ) : ( - )} diff --git a/shared/chat/conversation/bottom-banner.tsx b/shared/chat/conversation/bottom-banner.tsx index a3e4390de7d6..6a8980c66456 100644 --- a/shared/chat/conversation/bottom-banner.tsx +++ b/shared/chat/conversation/bottom-banner.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import * as Kb from '@/common-adapters' import * as React from 'react' import _openSMS from '@/util/sms' diff --git a/shared/chat/conversation/command-markdown.tsx b/shared/chat/conversation/command-markdown.tsx index f40010babebf..751f669af955 100644 --- a/shared/chat/conversation/command-markdown.tsx +++ b/shared/chat/conversation/command-markdown.tsx @@ -1,4 +1,4 @@ -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import * as Kb from '@/common-adapters' const CommandMarkdown = () => { diff --git a/shared/chat/conversation/command-status.tsx b/shared/chat/conversation/command-status.tsx index 4181640af8b6..857b132b787f 100644 --- a/shared/chat/conversation/command-status.tsx +++ b/shared/chat/conversation/command-status.tsx @@ -1,4 +1,4 @@ -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import * as T from '@/constants/types' import * as Kb from '@/common-adapters' import {useConfigState} from '@/stores/config' diff --git a/shared/chat/conversation/container.tsx b/shared/chat/conversation/container.tsx index d892d3f39949..9986ab9385db 100644 --- a/shared/chat/conversation/container.tsx +++ b/shared/chat/conversation/container.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import Normal from './normal/container' import NoConversation from './no-conversation' import Error from './error' diff --git a/shared/chat/conversation/error.tsx b/shared/chat/conversation/error.tsx index 4bb646a822d1..80862bb4bb99 100644 --- a/shared/chat/conversation/error.tsx +++ b/shared/chat/conversation/error.tsx @@ -1,4 +1,4 @@ -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import * as Kb from '@/common-adapters' const ConversationError = () => { diff --git a/shared/chat/conversation/fwd-msg.tsx b/shared/chat/conversation/fwd-msg.tsx index 265d9e69c756..f2a4b4f0c23c 100644 --- a/shared/chat/conversation/fwd-msg.tsx +++ b/shared/chat/conversation/fwd-msg.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import * as React from 'react' import * as Kb from '@/common-adapters' import * as T from '@/constants/types' @@ -164,10 +164,10 @@ const TeamPicker = (props: Props) => { {error} ) : ( - )} diff --git a/shared/chat/conversation/giphy/hooks.tsx b/shared/chat/conversation/giphy/hooks.tsx index b2aea6c79b09..2b3389b66fe8 100644 --- a/shared/chat/conversation/giphy/hooks.tsx +++ b/shared/chat/conversation/giphy/hooks.tsx @@ -1,4 +1,4 @@ -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' export const useHooks = () => { const giphy = Chat.useChatContext(s => s.giphyResult) diff --git a/shared/chat/conversation/header-area/index.d.ts b/shared/chat/conversation/header-area/index.d.ts index b4f2f643afca..2196679d1e13 100644 --- a/shared/chat/conversation/header-area/index.d.ts +++ b/shared/chat/conversation/header-area/index.d.ts @@ -1,4 +1,4 @@ -import type {GetOptionsRet} from '@/constants/types/router2' +import type {GetOptionsRet} from '@/constants/types/router' declare function headerNavigationOptions(route: { params: {conversationIDKey?: string} diff --git a/shared/chat/conversation/header-area/index.native.tsx b/shared/chat/conversation/header-area/index.native.tsx index 810761d4e8dc..ce0ede828305 100644 --- a/shared/chat/conversation/header-area/index.native.tsx +++ b/shared/chat/conversation/header-area/index.native.tsx @@ -1,12 +1,12 @@ import * as C from '@/constants' -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import {useProfileState} from '@/stores/profile' import * as Kb from '@/common-adapters' import * as React from 'react' import type {HeaderBackButtonProps} from '@react-navigation/elements' import {HeaderLeftArrow} from '@/common-adapters/header-hoc' import {Keyboard} from 'react-native' -// import {DebugChatDumpContext} from '@/constants/chat2/debug' +// import {DebugChatDumpContext} from '@/constants/chat/debug' import {assertionToDisplay} from '@/common-adapters/usernames' import {useSafeAreaFrame} from 'react-native-safe-area-context' import {useUsersState} from '@/stores/users' diff --git a/shared/chat/conversation/info-panel/add-people.tsx b/shared/chat/conversation/info-panel/add-people.tsx index 724cd29ec1c1..cf385ed6312f 100644 --- a/shared/chat/conversation/info-panel/add-people.tsx +++ b/shared/chat/conversation/info-panel/add-people.tsx @@ -1,4 +1,4 @@ -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import {useTeamsState} from '@/stores/teams' import * as React from 'react' import * as Kb from '@/common-adapters' diff --git a/shared/chat/conversation/info-panel/add-to-channel.tsx b/shared/chat/conversation/info-panel/add-to-channel.tsx index 2109aaae2717..6eef0248482b 100644 --- a/shared/chat/conversation/info-panel/add-to-channel.tsx +++ b/shared/chat/conversation/info-panel/add-to-channel.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import * as React from 'react' import * as Teams from '@/stores/teams' import * as Kb from '@/common-adapters' @@ -116,7 +116,7 @@ const AddToChannel = (props: Props) => { style={styles.filterInput} /> - { const alreadyIn = participants.includes(item.username) @@ -130,7 +130,7 @@ const AddToChannel = (props: Props) => { } } return ( - } type="Small" @@ -162,7 +162,7 @@ const AddToChannel = (props: Props) => { /> ) }} - itemHeight={{sizeType: 'Small', type: 'fixedListItem2Auto'}} + itemHeight={{sizeType: 'Small', type: 'fixedListItemAuto'}} style={styles.list} /> diff --git a/shared/chat/conversation/info-panel/attachments.tsx b/shared/chat/conversation/info-panel/attachments.tsx index 53ea4dd2432e..079708afde6e 100644 --- a/shared/chat/conversation/info-panel/attachments.tsx +++ b/shared/chat/conversation/info-panel/attachments.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import * as Kb from '@/common-adapters' import type {StylesTextCrossPlatform} from '@/common-adapters/text.shared' import * as T from '@/constants/types' @@ -194,7 +194,7 @@ const MediaThumb = (props: MediaThumbProps) => { )} ) : ( - + )} {thumb.typ === ThumbTyp.VIDEO && ( diff --git a/shared/chat/conversation/info-panel/bot.tsx b/shared/chat/conversation/info-panel/bot.tsx index c3d7cee00aad..180f739a2027 100644 --- a/shared/chat/conversation/info-panel/bot.tsx +++ b/shared/chat/conversation/info-panel/bot.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import * as Teams from '@/stores/teams' import * as Kb from '@/common-adapters' import * as React from 'react' @@ -115,7 +115,7 @@ export const Bot = (props: BotProps) => { ) return ( - onClick(botUsername)} type="Large" diff --git a/shared/chat/conversation/info-panel/common.tsx b/shared/chat/conversation/info-panel/common.tsx index d01c196b8579..eb437af22c32 100644 --- a/shared/chat/conversation/info-panel/common.tsx +++ b/shared/chat/conversation/info-panel/common.tsx @@ -1,4 +1,4 @@ -import type * as Chat from '@/stores/chat2' +import type * as Chat from '@/stores/chat' import {useTeamsState} from '@/stores/teams' import * as React from 'react' import * as Styles from '@/styles' diff --git a/shared/chat/conversation/info-panel/header.tsx b/shared/chat/conversation/info-panel/header.tsx index 64a003077ceb..04580c718009 100644 --- a/shared/chat/conversation/info-panel/header.tsx +++ b/shared/chat/conversation/info-panel/header.tsx @@ -1,4 +1,4 @@ -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import * as React from 'react' import * as Teams from '@/stores/teams' import * as Kb from '@/common-adapters' diff --git a/shared/chat/conversation/info-panel/index.tsx b/shared/chat/conversation/info-panel/index.tsx index 0bf1adf3fad2..e66a7be2dfc0 100644 --- a/shared/chat/conversation/info-panel/index.tsx +++ b/shared/chat/conversation/info-panel/index.tsx @@ -1,4 +1,4 @@ -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import * as Kb from '@/common-adapters' import * as Teams from '@/stores/teams' import * as React from 'react' diff --git a/shared/chat/conversation/info-panel/members.tsx b/shared/chat/conversation/info-panel/members.tsx index 72b3a4405985..f838a6cd6f93 100644 --- a/shared/chat/conversation/info-panel/members.tsx +++ b/shared/chat/conversation/info-panel/members.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import {useProfileState} from '@/stores/profile' import * as Teams from '@/stores/teams' import * as React from 'react' diff --git a/shared/chat/conversation/info-panel/menu.tsx b/shared/chat/conversation/info-panel/menu.tsx index f605acb68839..83c4fb2ddbbf 100644 --- a/shared/chat/conversation/info-panel/menu.tsx +++ b/shared/chat/conversation/info-panel/menu.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import * as Kb from '@/common-adapters' import * as Teams from '@/stores/teams' import * as React from 'react' diff --git a/shared/chat/conversation/info-panel/participant.tsx b/shared/chat/conversation/info-panel/participant.tsx index 3fda1dfec007..5708ae5d8757 100644 --- a/shared/chat/conversation/info-panel/participant.tsx +++ b/shared/chat/conversation/info-panel/participant.tsx @@ -28,7 +28,7 @@ const Participant = ({firstItem, fullname, isAdmin, isOwner, username, onShowPro ) return ( - onShowProfile(username)} firstItem={firstItem} type="Large" diff --git a/shared/chat/conversation/info-panel/settings/index.tsx b/shared/chat/conversation/info-panel/settings/index.tsx index 7656d2a941d9..b182626e1c2a 100644 --- a/shared/chat/conversation/info-panel/settings/index.tsx +++ b/shared/chat/conversation/info-panel/settings/index.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import * as Kb from '@/common-adapters' import * as Teams from '@/stores/teams' import * as T from '@/constants/types' diff --git a/shared/chat/conversation/info-panel/settings/min-writer-role.tsx b/shared/chat/conversation/info-panel/settings/min-writer-role.tsx index 56c8a09e4566..b588dc5186bc 100644 --- a/shared/chat/conversation/info-panel/settings/min-writer-role.tsx +++ b/shared/chat/conversation/info-panel/settings/min-writer-role.tsx @@ -1,4 +1,4 @@ -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import * as Kb from '@/common-adapters' import * as Teams from '@/stores/teams' import * as React from 'react' diff --git a/shared/chat/conversation/info-panel/settings/notifications.tsx b/shared/chat/conversation/info-panel/settings/notifications.tsx index fdfcb3a0bc94..3aed53cfc948 100644 --- a/shared/chat/conversation/info-panel/settings/notifications.tsx +++ b/shared/chat/conversation/info-panel/settings/notifications.tsx @@ -1,4 +1,4 @@ -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import * as React from 'react' import * as Kb from '@/common-adapters' import type * as T from '@/constants/types' diff --git a/shared/chat/conversation/input-area/container.tsx b/shared/chat/conversation/input-area/container.tsx index e318566ef103..0e4cbc99a011 100644 --- a/shared/chat/conversation/input-area/container.tsx +++ b/shared/chat/conversation/input-area/container.tsx @@ -1,6 +1,6 @@ import * as C from '@/constants' -import * as Chat from '@/stores/chat2' -import Normal from './normal2' +import * as Chat from '@/stores/chat' +import Normal from './normal' import Preview from './preview' import ThreadSearch from '../search' diff --git a/shared/chat/conversation/input-area/location-popup.native.tsx b/shared/chat/conversation/input-area/location-popup.native.tsx index ec5fef04455f..e543cf5941de 100644 --- a/shared/chat/conversation/input-area/location-popup.native.tsx +++ b/shared/chat/conversation/input-area/location-popup.native.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import * as React from 'react' import {useConfigState} from '@/stores/config' import logger from '@/logger' diff --git a/shared/chat/conversation/input-area/normal2/index.tsx b/shared/chat/conversation/input-area/normal/index.tsx similarity index 97% rename from shared/chat/conversation/input-area/normal2/index.tsx rename to shared/chat/conversation/input-area/normal/index.tsx index 248c3e9db0bd..322d65a9501f 100644 --- a/shared/chat/conversation/input-area/normal2/index.tsx +++ b/shared/chat/conversation/input-area/normal/index.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import * as Kb from '@/common-adapters' import * as React from 'react' import CommandMarkdown from '../../command-markdown' @@ -12,7 +12,7 @@ import {indefiniteArticle} from '@/util/string' import {infoPanelWidthTablet} from '../../info-panel/common' import {assertionToDisplay} from '@/common-adapters/usernames' import {FocusContext, ScrollContext} from '@/chat/conversation/normal/context' -import type {RefType as Input2Ref} from '@/common-adapters/input2' +import type {RefType as InputRef} from '@/common-adapters/input' import {useCurrentUserState} from '@/stores/current-user' const useHintText = (p: { @@ -119,8 +119,8 @@ const ConnectedPlatformInput = React.memo(function ConnectedPlatformInput() { const isExploding = explodingModeSeconds !== 0 const hintText = useHintText({cannotWrite, isEditing, isExploding, minWriterRole}) - const inputRef = React.useRef(null) - const setInput2Ref = React.useCallback((r: Input2Ref | null) => { + const inputRef = React.useRef(null) + const setLocalInputRef = React.useCallback((r: InputRef | null) => { inputRef.current = r }, []) const suggestionOverlayStyle = infoPanelShowing @@ -275,7 +275,7 @@ const ConnectedPlatformInput = React.memo(function ConnectedPlatformInput() { suggestionOverlayStyle={suggestionOverlayStyle} suggestBotCommandsUpdateStatus={suggestBotCommandsUpdateStatus} onSubmit={onSubmit} - setInput2Ref={setInput2Ref} + setInputRef={setLocalInputRef} onChangeText={onChangeText} onCancelEditing={onCancelEditing} cannotWrite={cannotWrite} diff --git a/shared/chat/conversation/input-area/normal2/max-input-area-context.tsx b/shared/chat/conversation/input-area/normal/max-input-area-context.tsx similarity index 100% rename from shared/chat/conversation/input-area/normal2/max-input-area-context.tsx rename to shared/chat/conversation/input-area/normal/max-input-area-context.tsx diff --git a/shared/chat/conversation/input-area/normal2/moremenu-popup.native.tsx b/shared/chat/conversation/input-area/normal/moremenu-popup.native.tsx similarity index 97% rename from shared/chat/conversation/input-area/normal2/moremenu-popup.native.tsx rename to shared/chat/conversation/input-area/normal/moremenu-popup.native.tsx index e75a2301b57f..6a8ebf689fb3 100644 --- a/shared/chat/conversation/input-area/normal2/moremenu-popup.native.tsx +++ b/shared/chat/conversation/input-area/normal/moremenu-popup.native.tsx @@ -1,4 +1,4 @@ -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import * as Kb from '@/common-adapters' type Props = { diff --git a/shared/chat/conversation/input-area/normal2/platform-input.d.ts b/shared/chat/conversation/input-area/normal/platform-input.d.ts similarity index 85% rename from shared/chat/conversation/input-area/normal2/platform-input.d.ts rename to shared/chat/conversation/input-area/normal/platform-input.d.ts index 22498f5ae560..d1056bd77ec2 100644 --- a/shared/chat/conversation/input-area/normal2/platform-input.d.ts +++ b/shared/chat/conversation/input-area/normal/platform-input.d.ts @@ -1,14 +1,14 @@ import type * as React from 'react' import type * as T from '@/constants/types' import type * as Styles from '@/styles' -import type {RefType as Input2Ref} from '@/common-adapters/input2' +import type {RefType as InputRef} from '@/common-adapters/input' export type Props = { cannotWrite: boolean explodingModeSeconds: number setExplodingMode: (mode: number) => void hintText: string - setInput2Ref: (r: Input2Ref | null) => void + setInputRef: (r: InputRef | null) => void isEditing: boolean isExploding: boolean minWriterRole: T.Teams.TeamRoleType diff --git a/shared/chat/conversation/input-area/normal2/platform-input.desktop.tsx b/shared/chat/conversation/input-area/normal/platform-input.desktop.tsx similarity index 97% rename from shared/chat/conversation/input-area/normal2/platform-input.desktop.tsx rename to shared/chat/conversation/input-area/normal/platform-input.desktop.tsx index ae52c4c6f365..3821109eddaa 100644 --- a/shared/chat/conversation/input-area/normal2/platform-input.desktop.tsx +++ b/shared/chat/conversation/input-area/normal/platform-input.desktop.tsx @@ -1,4 +1,4 @@ -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import * as T from '@/constants/types' import * as Kb from '@/common-adapters' import * as React from 'react' @@ -10,16 +10,16 @@ import {KeyEventHandler} from '@/common-adapters/key-event-handler.desktop' import {formatDurationShort} from '@/util/timestamp' import {useSuggestors} from '../suggestors' import {ScrollContext} from '@/chat/conversation/normal/context' -import type {RefType as Input2Ref} from '@/common-adapters/input2' +import type {RefType as InputRef} from '@/common-adapters/input' import KB2 from '@/util/electron.desktop' // import logger from '@/logger' -// import {DebugChatDumpContext, chatDebugEnabled} from '@/constants/chat2/debug' +// import {DebugChatDumpContext, chatDebugEnabled} from '@/constants/chat/debug' const {getPathForFile} = KB2.functions type HtmlInputRefType = React.RefObject -type InputRefType = React.RefObject +type InputRefType = React.RefObject type ExplodingButtonProps = Pick & { focusInput: () => void @@ -349,12 +349,12 @@ const PlatformInput = React.memo(function PlatformInput(p: Props) { // }, [conversationIDKey, chatDebugDump]) const {cannotWrite, explodingModeSeconds, onCancelEditing, setExplodingMode} = p - const {showReplyPreview, hintText, setInput2Ref, isEditing, onSubmit} = p + const {showReplyPreview, hintText, setInputRef, isEditing, onSubmit} = p const htmlInputRef = React.useRef(null) const setHtmlInputRef = React.useCallback((i: HTMLInputElement | null) => { htmlInputRef.current = i }, []) - const inputRef = React.useRef(null) + const inputRef = React.useRef(null) React.useEffect(() => { inputRef.current?.focus() @@ -405,13 +405,13 @@ const PlatformInput = React.memo(function PlatformInput(p: Props) { }) const setRefs = React.useCallback( - (ref: null | Input2Ref) => { + (ref: null | InputRef) => { // from normal/index - setInput2Ref(ref) + setInputRef(ref) // from suggestors/index inputRef.current = ref }, - [inputRef, setInput2Ref] + [inputRef, setInputRef] ) return ( @@ -445,7 +445,7 @@ const PlatformInput = React.memo(function PlatformInput(p: Props) { /> )} - { const [showAudioSend, setShowAudioSend] = React.useState(false) const [height, setHeight] = React.useState(0) const [expanded, setExpanded] = React.useState(false) // updates immediately, used for the icon etc - const inputRef = React.useRef(null) + const inputRef = React.useRef(null) const suggestionListStyle = React.useMemo(() => { return Kb.Styles.collapseStyles([styles.suggestionList, !!height && {marginBottom: height}]) }, [height]) @@ -277,7 +277,7 @@ const PlatformInput = (p: Props) => { suggestionOverlayStyle: p.suggestionOverlayStyle, suggestionSpinnerStyle, }) - const {cannotWrite, isEditing, isExploding, setInput2Ref, setExplodingMode} = p + const {cannotWrite, isEditing, isExploding, setInputRef, setExplodingMode} = p const {onSubmit, explodingModeSeconds, hintText, onCancelEditing} = p const lastText = React.useRef('') @@ -400,11 +400,11 @@ const PlatformInput = (p: Props) => { }, []) const onAnimatedInputRef = React.useCallback( - (ref: Input2Ref | null) => { - setInput2Ref(ref) + (ref: InputRef | null) => { + setInputRef(ref) inputRef.current = ref }, - [setInput2Ref, inputRef] + [setInputRef, inputRef] ) const aiOnChangeText = React.useCallback( (text: string) => { @@ -492,18 +492,18 @@ const PlatformInput = (p: Props) => { const AnimatedInput = (() => { if (skipAnimations) { return React.memo( - React.forwardRef(function AnimatedInput(p, ref) { + React.forwardRef(function AnimatedInput(p, ref) { const {expanded, ...rest} = p return ( - + ) }) ) } else { return React.memo( - React.forwardRef(function AnimatedInput(p, ref) { + React.forwardRef(function AnimatedInput(p, ref) { 'use no memo' const maxInputArea = React.useContext(MaxInputAreaContext) const {expanded, ...rest} = p @@ -522,7 +522,7 @@ const AnimatedInput = (() => { }, [expanded, offset]) return ( - + ) }) diff --git a/shared/chat/conversation/input-area/normal2/set-explode-popup/hooks.tsx b/shared/chat/conversation/input-area/normal/set-explode-popup/hooks.tsx similarity index 97% rename from shared/chat/conversation/input-area/normal2/set-explode-popup/hooks.tsx rename to shared/chat/conversation/input-area/normal/set-explode-popup/hooks.tsx index 781e2a289d60..7f1449cbbb95 100644 --- a/shared/chat/conversation/input-area/normal2/set-explode-popup/hooks.tsx +++ b/shared/chat/conversation/input-area/normal/set-explode-popup/hooks.tsx @@ -1,4 +1,4 @@ -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import * as React from 'react' import type * as T from '@/constants/types' import type {Props} from '.' diff --git a/shared/chat/conversation/input-area/normal2/set-explode-popup/index.d.ts b/shared/chat/conversation/input-area/normal/set-explode-popup/index.d.ts similarity index 100% rename from shared/chat/conversation/input-area/normal2/set-explode-popup/index.d.ts rename to shared/chat/conversation/input-area/normal/set-explode-popup/index.d.ts diff --git a/shared/chat/conversation/input-area/normal2/set-explode-popup/index.desktop.tsx b/shared/chat/conversation/input-area/normal/set-explode-popup/index.desktop.tsx similarity index 100% rename from shared/chat/conversation/input-area/normal2/set-explode-popup/index.desktop.tsx rename to shared/chat/conversation/input-area/normal/set-explode-popup/index.desktop.tsx diff --git a/shared/chat/conversation/input-area/normal2/set-explode-popup/index.native.tsx b/shared/chat/conversation/input-area/normal/set-explode-popup/index.native.tsx similarity index 100% rename from shared/chat/conversation/input-area/normal2/set-explode-popup/index.native.tsx rename to shared/chat/conversation/input-area/normal/set-explode-popup/index.native.tsx diff --git a/shared/chat/conversation/input-area/normal2/typing.tsx b/shared/chat/conversation/input-area/normal/typing.tsx similarity index 98% rename from shared/chat/conversation/input-area/normal2/typing.tsx rename to shared/chat/conversation/input-area/normal/typing.tsx index 9b80f6647870..9da2d4dddf3c 100644 --- a/shared/chat/conversation/input-area/normal2/typing.tsx +++ b/shared/chat/conversation/input-area/normal/typing.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import * as React from 'react' import * as Kb from '@/common-adapters' diff --git a/shared/chat/conversation/input-area/preview.tsx b/shared/chat/conversation/input-area/preview.tsx index 41f2eef83300..3bf3c4e9756c 100644 --- a/shared/chat/conversation/input-area/preview.tsx +++ b/shared/chat/conversation/input-area/preview.tsx @@ -1,4 +1,4 @@ -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import * as React from 'react' import * as Kb from '@/common-adapters' diff --git a/shared/chat/conversation/input-area/suggestors/channels.tsx b/shared/chat/conversation/input-area/suggestors/channels.tsx index 4e0bc22ec650..ef5ab6dc78a8 100644 --- a/shared/chat/conversation/input-area/suggestors/channels.tsx +++ b/shared/chat/conversation/input-area/suggestors/channels.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import * as T from '@/constants/types' import * as Teams from '@/stores/teams' import * as Common from './common' diff --git a/shared/chat/conversation/input-area/suggestors/commands.tsx b/shared/chat/conversation/input-area/suggestors/commands.tsx index 4cfa2041c692..8dd510f2236e 100644 --- a/shared/chat/conversation/input-area/suggestors/commands.tsx +++ b/shared/chat/conversation/input-area/suggestors/commands.tsx @@ -1,9 +1,9 @@ import * as C from '@/constants' -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import * as T from '@/constants/types' import * as Common from './common' import * as Kb from '@/common-adapters' -import type {RefType as Input2Ref} from '@/common-adapters/input2' +import type {RefType as InputRef} from '@/common-adapters/input' const getCommandPrefix = (command: T.RPCChat.ConversationCommand) => { return command.username ? '!' : '/' @@ -99,7 +99,7 @@ const ItemRenderer = (p: Common.ItemRendererProps) => { type UseDataSourceProps = { filter: string - inputRef: React.RefObject + inputRef: React.RefObject lastTextRef: React.RefObject } @@ -169,7 +169,7 @@ type ListProps = Pick< setOnMoveRef: (r: (up: boolean) => void) => void setOnSubmitRef: (r: () => boolean) => void } & { - inputRef: React.RefObject + inputRef: React.RefObject lastTextRef: React.RefObject } export const List = (p: ListProps) => { diff --git a/shared/chat/conversation/input-area/suggestors/emoji.tsx b/shared/chat/conversation/input-area/suggestors/emoji.tsx index df285c3eb4d2..4d6f3a49184a 100644 --- a/shared/chat/conversation/input-area/suggestors/emoji.tsx +++ b/shared/chat/conversation/input-area/suggestors/emoji.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import * as Common from './common' import * as Kb from '@/common-adapters' import * as React from 'react' diff --git a/shared/chat/conversation/input-area/suggestors/index.tsx b/shared/chat/conversation/input-area/suggestors/index.tsx index a075d4262c9c..c1daa9f7e38d 100644 --- a/shared/chat/conversation/input-area/suggestors/index.tsx +++ b/shared/chat/conversation/input-area/suggestors/index.tsx @@ -1,13 +1,13 @@ import * as Channels from './channels' -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import * as Commands from './commands' import * as Emoji from './emoji' import * as Kb from '@/common-adapters' import * as React from 'react' import * as Users from './users' import type * as Common from './common' -import type {Props} from '../normal2/platform-input' -import type {RefType as Input2Ref} from '@/common-adapters/input2' +import type {Props} from '../normal/platform-input' +import type {RefType as InputRef} from '@/common-adapters/input' const positionFallbacks = ['bottom center'] as const @@ -52,7 +52,7 @@ type UseSuggestorsProps = Pick< suggestionListStyle: Kb.Styles.StylesCrossPlatform suggestionSpinnerStyle: Kb.Styles.StylesCrossPlatform expanded: boolean - inputRef: React.RefObject + inputRef: React.RefObject onKeyDown?: (evt: React.KeyboardEvent) => void } @@ -63,7 +63,7 @@ type SelectedType = Parameters<(typeof transformers)['channels' | 'commands' | ' // handles watching the input and seeing which suggestor we need to use type UseSyncInputProps = { active: ActiveType - inputRef: React.RefObject + inputRef: React.RefObject setActive: React.Dispatch> setFilter: React.Dispatch> selectedItemRef: React.RefObject @@ -403,7 +403,7 @@ export const useSuggestors = (p: UseSuggestorsProps) => { type PopupProps = { suggestionOverlayStyle: Kb.Styles.StylesCrossPlatform setInactive: () => void - inputRef: React.RefObject + inputRef: React.RefObject children: React.ReactNode } const Popup = (p: PopupProps) => { diff --git a/shared/chat/conversation/input-area/suggestors/suggestion-list.desktop.tsx b/shared/chat/conversation/input-area/suggestors/suggestion-list.desktop.tsx index dac5d95447dd..f88ea851e666 100644 --- a/shared/chat/conversation/input-area/suggestors/suggestion-list.desktop.tsx +++ b/shared/chat/conversation/input-area/suggestors/suggestion-list.desktop.tsx @@ -41,7 +41,7 @@ const SuggestionList = (props: Props) => { fullWidth={true} style={Kb.Styles.collapseStyles([styles.listContainer, props.style])} > - + {props.suggestBotCommandsUpdateStatus && props.suggestBotCommandsUpdateStatus !== T.RPCChat.UIBotCommandsUpdateStatusTyp.blank ? ( diff --git a/shared/chat/conversation/input-area/suggestors/users.tsx b/shared/chat/conversation/input-area/suggestors/users.tsx index f951b0e3536a..859abd08d58a 100644 --- a/shared/chat/conversation/input-area/suggestors/users.tsx +++ b/shared/chat/conversation/input-area/suggestors/users.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import {useTeamsState} from '@/stores/teams' import * as T from '@/constants/types' import * as Common from './common' diff --git a/shared/chat/conversation/list-area/hooks.tsx b/shared/chat/conversation/list-area/hooks.tsx index 0570ca7ed8fd..cf0e3e41188b 100644 --- a/shared/chat/conversation/list-area/hooks.tsx +++ b/shared/chat/conversation/list-area/hooks.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import * as React from 'react' import JumpToRecent from './jump-to-recent' import type * as T from '@/constants/types' diff --git a/shared/chat/conversation/list-area/index.desktop.tsx b/shared/chat/conversation/list-area/index.desktop.tsx index ffe7fbb53330..652413b569ac 100644 --- a/shared/chat/conversation/list-area/index.desktop.tsx +++ b/shared/chat/conversation/list-area/index.desktop.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import * as Kb from '@/common-adapters' import * as Hooks from './hooks' import * as React from 'react' @@ -12,7 +12,7 @@ import {findLast} from '@/util/arrays' import {getMessageRender} from '../messages/wrapper' import {globalMargins} from '@/styles/shared' import {FocusContext, ScrollContext} from '../normal/context' -import {chatDebugEnabled} from '@/constants/chat2/debug' +import {chatDebugEnabled} from '@/constants/chat/debug' import logger from '@/logger' import shallowEqual from 'shallowequal' import useResizeObserver from '@/util/use-resize-observer.desktop' diff --git a/shared/chat/conversation/list-area/index.native.tsx b/shared/chat/conversation/list-area/index.native.tsx index e45a0773bde5..1dcd9f1c6049 100644 --- a/shared/chat/conversation/list-area/index.native.tsx +++ b/shared/chat/conversation/list-area/index.native.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import * as T from '@/constants/types' import * as Hooks from './hooks' import * as Kb from '@/common-adapters' @@ -11,10 +11,10 @@ import type {ItemType} from '.' import {FlatList} from 'react-native' // import {FlashList, type ListRenderItemInfo} from '@shopify/flash-list' import {getMessageRender} from '../messages/wrapper' -import {mobileTypingContainerHeight} from '../input-area/normal2/typing' +import {mobileTypingContainerHeight} from '../input-area/normal/typing' import {SetRecycleTypeContext} from '../recycle-type-context' import {ForceListRedrawContext} from '../force-list-redraw-context' -// import {useChatDebugDump} from '@/constants/chat2/debug' +// import {useChatDebugDump} from '@/constants/chat/debug' import {usingFlashList} from './flashlist-config' import {ScrollContext} from '../normal/context' import noop from 'lodash/noop' @@ -235,7 +235,7 @@ const ConversationList = React.memo(function ConversationList() { // // _totalWidth: unknown // // } // // const {_layouts, _renderWindowSize, _totalHeight, _totalWidth} = layoutManager - // // const mm = window.DEBUGStore.store.getState().chat2.messageMap.get(conversationIDKey) + // // const mm = window.DEBUGStore.store.getState().chat.messageMap.get(conversationIDKey) // // const stateItems = messageOrdinals.map(o => ({o, type: mm.get(o)?.type})) // // console.log(listRef.current) diff --git a/shared/chat/conversation/load-status.tsx b/shared/chat/conversation/load-status.tsx index 786061c50ab9..38030e6b6078 100644 --- a/shared/chat/conversation/load-status.tsx +++ b/shared/chat/conversation/load-status.tsx @@ -1,4 +1,4 @@ -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import * as React from 'react' import * as Kb from '@/common-adapters' import * as T from '@/constants/types' diff --git a/shared/chat/conversation/messages/account-payment/container.tsx b/shared/chat/conversation/messages/account-payment/container.tsx index 8682e953227f..8dd82b2c079d 100644 --- a/shared/chat/conversation/messages/account-payment/container.tsx +++ b/shared/chat/conversation/messages/account-payment/container.tsx @@ -1,4 +1,4 @@ -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import * as Kb from '@/common-adapters' import type * as T from '@/constants/types' import MarkdownMemo from '@/wallets/markdown-memo' diff --git a/shared/chat/conversation/messages/account-payment/wrapper.tsx b/shared/chat/conversation/messages/account-payment/wrapper.tsx index 619ed8222849..d387e5a2bfac 100644 --- a/shared/chat/conversation/messages/account-payment/wrapper.tsx +++ b/shared/chat/conversation/messages/account-payment/wrapper.tsx @@ -1,4 +1,4 @@ -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import * as React from 'react' import {WrapperMessage, useCommon, type Props} from '../wrapper/wrapper' import type PaymentMessageType from './container' diff --git a/shared/chat/conversation/messages/attachment/audio.tsx b/shared/chat/conversation/messages/attachment/audio.tsx index cd32586199c4..663952856b61 100644 --- a/shared/chat/conversation/messages/attachment/audio.tsx +++ b/shared/chat/conversation/messages/attachment/audio.tsx @@ -1,4 +1,4 @@ -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import type * as T from '@/constants/types' import * as Kb from '@/common-adapters' import {useOrdinal} from '../ids-context' diff --git a/shared/chat/conversation/messages/attachment/file.tsx b/shared/chat/conversation/messages/attachment/file.tsx index 70b30f78aeb1..2b7cae6d0c1a 100644 --- a/shared/chat/conversation/messages/attachment/file.tsx +++ b/shared/chat/conversation/messages/attachment/file.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import * as Crypto from '@/stores/crypto' import * as React from 'react' import {isPathSaltpack, isPathSaltpackEncrypted, isPathSaltpackSigned} from '@/util/path' diff --git a/shared/chat/conversation/messages/attachment/image/imageimpl.d.ts b/shared/chat/conversation/messages/attachment/image/imageimpl.d.ts new file mode 100644 index 000000000000..21bf021a53b4 --- /dev/null +++ b/shared/chat/conversation/messages/attachment/image/imageimpl.d.ts @@ -0,0 +1,3 @@ +import type * as React from 'react' +declare const ImageImpl: () => React.ReactNode +export default ImageImpl diff --git a/shared/chat/conversation/messages/attachment/image2/imageimpl.desktop.tsx b/shared/chat/conversation/messages/attachment/image/imageimpl.desktop.tsx similarity index 92% rename from shared/chat/conversation/messages/attachment/image2/imageimpl.desktop.tsx rename to shared/chat/conversation/messages/attachment/image/imageimpl.desktop.tsx index 782eae11e1a9..c2cc1a0fee9d 100644 --- a/shared/chat/conversation/messages/attachment/image2/imageimpl.desktop.tsx +++ b/shared/chat/conversation/messages/attachment/image/imageimpl.desktop.tsx @@ -2,7 +2,7 @@ import * as Kb from '@/common-adapters' import {useState} from './use-state' // its important we use explicit height/width so we never CLS while loading -const Image2Impl = () => { +const ImageImpl = () => { const {previewURL, height, width} = useState() return ( { +const ImageImpl = () => { const {previewURL, height, width} = useState() - return + return } const styles = Kb.Styles.styleSheetCreate(() => ({ @@ -14,4 +14,4 @@ const styles = Kb.Styles.styleSheetCreate(() => ({ }, })) -export default Image2Impl +export default ImageImpl diff --git a/shared/chat/conversation/messages/attachment/image2/index.tsx b/shared/chat/conversation/messages/attachment/image/index.tsx similarity index 97% rename from shared/chat/conversation/messages/attachment/image2/index.tsx rename to shared/chat/conversation/messages/attachment/image/index.tsx index 05becc79e356..23af75991546 100644 --- a/shared/chat/conversation/messages/attachment/image2/index.tsx +++ b/shared/chat/conversation/messages/attachment/image/index.tsx @@ -15,7 +15,7 @@ type Props = { showPopup: () => void } -const Image2 = React.memo(function Image2(p: Props) { +const Image = React.memo(function Image(p: Props) { const {showPopup} = p const {fileName, isCollapsed, showTitle, openFullscreen, transferState, transferProgress} = useAttachmentState() @@ -90,4 +90,4 @@ const styles = Kb.Styles.styleSheetCreate(() => { } as const }) -export default Image2 +export default Image diff --git a/shared/chat/conversation/messages/attachment/image2/use-state.tsx b/shared/chat/conversation/messages/attachment/image/use-state.tsx similarity index 96% rename from shared/chat/conversation/messages/attachment/image2/use-state.tsx rename to shared/chat/conversation/messages/attachment/image/use-state.tsx index 2399086be164..e540129c10e3 100644 --- a/shared/chat/conversation/messages/attachment/image2/use-state.tsx +++ b/shared/chat/conversation/messages/attachment/image/use-state.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import {useOrdinal} from '@/chat/conversation/messages/ids-context' import {maxWidth, maxHeight} from '../shared' diff --git a/shared/chat/conversation/messages/attachment/image2/imageimpl.d.ts b/shared/chat/conversation/messages/attachment/image2/imageimpl.d.ts deleted file mode 100644 index 31f0d04cf398..000000000000 --- a/shared/chat/conversation/messages/attachment/image2/imageimpl.d.ts +++ /dev/null @@ -1,3 +0,0 @@ -import type * as React from 'react' -declare const Image2Impl: () => React.ReactNode -export default Image2Impl diff --git a/shared/chat/conversation/messages/attachment/shared.tsx b/shared/chat/conversation/messages/attachment/shared.tsx index 3f0efebc0588..c0a0446646ae 100644 --- a/shared/chat/conversation/messages/attachment/shared.tsx +++ b/shared/chat/conversation/messages/attachment/shared.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import * as Kb from '@/common-adapters' import * as React from 'react' import * as T from '@/constants/types' diff --git a/shared/chat/conversation/messages/attachment/video/use-state.tsx b/shared/chat/conversation/messages/attachment/video/use-state.tsx index 5fc3c9eaa684..6234a9ea7b2b 100644 --- a/shared/chat/conversation/messages/attachment/video/use-state.tsx +++ b/shared/chat/conversation/messages/attachment/video/use-state.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import {useOrdinal} from '@/chat/conversation/messages/ids-context' import {missingMessage, maxWidth, maxHeight} from '../shared' diff --git a/shared/chat/conversation/messages/attachment/video/videoimpl.desktop.tsx b/shared/chat/conversation/messages/attachment/video/videoimpl.desktop.tsx index 67429b5aaded..fd1187395b50 100644 --- a/shared/chat/conversation/messages/attachment/video/videoimpl.desktop.tsx +++ b/shared/chat/conversation/messages/attachment/video/videoimpl.desktop.tsx @@ -29,7 +29,7 @@ const VideoImpl = (p: Props) => { return showPoster ? (
- + {allowPlay ? : null} diff --git a/shared/chat/conversation/messages/attachment/video/videoimpl.native.tsx b/shared/chat/conversation/messages/attachment/video/videoimpl.native.tsx index 7f2940eef95a..d21d5ba17e03 100644 --- a/shared/chat/conversation/messages/attachment/video/videoimpl.native.tsx +++ b/shared/chat/conversation/messages/attachment/video/videoimpl.native.tsx @@ -47,7 +47,7 @@ const VideoImpl = (p: Props) => { direction="vertical" style={Kb.Styles.collapseStyles([styles.posterContainer, {height, width}])} > - + {allowPlay ? : null} diff --git a/shared/chat/conversation/messages/attachment/wrapper.tsx b/shared/chat/conversation/messages/attachment/wrapper.tsx index 44e6367989b7..343b2b5bd23b 100644 --- a/shared/chat/conversation/messages/attachment/wrapper.tsx +++ b/shared/chat/conversation/messages/attachment/wrapper.tsx @@ -1,7 +1,7 @@ import * as React from 'react' import type AudioAttachmentType from './audio' import type FileAttachmentType from './file' -import type ImageAttachmentType from './image2' +import type ImageAttachmentType from './image' import type VideoAttachmentType from './video' import {WrapperMessage, useCommon, type Props} from '../wrapper/wrapper' @@ -44,7 +44,7 @@ export const WrapperAttachmentImage = React.memo(function WrapperAttachmentImage const {ordinal} = p const common = useCommon(ordinal) const {showPopup} = common - const {default: ImageAttachment} = require('./image2') as {default: typeof ImageAttachmentType} + const {default: ImageAttachment} = require('./image') as {default: typeof ImageAttachmentType} return ( diff --git a/shared/chat/conversation/messages/cards/make-team.tsx b/shared/chat/conversation/messages/cards/make-team.tsx index d01d46e8e954..be5720551a08 100644 --- a/shared/chat/conversation/messages/cards/make-team.tsx +++ b/shared/chat/conversation/messages/cards/make-team.tsx @@ -1,4 +1,4 @@ -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import * as Kb from '@/common-adapters' const MakeTeam = () => { diff --git a/shared/chat/conversation/messages/cards/team-journey/container.tsx b/shared/chat/conversation/messages/cards/team-journey/container.tsx index 00f24f9eac04..694bf7b82011 100644 --- a/shared/chat/conversation/messages/cards/team-journey/container.tsx +++ b/shared/chat/conversation/messages/cards/team-journey/container.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import * as T from '@/constants/types' import * as Teams from '@/stores/teams' import * as Kb from '@/common-adapters' diff --git a/shared/chat/conversation/messages/emoji-row.tsx b/shared/chat/conversation/messages/emoji-row.tsx index 813264d650fd..00def1d50e1a 100644 --- a/shared/chat/conversation/messages/emoji-row.tsx +++ b/shared/chat/conversation/messages/emoji-row.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import * as React from 'react' import {useOrdinal} from './ids-context' import * as Kb from '@/common-adapters' diff --git a/shared/chat/conversation/messages/message-popup/attachment.tsx b/shared/chat/conversation/messages/message-popup/attachment.tsx index b6da989e6deb..c2700e1a01ab 100644 --- a/shared/chat/conversation/messages/message-popup/attachment.tsx +++ b/shared/chat/conversation/messages/message-popup/attachment.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import * as React from 'react' import type * as T from '@/constants/types' import {type Position, fileUIName, type StylesCrossPlatform} from '@/styles' diff --git a/shared/chat/conversation/messages/message-popup/hooks.tsx b/shared/chat/conversation/messages/message-popup/hooks.tsx index 48b40ada8b7f..0ae37913a2b2 100644 --- a/shared/chat/conversation/messages/message-popup/hooks.tsx +++ b/shared/chat/conversation/messages/message-popup/hooks.tsx @@ -1,7 +1,7 @@ import * as React from 'react' import type * as T from '@/constants/types' import * as C from '@/constants' -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import * as Teams from '@/stores/teams' import {useConfigState} from '@/stores/config' import {useProfileState} from '@/stores/profile' diff --git a/shared/chat/conversation/messages/message-popup/index.tsx b/shared/chat/conversation/messages/message-popup/index.tsx index 3736bfdc3587..7c6495efcaa0 100644 --- a/shared/chat/conversation/messages/message-popup/index.tsx +++ b/shared/chat/conversation/messages/message-popup/index.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import * as Kb from '@/common-adapters' import * as React from 'react' import AttachmentMessage from './attachment' diff --git a/shared/chat/conversation/messages/message-popup/journeycard.tsx b/shared/chat/conversation/messages/message-popup/journeycard.tsx index f631f84246f6..cdab22ce687a 100644 --- a/shared/chat/conversation/messages/message-popup/journeycard.tsx +++ b/shared/chat/conversation/messages/message-popup/journeycard.tsx @@ -1,4 +1,4 @@ -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import * as Kb from '@/common-adapters' import * as T from '@/constants/types' import * as React from 'react' diff --git a/shared/chat/conversation/messages/message-popup/reactionitem.tsx b/shared/chat/conversation/messages/message-popup/reactionitem.tsx index 9c734e3f529c..74af1234a82d 100644 --- a/shared/chat/conversation/messages/message-popup/reactionitem.tsx +++ b/shared/chat/conversation/messages/message-popup/reactionitem.tsx @@ -1,4 +1,4 @@ -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import * as Kb from '@/common-adapters' type Props = { diff --git a/shared/chat/conversation/messages/message-popup/text.tsx b/shared/chat/conversation/messages/message-popup/text.tsx index 66425418691d..b9681480c350 100644 --- a/shared/chat/conversation/messages/message-popup/text.tsx +++ b/shared/chat/conversation/messages/message-popup/text.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import {useConfigState} from '@/stores/config' import * as React from 'react' import * as Kb from '@/common-adapters' diff --git a/shared/chat/conversation/messages/pin/index.tsx b/shared/chat/conversation/messages/pin/index.tsx index 7618510b7557..68bf1e8cccd4 100644 --- a/shared/chat/conversation/messages/pin/index.tsx +++ b/shared/chat/conversation/messages/pin/index.tsx @@ -1,4 +1,4 @@ -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import * as Kb from '@/common-adapters' import type * as T from '@/constants/types' diff --git a/shared/chat/conversation/messages/pin/wrapper.tsx b/shared/chat/conversation/messages/pin/wrapper.tsx index 67125e1e8575..f13b36dbbfcf 100644 --- a/shared/chat/conversation/messages/pin/wrapper.tsx +++ b/shared/chat/conversation/messages/pin/wrapper.tsx @@ -1,4 +1,4 @@ -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import * as React from 'react' import {WrapperMessage, useCommon, type Props} from '../wrapper/wrapper' import type PinType from '.' diff --git a/shared/chat/conversation/messages/placeholder/wrapper.tsx b/shared/chat/conversation/messages/placeholder/wrapper.tsx index b2a278feb43b..67671d6be73b 100644 --- a/shared/chat/conversation/messages/placeholder/wrapper.tsx +++ b/shared/chat/conversation/messages/placeholder/wrapper.tsx @@ -1,4 +1,4 @@ -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import * as Kb from '@/common-adapters' import * as React from 'react' import * as T from '@/constants/types' diff --git a/shared/chat/conversation/messages/react-button.tsx b/shared/chat/conversation/messages/react-button.tsx index a9dbf69bdaf1..6622ccf6f69a 100644 --- a/shared/chat/conversation/messages/react-button.tsx +++ b/shared/chat/conversation/messages/react-button.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import * as React from 'react' import type {StylesCrossPlatform} from '@/styles' import {useOrdinal} from './ids-context' diff --git a/shared/chat/conversation/messages/reaction-tooltip.tsx b/shared/chat/conversation/messages/reaction-tooltip.tsx index 70dbeaf79bcf..e4086ec8560c 100644 --- a/shared/chat/conversation/messages/reaction-tooltip.tsx +++ b/shared/chat/conversation/messages/reaction-tooltip.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import * as Kb from '@/common-adapters' import * as React from 'react' import ReactButton from './react-button' diff --git a/shared/chat/conversation/messages/reactions-rows.tsx b/shared/chat/conversation/messages/reactions-rows.tsx index e5bff9213792..acc53eac3665 100644 --- a/shared/chat/conversation/messages/reactions-rows.tsx +++ b/shared/chat/conversation/messages/reactions-rows.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import * as Kb from '@/common-adapters' import * as React from 'react' import EmojiRow from './emoji-row' diff --git a/shared/chat/conversation/messages/reset-user.tsx b/shared/chat/conversation/messages/reset-user.tsx index 5f1b93dffc40..0c653ce5fda7 100644 --- a/shared/chat/conversation/messages/reset-user.tsx +++ b/shared/chat/conversation/messages/reset-user.tsx @@ -1,4 +1,4 @@ -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import {useProfileState} from '@/stores/profile' import * as Kb from '@/common-adapters' diff --git a/shared/chat/conversation/messages/retention-notice.tsx b/shared/chat/conversation/messages/retention-notice.tsx index 2ec9b07ecdca..febffb978b53 100644 --- a/shared/chat/conversation/messages/retention-notice.tsx +++ b/shared/chat/conversation/messages/retention-notice.tsx @@ -1,5 +1,5 @@ import type * as T from '@/constants/types' -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import * as Teams from '@/stores/teams' import * as Kb from '@/common-adapters' import * as React from 'react' diff --git a/shared/chat/conversation/messages/separator.tsx b/shared/chat/conversation/messages/separator.tsx index a292536af678..9d5ba0179bf6 100644 --- a/shared/chat/conversation/messages/separator.tsx +++ b/shared/chat/conversation/messages/separator.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import {useTeamsState} from '@/stores/teams' import * as Kb from '@/common-adapters' import * as React from 'react' @@ -7,10 +7,10 @@ import * as T from '@/constants/types' import {formatTimeForConversationList, formatTimeForChat} from '@/util/timestamp' import {OrangeLineContext} from '../orange-line-context' import logger from '@/logger' -import {useTrackerState} from '@/stores/tracker2' +import {useTrackerState} from '@/stores/tracker' import {useProfileState} from '@/stores/profile' import {useCurrentUserState} from '@/stores/current-user' -// import {useChatDebugDump} from '@/constants/chat2/debug' +// import {useChatDebugDump} from '@/constants/chat/debug' const enoughTimeBetweenMessages = (mtimestamp?: number, ptimestamp?: number): boolean => !!ptimestamp && !!mtimestamp && mtimestamp - ptimestamp > 1000 * 60 * 15 diff --git a/shared/chat/conversation/messages/set-channelname/wrapper.tsx b/shared/chat/conversation/messages/set-channelname/wrapper.tsx index c507ae29e940..c4f03722926d 100644 --- a/shared/chat/conversation/messages/set-channelname/wrapper.tsx +++ b/shared/chat/conversation/messages/set-channelname/wrapper.tsx @@ -1,4 +1,4 @@ -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import * as React from 'react' import {WrapperMessage, useCommon, type Props} from '../wrapper/wrapper' import type SetChannelnameType from './container' diff --git a/shared/chat/conversation/messages/set-description/wrapper.tsx b/shared/chat/conversation/messages/set-description/wrapper.tsx index 0383a7edc43e..f255023374e5 100644 --- a/shared/chat/conversation/messages/set-description/wrapper.tsx +++ b/shared/chat/conversation/messages/set-description/wrapper.tsx @@ -1,4 +1,4 @@ -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import * as React from 'react' import {WrapperMessage, useCommon, type Props} from '../wrapper/wrapper' import type SetDescriptionType from './container' diff --git a/shared/chat/conversation/messages/special-bottom-message.tsx b/shared/chat/conversation/messages/special-bottom-message.tsx index 386a56528d86..0ea4d474fb71 100644 --- a/shared/chat/conversation/messages/special-bottom-message.tsx +++ b/shared/chat/conversation/messages/special-bottom-message.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import * as React from 'react' import OldProfileReset from './system-old-profile-reset-notice/container' import ResetUser from './reset-user' diff --git a/shared/chat/conversation/messages/special-top-message.tsx b/shared/chat/conversation/messages/special-top-message.tsx index beb4eeb662b5..e460dc8d3a85 100644 --- a/shared/chat/conversation/messages/special-top-message.tsx +++ b/shared/chat/conversation/messages/special-top-message.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import * as T from '@/constants/types' import * as Kb from '@/common-adapters' import * as React from 'react' @@ -73,7 +73,7 @@ const ErrorMessage = () => { {createConversationDisallowedUsers.length > 0 && ( <> {createConversationDisallowedUsers.map((username, idx) => ( - } diff --git a/shared/chat/conversation/messages/system-added-to-team/container.tsx b/shared/chat/conversation/messages/system-added-to-team/container.tsx index 4c684a85fd06..ffffa78178a4 100644 --- a/shared/chat/conversation/messages/system-added-to-team/container.tsx +++ b/shared/chat/conversation/messages/system-added-to-team/container.tsx @@ -1,4 +1,4 @@ -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import * as React from 'react' import * as Teams from '@/stores/teams' import type * as T from '@/constants/types' diff --git a/shared/chat/conversation/messages/system-added-to-team/wrapper.tsx b/shared/chat/conversation/messages/system-added-to-team/wrapper.tsx index dcefb1626e38..7e61eb441390 100644 --- a/shared/chat/conversation/messages/system-added-to-team/wrapper.tsx +++ b/shared/chat/conversation/messages/system-added-to-team/wrapper.tsx @@ -1,5 +1,5 @@ import * as React from 'react' -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import {WrapperMessage, useCommon, type Props} from '../wrapper/wrapper' import type SystemAddedToTeamType from './container' diff --git a/shared/chat/conversation/messages/system-change-avatar/wrapper.tsx b/shared/chat/conversation/messages/system-change-avatar/wrapper.tsx index ed14bda12cb3..db09c3f0ac60 100644 --- a/shared/chat/conversation/messages/system-change-avatar/wrapper.tsx +++ b/shared/chat/conversation/messages/system-change-avatar/wrapper.tsx @@ -1,4 +1,4 @@ -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import * as React from 'react' import {WrapperMessage, useCommon, type Props} from '../wrapper/wrapper' import type SystemChangeAvatarType from '.' diff --git a/shared/chat/conversation/messages/system-change-retention/container.tsx b/shared/chat/conversation/messages/system-change-retention/container.tsx index d192ebd93f88..700e326776e7 100644 --- a/shared/chat/conversation/messages/system-change-retention/container.tsx +++ b/shared/chat/conversation/messages/system-change-retention/container.tsx @@ -1,4 +1,4 @@ -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import * as React from 'react' import * as Teams from '@/stores/teams' import * as Kb from '@/common-adapters' diff --git a/shared/chat/conversation/messages/system-change-retention/wrapper.tsx b/shared/chat/conversation/messages/system-change-retention/wrapper.tsx index 8d6d231f78ed..81f501b835ad 100644 --- a/shared/chat/conversation/messages/system-change-retention/wrapper.tsx +++ b/shared/chat/conversation/messages/system-change-retention/wrapper.tsx @@ -1,4 +1,4 @@ -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import * as React from 'react' import {WrapperMessage, useCommon, type Props} from '../wrapper/wrapper' import type SystemChangeRetentionType from './container' diff --git a/shared/chat/conversation/messages/system-create-team/container.tsx b/shared/chat/conversation/messages/system-create-team/container.tsx index b3c654e66d2f..2c672492f69d 100644 --- a/shared/chat/conversation/messages/system-create-team/container.tsx +++ b/shared/chat/conversation/messages/system-create-team/container.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import * as React from 'react' import * as Teams from '@/stores/teams' import * as Kb from '@/common-adapters' diff --git a/shared/chat/conversation/messages/system-create-team/wrapper.tsx b/shared/chat/conversation/messages/system-create-team/wrapper.tsx index 2a9216ababec..03b052f362de 100644 --- a/shared/chat/conversation/messages/system-create-team/wrapper.tsx +++ b/shared/chat/conversation/messages/system-create-team/wrapper.tsx @@ -1,4 +1,4 @@ -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import * as React from 'react' import {WrapperMessage, useCommon, type Props} from '../wrapper/wrapper' import type SystemCreateTeamType from './container' diff --git a/shared/chat/conversation/messages/system-git-push/container.tsx b/shared/chat/conversation/messages/system-git-push/container.tsx index 725af44edf1a..d876b91f3b5c 100644 --- a/shared/chat/conversation/messages/system-git-push/container.tsx +++ b/shared/chat/conversation/messages/system-git-push/container.tsx @@ -1,4 +1,4 @@ -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import * as React from 'react' import * as T from '@/constants/types' import * as Kb from '@/common-adapters' diff --git a/shared/chat/conversation/messages/system-git-push/wrapper.tsx b/shared/chat/conversation/messages/system-git-push/wrapper.tsx index 89e05a0090f8..3e532981cde5 100644 --- a/shared/chat/conversation/messages/system-git-push/wrapper.tsx +++ b/shared/chat/conversation/messages/system-git-push/wrapper.tsx @@ -1,4 +1,4 @@ -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import * as React from 'react' import {WrapperMessage, useCommon, type Props} from '../wrapper/wrapper' import type SystemGitPushType from './container' diff --git a/shared/chat/conversation/messages/system-invite-accepted/container.tsx b/shared/chat/conversation/messages/system-invite-accepted/container.tsx index b2d1357a85fd..dd0b4f7c9043 100644 --- a/shared/chat/conversation/messages/system-invite-accepted/container.tsx +++ b/shared/chat/conversation/messages/system-invite-accepted/container.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import * as React from 'react' import * as Teams from '@/stores/teams' import type * as T from '@/constants/types' diff --git a/shared/chat/conversation/messages/system-invite-accepted/wrapper.tsx b/shared/chat/conversation/messages/system-invite-accepted/wrapper.tsx index aeed739b80d4..e5fdf0a2bdef 100644 --- a/shared/chat/conversation/messages/system-invite-accepted/wrapper.tsx +++ b/shared/chat/conversation/messages/system-invite-accepted/wrapper.tsx @@ -1,4 +1,4 @@ -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import * as React from 'react' import {WrapperMessage, useCommon, type Props} from '../wrapper/wrapper' import type SystemInviteAcceptedType from './container' diff --git a/shared/chat/conversation/messages/system-joined/container.tsx b/shared/chat/conversation/messages/system-joined/container.tsx index 970147b86a1d..70d7f1dde139 100644 --- a/shared/chat/conversation/messages/system-joined/container.tsx +++ b/shared/chat/conversation/messages/system-joined/container.tsx @@ -1,4 +1,4 @@ -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import * as React from 'react' import type * as T from '@/constants/types' import * as Kb from '@/common-adapters' diff --git a/shared/chat/conversation/messages/system-joined/wrapper.tsx b/shared/chat/conversation/messages/system-joined/wrapper.tsx index 7fe46e21979f..053632e50751 100644 --- a/shared/chat/conversation/messages/system-joined/wrapper.tsx +++ b/shared/chat/conversation/messages/system-joined/wrapper.tsx @@ -1,4 +1,4 @@ -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import * as React from 'react' import {WrapperMessage, useCommon, type Props} from '../wrapper/wrapper' import type SystemJoinedType from './container' diff --git a/shared/chat/conversation/messages/system-left/container.tsx b/shared/chat/conversation/messages/system-left/container.tsx index 65d0cdf61516..9fbe15a7ebd8 100644 --- a/shared/chat/conversation/messages/system-left/container.tsx +++ b/shared/chat/conversation/messages/system-left/container.tsx @@ -1,4 +1,4 @@ -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import * as React from 'react' import * as Kb from '@/common-adapters' import UserNotice from '../user-notice' diff --git a/shared/chat/conversation/messages/system-left/wrapper.tsx b/shared/chat/conversation/messages/system-left/wrapper.tsx index 53f8df6c82d2..6af0a8a25a1d 100644 --- a/shared/chat/conversation/messages/system-left/wrapper.tsx +++ b/shared/chat/conversation/messages/system-left/wrapper.tsx @@ -1,4 +1,4 @@ -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import * as React from 'react' import {WrapperMessage, useCommon, type Props} from '../wrapper/wrapper' import type SystemLeftType from './container' diff --git a/shared/chat/conversation/messages/system-new-channel/container.tsx b/shared/chat/conversation/messages/system-new-channel/container.tsx index f8e1e201837d..5831c9e05e81 100644 --- a/shared/chat/conversation/messages/system-new-channel/container.tsx +++ b/shared/chat/conversation/messages/system-new-channel/container.tsx @@ -1,4 +1,4 @@ -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import * as Kb from '@/common-adapters' import {useTeamsState} from '@/stores/teams' import * as React from 'react' diff --git a/shared/chat/conversation/messages/system-new-channel/wrapper.tsx b/shared/chat/conversation/messages/system-new-channel/wrapper.tsx index be12f9f0554f..642b1d04e11d 100644 --- a/shared/chat/conversation/messages/system-new-channel/wrapper.tsx +++ b/shared/chat/conversation/messages/system-new-channel/wrapper.tsx @@ -1,4 +1,4 @@ -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import * as React from 'react' import {WrapperMessage, useCommon, type Props} from '../wrapper/wrapper' import type SystemNewChannelType from './container' diff --git a/shared/chat/conversation/messages/system-old-profile-reset-notice/container.tsx b/shared/chat/conversation/messages/system-old-profile-reset-notice/container.tsx index e306630f87ad..bb7f873d73db 100644 --- a/shared/chat/conversation/messages/system-old-profile-reset-notice/container.tsx +++ b/shared/chat/conversation/messages/system-old-profile-reset-notice/container.tsx @@ -1,4 +1,4 @@ -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import type * as T from '@/constants/types' import {Text} from '@/common-adapters' import UserNotice from '../user-notice' diff --git a/shared/chat/conversation/messages/system-profile-reset-notice.tsx b/shared/chat/conversation/messages/system-profile-reset-notice.tsx index 0a2677a6140d..9385c298cce8 100644 --- a/shared/chat/conversation/messages/system-profile-reset-notice.tsx +++ b/shared/chat/conversation/messages/system-profile-reset-notice.tsx @@ -1,4 +1,4 @@ -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import type * as T from '@/constants/types' import * as Kb from '@/common-adapters' import UserNotice from './user-notice' diff --git a/shared/chat/conversation/messages/system-sbs-resolve/wrapper.tsx b/shared/chat/conversation/messages/system-sbs-resolve/wrapper.tsx index bd9d9626c95a..caa363113140 100644 --- a/shared/chat/conversation/messages/system-sbs-resolve/wrapper.tsx +++ b/shared/chat/conversation/messages/system-sbs-resolve/wrapper.tsx @@ -1,4 +1,4 @@ -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import * as React from 'react' import {WrapperMessage, useCommon, type Props} from '../wrapper/wrapper' import type SystemSBSResolvedType from './container' diff --git a/shared/chat/conversation/messages/system-simple-to-complex/container.tsx b/shared/chat/conversation/messages/system-simple-to-complex/container.tsx index baa73a2f6af5..e096d9c6b073 100644 --- a/shared/chat/conversation/messages/system-simple-to-complex/container.tsx +++ b/shared/chat/conversation/messages/system-simple-to-complex/container.tsx @@ -1,4 +1,4 @@ -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import * as React from 'react' import {useTeamsState} from '@/stores/teams' import type * as T from '@/constants/types' diff --git a/shared/chat/conversation/messages/system-simple-to-complex/wrapper.tsx b/shared/chat/conversation/messages/system-simple-to-complex/wrapper.tsx index d894327e7599..0a5a038bb766 100644 --- a/shared/chat/conversation/messages/system-simple-to-complex/wrapper.tsx +++ b/shared/chat/conversation/messages/system-simple-to-complex/wrapper.tsx @@ -1,4 +1,4 @@ -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import * as React from 'react' import {WrapperMessage, useCommon, type Props} from '../wrapper/wrapper' import type SystemSimpleToComplexType from './container' diff --git a/shared/chat/conversation/messages/system-text/wrapper.tsx b/shared/chat/conversation/messages/system-text/wrapper.tsx index 4f2efa4555f6..3927e2667157 100644 --- a/shared/chat/conversation/messages/system-text/wrapper.tsx +++ b/shared/chat/conversation/messages/system-text/wrapper.tsx @@ -1,4 +1,4 @@ -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import * as React from 'react' import {WrapperMessage, useCommon, type Props} from '../wrapper/wrapper' import type SystemTextType from './container' diff --git a/shared/chat/conversation/messages/system-users-added-to-conv/container.tsx b/shared/chat/conversation/messages/system-users-added-to-conv/container.tsx index 1a9d48e4233d..91c2a1f35751 100644 --- a/shared/chat/conversation/messages/system-users-added-to-conv/container.tsx +++ b/shared/chat/conversation/messages/system-users-added-to-conv/container.tsx @@ -1,4 +1,4 @@ -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import * as React from 'react' import type * as T from '@/constants/types' import * as Kb from '@/common-adapters' diff --git a/shared/chat/conversation/messages/system-users-added-to-conv/wrapper.tsx b/shared/chat/conversation/messages/system-users-added-to-conv/wrapper.tsx index 1fe3e0bf1c8b..ad56ce700b81 100644 --- a/shared/chat/conversation/messages/system-users-added-to-conv/wrapper.tsx +++ b/shared/chat/conversation/messages/system-users-added-to-conv/wrapper.tsx @@ -1,4 +1,4 @@ -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import * as React from 'react' import {WrapperMessage, useCommon, type Props} from '../wrapper/wrapper' import type SystemUsersAddedToConvType from './container' diff --git a/shared/chat/conversation/messages/text/bottom.tsx b/shared/chat/conversation/messages/text/bottom.tsx index e1a26112b6ab..5d9065360a69 100644 --- a/shared/chat/conversation/messages/text/bottom.tsx +++ b/shared/chat/conversation/messages/text/bottom.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import * as React from 'react' import type * as T from '@/constants/types' import type CoinFlipType from './coinflip' diff --git a/shared/chat/conversation/messages/text/coinflip/index.tsx b/shared/chat/conversation/messages/text/coinflip/index.tsx index 35d32126979f..fd0100293aa1 100644 --- a/shared/chat/conversation/messages/text/coinflip/index.tsx +++ b/shared/chat/conversation/messages/text/coinflip/index.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import * as Kb from '@/common-adapters' import * as React from 'react' import * as T from '@/constants/types' @@ -103,7 +103,7 @@ const CoinFlipContainer = React.memo(function CoinFlipContainer() { {(commitmentVis?.length ?? 0) > 0 ? ( - + ) : ( {(revealVis?.length ?? 0) > 0 && phase !== T.RPCChat.UICoinFlipPhase.commitment ? ( - + ) : ( { return ( - + ) diff --git a/shared/chat/conversation/messages/text/unfurl/prompt-list/container.tsx b/shared/chat/conversation/messages/text/unfurl/prompt-list/container.tsx index ea0da57b5081..085e3a4e5963 100644 --- a/shared/chat/conversation/messages/text/unfurl/prompt-list/container.tsx +++ b/shared/chat/conversation/messages/text/unfurl/prompt-list/container.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import * as React from 'react' import {useOrdinal} from '@/chat/conversation/messages/ids-context' import * as T from '@/constants/types' diff --git a/shared/chat/conversation/messages/text/unfurl/unfurl-list/generic.tsx b/shared/chat/conversation/messages/text/unfurl/unfurl-list/generic.tsx index 450be91670dc..d19974240748 100644 --- a/shared/chat/conversation/messages/text/unfurl/unfurl-list/generic.tsx +++ b/shared/chat/conversation/messages/text/unfurl/unfurl-list/generic.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import * as Kb from '@/common-adapters/index' import * as T from '@/constants/types' import * as React from 'react' @@ -65,7 +65,7 @@ const UnfurlGeneric = React.memo(function UnfurlGeneric(p: {idx: number}) { const publisher = ( - {favicon ? : null} + {favicon ? : null} {siteName} @@ -124,7 +124,7 @@ const UnfurlGeneric = React.memo(function UnfurlGeneric(p: {idx: number}) { const rightImage = imageLocation === 'side' && mediaUrl ? ( - + ) : null diff --git a/shared/chat/conversation/messages/text/unfurl/unfurl-list/giphy.tsx b/shared/chat/conversation/messages/text/unfurl/unfurl-list/giphy.tsx index 6546e4e912ed..3176520c467a 100644 --- a/shared/chat/conversation/messages/text/unfurl/unfurl-list/giphy.tsx +++ b/shared/chat/conversation/messages/text/unfurl/unfurl-list/giphy.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import * as Kb from '@/common-adapters/index' import * as React from 'react' import UnfurlImage from './image' @@ -50,7 +50,7 @@ const UnfurlGiphy = React.memo(function UnfurlGiphy(p: {idx: number}) { - {favicon ? : null} + {favicon ? : null} Giphy diff --git a/shared/chat/conversation/messages/text/unfurl/unfurl-list/image/index.tsx b/shared/chat/conversation/messages/text/unfurl/unfurl-list/image/index.tsx index e2a3ba2f2c5d..41517a6a77d9 100644 --- a/shared/chat/conversation/messages/text/unfurl/unfurl-list/image/index.tsx +++ b/shared/chat/conversation/messages/text/unfurl/unfurl-list/image/index.tsx @@ -1,6 +1,6 @@ import * as React from 'react' import * as Kb from '@/common-adapters/index' -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import {maxWidth} from '@/chat/conversation/messages/attachment/shared' import {Video} from './video' import openURL from '@/util/open-url' @@ -43,7 +43,7 @@ const UnfurlImage = (p: Props) => { /> ) : ( - = [] for (let i = 0; i < numImages; i++) { children.push( - { // const {conversationIDKey, ordinal} = getIds() - // return global.DEBUGStore.store.getState().chat2.messageMap.get(conversationIDKey)?.get(ordinal) + // return global.DEBUGStore.store.getState().chat.messageMap.get(conversationIDKey)?.get(ordinal) // }, [getIds]) const inner = ( diff --git a/shared/chat/conversation/messages/wrapper/send-indicator.tsx b/shared/chat/conversation/messages/wrapper/send-indicator.tsx index 919c3e08cde5..6a1cd99c7344 100644 --- a/shared/chat/conversation/messages/wrapper/send-indicator.tsx +++ b/shared/chat/conversation/messages/wrapper/send-indicator.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import {useOrdinal} from '../ids-context' import * as React from 'react' import * as Kb from '@/common-adapters' diff --git a/shared/chat/conversation/messages/wrapper/wrapper.tsx b/shared/chat/conversation/messages/wrapper/wrapper.tsx index bac490f7c9e0..91e484e72af8 100644 --- a/shared/chat/conversation/messages/wrapper/wrapper.tsx +++ b/shared/chat/conversation/messages/wrapper/wrapper.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import * as Kb from '@/common-adapters' import * as React from 'react' import {MessageContext, useOrdinal} from '../ids-context' diff --git a/shared/chat/conversation/normal/container.tsx b/shared/chat/conversation/normal/container.tsx index dc1ee30496aa..4bc804480c2a 100644 --- a/shared/chat/conversation/normal/container.tsx +++ b/shared/chat/conversation/normal/container.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import {useConfigState} from '@/stores/config' import * as React from 'react' import Normal from '.' diff --git a/shared/chat/conversation/normal/index.desktop.tsx b/shared/chat/conversation/normal/index.desktop.tsx index 2e5dde5320c0..12a1565ed2b4 100644 --- a/shared/chat/conversation/normal/index.desktop.tsx +++ b/shared/chat/conversation/normal/index.desktop.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import * as Kb from '@/common-adapters' import * as React from 'react' import Banner from '../bottom-banner' diff --git a/shared/chat/conversation/normal/index.native.tsx b/shared/chat/conversation/normal/index.native.tsx index 2e44b8a11b2d..c66fc7f459c6 100644 --- a/shared/chat/conversation/normal/index.native.tsx +++ b/shared/chat/conversation/normal/index.native.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import {PortalHost} from '@/common-adapters/portal.native' import * as Kb from '@/common-adapters' import * as React from 'react' @@ -11,7 +11,7 @@ import ListArea from '../list-area' import PinnedMessage from '../pinned-message' import ThreadLoadStatus from '../load-status' import type {LayoutEvent} from '@/common-adapters/box' -import {MaxInputAreaContext} from '../input-area/normal2/max-input-area-context' +import {MaxInputAreaContext} from '../input-area/normal/max-input-area-context' import logger from '@/logger' const Offline = () => ( diff --git a/shared/chat/conversation/pinned-message.tsx b/shared/chat/conversation/pinned-message.tsx index 27e6b8ee29bf..2c5caa060ee2 100644 --- a/shared/chat/conversation/pinned-message.tsx +++ b/shared/chat/conversation/pinned-message.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import * as React from 'react' import * as Teams from '@/stores/teams' import type * as T from '@/constants/types' @@ -67,7 +67,7 @@ const PinnedMessage = React.memo(function PinnedMessage() { {!!imageURL && ( - + )} diff --git a/shared/chat/conversation/rekey/container.tsx b/shared/chat/conversation/rekey/container.tsx index 8cc2428b7f7b..7340687e5322 100644 --- a/shared/chat/conversation/rekey/container.tsx +++ b/shared/chat/conversation/rekey/container.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import {useProfileState} from '@/stores/profile' import {useCurrentUserState} from '@/stores/current-user' import * as T from '@/constants/types' diff --git a/shared/chat/conversation/reply-preview.tsx b/shared/chat/conversation/reply-preview.tsx index eda03825e77d..f6c8b0a98dcf 100644 --- a/shared/chat/conversation/reply-preview.tsx +++ b/shared/chat/conversation/reply-preview.tsx @@ -1,4 +1,4 @@ -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import * as React from 'react' import * as Kb from '@/common-adapters' import * as T from '@/constants/types' @@ -54,7 +54,7 @@ const ReplyPreview = () => { {!!imageURL && ( - + )} diff --git a/shared/chat/conversation/search.tsx b/shared/chat/conversation/search.tsx index 455b96ee6743..706a69773584 100644 --- a/shared/chat/conversation/search.tsx +++ b/shared/chat/conversation/search.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import type * as Styles from '@/styles' import * as React from 'react' import * as Kb from '@/common-adapters' @@ -248,7 +248,7 @@ const ThreadSearchDesktop = React.memo(function ThreadSearchDesktop(p: OwnProps) {hits.length > 0 && ( - { diff --git a/shared/chat/emoji-picker/container.tsx b/shared/chat/emoji-picker/container.tsx index 34bf8218c18d..86cb0c022dd9 100644 --- a/shared/chat/emoji-picker/container.tsx +++ b/shared/chat/emoji-picker/container.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import * as Teams from '@/stores/teams' import * as React from 'react' import * as Kb from '@/common-adapters' diff --git a/shared/chat/inbox-and-conversation-2-get-options.tsx b/shared/chat/inbox-and-conversation-get-options.tsx similarity index 100% rename from shared/chat/inbox-and-conversation-2-get-options.tsx rename to shared/chat/inbox-and-conversation-get-options.tsx diff --git a/shared/chat/inbox-and-conversation-header.tsx b/shared/chat/inbox-and-conversation-header.tsx index 46fba91a82b5..66bbc6e02a0e 100644 --- a/shared/chat/inbox-and-conversation-header.tsx +++ b/shared/chat/inbox-and-conversation-header.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import * as React from 'react' import * as Kb from '@/common-adapters' import type {StyleOverride} from '@/common-adapters/markdown' diff --git a/shared/chat/inbox-and-conversation-2.tsx b/shared/chat/inbox-and-conversation.tsx similarity index 98% rename from shared/chat/inbox-and-conversation-2.tsx rename to shared/chat/inbox-and-conversation.tsx index f0a13e01420a..109483c6d64a 100644 --- a/shared/chat/inbox-and-conversation-2.tsx +++ b/shared/chat/inbox-and-conversation.tsx @@ -1,6 +1,6 @@ // Just for desktop and tablet, we show inbox and conversation side by side import * as C from '@/constants' -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import * as Kb from '@/common-adapters' import * as React from 'react' import type * as T from '@/constants/types' diff --git a/shared/chat/inbox-search/index.tsx b/shared/chat/inbox-search/index.tsx index e54b3e914a32..0e9ed7857492 100644 --- a/shared/chat/inbox-search/index.tsx +++ b/shared/chat/inbox-search/index.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import {useTeamsState} from '@/stores/teams' import * as Kb from '@/common-adapters' import * as React from 'react' diff --git a/shared/chat/inbox/container.tsx b/shared/chat/inbox/container.tsx index 09cc8d1e3397..3ee804066e79 100644 --- a/shared/chat/inbox/container.tsx +++ b/shared/chat/inbox/container.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import * as React from 'react' import * as T from '@/constants/types' import Inbox from '.' diff --git a/shared/chat/inbox/filter-row.tsx b/shared/chat/inbox/filter-row.tsx index 1d7214f8b4c1..30036865065d 100644 --- a/shared/chat/inbox/filter-row.tsx +++ b/shared/chat/inbox/filter-row.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import * as React from 'react' import * as Kb from '@/common-adapters' diff --git a/shared/chat/inbox/index.native.tsx b/shared/chat/inbox/index.native.tsx index 61c158575c56..cb4606869a39 100644 --- a/shared/chat/inbox/index.native.tsx +++ b/shared/chat/inbox/index.native.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import * as Kb from '@/common-adapters' import * as React from 'react' import * as RowSizes from './row/sizes' diff --git a/shared/chat/inbox/new-chat-button.tsx b/shared/chat/inbox/new-chat-button.tsx index dc17141c8e42..801240e6497d 100644 --- a/shared/chat/inbox/new-chat-button.tsx +++ b/shared/chat/inbox/new-chat-button.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import * as Kb from '@/common-adapters' const HeaderNewChatButton = () => { diff --git a/shared/chat/inbox/row/big-team-channel.tsx b/shared/chat/inbox/row/big-team-channel.tsx index e958f31ccf01..70f2144a21f4 100644 --- a/shared/chat/inbox/row/big-team-channel.tsx +++ b/shared/chat/inbox/row/big-team-channel.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import type * as React from 'react' import * as Kb from '@/common-adapters' import * as RowSizes from './sizes' diff --git a/shared/chat/inbox/row/big-team-header.tsx b/shared/chat/inbox/row/big-team-header.tsx index 8c70593b401c..3620b9a77269 100644 --- a/shared/chat/inbox/row/big-team-header.tsx +++ b/shared/chat/inbox/row/big-team-header.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import * as Kb from '@/common-adapters' import * as Teams from '@/stores/teams' import * as RowSizes from './sizes' diff --git a/shared/chat/inbox/row/big-teams-divider.tsx b/shared/chat/inbox/row/big-teams-divider.tsx index 944193379468..7f85e255f975 100644 --- a/shared/chat/inbox/row/big-teams-divider.tsx +++ b/shared/chat/inbox/row/big-teams-divider.tsx @@ -1,4 +1,4 @@ -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import * as Kb from '@/common-adapters' import * as RowSizes from './sizes' import * as T from '@/constants/types' diff --git a/shared/chat/inbox/row/opened-row-state.tsx b/shared/chat/inbox/row/opened-row-state.tsx index 29c1d4c415ff..2759454e5ae5 100644 --- a/shared/chat/inbox/row/opened-row-state.tsx +++ b/shared/chat/inbox/row/opened-row-state.tsx @@ -1,4 +1,4 @@ -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import * as Z from '@/util/zustand' import type * as T from '@/constants/types' diff --git a/shared/chat/inbox/row/small-team/index.tsx b/shared/chat/inbox/row/small-team/index.tsx index 7e66ad5d04c8..290dda68c551 100644 --- a/shared/chat/inbox/row/small-team/index.tsx +++ b/shared/chat/inbox/row/small-team/index.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import type * as React from 'react' import * as Kb from '@/common-adapters' import * as RowSizes from '../sizes' diff --git a/shared/chat/inbox/row/small-team/swipe-conv-actions/index.native.tsx b/shared/chat/inbox/row/small-team/swipe-conv-actions/index.native.tsx index 94ff76ff16f9..6565ac5e4f13 100644 --- a/shared/chat/inbox/row/small-team/swipe-conv-actions/index.native.tsx +++ b/shared/chat/inbox/row/small-team/swipe-conv-actions/index.native.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import * as Kb from '@/common-adapters' import * as React from 'react' import * as Reanimated from 'react-native-reanimated' diff --git a/shared/chat/inbox/row/teams-divider.tsx b/shared/chat/inbox/row/teams-divider.tsx index 5c90d6eab859..6ba9ae596771 100644 --- a/shared/chat/inbox/row/teams-divider.tsx +++ b/shared/chat/inbox/row/teams-divider.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import * as Kb from '@/common-adapters' import * as T from '@/constants/types' import * as React from 'react' diff --git a/shared/chat/inbox/rowitem.tsx b/shared/chat/inbox/rowitem.tsx index 71b5fe116891..a768c1b68b13 100644 --- a/shared/chat/inbox/rowitem.tsx +++ b/shared/chat/inbox/rowitem.tsx @@ -1,4 +1,4 @@ -import type {ConversationIDKey} from '@/constants/types/chat2' +import type {ConversationIDKey} from '@/constants/types/chat' import type * as T from '@/constants/types' export type ChatInboxRowItemSmall = { diff --git a/shared/chat/inbox/search-row.tsx b/shared/chat/inbox/search-row.tsx index 0ae7328faf2f..c7f4162a863e 100644 --- a/shared/chat/inbox/search-row.tsx +++ b/shared/chat/inbox/search-row.tsx @@ -1,6 +1,6 @@ import * as React from 'react' import * as C from '@/constants' -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import ChatFilterRow from './filter-row' import StartNewChat from './row/start-new-chat' diff --git a/shared/chat/location-map.desktop.tsx b/shared/chat/location-map.desktop.tsx index 5ddf7036b307..e2d0b5d3943d 100644 --- a/shared/chat/location-map.desktop.tsx +++ b/shared/chat/location-map.desktop.tsx @@ -19,7 +19,7 @@ const LocationMap = (props: Props) => { return ( - {!!mapSrc && } + {!!mapSrc && } {!mapLoaded && } { } return ( - {!!mapSrc && } + {!!mapSrc && } {!mapLoaded && } import('./conversation/container')) @@ -21,7 +21,7 @@ export const newRoutes = { }, chatRoot: Chat.isSplit ? Chat.makeChatScreen( - React.lazy(async () => import('./inbox-and-conversation-2')), + React.lazy(async () => import('./inbox-and-conversation')), {getOptions: inboxAndConvoGetOptions, skipProvider: true} ) : Chat.makeChatScreen( diff --git a/shared/chat/selectable-big-team-channel-container.tsx b/shared/chat/selectable-big-team-channel-container.tsx index 35e0b58f9614..a8903516e66b 100644 --- a/shared/chat/selectable-big-team-channel-container.tsx +++ b/shared/chat/selectable-big-team-channel-container.tsx @@ -1,4 +1,4 @@ -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import SelectableBigTeamChannel from './selectable-big-team-channel' type OwnProps = { diff --git a/shared/chat/selectable-small-team-container.tsx b/shared/chat/selectable-small-team-container.tsx index e287b21d4448..319b0ee533bc 100644 --- a/shared/chat/selectable-small-team-container.tsx +++ b/shared/chat/selectable-small-team-container.tsx @@ -1,4 +1,4 @@ -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import * as Kb from '@/common-adapters' import type {AllowedColors} from '@/common-adapters/text.shared' import SelectableSmallTeam from './selectable-small-team' diff --git a/shared/chat/send-to-chat/conversation-list/conversation-list.tsx b/shared/chat/send-to-chat/conversation-list/conversation-list.tsx index 96716aa5f4b3..b4ec448ed908 100644 --- a/shared/chat/send-to-chat/conversation-list/conversation-list.tsx +++ b/shared/chat/send-to-chat/conversation-list/conversation-list.tsx @@ -140,7 +140,7 @@ const ConversationListRender = (props: ConversationListRenderProps) => { /> - ({ isSelected: index === props.selected, diff --git a/shared/chat/send-to-chat/index.tsx b/shared/chat/send-to-chat/index.tsx index 532f8a5a55fd..627553238103 100644 --- a/shared/chat/send-to-chat/index.tsx +++ b/shared/chat/send-to-chat/index.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import * as T from '@/constants/types' import * as React from 'react' import * as Kb from '@/common-adapters' diff --git a/shared/common-adapters/avatar/hooks.tsx b/shared/common-adapters/avatar/hooks.tsx index e1177a28b72f..7c23da504e0d 100644 --- a/shared/common-adapters/avatar/hooks.tsx +++ b/shared/common-adapters/avatar/hooks.tsx @@ -9,7 +9,7 @@ import type {Props} from '.' import {useColorScheme} from 'react-native' import {useUsersState} from '@/stores/users' import {useFollowerState} from '@/stores/followers' -import {navToProfile} from '@/stores/router2' +import {navToProfile} from '@/stores/router' export const avatarSizes = [128, 96, 64, 48, 32, 24, 16] as const export type AvatarSize = (typeof avatarSizes)[number] diff --git a/shared/common-adapters/avatar/index.native.tsx b/shared/common-adapters/avatar/index.native.tsx index f959e17bda17..6ba6a7082505 100644 --- a/shared/common-adapters/avatar/index.native.tsx +++ b/shared/common-adapters/avatar/index.native.tsx @@ -2,7 +2,7 @@ import Icon from '../icon' import * as React from 'react' import * as Styles from '@/styles' import ClickableBox from '../clickable-box' -import Image2 from '../image2' +import Image from '../image' import {Box2} from '../box' import type {Props, AvatarSize} from '.' import useHook from './hooks' @@ -11,7 +11,7 @@ const Kb = { Box2, ClickableBox, Icon, - Image2, + Image, } const sizeToTeamBorderRadius = new Map([ @@ -47,7 +47,7 @@ const Avatar = React.memo(function Avatar(p: Props) { )} {!!props.url && ( - { ])} > - { @@ -11,7 +11,7 @@ const CustomEmoji = (props: Props) => { ...style, } - return + return } export default CustomEmoji diff --git a/shared/common-adapters/image2.d.ts b/shared/common-adapters/image.d.ts similarity index 87% rename from shared/common-adapters/image2.d.ts rename to shared/common-adapters/image.d.ts index 1c74414e1dd4..0d0b28ce27db 100644 --- a/shared/common-adapters/image2.d.ts +++ b/shared/common-adapters/image.d.ts @@ -11,5 +11,5 @@ export type Props = { allowDownscaling?: boolean } -declare const Image2: (p: Props) => React.ReactNode -export default Image2 +declare const Image: (p: Props) => React.ReactNode +export default Image diff --git a/shared/common-adapters/image2.desktop.tsx b/shared/common-adapters/image.desktop.tsx similarity index 92% rename from shared/common-adapters/image2.desktop.tsx rename to shared/common-adapters/image.desktop.tsx index 01b8188da058..ec91d6c55821 100644 --- a/shared/common-adapters/image2.desktop.tsx +++ b/shared/common-adapters/image.desktop.tsx @@ -1,11 +1,11 @@ import * as C from '@/constants' import * as React from 'react' import * as Styles from '@/styles' -import type {Props} from './image2' +import type {Props} from './image' import LoadingStateView from './loading-state-view' const onDragStart = (e: React.BaseSyntheticEvent) => e.preventDefault() -const Image2 = (p: Props) => { +const Image = (p: Props) => { const {showLoadingStateUntilLoaded, src, onLoad, onError} = p const [loading, setLoading] = React.useState(true) const isMounted = C.useIsMounted() @@ -41,4 +41,4 @@ const styles = Styles.styleSheetCreate(() => ({ absolute: {position: 'absolute'}, })) -export default Image2 +export default Image diff --git a/shared/common-adapters/image2.native.tsx b/shared/common-adapters/image.native.tsx similarity index 81% rename from shared/common-adapters/image2.native.tsx rename to shared/common-adapters/image.native.tsx index 5d43ae629a0d..84163cc9406d 100644 --- a/shared/common-adapters/image2.native.tsx +++ b/shared/common-adapters/image.native.tsx @@ -1,9 +1,9 @@ import * as React from 'react' import LoadingStateView from './loading-state-view' -import type {Props} from './image2' -import {Image, type ImageLoadEventData, type ImageErrorEventData} from 'expo-image' +import type {Props} from './image' +import {Image as ExpoImage, type ImageLoadEventData, type ImageErrorEventData} from 'expo-image' -const Image2 = (p: Props) => { +const Image = (p: Props) => { const { showLoadingStateUntilLoaded, src, @@ -32,7 +32,7 @@ const Image2 = (p: Props) => { const _onError = React.useCallback( (e: ImageErrorEventData) => { setLoading(false) - console.log('Image2 load error', e.error) + console.log('Image load error', e.error) onError?.() }, [setLoading, onError] @@ -40,7 +40,7 @@ const Image2 = (p: Props) => { return ( <> - { ) } -export default Image2 +export default Image diff --git a/shared/common-adapters/index-impl.js b/shared/common-adapters/index-impl.js index 49c55c4159df..ba887b9b89be 100644 --- a/shared/common-adapters/index-impl.js +++ b/shared/common-adapters/index-impl.js @@ -147,8 +147,8 @@ module.exports = { get Icon() { return require('./icon').default }, - get Image2() { - return require('./image2').default + get Image() { + return require('./image').default }, get InfoNote() { return require('./info-note').default @@ -156,8 +156,8 @@ module.exports = { get InlineDropdown() { return require('./dropdown').InlineDropdown }, - get Input2() { - return require('./input2').Input2 + get Input() { + return require('./input').Input }, get KeyboardAvoidingView2() { return require('./keyboard-avoiding-view').KeyboardAvoidingView2 @@ -165,15 +165,12 @@ module.exports = { get LabeledInput() { return require('./labeled-input').default }, - get List2() { - return require('./list2').default + get List() { + return require('./list').default }, - get ListItem() { +get ListItem() { return require('./list-item').default }, - get ListItem2() { - return require('./list-item2').default - }, get LoadingLine() { return require('./loading-line').default }, @@ -318,11 +315,11 @@ module.exports = { get isValidIconType() { return require('./icon.shared').isValidIconType }, - get largeListItem2Height() { - return require('./list-item2').largeHeight + get largeListItemHeight() { + return require('./list-item').largeHeight }, - get smallListItem2Height() { - return require('./list-item2').smallHeight + get smallListItemHeight() { + return require('./list-item').smallHeight }, get urlsToImgSet() { return require('./icon').urlsToImgSet diff --git a/shared/common-adapters/index.d.ts b/shared/common-adapters/index.d.ts index f7e8f74fc96e..e65686f2f987 100644 --- a/shared/common-adapters/index.d.ts +++ b/shared/common-adapters/index.d.ts @@ -45,19 +45,18 @@ export {HeaderHocHeader, HeaderHocWrapper, HeaderLeftBlank, HeaderLeftCancel} fr export {PopupWrapper} from './header-or-popup' export {useHotKey} from './hot-key' export {default as Icon, urlsToImgSet, type IconStyle} from './icon' -export {default as Image2} from './image2' +export {default as Image} from './image' export {default as InfoNote} from './info-note' -export {Input2} from './input2' +export {Input} from './input' export {KeyboardAvoidingView2} from './keyboard-avoiding-view' export {default as LabeledInput} from './labeled-input' -export {default as List2} from './list2' +export {default as List} from './list' export {default as LoadingLine} from './loading-line' -export {default as ListItem} from './list-item' export { - default as ListItem2, - largeHeight as largeListItem2Height, - smallHeight as smallListItem2Height, -} from './list-item2' + default as ListItem, + largeHeight as largeListItemHeight, + smallHeight as smallListItemHeight, +} from './list-item' export {default as Markdown} from './markdown' export {default as Meta} from './meta' diff --git a/shared/common-adapters/input2.d.ts b/shared/common-adapters/input.d.ts similarity index 96% rename from shared/common-adapters/input2.d.ts rename to shared/common-adapters/input.d.ts index 7952342b0494..97b12857da4c 100644 --- a/shared/common-adapters/input2.d.ts +++ b/shared/common-adapters/input.d.ts @@ -58,6 +58,6 @@ export type Props = { padding?: keyof typeof Styles.globalMargins | 0 // globalMargins does not have an option for 0 } -declare const Input2: React.ForwardRefExoticComponent< +declare const Input: React.ForwardRefExoticComponent< React.PropsWithoutRef & React.RefAttributes > diff --git a/shared/common-adapters/input2.desktop.tsx b/shared/common-adapters/input.desktop.tsx similarity index 97% rename from shared/common-adapters/input2.desktop.tsx rename to shared/common-adapters/input.desktop.tsx index 921a1b37df61..04b14a2ce685 100644 --- a/shared/common-adapters/input2.desktop.tsx +++ b/shared/common-adapters/input.desktop.tsx @@ -1,14 +1,14 @@ import * as React from 'react' import * as Styles from '@/styles' -import type {Props, TextInfo, RefType} from './input2' +import type {Props, TextInfo, RefType} from './input' import {getTextStyle} from './text.styles' import {useColorScheme} from 'react-native' const maybeParseInt = (input: string | number, radix: number): number => typeof input === 'string' ? parseInt(input, radix) : input -export const Input2 = React.memo( - React.forwardRef(function Input2(p, ref) { +export const Input = React.memo( + React.forwardRef(function Input(p, ref) { const {style: _style, onChangeText: _onChangeText, multiline} = p const {textType = 'Body', rowsMax, rowsMin, padding, placeholder, onKeyUp: _onKeyUp} = p const {allowKeyboardEvents, className, disabled, autoFocus, onKeyDown: _onKeyDown, onEnterKeyDown} = p diff --git a/shared/common-adapters/input2.native.tsx b/shared/common-adapters/input.native.tsx similarity index 96% rename from shared/common-adapters/input2.native.tsx rename to shared/common-adapters/input.native.tsx index ed191864d2db..517008704dba 100644 --- a/shared/common-adapters/input2.native.tsx +++ b/shared/common-adapters/input.native.tsx @@ -1,6 +1,6 @@ import * as React from 'react' import * as Styles from '@/styles' -import type {Props, TextInfo, RefType} from './input2' +import type {Props, TextInfo, RefType} from './input' import {isIOS} from '@/constants/platform' import {getTextStyle} from './text.styles' import { @@ -12,8 +12,8 @@ import { import {useColorScheme} from 'react-native' import {registerPasteImage} from 'react-native-kb' -export const Input2 = React.memo( - React.forwardRef(function Input2(p, ref) { +export const Input = React.memo( + React.forwardRef(function Input(p, ref) { const {style: _style, onChangeText: _onChangeText, multiline, placeholder} = p const {textType = 'Body', rowsMax, rowsMin, padding, disabled, onPasteImage} = p const { diff --git a/shared/common-adapters/list-item2.css b/shared/common-adapters/list-item.css similarity index 100% rename from shared/common-adapters/list-item2.css rename to shared/common-adapters/list-item.css diff --git a/shared/common-adapters/list-item.d.ts b/shared/common-adapters/list-item.d.ts deleted file mode 100644 index 744ff55b5f1f..000000000000 --- a/shared/common-adapters/list-item.d.ts +++ /dev/null @@ -1,22 +0,0 @@ -import type * as React from 'react' - -// splits the rendering into 3 parts like the style guide shows. -// Part 1 Icon -// Part 2 Text / Body -// Part 3 Action (could be text or button) - -export type Props = { - type: 'Small' | 'Large' - icon: React.ReactNode - body: React.ReactNode - action: React.ReactNode - extraRightMarginAction?: boolean // Spacing is different if the action is just text (for example), - onClick?: () => void - onPress?: never - containerStyle?: object - bodyContainerStyle?: object - swipeToAction?: boolean // Do you have to swipe the list item to reveal an action? -} - -declare const ListItem: (p: Props) => React.ReactNode -export default ListItem diff --git a/shared/common-adapters/list-item.desktop.tsx b/shared/common-adapters/list-item.desktop.tsx deleted file mode 100644 index 2d900d222598..000000000000 --- a/shared/common-adapters/list-item.desktop.tsx +++ /dev/null @@ -1,64 +0,0 @@ -import {Box2} from './box' -import {desktopStyles} from '@/styles' -import type {Props} from './list-item' - -const ListItem = (p: Props) => { - const clickable = !!p.onClick - const minHeight = {Large: 56, Small: 40}[p.type] - return ( - - - - {p.icon} - - - - {p.body} - - - {p.action} - - - ) -} - -function containerStyle(clickable: boolean) { - return clickable ? desktopStyles.clickable : {} -} - -function actionStyle(extraMargin: boolean) { - return extraMargin ? {marginRight: 32} : {marginRight: 16} -} - -const bodyContainerStyle = (type: 'Large' | 'Small') => - ({ - flex: 2, - justifyContent: 'center', - marginBottom: type === 'Small' ? 4 : 8, - marginLeft: 8, - marginRight: 8, - marginTop: type === 'Small' ? 4 : 8, - }) as const - -export default ListItem diff --git a/shared/common-adapters/list-item.native.tsx b/shared/common-adapters/list-item.native.tsx deleted file mode 100644 index 8902e3ae6f03..000000000000 --- a/shared/common-adapters/list-item.native.tsx +++ /dev/null @@ -1,71 +0,0 @@ -import type {Props} from './list-item' -import {Box2} from './box' -import ClickableBox from './clickable-box' - -const ListItem = (p: Props) => { - const height = {Large: 64, Small: 48}[p.type] // minimum height - const listItem = ( - - - - - {p.icon} - - - - {p.body} - - {!p.swipeToAction && ( - - {p.action} - - )} - - ) - return {listItem} -} - -const iconContainerThemed = { - Large: { - width: 64, - }, - Small: { - width: 48, - }, -} - -function actionStyle(extraMargin: boolean) { - return extraMargin ? {marginRight: 32} : {marginRight: 16} -} - -function bodyContainerStyle(swipeToAction?: boolean) { - return { - flex: 2, - justifyContent: 'center', - marginBottom: 8, - marginLeft: 8, - marginRight: swipeToAction ? 0 : 16, - marginTop: 8, - } as const -} - -export default ListItem diff --git a/shared/common-adapters/list-item2.tsx b/shared/common-adapters/list-item.tsx similarity index 99% rename from shared/common-adapters/list-item2.tsx rename to shared/common-adapters/list-item.tsx index 333295bae860..e27516e55ba3 100644 --- a/shared/common-adapters/list-item2.tsx +++ b/shared/common-adapters/list-item.tsx @@ -4,7 +4,7 @@ import ClickableBox from './clickable-box' import {Box2} from './box' import BoxGrow from './box-grow' import Divider from './divider' -import './list-item2.css' +import './list-item.css' const Kb = { Box2, diff --git a/shared/common-adapters/list2.d.ts b/shared/common-adapters/list.d.ts similarity index 81% rename from shared/common-adapters/list2.d.ts rename to shared/common-adapters/list.d.ts index 08209ee5133d..3e8bca62a710 100644 --- a/shared/common-adapters/list2.d.ts +++ b/shared/common-adapters/list.d.ts @@ -2,8 +2,8 @@ import type * as React from 'react' import type {CustomStyles} from '@/styles' import type {useListRef, DynamicRowHeight} from 'react-window' -// List2 differs from list in that on desktop it uses react-window. -// Don't use List2 if you need a list with dynamic item sizes +// List differs from list in that on desktop it uses react-window. +// Don't use List if you need a list with dynamic item sizes export type VariableItemHeight = { getItemLayout: ( @@ -22,9 +22,9 @@ export type FixedHeight = { type: 'fixed' } -export type FixedListItem2Auto = { +export type FixedListItemAuto = { sizeType: 'Small' | 'Large' - type: 'fixedListItem2Auto' + type: 'fixedListItemAuto' } export type TrueVariable = { @@ -40,7 +40,7 @@ export type Props = { keyProperty?: string // if passed uses item[keyProperty] for the item keys, items: ReadonlyArray renderItem: (index: number, item: Item) => React.ReactElement | null - itemHeight: VariableItemHeight | FixedHeight | FixedListItem2Auto | TrueVariable + itemHeight: VariableItemHeight | FixedHeight | FixedListItemAuto | TrueVariable estimatedItemHeight?: number selectedIndex?: number // TODO, bounces?: boolean // mobile only, @@ -51,5 +51,5 @@ export type Props = { desktopRef?: ReturnType } -export declare function List2(p: Props): React.ReactNode -export default List2 +export declare function List(p: Props): React.ReactNode +export default List diff --git a/shared/common-adapters/list2.desktop.tsx b/shared/common-adapters/list.desktop.tsx similarity index 90% rename from shared/common-adapters/list2.desktop.tsx rename to shared/common-adapters/list.desktop.tsx index 385faf885abd..1d7fe7ebf85d 100644 --- a/shared/common-adapters/list2.desktop.tsx +++ b/shared/common-adapters/list.desktop.tsx @@ -1,8 +1,8 @@ import * as React from 'react' import * as Styles from '@/styles' -import {List, type RowComponentProps} from 'react-window' -import type {Props} from './list2' -import {smallHeight, largeHeight} from './list-item2' +import {List as ReactWindowList, type RowComponentProps} from 'react-window' +import type {Props} from './list' +import {smallHeight, largeHeight} from './list-item' const Row = React.memo(function Row( p: RowComponentProps<{ @@ -20,14 +20,14 @@ const Row = React.memo(function Row( }> ) => React.ReactElement -function List2(props: Props) { +function List(props: Props) { const {items, renderItem, style, itemHeight} = props // Need to pass in itemData to make items re-render on prop changes. const _fixed = (p: {itemHeight: number}) => { const {itemHeight} = p return ( - (props: Props) { switch (props.itemHeight.type) { case 'fixed': return _fixed({itemHeight: props.itemHeight.height}) - case 'fixedListItem2Auto': { + case 'fixedListItemAuto': { const itemHeight = props.itemHeight.sizeType === 'Large' ? largeHeight : smallHeight return _fixed({itemHeight}) } case 'trueVariable': return ( - (props: Props) { ) case 'variable': return ( - (props: Props) { } } -export default List2 +export default List diff --git a/shared/common-adapters/list2.native.tsx b/shared/common-adapters/list.native.tsx similarity index 91% rename from shared/common-adapters/list2.native.tsx rename to shared/common-adapters/list.native.tsx index 5faf318741c4..6c0b1f04b0f2 100644 --- a/shared/common-adapters/list2.native.tsx +++ b/shared/common-adapters/list.native.tsx @@ -1,14 +1,14 @@ import * as React from 'react' import {FlatList, View} from 'react-native' import * as Styles from '@/styles' -import {smallHeight, largeHeight} from './list-item2' +import {smallHeight, largeHeight} from './list-item' import ReAnimated from './reanimated' -import type {Props} from './list2' +import type {Props} from './list' import noop from 'lodash/noop' const AnimatedFlatList = ReAnimated.FlatList -const List2 = React.memo(function List2(p: Props) { +const List = React.memo(function List(p: Props) { const {indexAsKey, keyProperty, itemHeight, renderItem, ...props} = p const itemRender = React.useCallback( @@ -23,7 +23,7 @@ const List2 = React.memo(function List2(p: Props) { switch (itemHeight.type) { case 'fixed': return {index, length: itemHeight.height, offset: itemHeight.height * index} - case 'fixedListItem2Auto': { + case 'fixedListItemAuto': { const length = itemHeight.sizeType === 'Large' ? largeHeight : smallHeight return {index, length, offset: length * index} } @@ -81,4 +81,4 @@ const styles = Styles.styleSheetCreate( }) as const ) -export default List2 +export default List diff --git a/shared/common-adapters/markdown/channel.tsx b/shared/common-adapters/markdown/channel.tsx index 538620210165..950d9bc66e58 100644 --- a/shared/common-adapters/markdown/channel.tsx +++ b/shared/common-adapters/markdown/channel.tsx @@ -1,4 +1,4 @@ -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import type * as T from '@/constants/types' import Text from '../text' import type {StylesTextCrossPlatform} from '../text.shared' diff --git a/shared/common-adapters/markdown/maybe-mention/index.tsx b/shared/common-adapters/markdown/maybe-mention/index.tsx index 3858e5d93845..f6bcaff1135f 100644 --- a/shared/common-adapters/markdown/maybe-mention/index.tsx +++ b/shared/common-adapters/markdown/maybe-mention/index.tsx @@ -1,4 +1,4 @@ -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import * as T from '@/constants/types' import Text from '@/common-adapters/text' import type {StylesTextCrossPlatform} from '@/common-adapters/text.shared' diff --git a/shared/common-adapters/markdown/maybe-mention/team.tsx b/shared/common-adapters/markdown/maybe-mention/team.tsx index 7a6acbc70570..03efda5ddbcd 100644 --- a/shared/common-adapters/markdown/maybe-mention/team.tsx +++ b/shared/common-adapters/markdown/maybe-mention/team.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import * as T from '@/constants/types' import {useTeamsState} from '@/stores/teams' import * as React from 'react' diff --git a/shared/common-adapters/mention-container.tsx b/shared/common-adapters/mention-container.tsx index 2f36d05b027b..3ee0e41e5709 100644 --- a/shared/common-adapters/mention-container.tsx +++ b/shared/common-adapters/mention-container.tsx @@ -1,7 +1,7 @@ import * as C from '@/constants' -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import Mention, {type OwnProps} from './mention' -import {useTrackerState} from '@/stores/tracker2' +import {useTrackerState} from '@/stores/tracker' import {useProfileState} from '@/stores/profile' import {useFollowerState} from '@/stores/followers' import {useCurrentUserState} from '@/stores/current-user' diff --git a/shared/common-adapters/mention.tsx b/shared/common-adapters/mention.tsx index 973ec7212085..7b9d15be37c6 100644 --- a/shared/common-adapters/mention.tsx +++ b/shared/common-adapters/mention.tsx @@ -1,4 +1,4 @@ -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import * as Styles from '@/styles' import {WithProfileCardPopup} from './profile-card' import Text from './text' diff --git a/shared/common-adapters/name-with-icon.tsx b/shared/common-adapters/name-with-icon.tsx index 488105de97ac..3351208c0a81 100644 --- a/shared/common-adapters/name-with-icon.tsx +++ b/shared/common-adapters/name-with-icon.tsx @@ -9,7 +9,7 @@ import Icon, {type IconType} from './icon' import Text from './text' import type {TextType, StylesTextCrossPlatform, AllowedColors, TextTypeBold} from './text.shared' import ConnectedUsernames from './usernames' -import {useTrackerState} from '@/stores/tracker2' +import {useTrackerState} from '@/stores/tracker' import {useProfileState} from '@/stores/profile' type Size = 'smaller' | 'small' | 'default' | 'big' | 'huge' diff --git a/shared/common-adapters/plain-input.desktop.tsx b/shared/common-adapters/plain-input.desktop.tsx index 895969a84daa..046412bcf15a 100644 --- a/shared/common-adapters/plain-input.desktop.tsx +++ b/shared/common-adapters/plain-input.desktop.tsx @@ -2,7 +2,7 @@ import * as React from 'react' import * as Styles from '@/styles' import {getTextStyle} from './text.styles' import logger from '@/logger' -import {checkTextInfo} from './input.shared' +import {checkTextInfo} from './plain-input.shared' import type {InternalProps, TextInfo, Selection} from './plain-input' import {stringToUint8Array} from 'uint8array-extras' import {useColorScheme} from 'react-native' diff --git a/shared/common-adapters/plain-input.native.tsx b/shared/common-adapters/plain-input.native.tsx index 5313fb84144d..9eeebbe351d1 100644 --- a/shared/common-adapters/plain-input.native.tsx +++ b/shared/common-adapters/plain-input.native.tsx @@ -10,7 +10,7 @@ import { type TextInputSelectionChangeEventData, } from 'react-native' import {Box2} from './box' -import {checkTextInfo} from './input.shared' +import {checkTextInfo} from './plain-input.shared' import {getTextStyle} from './text.styles' import {isIOS} from '@/constants/platform' import {stringToUint8Array} from 'uint8array-extras' diff --git a/shared/common-adapters/input.shared.tsx b/shared/common-adapters/plain-input.shared.tsx similarity index 100% rename from shared/common-adapters/input.shared.tsx rename to shared/common-adapters/plain-input.shared.tsx diff --git a/shared/common-adapters/profile-card.tsx b/shared/common-adapters/profile-card.tsx index 4296b59566c1..74c84a56d774 100644 --- a/shared/common-adapters/profile-card.tsx +++ b/shared/common-adapters/profile-card.tsx @@ -2,7 +2,7 @@ import * as C from '@/constants' import * as React from 'react' import * as Styles from '@/styles' import * as Platforms from '@/util/platforms' -import * as Tracker from '@/stores/tracker2' +import * as Tracker from '@/stores/tracker' import type * as T from '@/constants/types' import capitalize from 'lodash/capitalize' import {Box2, Box2Measure} from './box' @@ -21,7 +21,7 @@ import WithTooltip from './with-tooltip' import DelayedMounting from './delayed-mounting' import {type default as FollowButtonType} from '../profile/user/actions/follow-button' import type ChatButtonType from '../chat/chat-button' -import {useTrackerState} from '@/stores/tracker2' +import {useTrackerState} from '@/stores/tracker' import type {MeasureRef} from './measure-ref' const positionFallbacks = ['top center', 'bottom center'] as const diff --git a/shared/common-adapters/proof-broken-banner.tsx b/shared/common-adapters/proof-broken-banner.tsx index e1ecfd0cb296..d8dee2c0f03d 100644 --- a/shared/common-adapters/proof-broken-banner.tsx +++ b/shared/common-adapters/proof-broken-banner.tsx @@ -1,7 +1,7 @@ import * as C from '@/constants' import * as React from 'react' import {Banner, BannerParagraph} from './banner' -import {useTrackerState} from '@/stores/tracker2' +import {useTrackerState} from '@/stores/tracker' import {useProfileState} from '@/stores/profile' const Kb = {Banner} diff --git a/shared/common-adapters/usernames.tsx b/shared/common-adapters/usernames.tsx index a9d13dee81d7..daaa73ff5cda 100644 --- a/shared/common-adapters/usernames.tsx +++ b/shared/common-adapters/usernames.tsx @@ -6,7 +6,7 @@ import {backgroundModeIsNegative} from './text.shared' import type {TextType, Background, StylesTextCrossPlatform, AllowedColors, LineClampType, TextTypeBold} from './text.shared' import isArray from 'lodash/isArray' import type {e164ToDisplay as e164ToDisplayType} from '@/util/phone-numbers' -import {useTrackerState} from '@/stores/tracker2' +import {useTrackerState} from '@/stores/tracker' import {useUsersState} from '@/stores/users' import {useProfileState} from '@/stores/profile' import {useFollowerState} from '@/stores/followers' diff --git a/shared/common-adapters/wave-button.tsx b/shared/common-adapters/wave-button.tsx index 5a8de12f9f3f..d0f784408d01 100644 --- a/shared/common-adapters/wave-button.tsx +++ b/shared/common-adapters/wave-button.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import * as React from 'react' import {Box2} from './box' import Icon from './icon' diff --git a/shared/common-adapters/zoomable-image.native.tsx b/shared/common-adapters/zoomable-image.native.tsx index 49c99c978dcf..53753510c90f 100644 --- a/shared/common-adapters/zoomable-image.native.tsx +++ b/shared/common-adapters/zoomable-image.native.tsx @@ -1,7 +1,7 @@ import type {Props} from './zoomable-image' import * as Styles from '@/styles' import * as React from 'react' -import Image2 from './image2.native' +import Image from './image.native' import {View, type LayoutChangeEvent} from 'react-native' import {useSharedValue, runOnJS} from 'react-native-reanimated' import { @@ -78,7 +78,7 @@ const ZoomableImage = React.memo(function ZoomableImage(p: Props) { } else { const size = fitContainer(resolution.width / resolution.height, containerSize) content = ( - { }, } - const namespaces: Array = ['chat2', 'crypto', 'teams', 'people'] + const namespaces: Array = ['chat', 'crypto', 'teams', 'people'] for (const namespace of namespaces) { const store = createTBStore(namespace) const currentState = store.getState() @@ -151,7 +151,7 @@ export const initTeamBuildingCallbacks = () => { defer: { ...currentState.dispatch.defer, ...commonCallbacks, - ...(namespace === 'chat2' + ...(namespace === 'chat' ? { onFinishedTeamBuildingChat: users => { storeRegistry.getState('chat').dispatch.onTeamBuildingFinished(users) @@ -623,9 +623,9 @@ export const initSharedSubscriptions = () => { const prev = old.navState as Util.NavState if (prev === next) return - const namespaces = ['chat2', 'crypto', 'teams', 'people'] as const + const namespaces = ['chat', 'crypto', 'teams', 'people'] as const const namespaceToRoute = new Map([ - ['chat2', 'chatNewChat'], + ['chat', 'chatNewChat'], ['crypto', 'cryptoTeamBuilder'], ['teams', 'teamsTeamBuilder'], ['people', 'peopleTeamBuilder'], @@ -751,7 +751,7 @@ export const _onEngineIncoming = (action: EngineGen.Actions) => { const {useTeamsState} = require('@/stores/teams') as typeof UseTeamsStateType useTeamsState.getState().dispatch.onEngineIncomingImpl(action) - const {useChatState} = require('@/stores/chat2') as typeof UseChatStateType + const {useChatState} = require('@/stores/chat') as typeof UseChatStateType useChatState.getState().dispatch.onEngineIncomingImpl(action) } break @@ -862,7 +862,7 @@ export const _onEngineIncoming = (action: EngineGen.Actions) => { case EngineGen.chat1NotifyChatChatSetConvRetention: case EngineGen.chat1NotifyChatChatSetTeamRetention: { - const {useChatState} = require('@/stores/chat2') as typeof UseChatStateType + const {useChatState} = require('@/stores/chat') as typeof UseChatStateType useChatState.getState().dispatch.onEngineIncomingImpl(action) } break @@ -883,7 +883,7 @@ export const _onEngineIncoming = (action: EngineGen.Actions) => { case EngineGen.keybase1NotifyTrackingTrackingChanged: { const {isTracking, username} = action.payload.params useFollowerState.getState().dispatch.updateFollowing(username, isTracking) - const {useTrackerState} = require('@/stores/tracker2') as typeof UseTracker2StateType + const {useTrackerState} = require('@/stores/tracker') as typeof UseTracker2StateType useTrackerState.getState().dispatch.onEngineIncomingImpl(action) break } @@ -909,7 +909,7 @@ export const _onEngineIncoming = (action: EngineGen.Actions) => { case EngineGen.keybase1Identify3UiIdentify3UpdateUserCard: case EngineGen.keybase1Identify3UiIdentify3Summary: { - const {useTrackerState} = require('@/stores/tracker2') as typeof UseTracker2StateType + const {useTrackerState} = require('@/stores/tracker') as typeof UseTracker2StateType useTrackerState.getState().dispatch.onEngineIncomingImpl(action) } { diff --git a/shared/constants/router2.tsx b/shared/constants/router.tsx similarity index 98% rename from shared/constants/router2.tsx rename to shared/constants/router.tsx index 5da352fedcb7..e222c047efdb 100644 --- a/shared/constants/router2.tsx +++ b/shared/constants/router.tsx @@ -10,7 +10,7 @@ import { type NavigationState, } from '@react-navigation/core' import type {NavigateAppendType, RouteKeys, RootParamList as KBRootParamList} from '@/router-v2/route-params' -import type {GetOptionsRet} from './types/router2' +import type {GetOptionsRet} from './types/router' import {produce} from 'immer' import isEqual from 'lodash/isEqual' import {isMobile, isTablet} from './platform' @@ -411,7 +411,7 @@ export const appendPeopleBuilder = () => { } export const appendNewChatBuilder = () => { - navigateAppend({props: {namespace: 'chat2', title: 'New chat'}, selected: 'chatNewChat'}) + navigateAppend({props: {namespace: 'chat', title: 'New chat'}, selected: 'chatNewChat'}) } // Unless you're within the add members wizard you probably should use `TeamsGen.startAddMembersWizard` instead diff --git a/shared/constants/strings.tsx b/shared/constants/strings.tsx index cceff363d6c6..3413393ed3d8 100644 --- a/shared/constants/strings.tsx +++ b/shared/constants/strings.tsx @@ -1,6 +1,6 @@ import * as Platforms from './platform' import type * as T from './types' -import {conversationIDKeyToString} from './types/chat2/common' +import {conversationIDKeyToString} from './types/chat/common' export const refreshNotificationsWaitingKey = 'settingsTabs.refreshNotifications' export const addEmailWaitingKey = 'settings:addEmail' @@ -26,8 +26,8 @@ export const waitingKeyChatUnpin = (conversationIDKey: T.Chat.ConversationIDKey) export const waitingKeyChatMutualTeams = (conversationIDKey: T.Chat.ConversationIDKey) => `chat:mutualTeams:${conversationIDKeyToString(conversationIDKey)}` -export const waitingKeyTracker = 'tracker2:waitingKey' -export const waitingKeyTrackerProfileLoad = 'tracker2:profileLoad' +export const waitingKeyTracker = 'tracker:waitingKey' +export const waitingKeyTrackerProfileLoad = 'tracker:profileLoad' export const waitingKeyProvision = 'provision:waiting' export const waitingKeyProvisionForgotUsername = 'provision:forgotUsername' diff --git a/shared/constants/types/chat2/common.tsx b/shared/constants/types/chat/common.tsx similarity index 100% rename from shared/constants/types/chat2/common.tsx rename to shared/constants/types/chat/common.tsx diff --git a/shared/constants/types/chat2/index.tsx b/shared/constants/types/chat/index.tsx similarity index 100% rename from shared/constants/types/chat2/index.tsx rename to shared/constants/types/chat/index.tsx diff --git a/shared/constants/types/chat2/message.tsx b/shared/constants/types/chat/message.tsx similarity index 99% rename from shared/constants/types/chat2/message.tsx rename to shared/constants/types/chat/message.tsx index 16a90713c8bb..3926461a22fa 100644 --- a/shared/constants/types/chat2/message.tsx +++ b/shared/constants/types/chat/message.tsx @@ -365,7 +365,7 @@ export interface MessageSystemUsersAddedToConversation extends _MessageCommon { } // If you add a message type here, you'll probably want to check -// `deletableByDeleteHistory` stuff in constants/chat2/message +// `deletableByDeleteHistory` stuff in constants/chat/message export type Message = | MessageAttachment | MessageDeleted diff --git a/shared/constants/types/chat2/meta.tsx b/shared/constants/types/chat/meta.tsx similarity index 100% rename from shared/constants/types/chat2/meta.tsx rename to shared/constants/types/chat/meta.tsx diff --git a/shared/constants/types/index.tsx b/shared/constants/types/index.tsx index 9802de6cf896..bdf94f6bb687 100644 --- a/shared/constants/types/index.tsx +++ b/shared/constants/types/index.tsx @@ -1,5 +1,5 @@ export * as FS from './fs' -export * as Chat from './chat2' +export * as Chat from './chat' export type * as Config from './config' export type * as Crypto from './crypto' export * as Devices from './devices' @@ -13,7 +13,7 @@ export type * as RPCGregor from './rpc-gregor-gen' export * as RPCStellar from './rpc-stellar-gen' export * as TB from './team-building' export * as Teams from './teams' -export type * as Tracker from './tracker2' +export type * as Tracker from './tracker' export type * as Users from './users' export type * as Waiting from './waiting' export * as Wallets from './wallets' diff --git a/shared/constants/types/push.tsx b/shared/constants/types/push.tsx index ea0e33b9a4cc..5cbaf7eeb54c 100644 --- a/shared/constants/types/push.tsx +++ b/shared/constants/types/push.tsx @@ -1,4 +1,4 @@ -import type * as ChatTypes from './chat2' +import type * as ChatTypes from './chat' import type * as RPCChatTypes from './rpc-chat-gen' export type PushNotification = diff --git a/shared/constants/types/router2.tsx b/shared/constants/types/router.tsx similarity index 100% rename from shared/constants/types/router2.tsx rename to shared/constants/types/router.tsx diff --git a/shared/constants/types/team-building.tsx b/shared/constants/types/team-building.tsx index d542c8ed30da..119d8ae041b9 100644 --- a/shared/constants/types/team-building.tsx +++ b/shared/constants/types/team-building.tsx @@ -1,6 +1,6 @@ import type {ServiceId as _ServiceId} from '@/util/platforms' -export const allowedNamespace = ['chat2', 'crypto', 'teams', 'people', 'invalid'] as const +export const allowedNamespace = ['chat', 'crypto', 'teams', 'people', 'invalid'] as const export type AllowedNamespace = (typeof allowedNamespace)[number] export type FollowingState = 'Following' | 'NotFollowing' | 'NoState' | 'You' export type ServiceId = _ServiceId diff --git a/shared/constants/types/teams.tsx b/shared/constants/types/teams.tsx index ee365158a280..f76046fd61f1 100644 --- a/shared/constants/types/teams.tsx +++ b/shared/constants/types/teams.tsx @@ -1,5 +1,5 @@ import type * as RPCTypes from './rpc-gen' -import type {ConversationIDKey} from './chat2' +import type {ConversationIDKey} from './chat' export type TeamID = string export const stringToTeamID = (s: string): TeamID => s diff --git a/shared/constants/types/tracker2.tsx b/shared/constants/types/tracker.tsx similarity index 100% rename from shared/constants/types/tracker2.tsx rename to shared/constants/types/tracker.tsx diff --git a/shared/crypto/output.tsx b/shared/crypto/output.tsx index 1555b82232c0..c01157790fe8 100644 --- a/shared/crypto/output.tsx +++ b/shared/crypto/output.tsx @@ -1,5 +1,5 @@ import * as C from '@/constants' -import * as Chat from '@/stores/chat2' +import * as Chat from '@/stores/chat' import * as Crypto from '@/stores/crypto' import * as Kb from '@/common-adapters' import * as Path from '@/util/path' diff --git a/shared/crypto/sub-nav/left-nav.desktop.tsx b/shared/crypto/sub-nav/left-nav.desktop.tsx index 8b0f7b87065a..dcf4d7a7bb01 100644 --- a/shared/crypto/sub-nav/left-nav.desktop.tsx +++ b/shared/crypto/sub-nav/left-nav.desktop.tsx @@ -43,12 +43,12 @@ const SubNav = (props: Props) => { - diff --git a/shared/crypto/sub-nav/nav-row.tsx b/shared/crypto/sub-nav/nav-row.tsx index 62bcb8b6c6ed..d3f3cc991bcc 100644 --- a/shared/crypto/sub-nav/nav-row.tsx +++ b/shared/crypto/sub-nav/nav-row.tsx @@ -25,7 +25,7 @@ const NavRow = (props: Props) => { hover_background_color_blueGreyDark: !isSelected, })} > - , plat: string, arch: string) { } const subdir = plat === 'darwin' ? 'Keybase.app/Contents/Resources' : 'resources' const dir = path.join(appPaths[0]!, subdir, 'app/desktop/dist') - const modules = ['node', 'main', 'tracker2', 'menubar', 'unlock-folders', 'pinentry'] + const modules = ['node', 'main', 'tracker', 'menubar', 'unlock-folders', 'pinentry'] const files = [ ...modules.map(p => p + '.bundle.js'), ...modules.filter(p => p !== 'node').map(p => p + '.html'), diff --git a/shared/desktop/remote/component-loader.desktop.tsx b/shared/desktop/remote/component-loader.desktop.tsx index 5f358975f713..b391ade8d127 100644 --- a/shared/desktop/remote/component-loader.desktop.tsx +++ b/shared/desktop/remote/component-loader.desktop.tsx @@ -19,7 +19,7 @@ const {closeWindow, showInactive} = KB2.functions disableDragDrop() module.hot?.accept() -type RemoteComponents = 'unlock-folders' | 'menubar' | 'pinentry' | 'tracker2' +type RemoteComponents = 'unlock-folders' | 'menubar' | 'pinentry' | 'tracker' type Props = { child: (p: DeserializeProps) => React.ReactNode diff --git a/shared/desktop/remote/proxies.desktop.tsx b/shared/desktop/remote/proxies.desktop.tsx index 9e4fd5117a7d..fa311c66ec53 100644 --- a/shared/desktop/remote/proxies.desktop.tsx +++ b/shared/desktop/remote/proxies.desktop.tsx @@ -1,5 +1,5 @@ import RemoteMenubar from '@/menubar/remote-proxy.desktop' -import RemoteProfile from '@/tracker2/remote-proxy.desktop' +import RemoteProfile from '@/tracker/remote-proxy.desktop' import RemotePinentry from '@/pinentry/remote-proxy.desktop' import RemoteUnlockFolders from '@/unlock-folders/remote-proxy.desktop' diff --git a/shared/desktop/renderer/main2.desktop.tsx b/shared/desktop/renderer/main2.desktop.tsx index c42d21a86e53..21a28144698b 100644 --- a/shared/desktop/renderer/main2.desktop.tsx +++ b/shared/desktop/renderer/main2.desktop.tsx @@ -16,7 +16,7 @@ import {useConfigState} from '@/stores/config' import {usePinentryState} from '@/stores/pinentry' import * as T from '@/constants/types' import {RPCError} from '@/util/errors' -import {switchTab} from '@/constants/router2' +import {switchTab} from '@/constants/router' import {storeRegistry} from '@/stores/store-registry' import {onEngineConnected, onEngineDisconnected} from '@/constants/init/index.desktop' import {handleAppLink} from '@/constants/deeplinks' @@ -164,19 +164,19 @@ const eventFromRemoteWindows = (action: RemoteGen.Actions) => { break } case RemoteGen.trackerChangeFollow: { - storeRegistry.getState('tracker2').dispatch.changeFollow(action.payload.guiID, action.payload.follow) + storeRegistry.getState('tracker').dispatch.changeFollow(action.payload.guiID, action.payload.follow) break } case RemoteGen.trackerIgnore: { - storeRegistry.getState('tracker2').dispatch.ignore(action.payload.guiID) + storeRegistry.getState('tracker').dispatch.ignore(action.payload.guiID) break } case RemoteGen.trackerCloseTracker: { - storeRegistry.getState('tracker2').dispatch.closeTracker(action.payload.guiID) + storeRegistry.getState('tracker').dispatch.closeTracker(action.payload.guiID) break } case RemoteGen.trackerLoad: { - storeRegistry.getState('tracker2').dispatch.load(action.payload) + storeRegistry.getState('tracker').dispatch.load(action.payload) break } case RemoteGen.link: diff --git a/shared/desktop/webpack.config.babel.js b/shared/desktop/webpack.config.babel.js index b14d20a5fbf0..c73e4827bfba 100644 --- a/shared/desktop/webpack.config.babel.js +++ b/shared/desktop/webpack.config.babel.js @@ -301,7 +301,7 @@ ${htmlWebpackPlugin.options.isDev && name === 'main' ? '`).join('\n')} + ${(htmlWebpackPlugin.files.js ?? []).map((js: string) => ``).join('\n')} `, }) @@ -277,11 +305,13 @@ ${htmlWebpackPlugin.options.isDev && name === 'main' ? '` : ''} + +
+
+
+ + ${(files.js ?? []).map((js: string) => ``).join('\n')} + +` + +const makeViewPlugins = ({ + fileSuffix, + isDev, + isHot, + names, +}: { + fileSuffix: string + isDev: boolean + isHot: boolean + names: Array +}): Array => [ + ...(debugUnusedChunks + ? [ + new webpack.optimize.LimitChunkCountPlugin({ + maxChunks: 1, + }), + ] + : []), + new webpack.DefinePlugin({ + global: 'globalThis', + 'process.env.NODE_DEBUG': JSON.stringify(process.env['NODE_DEBUG']), + }), + ...(isHot ? [new ReactRefreshWebpackPlugin({forceEnable: true})] : []), + ...names.map( + (name: string) => + new HtmlWebpackPlugin({ + chunks: [name], + filename: `${name}${fileSuffix}.html`, + inject: false, + isDev, + name, + templateContent: ({htmlWebpackPlugin}) => + renderHtmlTemplate({ + files: htmlWebpackPlugin.files, + isDev: htmlWebpackPlugin.options.isDev, + name, + }), + }) + ), +] + +const config = (_: unknown, {mode}: {mode?: 'development' | 'none' | 'production'}): Array => { + const isDev = mode !== 'production' + const isHot = isDev && !!process.env['HOT'] + const isProfile = !isDev && !!process.env['PROFILE'] + const fileSuffix = isDev ? '.dev' : isProfile ? '.profile' : '' + const publicPath = isHot ? devServerDistURL : '../dist/' + const alias = makeAlias(isDev) + const defines = makeDefineValues(isDev, isHot, isProfile, fileSuffix) + + if (isProfile) { + console.warn('*** Webpack profiling on ***') } + logWebpackDebug('Flags:', {isDev, isHot, isProfile}) + logWebpackDebug('Detected electron from package.json:', elecVersion) + logWebpackDebug('Injecting defines:', defines) + const commonConfig: Configuration = { bail: true, - context: path.resolve(__dirname, '..'), + cache: { + type: 'filesystem', + buildDependencies: { + config: [configPath, babelConfigPath], + }, + }, + context: rootDir, devtool: evalDevtools ? 'eval' : isDev ? 'cheap-module-source-map' : 'source-map', mode: isDev ? 'development' : 'production', node: false, + optimization: makeCommonOptimization(isDev, isProfile), output: { filename: `[name]${fileSuffix}.bundle.js`, - path: path.resolve(__dirname, 'dist'), + path: distDir, publicPath, }, plugins: [ @@ -204,34 +359,11 @@ const config = (_: unknown, {mode}: {mode?: 'development' | 'none' | 'production alias, extensions: ['.desktop.js', '.desktop.tsx', '.web.js', '.js', '.jsx', '.tsx', '.ts', '.json'], }, - ...(isDev - ? {} - : { - optimization: { - minimizer: [ - new TerserPlugin({ - parallel: true, - terserOptions: { - parse: {ecma: 2020}, - compress: { - comparisons: false, - ecma: 2020, - inline: 2, - }, - keep_fnames: true, - keep_classnames: true, - mangle: false, - output: {comments: false}, - }, - }), - ], - }, - }), } const nodeConfig: Configuration = merge(commonConfig, { entry: {node: './desktop/app/node.desktop.tsx'}, - module: {rules: makeRules(true)}, + module: {rules: makeRules({isDev, isHot, nodeThread: true})}, name: 'node', plugins: [ // Ensure the view layer doesn't bleed into the node layer @@ -243,110 +375,39 @@ const config = (_: unknown, {mode}: {mode?: 'development' | 'none' | 'production target: 'electron-main', }) - const makeViewPlugins = (names: Array): Array => - [ - ...(debugUnusedChunks - ? [ - new webpack.optimize.LimitChunkCountPlugin({ - maxChunks: 1, - }), - ] - : []), - // needed to help webpack and electron renderer - new webpack.DefinePlugin({ - global: 'globalThis', - 'process.env.NODE_DEBUG': JSON.stringify(process.env['NODE_DEBUG']), - }), - ...(isHot ? [new ReactRefreshWebpackPlugin({forceEnable: true})] : []), - ...names.map( - (name: string) => - new HtmlWebpackPlugin({ - chunks: [name], - filename: `${name}${fileSuffix}.html`, - inject: false, - isDev, - name, - templateContent: ({htmlWebpackPlugin}) => ` - - - - ${htmlWebpackPlugin.options.isDev ? 'Keybase DEV' : 'Keybase'} - - -${htmlWebpackPlugin.options.isDev && name === 'main' ? '' : ''} - -
-
-
- - ${(htmlWebpackPlugin.files.js ?? []).map((js: string) => ``).join('\n')} - - `, - }) - ), - ].filter(Boolean) - - // just keeping main in its old place - const entryOverride: Record = {main: 'desktop/renderer'} - - // multiple entries so we can chunk shared parts - const entries = debugUnusedChunks ? ['main'] : ['main', 'menubar', 'pinentry', 'unlock-folders', 'tracker'] - const viewConfig: DesktopDevServerConfiguration = merge( - commonConfig as DesktopDevServerConfiguration, - { + const viewConfig: DesktopConfiguration = merge(commonConfig as DesktopConfiguration, { devServer: { compress: false, hot: isHot, - port: 4000, + port: devServerPort, devMiddleware: { - publicPath: 'http://localhost:4000/dist', + publicPath: devServerDistURL.slice(0, -1), }, client: { overlay: true, webSocketURL: { hostname: 'localhost', pathname: '/ws', - port: 4000, + port: devServerPort, }, }, - static: { - directory: path.resolve(__dirname, 'dist'), - publicPath: '/dist', - }, + static: [ + {directory: distDir, publicPath: devServerDistPath}, + {directory: rootDir, publicPath: '/', watch: false}, + ], }, - entry: entries.reduce>((map, name: string) => { + entry: viewEntries.reduce>((map, name: string) => { map[name] = `./${entryOverride[name] || name}/main.desktop.tsx` return map }, {}), - module: {rules: makeRules(false)}, + module: {rules: makeRules({isDev, isHot, nodeThread: false})}, name: 'renderer', ...(isHot ? {} : { - optimization: { - splitChunks: {chunks: 'all'}, - ...(debugUnusedChunks ? {usedExports: true} : {}), - }, + optimization: makeRendererOptimization(isDev, isProfile), }), - plugins: makeViewPlugins(entries), + plugins: makeViewPlugins({fileSuffix, isDev, isHot, names: viewEntries}), resolve: { alias: { ...alias, @@ -355,11 +416,10 @@ ${htmlWebpackPlugin.options.isDev && name === 'main' ? '