From c68c4f9e8c910c145ac460b0bacdc83328277643 Mon Sep 17 00:00:00 2001 From: Chris Nojima Date: Wed, 25 Mar 2026 14:00:11 -0400 Subject: [PATCH 1/3] merge small utils down --- shared/chat/conversation/bot/install.tsx | 2 +- shared/chat/conversation/bottom-banner.tsx | 2 +- .../conversation/messages/cards/new-chat.tsx | 2 +- .../messages/message-popup/text.tsx | 2 +- .../text/unfurl/unfurl-list/image/index.tsx | 2 +- .../text/unfurl/unfurl-list/map-popup.tsx | 2 +- shared/chat/location-map.desktop.tsx | 2 +- shared/chat/location-map.native.tsx | 2 +- shared/chat/punycode-link-warning.tsx | 2 +- shared/common-adapters/text-url.desktop.tsx | 2 +- shared/common-adapters/text-url.native.tsx | 2 +- shared/common-adapters/web-view.native.tsx | 2 +- shared/constants/init/index.desktop.tsx | 2 +- shared/constants/init/index.native.tsx | 2 +- shared/crypto/input.tsx | 2 +- shared/crypto/operations/encrypt.tsx | 2 +- shared/crypto/operations/sign.tsx | 2 +- shared/crypto/output.tsx | 2 +- shared/fs/banner/conflict-banner.tsx | 2 +- shared/git/row.tsx | 2 +- shared/menubar/index.desktop.tsx | 2 +- shared/people/announcement.tsx | 2 +- shared/people/todo.tsx | 2 +- shared/profile/generic/enter-username.tsx | 2 +- shared/profile/post-proof.tsx | 2 +- shared/provision/error.tsx | 2 +- shared/router-v2/tab-bar.desktop.tsx | 2 +- shared/settings/about.tsx | 2 +- shared/settings/archive/modal.tsx | 2 +- shared/signup/common.tsx | 2 +- shared/stores/convostate.tsx | 2 +- shared/stores/fs.tsx | 2 +- shared/stores/profile.tsx | 2 +- shared/stores/settings.tsx | 4 +- shared/stores/teams.tsx | 2 +- shared/teams/emojis/add-emoji.tsx | 2 +- shared/teams/new-team/index.tsx | 2 +- shared/tracker/assertion.tsx | 2 +- shared/tracker/index.desktop.tsx | 2 +- shared/util/clear-logs.android.tsx | 5 - shared/util/clear-logs.d.ts | 2 - shared/util/clear-logs.desktop.tsx | 5 - shared/util/clear-logs.ios.tsx | 3 - shared/util/hidden-string.d.ts | 7 -- shared/util/hidden-string.desktop.tsx | 3 - shared/util/hidden-string.native.tsx | 3 - ...dden-string-impl.tsx => hidden-string.tsx} | 5 +- shared/util/misc.desktop.tsx | 93 +++++++++++++++++++ shared/util/misc.native.tsx | 53 +++++++++++ shared/util/notify-popup.d.ts | 10 -- shared/util/notify-popup.desktop.tsx | 54 ----------- shared/util/notify-popup.native.tsx | 11 --- shared/util/open-url.d.ts | 1 - shared/util/open-url.desktop.tsx | 3 - shared/util/open-url.native.tsx | 11 --- shared/util/pick-files.d.ts | 5 - shared/util/pick-files.desktop.tsx | 25 ----- shared/util/pick-files.native.tsx | 17 ---- shared/util/sms.d.ts | 3 - shared/util/sms.desktop.tsx | 6 -- shared/util/sms.native.tsx | 12 --- 61 files changed, 188 insertions(+), 229 deletions(-) delete mode 100644 shared/util/clear-logs.android.tsx delete mode 100644 shared/util/clear-logs.d.ts delete mode 100644 shared/util/clear-logs.desktop.tsx delete mode 100644 shared/util/clear-logs.ios.tsx delete mode 100644 shared/util/hidden-string.d.ts delete mode 100644 shared/util/hidden-string.desktop.tsx delete mode 100644 shared/util/hidden-string.native.tsx rename shared/util/{hidden-string-impl.tsx => hidden-string.tsx} (88%) create mode 100644 shared/util/misc.desktop.tsx create mode 100644 shared/util/misc.native.tsx delete mode 100644 shared/util/notify-popup.d.ts delete mode 100644 shared/util/notify-popup.desktop.tsx delete mode 100644 shared/util/notify-popup.native.tsx delete mode 100644 shared/util/open-url.d.ts delete mode 100644 shared/util/open-url.desktop.tsx delete mode 100644 shared/util/open-url.native.tsx delete mode 100644 shared/util/pick-files.d.ts delete mode 100644 shared/util/pick-files.desktop.tsx delete mode 100644 shared/util/pick-files.native.tsx delete mode 100644 shared/util/sms.d.ts delete mode 100644 shared/util/sms.desktop.tsx delete mode 100644 shared/util/sms.native.tsx diff --git a/shared/chat/conversation/bot/install.tsx b/shared/chat/conversation/bot/install.tsx index d630b9ff2f8f..10270a8d9aca 100644 --- a/shared/chat/conversation/bot/install.tsx +++ b/shared/chat/conversation/bot/install.tsx @@ -5,7 +5,7 @@ import * as Teams from '@/stores/teams' import * as React from 'react' import {useModalHeaderState} from '@/stores/modal-header' import ChannelPicker from './channel-picker' -import openURL from '@/util/open-url' +import {openURL} from '@/util/misc' import * as T from '@/constants/types' import {useBotsState} from '@/stores/bots' import {useAllChannelMetas} from '@/teams/common/channel-hooks' diff --git a/shared/chat/conversation/bottom-banner.tsx b/shared/chat/conversation/bottom-banner.tsx index c199e96d1920..c98694933a77 100644 --- a/shared/chat/conversation/bottom-banner.tsx +++ b/shared/chat/conversation/bottom-banner.tsx @@ -2,7 +2,7 @@ import * as C from '@/constants' import * as Chat from '@/stores/chat' import * as Kb from '@/common-adapters' import type * as React from 'react' -import _openSMS from '@/util/sms' +import {openSMS as _openSMS} from '@/util/misc' import {assertionToDisplay} from '@/common-adapters/usernames' import {useUsersState} from '@/stores/users' import {useFollowerState} from '@/stores/followers' diff --git a/shared/chat/conversation/messages/cards/new-chat.tsx b/shared/chat/conversation/messages/cards/new-chat.tsx index 83967f359bd3..6a5649705217 100644 --- a/shared/chat/conversation/messages/cards/new-chat.tsx +++ b/shared/chat/conversation/messages/cards/new-chat.tsx @@ -1,5 +1,5 @@ import * as Kb from '@/common-adapters' -import openUrl from '@/util/open-url' +import {openURL as openUrl} from '@/util/misc' type Props = { self: boolean diff --git a/shared/chat/conversation/messages/message-popup/text.tsx b/shared/chat/conversation/messages/message-popup/text.tsx index c881ed0ed518..e9e45f5062df 100644 --- a/shared/chat/conversation/messages/message-popup/text.tsx +++ b/shared/chat/conversation/messages/message-popup/text.tsx @@ -6,7 +6,7 @@ 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 {openURL} from '@/util/misc' import {useCurrentUserState} from '@/stores/current-user' type OwnProps = { 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 2f89f84fa900..fff9f4e7ccae 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 @@ -3,7 +3,7 @@ import * as Kb from '@/common-adapters/index' import * as Chat from '@/stores/chat' import {maxWidth} from '@/chat/conversation/messages/attachment/shared' import {Video} from './video' -import openURL from '@/util/open-url' +import {openURL} from '@/util/misc' export type Props = { autoplayVideo: boolean 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 2c01367b4233..ea33152c125d 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 @@ -2,7 +2,7 @@ import * as C from '@/constants' import * as Chat from '@/stores/chat' import * as Kb from '@/common-adapters/index' import type * as T from '@/constants/types' -import openURL from '@/util/open-url' +import {openURL} from '@/util/misc' import LocationMap from '@/chat/location-map' import {useConfigState} from '@/stores/config' diff --git a/shared/chat/location-map.desktop.tsx b/shared/chat/location-map.desktop.tsx index 8756672cb596..cb6757ef214d 100644 --- a/shared/chat/location-map.desktop.tsx +++ b/shared/chat/location-map.desktop.tsx @@ -1,6 +1,6 @@ import * as React from 'react' import * as Kb from '@/common-adapters' -import openURL from '@/util/open-url' +import {openURL} from '@/util/misc' type Props = { height: number diff --git a/shared/chat/location-map.native.tsx b/shared/chat/location-map.native.tsx index d563f370ecf5..0a459b9b2f24 100644 --- a/shared/chat/location-map.native.tsx +++ b/shared/chat/location-map.native.tsx @@ -1,6 +1,6 @@ import * as React from 'react' import * as Kb from '@/common-adapters' -import openURL from '@/util/open-url' +import {openURL} from '@/util/misc' type Props = { height: number diff --git a/shared/chat/punycode-link-warning.tsx b/shared/chat/punycode-link-warning.tsx index ca0a08af4c4c..60a193f5b7f8 100644 --- a/shared/chat/punycode-link-warning.tsx +++ b/shared/chat/punycode-link-warning.tsx @@ -1,6 +1,6 @@ import * as Kb from '@/common-adapters' import {useSafeNavigation} from '@/util/safe-navigation' -import openURL from '@/util/open-url' +import {openURL} from '@/util/misc' type PunycodeLinkWarningProps = { display: string diff --git a/shared/common-adapters/text-url.desktop.tsx b/shared/common-adapters/text-url.desktop.tsx index b97636272d18..c72177810150 100644 --- a/shared/common-adapters/text-url.desktop.tsx +++ b/shared/common-adapters/text-url.desktop.tsx @@ -1,4 +1,4 @@ -import openURL from '@/util/open-url' +import {openURL} from '@/util/misc' import KB2 from '@/util/electron.desktop' const {showContextMenu} = KB2.functions diff --git a/shared/common-adapters/text-url.native.tsx b/shared/common-adapters/text-url.native.tsx index 24c0cd8931d5..a453b5cc46ca 100644 --- a/shared/common-adapters/text-url.native.tsx +++ b/shared/common-adapters/text-url.native.tsx @@ -1,4 +1,4 @@ -import openURL from '@/util/open-url' +import {openURL} from '@/util/misc' import * as Clipboard from 'expo-clipboard' import {Alert} from 'react-native' diff --git a/shared/common-adapters/web-view.native.tsx b/shared/common-adapters/web-view.native.tsx index b09dc28adf4e..551145b9bbf4 100644 --- a/shared/common-adapters/web-view.native.tsx +++ b/shared/common-adapters/web-view.native.tsx @@ -3,7 +3,7 @@ import * as React from 'react' import * as Styles from '@/styles' import LoadingStateView from './loading-state-view' import memoize from 'lodash/memoize' -import openURL from '@/util/open-url' +import {openURL} from '@/util/misc' import type {WebViewInjections, WebViewProps} from './web-view' import {View as NativeView} from 'react-native' import {WebView as NativeWebView} from 'react-native-webview' diff --git a/shared/constants/init/index.desktop.tsx b/shared/constants/init/index.desktop.tsx index 5e8db44b10eb..2b8e4f929246 100644 --- a/shared/constants/init/index.desktop.tsx +++ b/shared/constants/init/index.desktop.tsx @@ -17,7 +17,7 @@ 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 {NotifyPopup} from '@/util/misc' import {noKBFSFailReason} from '@/constants/config' import {initSharedSubscriptions, _onEngineIncoming} from './shared' import {wrapErrors} from '@/util/debug' diff --git a/shared/constants/init/index.native.tsx b/shared/constants/init/index.native.tsx index cd5c6f5d82f0..14a145c5aee8 100644 --- a/shared/constants/init/index.native.tsx +++ b/shared/constants/init/index.native.tsx @@ -16,7 +16,7 @@ import * as ExpoLocation from 'expo-location' import * as ExpoTaskManager from 'expo-task-manager' import * as Tabs from '@/constants/tabs' import * as NetInfo from '@react-native-community/netinfo' -import NotifyPopup from '@/util/notify-popup' +import {NotifyPopup} from '@/util/misc' import logger from '@/logger' import {Alert, Linking} from 'react-native' import {isAndroid} from '@/constants/platform.native' diff --git a/shared/crypto/input.tsx b/shared/crypto/input.tsx index 27194ae34fdf..d4901b87d186 100644 --- a/shared/crypto/input.tsx +++ b/shared/crypto/input.tsx @@ -6,7 +6,7 @@ import * as Kb from '@/common-adapters' import * as FS from '@/constants/fs' import type {IconType} from '@/common-adapters/icon.constants-gen' import capitalize from 'lodash/capitalize' -import {pickFiles} from '@/util/pick-files' +import {pickFiles} from '@/util/misc' type CommonProps = { operation: T.Crypto.Operations diff --git a/shared/crypto/operations/encrypt.tsx b/shared/crypto/operations/encrypt.tsx index 62647deb3ad9..7a2f051a276c 100644 --- a/shared/crypto/operations/encrypt.tsx +++ b/shared/crypto/operations/encrypt.tsx @@ -3,7 +3,7 @@ import * as Crypto from '@/stores/crypto' import * as Kb from '@/common-adapters' import * as React from 'react' import Recipients from '../recipients' -import openURL from '@/util/open-url' +import {openURL} from '@/util/misc' import {DragAndDrop, Input, InputActionsBar, OperationBanner} from '../input' import {OutputInfoBanner, OperationOutput, OutputActionsBar, SignedSender} from '../output' diff --git a/shared/crypto/operations/sign.tsx b/shared/crypto/operations/sign.tsx index 94ff3b45bfe7..90baa815c9c9 100644 --- a/shared/crypto/operations/sign.tsx +++ b/shared/crypto/operations/sign.tsx @@ -2,7 +2,7 @@ import * as C from '@/constants' import * as Crypto from '@/stores/crypto' import * as React from 'react' import * as Kb from '@/common-adapters' -import openURL from '@/util/open-url' +import {openURL} from '@/util/misc' import {Input, DragAndDrop, OperationBanner, InputActionsBar} from '../input' import {OutputInfoBanner, OperationOutput, OutputActionsBar, SignedSender} from '../output' diff --git a/shared/crypto/output.tsx b/shared/crypto/output.tsx index 3fbf74537bcc..59f87d96776d 100644 --- a/shared/crypto/output.tsx +++ b/shared/crypto/output.tsx @@ -6,7 +6,7 @@ import * as Path from '@/util/path' import * as React from 'react' import capitalize from 'lodash/capitalize' import type * as T from '@/constants/types' -import {pickFiles} from '@/util/pick-files' +import {pickFiles} from '@/util/misc' import type HiddenString from '@/util/hidden-string' import {useFSState} from '@/stores/fs' import * as FS from '@/constants/fs' diff --git a/shared/fs/banner/conflict-banner.tsx b/shared/fs/banner/conflict-banner.tsx index 11e15f14dfbb..612fbc340b2a 100644 --- a/shared/fs/banner/conflict-banner.tsx +++ b/shared/fs/banner/conflict-banner.tsx @@ -1,7 +1,7 @@ import * as C from '@/constants' import * as Kb from '@/common-adapters' import * as T from '@/constants/types' -import openUrl from '@/util/open-url' +import {openURL as openUrl} from '@/util/misc' import {useFSState} from '@/stores/fs' import * as FS from '@/stores/fs' diff --git a/shared/git/row.tsx b/shared/git/row.tsx index 732399891101..8c0e8cf18403 100644 --- a/shared/git/row.tsx +++ b/shared/git/row.tsx @@ -4,7 +4,7 @@ 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 {openURL} from '@/util/misc' import {useTrackerState} from '@/stores/tracker' import * as FS from '@/stores/fs' import {useCurrentUserState} from '@/stores/current-user' diff --git a/shared/menubar/index.desktop.tsx b/shared/menubar/index.desktop.tsx index 4e33a0d76b2b..6015116ac274 100644 --- a/shared/menubar/index.desktop.tsx +++ b/shared/menubar/index.desktop.tsx @@ -10,7 +10,7 @@ import Filename from '@/fs/common/filename' import KB2 from '@/util/electron.desktop' import OutOfDate from './out-of-date' import Upload from '@/fs/footer/upload' -import openUrl from '@/util/open-url' +import {openURL as openUrl} from '@/util/misc' import {Loading} from '@/fs/simple-screens' import {isLinux, isDarwin} from '@/constants/platform' import {type _InnerMenuItem} from '@/common-adapters/floating-menu/menu-layout' diff --git a/shared/people/announcement.tsx b/shared/people/announcement.tsx index 318f66b24265..42943297c1d2 100644 --- a/shared/people/announcement.tsx +++ b/shared/people/announcement.tsx @@ -1,7 +1,7 @@ import * as C from '@/constants' import * as Chat from '@/stores/chat' import * as T from '@/constants/types' -import openURL from '@/util/open-url' +import {openURL} from '@/util/misc' import * as Kb from '@/common-adapters' import PeopleItem from './item' import * as Settings from '@/constants/settings' diff --git a/shared/people/todo.tsx b/shared/people/todo.tsx index eb5b05f36bf9..e0f435f617a0 100644 --- a/shared/people/todo.tsx +++ b/shared/people/todo.tsx @@ -1,7 +1,7 @@ import * as C from '@/constants' import {useTeamsState} from '@/stores/teams' import * as React from 'react' -import openURL from '@/util/open-url' +import {openURL} from '@/util/misc' import type * as T from '@/constants/types' import type {IconType} from '@/common-adapters/icon.constants-gen' import PeopleItem, {type TaskButton} from './item' diff --git a/shared/profile/generic/enter-username.tsx b/shared/profile/generic/enter-username.tsx index 53fa32f555eb..a5e585332046 100644 --- a/shared/profile/generic/enter-username.tsx +++ b/shared/profile/generic/enter-username.tsx @@ -1,6 +1,6 @@ import * as C from '@/constants' import {useProfileState} from '@/stores/profile' -import openURL from '@/util/open-url' +import {openURL} from '@/util/misc' import * as React from 'react' import * as Kb from '@/common-adapters' import {SiteIcon} from './shared' diff --git a/shared/profile/post-proof.tsx b/shared/profile/post-proof.tsx index 7ae448928cea..222dd075bcd8 100644 --- a/shared/profile/post-proof.tsx +++ b/shared/profile/post-proof.tsx @@ -3,7 +3,7 @@ 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 {openURL as openUrl} from '@/util/misc' import Modal from './modal' import {useConfigState} from '@/stores/config' diff --git a/shared/provision/error.tsx b/shared/provision/error.tsx index 8feba1ee03ce..bc85c720d63e 100644 --- a/shared/provision/error.tsx +++ b/shared/provision/error.tsx @@ -3,7 +3,7 @@ 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 {openURL} from '@/util/misc' import * as T from '@/constants/types' import {useProvisionState} from '@/stores/provision' diff --git a/shared/router-v2/tab-bar.desktop.tsx b/shared/router-v2/tab-bar.desktop.tsx index 71c217b19fb6..0ab590004c60 100644 --- a/shared/router-v2/tab-bar.desktop.tsx +++ b/shared/router-v2/tab-bar.desktop.tsx @@ -9,7 +9,7 @@ import * as Tabs from '@/constants/tabs' import * as Common from './common.desktop' import AccountSwitcher from './account-switcher' import RuntimeStats from '../app/runtime-stats' -import openURL from '@/util/open-url' +import {openURL} from '@/util/misc' import {isLinux} from '@/constants/platform' import KB2 from '@/util/electron.desktop' import './tab-bar.css' diff --git a/shared/settings/about.tsx b/shared/settings/about.tsx index 2cf45e1b6f4d..67726b2e94ed 100644 --- a/shared/settings/about.tsx +++ b/shared/settings/about.tsx @@ -1,6 +1,6 @@ import * as C from '@/constants' import * as Kb from '@/common-adapters' -import openUrl from '@/util/open-url' +import {openURL as openUrl} from '@/util/misc' const privacyPolicy = 'https://keybase.io/_/webview/privacypolicy' const terms = 'https://keybase.io/_/webview/terms' diff --git a/shared/settings/archive/modal.tsx b/shared/settings/archive/modal.tsx index 6a66af48aa3b..6ed931e14e2f 100644 --- a/shared/settings/archive/modal.tsx +++ b/shared/settings/archive/modal.tsx @@ -2,7 +2,7 @@ import * as React from 'react' import * as Kb from '@/common-adapters' import * as C from '@/constants' import type * as T from '@/constants/types' -import {pickSave} from '@/util/pick-files' +import {pickSave} from '@/util/misc' import * as FsCommon from '@/fs/common' import {useArchiveState} from '@/stores/archive' import {settingsArchiveTab} from '@/stores/settings' diff --git a/shared/signup/common.tsx b/shared/signup/common.tsx index 0bfd0b91b3e5..977499143645 100644 --- a/shared/signup/common.tsx +++ b/shared/signup/common.tsx @@ -2,7 +2,7 @@ import * as C from '@/constants' import type * as React from 'react' import * as Kb from '@/common-adapters' import {type ButtonProps} from '@/common-adapters/button' -import openURL from '@/util/open-url' +import {openURL} from '@/util/misc' import {useConfigState} from '@/stores/config' type InfoIconProps = { diff --git a/shared/stores/convostate.tsx b/shared/stores/convostate.tsx index 05b2a68dd02d..2b596ac59e3f 100644 --- a/shared/stores/convostate.tsx +++ b/shared/stores/convostate.tsx @@ -36,7 +36,7 @@ import {noConversationIDKey} from '@/constants/types/chat/common' import {type StoreApi, type UseBoundStore, useStore} from 'zustand' import * as Platform from '@/constants/platform' import KB2 from '@/util/electron' -import NotifyPopup from '@/util/notify-popup' +import {NotifyPopup} from '@/util/misc' import {hexToUint8Array} from 'uint8array-extras' import {clearChatTimeCache} from '@/util/timestamp' import {registerDebugClear} from '@/util/debug' diff --git a/shared/stores/fs.tsx b/shared/stores/fs.tsx index 1c90d07ee31e..4f17eedeeab5 100644 --- a/shared/stores/fs.tsx +++ b/shared/stores/fs.tsx @@ -5,7 +5,7 @@ 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 {NotifyPopup} from '@/util/misc' import {RPCError} from '@/util/errors' import logger from '@/logger' import {tlfToPreferredOrder} from '@/util/kbfs' diff --git a/shared/stores/profile.tsx b/shared/stores/profile.tsx index d161bba662db..8ecd5124b00b 100644 --- a/shared/stores/profile.tsx +++ b/shared/stores/profile.tsx @@ -4,7 +4,7 @@ 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 {openURL} from '@/util/misc' import {RPCError} from '@/util/errors' import {fixCrop} from '@/util/crop' import {clearModals, navToProfile, navigateAppend, navigateUp} from '@/constants/router' diff --git a/shared/stores/settings.tsx b/shared/stores/settings.tsx index 6bdf39d6fa62..3ea76ae63d6b 100644 --- a/shared/stores/settings.tsx +++ b/shared/stores/settings.tsx @@ -2,7 +2,7 @@ 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 {openURL} from '@/util/misc' import * as Z from '@/util/zustand' import {RPCError} from '@/util/errors' import * as Tabs from '@/constants/tabs' @@ -105,7 +105,7 @@ export const useSettingsState = Z.createZustand('settings', (set, get) => }, clearLogs: () => { const f = async () => { - const clearLocalLogs = (await import('@/util/clear-logs')).default + const {clearLocalLogs} = await import('@/util/misc') await clearLocalLogs() } ignorePromise(f()) diff --git a/shared/stores/teams.tsx b/shared/stores/teams.tsx index a30e69363cde..47f306dc27d1 100644 --- a/shared/stores/teams.tsx +++ b/shared/stores/teams.tsx @@ -13,7 +13,7 @@ import { import * as Z from '@/util/zustand' import invert from 'lodash/invert' import logger from '@/logger' -import openSMS from '@/util/sms' +import {openSMS} from '@/util/misc' import {RPCError, logError} from '@/util/errors' import {isMobile, isPhone} from '@/constants/platform' import {mapGetEnsureValue} from '@/util/map' diff --git a/shared/teams/emojis/add-emoji.tsx b/shared/teams/emojis/add-emoji.tsx index 4ea477d4b6ce..9704a64990b2 100644 --- a/shared/teams/emojis/add-emoji.tsx +++ b/shared/teams/emojis/add-emoji.tsx @@ -4,7 +4,7 @@ import * as Chat from '@/stores/chat' import * as React from 'react' import * as Kb from '@/common-adapters' import {AliasInput, Modal} from './common' -import {pickImages} from '@/util/pick-files' +import {pickImages} from '@/util/misc' import kebabCase from 'lodash/kebabCase' import {useEmojiState} from './use-emoji' import {HeaderLeftButton} from '@/common-adapters/header-buttons' diff --git a/shared/teams/new-team/index.tsx b/shared/teams/new-team/index.tsx index c67c3d0d535f..90121bb5aed0 100644 --- a/shared/teams/new-team/index.tsx +++ b/shared/teams/new-team/index.tsx @@ -3,7 +3,7 @@ import * as React from 'react' import * as Teams from '@/stores/teams' import * as Kb from '@/common-adapters' import * as T from '@/constants/types' -import openUrl from '@/util/open-url' +import {openURL as openUrl} from '@/util/misc' import upperFirst from 'lodash/upperFirst' const openSubteamInfo = () => openUrl('https://book.keybase.io/docs/teams/design') diff --git a/shared/tracker/assertion.tsx b/shared/tracker/assertion.tsx index a9003597c878..e6f52e841b3b 100644 --- a/shared/tracker/assertion.tsx +++ b/shared/tracker/assertion.tsx @@ -3,7 +3,7 @@ import * as C from '@/constants' 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 {openURL as openUrl} from '@/util/misc' import * as Kb from '@/common-adapters' import {SiteIcon} from '@/profile/generic/shared' import {formatTimeForAssertionPopup} from '@/util/timestamp' diff --git a/shared/tracker/index.desktop.tsx b/shared/tracker/index.desktop.tsx index f5c63d4b5c77..c0d3dcc7ce87 100644 --- a/shared/tracker/index.desktop.tsx +++ b/shared/tracker/index.desktop.tsx @@ -1,6 +1,6 @@ import * as Kb from '@/common-adapters' import type * as T from '@/constants/types' -import openUrl from '@/util/open-url' +import {openURL as openUrl} from '@/util/misc' import {useColorScheme} from 'react-native' export type Props = { diff --git a/shared/util/clear-logs.android.tsx b/shared/util/clear-logs.android.tsx deleted file mode 100644 index d26232e8d3c2..000000000000 --- a/shared/util/clear-logs.android.tsx +++ /dev/null @@ -1,5 +0,0 @@ -const clearLocalLogs = async (): Promise => { - // noop on Android -} - -export default clearLocalLogs diff --git a/shared/util/clear-logs.d.ts b/shared/util/clear-logs.d.ts deleted file mode 100644 index f5892177b7d3..000000000000 --- a/shared/util/clear-logs.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const clearLocalLogs: () => Promise -export default clearLocalLogs diff --git a/shared/util/clear-logs.desktop.tsx b/shared/util/clear-logs.desktop.tsx deleted file mode 100644 index 313cf992564b..000000000000 --- a/shared/util/clear-logs.desktop.tsx +++ /dev/null @@ -1,5 +0,0 @@ -const clearLocalLogs = async (): Promise => { - // noop on desktop -} - -export default clearLocalLogs diff --git a/shared/util/clear-logs.ios.tsx b/shared/util/clear-logs.ios.tsx deleted file mode 100644 index 09eeb765a930..000000000000 --- a/shared/util/clear-logs.ios.tsx +++ /dev/null @@ -1,3 +0,0 @@ -import {clearLocalLogs} from 'react-native-kb' - -export default clearLocalLogs diff --git a/shared/util/hidden-string.d.ts b/shared/util/hidden-string.d.ts deleted file mode 100644 index 9fbe0fdaa805..000000000000 --- a/shared/util/hidden-string.d.ts +++ /dev/null @@ -1,7 +0,0 @@ -export class HiddenString { - constructor(s: string) - stringValue: () => string - equals: (other: HiddenString) => boolean -} - -export default HiddenString diff --git a/shared/util/hidden-string.desktop.tsx b/shared/util/hidden-string.desktop.tsx deleted file mode 100644 index cac487ec110d..000000000000 --- a/shared/util/hidden-string.desktop.tsx +++ /dev/null @@ -1,3 +0,0 @@ -import HiddenString from './hidden-string-impl' -export {HiddenString} -export default HiddenString diff --git a/shared/util/hidden-string.native.tsx b/shared/util/hidden-string.native.tsx deleted file mode 100644 index cac487ec110d..000000000000 --- a/shared/util/hidden-string.native.tsx +++ /dev/null @@ -1,3 +0,0 @@ -import HiddenString from './hidden-string-impl' -export {HiddenString} -export default HiddenString diff --git a/shared/util/hidden-string-impl.tsx b/shared/util/hidden-string.tsx similarity index 88% rename from shared/util/hidden-string-impl.tsx rename to shared/util/hidden-string.tsx index 0ffa2fa6efb3..4f4a9b1033d2 100644 --- a/shared/util/hidden-string-impl.tsx +++ b/shared/util/hidden-string.tsx @@ -1,8 +1,8 @@ // HiddenString tries to wrap a string value to prevent it from being easily -// output as a string to log, file or console +// output as a string to log, file or console. const valueKey = Symbol('valueKey') -class HiddenString { +export class HiddenString { private [valueKey]: string = '' constructor(stringValue: string) { @@ -32,4 +32,3 @@ class HiddenString { } export default HiddenString - diff --git a/shared/util/misc.desktop.tsx b/shared/util/misc.desktop.tsx new file mode 100644 index 000000000000..c2c42d7a0aea --- /dev/null +++ b/shared/util/misc.desktop.tsx @@ -0,0 +1,93 @@ +import logger from '@/logger' +import debounce from 'lodash/debounce' +import KB2, {type OpenDialogOptions, type SaveDialogOptions} from './electron.desktop' + +const {openURL: openURLImpl, showOpenDialog, showSaveDialog} = KB2.functions + +type NotifyPopupOpts = {body?: string; sound?: boolean} + +const rateLimit: {[K in string]: () => void} = {} +const rateLimitPayloads: { + [K in string]: + | { + title: string + opts?: NotifyPopupOpts + onClick?: () => void + } + | undefined +} = {} + +export const openURL = (url?: string) => { + if (!url) { + console.log('Skipping null url click') + return + } + return openURLImpl?.(url) +} + +export const openSMS = async (): Promise => { + console.warn('Attempted to open SMS on desktop') + return Promise.reject(new Error("Can't open SMS on desktop")) +} + +export const clearLocalLogs = async (): Promise => { + // noop on desktop +} + +export const pickImages = async (title: string) => { + if (!showOpenDialog) return [] + const filePaths = await showOpenDialog({ + allowFiles: true, + allowMultiselect: true, + filters: [{extensions: ['jpg', 'png', 'gif'], name: 'Images'}], + title, + }) + return filePaths +} + +export const pickFiles = async (options: OpenDialogOptions) => { + if (!showOpenDialog) return [] + const filePaths = await showOpenDialog(options) + return filePaths +} + +export const pickSave = async (options: SaveDialogOptions): Promise => { + if (!showSaveDialog) return [] as unknown as string + const res = await showSaveDialog(options) + return res +} + +export function NotifyPopup( + title: string, + opts?: NotifyPopupOpts, + rateLimitSeconds: number = -1, + rateLimitKey?: string, + onClick?: () => void, + onClose?: () => void +): void { + const sound = opts?.sound + if (rateLimitSeconds > 0) { + const key = rateLimitKey || title + + if (rateLimit[key]) { + rateLimitPayloads[key] = {onClick, opts, title} + rateLimit[key]() + return + } + + rateLimit[key] = debounce(() => { + if (rateLimitPayloads[key]) { + const {title, opts, onClick} = rateLimitPayloads[key] + rateLimitPayloads[key] = undefined + const notification = new Notification(title, {...opts, silent: !sound}) + notification.onclick = onClick ?? null + notification.onclose = onClose ?? null + } + }, rateLimitSeconds * 1_000) + } + + logger.info('NotifyPopup: creating notification') + const notification = new Notification(title, {...opts, silent: !sound}) + notification.onclick = onClick ?? null + notification.onclose = onClose ?? null +} diff --git a/shared/util/misc.native.tsx b/shared/util/misc.native.tsx new file mode 100644 index 000000000000..78b38e958cef --- /dev/null +++ b/shared/util/misc.native.tsx @@ -0,0 +1,53 @@ +import {isIOS} from '@/constants/platform.native' +import {pickDocumentsAsync} from './expo-document-picker.native' +import {launchImageLibraryAsync} from './expo-image-picker.native' +import type {OpenDialogOptions, SaveDialogOptions} from './electron.desktop' +import * as SMS from 'expo-sms' +import {Linking} from 'react-native' +import {addNotificationRequest, clearLocalLogs as clearLocalLogsNative} from 'react-native-kb' + +type NotifyPopupOpts = {body?: string; sound?: boolean} + +export function openURL(url?: string) { + if (url) { + Linking.openURL(url).catch((err: unknown) => console.warn('An error occurred', err)) + } else { + console.log('Skipping null url click') + } +} + +export const openSMS = async (phonenos: Array, body?: string): Promise => { + return SMS.isAvailableAsync().then(async isAvailable => { + if (!isAvailable) { + throw new Error('SMS not available') + } + return SMS.sendSMSAsync(phonenos, body || '') + }) +} + +export const clearLocalLogs = async (): Promise => { + if (!isIOS) return + return clearLocalLogsNative() +} + +export const pickImages = async (_: string): Promise> => { + const result = await launchImageLibraryAsync('photo') + return result.canceled ? [] : result.assets.map(a => a.uri) +} + +export const pickFiles = async (_options: OpenDialogOptions): Promise> => { + const result = await pickDocumentsAsync(true) + return result.canceled ? [] : result.assets.map(a => a.uri) +} + +export const pickSave = (_options: SaveDialogOptions): Promise => { + throw new Error('No supported platform') +} + +export function NotifyPopup(title: string, _opts?: NotifyPopupOpts): void { + console.log('NotifyPopup: ', title) + addNotificationRequest({ + body: title, + id: Math.floor(Math.random() * 2 ** 32).toString(), + }).catch(() => {}) +} diff --git a/shared/util/notify-popup.d.ts b/shared/util/notify-popup.d.ts deleted file mode 100644 index dc059195f874..000000000000 --- a/shared/util/notify-popup.d.ts +++ /dev/null @@ -1,10 +0,0 @@ -declare function NotifyPopup( - title: string, - opts?: {body?: string; sound?: boolean}, - rateLimitSeconds?: number, - rateLimitKey?: string, - onClick?: () => void, - onClose?: () => void -): void - -export default NotifyPopup diff --git a/shared/util/notify-popup.desktop.tsx b/shared/util/notify-popup.desktop.tsx deleted file mode 100644 index 0db06aa7ebcb..000000000000 --- a/shared/util/notify-popup.desktop.tsx +++ /dev/null @@ -1,54 +0,0 @@ -import logger from '@/logger' -import debounce from 'lodash/debounce' - -type Opts = {body?: string; sound?: boolean} - -const rateLimit: {[K in string]: () => void} = {} -const rateLimitPayloads: { - [K in string]: - | { - title: string - opts?: Opts - onClick?: () => void - } - | undefined -} = {} - -function NotifyPopup( - title: string, - opts?: Opts, - rateLimitSeconds: number = -1, - rateLimitKey?: string, - onClick?: () => void, - onClose?: () => void -): void { - const sound = opts?.sound - if (rateLimitSeconds > 0) { - const key = rateLimitKey || title - - // Exists? just call it to push the time back - if (rateLimit[key]) { - rateLimitPayloads[key] = {onClick: onClick, opts, title} - rateLimit[key]() - return - } else { - // else set it up and call it below - rateLimit[key] = debounce(() => { - if (rateLimitPayloads[key]) { - const {title, opts, onClick} = rateLimitPayloads[key] - rateLimitPayloads[key] = undefined - const notification = new Notification(title, {...opts, silent: !sound}) - notification.onclick = onClick ?? null - notification.onclose = onClose ?? null - } - }, rateLimitSeconds * 1000) - } - } - - logger.info('NotifyPopup: creating notification') - const notification = new Notification(title, {...opts, silent: !sound}) - notification.onclick = onClick ?? null - notification.onclose = onClose ?? null -} - -export default NotifyPopup diff --git a/shared/util/notify-popup.native.tsx b/shared/util/notify-popup.native.tsx deleted file mode 100644 index 0577cc60ee8c..000000000000 --- a/shared/util/notify-popup.native.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import {addNotificationRequest} from 'react-native-kb' - -function NotifyPopup(title: string): void { - console.log('NotifyPopup: ', title) - addNotificationRequest({ - body: title, - id: Math.floor(Math.random() * 2 ** 32).toString(), - }).catch(() => {}) -} - -export default NotifyPopup diff --git a/shared/util/open-url.d.ts b/shared/util/open-url.d.ts deleted file mode 100644 index 5a2946eccc14..000000000000 --- a/shared/util/open-url.d.ts +++ /dev/null @@ -1 +0,0 @@ -export default function openURL(url?: string): void diff --git a/shared/util/open-url.desktop.tsx b/shared/util/open-url.desktop.tsx deleted file mode 100644 index 21e5fa28bd6f..000000000000 --- a/shared/util/open-url.desktop.tsx +++ /dev/null @@ -1,3 +0,0 @@ -import KB2 from './electron.desktop' -const {openURL} = KB2.functions -export default openURL diff --git a/shared/util/open-url.native.tsx b/shared/util/open-url.native.tsx deleted file mode 100644 index 451bef95e5f0..000000000000 --- a/shared/util/open-url.native.tsx +++ /dev/null @@ -1,11 +0,0 @@ -// this MUST not include common-adapters cause it causes circular refs. TODO text shouldn't even have url helper like this. it should -// be some wrapper -import {Linking} from 'react-native' - -export default function openURL(url?: string) { - if (url) { - Linking.openURL(url).catch((err: unknown) => console.warn('An error occurred', err)) - } else { - console.log('Skipping null url click') - } -} diff --git a/shared/util/pick-files.d.ts b/shared/util/pick-files.d.ts deleted file mode 100644 index bb9a52d8a3a1..000000000000 --- a/shared/util/pick-files.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -import type {OpenDialogOptions, SaveDialogOptions} from './electron.desktop' - -export declare function pickImages(title: string): Promise> -export declare function pickFiles(options: OpenDialogOptions): Promise> -export declare function pickSave(options: SaveDialogOptions): Promise diff --git a/shared/util/pick-files.desktop.tsx b/shared/util/pick-files.desktop.tsx deleted file mode 100644 index 7298f57d1bd0..000000000000 --- a/shared/util/pick-files.desktop.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import KB2, {type OpenDialogOptions, type SaveDialogOptions} from './electron.desktop' -const {showOpenDialog, showSaveDialog} = KB2.functions - -export const pickImages = async (title: string) => { - if (!showOpenDialog) return [] - const filePaths = await showOpenDialog({ - allowFiles: true, - allowMultiselect: true, - filters: [{extensions: ['jpg', 'png', 'gif'], name: 'Images'}], - title, - }) - return filePaths -} - -export const pickFiles = async (options: OpenDialogOptions) => { - if (!showOpenDialog) return [] - const filePaths = await showOpenDialog(options) - return filePaths -} - -export const pickSave = async (options: SaveDialogOptions) => { - if (!showSaveDialog) return [] - const res = await showSaveDialog(options) - return res -} diff --git a/shared/util/pick-files.native.tsx b/shared/util/pick-files.native.tsx deleted file mode 100644 index e43d26757e7e..000000000000 --- a/shared/util/pick-files.native.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import {launchImageLibraryAsync} from './expo-image-picker.native' -import {pickDocumentsAsync} from './expo-document-picker.native' -import type {OpenDialogOptions, SaveDialogOptions} from './electron.desktop' - -export const pickImages = async (_: string): Promise> => { - const result = await launchImageLibraryAsync('photo') - return result.canceled ? [] : result.assets.map(a => a.uri) -} - -export const pickFiles = async (_options: OpenDialogOptions): Promise> => { - const result = await pickDocumentsAsync(true) - return result.canceled ? [] : result.assets.map(a => a.uri) -} - -export const pickSave = (_options: SaveDialogOptions) => { - throw new Error('No supported platform') -} diff --git a/shared/util/sms.d.ts b/shared/util/sms.d.ts deleted file mode 100644 index d80c0648a33d..000000000000 --- a/shared/util/sms.d.ts +++ /dev/null @@ -1,3 +0,0 @@ -declare function openSMS(phonenos: Array, body?: string): Promise - -export default openSMS diff --git a/shared/util/sms.desktop.tsx b/shared/util/sms.desktop.tsx deleted file mode 100644 index 112eb35337d8..000000000000 --- a/shared/util/sms.desktop.tsx +++ /dev/null @@ -1,6 +0,0 @@ -const openSMS = async () => { - console.warn('Attempted to open SMS on desktop') - return Promise.reject(new Error("Can't open SMS on desktop")) -} - -export default openSMS diff --git a/shared/util/sms.native.tsx b/shared/util/sms.native.tsx deleted file mode 100644 index 7507c44b1cea..000000000000 --- a/shared/util/sms.native.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import * as SMS from 'expo-sms' - -const openSMS = async (phonenos: Array, body?: string): Promise => { - return SMS.isAvailableAsync().then(async isAvailable => { - if (!isAvailable) { - throw new Error('SMS not available') - } - return SMS.sendSMSAsync(phonenos, body || '') - }) -} - -export default openSMS From 5c71789765d1a7034e769c053f1b154537fd3bca Mon Sep 17 00:00:00 2001 From: chrisnojima Date: Thu, 26 Mar 2026 08:30:13 -0400 Subject: [PATCH 2/3] WIP --- shared/util/hidden-string.tsx | 7 +++---- shared/util/misc.d.ts | 18 ++++++++++++++++++ 2 files changed, 21 insertions(+), 4 deletions(-) create mode 100644 shared/util/misc.d.ts diff --git a/shared/util/hidden-string.tsx b/shared/util/hidden-string.tsx index 4f4a9b1033d2..cbd83a1be668 100644 --- a/shared/util/hidden-string.tsx +++ b/shared/util/hidden-string.tsx @@ -1,10 +1,9 @@ // HiddenString tries to wrap a string value to prevent it from being easily // output as a string to log, file or console. const valueKey = Symbol('valueKey') +type WithValue = {[valueKey]: string} export class HiddenString { - private [valueKey]: string = '' - constructor(stringValue: string) { Object.defineProperty(this, valueKey, { configurable: false, @@ -15,11 +14,11 @@ export class HiddenString { } stringValue() { - return this[valueKey] + return (this as unknown as WithValue)[valueKey] } equals(other: HiddenString) { - return this[valueKey] === other[valueKey] + return (this as unknown as WithValue)[valueKey] === (other as unknown as WithValue)[valueKey] } toString() { diff --git a/shared/util/misc.d.ts b/shared/util/misc.d.ts new file mode 100644 index 000000000000..cb76bc7ea9ac --- /dev/null +++ b/shared/util/misc.d.ts @@ -0,0 +1,18 @@ +import type {OpenDialogOptions, SaveDialogOptions} from './electron.desktop' + +type NotifyPopupOpts = {body?: string; sound?: boolean} + +export declare function openURL(url?: string): void +export declare function openSMS(phonenos: Array, body?: string): Promise +export declare function clearLocalLogs(): Promise +export declare function pickImages(title: string): Promise> +export declare function pickFiles(options: OpenDialogOptions): Promise> +export declare function pickSave(options: SaveDialogOptions): Promise +export declare function NotifyPopup( + title: string, + opts?: NotifyPopupOpts, + rateLimitSeconds?: number, + rateLimitKey?: string, + onClick?: () => void, + onClose?: () => void +): void From 5815048817e232ccbec0491ab607ea8a0c5cb203 Mon Sep 17 00:00:00 2001 From: chrisnojima Date: Thu, 26 Mar 2026 08:31:31 -0400 Subject: [PATCH 3/3] WIP --- shared/teams/emojis/add-emoji.tsx | 2 +- shared/util/misc.desktop.tsx | 2 +- shared/util/misc.native.tsx | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/shared/teams/emojis/add-emoji.tsx b/shared/teams/emojis/add-emoji.tsx index 9704a64990b2..5765e3f3754a 100644 --- a/shared/teams/emojis/add-emoji.tsx +++ b/shared/teams/emojis/add-emoji.tsx @@ -13,7 +13,7 @@ import KB2 from '@/util/electron' const {getPathForFile} = KB2.functions -const pickEmojisPromise = async () => pickImages('Select emoji images to upload') +const pickEmojisPromise = async () => await pickImages('Select emoji images to upload') type Props = { conversationIDKey: T.Chat.ConversationIDKey diff --git a/shared/util/misc.desktop.tsx b/shared/util/misc.desktop.tsx index c2c42d7a0aea..957fd706264e 100644 --- a/shared/util/misc.desktop.tsx +++ b/shared/util/misc.desktop.tsx @@ -17,7 +17,7 @@ const rateLimitPayloads: { | undefined } = {} -export const openURL = (url?: string) => { +export const openURL = async (url?: string) => { if (!url) { console.log('Skipping null url click') return diff --git a/shared/util/misc.native.tsx b/shared/util/misc.native.tsx index 78b38e958cef..d27544e6a9f8 100644 --- a/shared/util/misc.native.tsx +++ b/shared/util/misc.native.tsx @@ -40,7 +40,7 @@ export const pickFiles = async (_options: OpenDialogOptions): Promise a.uri) } -export const pickSave = (_options: SaveDialogOptions): Promise => { +export const pickSave = (_options: SaveDialogOptions): never => { throw new Error('No supported platform') }