diff --git a/src/App.native.tsx b/src/App.native.tsx index e2fcd6d2ec..c6334379f7 100644 --- a/src/App.native.tsx +++ b/src/App.native.tsx @@ -1,6 +1,6 @@ import 'react-native-url-polyfill/auto' -import 'lib/sentry' // must be near top -import 'view/icons' +import '#/lib/sentry' // must be near top +import '#/view/icons' import React, {useEffect, useState} from 'react' import {GestureHandlerRootView} from 'react-native-gesture-handler' diff --git a/src/components/ProfileHoverCard/index.web.tsx b/src/components/ProfileHoverCard/index.web.tsx index 3854ee014c..4cda42fdbe 100644 --- a/src/components/ProfileHoverCard/index.web.tsx +++ b/src/components/ProfileHoverCard/index.web.tsx @@ -5,15 +5,15 @@ import {flip, offset, shift, size, useFloating} from '@floating-ui/react-dom' import {msg, plural} from '@lingui/macro' import {useLingui} from '@lingui/react' +import {isTouchDevice} from '#/lib/browser' import {getModerationCauseKey} from '#/lib/moderation' import {makeProfileLink} from '#/lib/routes/links' import {sanitizeDisplayName} from '#/lib/strings/display-names' import {sanitizeHandle} from '#/lib/strings/handles' +import {useProfileShadow} from '#/state/cache/profile-shadow' import {useModerationOpts} from '#/state/preferences/moderation-opts' import {usePrefetchProfileQuery, useProfileQuery} from '#/state/queries/profile' import {useSession} from '#/state/session' -import {isTouchDevice} from 'lib/browser' -import {useProfileShadow} from 'state/cache/profile-shadow' import {formatCount} from '#/view/com/util/numeric/format' import {UserAvatar} from '#/view/com/util/UserAvatar' import {ProfileHeaderHandle} from '#/screens/Profile/Header/Handle' diff --git a/src/components/StarterPack/Wizard/WizardEditListDialog.tsx b/src/components/StarterPack/Wizard/WizardEditListDialog.tsx index f7b0aba344..a848cd5b92 100644 --- a/src/components/StarterPack/Wizard/WizardEditListDialog.tsx +++ b/src/components/StarterPack/Wizard/WizardEditListDialog.tsx @@ -7,9 +7,9 @@ import {BottomSheetFlatListMethods} from '@discord/bottom-sheet' import {msg, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' -import {useInitialNumToRender} from 'lib/hooks/useInitialNumToRender' -import {isWeb} from 'platform/detection' -import {useSession} from 'state/session' +import {useInitialNumToRender} from '#/lib/hooks/useInitialNumToRender' +import {isWeb} from '#/platform/detection' +import {useSession} from '#/state/session' import {WizardAction, WizardState} from '#/screens/StarterPack/Wizard/State' import {atoms as a, native, useTheme, web} from '#/alf' import {Button, ButtonText} from '#/components/Button' diff --git a/src/components/forms/Toggle.tsx b/src/components/forms/Toggle.tsx index 6dc387b239..4e3695bbf2 100644 --- a/src/components/forms/Toggle.tsx +++ b/src/components/forms/Toggle.tsx @@ -2,8 +2,8 @@ import React from 'react' import {Pressable, View, ViewStyle} from 'react-native' import Animated, {LinearTransition} from 'react-native-reanimated' +import {HITSLOP_10} from '#/lib/constants' import {isNative} from '#/platform/detection' -import {HITSLOP_10} from 'lib/constants' import { atoms as a, flatten, diff --git a/src/components/intents/VerifyEmailIntentDialog.tsx b/src/components/intents/VerifyEmailIntentDialog.tsx index e8c63af82f..28af61fedc 100644 --- a/src/components/intents/VerifyEmailIntentDialog.tsx +++ b/src/components/intents/VerifyEmailIntentDialog.tsx @@ -3,7 +3,7 @@ import {View} from 'react-native' import {msg, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' -import {useAgent, useSession} from 'state/session' +import {useAgent, useSession} from '#/state/session' import {atoms as a} from '#/alf' import {Button, ButtonText} from '#/components/Button' import * as Dialog from '#/components/Dialog' diff --git a/src/lib/api/index.ts b/src/lib/api/index.ts index 1e51c7f255..45276506c2 100644 --- a/src/lib/api/index.ts +++ b/src/lib/api/index.ts @@ -13,6 +13,8 @@ import { RichText, } from '@atproto/api' +import {isNetworkError} from '#/lib/strings/errors' +import {shortenLinks, stripInvalidMentions} from '#/lib/strings/rich-text-manip' import {logger} from '#/logger' import {ComposerImage, compressImage} from '#/state/gallery' import {writePostgateRecord} from '#/state/queries/postgate' @@ -22,8 +24,6 @@ import { threadgateAllowUISettingToAllowRecordValue, writeThreadgateRecord, } from '#/state/queries/threadgate' -import {isNetworkError} from 'lib/strings/errors' -import {shortenLinks, stripInvalidMentions} from 'lib/strings/rich-text-manip' import {LinkMeta} from '../link-meta/link-meta' import {uploadBlob} from './upload-blob' diff --git a/src/lib/media/picker.shared.ts b/src/lib/media/picker.shared.ts index b959ce8be9..85539a833e 100644 --- a/src/lib/media/picker.shared.ts +++ b/src/lib/media/picker.shared.ts @@ -4,7 +4,7 @@ import { MediaTypeOptions, } from 'expo-image-picker' -import * as Toast from 'view/com/util/Toast' +import * as Toast from '#/view/com/util/Toast' import {getDataUriSize} from './util' export async function openPicker(opts?: ImagePickerOptions) { diff --git a/src/locale/helpers.ts b/src/locale/helpers.ts index a7517eae9b..eb60fc5cf4 100644 --- a/src/locale/helpers.ts +++ b/src/locale/helpers.ts @@ -2,7 +2,7 @@ import {AppBskyFeedDefs, AppBskyFeedPost} from '@atproto/api' import * as bcp47Match from 'bcp-47-match' import lande from 'lande' -import {hasProp} from 'lib/type-guards' +import {hasProp} from '#/lib/type-guards' import { AppLanguage, LANGUAGES_MAP_CODE2, diff --git a/src/screens/E2E/SharedPreferencesTesterScreen.tsx b/src/screens/E2E/SharedPreferencesTesterScreen.tsx index 06bf538ea6..3f4ce563be 100644 --- a/src/screens/E2E/SharedPreferencesTesterScreen.tsx +++ b/src/screens/E2E/SharedPreferencesTesterScreen.tsx @@ -1,7 +1,7 @@ import React from 'react' import {View} from 'react-native' -import {ScrollView} from 'view/com/util/Views' +import {ScrollView} from '#/view/com/util/Views' import {atoms as a} from '#/alf' import {Button, ButtonText} from '#/components/Button' import {Text} from '#/components/Typography' diff --git a/src/screens/List/ListHiddenScreen.tsx b/src/screens/List/ListHiddenScreen.tsx index e4354337a8..a694cbb837 100644 --- a/src/screens/List/ListHiddenScreen.tsx +++ b/src/screens/List/ListHiddenScreen.tsx @@ -5,18 +5,18 @@ import {msg, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' import {useQueryClient} from '@tanstack/react-query' +import {useGoBack} from '#/lib/hooks/useGoBack' +import {sanitizeHandle} from '#/lib/strings/handles' import {logger} from '#/logger' import {RQKEY_ROOT as listQueryRoot} from '#/state/queries/list' -import {useGoBack} from 'lib/hooks/useGoBack' -import {sanitizeHandle} from 'lib/strings/handles' -import {useListBlockMutation, useListMuteMutation} from 'state/queries/list' +import {useListBlockMutation, useListMuteMutation} from '#/state/queries/list' import { UsePreferencesQueryResponse, useRemoveFeedMutation, -} from 'state/queries/preferences' -import {useSession} from 'state/session' -import * as Toast from 'view/com/util/Toast' -import {CenteredView} from 'view/com/util/Views' +} from '#/state/queries/preferences' +import {useSession} from '#/state/session' +import * as Toast from '#/view/com/util/Toast' +import {CenteredView} from '#/view/com/util/Views' import {atoms as a, useBreakpoints, useTheme} from '#/alf' import {Button, ButtonIcon, ButtonText} from '#/components/Button' import {EyeSlash_Stroke2_Corner0_Rounded as EyeSlash} from '#/components/icons/EyeSlash' diff --git a/src/screens/StarterPack/StarterPackScreen.tsx b/src/screens/StarterPack/StarterPackScreen.tsx index 0aa863f7b2..e3d32a1dd5 100644 --- a/src/screens/StarterPack/StarterPackScreen.tsx +++ b/src/screens/StarterPack/StarterPackScreen.tsx @@ -15,35 +15,35 @@ import {useNavigation} from '@react-navigation/native' import {NativeStackScreenProps} from '@react-navigation/native-stack' import {useQueryClient} from '@tanstack/react-query' +import {batchedUpdates} from '#/lib/batchedUpdates' +import {HITSLOP_20} from '#/lib/constants' +import {isBlockedOrBlocking, isMuted} from '#/lib/moderation/blocked-and-muted' +import {makeProfileLink, makeStarterPackLink} from '#/lib/routes/links' +import {CommonNavigatorParams, NavigationProp} from '#/lib/routes/types' +import {logEvent} from '#/lib/statsig/statsig' import {cleanError} from '#/lib/strings/errors' +import {getStarterPackOgCard} from '#/lib/strings/starter-pack' import {logger} from '#/logger' +import {isWeb} from '#/platform/detection' +import {updateProfileShadow} from '#/state/cache/profile-shadow' +import {useModerationOpts} from '#/state/preferences/moderation-opts' +import {getAllListMembers} from '#/state/queries/list-members' +import {useResolvedStarterPackShortLink} from '#/state/queries/resolve-short-link' +import {useResolveDidQuery} from '#/state/queries/resolve-uri' +import {useShortenLink} from '#/state/queries/shorten-link' import {useDeleteStarterPackMutation} from '#/state/queries/starter-packs' +import {useStarterPackQuery} from '#/state/queries/starter-packs' +import {useAgent, useSession} from '#/state/session' +import {useLoggedOutViewControls} from '#/state/shell/logged-out' import { ProgressGuideAction, useProgressGuideControls, } from '#/state/shell/progress-guide' -import {batchedUpdates} from 'lib/batchedUpdates' -import {HITSLOP_20} from 'lib/constants' -import {isBlockedOrBlocking, isMuted} from 'lib/moderation/blocked-and-muted' -import {makeProfileLink, makeStarterPackLink} from 'lib/routes/links' -import {CommonNavigatorParams, NavigationProp} from 'lib/routes/types' -import {logEvent} from 'lib/statsig/statsig' -import {getStarterPackOgCard} from 'lib/strings/starter-pack' -import {isWeb} from 'platform/detection' -import {updateProfileShadow} from 'state/cache/profile-shadow' -import {useModerationOpts} from 'state/preferences/moderation-opts' -import {getAllListMembers} from 'state/queries/list-members' -import {useResolvedStarterPackShortLink} from 'state/queries/resolve-short-link' -import {useResolveDidQuery} from 'state/queries/resolve-uri' -import {useShortenLink} from 'state/queries/shorten-link' -import {useStarterPackQuery} from 'state/queries/starter-packs' -import {useAgent, useSession} from 'state/session' -import {useLoggedOutViewControls} from 'state/shell/logged-out' -import {useSetActiveStarterPack} from 'state/shell/starter-pack' +import {useSetActiveStarterPack} from '#/state/shell/starter-pack' +import {PagerWithHeader} from '#/view/com/pager/PagerWithHeader' +import {ProfileSubpageHeader} from '#/view/com/profile/ProfileSubpageHeader' import * as Toast from '#/view/com/util/Toast' -import {PagerWithHeader} from 'view/com/pager/PagerWithHeader' -import {ProfileSubpageHeader} from 'view/com/profile/ProfileSubpageHeader' -import {CenteredView} from 'view/com/util/Views' +import {CenteredView} from '#/view/com/util/Views' import {bulkWriteFollows} from '#/screens/Onboarding/util' import {atoms as a, useBreakpoints, useTheme} from '#/alf' import {Button, ButtonIcon, ButtonText} from '#/components/Button' diff --git a/src/screens/StarterPack/Wizard/index.tsx b/src/screens/StarterPack/Wizard/index.tsx index 29ef44ee09..65a3500f62 100644 --- a/src/screens/StarterPack/Wizard/index.tsx +++ b/src/screens/StarterPack/Wizard/index.tsx @@ -19,32 +19,32 @@ import {useLingui} from '@lingui/react' import {useFocusEffect, useNavigation} from '@react-navigation/native' import {NativeStackScreenProps} from '@react-navigation/native-stack' -import {logger} from '#/logger' -import {HITSLOP_10, STARTER_PACK_MAX_SIZE} from 'lib/constants' -import {createSanitizedDisplayName} from 'lib/moderation/create-sanitized-display-name' -import {CommonNavigatorParams, NavigationProp} from 'lib/routes/types' -import {logEvent} from 'lib/statsig/statsig' -import {sanitizeDisplayName} from 'lib/strings/display-names' -import {sanitizeHandle} from 'lib/strings/handles' -import {enforceLen} from 'lib/strings/helpers' +import {HITSLOP_10, STARTER_PACK_MAX_SIZE} from '#/lib/constants' +import {createSanitizedDisplayName} from '#/lib/moderation/create-sanitized-display-name' +import {CommonNavigatorParams, NavigationProp} from '#/lib/routes/types' +import {logEvent} from '#/lib/statsig/statsig' +import {sanitizeDisplayName} from '#/lib/strings/display-names' +import {sanitizeHandle} from '#/lib/strings/handles' +import {enforceLen} from '#/lib/strings/helpers' import { getStarterPackOgCard, parseStarterPackUri, -} from 'lib/strings/starter-pack' -import {isAndroid, isNative, isWeb} from 'platform/detection' -import {useModerationOpts} from 'state/preferences/moderation-opts' -import {useAllListMembersQuery} from 'state/queries/list-members' -import {useProfileQuery} from 'state/queries/profile' +} from '#/lib/strings/starter-pack' +import {logger} from '#/logger' +import {isAndroid, isNative, isWeb} from '#/platform/detection' +import {useModerationOpts} from '#/state/preferences/moderation-opts' +import {useAllListMembersQuery} from '#/state/queries/list-members' +import {useProfileQuery} from '#/state/queries/profile' import { useCreateStarterPackMutation, useEditStarterPackMutation, useStarterPackQuery, -} from 'state/queries/starter-packs' -import {useSession} from 'state/session' -import {useSetMinimalShellMode} from 'state/shell' +} from '#/state/queries/starter-packs' +import {useSession} from '#/state/session' +import {useSetMinimalShellMode} from '#/state/shell' import * as Toast from '#/view/com/util/Toast' -import {UserAvatar} from 'view/com/util/UserAvatar' -import {CenteredView} from 'view/com/util/Views' +import {UserAvatar} from '#/view/com/util/UserAvatar' +import {CenteredView} from '#/view/com/util/Views' import {useWizardState, WizardStep} from '#/screens/StarterPack/Wizard/State' import {StepDetails} from '#/screens/StarterPack/Wizard/StepDetails' import {StepFeeds} from '#/screens/StarterPack/Wizard/StepFeeds' diff --git a/src/view/com/composer/ExternalEmbed.tsx b/src/view/com/composer/ExternalEmbed.tsx index f61d410dfc..f48e50cfd7 100644 --- a/src/view/com/composer/ExternalEmbed.tsx +++ b/src/view/com/composer/ExternalEmbed.tsx @@ -1,10 +1,10 @@ import React from 'react' import {StyleProp, View, ViewStyle} from 'react-native' -import {ExternalEmbedDraft} from 'lib/api/index' -import {Gif} from 'state/queries/tenor' -import {ExternalEmbedRemoveBtn} from 'view/com/composer/ExternalEmbedRemoveBtn' -import {ExternalLinkEmbed} from 'view/com/util/post-embeds/ExternalLinkEmbed' +import {ExternalEmbedDraft} from '#/lib/api/index' +import {Gif} from '#/state/queries/tenor' +import {ExternalEmbedRemoveBtn} from '#/view/com/composer/ExternalEmbedRemoveBtn' +import {ExternalLinkEmbed} from '#/view/com/util/post-embeds/ExternalLinkEmbed' import {atoms as a, useTheme} from '#/alf' import {Loader} from '#/components/Loader' import {Text} from '#/components/Typography' diff --git a/src/view/com/composer/text-input/TextInput.web.tsx b/src/view/com/composer/text-input/TextInput.web.tsx index acec61516a..77f69fa890 100644 --- a/src/view/com/composer/text-input/TextInput.web.tsx +++ b/src/view/com/composer/text-input/TextInput.web.tsx @@ -13,15 +13,15 @@ import {Text as TiptapText} from '@tiptap/extension-text' import {generateJSON} from '@tiptap/html' import {EditorContent, JSONContent, useEditor} from '@tiptap/react' +import {useColorSchemeStyle} from '#/lib/hooks/useColorSchemeStyle' import {usePalette} from '#/lib/hooks/usePalette' +import {blobToDataUri, isUriImage} from '#/lib/media/util' import {useActorAutocompleteFn} from '#/state/queries/actor-autocomplete' -import {useColorSchemeStyle} from 'lib/hooks/useColorSchemeStyle' -import {blobToDataUri, isUriImage} from 'lib/media/util' -import {textInputWebEmitter} from '#/view/com/composer/text-input/textInputWebEmitter' import { LinkFacetMatch, suggestLinkCardUri, -} from 'view/com/composer/text-input/text-input-util' +} from '#/view/com/composer/text-input/text-input-util' +import {textInputWebEmitter} from '#/view/com/composer/text-input/textInputWebEmitter' import {atoms as a, useAlf} from '#/alf' import {Portal} from '#/components/Portal' import {normalizeTextStyles} from '#/components/Typography' diff --git a/src/view/com/notifications/FeedItem.tsx b/src/view/com/notifications/FeedItem.tsx index 3c1f51249f..53e3561fab 100644 --- a/src/view/com/notifications/FeedItem.tsx +++ b/src/view/com/notifications/FeedItem.tsx @@ -1,4 +1,10 @@ -import React, {memo, useEffect, useMemo, useState} from 'react' +import React, { + memo, + type ReactElement, + useEffect, + useMemo, + useState, +} from 'react' import { Animated, Pressable, @@ -17,7 +23,7 @@ import { } from '@atproto/api' import {AtUri} from '@atproto/api' import {TID} from '@atproto/common-web' -import {msg, plural, Trans} from '@lingui/macro' +import {msg, Plural, plural, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' import {useNavigation} from '@react-navigation/native' import {useQueryClient} from '@tanstack/react-query' @@ -164,7 +170,30 @@ let FeedItem = ({ ) } - let action = '' + const formattedCount = + authors.length > 1 ? formatCount(i18n, authors.length - 1) : '' + const firstAuthorName = sanitizeDisplayName( + authors[0].profile.displayName || authors[0].profile.handle, + ) + const niceTimestamp = niceDate(i18n, item.notification.indexedAt) + + let a11yAuthor = firstAuthorName + let author = ( + + {forceLTR(firstAuthorName)} + + } + disableMismatchWarning + /> + ) + + let a11yLabel = '' + let action: ReactElement let icon = ( ) + if (item.type === 'post-like') { - action = _(msg`liked your post`) + a11yLabel = + authors.length > 1 + ? _( + msg`${a11yAuthor} and ${plural(authors.length - 1, { + one: `${formattedCount} other`, + other: `${formattedCount} others`, + })} liked your post`, + ) + : _(msg`${a11yAuthor} liked your post`) + action = + authors.length > 1 ? ( + + {author} and{' '} + + + {' '} + liked your post + + ) : ( + {author} liked your post + ) } else if (item.type === 'repost') { - action = _(msg`reposted your post`) + a11yLabel = + authors.length > 1 + ? _( + msg`${a11yAuthor} and ${plural(authors.length - 1, { + one: `${formattedCount} other`, + other: `${formattedCount} others`, + })} reposted your post`, + ) + : _(msg`${a11yAuthor} reposted your post`) + action = + authors.length > 1 ? ( + + {author} and{' '} + + + {' '} + reposted your post + + ) : ( + {author} reposted your post + ) icon = } else if (item.type === 'follow') { let isFollowBack = false @@ -202,39 +280,120 @@ let FeedItem = ({ } if (isFollowBack) { - action = _(msg`followed you back`) + a11yLabel = + authors.length > 1 + ? _( + msg`${a11yAuthor} and ${plural(authors.length - 1, { + one: `${formattedCount} other`, + other: `${formattedCount} others`, + })} followed you back`, + ) + : _(msg`${a11yAuthor} followed you back`) + action = + authors.length > 1 ? ( + + {author} and{' '} + + + {' '} + followed you back + + ) : ( + {author} followed you back + ) } else { - action = _(msg`followed you`) + a11yLabel = + authors.length > 1 + ? _( + msg`${a11yAuthor} and ${plural(authors.length - 1, { + one: `${formattedCount} other`, + other: `${formattedCount} others`, + })} followed you`, + ) + : _(msg`${a11yAuthor} followed you`) + action = + authors.length > 1 ? ( + + {author} and{' '} + + + {' '} + followed you + + ) : ( + {author} followed you + ) } icon = } else if (item.type === 'feedgen-like') { - action = _(msg`liked your custom feed`) + a11yLabel = + authors.length > 1 + ? _( + msg`${a11yAuthor} and ${plural(authors.length - 1, { + one: `${formattedCount} other`, + other: `${formattedCount} others`, + })} liked your custom feed`, + ) + : _(msg`${a11yAuthor} liked your custom feed`) + action = + authors.length > 1 ? ( + + {author} and{' '} + + + {' '} + liked your custom feed + + ) : ( + {author} liked your custom feed + ) } else if (item.type === 'starterpack-joined') { + a11yLabel = + authors.length > 1 + ? _( + msg`${a11yAuthor} and ${plural(authors.length - 1, { + one: `${formattedCount} other`, + other: `${formattedCount} others`, + })} signed up with your starter pack`, + ) + : _(msg`${a11yAuthor} signed up with your starter pack`) + action = + authors.length > 1 ? ( + + {author} and{' '} + + + {' '} + signed up with your starter pack + + ) : ( + {author} signed up with your starter pack + ) icon = ( ) - action = _(msg`signed up with your starter pack`) } else { return null } - - const formattedCount = - authors.length > 1 ? formatCount(i18n, authors.length - 1) : '' - const firstAuthorName = sanitizeDisplayName( - authors[0].profile.displayName || authors[0].profile.handle, - ) - const niceTimestamp = niceDate(i18n, item.notification.indexedAt) - const a11yLabelUsers = - authors.length > 1 - ? _(msg` and `) + - plural(authors.length - 1, { - one: `${formattedCount} other`, - other: `${formattedCount} others`, - }) - : '' - const a11yLabel = `${firstAuthorName}${a11yLabelUsers} ${action} ${niceTimestamp}` + a11yLabel += ` ยท ${niceTimestamp}` return ( - - {forceLTR(firstAuthorName)} - - } - disableMismatchWarning - /> - {authors.length > 1 ? ( - <> - - {' '} - and{' '} - - - {plural(authors.length - 1, { - one: `${formattedCount} other`, - other: `${formattedCount} others`, - })} - - - ) : undefined} - {action} + {action} {({timeElapsed}) => ( - - {' ' + timeElapsed} - + <> + · + + {timeElapsed} + + )}