From aed4dfdf675b97a3e3d884b390b7741799fbb533 Mon Sep 17 00:00:00 2001 From: "xan.lol" Date: Fri, 17 Apr 2026 14:19:01 -0700 Subject: [PATCH] refactor: stop filling console on web + refactor contacts handling + update video saving methods --- rspack.config.ts | 3 ++ src/alf/typography.tsx | 2 +- .../PostControls/PostMenu/PostMenuItems.tsx | 11 ++--- src/components/Typography.tsx | 3 +- .../contacts/screens/GetContacts.tsx | 2 +- src/components/contacts/state.ts | 2 +- src/lib/media/saveVideoToDevice.ts | 5 ++ src/lib/media/saveVideoToDevice.web.ts | 5 ++ src/platform/contacts/index.ts | 1 + src/platform/contacts/index.web.ts | 47 +++++++++++++++++++ src/platform/ui-text-view/index.ts | 1 + src/platform/ui-text-view/index.web.tsx | 17 +++++++ .../StepFindContactsIntro/index.tsx | 2 +- src/screens/Settings/FindContactsSettings.tsx | 2 +- src/state/appConfig.web.tsx | 38 +++++++++++++++ src/view/com/util/text/Text.tsx | 2 +- 16 files changed, 128 insertions(+), 15 deletions(-) create mode 100644 src/lib/media/saveVideoToDevice.ts create mode 100644 src/lib/media/saveVideoToDevice.web.ts create mode 100644 src/platform/contacts/index.ts create mode 100644 src/platform/contacts/index.web.ts create mode 100644 src/platform/ui-text-view/index.ts create mode 100644 src/platform/ui-text-view/index.web.tsx create mode 100644 src/state/appConfig.web.tsx diff --git a/rspack.config.ts b/rspack.config.ts index 8075db324..b927ccaf6 100644 --- a/rspack.config.ts +++ b/rspack.config.ts @@ -347,6 +347,9 @@ module.exports = { // Don't bundle node built-ins (shouldn't be needed on web) externalsPresets: {node: false}, + node: { + __filename: false, + }, stats: GENERATE_STATS ? 'verbose' : 'normal', diff --git a/src/alf/typography.tsx b/src/alf/typography.tsx index 9b750f153..f4a38a25c 100644 --- a/src/alf/typography.tsx +++ b/src/alf/typography.tsx @@ -4,9 +4,9 @@ import { type TextProps as RNTextProps, type TextStyle, } from 'react-native' -import {UITextView} from 'react-native-uitextview' import createEmojiRegex from 'emoji-regex' +import {UITextView} from '#/platform/ui-text-view' import {type Alf, applyFonts, atoms, flatten} from '#/alf' import {IS_IOS, IS_NATIVE} from '#/env' diff --git a/src/components/PostControls/PostMenu/PostMenuItems.tsx b/src/components/PostControls/PostMenu/PostMenuItems.tsx index 93abfbd0a..aeb1c952f 100644 --- a/src/components/PostControls/PostMenu/PostMenuItems.tsx +++ b/src/components/PostControls/PostMenu/PostMenuItems.tsx @@ -27,8 +27,7 @@ import {useNavigation} from '@react-navigation/native' import {DISCOVER_DEBUG_DIDS} from '#/lib/constants' import {useOpenComposer} from '#/lib/hooks/useOpenComposer' import {useOpenLink} from '#/lib/hooks/useOpenLink' -import {saveVideoToMediaLibrary} from '#/lib/media/manip' -import {downloadVideoWeb} from '#/lib/media/manip.web' +import {saveVideoToDevice} from '#/lib/media/saveVideoToDevice' import {getCurrentRoute} from '#/lib/routes/helpers' import {makeProfileLink} from '#/lib/routes/links' import { @@ -109,7 +108,7 @@ import { import * as Prompt from '#/components/Prompt' import * as Toast from '#/components/Toast' import {useAnalytics} from '#/analytics' -import {IS_INTERNAL, IS_WEB} from '#/env' +import {IS_INTERNAL} from '#/env' let PostMenuItems = ({ post, @@ -617,8 +616,7 @@ let PostMenuItems = ({ Toast.show(l({message: 'Downloading video...', context: 'toast'})) let success - if (IS_WEB) success = await downloadVideoWeb({uri: uri}) - else success = await saveVideoToMediaLibrary({uri: uri}) + success = await saveVideoToDevice({uri: uri}) if (success) Toast.show(l({message: 'Video downloaded', context: 'toast'}), { @@ -636,8 +634,7 @@ let PostMenuItems = ({ Toast.show(l({message: 'Downloading GIF...', context: 'toast'})) let success - if (IS_WEB) success = await downloadVideoWeb({uri: gifEmbed.external.uri}) - else success = await saveVideoToMediaLibrary({uri: gifEmbed.external.uri}) + success = await saveVideoToDevice({uri: gifEmbed.external.uri}) if (success) Toast.show(l({message: 'GIF downloaded', context: 'toast'}), { diff --git a/src/components/Typography.tsx b/src/components/Typography.tsx index 04b5e5499..f1bcf8792 100644 --- a/src/components/Typography.tsx +++ b/src/components/Typography.tsx @@ -1,6 +1,5 @@ -import {UITextView} from 'react-native-uitextview' - import {logger} from '#/logger' +import {UITextView} from '#/platform/ui-text-view' import {atoms as a, type TextStyleProp, useAlf, useTheme, web} from '#/alf' import { childHasEmoji, diff --git a/src/components/contacts/screens/GetContacts.tsx b/src/components/contacts/screens/GetContacts.tsx index 879864b16..5be0298c9 100644 --- a/src/components/contacts/screens/GetContacts.tsx +++ b/src/components/contacts/screens/GetContacts.tsx @@ -1,7 +1,6 @@ import {useContext} from 'react' import {Alert, View} from 'react-native' import {useSafeAreaInsets} from 'react-native-safe-area-context' -import * as Contacts from 'expo-contacts' import type AtpAgent from '@atproto/api' import { type AppBskyActorProfile, @@ -16,6 +15,7 @@ import {useMutation, useQueryClient} from '@tanstack/react-query' import {uploadBlob} from '#/lib/api' import {cleanError, isNetworkError} from '#/lib/strings/errors' import {logger} from '#/logger' +import * as Contacts from '#/platform/contacts' import {findContactsStatusQueryKey} from '#/state/queries/find-contacts' import {useAgent} from '#/state/session' import { diff --git a/src/components/contacts/state.ts b/src/components/contacts/state.ts index 2514a6a65..c0ba408a9 100644 --- a/src/components/contacts/state.ts +++ b/src/components/contacts/state.ts @@ -1,8 +1,8 @@ import {createContext, useContext, useReducer} from 'react' import {type GestureResponderEvent} from 'react-native' -import {type ExistingContact} from 'expo-contacts' import {type CountryCode} from '#/lib/international-telephone-codes' +import {type ExistingContact} from '#/platform/contacts' import type * as bsky from '#/types/bsky' export type Contact = ExistingContact diff --git a/src/lib/media/saveVideoToDevice.ts b/src/lib/media/saveVideoToDevice.ts new file mode 100644 index 000000000..cac529d5f --- /dev/null +++ b/src/lib/media/saveVideoToDevice.ts @@ -0,0 +1,5 @@ +import {saveVideoToMediaLibrary} from './manip' + +export async function saveVideoToDevice({uri}: {uri: string}) { + return await saveVideoToMediaLibrary({uri}) +} diff --git a/src/lib/media/saveVideoToDevice.web.ts b/src/lib/media/saveVideoToDevice.web.ts new file mode 100644 index 000000000..6a949d686 --- /dev/null +++ b/src/lib/media/saveVideoToDevice.web.ts @@ -0,0 +1,5 @@ +import {downloadVideoWeb} from './manip.web' + +export async function saveVideoToDevice({uri}: {uri: string}) { + return await downloadVideoWeb({uri}) +} diff --git a/src/platform/contacts/index.ts b/src/platform/contacts/index.ts new file mode 100644 index 000000000..3e8948f08 --- /dev/null +++ b/src/platform/contacts/index.ts @@ -0,0 +1 @@ +export * from 'expo-contacts' diff --git a/src/platform/contacts/index.web.ts b/src/platform/contacts/index.web.ts new file mode 100644 index 000000000..73f6e2ec7 --- /dev/null +++ b/src/platform/contacts/index.web.ts @@ -0,0 +1,47 @@ +type AccessPrivileges = 'all' | 'limited' | 'none' + +export type ExistingContact = { + id?: string + firstName?: string + lastName?: string + phoneNumbers?: Array<{ + label?: string + number?: string + digits?: string + countryCode?: string + }> + imageAvailable?: boolean +} + +export type PermissionResponse = { + granted: boolean + canAskAgain: boolean + accessPrivileges?: AccessPrivileges +} + +export const Fields = { + FirstName: 'firstName', + LastName: 'lastName', + PhoneNumbers: 'phoneNumbers', + Image: 'image', +} as const + +export async function isAvailableAsync() { + return false +} + +export async function getPermissionsAsync(): Promise { + return { + granted: false, + canAskAgain: false, + accessPrivileges: 'none', + } +} + +export async function requestPermissionsAsync(): Promise { + return await getPermissionsAsync() +} + +export async function getContactsAsync() { + return {data: [] as ExistingContact[]} +} diff --git a/src/platform/ui-text-view/index.ts b/src/platform/ui-text-view/index.ts new file mode 100644 index 000000000..15a53d795 --- /dev/null +++ b/src/platform/ui-text-view/index.ts @@ -0,0 +1 @@ +export {UITextView} from 'react-native-uitextview' diff --git a/src/platform/ui-text-view/index.web.tsx b/src/platform/ui-text-view/index.web.tsx new file mode 100644 index 000000000..22950ce81 --- /dev/null +++ b/src/platform/ui-text-view/index.web.tsx @@ -0,0 +1,17 @@ +import {Text as RNText} from 'react-native' + +type UITextViewProps = React.ComponentProps & { + dataSet?: Record + uiTextView?: boolean +} + +export function UITextView({ + children, + dataSet, + uiTextView: _uiTextView, + ...rest +}: UITextViewProps) { + const textProps = dataSet ? ({...rest, dataSet} as UITextViewProps) : rest + + return {children} +} diff --git a/src/screens/Onboarding/StepFindContactsIntro/index.tsx b/src/screens/Onboarding/StepFindContactsIntro/index.tsx index bc6f309b7..bb5447c67 100644 --- a/src/screens/Onboarding/StepFindContactsIntro/index.tsx +++ b/src/screens/Onboarding/StepFindContactsIntro/index.tsx @@ -1,5 +1,4 @@ import {View} from 'react-native' -import * as Contacts from 'expo-contacts' import {msg} from '@lingui/core/macro' import {useLingui} from '@lingui/react' import {Trans} from '@lingui/react/macro' @@ -7,6 +6,7 @@ import {useQuery} from '@tanstack/react-query' import {urls} from '#/lib/constants' import {useCallOnce} from '#/lib/once' +import * as Contacts from '#/platform/contacts' import {atoms as a} from '#/alf' import {Admonition} from '#/components/Admonition' import {Button, ButtonText} from '#/components/Button' diff --git a/src/screens/Settings/FindContactsSettings.tsx b/src/screens/Settings/FindContactsSettings.tsx index e1973c74f..fb26361fa 100644 --- a/src/screens/Settings/FindContactsSettings.tsx +++ b/src/screens/Settings/FindContactsSettings.tsx @@ -1,6 +1,5 @@ import {useCallback, useEffect, useState} from 'react' import {type ListRenderItemInfo, View} from 'react-native' -import * as Contacts from 'expo-contacts' import { type AppBskyContactDefs, type AppBskyContactGetSyncStatus, @@ -21,6 +20,7 @@ import { } from '#/lib/routes/types' import {cleanError, isNetworkError} from '#/lib/strings/errors' import {logger} from '#/logger' +import * as Contacts from '#/platform/contacts' import { updateProfileShadow, useProfileShadow, diff --git a/src/state/appConfig.web.tsx b/src/state/appConfig.web.tsx new file mode 100644 index 000000000..5f85b4953 --- /dev/null +++ b/src/state/appConfig.web.tsx @@ -0,0 +1,38 @@ +import {createContext, useContext} from 'react' + +type AppConfigResponse = { + liveNow: { + allow: string[] + exceptions: { + did: string + allow: string[] + }[] + } +} + +export const DEFAULT_APP_CONFIG_RESPONSE: AppConfigResponse = { + liveNow: { + allow: [], + exceptions: [], + }, +} + +const Context = createContext(DEFAULT_APP_CONFIG_RESPONSE) + +export function Provider({children}: React.PropsWithChildren<{}>) { + return ( + + {children} + + ) +} + +export async function prefetchAppConfig() {} + +export function useAppConfig() { + const ctx = useContext(Context) + if (!ctx) { + throw new Error('useAppConfig must be used within a Provider') + } + return ctx +} diff --git a/src/view/com/util/text/Text.tsx b/src/view/com/util/text/Text.tsx index 88682d917..d81a8895c 100644 --- a/src/view/com/util/text/Text.tsx +++ b/src/view/com/util/text/Text.tsx @@ -1,10 +1,10 @@ import {useMemo} from 'react' import {StyleSheet, type TextProps} from 'react-native' -import {UITextView} from 'react-native-uitextview' import {lh, s} from '#/lib/styles' import {type TypographyVariant, useTheme} from '#/lib/ThemeContext' import {logger} from '#/logger' +import {UITextView} from '#/platform/ui-text-view' import {applyFonts, useAlf} from '#/alf' import { childHasEmoji, -- 2.51.2