From 9007810cdb5ffc8fbdf8e2a2af6c073b76b318f3 Mon Sep 17 00:00:00 2001 From: Samuel Newman Date: Wed, 10 Apr 2024 23:32:31 +0100 Subject: [PATCH 001/167] Search - only enable queries once tab is active (#3471) * only enable queries once tab is active * remove hasBeenTrue hook * make enabled optional --- src/state/queries/actor-search.ts | 16 ++++++--- src/state/queries/search-posts.ts | 3 ++ src/view/screens/Search/Search.tsx | 58 ++++++++++++++++++++++++------ 3 files changed, 61 insertions(+), 16 deletions(-) diff --git a/src/state/queries/actor-search.ts b/src/state/queries/actor-search.ts index f19916103c..eb065b6cf5 100644 --- a/src/state/queries/actor-search.ts +++ b/src/state/queries/actor-search.ts @@ -5,19 +5,25 @@ import {STALE} from '#/state/queries' import {getAgent} from '#/state/session' const RQKEY_ROOT = 'actor-search' -export const RQKEY = (prefix: string) => [RQKEY_ROOT, prefix] +export const RQKEY = (query: string) => [RQKEY_ROOT, query] -export function useActorSearch(prefix: string) { +export function useActorSearch({ + query, + enabled, +}: { + query: string + enabled?: boolean +}) { return useQuery({ staleTime: STALE.MINUTES.ONE, - queryKey: RQKEY(prefix || ''), + queryKey: RQKEY(query || ''), async queryFn() { const res = await getAgent().searchActors({ - q: prefix, + q: query, }) return res.data.actors }, - enabled: !!prefix, + enabled: enabled && !!query, }) } diff --git a/src/state/queries/search-posts.ts b/src/state/queries/search-posts.ts index ef8b083584..1822577c93 100644 --- a/src/state/queries/search-posts.ts +++ b/src/state/queries/search-posts.ts @@ -19,9 +19,11 @@ const searchPostsQueryKey = ({query, sort}: {query: string; sort?: string}) => [ export function useSearchPostsQuery({ query, sort, + enabled, }: { query: string sort?: 'top' | 'latest' + enabled?: boolean }) { return useInfiniteQuery< AppBskyFeedSearchPosts.OutputSchema, @@ -47,6 +49,7 @@ export function useSearchPostsQuery({ }, initialPageParam: undefined, getNextPageParam: lastPage => lastPage.cursor, + enabled, }) } diff --git a/src/view/screens/Search/Search.tsx b/src/view/screens/Search/Search.tsx index 0f24252ce6..3b06992fc9 100644 --- a/src/view/screens/Search/Search.tsx +++ b/src/view/screens/Search/Search.tsx @@ -195,9 +195,11 @@ type SearchResultSlice = function SearchScreenPostResults({ query, sort, + active, }: { query: string sort?: 'top' | 'latest' + active: boolean }) { const {_} = useLingui() const {currentAccount} = useSession() @@ -216,7 +218,7 @@ function SearchScreenPostResults({ fetchNextPage, isFetchingNextPage, hasNextPage, - } = useSearchPostsQuery({query: augmentedQuery, sort}) + } = useSearchPostsQuery({query: augmentedQuery, sort, enabled: active}) const onPullToRefresh = React.useCallback(async () => { setIsPTR(true) @@ -297,9 +299,19 @@ function SearchScreenPostResults({ ) } -function SearchScreenUserResults({query}: {query: string}) { +function SearchScreenUserResults({ + query, + active, +}: { + query: string + active: boolean +}) { const {_} = useLingui() - const {data: results, isFetched} = useActorSearch(query) + + const {data: results, isFetched} = useActorSearch({ + query, + enabled: active, + }) return isFetched && results ? ( <> @@ -335,6 +347,7 @@ export function SearchScreenInner({ const setDrawerSwipeDisabled = useSetDrawerSwipeDisabled() const {hasSession} = useSession() const {isDesktop} = useWebMediaQueries() + const [activeTab, setActiveTab] = React.useState(0) const {_} = useLingui() const isNewSearch = useGate('new_search') @@ -343,6 +356,7 @@ export function SearchScreenInner({ (index: number) => { setMinimalShellMode(false) setDrawerSwipeDisabled(index > 0) + setActiveTab(index) }, [setDrawerSwipeDisabled, setMinimalShellMode], ) @@ -354,22 +368,38 @@ export function SearchScreenInner({ return [ { title: _(msg`Top`), - component: , + component: ( + + ), }, { title: _(msg`Latest`), - component: , + component: ( + + ), }, { title: _(msg`People`), - component: , + component: ( + + ), }, ] } else { return [ { title: _(msg`People`), - component: , + component: ( + + ), }, ] } @@ -378,23 +408,29 @@ export function SearchScreenInner({ return [ { title: _(msg`Posts`), - component: , + component: ( + + ), }, { title: _(msg`Users`), - component: , + component: ( + + ), }, ] } else { return [ { title: _(msg`Users`), - component: , + component: ( + + ), }, ] } } - }, [hasSession, isNewSearch, _, query]) + }, [hasSession, isNewSearch, _, query, activeTab]) if (hasSession) { return query ? ( From 740cd029d7162a936d16b427201eb8972e365b94 Mon Sep 17 00:00:00 2001 From: Hailey Date: Thu, 11 Apr 2024 15:20:26 -0700 Subject: [PATCH 002/167] Improve Android haptic, offer toggle for haptics in the app (#3482) * improve android haptics, offer toggle for haptics * update haptics.ts * default to false * simplify to `playHaptic` * just leave them as `feedInfo` * use a hook for `playHaptic` * missed one of them --- patches/expo-haptics+12.8.1.md | 11 ++ patches/expo-haptics+12.8.1.patch | 13 ++ src/lib/haptics.ts | 45 ++----- .../Profile/Header/ProfileHeaderLabeler.tsx | 7 +- src/state/persisted/legacy.ts | 3 +- src/state/persisted/schema.ts | 3 + src/state/preferences/disable-haptics.tsx | 42 +++++++ src/state/preferences/index.tsx | 10 +- src/view/com/util/post-ctrls/PostCtrls.tsx | 18 +-- src/view/screens/ProfileFeed.tsx | 30 +++-- src/view/screens/ProfileList.tsx | 114 +++++++++--------- src/view/screens/SavedFeeds.tsx | 52 ++++---- src/view/screens/Settings/index.tsx | 84 +++---------- src/view/shell/bottom-bar/BottomBar.tsx | 7 +- 14 files changed, 235 insertions(+), 204 deletions(-) create mode 100644 patches/expo-haptics+12.8.1.md create mode 100644 patches/expo-haptics+12.8.1.patch create mode 100644 src/state/preferences/disable-haptics.tsx diff --git a/patches/expo-haptics+12.8.1.md b/patches/expo-haptics+12.8.1.md new file mode 100644 index 0000000000..afa7395bc0 --- /dev/null +++ b/patches/expo-haptics+12.8.1.md @@ -0,0 +1,11 @@ +# Expo Haptics Patch + +Whenever we migrated to Expo Haptics, there was a difference between how the previous and new libraries handled the +Android implementation of an iOS "light" haptic. The previous library used the `Vibration` API solely, which does not +have any configuration for intensity of vibration. The `Vibration` API has also been deprecated since SDK 26. See: +https://github.com/mkuczera/react-native-haptic-feedback/blob/master/android/src/main/java/com/mkuczera/vibrateFactory/VibrateWithDuration.java + +Expo Haptics is using `VibrationManager` API on SDK >= 31. See: https://github.com/expo/expo/blob/main/packages/expo-haptics/android/src/main/java/expo/modules/haptics/HapticsModule.kt#L19 +The timing and intensity of their haptic configurations though differs greatly from the original implementation. This +patch uses the new `VibrationManager` API to create the same vibration that would have been seen in the deprecated +`Vibration` API. diff --git a/patches/expo-haptics+12.8.1.patch b/patches/expo-haptics+12.8.1.patch new file mode 100644 index 0000000000..a95b56f3be --- /dev/null +++ b/patches/expo-haptics+12.8.1.patch @@ -0,0 +1,13 @@ +diff --git a/node_modules/expo-haptics/android/src/main/java/expo/modules/haptics/HapticsModule.kt b/node_modules/expo-haptics/android/src/main/java/expo/modules/haptics/HapticsModule.kt +index 26c52af..b949a4c 100644 +--- a/node_modules/expo-haptics/android/src/main/java/expo/modules/haptics/HapticsModule.kt ++++ b/node_modules/expo-haptics/android/src/main/java/expo/modules/haptics/HapticsModule.kt +@@ -42,7 +42,7 @@ class HapticsModule : Module() { + + private fun vibrate(type: HapticsVibrationType) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { +- vibrator.vibrate(VibrationEffect.createWaveform(type.timings, type.amplitudes, -1)) ++ vibrator.vibrate(VibrationEffect.createWaveform(type.oldSDKPattern, intArrayOf(0, 100), -1)) + } else { + @Suppress("DEPRECATION") + vibrator.vibrate(type.oldSDKPattern, -1) diff --git a/src/lib/haptics.ts b/src/lib/haptics.ts index b22d69d703..02940f793d 100644 --- a/src/lib/haptics.ts +++ b/src/lib/haptics.ts @@ -1,47 +1,20 @@ -import { - impactAsync, - ImpactFeedbackStyle, - notificationAsync, - NotificationFeedbackType, - selectionAsync, -} from 'expo-haptics' +import React from 'react' +import {impactAsync, ImpactFeedbackStyle} from 'expo-haptics' import {isIOS, isWeb} from 'platform/detection' +import {useHapticsDisabled} from 'state/preferences/disable-haptics' const hapticImpact: ImpactFeedbackStyle = isIOS ? ImpactFeedbackStyle.Medium : ImpactFeedbackStyle.Light // Users said the medium impact was too strong on Android; see APP-537s -export class Haptics { - static default() { - if (isWeb) { +export function useHaptics() { + const isHapticsDisabled = useHapticsDisabled() + + return React.useCallback(() => { + if (isHapticsDisabled || isWeb) { return } impactAsync(hapticImpact) - } - static impact(type: ImpactFeedbackStyle = hapticImpact) { - if (isWeb) { - return - } - impactAsync(type) - } - static selection() { - if (isWeb) { - return - } - selectionAsync() - } - static notification = (type: 'success' | 'warning' | 'error') => { - if (isWeb) { - return - } - switch (type) { - case 'success': - return notificationAsync(NotificationFeedbackType.Success) - case 'warning': - return notificationAsync(NotificationFeedbackType.Warning) - case 'error': - return notificationAsync(NotificationFeedbackType.Error) - } - } + }, [isHapticsDisabled]) } diff --git a/src/screens/Profile/Header/ProfileHeaderLabeler.tsx b/src/screens/Profile/Header/ProfileHeaderLabeler.tsx index 4d8dbad86c..d0fd5e20bd 100644 --- a/src/screens/Profile/Header/ProfileHeaderLabeler.tsx +++ b/src/screens/Profile/Header/ProfileHeaderLabeler.tsx @@ -10,7 +10,6 @@ import { import {msg, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' -import {Haptics} from '#/lib/haptics' import {isAppLabeler} from '#/lib/moderation' import {pluralize} from '#/lib/strings/helpers' import {logger} from '#/logger' @@ -21,6 +20,7 @@ import {useLikeMutation, useUnlikeMutation} from '#/state/queries/like' import {usePreferencesQuery} from '#/state/queries/preferences' import {useSession} from '#/state/session' import {useAnalytics} from 'lib/analytics/analytics' +import {useHaptics} from 'lib/haptics' import {useProfileShadow} from 'state/cache/profile-shadow' import {ProfileMenu} from '#/view/com/profile/ProfileMenu' import * as Toast from '#/view/com/util/Toast' @@ -64,6 +64,7 @@ let ProfileHeaderLabeler = ({ const {currentAccount, hasSession} = useSession() const {openModal} = useModalControls() const {track} = useAnalytics() + const playHaptic = useHaptics() const cantSubscribePrompt = Prompt.usePromptControl() const isSelf = currentAccount?.did === profile.did @@ -93,7 +94,7 @@ let ProfileHeaderLabeler = ({ return } try { - Haptics.default() + playHaptic() if (likeUri) { await unlikeMod({uri: likeUri}) @@ -114,7 +115,7 @@ let ProfileHeaderLabeler = ({ ) logger.error(`Failed to toggle labeler like`, {message: e.message}) } - }, [labeler, likeUri, likeMod, unlikeMod, track, _]) + }, [labeler, playHaptic, likeUri, unlikeMod, track, likeMod, _]) const onPressEditProfile = React.useCallback(() => { track('ProfileHeader:EditProfileButtonClicked') diff --git a/src/state/persisted/legacy.ts b/src/state/persisted/legacy.ts index fd94a96a24..ca7967cd2e 100644 --- a/src/state/persisted/legacy.ts +++ b/src/state/persisted/legacy.ts @@ -2,7 +2,7 @@ import AsyncStorage from '@react-native-async-storage/async-storage' import {logger} from '#/logger' import {defaults, Schema, schema} from '#/state/persisted/schema' -import {write, read} from '#/state/persisted/store' +import {read, write} from '#/state/persisted/store' /** * The shape of the serialized data from our legacy Mobx store. @@ -113,6 +113,7 @@ export function transform(legacy: Partial): Schema { externalEmbeds: defaults.externalEmbeds, lastSelectedHomeFeed: defaults.lastSelectedHomeFeed, pdsAddressHistory: defaults.pdsAddressHistory, + disableHaptics: defaults.disableHaptics, } } diff --git a/src/state/persisted/schema.ts b/src/state/persisted/schema.ts index 0aefaa4744..67e082a95d 100644 --- a/src/state/persisted/schema.ts +++ b/src/state/persisted/schema.ts @@ -1,4 +1,5 @@ import {z} from 'zod' + import {deviceLocales} from '#/platform/detection' const externalEmbedOptions = ['show', 'hide'] as const @@ -58,6 +59,7 @@ export const schema = z.object({ useInAppBrowser: z.boolean().optional(), lastSelectedHomeFeed: z.string().optional(), pdsAddressHistory: z.array(z.string()).optional(), + disableHaptics: z.boolean().optional(), }) export type Schema = z.infer @@ -93,4 +95,5 @@ export const defaults: Schema = { useInAppBrowser: undefined, lastSelectedHomeFeed: undefined, pdsAddressHistory: [], + disableHaptics: false, } diff --git a/src/state/preferences/disable-haptics.tsx b/src/state/preferences/disable-haptics.tsx new file mode 100644 index 0000000000..af2c55a182 --- /dev/null +++ b/src/state/preferences/disable-haptics.tsx @@ -0,0 +1,42 @@ +import React from 'react' + +import * as persisted from '#/state/persisted' + +type StateContext = boolean +type SetContext = (v: boolean) => void + +const stateContext = React.createContext( + Boolean(persisted.defaults.disableHaptics), +) +const setContext = React.createContext((_: boolean) => {}) + +export function Provider({children}: {children: React.ReactNode}) { + const [state, setState] = React.useState( + Boolean(persisted.get('disableHaptics')), + ) + + const setStateWrapped = React.useCallback( + (hapticsEnabled: persisted.Schema['disableHaptics']) => { + setState(Boolean(hapticsEnabled)) + persisted.write('disableHaptics', hapticsEnabled) + }, + [setState], + ) + + React.useEffect(() => { + return persisted.onUpdate(() => { + setState(Boolean(persisted.get('disableHaptics'))) + }) + }, [setStateWrapped]) + + return ( + + + {children} + + + ) +} + +export const useHapticsDisabled = () => React.useContext(stateContext) +export const useSetHapticsDisabled = () => React.useContext(setContext) diff --git a/src/state/preferences/index.tsx b/src/state/preferences/index.tsx index cf1d901511..804d0fc310 100644 --- a/src/state/preferences/index.tsx +++ b/src/state/preferences/index.tsx @@ -1,11 +1,12 @@ import React from 'react' -import {Provider as LanguagesProvider} from './languages' + import {Provider as AltTextRequiredProvider} from '../preferences/alt-text-required' import {Provider as HiddenPostsProvider} from '../preferences/hidden-posts' +import {Provider as DisableHapticsProvider} from './disable-haptics' import {Provider as ExternalEmbedsProvider} from './external-embeds-prefs' import {Provider as InAppBrowserProvider} from './in-app-browser' +import {Provider as LanguagesProvider} from './languages' -export {useLanguagePrefs, useLanguagePrefsApi} from './languages' export { useRequireAltTextEnabled, useSetRequireAltTextEnabled, @@ -16,6 +17,7 @@ export { } from './external-embeds-prefs' export * from './hidden-posts' export {useLabelDefinitions} from './label-defs' +export {useLanguagePrefs, useLanguagePrefsApi} from './languages' export function Provider({children}: React.PropsWithChildren<{}>) { return ( @@ -23,7 +25,9 @@ export function Provider({children}: React.PropsWithChildren<{}>) { - {children} + + {children} + diff --git a/src/view/com/util/post-ctrls/PostCtrls.tsx b/src/view/com/util/post-ctrls/PostCtrls.tsx index 58874cd551..cd4a363730 100644 --- a/src/view/com/util/post-ctrls/PostCtrls.tsx +++ b/src/view/com/util/post-ctrls/PostCtrls.tsx @@ -16,7 +16,6 @@ import {msg} from '@lingui/macro' import {useLingui} from '@lingui/react' import {HITSLOP_10, HITSLOP_20} from '#/lib/constants' -import {Haptics} from '#/lib/haptics' import {CommentBottomArrow, HeartIcon, HeartIconSolid} from '#/lib/icons' import {makeProfileLink} from '#/lib/routes/links' import {shareUrl} from '#/lib/sharing' @@ -32,6 +31,7 @@ import { } from '#/state/queries/post' import {useRequireAuth} from '#/state/session' import {useComposerControls} from '#/state/shell/composer' +import {useHaptics} from 'lib/haptics' import {useDialogControl} from '#/components/Dialog' import {ArrowOutOfBox_Stroke2_Corner0_Rounded as ArrowOutOfBox} from '#/components/icons/ArrowOutOfBox' import * as Prompt from '#/components/Prompt' @@ -67,6 +67,7 @@ let PostCtrls = ({ ) const requireAuth = useRequireAuth() const loggedOutWarningPromptControl = useDialogControl() + const playHaptic = useHaptics() const shouldShowLoggedOutWarning = React.useMemo(() => { return !!post.author.labels?.find( @@ -84,7 +85,7 @@ let PostCtrls = ({ const onPressToggleLike = React.useCallback(async () => { try { if (!post.viewer?.like) { - Haptics.default() + playHaptic() await queueLike() } else { await queueUnlike() @@ -94,13 +95,13 @@ let PostCtrls = ({ throw e } } - }, [post.viewer?.like, queueLike, queueUnlike]) + }, [playHaptic, post.viewer?.like, queueLike, queueUnlike]) const onRepost = useCallback(async () => { closeModal() try { if (!post.viewer?.repost) { - Haptics.default() + playHaptic() await queueRepost() } else { await queueUnrepost() @@ -110,7 +111,7 @@ let PostCtrls = ({ throw e } } - }, [post.viewer?.repost, queueRepost, queueUnrepost, closeModal]) + }, [closeModal, post.viewer?.repost, playHaptic, queueRepost, queueUnrepost]) const onQuote = useCallback(() => { closeModal() @@ -123,15 +124,16 @@ let PostCtrls = ({ indexedAt: post.indexedAt, }, }) - Haptics.default() + playHaptic() }, [ + closeModal, + openComposer, post.uri, post.cid, post.author, post.indexedAt, record.text, - openComposer, - closeModal, + playHaptic, ]) const onShare = useCallback(() => { diff --git a/src/view/screens/ProfileFeed.tsx b/src/view/screens/ProfileFeed.tsx index 4560e14ebc..814c1e8558 100644 --- a/src/view/screens/ProfileFeed.tsx +++ b/src/view/screens/ProfileFeed.tsx @@ -27,7 +27,7 @@ import {truncateAndInvalidate} from '#/state/queries/util' import {useSession} from '#/state/session' import {useComposerControls} from '#/state/shell/composer' import {useAnalytics} from 'lib/analytics/analytics' -import {Haptics} from 'lib/haptics' +import {useHaptics} from 'lib/haptics' import {usePalette} from 'lib/hooks/usePalette' import {useSetTitle} from 'lib/hooks/useSetTitle' import {ComposeIcon2} from 'lib/icons' @@ -159,6 +159,7 @@ export function ProfileFeedScreenInner({ const reportDialogControl = useReportDialogControl() const {openComposer} = useComposerControls() const {track} = useAnalytics() + const playHaptic = useHaptics() const feedSectionRef = React.useRef(null) const isScreenFocused = useIsFocused() @@ -201,7 +202,7 @@ export function ProfileFeedScreenInner({ const onToggleSaved = React.useCallback(async () => { try { - Haptics.default() + playHaptic() if (isSaved) { await removeFeed({uri: feedInfo.uri}) @@ -221,18 +222,19 @@ export function ProfileFeedScreenInner({ logger.error('Failed up update feeds', {message: err}) } }, [ - feedInfo, + playHaptic, isSaved, - saveFeed, removeFeed, - resetSaveFeed, + feedInfo, resetRemoveFeed, _, + saveFeed, + resetSaveFeed, ]) const onTogglePinned = React.useCallback(async () => { try { - Haptics.default() + playHaptic() if (isPinned) { await unpinFeed({uri: feedInfo.uri}) @@ -245,7 +247,16 @@ export function ProfileFeedScreenInner({ Toast.show(_(msg`There was an issue contacting the server`)) logger.error('Failed to toggle pinned feed', {message: e}) } - }, [isPinned, feedInfo, pinFeed, unpinFeed, resetPinFeed, resetUnpinFeed, _]) + }, [ + playHaptic, + isPinned, + unpinFeed, + feedInfo, + resetUnpinFeed, + pinFeed, + resetPinFeed, + _, + ]) const onPressShare = React.useCallback(() => { const url = toShareUrl(feedInfo.route.href) @@ -517,6 +528,7 @@ function AboutSection({ const [likeUri, setLikeUri] = React.useState(feedInfo.likeUri) const {hasSession} = useSession() const {track} = useAnalytics() + const playHaptic = useHaptics() const {mutateAsync: likeFeed, isPending: isLikePending} = useLikeMutation() const {mutateAsync: unlikeFeed, isPending: isUnlikePending} = useUnlikeMutation() @@ -527,7 +539,7 @@ function AboutSection({ const onToggleLiked = React.useCallback(async () => { try { - Haptics.default() + playHaptic() if (isLiked && likeUri) { await unlikeFeed({uri: likeUri}) @@ -546,7 +558,7 @@ function AboutSection({ ) logger.error('Failed up toggle like', {message: err}) } - }, [likeUri, isLiked, feedInfo, likeFeed, unlikeFeed, track, _]) + }, [playHaptic, isLiked, likeUri, unlikeFeed, track, likeFeed, feedInfo, _]) return ( diff --git a/src/view/screens/ProfileList.tsx b/src/view/screens/ProfileList.tsx index 58b89f2399..1d93a9fd7d 100644 --- a/src/view/screens/ProfileList.tsx +++ b/src/view/screens/ProfileList.tsx @@ -1,69 +1,70 @@ import React, {useCallback, useMemo} from 'react' import {Pressable, StyleSheet, View} from 'react-native' -import {useFocusEffect, useIsFocused} from '@react-navigation/native' -import {NativeStackScreenProps, CommonNavigatorParams} from 'lib/routes/types' -import {useNavigation} from '@react-navigation/native' -import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' import {AppBskyGraphDefs, AtUri, RichText as RichTextAPI} from '@atproto/api' +import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' +import {msg, Trans} from '@lingui/macro' +import {useLingui} from '@lingui/react' +import {useFocusEffect, useIsFocused} from '@react-navigation/native' +import {useNavigation} from '@react-navigation/native' import {useQueryClient} from '@tanstack/react-query' -import {PagerWithHeader} from 'view/com/pager/PagerWithHeader' -import {ProfileSubpageHeader} from 'view/com/profile/ProfileSubpageHeader' -import {Feed} from 'view/com/posts/Feed' -import {Text} from 'view/com/util/text/Text' -import {NativeDropdown, DropdownItem} from 'view/com/util/forms/NativeDropdown' -import {CenteredView} from 'view/com/util/Views' -import {EmptyState} from 'view/com/util/EmptyState' -import {LoadingScreen} from 'view/com/util/LoadingScreen' -import {RichText} from '#/components/RichText' -import {Button} from 'view/com/util/forms/Button' -import {TextLink} from 'view/com/util/Link' -import {ListRef} from 'view/com/util/List' -import * as Toast from 'view/com/util/Toast' -import {LoadLatestBtn} from 'view/com/util/load-latest/LoadLatestBtn' -import {FAB} from 'view/com/util/fab/FAB' -import {Haptics} from 'lib/haptics' + +import {useAnalytics} from '#/lib/analytics/analytics' +import {cleanError} from '#/lib/strings/errors' +import {logger} from '#/logger' +import {isNative, isWeb} from '#/platform/detection' +import {listenSoftReset} from '#/state/events' +import {useModalControls} from '#/state/modals' +import { + useListBlockMutation, + useListDeleteMutation, + useListMuteMutation, + useListQuery, +} from '#/state/queries/list' import {FeedDescriptor} from '#/state/queries/post-feed' +import {RQKEY as FEED_RQKEY} from '#/state/queries/post-feed' +import { + usePinFeedMutation, + usePreferencesQuery, + useSetSaveFeedsMutation, + useUnpinFeedMutation, +} from '#/state/queries/preferences' +import {useResolveUriQuery} from '#/state/queries/resolve-uri' +import {truncateAndInvalidate} from '#/state/queries/util' +import {useSession} from '#/state/session' +import {useSetMinimalShellMode} from '#/state/shell' +import {useComposerControls} from '#/state/shell/composer' +import {useHaptics} from 'lib/haptics' import {usePalette} from 'lib/hooks/usePalette' import {useSetTitle} from 'lib/hooks/useSetTitle' import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' -import {RQKEY as FEED_RQKEY} from '#/state/queries/post-feed' -import {NavigationProp} from 'lib/routes/types' -import {toShareUrl} from 'lib/strings/url-helpers' -import {shareUrl} from 'lib/sharing' -import {s} from 'lib/styles' -import {sanitizeHandle} from 'lib/strings/handles' -import {makeProfileLink, makeListLink} from 'lib/routes/links' import {ComposeIcon2} from 'lib/icons' +import {makeListLink, makeProfileLink} from 'lib/routes/links' +import {CommonNavigatorParams, NativeStackScreenProps} from 'lib/routes/types' +import {NavigationProp} from 'lib/routes/types' +import {shareUrl} from 'lib/sharing' +import {sanitizeHandle} from 'lib/strings/handles' +import {toShareUrl} from 'lib/strings/url-helpers' +import {s} from 'lib/styles' import {ListMembers} from '#/view/com/lists/ListMembers' -import {Trans, msg} from '@lingui/macro' -import {useLingui} from '@lingui/react' -import {useSetMinimalShellMode} from '#/state/shell' -import {useModalControls} from '#/state/modals' -import {ReportDialog, useReportDialogControl} from '#/components/ReportDialog' -import {useResolveUriQuery} from '#/state/queries/resolve-uri' -import { - useListQuery, - useListMuteMutation, - useListBlockMutation, - useListDeleteMutation, -} from '#/state/queries/list' -import {cleanError} from '#/lib/strings/errors' -import {useSession} from '#/state/session' -import {useComposerControls} from '#/state/shell/composer' -import {isNative, isWeb} from '#/platform/detection' -import {truncateAndInvalidate} from '#/state/queries/util' -import { - usePreferencesQuery, - usePinFeedMutation, - useUnpinFeedMutation, - useSetSaveFeedsMutation, -} from '#/state/queries/preferences' -import {logger} from '#/logger' -import {useAnalytics} from '#/lib/analytics/analytics' -import {listenSoftReset} from '#/state/events' +import {PagerWithHeader} from 'view/com/pager/PagerWithHeader' +import {Feed} from 'view/com/posts/Feed' +import {ProfileSubpageHeader} from 'view/com/profile/ProfileSubpageHeader' +import {EmptyState} from 'view/com/util/EmptyState' +import {FAB} from 'view/com/util/fab/FAB' +import {Button} from 'view/com/util/forms/Button' +import {DropdownItem, NativeDropdown} from 'view/com/util/forms/NativeDropdown' +import {TextLink} from 'view/com/util/Link' +import {ListRef} from 'view/com/util/List' +import {LoadLatestBtn} from 'view/com/util/load-latest/LoadLatestBtn' +import {LoadingScreen} from 'view/com/util/LoadingScreen' +import {Text} from 'view/com/util/text/Text' +import * as Toast from 'view/com/util/Toast' +import {CenteredView} from 'view/com/util/Views' import {atoms as a, useTheme} from '#/alf' -import * as Prompt from '#/components/Prompt' import {useDialogControl} from '#/components/Dialog' +import * as Prompt from '#/components/Prompt' +import {ReportDialog, useReportDialogControl} from '#/components/ReportDialog' +import {RichText} from '#/components/RichText' const SECTION_TITLES_CURATE = ['Posts', 'About'] const SECTION_TITLES_MOD = ['About'] @@ -254,6 +255,7 @@ function Header({rkey, list}: {rkey: string; list: AppBskyGraphDefs.ListView}) { const {data: preferences} = usePreferencesQuery() const {mutate: setSavedFeeds} = useSetSaveFeedsMutation() const {track} = useAnalytics() + const playHaptic = useHaptics() const deleteListPromptControl = useDialogControl() const subscribeMutePromptControl = useDialogControl() @@ -263,7 +265,7 @@ function Header({rkey, list}: {rkey: string; list: AppBskyGraphDefs.ListView}) { const isSaved = preferences?.feeds?.saved?.includes(list.uri) const onTogglePinned = React.useCallback(async () => { - Haptics.default() + playHaptic() try { if (isPinned) { @@ -275,7 +277,7 @@ function Header({rkey, list}: {rkey: string; list: AppBskyGraphDefs.ListView}) { Toast.show(_(msg`There was an issue contacting the server`)) logger.error('Failed to toggle pinned feed', {message: e}) } - }, [list.uri, isPinned, pinFeed, unpinFeed, _]) + }, [playHaptic, isPinned, unpinFeed, list.uri, pinFeed, _]) const onSubscribeMute = useCallback(async () => { try { diff --git a/src/view/screens/SavedFeeds.tsx b/src/view/screens/SavedFeeds.tsx index 251c706384..0003dbd5d9 100644 --- a/src/view/screens/SavedFeeds.tsx +++ b/src/view/screens/SavedFeeds.tsx @@ -1,31 +1,32 @@ import React from 'react' -import {StyleSheet, View, ActivityIndicator, Pressable} from 'react-native' +import {ActivityIndicator, Pressable, StyleSheet, View} from 'react-native' +import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' +import {msg, Trans} from '@lingui/macro' +import {useLingui} from '@lingui/react' import {useFocusEffect} from '@react-navigation/native' import {NativeStackScreenProps} from '@react-navigation/native-stack' + import {track} from '#/lib/analytics/analytics' -import {useAnalytics} from 'lib/analytics/analytics' -import {usePalette} from 'lib/hooks/usePalette' -import {CommonNavigatorParams} from 'lib/routes/types' -import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' -import {ViewHeader} from 'view/com/util/ViewHeader' -import {ScrollView, CenteredView} from 'view/com/util/Views' -import {Text} from 'view/com/util/text/Text' -import {s, colors} from 'lib/styles' -import {FeedSourceCard} from 'view/com/feeds/FeedSourceCard' -import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' -import * as Toast from 'view/com/util/Toast' -import {Haptics} from 'lib/haptics' -import {TextLink} from 'view/com/util/Link' import {logger} from '#/logger' -import {useSetMinimalShellMode} from '#/state/shell' -import {Trans, msg} from '@lingui/macro' -import {useLingui} from '@lingui/react' import { - usePreferencesQuery, usePinFeedMutation, - useUnpinFeedMutation, + usePreferencesQuery, useSetSaveFeedsMutation, + useUnpinFeedMutation, } from '#/state/queries/preferences' +import {useSetMinimalShellMode} from '#/state/shell' +import {useAnalytics} from 'lib/analytics/analytics' +import {useHaptics} from 'lib/haptics' +import {usePalette} from 'lib/hooks/usePalette' +import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' +import {CommonNavigatorParams} from 'lib/routes/types' +import {colors, s} from 'lib/styles' +import {FeedSourceCard} from 'view/com/feeds/FeedSourceCard' +import {TextLink} from 'view/com/util/Link' +import {Text} from 'view/com/util/text/Text' +import * as Toast from 'view/com/util/Toast' +import {ViewHeader} from 'view/com/util/ViewHeader' +import {CenteredView, ScrollView} from 'view/com/util/Views' const HITSLOP_TOP = { top: 20, @@ -189,13 +190,14 @@ function ListItem({ }) { const pal = usePalette('default') const {_} = useLingui() + const playHaptic = useHaptics() const {isPending: isPinPending, mutateAsync: pinFeed} = usePinFeedMutation() const {isPending: isUnpinPending, mutateAsync: unpinFeed} = useUnpinFeedMutation() const isPending = isPinPending || isUnpinPending const onTogglePinned = React.useCallback(async () => { - Haptics.default() + playHaptic() try { resetSaveFeedsMutationState() @@ -209,7 +211,15 @@ function ListItem({ Toast.show(_(msg`There was an issue contacting the server`)) logger.error('Failed to toggle pinned feed', {message: e}) } - }, [feedUri, isPinned, pinFeed, unpinFeed, resetSaveFeedsMutationState, _]) + }, [ + playHaptic, + resetSaveFeedsMutationState, + isPinned, + unpinFeed, + feedUri, + pinFeed, + _, + ]) const onPressUp = React.useCallback(async () => { if (!isPinned) return diff --git a/src/view/screens/Settings/index.tsx b/src/view/screens/Settings/index.tsx index 830a73ff26..8a7fa5e714 100644 --- a/src/view/screens/Settings/index.tsx +++ b/src/view/screens/Settings/index.tsx @@ -20,10 +20,9 @@ import {useLingui} from '@lingui/react' import {useFocusEffect, useNavigation} from '@react-navigation/native' import {useQueryClient} from '@tanstack/react-query' -import {isNative} from '#/platform/detection' +import {isIOS, isNative} from '#/platform/detection' import {useModalControls} from '#/state/modals' import {clearLegacyStorage} from '#/state/persisted/legacy' -// TODO import {useInviteCodesQuery} from '#/state/queries/invites' import {clear as clearStorage} from '#/state/persisted/store' import { useRequireAltTextEnabled, @@ -57,6 +56,10 @@ import {makeProfileLink} from 'lib/routes/links' import {CommonNavigatorParams, NativeStackScreenProps} from 'lib/routes/types' import {NavigationProp} from 'lib/routes/types' import {colors, s} from 'lib/styles' +import { + useHapticsDisabled, + useSetHapticsDisabled, +} from 'state/preferences/disable-haptics' import {AccountDropdownBtn} from 'view/com/util/AccountDropdownBtn' import {SelectableBtn} from 'view/com/util/forms/SelectableBtn' import {ToggleButton} from 'view/com/util/forms/ToggleButton' @@ -155,6 +158,8 @@ export function SettingsScreen({}: Props) { const setRequireAltTextEnabled = useSetRequireAltTextEnabled() const inAppBrowserPref = useInAppBrowser() const setUseInAppBrowser = useSetInAppBrowser() + const isHapticsDisabled = useHapticsDisabled() + const setHapticsDisabled = useSetHapticsDisabled() const onboardingDispatch = useOnboardingDispatch() const navigation = useNavigation() const {isMobile} = useWebMediaQueries() @@ -162,9 +167,6 @@ export function SettingsScreen({}: Props) { const {openModal} = useModalControls() const {isSwitchingAccounts, accounts, currentAccount} = useSession() const {mutate: clearPreferences} = useClearPreferencesMutation() - // TODO - // const {data: invites} = useInviteCodesQuery() - // const invitesAvailable = invites?.available?.length ?? 0 const {setShowLoggedOut} = useLoggedOutViewControls() const closeAllActiveElements = useCloseAllActiveElements() const exportCarControl = useDialogControl() @@ -220,13 +222,6 @@ export function SettingsScreen({}: Props) { exportCarControl.open() }, [exportCarControl]) - /* TODO - const onPressInviteCodes = React.useCallback(() => { - track('Settings:InvitecodesButtonClicked') - openModal({name: 'invite-codes'}) - }, [track, openModal]) - */ - const onPressLanguageSettings = React.useCallback(() => { navigation.navigate('LanguageSettings') }, [navigation]) @@ -414,58 +409,6 @@ export function SettingsScreen({}: Props) { - {/* TODO ( - <> - - Invite a Friend - - - - 0 ? primaryBg : pal.btn, - ]}> - 0 - ? primaryText - : pal.text) as FontAwesomeIconStyle - } - /> - - 0 ? pal.link : pal.text}> - {invites?.disabled ? ( - - Your invite codes are hidden when logged in using an App - Password - - ) : invitesAvailable === 1 ? ( - {invitesAvailable} invite code available - ) : ( - {invitesAvailable} invite codes available - )} - - - - - - )*/} - Accessibility @@ -738,6 +681,19 @@ export function SettingsScreen({}: Props) { /> )} + {isNative && ( + + setHapticsDisabled(!isHapticsDisabled)} + /> + + )} Account diff --git a/src/view/shell/bottom-bar/BottomBar.tsx b/src/view/shell/bottom-bar/BottomBar.tsx index f41631a969..c35fa106d2 100644 --- a/src/view/shell/bottom-bar/BottomBar.tsx +++ b/src/view/shell/bottom-bar/BottomBar.tsx @@ -8,7 +8,7 @@ import {BottomTabBarProps} from '@react-navigation/bottom-tabs' import {StackActions} from '@react-navigation/native' import {useAnalytics} from '#/lib/analytics/analytics' -import {Haptics} from '#/lib/haptics' +import {useHaptics} from '#/lib/haptics' import {useDedupe} from '#/lib/hooks/useDedupe' import {useMinimalShellMode} from '#/lib/hooks/useMinimalShellMode' import {useNavigationTabState} from '#/lib/hooks/useNavigationTabState' @@ -59,6 +59,7 @@ export function BottomBar({navigation}: BottomTabBarProps) { const closeAllActiveElements = useCloseAllActiveElements() const dedupe = useDedupe() const accountSwitchControl = useDialogControl() + const playHaptic = useHaptics() const showSignIn = React.useCallback(() => { closeAllActiveElements() @@ -104,9 +105,9 @@ export function BottomBar({navigation}: BottomTabBarProps) { }, [onPressTab]) const onLongPressProfile = React.useCallback(() => { - Haptics.default() + playHaptic() accountSwitchControl.open() - }, [accountSwitchControl]) + }, [accountSwitchControl, playHaptic]) return ( <> From 4e517720030184ef8c003ffad9b3ca5100619d2e Mon Sep 17 00:00:00 2001 From: Hailey Date: Thu, 11 Apr 2024 15:20:38 -0700 Subject: [PATCH 003/167] Make bio area scrollable on iOS (#2931) * fix dampen logic prevent ghost presses handle refreshes, animations, and clamps handle most cases for cancelling the scroll animation handle animations save point simplify remove unnecessary context readme apply offset on pan find the RCTScrollView send props, add native gesture recognizer get the react tag wrap the profile in context create module * fix swiping to go back * remove debug * use `findNodeHandle` * create an expo module view * port most of it to expo modules * finish most of expomodules impl * experiments * remove refresh ability for now * remove rn module * changes * cleanup a few issues allow swipe back gesture clean up types always run animation if the final offset is < 0 separate logic update patch readme get the `RCTRefreshControl` working nicely * gate new header * organize --- .../expo-module.config.json | 6 + modules/expo-scroll-forwarder/index.ts | 1 + .../ios/ExpoScrollForwarder.podspec | 21 ++ .../ios/ExpoScrollForwarderModule.swift | 13 ++ .../ios/ExpoScrollForwarderView.swift | 215 ++++++++++++++++++ .../src/ExpoScrollForwarder.types.ts | 6 + .../src/ExpoScrollForwarderView.ios.tsx | 13 ++ .../src/ExpoScrollForwarderView.tsx | 7 + patches/react-native+0.73.2.patch | 58 ++++- patches/react-native+0.73.2.patch.md | 12 +- src/screens/Profile/Sections/Feed.tsx | 34 ++- src/screens/Profile/Sections/Labels.tsx | 13 +- src/view/com/feeds/ProfileFeedgens.tsx | 45 ++-- src/view/com/lists/ProfileLists.tsx | 43 ++-- src/view/screens/Profile.tsx | 69 ++++-- 15 files changed, 491 insertions(+), 65 deletions(-) create mode 100644 modules/expo-scroll-forwarder/expo-module.config.json create mode 100644 modules/expo-scroll-forwarder/index.ts create mode 100644 modules/expo-scroll-forwarder/ios/ExpoScrollForwarder.podspec create mode 100644 modules/expo-scroll-forwarder/ios/ExpoScrollForwarderModule.swift create mode 100644 modules/expo-scroll-forwarder/ios/ExpoScrollForwarderView.swift create mode 100644 modules/expo-scroll-forwarder/src/ExpoScrollForwarder.types.ts create mode 100644 modules/expo-scroll-forwarder/src/ExpoScrollForwarderView.ios.tsx create mode 100644 modules/expo-scroll-forwarder/src/ExpoScrollForwarderView.tsx diff --git a/modules/expo-scroll-forwarder/expo-module.config.json b/modules/expo-scroll-forwarder/expo-module.config.json new file mode 100644 index 0000000000..1fd49f79b7 --- /dev/null +++ b/modules/expo-scroll-forwarder/expo-module.config.json @@ -0,0 +1,6 @@ +{ + "platforms": ["ios"], + "ios": { + "modules": ["ExpoScrollForwarderModule"] + } +} diff --git a/modules/expo-scroll-forwarder/index.ts b/modules/expo-scroll-forwarder/index.ts new file mode 100644 index 0000000000..a4ad4b8506 --- /dev/null +++ b/modules/expo-scroll-forwarder/index.ts @@ -0,0 +1 @@ +export {ExpoScrollForwarderView} from './src/ExpoScrollForwarderView' diff --git a/modules/expo-scroll-forwarder/ios/ExpoScrollForwarder.podspec b/modules/expo-scroll-forwarder/ios/ExpoScrollForwarder.podspec new file mode 100644 index 0000000000..78ca9812e4 --- /dev/null +++ b/modules/expo-scroll-forwarder/ios/ExpoScrollForwarder.podspec @@ -0,0 +1,21 @@ +Pod::Spec.new do |s| + s.name = 'ExpoScrollForwarder' + s.version = '1.0.0' + s.summary = 'Forward scroll gesture from UIView to UIScrollView' + s.description = 'Forward scroll gesture from UIView to UIScrollView' + s.author = 'bluesky-social' + s.homepage = 'https://github.com/bluesky-social/social-app' + s.platforms = { :ios => '13.4', :tvos => '13.4' } + s.source = { git: '' } + s.static_framework = true + + s.dependency 'ExpoModulesCore' + + # Swift/Objective-C compatibility + s.pod_target_xcconfig = { + 'DEFINES_MODULE' => 'YES', + 'SWIFT_COMPILATION_MODE' => 'wholemodule' + } + + s.source_files = "**/*.{h,m,mm,swift,hpp,cpp}" +end diff --git a/modules/expo-scroll-forwarder/ios/ExpoScrollForwarderModule.swift b/modules/expo-scroll-forwarder/ios/ExpoScrollForwarderModule.swift new file mode 100644 index 0000000000..c4ecc788e5 --- /dev/null +++ b/modules/expo-scroll-forwarder/ios/ExpoScrollForwarderModule.swift @@ -0,0 +1,13 @@ +import ExpoModulesCore + +public class ExpoScrollForwarderModule: Module { + public func definition() -> ModuleDefinition { + Name("ExpoScrollForwarder") + + View(ExpoScrollForwarderView.self) { + Prop("scrollViewTag") { (view: ExpoScrollForwarderView, prop: Int) in + view.scrollViewTag = prop + } + } + } +} diff --git a/modules/expo-scroll-forwarder/ios/ExpoScrollForwarderView.swift b/modules/expo-scroll-forwarder/ios/ExpoScrollForwarderView.swift new file mode 100644 index 0000000000..9c0e2f8728 --- /dev/null +++ b/modules/expo-scroll-forwarder/ios/ExpoScrollForwarderView.swift @@ -0,0 +1,215 @@ +import ExpoModulesCore + +// This view will be used as a native component. Make sure to inherit from `ExpoView` +// to apply the proper styling (e.g. border radius and shadows). +class ExpoScrollForwarderView: ExpoView, UIGestureRecognizerDelegate { + var scrollViewTag: Int? { + didSet { + self.tryFindScrollView() + } + } + + private var rctScrollView: RCTScrollView? + private var rctRefreshCtrl: RCTRefreshControl? + private var cancelGestureRecognizers: [UIGestureRecognizer]? + private var animTimer: Timer? + private var initialOffset: CGFloat = 0.0 + private var didImpact: Bool = false + + required init(appContext: AppContext? = nil) { + super.init(appContext: appContext) + + let pg = UIPanGestureRecognizer(target: self, action: #selector(callOnPan(_:))) + pg.delegate = self + self.addGestureRecognizer(pg) + + let tg = UITapGestureRecognizer(target: self, action: #selector(callOnPress(_:))) + tg.isEnabled = false + tg.delegate = self + + let lpg = UILongPressGestureRecognizer(target: self, action: #selector(callOnPress(_:))) + lpg.minimumPressDuration = 0.01 + lpg.isEnabled = false + lpg.delegate = self + + self.cancelGestureRecognizers = [lpg, tg] + } + + + // We don't want to recognize the scroll pan gesture and the swipe back gesture together + func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool { + if gestureRecognizer is UIPanGestureRecognizer, otherGestureRecognizer is UIPanGestureRecognizer { + return false + } + + return true + } + + // We only want the "scroll" gesture to happen whenever the pan is vertical, otherwise it will + // interfere with the native swipe back gesture. + override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool { + guard let gestureRecognizer = gestureRecognizer as? UIPanGestureRecognizer else { + return true + } + + let velocity = gestureRecognizer.velocity(in: self) + return abs(velocity.y) > abs(velocity.x) + } + + // This will be used to cancel the scroll animation whenever we tap inside of the header. We don't need another + // recognizer for this one. + override func touchesBegan(_ touches: Set, with event: UIEvent?) { + self.stopTimer() + } + + // This will be used to cancel the animation whenever we press inside of the scroll view. We don't want to change + // the scroll view gesture's delegate, so we add an additional recognizer to detect this. + @IBAction func callOnPress(_ sender: UITapGestureRecognizer) -> Void { + self.stopTimer() + } + + @IBAction func callOnPan(_ sender: UIPanGestureRecognizer) -> Void { + guard let rctsv = self.rctScrollView, let sv = rctsv.scrollView else { + return + } + + let translation = sender.translation(in: self).y + + if sender.state == .began { + if sv.contentOffset.y < 0 { + sv.contentOffset.y = 0 + } + + self.initialOffset = sv.contentOffset.y + } + + if sender.state == .changed { + sv.contentOffset.y = self.dampenOffset(-translation + self.initialOffset) + + if sv.contentOffset.y <= -130, !didImpact { + let generator = UIImpactFeedbackGenerator(style: .light) + generator.impactOccurred() + + self.didImpact = true + } + } + + if sender.state == .ended { + let velocity = sender.velocity(in: self).y + self.didImpact = false + + if sv.contentOffset.y <= -130 { + self.rctRefreshCtrl?.forwarderBeginRefreshing() + return + } + + // A check for a velocity under 250 prevents animations from occurring when they wouldn't in a normal + // scroll view + if abs(velocity) < 250, sv.contentOffset.y >= 0 { + return + } + + self.startDecayAnimation(translation, velocity) + } + } + + func startDecayAnimation(_ translation: CGFloat, _ velocity: CGFloat) { + guard let sv = self.rctScrollView?.scrollView else { + return + } + + var velocity = velocity + + self.enableCancelGestureRecognizers() + + if velocity > 0 { + velocity = min(velocity, 5000) + } else { + velocity = max(velocity, -5000) + } + + var animTranslation = -translation + self.animTimer = Timer.scheduledTimer(withTimeInterval: 1.0 / 120, repeats: true) { timer in + velocity *= 0.9875 + animTranslation = (-velocity / 120) + animTranslation + + let nextOffset = self.dampenOffset(animTranslation + self.initialOffset) + + if nextOffset <= 0 { + if self.initialOffset <= 1 { + self.scrollToOffset(0) + } else { + sv.contentOffset.y = 0 + } + + self.stopTimer() + return + } else { + sv.contentOffset.y = nextOffset + } + + if abs(velocity) < 5 { + self.stopTimer() + } + } + } + + func dampenOffset(_ offset: CGFloat) -> CGFloat { + if offset < 0 { + return offset - (offset * 0.55) + } + + return offset + } + + func tryFindScrollView() { + guard let scrollViewTag = scrollViewTag else { + return + } + + // Before we switch to a different scrollview, we always want to remove the cancel gesture recognizer. + // Otherwise we might end up with duplicates when we switch back to that scrollview. + self.removeCancelGestureRecognizers() + + self.rctScrollView = self.appContext? + .findView(withTag: scrollViewTag, ofType: RCTScrollView.self) + self.rctRefreshCtrl = self.rctScrollView?.scrollView.refreshControl as? RCTRefreshControl + + self.addCancelGestureRecognizers() + } + + func addCancelGestureRecognizers() { + self.cancelGestureRecognizers?.forEach { r in + self.rctScrollView?.scrollView?.addGestureRecognizer(r) + } + } + + func removeCancelGestureRecognizers() { + self.cancelGestureRecognizers?.forEach { r in + self.rctScrollView?.scrollView?.removeGestureRecognizer(r) + } + } + + + func enableCancelGestureRecognizers() { + self.cancelGestureRecognizers?.forEach { r in + r.isEnabled = true + } + } + + func disableCancelGestureRecognizers() { + self.cancelGestureRecognizers?.forEach { r in + r.isEnabled = false + } + } + + func scrollToOffset(_ offset: Int, animated: Bool = true) -> Void { + self.rctScrollView?.scroll(toOffset: CGPoint(x: 0, y: offset), animated: animated) + } + + func stopTimer() -> Void { + self.disableCancelGestureRecognizers() + self.animTimer?.invalidate() + self.animTimer = nil + } +} diff --git a/modules/expo-scroll-forwarder/src/ExpoScrollForwarder.types.ts b/modules/expo-scroll-forwarder/src/ExpoScrollForwarder.types.ts new file mode 100644 index 0000000000..26b9e7553a --- /dev/null +++ b/modules/expo-scroll-forwarder/src/ExpoScrollForwarder.types.ts @@ -0,0 +1,6 @@ +import React from 'react' + +export interface ExpoScrollForwarderViewProps { + scrollViewTag: number | null + children: React.ReactNode +} diff --git a/modules/expo-scroll-forwarder/src/ExpoScrollForwarderView.ios.tsx b/modules/expo-scroll-forwarder/src/ExpoScrollForwarderView.ios.tsx new file mode 100644 index 0000000000..a91aebd4dc --- /dev/null +++ b/modules/expo-scroll-forwarder/src/ExpoScrollForwarderView.ios.tsx @@ -0,0 +1,13 @@ +import {requireNativeViewManager} from 'expo-modules-core' +import * as React from 'react' +import {ExpoScrollForwarderViewProps} from './ExpoScrollForwarder.types' + +const NativeView: React.ComponentType = + requireNativeViewManager('ExpoScrollForwarder') + +export function ExpoScrollForwarderView({ + children, + ...rest +}: ExpoScrollForwarderViewProps) { + return {children} +} diff --git a/modules/expo-scroll-forwarder/src/ExpoScrollForwarderView.tsx b/modules/expo-scroll-forwarder/src/ExpoScrollForwarderView.tsx new file mode 100644 index 0000000000..93e69333fd --- /dev/null +++ b/modules/expo-scroll-forwarder/src/ExpoScrollForwarderView.tsx @@ -0,0 +1,7 @@ +import React from 'react' +import {ExpoScrollForwarderViewProps} from './ExpoScrollForwarder.types' +export function ExpoScrollForwarderView({ + children, +}: React.PropsWithChildren) { + return children +} diff --git a/patches/react-native+0.73.2.patch b/patches/react-native+0.73.2.patch index 8db23da0c7..db8b7da2d2 100644 --- a/patches/react-native+0.73.2.patch +++ b/patches/react-native+0.73.2.patch @@ -1,11 +1,22 @@ +diff --git a/node_modules/react-native/React/Views/RefreshControl/RCTRefreshControl.h b/node_modules/react-native/React/Views/RefreshControl/RCTRefreshControl.h +index e9b330f..1ecdf0a 100644 +--- a/node_modules/react-native/React/Views/RefreshControl/RCTRefreshControl.h ++++ b/node_modules/react-native/React/Views/RefreshControl/RCTRefreshControl.h +@@ -16,4 +16,6 @@ + @property (nonatomic, copy) RCTDirectEventBlock onRefresh; + @property (nonatomic, weak) UIScrollView *scrollView; + ++- (void)forwarderBeginRefreshing; ++ + @end diff --git a/node_modules/react-native/React/Views/RefreshControl/RCTRefreshControl.m b/node_modules/react-native/React/Views/RefreshControl/RCTRefreshControl.m -index b09e653..d290dab 100644 +index b09e653..4c32b31 100644 --- a/node_modules/react-native/React/Views/RefreshControl/RCTRefreshControl.m +++ b/node_modules/react-native/React/Views/RefreshControl/RCTRefreshControl.m -@@ -198,6 +198,14 @@ - (void)refreshControlValueChanged +@@ -198,9 +198,53 @@ - (void)refreshControlValueChanged [self setCurrentRefreshingState:super.refreshing]; _refreshingProgrammatically = NO; - + + if (@available(iOS 17.4, *)) { + if (_currentRefreshingState) { + UIImpactFeedbackGenerator *feedbackGenerator = [[UIImpactFeedbackGenerator alloc] initWithStyle:UIImpactFeedbackStyleLight]; @@ -16,4 +27,43 @@ index b09e653..d290dab 100644 + if (_onRefresh) { _onRefresh(nil); - } \ No newline at end of file + } + } + ++/* ++ This method is used by Bluesky's ExpoScrollForwarder. This allows other React Native ++ libraries to perform a refresh of a scrollview and access the refresh control's onRefresh ++ function. ++ */ ++- (void)forwarderBeginRefreshing ++{ ++ _refreshingProgrammatically = NO; ++ ++ [self sizeToFit]; ++ ++ if (!self.scrollView) { ++ return; ++ } ++ ++ UIScrollView *scrollView = (UIScrollView *)self.scrollView; ++ ++ [UIView animateWithDuration:0.3 ++ delay:0 ++ options:UIViewAnimationOptionBeginFromCurrentState ++ animations:^(void) { ++ // Whenever we call this method, the scrollview will always be at a position of ++ // -130 or less. Scrolling back to -65 simulates the default behavior of RCTRefreshControl ++ [scrollView setContentOffset:CGPointMake(0, -65)]; ++ } ++ completion:^(__unused BOOL finished) { ++ [super beginRefreshing]; ++ [self setCurrentRefreshingState:super.refreshing]; ++ ++ if (self->_onRefresh) { ++ self->_onRefresh(nil); ++ } ++ } ++ ]; ++} ++ + @end diff --git a/patches/react-native+0.73.2.patch.md b/patches/react-native+0.73.2.patch.md index 7f70baf2fd..9c93aee5cb 100644 --- a/patches/react-native+0.73.2.patch.md +++ b/patches/react-native+0.73.2.patch.md @@ -1,5 +1,13 @@ -# RefreshControl Patch +# ***This second part of this patch is load bearing, do not remove.*** + +## RefreshControl Patch - iOS 17.4 Haptic Regression Patching `RCTRefreshControl.mm` temporarily to play an impact haptic on refresh when using iOS 17.4 or higher. Since 17.4, there has been a regression somewhere causing haptics to not play on iOS on refresh. Should monitor for an update -in the RN repo: https://github.com/facebook/react-native/issues/43388 \ No newline at end of file +in the RN repo: https://github.com/facebook/react-native/issues/43388 + +## RefreshControl Path - ScrollForwarder + +Patching `RCTRefreshControl.m` and `RCTRefreshControl.h` to add a new `forwarderBeginRefreshing` method to the class. +This method is used by `ExpoScrollForwarder` to initiate a refresh of the underlying `UIScrollView` from inside that +module. diff --git a/src/screens/Profile/Sections/Feed.tsx b/src/screens/Profile/Sections/Feed.tsx index 0a5e2208d6..bc106fcfb9 100644 --- a/src/screens/Profile/Sections/Feed.tsx +++ b/src/screens/Profile/Sections/Feed.tsx @@ -1,18 +1,19 @@ import React from 'react' -import {View} from 'react-native' +import {findNodeHandle, View} from 'react-native' import {msg, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' -import {ListRef} from 'view/com/util/List' -import {Feed} from 'view/com/posts/Feed' -import {EmptyState} from 'view/com/util/EmptyState' +import {useQueryClient} from '@tanstack/react-query' + +import {isNative} from '#/platform/detection' import {FeedDescriptor} from '#/state/queries/post-feed' import {RQKEY as FEED_RQKEY} from '#/state/queries/post-feed' -import {LoadLatestBtn} from 'view/com/util/load-latest/LoadLatestBtn' -import {useQueryClient} from '@tanstack/react-query' import {truncateAndInvalidate} from '#/state/queries/util' -import {Text} from '#/view/com/util/text/Text' import {usePalette} from 'lib/hooks/usePalette' -import {isNative} from '#/platform/detection' +import {Text} from '#/view/com/util/text/Text' +import {Feed} from 'view/com/posts/Feed' +import {EmptyState} from 'view/com/util/EmptyState' +import {ListRef} from 'view/com/util/List' +import {LoadLatestBtn} from 'view/com/util/load-latest/LoadLatestBtn' import {SectionRef} from './types' interface FeedSectionProps { @@ -21,12 +22,20 @@ interface FeedSectionProps { isFocused: boolean scrollElRef: ListRef ignoreFilterFor?: string + setScrollViewTag: (tag: number | null) => void } export const ProfileFeedSection = React.forwardRef< SectionRef, FeedSectionProps >(function FeedSectionImpl( - {feed, headerHeight, isFocused, scrollElRef, ignoreFilterFor}, + { + feed, + headerHeight, + isFocused, + scrollElRef, + ignoreFilterFor, + setScrollViewTag, + }, ref, ) { const {_} = useLingui() @@ -50,6 +59,13 @@ export const ProfileFeedSection = React.forwardRef< return }, [_]) + React.useEffect(() => { + if (isFocused && scrollElRef.current) { + const nativeTag = findNodeHandle(scrollElRef.current) + setScrollViewTag(nativeTag) + } + }, [isFocused, scrollElRef, setScrollViewTag]) + return ( void } export const ProfileLabelsSection = React.forwardRef< SectionRef, @@ -44,6 +46,8 @@ export const ProfileLabelsSection = React.forwardRef< moderationOpts, scrollElRef, headerHeight, + isFocused, + setScrollViewTag, }, ref, ) { @@ -63,6 +67,13 @@ export const ProfileLabelsSection = React.forwardRef< scrollToTop: onScrollToTop, })) + React.useEffect(() => { + if (isFocused && scrollElRef.current) { + const nativeTag = findNodeHandle(scrollElRef.current) + setScrollViewTag(nativeTag) + } + }, [isFocused, scrollElRef, setScrollViewTag]) + return ( {isLabelerLoading ? ( diff --git a/src/view/com/feeds/ProfileFeedgens.tsx b/src/view/com/feeds/ProfileFeedgens.tsx index e9cf9e5359..a006b11c06 100644 --- a/src/view/com/feeds/ProfileFeedgens.tsx +++ b/src/view/com/feeds/ProfileFeedgens.tsx @@ -1,22 +1,29 @@ import React from 'react' -import {StyleProp, StyleSheet, View, ViewStyle} from 'react-native' +import { + findNodeHandle, + StyleProp, + StyleSheet, + View, + ViewStyle, +} from 'react-native' +import {msg, Trans} from '@lingui/macro' +import {useLingui} from '@lingui/react' import {useQueryClient} from '@tanstack/react-query' -import {List, ListRef} from '../util/List' -import {FeedSourceCardLoaded} from './FeedSourceCard' -import {ErrorMessage} from '../util/error/ErrorMessage' -import {LoadMoreRetryBtn} from '../util/LoadMoreRetryBtn' -import {Text} from '../util/text/Text' -import {usePalette} from 'lib/hooks/usePalette' -import {useProfileFeedgensQuery, RQKEY} from '#/state/queries/profile-feedgens' -import {logger} from '#/logger' -import {Trans, msg} from '@lingui/macro' + import {cleanError} from '#/lib/strings/errors' import {useTheme} from '#/lib/ThemeContext' -import {usePreferencesQuery} from '#/state/queries/preferences' -import {hydrateFeedGenerator} from '#/state/queries/feed' -import {FeedLoadingPlaceholder} from '#/view/com/util/LoadingPlaceholder' +import {logger} from '#/logger' import {isNative} from '#/platform/detection' -import {useLingui} from '@lingui/react' +import {hydrateFeedGenerator} from '#/state/queries/feed' +import {usePreferencesQuery} from '#/state/queries/preferences' +import {RQKEY, useProfileFeedgensQuery} from '#/state/queries/profile-feedgens' +import {usePalette} from 'lib/hooks/usePalette' +import {FeedLoadingPlaceholder} from '#/view/com/util/LoadingPlaceholder' +import {ErrorMessage} from '../util/error/ErrorMessage' +import {List, ListRef} from '../util/List' +import {LoadMoreRetryBtn} from '../util/LoadMoreRetryBtn' +import {Text} from '../util/text/Text' +import {FeedSourceCardLoaded} from './FeedSourceCard' const LOADING = {_reactKey: '__loading__'} const EMPTY = {_reactKey: '__empty__'} @@ -34,13 +41,14 @@ interface ProfileFeedgensProps { enabled?: boolean style?: StyleProp testID?: string + setScrollViewTag: (tag: number | null) => void } export const ProfileFeedgens = React.forwardRef< SectionRef, ProfileFeedgensProps >(function ProfileFeedgensImpl( - {did, scrollElRef, headerOffset, enabled, style, testID}, + {did, scrollElRef, headerOffset, enabled, style, testID, setScrollViewTag}, ref, ) { const pal = usePalette('default') @@ -169,6 +177,13 @@ export const ProfileFeedgens = React.forwardRef< [error, refetch, onPressRetryLoadMore, pal, preferences, _], ) + React.useEffect(() => { + if (enabled && scrollElRef.current) { + const nativeTag = findNodeHandle(scrollElRef.current) + setScrollViewTag(nativeTag) + } + }, [enabled, scrollElRef, setScrollViewTag]) + return ( testID?: string + setScrollViewTag: (tag: number | null) => void } export const ProfileLists = React.forwardRef( function ProfileListsImpl( - {did, scrollElRef, headerOffset, enabled, style, testID}, + {did, scrollElRef, headerOffset, enabled, style, testID, setScrollViewTag}, ref, ) { const pal = usePalette('default') @@ -171,6 +179,13 @@ export const ProfileLists = React.forwardRef( [error, refetch, onPressRetryLoadMore, pal, _], ) + React.useEffect(() => { + if (enabled && scrollElRef.current) { + const nativeTag = findNodeHandle(scrollElRef.current) + setScrollViewTag(nativeTag) + } + }, [enabled, scrollElRef, setScrollViewTag]) + return ( (null) + const postsSectionRef = React.useRef(null) const repliesSectionRef = React.useRef(null) const mediaSectionRef = React.useRef(null) @@ -297,12 +303,9 @@ function ProfileScreenLoaded({ openComposer({mention}) }, [openComposer, currentAccount, track, profile]) - const onPageSelected = React.useCallback( - (i: number) => { - setCurrentPage(i) - }, - [setCurrentPage], - ) + const onPageSelected = React.useCallback((i: number) => { + setCurrentPage(i) + }, []) const onCurrentPageSelected = React.useCallback( (index: number) => { @@ -315,21 +318,38 @@ function ProfileScreenLoaded({ // = const renderHeader = React.useCallback(() => { - return ( - - ) + if (shouldUseScrollableHeader) { + return ( + + + + ) + } else { + return ( + + ) + } }, [ + shouldUseScrollableHeader, + scrollViewTag, profile, labelerInfo, - descriptionRT, hasDescription, + descriptionRT, moderationOpts, hideBackButton, showPlaceholder, @@ -349,7 +369,7 @@ function ProfileScreenLoaded({ onCurrentPageSelected={onCurrentPageSelected} renderHeader={renderHeader}> {showFiltersTab - ? ({headerHeight, scrollElRef}) => ( + ? ({headerHeight, isFocused, scrollElRef}) => ( ) : null} @@ -369,6 +391,7 @@ function ProfileScreenLoaded({ scrollElRef={scrollElRef as ListRef} headerOffset={headerHeight} enabled={isFocused} + setScrollViewTag={setScrollViewTag} /> ) : null} @@ -381,6 +404,7 @@ function ProfileScreenLoaded({ isFocused={isFocused} scrollElRef={scrollElRef as ListRef} ignoreFilterFor={profile.did} + setScrollViewTag={setScrollViewTag} /> ) : null} @@ -393,6 +417,7 @@ function ProfileScreenLoaded({ isFocused={isFocused} scrollElRef={scrollElRef as ListRef} ignoreFilterFor={profile.did} + setScrollViewTag={setScrollViewTag} /> ) : null} @@ -405,6 +430,7 @@ function ProfileScreenLoaded({ isFocused={isFocused} scrollElRef={scrollElRef as ListRef} ignoreFilterFor={profile.did} + setScrollViewTag={setScrollViewTag} /> ) : null} @@ -417,6 +443,7 @@ function ProfileScreenLoaded({ isFocused={isFocused} scrollElRef={scrollElRef as ListRef} ignoreFilterFor={profile.did} + setScrollViewTag={setScrollViewTag} /> ) : null} @@ -428,6 +455,7 @@ function ProfileScreenLoaded({ scrollElRef={scrollElRef as ListRef} headerOffset={headerHeight} enabled={isFocused} + setScrollViewTag={setScrollViewTag} /> ) : null} @@ -439,6 +467,7 @@ function ProfileScreenLoaded({ scrollElRef={scrollElRef as ListRef} headerOffset={headerHeight} enabled={isFocused} + setScrollViewTag={setScrollViewTag} /> ) : null} From 491116ca66564d9940f32602294ee0d743ea4756 Mon Sep 17 00:00:00 2001 From: dan Date: Thu, 11 Apr 2024 23:59:02 +0100 Subject: [PATCH 004/167] Fix useGate lint rule (#3486) --- eslint/use-typed-gates.js | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/eslint/use-typed-gates.js b/eslint/use-typed-gates.js index 3625a7da37..6c0331afee 100644 --- a/eslint/use-typed-gates.js +++ b/eslint/use-typed-gates.js @@ -18,14 +18,13 @@ exports.create = function create(context) { return } const source = node.parent.source.value - if (source.startsWith('.') || source.startsWith('#')) { - return + if (source.startsWith('statsig') || source.startsWith('@statsig')) { + context.report({ + node, + message: + "Use useGate() from '#/lib/statsig/statsig' instead of the one on npm.", + }) } - context.report({ - node, - message: - "Use useGate() from '#/lib/statsig/statsig' instead of the one on npm.", - }) }, } } From e3e8f10538bba57f1cf298fd1205846f801557eb Mon Sep 17 00:00:00 2001 From: Hailey Date: Thu, 11 Apr 2024 15:59:13 -0700 Subject: [PATCH 005/167] Added `new_profile_scroll_component` to `Gate` type (#3487) * added to the types * alphabetical pls --------- Co-authored-by: dan --- src/lib/statsig/gates.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib/statsig/gates.ts b/src/lib/statsig/gates.ts index c755ad437e..5a95823833 100644 --- a/src/lib/statsig/gates.ts +++ b/src/lib/statsig/gates.ts @@ -3,6 +3,7 @@ export type Gate = | 'autoexpand_suggestions_on_profile_follow' | 'disable_min_shell_on_foregrounding' | 'disable_poll_on_discover' + | 'new_profile_scroll_component' | 'new_search' | 'show_follow_back_label' | 'start_session_with_following' From bedb0c3fbd65b6520c97f22c99f10bb535a177bc Mon Sep 17 00:00:00 2001 From: dan Date: Fri, 12 Apr 2024 13:02:15 +0100 Subject: [PATCH 006/167] Use getSuggestions endpoint behind the gate (#3499) * Move suggested follows out of the component * Add new suggestions implementation * Put new endpoint behind the gate * Make bottom less weird --- src/lib/statsig/gates.ts | 1 + src/lib/statsig/statsig.tsx | 5 ++- src/view/screens/Search/Search.tsx | 65 ++++++++++++++++++++++++++++-- 3 files changed, 66 insertions(+), 5 deletions(-) diff --git a/src/lib/statsig/gates.ts b/src/lib/statsig/gates.ts index 5a95823833..acf0b2aff2 100644 --- a/src/lib/statsig/gates.ts +++ b/src/lib/statsig/gates.ts @@ -7,3 +7,4 @@ export type Gate = | 'new_search' | 'show_follow_back_label' | 'start_session_with_following' + | 'use_new_suggestions_endpoint' diff --git a/src/lib/statsig/statsig.tsx b/src/lib/statsig/statsig.tsx index 159438647a..7513b945c6 100644 --- a/src/lib/statsig/statsig.tsx +++ b/src/lib/statsig/statsig.tsx @@ -82,7 +82,10 @@ export function useGate(gateName: Gate): boolean { // This should not happen because of waitForInitialization={true}. console.error('Did not expected isLoading to ever be true.') } - return value + // This shouldn't technically be necessary but let's get a strong + // guarantee that a gate value can never change while mounted. + const [initialValue] = React.useState(value) + return initialValue } function toStatsigUser(did: string | undefined) { diff --git a/src/view/screens/Search/Search.tsx b/src/view/screens/Search/Search.tsx index 3b06992fc9..f5ebd155c8 100644 --- a/src/view/screens/Search/Search.tsx +++ b/src/view/screens/Search/Search.tsx @@ -32,7 +32,10 @@ import {useActorAutocompleteFn} from '#/state/queries/actor-autocomplete' import {useActorSearch} from '#/state/queries/actor-search' import {useModerationOpts} from '#/state/queries/preferences' import {useSearchPostsQuery} from '#/state/queries/search-posts' -import {useGetSuggestedFollowersByActor} from '#/state/queries/suggested-follows' +import { + useGetSuggestedFollowersByActor, + useSuggestedFollowsQuery, +} from '#/state/queries/suggested-follows' import {useSession} from '#/state/session' import {useSetDrawerOpen} from '#/state/shell' import {useSetDrawerSwipeDisabled, useSetMinimalShellMode} from '#/state/shell' @@ -118,8 +121,10 @@ function EmptyState({message, error}: {message: string; error?: string}) { ) } -function SearchScreenSuggestedFollows() { - const pal = usePalette('default') +function useSuggestedFollowsV1(): [ + AppBskyActorDefs.ProfileViewBasic[], + () => void, +] { const {currentAccount} = useSession() const [suggestions, setSuggestions] = React.useState< AppBskyActorDefs.ProfileViewBasic[] @@ -162,6 +167,56 @@ function SearchScreenSuggestedFollows() { } }, [currentAccount, setSuggestions, getSuggestedFollowsByActor]) + return [suggestions, () => {}] +} + +function useSuggestedFollowsV2(): [ + AppBskyActorDefs.ProfileViewBasic[], + () => void, +] { + const { + data: suggestions, + hasNextPage, + isFetchingNextPage, + isError, + fetchNextPage, + } = useSuggestedFollowsQuery() + + const onEndReached = React.useCallback(async () => { + if (isFetchingNextPage || !hasNextPage || isError) return + try { + await fetchNextPage() + } catch (err) { + logger.error('Failed to load more suggested follows', {message: err}) + } + }, [isFetchingNextPage, hasNextPage, isError, fetchNextPage]) + + const items: AppBskyActorDefs.ProfileViewBasic[] = [] + if (suggestions) { + // Currently the responses contain duplicate items. + // Needs to be fixed on backend, but let's dedupe to be safe. + let seen = new Set() + for (const page of suggestions.pages) { + for (const actor of page.actors) { + if (!seen.has(actor.did)) { + seen.add(actor.did) + items.push(actor) + } + } + } + } + return [items, onEndReached] +} + +function SearchScreenSuggestedFollows() { + const pal = usePalette('default') + const useSuggestedFollows = useGate('use_new_suggestions_endpoint') + ? // Conditional hook call here is *only* OK because useGate() + // result won't change until a remount. + useSuggestedFollowsV2 + : useSuggestedFollowsV1 + const [suggestions, onEndReached] = useSuggestedFollows() + return suggestions.length ? ( item.did} // @ts-ignore web only -prf desktopFixedHeight - contentContainerStyle={{paddingBottom: 1200}} + contentContainerStyle={{paddingBottom: 200}} keyboardShouldPersistTaps="handled" keyboardDismissMode="on-drag" + onEndReached={onEndReached} + onEndReachedThreshold={2} /> ) : ( From 24bd3d6986a8080a34836b34ee1dbd88357d1cb5 Mon Sep 17 00:00:00 2001 From: Hailey Date: Fri, 12 Apr 2024 07:49:09 -0700 Subject: [PATCH 007/167] add `likeCount` etal. to `embedViewRecordToPostView` (#3500) * fix qt jumps Revert "don't show loading placeholder if we don't need it" This reverts commit 406f801f217b2733fdd82732c0af74186fc47464. don't show loading placeholder if we don't need it add `likeCount` etal. to `embedViewRecordToPostView` * lint * Revert the shimmer change --------- Co-authored-by: Dan Abramov --- src/state/queries/util.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/state/queries/util.ts b/src/state/queries/util.ts index 54752b332a..94d6c9df7c 100644 --- a/src/state/queries/util.ts +++ b/src/state/queries/util.ts @@ -1,10 +1,10 @@ -import {QueryClient, QueryKey, InfiniteData} from '@tanstack/react-query' import { AppBskyEmbedRecord, AppBskyEmbedRecordWithMedia, AppBskyFeedDefs, AppBskyFeedPost, } from '@atproto/api' +import {InfiniteData, QueryClient, QueryKey} from '@tanstack/react-query' export function truncateAndInvalidate( queryClient: QueryClient, @@ -54,5 +54,9 @@ export function embedViewRecordToPostView( indexedAt: v.indexedAt, labels: v.labels, embed: v.embeds?.[0], + // TODO we can remove the `as` once we update @atproto/api + likeCount: v.likeCount as number | undefined, + replyCount: v.replyCount as number | undefined, + repostCount: v.repostCount as number | undefined, } } From ad97d4350c55055c3fcf084915a0a067c09939da Mon Sep 17 00:00:00 2001 From: Samuel Newman Date: Fri, 12 Apr 2024 15:52:26 +0100 Subject: [PATCH 008/167] [Embeds] Create vite project and add to build pipeline (#3448) * add bskyembed vite app * create build script (temp until embedr is ready) --- Dockerfile | 3 +- Makefile | 1 + bskyembed/.eslintrc | 20 + bskyembed/.gitignore | 5 + bskyembed/index.html | 12 + bskyembed/package.json | 21 + bskyembed/src/app.tsx | 18 + bskyembed/src/index.css | 29 + bskyembed/src/main.tsx | 9 + bskyembed/tsconfig.json | 23 + bskyembed/vite.config.ts | 18 + bskyembed/yarn.lock | 3737 +++++++++++++++++++++++++++++++++++ docs/build.md | 3 +- package.json | 1 + scripts/post-embed-build.js | 49 + 15 files changed, 3946 insertions(+), 3 deletions(-) create mode 100644 bskyembed/.eslintrc create mode 100644 bskyembed/.gitignore create mode 100644 bskyembed/index.html create mode 100644 bskyembed/package.json create mode 100644 bskyembed/src/app.tsx create mode 100644 bskyembed/src/index.css create mode 100644 bskyembed/src/main.tsx create mode 100644 bskyembed/tsconfig.json create mode 100644 bskyembed/vite.config.ts create mode 100644 bskyembed/yarn.lock create mode 100644 scripts/post-embed-build.js diff --git a/Dockerfile b/Dockerfile index 3ad05b6ec6..e36a959293 100644 --- a/Dockerfile +++ b/Dockerfile @@ -32,7 +32,8 @@ RUN \. "$NVM_DIR/nvm.sh" && \ npm install --global yarn && \ yarn && \ yarn intl:build && \ - yarn build-web + yarn build-web && \ + yarn build-embed # DEBUG RUN find ./bskyweb/static && find ./web-build/static diff --git a/Makefile b/Makefile index c90abb783e..9e82e0fe47 100644 --- a/Makefile +++ b/Makefile @@ -12,6 +12,7 @@ help: ## Print info about all commands build-web: ## Compile web bundle, copy to bskyweb directory yarn intl:build yarn build-web + yarn build-embed .PHONY: test test: ## Run all tests diff --git a/bskyembed/.eslintrc b/bskyembed/.eslintrc new file mode 100644 index 0000000000..339900dd09 --- /dev/null +++ b/bskyembed/.eslintrc @@ -0,0 +1,20 @@ +{ + "root": true, + "parser": "@typescript-eslint/parser", + "plugins": ["@typescript-eslint", "simple-import-sort"], + "extends": [ + "eslint:recommended", + "preact", + "plugin:@typescript-eslint/recommended", + "plugin:@typescript-eslint/recommended-requiring-type-checking" + ], + "rules": { + "simple-import-sort/imports": "warn", + "simple-import-sort/exports": "warn" + }, + "parserOptions": { + "sourceType": "module", + "ecmaVersion": "latest", + "project": "./tsconfig.json" + } +} \ No newline at end of file diff --git a/bskyembed/.gitignore b/bskyembed/.gitignore new file mode 100644 index 0000000000..d451ff16c1 --- /dev/null +++ b/bskyembed/.gitignore @@ -0,0 +1,5 @@ +node_modules +.DS_Store +dist +dist-ssr +*.local diff --git a/bskyembed/index.html b/bskyembed/index.html new file mode 100644 index 0000000000..1c6e2a4173 --- /dev/null +++ b/bskyembed/index.html @@ -0,0 +1,12 @@ + + + + + + Vite App + + +
+ + + diff --git a/bskyembed/package.json b/bskyembed/package.json new file mode 100644 index 0000000000..048c721eb4 --- /dev/null +++ b/bskyembed/package.json @@ -0,0 +1,21 @@ +{ + "name": "bskyembed", + "version": "0.0.0", + "scripts": { + "dev": "vite", + "build": "tsc && vite build", + "lint": "eslint --cache --ext .js,.jsx,.ts,.tsx src" + }, + "dependencies": { + "preact": "^10.4.8" + }, + "devDependencies": { + "@prefresh/vite": "^1.2.1", + "eslint": "^8.19.0", + "eslint-config-preact": "^1.3.0", + "eslint-plugin-simple-import-sort": "^12.0.0", + "typescript": "^4.0.5", + "vite": "^1.0.0-rc.13", + "vite-tsconfig-paths": "^4.3.2" + } +} diff --git a/bskyembed/src/app.tsx b/bskyembed/src/app.tsx new file mode 100644 index 0000000000..4fba80d59b --- /dev/null +++ b/bskyembed/src/app.tsx @@ -0,0 +1,18 @@ +import {Fragment, h} from 'preact' + +export function App() { + return ( + <> +

Hello Vite + Preact!

+

+ + Learn Preact + +

+ + ) +} diff --git a/bskyembed/src/index.css b/bskyembed/src/index.css new file mode 100644 index 0000000000..b8c94dfb55 --- /dev/null +++ b/bskyembed/src/index.css @@ -0,0 +1,29 @@ +html, body { + height: 100%; + width: 100%; + padding: 0; + margin: 0; + background: #FAFAFA; + font-family: 'Helvetica Neue', arial, sans-serif; + font-weight: 400; + color: #444; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +* { + box-sizing: border-box; +} + +#app { + height: 100%; + text-align: center; + background-color: #673ab8; + color: #fff; + font-size: 1.5em; + padding-top: 100px; +} + +.link { + color: #fff; +} diff --git a/bskyembed/src/main.tsx b/bskyembed/src/main.tsx new file mode 100644 index 0000000000..349f0ee786 --- /dev/null +++ b/bskyembed/src/main.tsx @@ -0,0 +1,9 @@ +import './index.css' + +import {h, render} from 'preact' + +import {App} from './app' + +const root = document.getElementById('app') +if (!root) throw new Error('No root element') +render(, root) diff --git a/bskyembed/tsconfig.json b/bskyembed/tsconfig.json new file mode 100644 index 0000000000..bbfce13ed8 --- /dev/null +++ b/bskyembed/tsconfig.json @@ -0,0 +1,23 @@ + +{ + "compilerOptions": { + "target": "ES5", + "lib": ["DOM", "DOM.Iterable", "ESNext"], + "types": [], + "allowJs": true, + "skipLibCheck": true, + "esModuleInterop": false, + "allowSyntheticDefaultImports": true, + "strict": true, + "forceConsistentCasingInFileNames": true, + "module": "ESNext", + "moduleResolution": "Node", + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react", + "jsxFactory": "h", + "jsxFragmentFactory": "Fragment" + }, + "include": ["src"] +} diff --git a/bskyembed/vite.config.ts b/bskyembed/vite.config.ts new file mode 100644 index 0000000000..1f5ec0ed9e --- /dev/null +++ b/bskyembed/vite.config.ts @@ -0,0 +1,18 @@ +import {resolve} from 'node:path' + +// @ts-expect-error - not important +import preactRefresh from '@prefresh/vite' +import type {UserConfig} from 'vite' +import paths from 'vite-tsconfig-paths' + +const config: UserConfig = { + jsx: { + factory: 'h', + fragment: 'Fragment', + }, + // eslint-disable-next-line @typescript-eslint/no-unsafe-call + plugins: [preactRefresh(), paths()], + assetsDir: 'static/embed/assets', +} + +export default config diff --git a/bskyembed/yarn.lock b/bskyembed/yarn.lock new file mode 100644 index 0000000000..bfa20c553b --- /dev/null +++ b/bskyembed/yarn.lock @@ -0,0 +1,3737 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@aashutoshrathi/word-wrap@^1.2.3": + version "1.2.6" + resolved "https://registry.yarnpkg.com/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz#bd9154aec9983f77b3a034ecaa015c2e4201f6cf" + integrity sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA== + +"@ampproject/remapping@^2.2.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.3.0.tgz#ed441b6fa600072520ce18b43d2c8cc8caecc7f4" + integrity sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw== + dependencies: + "@jridgewell/gen-mapping" "^0.3.5" + "@jridgewell/trace-mapping" "^0.3.24" + +"@babel/code-frame@^7.10.4", "@babel/code-frame@^7.23.5", "@babel/code-frame@^7.24.1", "@babel/code-frame@^7.24.2": + version "7.24.2" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.24.2.tgz#718b4b19841809a58b29b68cde80bc5e1aa6d9ae" + integrity sha512-y5+tLQyV8pg3fsiln67BVLD1P13Eg4lh5RW9mF0zUuvLrv9uIQ4MCL+CRT+FTsBlBjcIan6PGsLcBN0m3ClUyQ== + dependencies: + "@babel/highlight" "^7.24.2" + picocolors "^1.0.0" + +"@babel/compat-data@^7.23.5": + version "7.24.4" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.24.4.tgz#6f102372e9094f25d908ca0d34fc74c74606059a" + integrity sha512-vg8Gih2MLK+kOkHJp4gBEIkyaIi00jgWot2D9QOmmfLC8jINSOzmCLta6Bvz/JSBCqnegV0L80jhxkol5GWNfQ== + +"@babel/core@^7.13.16", "@babel/core@^7.9.6": + version "7.24.4" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.24.4.tgz#1f758428e88e0d8c563874741bc4ffc4f71a4717" + integrity sha512-MBVlMXP+kkl5394RBLSxxk/iLTeVGuXTV3cIDXavPpMMqnSnt6apKgan/U8O3USWZCWZT/TbgfEpKa4uMgN4Dg== + dependencies: + "@ampproject/remapping" "^2.2.0" + "@babel/code-frame" "^7.24.2" + "@babel/generator" "^7.24.4" + "@babel/helper-compilation-targets" "^7.23.6" + "@babel/helper-module-transforms" "^7.23.3" + "@babel/helpers" "^7.24.4" + "@babel/parser" "^7.24.4" + "@babel/template" "^7.24.0" + "@babel/traverse" "^7.24.1" + "@babel/types" "^7.24.0" + convert-source-map "^2.0.0" + debug "^4.1.0" + gensync "^1.0.0-beta.2" + json5 "^2.2.3" + semver "^6.3.1" + +"@babel/eslint-parser@^7.13.14": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/eslint-parser/-/eslint-parser-7.24.1.tgz#e27eee93ed1d271637165ef3a86e2b9332395c32" + integrity sha512-d5guuzMlPeDfZIbpQ8+g1NaCNuAGBBGNECh0HVqz1sjOeVLh2CEaifuOysCH18URW6R7pqXINvf5PaR/dC6jLQ== + dependencies: + "@nicolo-ribaudo/eslint-scope-5-internals" "5.1.1-v1" + eslint-visitor-keys "^2.1.0" + semver "^6.3.1" + +"@babel/generator@^7.24.1", "@babel/generator@^7.24.4": + version "7.24.4" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.24.4.tgz#1fc55532b88adf952025d5d2d1e71f946cb1c498" + integrity sha512-Xd6+v6SnjWVx/nus+y0l1sxMOTOMBkyL4+BIdbALyatQnAe/SRVjANeDPSCYaX+i1iJmuGSKf3Z+E+V/va1Hvw== + dependencies: + "@babel/types" "^7.24.0" + "@jridgewell/gen-mapping" "^0.3.5" + "@jridgewell/trace-mapping" "^0.3.25" + jsesc "^2.5.1" + +"@babel/helper-compilation-targets@^7.23.6": + version "7.23.6" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz#4d79069b16cbcf1461289eccfbbd81501ae39991" + integrity sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ== + dependencies: + "@babel/compat-data" "^7.23.5" + "@babel/helper-validator-option" "^7.23.5" + browserslist "^4.22.2" + lru-cache "^5.1.1" + semver "^6.3.1" + +"@babel/helper-environment-visitor@^7.22.20": + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz#96159db61d34a29dba454c959f5ae4a649ba9167" + integrity sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA== + +"@babel/helper-function-name@^7.23.0": + version "7.23.0" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz#1f9a3cdbd5b2698a670c30d2735f9af95ed52759" + integrity sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw== + dependencies: + "@babel/template" "^7.22.15" + "@babel/types" "^7.23.0" + +"@babel/helper-hoist-variables@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz#c01a007dac05c085914e8fb652b339db50d823bb" + integrity sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw== + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-module-imports@^7.22.15": + version "7.24.3" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.24.3.tgz#6ac476e6d168c7c23ff3ba3cf4f7841d46ac8128" + integrity sha512-viKb0F9f2s0BCS22QSF308z/+1YWKV/76mwt61NBzS5izMzDPwdq1pTrzf+Li3npBWX9KdQbkeCt1jSAM7lZqg== + dependencies: + "@babel/types" "^7.24.0" + +"@babel/helper-module-transforms@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz#d7d12c3c5d30af5b3c0fcab2a6d5217773e2d0f1" + integrity sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ== + dependencies: + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-module-imports" "^7.22.15" + "@babel/helper-simple-access" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.6" + "@babel/helper-validator-identifier" "^7.22.20" + +"@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.24.0": + version "7.24.0" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.0.tgz#945681931a52f15ce879fd5b86ce2dae6d3d7f2a" + integrity sha512-9cUznXMG0+FxRuJfvL82QlTqIzhVW9sL0KjMPHhAOOvpQGL8QtdxnBKILjBqxlHyliz0yCa1G903ZXI/FuHy2w== + +"@babel/helper-simple-access@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz#4938357dc7d782b80ed6dbb03a0fba3d22b1d5de" + integrity sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w== + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-split-export-declaration@^7.22.6": + version "7.22.6" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz#322c61b7310c0997fe4c323955667f18fcefb91c" + integrity sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g== + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-string-parser@^7.23.4": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.24.1.tgz#f99c36d3593db9540705d0739a1f10b5e20c696e" + integrity sha512-2ofRCjnnA9y+wk8b9IAREroeUP02KHp431N2mhKniy2yKIDKpbrHv9eXwm8cBeWQYcJmzv5qKCu65P47eCF7CQ== + +"@babel/helper-validator-identifier@^7.22.20": + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz#c4ae002c61d2879e724581d96665583dbc1dc0e0" + integrity sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A== + +"@babel/helper-validator-option@^7.23.5": + version "7.23.5" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz#907a3fbd4523426285365d1206c423c4c5520307" + integrity sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw== + +"@babel/helpers@^7.24.4": + version "7.24.4" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.24.4.tgz#dc00907fd0d95da74563c142ef4cd21f2cb856b6" + integrity sha512-FewdlZbSiwaVGlgT1DPANDuCHaDMiOo+D/IDYRFYjHOuv66xMSJ7fQwwODwRNAPkADIO/z1EoF/l2BCWlWABDw== + dependencies: + "@babel/template" "^7.24.0" + "@babel/traverse" "^7.24.1" + "@babel/types" "^7.24.0" + +"@babel/highlight@^7.24.2": + version "7.24.2" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.24.2.tgz#3f539503efc83d3c59080a10e6634306e0370d26" + integrity sha512-Yac1ao4flkTxTteCDZLEvdxg2fZfz1v8M4QpaGypq/WPDqg3ijHYbDfs+LG5hvzSoqaSZ9/Z9lKSP3CjZjv+pA== + dependencies: + "@babel/helper-validator-identifier" "^7.22.20" + chalk "^2.4.2" + js-tokens "^4.0.0" + picocolors "^1.0.0" + +"@babel/parser@^7.12.7", "@babel/parser@^7.23.9", "@babel/parser@^7.24.0", "@babel/parser@^7.24.1", "@babel/parser@^7.24.4": + version "7.24.4" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.24.4.tgz#234487a110d89ad5a3ed4a8a566c36b9453e8c88" + integrity sha512-zTvEBcghmeBma9QIGunWevvBAp4/Qu9Bdq+2k0Ot4fVMD6v3dsC9WOcRSKk7tRRyBM/53yKMJko9xOatGQAwSg== + +"@babel/plugin-syntax-class-properties@^7.12.13": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz#b5c987274c4a3a82b89714796931a6b53544ae10" + integrity sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA== + dependencies: + "@babel/helper-plugin-utils" "^7.12.13" + +"@babel/plugin-syntax-decorators@^7.12.13": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.24.1.tgz#71d9ad06063a6ac5430db126b5df48c70ee885fa" + integrity sha512-05RJdO/cCrtVWuAaSn1tS3bH8jbsJa/Y1uD186u6J4C/1mnHFxseeuWpsqr9anvo7TUulev7tm7GDwRV+VuhDw== + dependencies: + "@babel/helper-plugin-utils" "^7.24.0" + +"@babel/plugin-syntax-jsx@^7.12.13": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.24.1.tgz#3f6ca04b8c841811dbc3c5c5f837934e0d626c10" + integrity sha512-2eCtxZXf+kbkMIsXS4poTvT4Yu5rXiRa+9xGVT56raghjmBTKMpFNc9R4IDiB4emao9eO22Ox7CxuJG7BgExqA== + dependencies: + "@babel/helper-plugin-utils" "^7.24.0" + +"@babel/template@^7.22.15", "@babel/template@^7.24.0": + version "7.24.0" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.24.0.tgz#c6a524aa93a4a05d66aaf31654258fae69d87d50" + integrity sha512-Bkf2q8lMB0AFpX0NFEqSbx1OkTHf0f+0j82mkw+ZpzBnkk7e9Ql0891vlfgi+kHwOk8tQjiQHpqh4LaSa0fKEA== + dependencies: + "@babel/code-frame" "^7.23.5" + "@babel/parser" "^7.24.0" + "@babel/types" "^7.24.0" + +"@babel/traverse@^7.24.1": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.24.1.tgz#d65c36ac9dd17282175d1e4a3c49d5b7988f530c" + integrity sha512-xuU6o9m68KeqZbQuDt2TcKSxUw/mrsvavlEqQ1leZ/B+C9tk6E4sRWy97WaXgvq5E+nU3cXMxv3WKOCanVMCmQ== + dependencies: + "@babel/code-frame" "^7.24.1" + "@babel/generator" "^7.24.1" + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-function-name" "^7.23.0" + "@babel/helper-hoist-variables" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.6" + "@babel/parser" "^7.24.1" + "@babel/types" "^7.24.0" + debug "^4.3.1" + globals "^11.1.0" + +"@babel/types@^7.22.5", "@babel/types@^7.23.0", "@babel/types@^7.24.0": + version "7.24.0" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.24.0.tgz#3b951f435a92e7333eba05b7566fd297960ea1bf" + integrity sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w== + dependencies: + "@babel/helper-string-parser" "^7.23.4" + "@babel/helper-validator-identifier" "^7.22.20" + to-fast-properties "^2.0.0" + +"@eslint-community/eslint-utils@^4.2.0": + version "4.4.0" + resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59" + integrity sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA== + dependencies: + eslint-visitor-keys "^3.3.0" + +"@eslint-community/regexpp@^4.6.1": + version "4.10.0" + resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.10.0.tgz#548f6de556857c8bb73bbee70c35dc82a2e74d63" + integrity sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA== + +"@eslint/eslintrc@^2.1.4": + version "2.1.4" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.1.4.tgz#388a269f0f25c1b6adc317b5a2c55714894c70ad" + integrity sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ== + dependencies: + ajv "^6.12.4" + debug "^4.3.2" + espree "^9.6.0" + globals "^13.19.0" + ignore "^5.2.0" + import-fresh "^3.2.1" + js-yaml "^4.1.0" + minimatch "^3.1.2" + strip-json-comments "^3.1.1" + +"@eslint/js@8.57.0": + version "8.57.0" + resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.57.0.tgz#a5417ae8427873f1dd08b70b3574b453e67b5f7f" + integrity sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g== + +"@humanwhocodes/config-array@^0.11.14": + version "0.11.14" + resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.14.tgz#d78e481a039f7566ecc9660b4ea7fe6b1fec442b" + integrity sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg== + dependencies: + "@humanwhocodes/object-schema" "^2.0.2" + debug "^4.3.1" + minimatch "^3.0.5" + +"@humanwhocodes/module-importer@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c" + integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== + +"@humanwhocodes/object-schema@^2.0.2": + version "2.0.3" + resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz#4a2868d75d6d6963e423bcf90b7fd1be343409d3" + integrity sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA== + +"@jridgewell/gen-mapping@^0.3.5": + version "0.3.5" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz#dcce6aff74bdf6dad1a95802b69b04a2fcb1fb36" + integrity sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg== + dependencies: + "@jridgewell/set-array" "^1.2.1" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/trace-mapping" "^0.3.24" + +"@jridgewell/resolve-uri@^3.1.0": + version "3.1.2" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6" + integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== + +"@jridgewell/set-array@^1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.2.1.tgz#558fb6472ed16a4c850b889530e6b36438c49280" + integrity sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A== + +"@jridgewell/source-map@^0.3.3": + version "0.3.6" + resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.6.tgz#9d71ca886e32502eb9362c9a74a46787c36df81a" + integrity sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ== + dependencies: + "@jridgewell/gen-mapping" "^0.3.5" + "@jridgewell/trace-mapping" "^0.3.25" + +"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14", "@jridgewell/sourcemap-codec@^1.4.15": + version "1.4.15" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" + integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== + +"@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25": + version "0.3.25" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz#15f190e98895f3fc23276ee14bc76b675c2e50f0" + integrity sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ== + dependencies: + "@jridgewell/resolve-uri" "^3.1.0" + "@jridgewell/sourcemap-codec" "^1.4.14" + +"@koa/cors@^3.1.0": + version "3.4.3" + resolved "https://registry.yarnpkg.com/@koa/cors/-/cors-3.4.3.tgz#d669ee6e8d6e4f0ec4a7a7b0a17e7a3ed3752ebb" + integrity sha512-WPXQUaAeAMVaLTEFpoq3T2O1C+FstkjJnDQqy95Ck1UdILajsRhu6mhJ8H2f4NFPRBoCNN+qywTJfq/gGki5mw== + dependencies: + vary "^1.1.2" + +"@mdn/browser-compat-data@^5.2.34", "@mdn/browser-compat-data@^5.3.13": + version "5.5.19" + resolved "https://registry.yarnpkg.com/@mdn/browser-compat-data/-/browser-compat-data-5.5.19.tgz#5c661edd669ee990dbdf2e1a8ee3c9c1c6fa7117" + integrity sha512-ntKBZtwWCy4XvJosdTJKqIMdmzgbxjopfoiMxgpzsml3dXqA7MIHCE/amidfQc06a6KvmMrpiVuYHIBt2feDog== + +"@nicolo-ribaudo/eslint-scope-5-internals@5.1.1-v1": + version "5.1.1-v1" + resolved "https://registry.yarnpkg.com/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz#dbf733a965ca47b1973177dc0bb6c889edcfb129" + integrity sha512-54/JRvkLIzzDWshCWfuhadfrfZVPiElY8Fcgmg1HroEly/EDSszzhBAsarCux+D/kOslTRquNzuyGSmUSTTHGg== + dependencies: + eslint-scope "5.1.1" + +"@nodelib/fs.scandir@2.1.5": + version "2.1.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" + integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== + dependencies: + "@nodelib/fs.stat" "2.0.5" + run-parallel "^1.1.9" + +"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" + integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== + +"@nodelib/fs.walk@^1.2.3", "@nodelib/fs.walk@^1.2.8": + version "1.2.8" + resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" + integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== + dependencies: + "@nodelib/fs.scandir" "2.1.5" + fastq "^1.6.0" + +"@prefresh/babel-plugin@0.4.0": + version "0.4.0" + resolved "https://registry.yarnpkg.com/@prefresh/babel-plugin/-/babel-plugin-0.4.0.tgz#78ca60adb51095b20e6afdaadc1015e549ae89c9" + integrity sha512-fFwyfIHm/B8BBY7HL4j9iJl7KFk/5yVIWE+aozRRPPxI8lRFkyXMAgUFtTSmP3/jiMA6jyOcBeYUhWsyEUynpQ== + +"@prefresh/core@^1.3.0": + version "1.5.2" + resolved "https://registry.yarnpkg.com/@prefresh/core/-/core-1.5.2.tgz#750e1936d82f3b0a1199d3cda5c35e3443128490" + integrity sha512-A/08vkaM1FogrCII5PZKCrygxSsc11obExBScm3JF1CryK2uDS3ZXeni7FeKCx1nYdUkj4UcJxzPzc1WliMzZA== + +"@prefresh/utils@^1.0.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@prefresh/utils/-/utils-1.2.0.tgz#cbdfe549b207041e38bb6cc382408b30cd24fec8" + integrity sha512-KtC/fZw+oqtwOLUFM9UtiitB0JsVX0zLKNyRTA332sqREqSALIIQQxdUCS1P3xR/jT1e2e8/5rwH6gdcMLEmsQ== + +"@prefresh/vite@^1.2.1": + version "1.2.3" + resolved "https://registry.yarnpkg.com/@prefresh/vite/-/vite-1.2.3.tgz#64a0c5e7216377011e806e0b2d64b8b52716f7e6" + integrity sha512-XzGXwjB7c4uvfMvD9g/FVGkXBuKv43lwGJIUzOgcGPyVpiNAgYz4NhfoaNMDExCN4EUFjXa5o4aOFLTFTDcwsA== + dependencies: + "@babel/core" "^7.9.6" + "@prefresh/babel-plugin" "0.4.0" + "@prefresh/core" "^1.3.0" + "@prefresh/utils" "^1.0.0" + +"@rollup/plugin-commonjs@^16.0.0": + version "16.0.0" + resolved "https://registry.yarnpkg.com/@rollup/plugin-commonjs/-/plugin-commonjs-16.0.0.tgz#169004d56cd0f0a1d0f35915d31a036b0efe281f" + integrity sha512-LuNyypCP3msCGVQJ7ki8PqYdpjfEkE/xtFa5DqlF+7IBD0JsfMZ87C58heSwIMint58sAUZbt3ITqOmdQv/dXw== + dependencies: + "@rollup/pluginutils" "^3.1.0" + commondir "^1.0.1" + estree-walker "^2.0.1" + glob "^7.1.6" + is-reference "^1.2.1" + magic-string "^0.25.7" + resolve "^1.17.0" + +"@rollup/plugin-json@^4.1.0": + version "4.1.0" + resolved "https://registry.yarnpkg.com/@rollup/plugin-json/-/plugin-json-4.1.0.tgz#54e09867ae6963c593844d8bd7a9c718294496f3" + integrity sha512-yfLbTdNS6amI/2OpmbiBoW12vngr5NW2jCJVZSBEz+H5KfUJZ2M7sDjk0U6GOOdCWFVScShte29o9NezJ53TPw== + dependencies: + "@rollup/pluginutils" "^3.0.8" + +"@rollup/plugin-node-resolve@^10.0.0": + version "10.0.0" + resolved "https://registry.yarnpkg.com/@rollup/plugin-node-resolve/-/plugin-node-resolve-10.0.0.tgz#44064a2b98df7530e66acf8941ff262fc9b4ead8" + integrity sha512-sNijGta8fqzwA1VwUEtTvWCx2E7qC70NMsDh4ZG13byAXYigBNZMxALhKUSycBks5gupJdq0lFrKumFrRZ8H3A== + dependencies: + "@rollup/pluginutils" "^3.1.0" + "@types/resolve" "1.17.1" + builtin-modules "^3.1.0" + deepmerge "^4.2.2" + is-module "^1.0.0" + resolve "^1.17.0" + +"@rollup/pluginutils@^3.0.8", "@rollup/pluginutils@^3.0.9", "@rollup/pluginutils@^3.1.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-3.1.0.tgz#706b4524ee6dc8b103b3c995533e5ad680c02b9b" + integrity sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg== + dependencies: + "@types/estree" "0.0.39" + estree-walker "^1.0.1" + picomatch "^2.2.2" + +"@rollup/pluginutils@^4.1.0": + version "4.2.1" + resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-4.2.1.tgz#e6c6c3aba0744edce3fb2074922d3776c0af2a6d" + integrity sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ== + dependencies: + estree-walker "^2.0.1" + picomatch "^2.2.2" + +"@types/accepts@*": + version "1.3.7" + resolved "https://registry.yarnpkg.com/@types/accepts/-/accepts-1.3.7.tgz#3b98b1889d2b2386604c2bbbe62e4fb51e95b265" + integrity sha512-Pay9fq2lM2wXPWbteBsRAGiWH2hig4ZE2asK+mm7kUzlxRTfL961rj89I6zV/E3PcIkDqyuBEcMxFT7rccugeQ== + dependencies: + "@types/node" "*" + +"@types/body-parser@*": + version "1.19.5" + resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.5.tgz#04ce9a3b677dc8bd681a17da1ab9835dc9d3ede4" + integrity sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg== + dependencies: + "@types/connect" "*" + "@types/node" "*" + +"@types/connect@*": + version "3.4.38" + resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.38.tgz#5ba7f3bc4fbbdeaff8dded952e5ff2cc53f8d858" + integrity sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug== + dependencies: + "@types/node" "*" + +"@types/content-disposition@*": + version "0.5.8" + resolved "https://registry.yarnpkg.com/@types/content-disposition/-/content-disposition-0.5.8.tgz#6742a5971f490dc41e59d277eee71361fea0b537" + integrity sha512-QVSSvno3dE0MgO76pJhmv4Qyi/j0Yk9pBp0Y7TJ2Tlj+KCgJWY6qX7nnxCOLkZ3VYRSIk1WTxCvwUSdx6CCLdg== + +"@types/cookies@*": + version "0.9.0" + resolved "https://registry.yarnpkg.com/@types/cookies/-/cookies-0.9.0.tgz#a2290cfb325f75f0f28720939bee854d4142aee2" + integrity sha512-40Zk8qR147RABiQ7NQnBzWzDcjKzNrntB5BAmeGCb2p/MIyOE+4BVvc17wumsUqUw00bJYqoXFHYygQnEFh4/Q== + dependencies: + "@types/connect" "*" + "@types/express" "*" + "@types/keygrip" "*" + "@types/node" "*" + +"@types/estree@*": + version "1.0.5" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.5.tgz#a6ce3e556e00fd9895dd872dd172ad0d4bd687f4" + integrity sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw== + +"@types/estree@0.0.39": + version "0.0.39" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.39.tgz#e177e699ee1b8c22d23174caaa7422644389509f" + integrity sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw== + +"@types/express-serve-static-core@^4.17.33": + version "4.19.0" + resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.19.0.tgz#3ae8ab3767d98d0b682cda063c3339e1e86ccfaa" + integrity sha512-bGyep3JqPCRry1wq+O5n7oiBgGWmeIJXPjXXCo8EK0u8duZGSYar7cGqd3ML2JUsLGeB7fmc06KYo9fLGWqPvQ== + dependencies: + "@types/node" "*" + "@types/qs" "*" + "@types/range-parser" "*" + "@types/send" "*" + +"@types/express@*": + version "4.17.21" + resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.21.tgz#c26d4a151e60efe0084b23dc3369ebc631ed192d" + integrity sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ== + dependencies: + "@types/body-parser" "*" + "@types/express-serve-static-core" "^4.17.33" + "@types/qs" "*" + "@types/serve-static" "*" + +"@types/http-assert@*": + version "1.5.5" + resolved "https://registry.yarnpkg.com/@types/http-assert/-/http-assert-1.5.5.tgz#dfb1063eb7c240ee3d3fe213dac5671cfb6a8dbf" + integrity sha512-4+tE/lwdAahgZT1g30Jkdm9PzFRde0xwxBNUyRsCitRvCQB90iuA2uJYdUnhnANRcqGXaWOGY4FEoxeElNAK2g== + +"@types/http-errors@*": + version "2.0.4" + resolved "https://registry.yarnpkg.com/@types/http-errors/-/http-errors-2.0.4.tgz#7eb47726c391b7345a6ec35ad7f4de469cf5ba4f" + integrity sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA== + +"@types/http-proxy@^1.17.4": + version "1.17.14" + resolved "https://registry.yarnpkg.com/@types/http-proxy/-/http-proxy-1.17.14.tgz#57f8ccaa1c1c3780644f8a94f9c6b5000b5e2eec" + integrity sha512-SSrD0c1OQzlFX7pGu1eXxSEjemej64aaNPRhhVYUGqXh0BtldAAx37MG8btcumvpgKyZp1F5Gn3JkktdxiFv6w== + dependencies: + "@types/node" "*" + +"@types/json-schema@^7.0.9": + version "7.0.15" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" + integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== + +"@types/keygrip@*": + version "1.0.6" + resolved "https://registry.yarnpkg.com/@types/keygrip/-/keygrip-1.0.6.tgz#1749535181a2a9b02ac04a797550a8787345b740" + integrity sha512-lZuNAY9xeJt7Bx4t4dx0rYCDqGPW8RXhQZK1td7d4H6E9zYbLoOtjBvfwdTKpsyxQI/2jv+armjX/RW+ZNpXOQ== + +"@types/koa-compose@*": + version "3.2.8" + resolved "https://registry.yarnpkg.com/@types/koa-compose/-/koa-compose-3.2.8.tgz#dec48de1f6b3d87f87320097686a915f1e954b57" + integrity sha512-4Olc63RY+MKvxMwVknCUDhRQX1pFQoBZ/lXcRLP69PQkEpze/0cr8LNqJQe5NFb/b19DWi2a5bTi2VAlQzhJuA== + dependencies: + "@types/koa" "*" + +"@types/koa@*", "@types/koa@^2.11.4": + version "2.15.0" + resolved "https://registry.yarnpkg.com/@types/koa/-/koa-2.15.0.tgz#eca43d76f527c803b491731f95df575636e7b6f2" + integrity sha512-7QFsywoE5URbuVnG3loe03QXuGajrnotr3gQkXcEBShORai23MePfFYdhz90FEtBBpkyIYQbVD+evKtloCgX3g== + dependencies: + "@types/accepts" "*" + "@types/content-disposition" "*" + "@types/cookies" "*" + "@types/http-assert" "*" + "@types/http-errors" "*" + "@types/keygrip" "*" + "@types/koa-compose" "*" + "@types/node" "*" + +"@types/lru-cache@^5.1.0": + version "5.1.1" + resolved "https://registry.yarnpkg.com/@types/lru-cache/-/lru-cache-5.1.1.tgz#c48c2e27b65d2a153b19bfc1a317e30872e01eef" + integrity sha512-ssE3Vlrys7sdIzs5LOxCzTVMsU7i9oa/IaW92wF32JFb3CVczqOkru2xspuKczHEbG3nvmPY7IFqVmGGHdNbYw== + +"@types/mime@^1": + version "1.3.5" + resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.5.tgz#1ef302e01cf7d2b5a0fa526790c9123bf1d06690" + integrity sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w== + +"@types/node@*": + version "20.12.4" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.12.4.tgz#af5921bd75ccdf3a3d8b3fa75bf3d3359268cd11" + integrity sha512-E+Fa9z3wSQpzgYQdYmme5X3OTuejnnTx88A6p6vkkJosR3KBz+HpE3kqNm98VE6cfLFcISx7zW7MsJkH6KwbTw== + dependencies: + undici-types "~5.26.4" + +"@types/qs@*": + version "6.9.14" + resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.14.tgz#169e142bfe493895287bee382af6039795e9b75b" + integrity sha512-5khscbd3SwWMhFqylJBLQ0zIu7c1K6Vz0uBIt915BI3zV0q1nfjRQD3RqSBcPaO6PHEF4ov/t9y89fSiyThlPA== + +"@types/range-parser@*": + version "1.2.7" + resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.7.tgz#50ae4353eaaddc04044279812f52c8c65857dbcb" + integrity sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ== + +"@types/resolve@1.17.1": + version "1.17.1" + resolved "https://registry.yarnpkg.com/@types/resolve/-/resolve-1.17.1.tgz#3afd6ad8967c77e4376c598a82ddd58f46ec45d6" + integrity sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw== + dependencies: + "@types/node" "*" + +"@types/semver@^7.3.12": + version "7.5.8" + resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.8.tgz#8268a8c57a3e4abd25c165ecd36237db7948a55e" + integrity sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ== + +"@types/send@*": + version "0.17.4" + resolved "https://registry.yarnpkg.com/@types/send/-/send-0.17.4.tgz#6619cd24e7270793702e4e6a4b958a9010cfc57a" + integrity sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA== + dependencies: + "@types/mime" "^1" + "@types/node" "*" + +"@types/serve-static@*": + version "1.15.7" + resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.15.7.tgz#22174bbd74fb97fe303109738e9b5c2f3064f714" + integrity sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw== + dependencies: + "@types/http-errors" "*" + "@types/node" "*" + "@types/send" "*" + +"@typescript-eslint/experimental-utils@^5.0.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-5.62.0.tgz#14559bf73383a308026b427a4a6129bae2146741" + integrity sha512-RTXpeB3eMkpoclG3ZHft6vG/Z30azNHuqY6wKPBHlVMZFuEvrtlEDe8gMqDb+SO+9hjC/pLekeSCryf9vMZlCw== + dependencies: + "@typescript-eslint/utils" "5.62.0" + +"@typescript-eslint/scope-manager@5.62.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz#d9457ccc6a0b8d6b37d0eb252a23022478c5460c" + integrity sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w== + dependencies: + "@typescript-eslint/types" "5.62.0" + "@typescript-eslint/visitor-keys" "5.62.0" + +"@typescript-eslint/types@5.62.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.62.0.tgz#258607e60effa309f067608931c3df6fed41fd2f" + integrity sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ== + +"@typescript-eslint/typescript-estree@5.62.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz#7d17794b77fabcac615d6a48fb143330d962eb9b" + integrity sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA== + dependencies: + "@typescript-eslint/types" "5.62.0" + "@typescript-eslint/visitor-keys" "5.62.0" + debug "^4.3.4" + globby "^11.1.0" + is-glob "^4.0.3" + semver "^7.3.7" + tsutils "^3.21.0" + +"@typescript-eslint/utils@5.62.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.62.0.tgz#141e809c71636e4a75daa39faed2fb5f4b10df86" + integrity sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ== + dependencies: + "@eslint-community/eslint-utils" "^4.2.0" + "@types/json-schema" "^7.0.9" + "@types/semver" "^7.3.12" + "@typescript-eslint/scope-manager" "5.62.0" + "@typescript-eslint/types" "5.62.0" + "@typescript-eslint/typescript-estree" "5.62.0" + eslint-scope "^5.1.1" + semver "^7.3.7" + +"@typescript-eslint/visitor-keys@5.62.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz#2174011917ce582875954ffe2f6912d5931e353e" + integrity sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw== + dependencies: + "@typescript-eslint/types" "5.62.0" + eslint-visitor-keys "^3.3.0" + +"@ungap/structured-clone@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.2.0.tgz#756641adb587851b5ccb3e095daf27ae581c8406" + integrity sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ== + +"@vue/compiler-core@3.4.21": + version "3.4.21" + resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.4.21.tgz#868b7085378fc24e58c9aed14c8d62110a62be1a" + integrity sha512-MjXawxZf2SbZszLPYxaFCjxfibYrzr3eYbKxwpLR9EQN+oaziSu3qKVbwBERj1IFIB8OLUewxB5m/BFzi613og== + dependencies: + "@babel/parser" "^7.23.9" + "@vue/shared" "3.4.21" + entities "^4.5.0" + estree-walker "^2.0.2" + source-map-js "^1.0.2" + +"@vue/compiler-dom@3.4.21", "@vue/compiler-dom@^3.0.3": + version "3.4.21" + resolved "https://registry.yarnpkg.com/@vue/compiler-dom/-/compiler-dom-3.4.21.tgz#0077c355e2008207283a5a87d510330d22546803" + integrity sha512-IZC6FKowtT1sl0CR5DpXSiEB5ayw75oT2bma1BEhV7RRR1+cfwLrxc2Z8Zq/RGFzJ8w5r9QtCOvTjQgdn0IKmA== + dependencies: + "@vue/compiler-core" "3.4.21" + "@vue/shared" "3.4.21" + +"@vue/compiler-sfc@3.4.21", "@vue/compiler-sfc@^3.0.3": + version "3.4.21" + resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.4.21.tgz#4af920dc31ab99e1ff5d152b5fe0ad12181145b2" + integrity sha512-me7epoTxYlY+2CUM7hy9PCDdpMPfIwrOvAXud2Upk10g4YLv9UBW7kL798TvMeDhPthkZ0CONNrK2GoeI1ODiQ== + dependencies: + "@babel/parser" "^7.23.9" + "@vue/compiler-core" "3.4.21" + "@vue/compiler-dom" "3.4.21" + "@vue/compiler-ssr" "3.4.21" + "@vue/shared" "3.4.21" + estree-walker "^2.0.2" + magic-string "^0.30.7" + postcss "^8.4.35" + source-map-js "^1.0.2" + +"@vue/compiler-ssr@3.4.21": + version "3.4.21" + resolved "https://registry.yarnpkg.com/@vue/compiler-ssr/-/compiler-ssr-3.4.21.tgz#b84ae64fb9c265df21fc67f7624587673d324fef" + integrity sha512-M5+9nI2lPpAsgXOGQobnIueVqc9sisBFexh5yMIMRAPYLa7+5wEJs8iqOZc1WAa9WQbx9GR2twgznU8LTIiZ4Q== + dependencies: + "@vue/compiler-dom" "3.4.21" + "@vue/shared" "3.4.21" + +"@vue/reactivity@3.4.21": + version "3.4.21" + resolved "https://registry.yarnpkg.com/@vue/reactivity/-/reactivity-3.4.21.tgz#affd3415115b8ebf4927c8d2a0d6a24bccfa9f02" + integrity sha512-UhenImdc0L0/4ahGCyEzc/pZNwVgcglGy9HVzJ1Bq2Mm9qXOpP8RyNTjookw/gOCUlXSEtuZ2fUg5nrHcoqJcw== + dependencies: + "@vue/shared" "3.4.21" + +"@vue/runtime-core@3.4.21": + version "3.4.21" + resolved "https://registry.yarnpkg.com/@vue/runtime-core/-/runtime-core-3.4.21.tgz#3749c3f024a64c4c27ecd75aea4ca35634db0062" + integrity sha512-pQthsuYzE1XcGZznTKn73G0s14eCJcjaLvp3/DKeYWoFacD9glJoqlNBxt3W2c5S40t6CCcpPf+jG01N3ULyrA== + dependencies: + "@vue/reactivity" "3.4.21" + "@vue/shared" "3.4.21" + +"@vue/runtime-dom@3.4.21": + version "3.4.21" + resolved "https://registry.yarnpkg.com/@vue/runtime-dom/-/runtime-dom-3.4.21.tgz#91f867ef64eff232cac45095ab28ebc93ac74588" + integrity sha512-gvf+C9cFpevsQxbkRBS1NpU8CqxKw0ebqMvLwcGQrNpx6gqRDodqKqA+A2VZZpQ9RpK2f9yfg8VbW/EpdFUOJw== + dependencies: + "@vue/runtime-core" "3.4.21" + "@vue/shared" "3.4.21" + csstype "^3.1.3" + +"@vue/server-renderer@3.4.21": + version "3.4.21" + resolved "https://registry.yarnpkg.com/@vue/server-renderer/-/server-renderer-3.4.21.tgz#150751579d26661ee3ed26a28604667fa4222a97" + integrity sha512-aV1gXyKSN6Rz+6kZ6kr5+Ll14YzmIbeuWe7ryJl5muJ4uwSwY/aStXTixx76TwkZFJLm1aAlA/HSWEJ4EyiMkg== + dependencies: + "@vue/compiler-ssr" "3.4.21" + "@vue/shared" "3.4.21" + +"@vue/shared@3.4.21": + version "3.4.21" + resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.4.21.tgz#de526a9059d0a599f0b429af7037cd0c3ed7d5a1" + integrity sha512-PuJe7vDIi6VYSinuEbUIQgMIRZGgM8e4R+G+/dQTk0X1NEdvgvvgv7m+rfmDH1gZzyA1OjjoWskvHlfRNfQf3g== + +accepts@^1.3.5: + 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== + dependencies: + mime-types "~2.1.34" + negotiator "0.6.3" + +acorn-jsx@^5.3.2: + version "5.3.2" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" + integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== + +acorn@^8.8.2, acorn@^8.9.0: + version "8.11.3" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.11.3.tgz#71e0b14e13a4ec160724b38fb7b0f233b1b81d7a" + integrity sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg== + +ajv@^6.12.4: + version "6.12.6" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" + integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== + dependencies: + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + +ansi-regex@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== + +ansi-styles@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== + dependencies: + color-convert "^1.9.0" + +ansi-styles@^4.1.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + +anymatch@~3.1.2: + version "3.1.3" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" + integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + +argparse@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" + integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== + +array-buffer-byte-length@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz#1e5583ec16763540a27ae52eed99ff899223568f" + integrity sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg== + dependencies: + call-bind "^1.0.5" + is-array-buffer "^3.0.4" + +array-includes@^3.1.6, array-includes@^3.1.7: + version "3.1.8" + resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.8.tgz#5e370cbe172fdd5dd6530c1d4aadda25281ba97d" + integrity sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-abstract "^1.23.2" + es-object-atoms "^1.0.0" + get-intrinsic "^1.2.4" + is-string "^1.0.7" + +array-union@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" + integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== + +array.prototype.findlast@^1.2.4: + version "1.2.5" + resolved "https://registry.yarnpkg.com/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz#3e4fbcb30a15a7f5bf64cf2faae22d139c2e4904" + integrity sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-abstract "^1.23.2" + es-errors "^1.3.0" + es-object-atoms "^1.0.0" + es-shim-unscopables "^1.0.2" + +array.prototype.flat@^1.3.1: + version "1.3.2" + resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz#1476217df8cff17d72ee8f3ba06738db5b387d18" + integrity sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + es-shim-unscopables "^1.0.0" + +array.prototype.flatmap@^1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz#c9a7c6831db8e719d6ce639190146c24bbd3e527" + integrity sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + es-shim-unscopables "^1.0.0" + +array.prototype.toreversed@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/array.prototype.toreversed/-/array.prototype.toreversed-1.1.2.tgz#b989a6bf35c4c5051e1dc0325151bf8088954eba" + integrity sha512-wwDCoT4Ck4Cz7sLtgUmzR5UV3YF5mFHUlbChCzZBQZ+0m2cl/DH3tKgvphv1nKgFsJ48oCSg6p91q2Vm0I/ZMA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + es-shim-unscopables "^1.0.0" + +array.prototype.tosorted@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/array.prototype.tosorted/-/array.prototype.tosorted-1.1.3.tgz#c8c89348337e51b8a3c48a9227f9ce93ceedcba8" + integrity sha512-/DdH4TiTmOKzyQbp/eadcCVexiCb36xJg7HshYOYJnNZFDj33GEv0P7GxsynpShhq4OLYJzbGcBDkLsDt7MnNg== + dependencies: + call-bind "^1.0.5" + define-properties "^1.2.1" + es-abstract "^1.22.3" + es-errors "^1.1.0" + es-shim-unscopables "^1.0.2" + +arraybuffer.prototype.slice@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz#097972f4255e41bc3425e37dc3f6421cf9aefde6" + integrity sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A== + dependencies: + array-buffer-byte-length "^1.0.1" + call-bind "^1.0.5" + define-properties "^1.2.1" + es-abstract "^1.22.3" + es-errors "^1.2.1" + get-intrinsic "^1.2.3" + is-array-buffer "^3.0.4" + is-shared-array-buffer "^1.0.2" + +ast-metadata-inferer@^0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/ast-metadata-inferer/-/ast-metadata-inferer-0.8.0.tgz#0f94c3425e310d8da45823ab2161142e3f134343" + integrity sha512-jOMKcHht9LxYIEQu+RVd22vtgrPaVCtDRQ/16IGmurdzxvYbDd5ynxjnyrzLnieG96eTcAyaoj/wN/4/1FyyeA== + dependencies: + "@mdn/browser-compat-data" "^5.2.34" + +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== + +available-typed-arrays@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz#a5cc375d6a03c2efc87a553f3e0b1522def14846" + integrity sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ== + dependencies: + possible-typed-array-names "^1.0.0" + +balanced-match@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== + +base64-js@^1.3.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" + integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== + +binary-extensions@^2.0.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.3.0.tgz#f6e14a97858d327252200242d4ccfe522c445522" + integrity sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw== + +bl@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a" + integrity sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w== + dependencies: + buffer "^5.5.0" + inherits "^2.0.4" + readable-stream "^3.4.0" + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +braces@^3.0.2, braces@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" + integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== + dependencies: + fill-range "^7.0.1" + +brotli-size@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/brotli-size/-/brotli-size-4.0.0.tgz#a05ee3faad3c0e700a2f2da826ba6b4d76e69e5e" + integrity sha512-uA9fOtlTRC0iqKfzff1W34DXUA3GyVqbUaeo3Rw3d4gd1eavKVCETXrn3NzO74W+UVkG3UHu8WxUi+XvKI/huA== + dependencies: + duplexer "0.1.1" + +browserslist@^4.21.10, browserslist@^4.22.2: + version "4.23.0" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.23.0.tgz#8f3acc2bbe73af7213399430890f86c63a5674ab" + integrity sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ== + dependencies: + caniuse-lite "^1.0.30001587" + electron-to-chromium "^1.4.668" + node-releases "^2.0.14" + update-browserslist-db "^1.0.13" + +buffer-from@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" + integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== + +buffer@^5.5.0: + version "5.7.1" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" + integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.1.13" + +builtin-modules@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" + integrity sha512-wxXCdllwGhI2kCC0MnvTGYTMvnVZTvqgypkiTI8Pa5tcz2i6VqsqwYGgqwXji+4RgCzms6EajE4IxiUH6HH8nQ== + +builtin-modules@^3.1.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-3.3.0.tgz#cae62812b89801e9656336e46223e030386be7b6" + integrity sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw== + +cac@^6.6.1: + version "6.7.14" + resolved "https://registry.yarnpkg.com/cac/-/cac-6.7.14.tgz#804e1e6f506ee363cb0e3ccbb09cad5dd9870959" + integrity sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ== + +cache-content-type@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/cache-content-type/-/cache-content-type-1.0.1.tgz#035cde2b08ee2129f4a8315ea8f00a00dba1453c" + integrity sha512-IKufZ1o4Ut42YUrZSo8+qnMTrFuKkvyoLXUywKz9GJ5BrhOFGhLdkx9sG4KAnVvbY6kEcSFjLQul+DVmBm2bgA== + dependencies: + mime-types "^2.1.18" + ylru "^1.2.0" + +call-bind@^1.0.2, call-bind@^1.0.5, call-bind@^1.0.6, call-bind@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.7.tgz#06016599c40c56498c18769d2730be242b6fa3b9" + integrity sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w== + dependencies: + es-define-property "^1.0.0" + es-errors "^1.3.0" + function-bind "^1.1.2" + get-intrinsic "^1.2.4" + set-function-length "^1.2.1" + +callsites@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" + integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== + +caniuse-lite@^1.0.30001524, caniuse-lite@^1.0.30001587: + version "1.0.30001606" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001606.tgz#b4d5f67ab0746a3b8b5b6d1f06e39c51beb39a9e" + integrity sha512-LPbwnW4vfpJId225pwjZJOgX1m9sGfbw/RKJvw/t0QhYOOaTXHvkjVGFGPpvwEzufrjvTlsULnVTxdy4/6cqkg== + +chalk@^2.4.2: + version "2.4.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + +chalk@^4.0.0, chalk@^4.1.0: + version "4.1.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" + integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +chokidar@^3.4.2: + version "3.6.0" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.6.0.tgz#197c6cc669ef2a8dc5e7b4d97ee4e092c3eb0d5b" + integrity sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw== + dependencies: + anymatch "~3.1.2" + braces "~3.0.2" + glob-parent "~5.1.2" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.6.0" + optionalDependencies: + fsevents "~2.3.2" + +clean-css@^4.2.3: + version "4.2.4" + resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-4.2.4.tgz#733bf46eba4e607c6891ea57c24a989356831178" + integrity sha512-EJUDT7nDVFDvaQgAo2G/PJvxmp1o/c6iXLbswsBbUFXi1Nr+AjA2cKmfbKDMjMvzEe75g3P6JkaDDAKk96A85A== + dependencies: + source-map "~0.6.0" + +cli-cursor@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" + integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw== + dependencies: + restore-cursor "^3.1.0" + +cli-spinners@^2.5.0: + version "2.9.2" + resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.9.2.tgz#1773a8f4b9c4d6ac31563df53b3fc1d79462fe41" + integrity sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg== + +clone@^1.0.2: + version "1.0.4" + resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" + integrity sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg== + +co@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" + integrity sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ== + +color-convert@^1.9.0: + version "1.9.3" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== + dependencies: + color-name "1.1.3" + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== + +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +commander@^2.20.0: + version "2.20.3" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" + integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== + +commondir@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" + integrity sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg== + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== + +content-disposition@~0.5.2: + version "0.5.4" + resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe" + integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ== + dependencies: + safe-buffer "5.2.1" + +content-type@^1.0.4: + version "1.0.5" + resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918" + integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA== + +convert-source-map@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a" + integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== + +cookies@~0.9.0: + version "0.9.1" + resolved "https://registry.yarnpkg.com/cookies/-/cookies-0.9.1.tgz#3ffed6f60bb4fb5f146feeedba50acc418af67e3" + integrity sha512-TG2hpqe4ELx54QER/S3HQ9SRVnQnGBtKUz5bLQWtYAQ+o6GpgMs6sYUvaiJjVxb+UXwhRhAEP3m7LbsIZ77Hmw== + dependencies: + depd "~2.0.0" + keygrip "~1.1.0" + +cross-spawn@^7.0.0, cross-spawn@^7.0.2: + version "7.0.3" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" + integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + +csstype@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.3.tgz#d80ff294d114fb0e6ac500fbf85b60137d7eff81" + integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw== + +data-view-buffer@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/data-view-buffer/-/data-view-buffer-1.0.1.tgz#8ea6326efec17a2e42620696e671d7d5a8bc66b2" + integrity sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA== + dependencies: + call-bind "^1.0.6" + es-errors "^1.3.0" + is-data-view "^1.0.1" + +data-view-byte-length@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/data-view-byte-length/-/data-view-byte-length-1.0.1.tgz#90721ca95ff280677eb793749fce1011347669e2" + integrity sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ== + dependencies: + call-bind "^1.0.7" + es-errors "^1.3.0" + is-data-view "^1.0.1" + +data-view-byte-offset@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/data-view-byte-offset/-/data-view-byte-offset-1.0.0.tgz#5e0bbfb4828ed2d1b9b400cd8a7d119bca0ff18a" + integrity sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA== + dependencies: + call-bind "^1.0.6" + es-errors "^1.3.0" + is-data-view "^1.0.1" + +debug@^3.1.0: + version "3.2.7" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" + integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== + dependencies: + ms "^2.1.1" + +debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4: + version "4.3.4" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" + integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== + dependencies: + ms "2.1.2" + +deep-equal@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.0.1.tgz#f5d260292b660e084eff4cdbc9f08ad3247448b5" + integrity sha512-bHtC0iYvWhyaTzvV3CZgPeZQqCOBGyGsVV7v4eevpdkLHfiSrXUdBG+qAuSz4RI70sszvjQ1QSZ98An1yNwpSw== + +deep-is@^0.1.3: + version "0.1.4" + resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" + integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== + +deepmerge@^4.2.2: + version "4.3.1" + resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.1.tgz#44b5f2147cd3b00d4b56137685966f26fd25dd4a" + integrity sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A== + +defaults@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.4.tgz#b0b02062c1e2aa62ff5d9528f0f98baa90978d7a" + integrity sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A== + dependencies: + clone "^1.0.2" + +define-data-property@^1.0.1, define-data-property@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.4.tgz#894dc141bb7d3060ae4366f6a0107e68fbe48c5e" + integrity sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A== + dependencies: + es-define-property "^1.0.0" + es-errors "^1.3.0" + gopd "^1.0.1" + +define-properties@^1.1.3, define-properties@^1.2.0, define-properties@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.1.tgz#10781cc616eb951a80a034bafcaa7377f6af2b6c" + integrity sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg== + dependencies: + define-data-property "^1.0.1" + has-property-descriptors "^1.0.0" + object-keys "^1.1.1" + +delegates@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" + integrity sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ== + +depd@^2.0.0, depd@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" + integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== + +depd@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" + integrity sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ== + +destroy@^1.0.4: + version "1.2.0" + resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015" + integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg== + +dir-glob@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" + integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== + dependencies: + path-type "^4.0.0" + +doctrine@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" + integrity sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw== + dependencies: + esutils "^2.0.2" + +doctrine@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" + integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== + dependencies: + esutils "^2.0.2" + +dotenv-expand@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/dotenv-expand/-/dotenv-expand-5.1.0.tgz#3fbaf020bfd794884072ea26b1e9791d45a629f0" + integrity sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA== + +dotenv@^8.2.0: + version "8.6.0" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.6.0.tgz#061af664d19f7f4d8fc6e4ff9b584ce237adcb8b" + integrity sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g== + +duplexer@0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.1.tgz#ace6ff808c1ce66b57d1ebf97977acb02334cfc1" + integrity sha512-sxNZ+ljy+RA1maXoUReeqBBpBC6RLKmg5ewzV+x+mSETmWNoKdZN6vcQjpFROemza23hGFskJtFNoUWUaQ+R4Q== + +ee-first@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" + integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== + +electron-to-chromium@^1.4.668: + version "1.4.728" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.728.tgz#ac54d9d1b38752b920ec737a48c83dec2bf45ea1" + integrity sha512-Ud1v7hJJYIqehlUJGqR6PF1Ek8l80zWwxA6nGxigBsGJ9f9M2fciHyrIiNMerSHSH3p+0/Ia7jIlnDkt41h5cw== + +encodeurl@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" + integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w== + +end-of-stream@^1.1.0: + version "1.4.4" + resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" + integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== + dependencies: + once "^1.4.0" + +entities@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48" + integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw== + +es-abstract@^1.22.1, es-abstract@^1.22.3, es-abstract@^1.23.0, es-abstract@^1.23.1, es-abstract@^1.23.2: + version "1.23.3" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.23.3.tgz#8f0c5a35cd215312573c5a27c87dfd6c881a0aa0" + integrity sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A== + dependencies: + array-buffer-byte-length "^1.0.1" + arraybuffer.prototype.slice "^1.0.3" + available-typed-arrays "^1.0.7" + call-bind "^1.0.7" + data-view-buffer "^1.0.1" + data-view-byte-length "^1.0.1" + data-view-byte-offset "^1.0.0" + es-define-property "^1.0.0" + es-errors "^1.3.0" + es-object-atoms "^1.0.0" + es-set-tostringtag "^2.0.3" + es-to-primitive "^1.2.1" + function.prototype.name "^1.1.6" + get-intrinsic "^1.2.4" + get-symbol-description "^1.0.2" + globalthis "^1.0.3" + gopd "^1.0.1" + has-property-descriptors "^1.0.2" + has-proto "^1.0.3" + has-symbols "^1.0.3" + hasown "^2.0.2" + internal-slot "^1.0.7" + is-array-buffer "^3.0.4" + is-callable "^1.2.7" + is-data-view "^1.0.1" + is-negative-zero "^2.0.3" + is-regex "^1.1.4" + is-shared-array-buffer "^1.0.3" + is-string "^1.0.7" + is-typed-array "^1.1.13" + is-weakref "^1.0.2" + object-inspect "^1.13.1" + object-keys "^1.1.1" + object.assign "^4.1.5" + regexp.prototype.flags "^1.5.2" + safe-array-concat "^1.1.2" + safe-regex-test "^1.0.3" + string.prototype.trim "^1.2.9" + string.prototype.trimend "^1.0.8" + string.prototype.trimstart "^1.0.8" + typed-array-buffer "^1.0.2" + typed-array-byte-length "^1.0.1" + typed-array-byte-offset "^1.0.2" + typed-array-length "^1.0.6" + unbox-primitive "^1.0.2" + which-typed-array "^1.1.15" + +es-define-property@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.0.tgz#c7faefbdff8b2696cf5f46921edfb77cc4ba3845" + integrity sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ== + dependencies: + get-intrinsic "^1.2.4" + +es-errors@^1.1.0, es-errors@^1.2.1, es-errors@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" + integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== + +es-iterator-helpers@^1.0.17: + version "1.0.18" + resolved "https://registry.yarnpkg.com/es-iterator-helpers/-/es-iterator-helpers-1.0.18.tgz#4d3424f46b24df38d064af6fbbc89274e29ea69d" + integrity sha512-scxAJaewsahbqTYrGKJihhViaM6DDZDDoucfvzNbK0pOren1g/daDQ3IAhzn+1G14rBG7w+i5N+qul60++zlKA== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-abstract "^1.23.0" + es-errors "^1.3.0" + es-set-tostringtag "^2.0.3" + function-bind "^1.1.2" + get-intrinsic "^1.2.4" + globalthis "^1.0.3" + has-property-descriptors "^1.0.2" + has-proto "^1.0.3" + has-symbols "^1.0.3" + internal-slot "^1.0.7" + iterator.prototype "^1.1.2" + safe-array-concat "^1.1.2" + +es-module-lexer@^0.3.25: + version "0.3.26" + resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-0.3.26.tgz#7b507044e97d5b03b01d4392c74ffeb9c177a83b" + integrity sha512-Va0Q/xqtrss45hWzP8CZJwzGSZJjDM5/MJRE3IXXnUCcVLElR9BRaE9F62BopysASyc4nM3uwhSW7FFB9nlWAA== + +es-object-atoms@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/es-object-atoms/-/es-object-atoms-1.0.0.tgz#ddb55cd47ac2e240701260bc2a8e31ecb643d941" + integrity sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw== + dependencies: + es-errors "^1.3.0" + +es-set-tostringtag@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz#8bb60f0a440c2e4281962428438d58545af39777" + integrity sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ== + dependencies: + get-intrinsic "^1.2.4" + has-tostringtag "^1.0.2" + hasown "^2.0.1" + +es-shim-unscopables@^1.0.0, es-shim-unscopables@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz#1f6942e71ecc7835ed1c8a83006d8771a63a3763" + integrity sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw== + dependencies: + hasown "^2.0.0" + +es-to-primitive@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" + integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA== + dependencies: + is-callable "^1.1.4" + is-date-object "^1.0.1" + is-symbol "^1.0.2" + +esbuild@^0.8.12: + version "0.8.57" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.8.57.tgz#a42d02bc2b57c70bcd0ef897fe244766bb6dd926" + integrity sha512-j02SFrUwFTRUqiY0Kjplwjm1psuzO1d6AjaXKuOR9hrY0HuPsT6sV42B6myW34h1q4CRy+Y3g4RU/cGJeI/nNA== + +escalade@^3.1.1: + version "3.1.2" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.2.tgz#54076e9ab29ea5bf3d8f1ed62acffbb88272df27" + integrity sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA== + +escape-html@^1.0.3: + version "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.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== + +escape-string-regexp@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" + integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== + +eslint-config-preact@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/eslint-config-preact/-/eslint-config-preact-1.3.0.tgz#17b72813078f4d1d4d2b79938ec21f92338bc9c0" + integrity sha512-yHYXg5qNzEJd3D/30AmsIW0W8MuY858KpApXp7xxBF08IYUljSKCOqMx+dVucXHQnAm7+11wOnMkgVHIBAechw== + dependencies: + "@babel/core" "^7.13.16" + "@babel/eslint-parser" "^7.13.14" + "@babel/plugin-syntax-class-properties" "^7.12.13" + "@babel/plugin-syntax-decorators" "^7.12.13" + "@babel/plugin-syntax-jsx" "^7.12.13" + eslint-plugin-compat "^4.0.0" + eslint-plugin-jest "^25.2.4" + eslint-plugin-react "^7.27.0" + eslint-plugin-react-hooks "^4.3.0" + +eslint-plugin-compat@^4.0.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-compat/-/eslint-plugin-compat-4.2.0.tgz#eeaf80daa1afe495c88a47e9281295acae45c0aa" + integrity sha512-RDKSYD0maWy5r7zb5cWQS+uSPc26mgOzdORJ8hxILmWM7S/Ncwky7BcAtXVY5iRbKjBdHsWU8Yg7hfoZjtkv7w== + dependencies: + "@mdn/browser-compat-data" "^5.3.13" + ast-metadata-inferer "^0.8.0" + browserslist "^4.21.10" + caniuse-lite "^1.0.30001524" + find-up "^5.0.0" + lodash.memoize "^4.1.2" + semver "^7.5.4" + +eslint-plugin-jest@^25.2.4: + version "25.7.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-jest/-/eslint-plugin-jest-25.7.0.tgz#ff4ac97520b53a96187bad9c9814e7d00de09a6a" + integrity sha512-PWLUEXeeF7C9QGKqvdSbzLOiLTx+bno7/HC9eefePfEb257QFHg7ye3dh80AZVkaa/RQsBB1Q/ORQvg2X7F0NQ== + dependencies: + "@typescript-eslint/experimental-utils" "^5.0.0" + +eslint-plugin-react-hooks@^4.3.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz#4c3e697ad95b77e93f8646aaa1630c1ba607edd3" + integrity sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g== + +eslint-plugin-react@^7.27.0: + version "7.34.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.34.1.tgz#6806b70c97796f5bbfb235a5d3379ece5f4da997" + integrity sha512-N97CxlouPT1AHt8Jn0mhhN2RrADlUAsk1/atcT2KyA/l9Q/E6ll7OIGwNumFmWfZ9skV3XXccYS19h80rHtgkw== + dependencies: + array-includes "^3.1.7" + array.prototype.findlast "^1.2.4" + array.prototype.flatmap "^1.3.2" + array.prototype.toreversed "^1.1.2" + array.prototype.tosorted "^1.1.3" + doctrine "^2.1.0" + es-iterator-helpers "^1.0.17" + estraverse "^5.3.0" + jsx-ast-utils "^2.4.1 || ^3.0.0" + minimatch "^3.1.2" + object.entries "^1.1.7" + object.fromentries "^2.0.7" + object.hasown "^1.1.3" + object.values "^1.1.7" + prop-types "^15.8.1" + resolve "^2.0.0-next.5" + semver "^6.3.1" + string.prototype.matchall "^4.0.10" + +eslint-plugin-simple-import-sort@^12.0.0: + version "12.0.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-simple-import-sort/-/eslint-plugin-simple-import-sort-12.0.0.tgz#3cfa05d74509bd4dc329a956938823812194dbb6" + integrity sha512-8o0dVEdAkYap0Cn5kNeklaKcT1nUsa3LITWEuFk3nJifOoD+5JQGoyDUW2W/iPWwBsNBJpyJS9y4je/BgxLcyQ== + +eslint-scope@5.1.1, eslint-scope@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" + integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== + dependencies: + esrecurse "^4.3.0" + estraverse "^4.1.1" + +eslint-scope@^7.2.2: + version "7.2.2" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.2.2.tgz#deb4f92563390f32006894af62a22dba1c46423f" + integrity sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg== + dependencies: + esrecurse "^4.3.0" + estraverse "^5.2.0" + +eslint-visitor-keys@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz#f65328259305927392c938ed44eb0a5c9b2bd303" + integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw== + +eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4.3: + version "3.4.3" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800" + integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== + +eslint@^8.19.0: + version "8.57.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.57.0.tgz#c786a6fd0e0b68941aaf624596fb987089195668" + integrity sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ== + dependencies: + "@eslint-community/eslint-utils" "^4.2.0" + "@eslint-community/regexpp" "^4.6.1" + "@eslint/eslintrc" "^2.1.4" + "@eslint/js" "8.57.0" + "@humanwhocodes/config-array" "^0.11.14" + "@humanwhocodes/module-importer" "^1.0.1" + "@nodelib/fs.walk" "^1.2.8" + "@ungap/structured-clone" "^1.2.0" + ajv "^6.12.4" + chalk "^4.0.0" + cross-spawn "^7.0.2" + debug "^4.3.2" + doctrine "^3.0.0" + escape-string-regexp "^4.0.0" + eslint-scope "^7.2.2" + eslint-visitor-keys "^3.4.3" + espree "^9.6.1" + esquery "^1.4.2" + esutils "^2.0.2" + fast-deep-equal "^3.1.3" + file-entry-cache "^6.0.1" + find-up "^5.0.0" + glob-parent "^6.0.2" + globals "^13.19.0" + graphemer "^1.4.0" + ignore "^5.2.0" + imurmurhash "^0.1.4" + is-glob "^4.0.0" + is-path-inside "^3.0.3" + js-yaml "^4.1.0" + json-stable-stringify-without-jsonify "^1.0.1" + levn "^0.4.1" + lodash.merge "^4.6.2" + minimatch "^3.1.2" + natural-compare "^1.4.0" + optionator "^0.9.3" + strip-ansi "^6.0.1" + text-table "^0.2.0" + +espree@^9.6.0, espree@^9.6.1: + version "9.6.1" + resolved "https://registry.yarnpkg.com/espree/-/espree-9.6.1.tgz#a2a17b8e434690a5432f2f8018ce71d331a48c6f" + integrity sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ== + dependencies: + acorn "^8.9.0" + acorn-jsx "^5.3.2" + eslint-visitor-keys "^3.4.1" + +esquery@^1.4.2: + version "1.5.0" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.5.0.tgz#6ce17738de8577694edd7361c57182ac8cb0db0b" + integrity sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg== + dependencies: + estraverse "^5.1.0" + +esrecurse@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" + integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== + dependencies: + estraverse "^5.2.0" + +estraverse@^4.1.1: + version "4.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" + integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== + +estraverse@^5.1.0, estraverse@^5.2.0, estraverse@^5.3.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" + integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== + +estree-walker@^0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-0.6.1.tgz#53049143f40c6eb918b23671d1fe3219f3a1b362" + integrity sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w== + +estree-walker@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-1.0.1.tgz#31bc5d612c96b704106b477e6dd5d8aa138cb700" + integrity sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg== + +estree-walker@^2.0.1, estree-walker@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-2.0.2.tgz#52f010178c2a4c117a7757cfe942adb7d2da4cac" + integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w== + +esutils@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" + integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== + +etag@^1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" + integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg== + +eventemitter3@^4.0.0: + version "4.0.7" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" + integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== + +execa@^4.0.3: + version "4.1.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-4.1.0.tgz#4e5491ad1572f2f17a77d388c6c857135b22847a" + integrity sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA== + dependencies: + cross-spawn "^7.0.0" + get-stream "^5.0.0" + human-signals "^1.1.1" + is-stream "^2.0.0" + merge-stream "^2.0.0" + npm-run-path "^4.0.0" + onetime "^5.1.0" + signal-exit "^3.0.2" + strip-final-newline "^2.0.0" + +fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== + +fast-glob@^3.2.9: + version "3.3.2" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.2.tgz#a904501e57cfdd2ffcded45e99a54fef55e46129" + integrity sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow== + dependencies: + "@nodelib/fs.stat" "^2.0.2" + "@nodelib/fs.walk" "^1.2.3" + glob-parent "^5.1.2" + merge2 "^1.3.0" + micromatch "^4.0.4" + +fast-json-stable-stringify@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" + integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== + +fast-levenshtein@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== + +fastq@^1.6.0: + version "1.17.1" + resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.17.1.tgz#2a523f07a4e7b1e81a42b91b8bf2254107753b47" + integrity sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w== + dependencies: + reusify "^1.0.4" + +file-entry-cache@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027" + integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg== + dependencies: + flat-cache "^3.0.4" + +fill-range@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" + integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== + dependencies: + to-regex-range "^5.0.1" + +find-up@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" + integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== + dependencies: + locate-path "^6.0.0" + path-exists "^4.0.0" + +flat-cache@^3.0.4: + version "3.2.0" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.2.0.tgz#2c0c2d5040c99b1632771a9d105725c0115363ee" + integrity sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw== + dependencies: + flatted "^3.2.9" + keyv "^4.5.3" + rimraf "^3.0.2" + +flatted@^3.2.9: + version "3.3.1" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.1.tgz#21db470729a6734d4997002f439cb308987f567a" + integrity sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw== + +follow-redirects@^1.0.0: + version "1.15.6" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.6.tgz#7f815c0cda4249c74ff09e95ef97c23b5fd0399b" + integrity sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA== + +for-each@^0.3.3: + version "0.3.3" + resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e" + integrity sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw== + dependencies: + is-callable "^1.1.3" + +fresh@~0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" + integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q== + +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" + integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== + +fsevents@~2.3.2: + version "2.3.3" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" + integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== + +function-bind@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" + integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== + +function.prototype.name@^1.1.5, function.prototype.name@^1.1.6: + version "1.1.6" + resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.6.tgz#cdf315b7d90ee77a4c6ee216c3c3362da07533fd" + integrity sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + functions-have-names "^1.2.3" + +functions-have-names@^1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" + integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== + +gensync@^1.0.0-beta.2: + version "1.0.0-beta.2" + resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" + integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== + +get-intrinsic@^1.1.3, get-intrinsic@^1.2.1, get-intrinsic@^1.2.3, get-intrinsic@^1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.4.tgz#e385f5a4b5227d449c3eabbad05494ef0abbeadd" + integrity sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ== + dependencies: + es-errors "^1.3.0" + function-bind "^1.1.2" + has-proto "^1.0.1" + has-symbols "^1.0.3" + hasown "^2.0.0" + +get-stream@^5.0.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3" + integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA== + dependencies: + pump "^3.0.0" + +get-symbol-description@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.0.2.tgz#533744d5aa20aca4e079c8e5daf7fd44202821f5" + integrity sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg== + dependencies: + call-bind "^1.0.5" + es-errors "^1.3.0" + get-intrinsic "^1.2.4" + +glob-parent@^5.1.2, glob-parent@~5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== + dependencies: + is-glob "^4.0.1" + +glob-parent@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3" + integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== + dependencies: + is-glob "^4.0.3" + +glob@^7.1.3, glob@^7.1.6: + version "7.2.3" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" + integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.1.1" + once "^1.3.0" + path-is-absolute "^1.0.0" + +globals@^11.1.0: + version "11.12.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" + integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== + +globals@^13.19.0: + version "13.24.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-13.24.0.tgz#8432a19d78ce0c1e833949c36adb345400bb1171" + integrity sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ== + dependencies: + type-fest "^0.20.2" + +globalthis@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.3.tgz#5852882a52b80dc301b0660273e1ed082f0b6ccf" + integrity sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA== + dependencies: + define-properties "^1.1.3" + +globby@^11.0.0, globby@^11.1.0: + version "11.1.0" + resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" + integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== + dependencies: + array-union "^2.1.0" + dir-glob "^3.0.1" + fast-glob "^3.2.9" + ignore "^5.2.0" + merge2 "^1.4.1" + slash "^3.0.0" + +globrex@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/globrex/-/globrex-0.1.2.tgz#dd5d9ec826232730cd6793a5e33a9302985e6098" + integrity sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg== + +gopd@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c" + integrity sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA== + dependencies: + get-intrinsic "^1.1.3" + +graceful-fs@^4.1.6, graceful-fs@^4.2.0: + version "4.2.11" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" + integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== + +graphemer@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6" + integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag== + +has-bigints@^1.0.1, has-bigints@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.2.tgz#0871bd3e3d51626f6ca0966668ba35d5602d6eaa" + integrity sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ== + +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw== + +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + +has-property-descriptors@^1.0.0, has-property-descriptors@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz#963ed7d071dc7bf5f084c5bfbe0d1b6222586854" + integrity sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg== + dependencies: + es-define-property "^1.0.0" + +has-proto@^1.0.1, has-proto@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.3.tgz#b31ddfe9b0e6e9914536a6ab286426d0214f77fd" + integrity sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q== + +has-symbols@^1.0.2, has-symbols@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" + integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== + +has-tostringtag@^1.0.0, has-tostringtag@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz#2cdc42d40bef2e5b4eeab7c01a73c54ce7ab5abc" + integrity sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw== + dependencies: + has-symbols "^1.0.3" + +hash-sum@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/hash-sum/-/hash-sum-2.0.0.tgz#81d01bb5de8ea4a214ad5d6ead1b523460b0b45a" + integrity sha512-WdZTbAByD+pHfl/g9QSsBIIwy8IT+EsPiKDs0KNX+zSHhdDLFKdZu0BQHljvO+0QI/BasbMSUa8wYNCZTvhslg== + +hasown@^2.0.0, hasown@^2.0.1, hasown@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" + integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== + dependencies: + function-bind "^1.1.2" + +http-assert@^1.3.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/http-assert/-/http-assert-1.5.0.tgz#c389ccd87ac16ed2dfa6246fd73b926aa00e6b8f" + integrity sha512-uPpH7OKX4H25hBmU6G1jWNaqJGpTXxey+YOUizJUAgu0AjLUeC8D73hTrhvDS5D+GJN1DN1+hhc/eF/wpxtp0w== + dependencies: + deep-equal "~1.0.1" + http-errors "~1.8.0" + +http-errors@^1.6.3, http-errors@^1.7.3, 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 "~1.1.2" + inherits "2.0.4" + setprototypeof "1.2.0" + statuses ">= 1.5.0 < 2" + toidentifier "1.0.1" + +http-errors@~1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.4.0.tgz#6c0242dea6b3df7afda153c71089b31c6e82aabf" + integrity sha512-oLjPqve1tuOl5aRhv8GK5eHpqP1C9fb+Ol+XTLjKfLltE44zdDbEdjPSbU7Ch5rSNsVFqZn97SrMmZLdu1/YMw== + dependencies: + inherits "2.0.1" + statuses ">= 1.2.1 < 2" + +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-proxy@^1.16.2: + version "1.18.1" + resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.18.1.tgz#401541f0534884bbf95260334e72f88ee3976549" + integrity sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ== + dependencies: + eventemitter3 "^4.0.0" + follow-redirects "^1.0.0" + requires-port "^1.0.0" + +human-signals@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-1.1.1.tgz#c5b1cd14f50aeae09ab6c59fe63ba3395fe4dfa3" + integrity sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw== + +ieee754@^1.1.13: + version "1.2.1" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" + integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== + +ignore@^5.2.0: + version "5.3.1" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.1.tgz#5073e554cd42c5b33b394375f538b8593e34d4ef" + integrity sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw== + +import-fresh@^3.2.1: + version "3.3.0" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" + integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== + dependencies: + parent-module "^1.0.0" + resolve-from "^4.0.0" + +imurmurhash@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2, inherits@2.0.4, inherits@^2.0.3, inherits@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +inherits@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1" + integrity sha512-8nWq2nLTAwd02jTqJExUYFSD/fKq6VH9Y/oG2accc/kdI0V98Bag8d5a4gi3XHz73rDWa2PvTtvcWYquKqSENA== + +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== + +internal-slot@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.7.tgz#c06dcca3ed874249881007b0a5523b172a190802" + integrity sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g== + dependencies: + es-errors "^1.3.0" + hasown "^2.0.0" + side-channel "^1.0.4" + +is-array-buffer@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.4.tgz#7a1f92b3d61edd2bc65d24f130530ea93d7fae98" + integrity sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.2.1" + +is-async-function@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-async-function/-/is-async-function-2.0.0.tgz#8e4418efd3e5d3a6ebb0164c05ef5afb69aa9646" + integrity sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA== + dependencies: + has-tostringtag "^1.0.0" + +is-bigint@^1.0.1: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.4.tgz#08147a1875bc2b32005d41ccd8291dffc6691df3" + integrity sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg== + dependencies: + has-bigints "^1.0.1" + +is-binary-path@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" + integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== + dependencies: + binary-extensions "^2.0.0" + +is-boolean-object@^1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.2.tgz#5c6dc200246dd9321ae4b885a114bb1f75f63719" + integrity sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + +is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.2.7: + version "1.2.7" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" + integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== + +is-core-module@^2.13.0: + version "2.13.1" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.13.1.tgz#ad0d7532c6fea9da1ebdc82742d74525c6273384" + integrity sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw== + dependencies: + hasown "^2.0.0" + +is-data-view@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-data-view/-/is-data-view-1.0.1.tgz#4b4d3a511b70f3dc26d42c03ca9ca515d847759f" + integrity sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w== + dependencies: + is-typed-array "^1.1.13" + +is-date-object@^1.0.1, is-date-object@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.5.tgz#0841d5536e724c25597bf6ea62e1bd38298df31f" + integrity sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ== + dependencies: + has-tostringtag "^1.0.0" + +is-docker@^2.0.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa" + integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ== + +is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== + +is-finalizationregistry@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-finalizationregistry/-/is-finalizationregistry-1.0.2.tgz#c8749b65f17c133313e661b1289b95ad3dbd62e6" + integrity sha512-0by5vtUJs8iFQb5TYUHHPudOR+qXYIMKtiUzvLIZITZUjknFmziyBJuLhVRc+Ds0dREFlskDNJKYIdIzu/9pfw== + dependencies: + call-bind "^1.0.2" + +is-generator-function@^1.0.10, is-generator-function@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.0.10.tgz#f1558baf1ac17e0deea7c0415c438351ff2b3c72" + integrity sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A== + dependencies: + has-tostringtag "^1.0.0" + +is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: + version "4.0.3" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== + dependencies: + is-extglob "^2.1.1" + +is-interactive@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e" + integrity sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w== + +is-map@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.3.tgz#ede96b7fe1e270b3c4465e3a465658764926d62e" + integrity sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw== + +is-module@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-module/-/is-module-1.0.0.tgz#3258fb69f78c14d5b815d664336b4cffb6441591" + integrity sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g== + +is-negative-zero@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.3.tgz#ced903a027aca6381b777a5743069d7376a49747" + integrity sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw== + +is-number-object@^1.0.4: + version "1.0.7" + resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.7.tgz#59d50ada4c45251784e9904f5246c742f07a42fc" + integrity sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ== + dependencies: + has-tostringtag "^1.0.0" + +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + +is-path-inside@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" + integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== + +is-reference@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/is-reference/-/is-reference-1.2.1.tgz#8b2dac0b371f4bc994fdeaba9eb542d03002d0b7" + integrity sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ== + dependencies: + "@types/estree" "*" + +is-regex@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958" + integrity sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + +is-set@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/is-set/-/is-set-2.0.3.tgz#8ab209ea424608141372ded6e0cb200ef1d9d01d" + integrity sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg== + +is-shared-array-buffer@^1.0.2, is-shared-array-buffer@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz#1237f1cba059cdb62431d378dcc37d9680181688" + integrity sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg== + dependencies: + call-bind "^1.0.7" + +is-stream@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" + integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== + +is-string@^1.0.5, is-string@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.7.tgz#0dd12bf2006f255bb58f695110eff7491eebc0fd" + integrity sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg== + dependencies: + has-tostringtag "^1.0.0" + +is-symbol@^1.0.2, is-symbol@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.4.tgz#a6dac93b635b063ca6872236de88910a57af139c" + integrity sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg== + dependencies: + has-symbols "^1.0.2" + +is-typed-array@^1.1.13: + version "1.1.13" + resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.13.tgz#d6c5ca56df62334959322d7d7dd1cca50debe229" + integrity sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw== + dependencies: + which-typed-array "^1.1.14" + +is-unicode-supported@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" + integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== + +is-weakmap@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/is-weakmap/-/is-weakmap-2.0.2.tgz#bf72615d649dfe5f699079c54b83e47d1ae19cfd" + integrity sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w== + +is-weakref@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2" + integrity sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ== + dependencies: + call-bind "^1.0.2" + +is-weakset@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/is-weakset/-/is-weakset-2.0.3.tgz#e801519df8c0c43e12ff2834eead84ec9e624007" + integrity sha512-LvIm3/KWzS9oRFHugab7d+M/GcBXuXX5xZkzPmN+NxihdQlZUQ4dWuSV1xR/sq6upL1TJEDrfBgRepHFdBtSNQ== + dependencies: + call-bind "^1.0.7" + get-intrinsic "^1.2.4" + +is-wsl@^2.1.1: + version "2.2.0" + resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271" + integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww== + dependencies: + is-docker "^2.0.0" + +isarray@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" + integrity sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ== + +isarray@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723" + integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw== + +isbuiltin@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isbuiltin/-/isbuiltin-1.0.0.tgz#4453b2915690cb47c0cb9c9255a0807778315c96" + integrity sha512-5D5GIRCjYK/KtHQ2vIPIwKcma05iHYJag0syBtpo8/V1LuPt+a6Zowyrgpn0Bxw2pV9m2lxmX/0Z8OMQvWLXfw== + dependencies: + builtin-modules "^1.1.1" + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== + +iterator.prototype@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/iterator.prototype/-/iterator.prototype-1.1.2.tgz#5e29c8924f01916cb9335f1ff80619dcff22b0c0" + integrity sha512-DR33HMMr8EzwuRL8Y9D3u2BMj8+RqSE850jfGu59kS7tbmPLzGkZmVSfyCFSDxuZiEY6Rzt3T2NA/qU+NwVj1w== + dependencies: + define-properties "^1.2.1" + get-intrinsic "^1.2.1" + has-symbols "^1.0.3" + reflect.getprototypeof "^1.0.4" + set-function-name "^2.0.1" + +jest-worker@^26.2.1: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-26.6.2.tgz#7f72cbc4d643c365e27b9fd775f9d0eaa9c7a8ed" + integrity sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ== + dependencies: + "@types/node" "*" + merge-stream "^2.0.0" + supports-color "^7.0.0" + +"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== + +js-yaml@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" + integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== + dependencies: + argparse "^2.0.1" + +jsesc@^2.5.1: + version "2.5.2" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" + integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== + +json-buffer@3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" + integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== + +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== + +json-stable-stringify-without-jsonify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" + integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== + +json5@^2.2.3: + version "2.2.3" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" + integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== + +jsonfile@^6.0.1: + version "6.1.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae" + integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ== + dependencies: + universalify "^2.0.0" + optionalDependencies: + graceful-fs "^4.1.6" + +"jsx-ast-utils@^2.4.1 || ^3.0.0": + version "3.3.5" + resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz#4766bd05a8e2a11af222becd19e15575e52a853a" + integrity sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ== + dependencies: + array-includes "^3.1.6" + array.prototype.flat "^1.3.1" + object.assign "^4.1.4" + object.values "^1.1.6" + +keygrip@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/keygrip/-/keygrip-1.1.0.tgz#871b1681d5e159c62a445b0c74b615e0917e7226" + integrity sha512-iYSchDJ+liQ8iwbSI2QqsQOvqv58eJCEanyJPJi+Khyu8smkcKSFUCbPwzFcL7YVtZ6eONjqRX/38caJ7QjRAQ== + dependencies: + tsscmp "1.0.6" + +keyv@^4.5.3: + version "4.5.4" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93" + integrity sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw== + dependencies: + json-buffer "3.0.1" + +klona@^2.0.4: + version "2.0.6" + resolved "https://registry.yarnpkg.com/klona/-/klona-2.0.6.tgz#85bffbf819c03b2f53270412420a4555ef882e22" + integrity sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA== + +koa-compose@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/koa-compose/-/koa-compose-4.1.0.tgz#507306b9371901db41121c812e923d0d67d3e877" + integrity sha512-8ODW8TrDuMYvXRwra/Kh7/rJo9BtOfPc6qO8eAfC80CnCvSjSl0bkRM24X6/XBBEyj0v1nRUQ1LyOy3dbqOWXw== + +koa-conditional-get@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/koa-conditional-get/-/koa-conditional-get-3.0.0.tgz#552cb64a217dfb907e90b7c34f42009e441c4b8e" + integrity sha512-VKyPS7SuNH26TjTV2IRz+oh0HV/jc2lYAo51PTQTkj0XFn8ebNZW9riczmrW7ZVBFSnls1Z88DPUYKnvVymruA== + +koa-convert@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/koa-convert/-/koa-convert-2.0.0.tgz#86a0c44d81d40551bae22fee6709904573eea4f5" + integrity sha512-asOvN6bFlSnxewce2e/DK3p4tltyfC4VM7ZwuTuepI7dEQVcvpyFuBcEARu1+Hxg8DIwytce2n7jrZtRlPrARA== + dependencies: + co "^4.6.0" + koa-compose "^4.1.0" + +koa-etag@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/koa-etag/-/koa-etag-4.0.0.tgz#2c2bb7ae69ca1ac6ced09ba28dcb78523c810414" + integrity sha512-1cSdezCkBWlyuB9l6c/IFoe1ANCDdPBxkDkRiaIup40xpUub6U/wwRXoKBZw/O5BifX9OlqAjYnDyzM6+l+TAg== + dependencies: + etag "^1.8.1" + +koa-proxies@^0.11.0: + version "0.11.0" + resolved "https://registry.yarnpkg.com/koa-proxies/-/koa-proxies-0.11.0.tgz#43dde4260080f7cb0f284655f85cf654bbe9ec84" + integrity sha512-iXGRADBE0fM7g7AttNOlLZ/cCFKXeVMHbFJKIRb0dUCrSYXi02loyVSdBlKlBQ5ZfVKJLo9Q9FyqwVTp1poVVA== + dependencies: + http-proxy "^1.16.2" + path-match "^1.2.4" + +koa-send@^5.0.0, koa-send@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/koa-send/-/koa-send-5.0.1.tgz#39dceebfafb395d0d60beaffba3a70b4f543fe79" + integrity sha512-tmcyQ/wXXuxpDxyNXv5yNNkdAMdFRqwtegBXUaowiQzUKqJehttS0x2j0eOZDQAyloAth5w6wwBImnFzkUz3pQ== + dependencies: + debug "^4.1.1" + http-errors "^1.7.3" + resolve-path "^1.4.0" + +koa-static@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/koa-static/-/koa-static-5.0.0.tgz#5e92fc96b537ad5219f425319c95b64772776943" + integrity sha512-UqyYyH5YEXaJrf9S8E23GoJFQZXkBVJ9zYYMPGz919MSX1KuvAcycIuS0ci150HCoPf4XQVhQ84Qf8xRPWxFaQ== + dependencies: + debug "^3.1.0" + koa-send "^5.0.0" + +koa@^2.13.0: + version "2.15.2" + resolved "https://registry.yarnpkg.com/koa/-/koa-2.15.2.tgz#1e4afe1482d01bd24ed6e30f630a960411f5ebf2" + integrity sha512-MXTeZH3M6AJ8ukW2QZ8wqO3Dcdfh2WRRmjCBkEP+NhKNCiqlO5RDqHmSnsyNrbRJrdjyvIGSJho4vQiWgQJSVA== + dependencies: + accepts "^1.3.5" + cache-content-type "^1.0.0" + content-disposition "~0.5.2" + content-type "^1.0.4" + cookies "~0.9.0" + debug "^4.3.2" + delegates "^1.0.0" + depd "^2.0.0" + destroy "^1.0.4" + encodeurl "^1.0.2" + escape-html "^1.0.3" + fresh "~0.5.2" + http-assert "^1.3.0" + http-errors "^1.6.3" + is-generator-function "^1.0.7" + koa-compose "^4.1.0" + koa-convert "^2.0.0" + on-finished "^2.3.0" + only "~0.0.2" + parseurl "^1.3.2" + statuses "^1.5.0" + type-is "^1.6.16" + vary "^1.1.2" + +levn@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" + integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== + dependencies: + prelude-ls "^1.2.1" + type-check "~0.4.0" + +lilconfig@^2.0.5: + version "2.1.0" + resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.1.0.tgz#78e23ac89ebb7e1bfbf25b18043de756548e7f52" + integrity sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ== + +locate-path@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" + integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== + dependencies: + p-locate "^5.0.0" + +lodash.memoize@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" + integrity sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag== + +lodash.merge@^4.6.2: + version "4.6.2" + resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" + integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== + +log-symbols@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" + integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg== + dependencies: + chalk "^4.1.0" + is-unicode-supported "^0.1.0" + +loose-envify@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" + integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== + dependencies: + js-tokens "^3.0.0 || ^4.0.0" + +lru-cache@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" + integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== + dependencies: + yallist "^3.0.2" + +lru-cache@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" + integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== + dependencies: + yallist "^4.0.0" + +magic-string@^0.25.7: + version "0.25.9" + resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.9.tgz#de7f9faf91ef8a1c91d02c2e5314c8277dbcdd1c" + integrity sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ== + dependencies: + sourcemap-codec "^1.4.8" + +magic-string@^0.30.7: + version "0.30.9" + resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.9.tgz#8927ae21bfdd856310e07a1bc8dd5e73cb6c251d" + integrity sha512-S1+hd+dIrC8EZqKyT9DstTH/0Z+f76kmmvZnkfQVmOpDEF9iVgdYif3Q/pIWHmCoo59bQVGW0kVL3e2nl+9+Sw== + dependencies: + "@jridgewell/sourcemap-codec" "^1.4.15" + +media-typer@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" + integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ== + +merge-source-map@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/merge-source-map/-/merge-source-map-1.1.0.tgz#2fdde7e6020939f70906a68f2d7ae685e4c8c646" + integrity sha512-Qkcp7P2ygktpMPh2mCQZaf3jhN6D3Z/qVZHSdWvQ+2Ef5HgRAPBO57A77+ENm0CPx2+1Ce/MYKi3ymqdfuqibw== + dependencies: + source-map "^0.6.1" + +merge-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" + integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== + +merge2@^1.3.0, merge2@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" + integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== + +micromatch@^4.0.4: + version "4.0.5" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" + integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== + dependencies: + braces "^3.0.2" + picomatch "^2.3.1" + +mime-db@1.52.0: + version "1.52.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== + +mime-types@^2.1.18, mime-types@^2.1.27, mime-types@~2.1.24, mime-types@~2.1.34: + 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== + dependencies: + mime-db "1.52.0" + +mimic-fn@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" + integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== + +minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" + integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== + dependencies: + brace-expansion "^1.1.7" + +minimist@^1.2.5: + version "1.2.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" + integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== + +ms@2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + +ms@^2.1.1: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + +nanoid@^3.3.7: + version "3.3.7" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.7.tgz#d0c301a691bc8d54efa0a2226ccf3fe2fd656bd8" + integrity sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g== + +natural-compare@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" + integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== + +negotiator@0.6.3: + version "0.6.3" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" + integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== + +node-forge@^0.10.0: + version "0.10.0" + resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.10.0.tgz#32dea2afb3e9926f02ee5ce8794902691a676bf3" + integrity sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA== + +node-releases@^2.0.14: + version "2.0.14" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.14.tgz#2ffb053bceb8b2be8495ece1ab6ce600c4461b0b" + integrity sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw== + +normalize-path@^3.0.0, normalize-path@~3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" + integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== + +npm-run-path@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" + integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== + dependencies: + path-key "^3.0.0" + +object-assign@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== + +object-inspect@^1.13.1: + version "1.13.1" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.1.tgz#b96c6109324ccfef6b12216a956ca4dc2ff94bc2" + integrity sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ== + +object-keys@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" + integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== + +object.assign@^4.1.4, object.assign@^4.1.5: + version "4.1.5" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.5.tgz#3a833f9ab7fdb80fc9e8d2300c803d216d8fdbb0" + integrity sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ== + dependencies: + call-bind "^1.0.5" + define-properties "^1.2.1" + has-symbols "^1.0.3" + object-keys "^1.1.1" + +object.entries@^1.1.7: + version "1.1.8" + resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.8.tgz#bffe6f282e01f4d17807204a24f8edd823599c41" + integrity sha512-cmopxi8VwRIAw/fkijJohSfpef5PdN0pMQJN6VC/ZKvn0LIknWD8KtgY6KlQdEc4tIjcQ3HxSMmnvtzIscdaYQ== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-object-atoms "^1.0.0" + +object.fromentries@^2.0.7: + version "2.0.8" + resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.8.tgz#f7195d8a9b97bd95cbc1999ea939ecd1a2b00c65" + integrity sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-abstract "^1.23.2" + es-object-atoms "^1.0.0" + +object.hasown@^1.1.3: + version "1.1.4" + resolved "https://registry.yarnpkg.com/object.hasown/-/object.hasown-1.1.4.tgz#e270ae377e4c120cdcb7656ce66884a6218283dc" + integrity sha512-FZ9LZt9/RHzGySlBARE3VF+gE26TxR38SdmqOqliuTnl9wrKulaQs+4dee1V+Io8VfxqzAfHu6YuRgUy8OHoTg== + dependencies: + define-properties "^1.2.1" + es-abstract "^1.23.2" + es-object-atoms "^1.0.0" + +object.values@^1.1.6, object.values@^1.1.7: + version "1.2.0" + resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.2.0.tgz#65405a9d92cee68ac2d303002e0b8470a4d9ab1b" + integrity sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-object-atoms "^1.0.0" + +on-finished@^2.3.0: + 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== + dependencies: + ee-first "1.1.1" + +once@^1.3.0, once@^1.3.1, once@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== + dependencies: + wrappy "1" + +onetime@^5.1.0: + version "5.1.2" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" + integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== + dependencies: + mimic-fn "^2.1.0" + +only@~0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/only/-/only-0.0.2.tgz#2afde84d03e50b9a8edc444e30610a70295edfb4" + integrity sha512-Fvw+Jemq5fjjyWz6CpKx6w9s7xxqo3+JCyM0WXWeCSOboZ8ABkyvP8ID4CZuChA/wxSx+XSJmdOm8rGVyJ1hdQ== + +open@^7.2.1: + version "7.4.2" + resolved "https://registry.yarnpkg.com/open/-/open-7.4.2.tgz#b8147e26dcf3e426316c730089fd71edd29c2321" + integrity sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q== + dependencies: + is-docker "^2.0.0" + is-wsl "^2.1.1" + +optionator@^0.9.3: + version "0.9.3" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.3.tgz#007397d44ed1872fdc6ed31360190f81814e2c64" + integrity sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg== + dependencies: + "@aashutoshrathi/word-wrap" "^1.2.3" + deep-is "^0.1.3" + fast-levenshtein "^2.0.6" + levn "^0.4.1" + prelude-ls "^1.2.1" + type-check "^0.4.0" + +ora@^5.1.0: + version "5.4.1" + resolved "https://registry.yarnpkg.com/ora/-/ora-5.4.1.tgz#1b2678426af4ac4a509008e5e4ac9e9959db9e18" + integrity sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ== + dependencies: + bl "^4.1.0" + chalk "^4.1.0" + cli-cursor "^3.1.0" + cli-spinners "^2.5.0" + is-interactive "^1.0.0" + is-unicode-supported "^0.1.0" + log-symbols "^4.1.0" + strip-ansi "^6.0.0" + wcwidth "^1.0.1" + +p-limit@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" + integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== + dependencies: + yocto-queue "^0.1.0" + +p-locate@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" + integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== + dependencies: + p-limit "^3.0.2" + +p-map-series@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/p-map-series/-/p-map-series-2.1.0.tgz#7560d4c452d9da0c07e692fdbfe6e2c81a2a91f2" + integrity sha512-RpYIIK1zXSNEOdwxcfe7FdvGcs7+y5n8rifMhMNWvaxRNMPINJHF5GDeuVxWqnfrcHPSCnp7Oo5yNXHId9Av2Q== + +parent-module@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" + integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== + dependencies: + callsites "^3.0.0" + +parseurl@^1.3.2: + version "1.3.3" + resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" + integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== + +path-exists@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== + +path-is-absolute@1.0.1, path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== + +path-key@^3.0.0, path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + +path-match@^1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/path-match/-/path-match-1.2.4.tgz#a62747f3c7e0c2514762697f24443585b09100ea" + integrity sha512-UWlehEdqu36jmh4h5CWJ7tARp1OEVKGHKm6+dg9qMq5RKUTV5WJrGgaZ3dN2m7WFAXDbjlHzvJvL/IUpy84Ktw== + dependencies: + http-errors "~1.4.0" + path-to-regexp "^1.0.0" + +path-parse@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== + +path-to-regexp@^1.0.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-1.8.0.tgz#887b3ba9d84393e87a0a0b9f4cb756198b53548a" + integrity sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA== + dependencies: + isarray "0.0.1" + +path-type@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" + integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== + +picocolors@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-0.2.1.tgz#570670f793646851d1ba135996962abad587859f" + integrity sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA== + +picocolors@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" + integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== + +picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.2, picomatch@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + +pify@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" + integrity sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog== + +possible-typed-array-names@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz#89bb63c6fada2c3e90adc4a647beeeb39cc7bf8f" + integrity sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q== + +postcss-discard-comments@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-discard-comments/-/postcss-discard-comments-4.0.2.tgz#1fbabd2c246bff6aaad7997b2b0918f4d7af4033" + integrity sha512-RJutN259iuRf3IW7GZyLM5Sw4GLTOH8FmsXBnv8Ab/Tc2k4SR4qbV4DNbyyY4+Sjo362SyDmW2DQ7lBSChrpkg== + dependencies: + postcss "^7.0.0" + +postcss-import@^12.0.1: + version "12.0.1" + resolved "https://registry.yarnpkg.com/postcss-import/-/postcss-import-12.0.1.tgz#cf8c7ab0b5ccab5649024536e565f841928b7153" + integrity sha512-3Gti33dmCjyKBgimqGxL3vcV8w9+bsHwO5UrBawp796+jdardbcFl4RP5w/76BwNL7aGzpKstIfF9I+kdE8pTw== + dependencies: + postcss "^7.0.1" + postcss-value-parser "^3.2.3" + read-cache "^1.0.0" + resolve "^1.1.7" + +postcss-load-config@^3.0.0: + version "3.1.4" + resolved "https://registry.yarnpkg.com/postcss-load-config/-/postcss-load-config-3.1.4.tgz#1ab2571faf84bb078877e1d07905eabe9ebda855" + integrity sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg== + dependencies: + lilconfig "^2.0.5" + yaml "^1.10.2" + +postcss-value-parser@^3.2.3: + version "3.3.1" + resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz#9ff822547e2893213cf1c30efa51ac5fd1ba8281" + integrity sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ== + +postcss@^7.0.0, postcss@^7.0.1: + version "7.0.39" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.39.tgz#9624375d965630e2e1f2c02a935c82a59cb48309" + integrity sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA== + dependencies: + picocolors "^0.2.1" + source-map "^0.6.1" + +postcss@^8.4.35: + version "8.4.38" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.38.tgz#b387d533baf2054288e337066d81c6bee9db9e0e" + integrity sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A== + dependencies: + nanoid "^3.3.7" + picocolors "^1.0.0" + source-map-js "^1.2.0" + +preact@^10.4.8: + version "10.20.1" + resolved "https://registry.yarnpkg.com/preact/-/preact-10.20.1.tgz#1bc598ab630d8612978f7533da45809a8298542b" + integrity sha512-JIFjgFg9B2qnOoGiYMVBtrcFxHqn+dNXbq76bVmcaHYJFYR4lW67AOcXgAYQQTDYXDOg/kTZrKPNCdRgJ2UJmw== + +prelude-ls@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" + integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== + +prop-types@^15.8.1: + version "15.8.1" + resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" + integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== + dependencies: + loose-envify "^1.4.0" + object-assign "^4.1.1" + react-is "^16.13.1" + +pump@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" + integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + +punycode@^2.1.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" + integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== + +queue-microtask@^1.2.2: + version "1.2.3" + resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" + integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== + +randombytes@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" + integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== + dependencies: + safe-buffer "^5.1.0" + +react-is@^16.13.1: + version "16.13.1" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" + integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== + +read-cache@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/read-cache/-/read-cache-1.0.0.tgz#e664ef31161166c9751cdbe8dbcf86b5fb58f774" + integrity sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA== + dependencies: + pify "^2.3.0" + +readable-stream@^3.4.0: + version "3.6.2" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" + integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + +readdirp@~3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" + integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== + dependencies: + picomatch "^2.2.1" + +reflect.getprototypeof@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/reflect.getprototypeof/-/reflect.getprototypeof-1.0.6.tgz#3ab04c32a8390b770712b7a8633972702d278859" + integrity sha512-fmfw4XgoDke3kdI6h4xcUz1dG8uaiv5q9gcEwLS4Pnth2kxT+GZ7YehS1JTMGBQmtV7Y4GFGbs2re2NqhdozUg== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-abstract "^1.23.1" + es-errors "^1.3.0" + get-intrinsic "^1.2.4" + globalthis "^1.0.3" + which-builtin-type "^1.1.3" + +regexp.prototype.flags@^1.5.2: + version "1.5.2" + resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz#138f644a3350f981a858c44f6bb1a61ff59be334" + integrity sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw== + dependencies: + call-bind "^1.0.6" + define-properties "^1.2.1" + es-errors "^1.3.0" + set-function-name "^2.0.1" + +requires-port@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" + integrity sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ== + +resolve-from@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" + integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== + +resolve-path@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/resolve-path/-/resolve-path-1.4.0.tgz#c4bda9f5efb2fce65247873ab36bb4d834fe16f7" + integrity sha512-i1xevIst/Qa+nA9olDxLWnLk8YZbi8R/7JPbCMcgyWaFR6bKWaexgJgEB5oc2PKMjYdrHynyz0NY+if+H98t1w== + dependencies: + http-errors "~1.6.2" + path-is-absolute "1.0.1" + +resolve@^1.1.7, resolve@^1.17.0: + version "1.22.8" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d" + integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw== + dependencies: + is-core-module "^2.13.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + +resolve@^2.0.0-next.5: + version "2.0.0-next.5" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-2.0.0-next.5.tgz#6b0ec3107e671e52b68cd068ef327173b90dc03c" + integrity sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA== + dependencies: + is-core-module "^2.13.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + +restore-cursor@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" + integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA== + dependencies: + onetime "^5.1.0" + signal-exit "^3.0.2" + +reusify@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" + integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== + +rimraf@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" + integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== + dependencies: + glob "^7.1.3" + +rollup-plugin-dynamic-import-variables@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/rollup-plugin-dynamic-import-variables/-/rollup-plugin-dynamic-import-variables-1.1.0.tgz#4981d38907a471b35234398a09047bef47a2006a" + integrity sha512-C1avEmnXC8cC4aAQ5dB63O9oQf7IrhEHc98bQw9Qd6H36FxtZooLCvVfcO4SNYrqaNrzH3ErucQt/zdFSLPHNw== + dependencies: + "@rollup/pluginutils" "^3.0.9" + estree-walker "^2.0.1" + globby "^11.0.0" + magic-string "^0.25.7" + +rollup-plugin-terser@^7.0.2: + version "7.0.2" + resolved "https://registry.yarnpkg.com/rollup-plugin-terser/-/rollup-plugin-terser-7.0.2.tgz#e8fbba4869981b2dc35ae7e8a502d5c6c04d324d" + integrity sha512-w3iIaU4OxcF52UUXiZNsNeuXIMDvFrr+ZXK6bFZ0Q60qyVfq4uLptoS4bbq3paG3x216eQllFZX7zt6TIImguQ== + dependencies: + "@babel/code-frame" "^7.10.4" + jest-worker "^26.2.1" + serialize-javascript "^4.0.0" + terser "^5.0.0" + +rollup-plugin-vue@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/rollup-plugin-vue/-/rollup-plugin-vue-6.0.0.tgz#e379e93e5ae9a8648522f698be2e452e8672aaf2" + integrity sha512-oVvUd84d5u73M2HYM3XsMDLtZRIA/tw2U0dmHlXU2UWP5JARYHzh/U9vcxaN/x/9MrepY7VH3pHFeOhrWpxs/Q== + dependencies: + debug "^4.1.1" + hash-sum "^2.0.0" + rollup-pluginutils "^2.8.2" + +rollup-plugin-web-worker-loader@^1.3.1: + version "1.6.1" + resolved "https://registry.yarnpkg.com/rollup-plugin-web-worker-loader/-/rollup-plugin-web-worker-loader-1.6.1.tgz#9d7a27575b64b0780fe4e8b3bc87470d217e485f" + integrity sha512-4QywQSz1NXFHKdyiou16mH3ijpcfLtLGOrAqvAqu1Gx+P8+zj+3gwC2BSL/VW1d+LW4nIHC8F7d7OXhs9UdR2A== + +rollup-pluginutils@^2.8.2: + version "2.8.2" + resolved "https://registry.yarnpkg.com/rollup-pluginutils/-/rollup-pluginutils-2.8.2.tgz#72f2af0748b592364dbd3389e600e5a9444a351e" + integrity sha512-EEp9NhnUkwY8aif6bxgovPHMoMoNr2FulJziTndpt5H9RdwC47GSGuII9XxpSdzVGM0GWrNPHV6ie1LTNJPaLQ== + dependencies: + estree-walker "^0.6.1" + +rollup@^2.32.1: + version "2.79.1" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.79.1.tgz#bedee8faef7c9f93a2647ac0108748f497f081c7" + integrity sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw== + optionalDependencies: + fsevents "~2.3.2" + +run-parallel@^1.1.9: + version "1.2.0" + resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" + integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== + dependencies: + queue-microtask "^1.2.2" + +safe-array-concat@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/safe-array-concat/-/safe-array-concat-1.1.2.tgz#81d77ee0c4e8b863635227c721278dd524c20edb" + integrity sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q== + dependencies: + call-bind "^1.0.7" + get-intrinsic "^1.2.4" + has-symbols "^1.0.3" + isarray "^2.0.5" + +safe-buffer@5.2.1, safe-buffer@^5.1.0, safe-buffer@~5.2.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +safe-regex-test@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/safe-regex-test/-/safe-regex-test-1.0.3.tgz#a5b4c0f06e0ab50ea2c395c14d8371232924c377" + integrity sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw== + dependencies: + call-bind "^1.0.6" + es-errors "^1.3.0" + is-regex "^1.1.4" + +selfsigned@^1.10.8: + version "1.10.14" + resolved "https://registry.yarnpkg.com/selfsigned/-/selfsigned-1.10.14.tgz#ee51d84d9dcecc61e07e4aba34f229ab525c1574" + integrity sha512-lkjaiAye+wBZDCBsu5BGi0XiLRxeUlsGod5ZP924CRSEoGuZAw/f7y9RKu28rwTfiHVhdavhB0qH0INV6P1lEA== + dependencies: + node-forge "^0.10.0" + +semver@^6.3.1: + version "6.3.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" + integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== + +semver@^7.3.7, semver@^7.5.4: + version "7.6.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.0.tgz#1a46a4db4bffcccd97b743b5005c8325f23d4e2d" + integrity sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg== + dependencies: + lru-cache "^6.0.0" + +serialize-javascript@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-4.0.0.tgz#b525e1238489a5ecfc42afacc3fe99e666f4b1aa" + integrity sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw== + dependencies: + randombytes "^2.1.0" + +set-function-length@^1.2.1: + version "1.2.2" + resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.2.tgz#aac72314198eaed975cf77b2c3b6b880695e5449" + integrity sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg== + dependencies: + define-data-property "^1.1.4" + es-errors "^1.3.0" + function-bind "^1.1.2" + get-intrinsic "^1.2.4" + gopd "^1.0.1" + has-property-descriptors "^1.0.2" + +set-function-name@^2.0.1, set-function-name@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/set-function-name/-/set-function-name-2.0.2.tgz#16a705c5a0dc2f5e638ca96d8a8cd4e1c2b90985" + integrity sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ== + dependencies: + define-data-property "^1.1.4" + es-errors "^1.3.0" + functions-have-names "^1.2.3" + has-property-descriptors "^1.0.2" + +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: + version "1.2.0" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" + integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== + +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + +side-channel@^1.0.4, side-channel@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.6.tgz#abd25fb7cd24baf45466406b1096b7831c9215f2" + integrity sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA== + dependencies: + call-bind "^1.0.7" + es-errors "^1.3.0" + get-intrinsic "^1.2.4" + object-inspect "^1.13.1" + +signal-exit@^3.0.2: + version "3.0.7" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" + integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== + +slash@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" + integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== + +source-map-js@^1.0.2, source-map-js@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.0.tgz#16b809c162517b5b8c3e7dcd315a2a5c2612b2af" + integrity sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg== + +source-map-support@~0.5.20: + version "0.5.21" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" + integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + +source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + +source-map@^0.7.3: + version "0.7.4" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.4.tgz#a9bbe705c9d8846f4e08ff6765acf0f1b0898656" + integrity sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA== + +sourcemap-codec@^1.4.8: + version "1.4.8" + resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4" + integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA== + +"statuses@>= 1.2.1 < 2", "statuses@>= 1.4.0 < 2", "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== + +string.prototype.matchall@^4.0.10: + version "4.0.11" + resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.11.tgz#1092a72c59268d2abaad76582dccc687c0297e0a" + integrity sha512-NUdh0aDavY2og7IbBPenWqR9exH+E26Sv8e0/eTe1tltDGZL+GtBkDAnnyBtmekfK6/Dq3MkcGtzXFEd1LQrtg== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-abstract "^1.23.2" + es-errors "^1.3.0" + es-object-atoms "^1.0.0" + get-intrinsic "^1.2.4" + gopd "^1.0.1" + has-symbols "^1.0.3" + internal-slot "^1.0.7" + regexp.prototype.flags "^1.5.2" + set-function-name "^2.0.2" + side-channel "^1.0.6" + +string.prototype.trim@^1.2.9: + version "1.2.9" + resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz#b6fa326d72d2c78b6df02f7759c73f8f6274faa4" + integrity sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-abstract "^1.23.0" + es-object-atoms "^1.0.0" + +string.prototype.trimend@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.8.tgz#3651b8513719e8a9f48de7f2f77640b26652b229" + integrity sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-object-atoms "^1.0.0" + +string.prototype.trimstart@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz#7ee834dda8c7c17eff3118472bb35bfedaa34dde" + integrity sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-object-atoms "^1.0.0" + +string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + +strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-final-newline@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" + integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== + +strip-json-comments@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" + integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== + +supports-color@^5.3.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== + dependencies: + has-flag "^3.0.0" + +supports-color@^7.0.0, supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + +supports-preserve-symlinks-flag@^1.0.0: + version "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== + +terser@^5.0.0: + version "5.30.3" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.30.3.tgz#f1bb68ded42408c316b548e3ec2526d7dd03f4d2" + integrity sha512-STdUgOUx8rLbMGO9IOwHLpCqolkDITFFQSMYYwKE1N2lY6MVSaeoi10z/EhWxRc6ybqoVmKSkhKYH/XUpl7vSA== + dependencies: + "@jridgewell/source-map" "^0.3.3" + acorn "^8.8.2" + commander "^2.20.0" + source-map-support "~0.5.20" + +text-table@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" + integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== + +to-fast-properties@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" + integrity sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog== + +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + +toidentifier@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" + integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== + +tsconfck@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/tsconfck/-/tsconfck-3.0.3.tgz#d9bda0e87d05b1c360e996c9050473c7e6f8084f" + integrity sha512-4t0noZX9t6GcPTfBAbIbbIU4pfpCwh0ueq3S4O/5qXI1VwK1outmxhe9dOiEWqMz3MW2LKgDTpqWV+37IWuVbA== + +tslib@^1.8.1: + version "1.14.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" + integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== + +tsscmp@1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/tsscmp/-/tsscmp-1.0.6.tgz#85b99583ac3589ec4bfef825b5000aa911d605eb" + integrity sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA== + +tsutils@^3.21.0: + version "3.21.0" + resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623" + integrity sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA== + dependencies: + tslib "^1.8.1" + +type-check@^0.4.0, type-check@~0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" + integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== + dependencies: + prelude-ls "^1.2.1" + +type-fest@^0.20.2: + version "0.20.2" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" + integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== + +type-is@^1.6.16: + version "1.6.18" + resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" + integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== + dependencies: + media-typer "0.3.0" + mime-types "~2.1.24" + +typed-array-buffer@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz#1867c5d83b20fcb5ccf32649e5e2fc7424474ff3" + integrity sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ== + dependencies: + call-bind "^1.0.7" + es-errors "^1.3.0" + is-typed-array "^1.1.13" + +typed-array-byte-length@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz#d92972d3cff99a3fa2e765a28fcdc0f1d89dec67" + integrity sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw== + dependencies: + call-bind "^1.0.7" + for-each "^0.3.3" + gopd "^1.0.1" + has-proto "^1.0.3" + is-typed-array "^1.1.13" + +typed-array-byte-offset@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/typed-array-byte-offset/-/typed-array-byte-offset-1.0.2.tgz#f9ec1acb9259f395093e4567eb3c28a580d02063" + integrity sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA== + dependencies: + available-typed-arrays "^1.0.7" + call-bind "^1.0.7" + for-each "^0.3.3" + gopd "^1.0.1" + has-proto "^1.0.3" + is-typed-array "^1.1.13" + +typed-array-length@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/typed-array-length/-/typed-array-length-1.0.6.tgz#57155207c76e64a3457482dfdc1c9d1d3c4c73a3" + integrity sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g== + dependencies: + call-bind "^1.0.7" + for-each "^0.3.3" + gopd "^1.0.1" + has-proto "^1.0.3" + is-typed-array "^1.1.13" + possible-typed-array-names "^1.0.0" + +typescript@^4.0.5: + version "4.9.5" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.5.tgz#095979f9bcc0d09da324d58d03ce8f8374cbe65a" + integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g== + +unbox-primitive@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.2.tgz#29032021057d5e6cdbd08c5129c226dff8ed6f9e" + integrity sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw== + dependencies: + call-bind "^1.0.2" + has-bigints "^1.0.2" + has-symbols "^1.0.3" + which-boxed-primitive "^1.0.2" + +undici-types@~5.26.4: + version "5.26.5" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" + integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== + +universalify@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.1.tgz#168efc2180964e6386d061e094df61afe239b18d" + integrity sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw== + +update-browserslist-db@^1.0.13: + version "1.0.13" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz#3c5e4f5c083661bd38ef64b6328c26ed6c8248c4" + integrity sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg== + dependencies: + escalade "^3.1.1" + picocolors "^1.0.0" + +uri-js@^4.2.2: + version "4.4.1" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" + integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== + dependencies: + punycode "^2.1.0" + +util-deprecate@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== + +vary@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" + integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== + +vite-tsconfig-paths@^4.3.2: + version "4.3.2" + resolved "https://registry.yarnpkg.com/vite-tsconfig-paths/-/vite-tsconfig-paths-4.3.2.tgz#321f02e4b736a90ff62f9086467faf4e2da857a9" + integrity sha512-0Vd/a6po6Q+86rPlntHye7F31zA2URZMbH8M3saAZ/xR9QoGN/L21bxEGfXdWmFdNkqPpRdxFT7nmNe12e9/uA== + dependencies: + debug "^4.1.1" + globrex "^0.1.2" + tsconfck "^3.0.3" + +vite@^1.0.0-rc.13: + version "1.0.0-rc.13" + resolved "https://registry.yarnpkg.com/vite/-/vite-1.0.0-rc.13.tgz#0e0b3b6138998a1d0c02459908a6c4fb2f294727" + integrity sha512-hLfTbhNPDhwXMCAWR6s6C79G/O8Is0MbslglgoHSQsRby+KnqHgtHChCVBHFeV2oZBV/3xhHhnfm94BDPFe8Ww== + dependencies: + "@babel/parser" "^7.12.7" + "@koa/cors" "^3.1.0" + "@rollup/plugin-commonjs" "^16.0.0" + "@rollup/plugin-json" "^4.1.0" + "@rollup/plugin-node-resolve" "^10.0.0" + "@rollup/pluginutils" "^4.1.0" + "@types/http-proxy" "^1.17.4" + "@types/koa" "^2.11.4" + "@types/lru-cache" "^5.1.0" + "@vue/compiler-dom" "^3.0.3" + "@vue/compiler-sfc" "^3.0.3" + brotli-size "^4.0.0" + cac "^6.6.1" + chalk "^4.1.0" + chokidar "^3.4.2" + clean-css "^4.2.3" + debug "^4.3.1" + dotenv "^8.2.0" + dotenv-expand "^5.1.0" + es-module-lexer "^0.3.25" + esbuild "^0.8.12" + etag "^1.8.1" + execa "^4.0.3" + fs-extra "^9.0.1" + hash-sum "^2.0.0" + isbuiltin "^1.0.0" + klona "^2.0.4" + koa "^2.13.0" + koa-conditional-get "^3.0.0" + koa-etag "^4.0.0" + koa-proxies "^0.11.0" + koa-send "^5.0.1" + koa-static "^5.0.0" + lru-cache "^6.0.0" + magic-string "^0.25.7" + merge-source-map "^1.1.0" + mime-types "^2.1.27" + minimist "^1.2.5" + open "^7.2.1" + ora "^5.1.0" + p-map-series "^2.1.0" + postcss-discard-comments "^4.0.2" + postcss-import "^12.0.1" + postcss-load-config "^3.0.0" + resolve "^1.17.0" + rollup "^2.32.1" + rollup-plugin-dynamic-import-variables "^1.1.0" + rollup-plugin-terser "^7.0.2" + rollup-plugin-vue "^6.0.0" + rollup-plugin-web-worker-loader "^1.3.1" + selfsigned "^1.10.8" + slash "^3.0.0" + source-map "^0.7.3" + vue "^3.0.3" + ws "^7.3.1" + +vue@^3.0.3: + version "3.4.21" + resolved "https://registry.yarnpkg.com/vue/-/vue-3.4.21.tgz#69ec30e267d358ee3a0ce16612ba89e00aaeb731" + integrity sha512-5hjyV/jLEIKD/jYl4cavMcnzKwjMKohureP8ejn3hhEjwhWIhWeuzL2kJAjzl/WyVsgPY56Sy4Z40C3lVshxXA== + dependencies: + "@vue/compiler-dom" "3.4.21" + "@vue/compiler-sfc" "3.4.21" + "@vue/runtime-dom" "3.4.21" + "@vue/server-renderer" "3.4.21" + "@vue/shared" "3.4.21" + +wcwidth@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8" + integrity sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg== + dependencies: + defaults "^1.0.3" + +which-boxed-primitive@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6" + integrity sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg== + dependencies: + is-bigint "^1.0.1" + is-boolean-object "^1.1.0" + is-number-object "^1.0.4" + is-string "^1.0.5" + is-symbol "^1.0.3" + +which-builtin-type@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/which-builtin-type/-/which-builtin-type-1.1.3.tgz#b1b8443707cc58b6e9bf98d32110ff0c2cbd029b" + integrity sha512-YmjsSMDBYsM1CaFiayOVT06+KJeXf0o5M/CAd4o1lTadFAtacTUM49zoYxr/oroopFDfhvN6iEcBxUyc3gvKmw== + dependencies: + function.prototype.name "^1.1.5" + has-tostringtag "^1.0.0" + is-async-function "^2.0.0" + is-date-object "^1.0.5" + is-finalizationregistry "^1.0.2" + is-generator-function "^1.0.10" + is-regex "^1.1.4" + is-weakref "^1.0.2" + isarray "^2.0.5" + which-boxed-primitive "^1.0.2" + which-collection "^1.0.1" + which-typed-array "^1.1.9" + +which-collection@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/which-collection/-/which-collection-1.0.2.tgz#627ef76243920a107e7ce8e96191debe4b16c2a0" + integrity sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw== + dependencies: + is-map "^2.0.3" + is-set "^2.0.3" + is-weakmap "^2.0.2" + is-weakset "^2.0.3" + +which-typed-array@^1.1.14, which-typed-array@^1.1.15, which-typed-array@^1.1.9: + version "1.1.15" + resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.15.tgz#264859e9b11a649b388bfaaf4f767df1f779b38d" + integrity sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA== + dependencies: + available-typed-arrays "^1.0.7" + call-bind "^1.0.7" + for-each "^0.3.3" + gopd "^1.0.1" + has-tostringtag "^1.0.2" + +which@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== + +ws@^7.3.1: + version "7.5.9" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.9.tgz#54fa7db29f4c7cec68b1ddd3a89de099942bb591" + integrity sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q== + +yallist@^3.0.2: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" + integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== + +yallist@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" + integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== + +yaml@^1.10.2: + version "1.10.2" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" + integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== + +ylru@^1.2.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/ylru/-/ylru-1.4.0.tgz#0cf0aa57e9c24f8a2cbde0cc1ca2c9592ac4e0f6" + integrity sha512-2OQsPNEmBCvXuFlIni/a+Rn+R2pHW9INm0BxXJ4hVDA8TirqMj+J/Rp9ItLatT/5pZqWwefVrTQcHpixsxnVlA== + +yocto-queue@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" + integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== diff --git a/docs/build.md b/docs/build.md index d1f9f93b5a..0eb2315098 100644 --- a/docs/build.md +++ b/docs/build.md @@ -83,11 +83,10 @@ To run the build with Go, use staging credentials, your own, or any other accoun ``` cd social-app yarn && yarn build-web -cp ./web-build/static/js/*.* bskyweb/static/js/ cd bskyweb/ go mod tidy go build -v -tags timetzdata -o bskyweb ./cmd/bskyweb -./bskyweb serve --pds-host=https://staging.bsky.dev --handle= --password= +./bskyweb serve --appview-host=https://public.api.bsky.app ``` On build success, access the application at [http://localhost:8100/](http://localhost:8100/). Subsequent changes require re-running the above steps in order to be reflected. diff --git a/package.json b/package.json index 5eaba7a972..85db718dc1 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ "build-ios": "yarn use-build-number-with-bump eas build -p ios", "build-android": "yarn use-build-number-with-bump eas build -p android", "build": "yarn use-build-number-with-bump eas build", + "build-embed": "cd bskyembed && yarn build && cd .. && node ./scripts/post-embed-build.js", "start": "expo start --dev-client", "start:prod": "expo start --dev-client --no-dev --minify", "clean-cache": "rm -rf node_modules/.cache/babel-loader/*", diff --git a/scripts/post-embed-build.js b/scripts/post-embed-build.js new file mode 100644 index 0000000000..5bece544aa --- /dev/null +++ b/scripts/post-embed-build.js @@ -0,0 +1,49 @@ +// const path = require('node:path') +// const fs = require('node:fs') + +// const projectRoot = path.join(__dirname, '..') + +// // copy embed assets to web-build + +// const embedAssetSource = path.join( +// projectRoot, +// 'bskyembed', +// 'dist', +// 'static', +// 'embed', +// 'assets', +// ) + +// const embedAssetDest = path.join( +// projectRoot, +// 'web-build', +// 'static', +// 'embed', +// 'assets', +// ) + +// fs.cpSync(embedAssetSource, embedAssetDest, {recursive: true}) + +// // copy entrypoint(s) to web-build + +// // additional entrypoints will need more work, but this'll do for now +// const embedHtmlSource = path.join( +// projectRoot, +// 'bskyembed', +// 'dist', +// 'index.html', +// ) + +// const embedHtmlDest = path.join( +// projectRoot, +// 'web-build', +// 'static', +// 'embed', +// 'post.html', +// ) + +// fs.copyFileSync(embedHtmlSource, embedHtmlDest) + +// console.log(`Copied embed assets to web-build`) + +console.log('post-embed-build.js - waiting for embedr!') From ed2c8b720edb0886f72e97539fc5b08d943dda42 Mon Sep 17 00:00:00 2001 From: Paul Frazee Date: Fri, 12 Apr 2024 07:53:11 -0700 Subject: [PATCH 009/167] Dont apply the content-language filter if it will remove all content (#3492) * Dont apply the content-language filter if it will remove all content * Improve code --- src/lib/api/feed-manip.ts | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/lib/api/feed-manip.ts b/src/lib/api/feed-manip.ts index 227062592b..85089608a7 100644 --- a/src/lib/api/feed-manip.ts +++ b/src/lib/api/feed-manip.ts @@ -1,11 +1,12 @@ import { + AppBskyEmbedRecord, + AppBskyEmbedRecordWithMedia, AppBskyFeedDefs, AppBskyFeedPost, - AppBskyEmbedRecordWithMedia, - AppBskyEmbedRecord, } from '@atproto/api' -import {ReasonFeedSource} from './feed/types' + import {isPostInLanguage} from '../../locale/helpers' +import {ReasonFeedSource} from './feed/types' type FeedViewPost = AppBskyFeedDefs.FeedViewPost export type FeedTunerFn = ( @@ -341,6 +342,8 @@ export class FeedTuner { tuner: FeedTuner, slices: FeedViewPostsSlice[], ): FeedViewPostsSlice[] => { + const candidateSlices = slices.slice() + // early return if no languages have been specified if (!preferredLangsCode2.length || preferredLangsCode2.length === 0) { return slices @@ -357,10 +360,17 @@ export class FeedTuner { // if item does not fit preferred language, remove it if (!hasPreferredLang) { - slices.splice(i, 1) + candidateSlices.splice(i, 1) } } - return slices + + // if the language filter cleared out the entire page, return the original set + // so that something always shows + if (candidateSlices.length === 0) { + return slices + } + + return candidateSlices } } } From f3951f2718307ddbe1a4a90c599f777359513e4d Mon Sep 17 00:00:00 2001 From: Samuel Newman Date: Fri, 12 Apr 2024 16:14:20 +0100 Subject: [PATCH 010/167] remove build-embed from Dockerfile (#3502) --- Dockerfile | 3 +-- Makefile | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/Dockerfile b/Dockerfile index e36a959293..3ad05b6ec6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -32,8 +32,7 @@ RUN \. "$NVM_DIR/nvm.sh" && \ npm install --global yarn && \ yarn && \ yarn intl:build && \ - yarn build-web && \ - yarn build-embed + yarn build-web # DEBUG RUN find ./bskyweb/static && find ./web-build/static diff --git a/Makefile b/Makefile index 9e82e0fe47..c90abb783e 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,6 @@ help: ## Print info about all commands build-web: ## Compile web bundle, copy to bskyweb directory yarn intl:build yarn build-web - yarn build-embed .PHONY: test test: ## Run all tests From 7047755c509716a6ab1d63ffef24dd9540a88915 Mon Sep 17 00:00:00 2001 From: dan Date: Fri, 12 Apr 2024 16:39:59 +0100 Subject: [PATCH 011/167] Fix optimistic like/repost (#3503) --- src/state/cache/post-shadow.ts | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/src/state/cache/post-shadow.ts b/src/state/cache/post-shadow.ts index 6225cbdba0..48183739b2 100644 --- a/src/state/cache/post-shadow.ts +++ b/src/state/cache/post-shadow.ts @@ -62,25 +62,29 @@ function mergeShadow( return POST_TOMBSTONE } - const wasLiked = !!post.viewer?.like - const isLiked = !!shadow.likeUri let likeCount = post.likeCount ?? 0 - if (wasLiked && !isLiked) { - likeCount-- - } else if (!wasLiked && isLiked) { - likeCount++ + if ('likeUri' in shadow) { + const wasLiked = !!post.viewer?.like + const isLiked = !!shadow.likeUri + if (wasLiked && !isLiked) { + likeCount-- + } else if (!wasLiked && isLiked) { + likeCount++ + } + likeCount = Math.max(0, likeCount) } - likeCount = Math.max(0, likeCount) - const wasReposted = !!post.viewer?.repost - const isReposted = !!shadow.repostUri let repostCount = post.repostCount ?? 0 - if (wasReposted && !isReposted) { - repostCount-- - } else if (!wasReposted && isReposted) { - repostCount++ + if ('repostUri' in shadow) { + const wasReposted = !!post.viewer?.repost + const isReposted = !!shadow.repostUri + if (wasReposted && !isReposted) { + repostCount-- + } else if (!wasReposted && isReposted) { + repostCount++ + } + repostCount = Math.max(0, repostCount) } - repostCount = Math.max(0, repostCount) return castAsShadow({ ...post, From eb2fd53340005316af007afc8af899ba11929098 Mon Sep 17 00:00:00 2001 From: Hailey Date: Fri, 12 Apr 2024 10:00:44 -0700 Subject: [PATCH 012/167] QT Jump Pt. 2 - Remove code duplication (#3506) * remove code duplication * now it's safe to remove shimmer --- src/state/queries/post-thread.ts | 11 +-- src/view/com/post-thread/PostThreadItem.tsx | 74 ++++++++++----------- 2 files changed, 36 insertions(+), 49 deletions(-) diff --git a/src/state/queries/post-thread.ts b/src/state/queries/post-thread.ts index 832794bf54..dfc568b7d5 100644 --- a/src/state/queries/post-thread.ts +++ b/src/state/queries/post-thread.ts @@ -11,7 +11,7 @@ import {getAgent} from '#/state/session' import {findAllPostsInQueryData as findAllPostsInNotifsQueryData} from './notifications/feed' import {findAllPostsInQueryData as findAllPostsInFeedQueryData} from './post-feed' import {precacheThreadPostProfiles} from './profile' -import {getEmbeddedPost} from './util' +import {embedViewRecordToPostView, getEmbeddedPost} from './util' const RQKEY_ROOT = 'post-thread' export const RQKEY = (uri: string) => [RQKEY_ROOT, uri] @@ -332,14 +332,7 @@ function embedViewRecordToPlaceholderThread( type: 'post', _reactKey: record.uri, uri: record.uri, - post: { - uri: record.uri, - cid: record.cid, - author: record.author, - record: record.value, - indexedAt: record.indexedAt, - labels: record.labels, - }, + post: embedViewRecordToPostView(record), record: record.value as AppBskyFeedPost.Record, // validated in getEmbeddedPost parent: undefined, replies: undefined, diff --git a/src/view/com/post-thread/PostThreadItem.tsx b/src/view/com/post-thread/PostThreadItem.tsx index 6555bdf73c..089714c727 100644 --- a/src/view/com/post-thread/PostThreadItem.tsx +++ b/src/view/com/post-thread/PostThreadItem.tsx @@ -1,50 +1,50 @@ import React, {memo, useMemo} from 'react' import {StyleSheet, View} from 'react-native' import { - AtUri, AppBskyFeedDefs, AppBskyFeedPost, - RichText as RichTextAPI, + AtUri, ModerationDecision, + RichText as RichTextAPI, } from '@atproto/api' -import {moderatePost_wrapped as moderatePost} from '#/lib/moderatePost_wrapped' import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' -import {PostThreadFollowBtn} from 'view/com/post-thread/PostThreadFollowBtn' -import {Link, TextLink} from '../util/Link' -import {RichText} from '#/components/RichText' -import {Text} from '../util/text/Text' -import {PreviewableUserAvatar} from '../util/UserAvatar' -import {s} from 'lib/styles' -import {niceDate} from 'lib/strings/time' +import {msg, Trans} from '@lingui/macro' +import {useLingui} from '@lingui/react' + +import {moderatePost_wrapped as moderatePost} from '#/lib/moderatePost_wrapped' +import {POST_TOMBSTONE, Shadow, usePostShadow} from '#/state/cache/post-shadow' +import {useLanguagePrefs} from '#/state/preferences' +import {useOpenLink} from '#/state/preferences/in-app-browser' +import {ThreadPost} from '#/state/queries/post-thread' +import {useModerationOpts} from '#/state/queries/preferences' +import {useComposerControls} from '#/state/shell/composer' +import {MAX_POST_LINES} from 'lib/constants' +import {usePalette} from 'lib/hooks/usePalette' +import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' +import {makeProfileLink} from 'lib/routes/links' import {sanitizeDisplayName} from 'lib/strings/display-names' import {sanitizeHandle} from 'lib/strings/handles' import {countLines, pluralize} from 'lib/strings/helpers' -import {getTranslatorLink, isPostInLanguage} from '../../../locale/helpers' -import {PostMeta} from '../util/PostMeta' -import {PostEmbeds} from '../util/post-embeds' -import {PostCtrls} from '../util/post-ctrls/PostCtrls' -import {PostHider} from '../../../components/moderation/PostHider' -import {ContentHider} from '../../../components/moderation/ContentHider' -import {PostAlerts} from '../../../components/moderation/PostAlerts' -import {LabelsOnMyPost} from '../../../components/moderation/LabelsOnMe' -import {ErrorMessage} from '../util/error/ErrorMessage' -import {usePalette} from 'lib/hooks/usePalette' -import {formatCount} from '../util/numeric/format' -import {makeProfileLink} from 'lib/routes/links' -import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' -import {MAX_POST_LINES} from 'lib/constants' -import {Trans, msg} from '@lingui/macro' -import {useLingui} from '@lingui/react' -import {useLanguagePrefs} from '#/state/preferences' -import {useComposerControls} from '#/state/shell/composer' -import {useModerationOpts} from '#/state/queries/preferences' -import {useOpenLink} from '#/state/preferences/in-app-browser' -import {Shadow, usePostShadow, POST_TOMBSTONE} from '#/state/cache/post-shadow' -import {ThreadPost} from '#/state/queries/post-thread' +import {niceDate} from 'lib/strings/time' +import {s} from 'lib/styles' import {useSession} from 'state/session' -import {WhoCanReply} from '../threadgate/WhoCanReply' -import {LoadingPlaceholder} from '../util/LoadingPlaceholder' +import {PostThreadFollowBtn} from 'view/com/post-thread/PostThreadFollowBtn' import {atoms as a} from '#/alf' +import {RichText} from '#/components/RichText' +import {ContentHider} from '../../../components/moderation/ContentHider' +import {LabelsOnMyPost} from '../../../components/moderation/LabelsOnMe' +import {PostAlerts} from '../../../components/moderation/PostAlerts' +import {PostHider} from '../../../components/moderation/PostHider' +import {getTranslatorLink, isPostInLanguage} from '../../../locale/helpers' +import {WhoCanReply} from '../threadgate/WhoCanReply' +import {ErrorMessage} from '../util/error/ErrorMessage' +import {Link, TextLink} from '../util/Link' +import {formatCount} from '../util/numeric/format' +import {PostCtrls} from '../util/post-ctrls/PostCtrls' +import {PostEmbeds} from '../util/post-embeds' +import {PostMeta} from '../util/PostMeta' +import {Text} from '../util/text/Text' +import {PreviewableUserAvatar} from '../util/UserAvatar' export function PostThreadItem({ post, @@ -325,12 +325,6 @@ let PostThreadItemLoaded = ({ {post.repostCount !== 0 || post.likeCount !== 0 ? ( // Show this section unless we're *sure* it has no engagement. - {post.repostCount == null && post.likeCount == null && ( - // If we're still loading and not sure, assume this post has engagement. - // This lets us avoid a layout shift for the common case (embedded post with likes/reposts). - // TODO: embeds should include metrics to avoid us having to guess. - - )} {post.repostCount != null && post.repostCount !== 0 ? ( Date: Fri, 12 Apr 2024 10:21:55 -0700 Subject: [PATCH 013/167] Fix: dont let notifications count go behind the icon (#3505) --- src/view/shell/bottom-bar/BottomBar.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/view/shell/bottom-bar/BottomBar.tsx b/src/view/shell/bottom-bar/BottomBar.tsx index c35fa106d2..4caff6c4d9 100644 --- a/src/view/shell/bottom-bar/BottomBar.tsx +++ b/src/view/shell/bottom-bar/BottomBar.tsx @@ -348,12 +348,12 @@ function Btn({ accessible={accessible} accessibilityLabel={accessibilityLabel} accessibilityHint={accessibilityHint}> + {icon} {notificationCount ? ( {notificationCount} ) : undefined} - {icon} ) } From 835f2e6548a9a55fded1f506b17692154a07caab Mon Sep 17 00:00:00 2001 From: dan Date: Fri, 12 Apr 2024 19:33:34 +0100 Subject: [PATCH 014/167] Fix stale Notifications after push (#3507) --- src/lib/notifications/notifications.ts | 4 +++ src/state/queries/notifications/unread.tsx | 34 +++++++++++++++++----- 2 files changed, 31 insertions(+), 7 deletions(-) diff --git a/src/lib/notifications/notifications.ts b/src/lib/notifications/notifications.ts index 0f628f4288..e0b3d8f3d9 100644 --- a/src/lib/notifications/notifications.ts +++ b/src/lib/notifications/notifications.ts @@ -4,6 +4,7 @@ import {QueryClient} from '@tanstack/react-query' import {logger} from '#/logger' import {RQKEY as RQKEY_NOTIFS} from '#/state/queries/notifications/feed' +import {invalidateCachedUnreadPage} from '#/state/queries/notifications/unread' import {truncateAndInvalidate} from '#/state/queries/util' import {getAgent, SessionAccount} from '#/state/session' import {track} from 'lib/analytics/analytics' @@ -87,6 +88,7 @@ export function useNotificationsListener(queryClient: QueryClient) { // handle notifications that are received, both in the foreground or background // NOTE: currently just here for debug logging const sub1 = Notifications.addNotificationReceivedListener(event => { + invalidateCachedUnreadPage() logger.debug( 'Notifications: received', {event}, @@ -131,11 +133,13 @@ export function useNotificationsListener(queryClient: QueryClient) { ) track('Notificatons:OpenApp') logEvent('notifications:openApp', {}) + invalidateCachedUnreadPage() truncateAndInvalidate(queryClient, RQKEY_NOTIFS()) resetToTab('NotificationsTab') // open notifications tab } }, ) + return () => { sub1.remove() sub2.remove() diff --git a/src/state/queries/notifications/unread.tsx b/src/state/queries/notifications/unread.tsx index e7a0631ecf..1c01d71a5e 100644 --- a/src/state/queries/notifications/unread.tsx +++ b/src/state/queries/notifications/unread.tsx @@ -3,24 +3,28 @@ */ import React from 'react' +import {AppState} from 'react-native' import * as Notifications from 'expo-notifications' import {useQueryClient} from '@tanstack/react-query' +import EventEmitter from 'eventemitter3' + import BroadcastChannel from '#/lib/broadcast' -import {useSession, getAgent} from '#/state/session' -import {useModerationOpts} from '../preferences' -import {fetchPage} from './util' -import {CachedFeedPage, FeedPage} from './types' +import {logger} from '#/logger' import {isNative} from '#/platform/detection' import {useMutedThreads} from '#/state/muted-threads' -import {RQKEY as RQKEY_NOTIFS} from './feed' -import {logger} from '#/logger' +import {getAgent, useSession} from '#/state/session' +import {useModerationOpts} from '../preferences' import {truncateAndInvalidate} from '../util' -import {AppState} from 'react-native' +import {RQKEY as RQKEY_NOTIFS} from './feed' +import {CachedFeedPage, FeedPage} from './types' +import {fetchPage} from './util' const UPDATE_INTERVAL = 30 * 1e3 // 30sec const broadcast = new BroadcastChannel('NOTIFS_BROADCAST_CHANNEL') +const emitter = new EventEmitter() + type StateContext = string interface ApiContext { @@ -56,6 +60,18 @@ export function Provider({children}: React.PropsWithChildren<{}>) { unreadCount: 0, }) + React.useEffect(() => { + function markAsUnusable() { + if (cacheRef.current) { + cacheRef.current.usableInFeed = false + } + } + emitter.addListener('invalidate', markAsUnusable) + return () => { + emitter.removeListener('invalidate', markAsUnusable) + } + }, []) + // periodic sync React.useEffect(() => { if (!hasSession || !checkUnreadRef.current) { @@ -214,3 +230,7 @@ function countUnread(page: FeedPage) { } return num } + +export function invalidateCachedUnreadPage() { + emitter.emit('invalidate') +} From 44039c68d678e99f9dc712f1a6dae87aed970ca3 Mon Sep 17 00:00:00 2001 From: Hailey Date: Fri, 12 Apr 2024 12:53:48 -0700 Subject: [PATCH 015/167] Store QP authors in the DID cache (#3509) * store qp author in did cache * organize * this seems nicer * move outside of jsx --- src/view/com/util/post-embeds/QuoteEmbed.tsx | 45 ++++++++++++-------- 1 file changed, 27 insertions(+), 18 deletions(-) diff --git a/src/view/com/util/post-embeds/QuoteEmbed.tsx b/src/view/com/util/post-embeds/QuoteEmbed.tsx index 2b1c3e6179..b5f57825b6 100644 --- a/src/view/com/util/post-embeds/QuoteEmbed.tsx +++ b/src/view/com/util/post-embeds/QuoteEmbed.tsx @@ -1,31 +1,34 @@ import React from 'react' import {StyleProp, StyleSheet, View, ViewStyle} from 'react-native' import { - AppBskyFeedDefs, - AppBskyEmbedRecord, - AppBskyFeedPost, - AppBskyEmbedImages, - AppBskyEmbedRecordWithMedia, AppBskyEmbedExternal, - RichText as RichTextAPI, + AppBskyEmbedImages, + AppBskyEmbedRecord, + AppBskyEmbedRecordWithMedia, + AppBskyFeedDefs, + AppBskyFeedPost, moderatePost, ModerationDecision, + RichText as RichTextAPI, } from '@atproto/api' import {AtUri} from '@atproto/api' -import {PostMeta} from '../PostMeta' -import {Link} from '../Link' -import {Text} from '../text/Text' -import {usePalette} from 'lib/hooks/usePalette' -import {ComposerOptsQuote} from 'state/shell/composer' -import {PostEmbeds} from '.' -import {PostAlerts} from '../../../../components/moderation/PostAlerts' -import {makeProfileLink} from 'lib/routes/links' -import {InfoCircleIcon} from 'lib/icons' import {Trans} from '@lingui/macro' +import {useQueryClient} from '@tanstack/react-query' + import {useModerationOpts} from '#/state/queries/preferences' -import {ContentHider} from '../../../../components/moderation/ContentHider' -import {RichText} from '#/components/RichText' +import {RQKEY as RQKEY_URI} from '#/state/queries/resolve-uri' +import {usePalette} from 'lib/hooks/usePalette' +import {InfoCircleIcon} from 'lib/icons' +import {makeProfileLink} from 'lib/routes/links' +import {ComposerOptsQuote} from 'state/shell/composer' import {atoms as a} from '#/alf' +import {RichText} from '#/components/RichText' +import {ContentHider} from '../../../../components/moderation/ContentHider' +import {PostAlerts} from '../../../../components/moderation/PostAlerts' +import {Link} from '../Link' +import {PostMeta} from '../PostMeta' +import {Text} from '../text/Text' +import {PostEmbeds} from '.' export function MaybeQuoteEmbed({ embed, @@ -107,6 +110,7 @@ export function QuoteEmbed({ moderation?: ModerationDecision style?: StyleProp }) { + const queryClient = useQueryClient() const pal = usePalette('default') const itemUrip = new AtUri(quote.uri) const itemHref = makeProfileLink(quote.author, 'post', itemUrip.rkey) @@ -134,13 +138,18 @@ export function QuoteEmbed({ } }, [quote.embeds]) + const onBeforePress = React.useCallback(() => { + queryClient.setQueryData(RQKEY_URI(quote.author.handle), quote.author.did) + }, [queryClient, quote.author.did, quote.author.handle]) + return ( + title={itemTitle} + onBeforePress={onBeforePress}> Date: Fri, 12 Apr 2024 14:13:13 -0700 Subject: [PATCH 016/167] PWI improvements (#3489) * Enable home and feeds on the PWI * Add global SigninDialog to drive useRequireAuth() * Tweak desktop styles * Make the logo in leftnav PWI a clickable home link * Add label * Improve dialog on web * Fix query key * Go to home after signout from settings screen * Filter out feeds from the discover listing for logged out users which are known to break without auth * Update profile header follow/subscribe to give signin prompt * Show profile feeds tabs on pwi * Add language selector to account creation footer and pwi left nav desktop --------- Co-authored-by: dan --- src/Navigation.tsx | 18 +- src/components/AppLanguageDropdown.tsx | 67 +++++++ src/components/AppLanguageDropdown.web.tsx | 62 +++++++ src/components/dialogs/Context.tsx | 7 +- src/components/dialogs/Signin.tsx | 99 ++++++++++ .../Profile/Header/ProfileHeaderLabeler.tsx | 51 +++--- .../Profile/Header/ProfileHeaderStandard.tsx | 1 - src/screens/Signup/index.tsx | 11 +- src/state/queries/feed.ts | 27 ++- src/state/session/index.tsx | 8 +- src/view/com/auth/HomeLoggedOutCTA.tsx | 170 ------------------ src/view/com/auth/SplashScreen.tsx | 58 +----- src/view/com/auth/SplashScreen.web.tsx | 56 +----- src/view/com/home/HomeHeaderLayout.web.tsx | 63 ++++--- src/view/com/home/HomeHeaderLayoutMobile.tsx | 44 ++--- src/view/screens/Feeds.tsx | 89 +++++---- src/view/screens/Home.tsx | 9 +- src/view/screens/Profile.tsx | 3 +- src/view/screens/Settings/index.tsx | 10 +- src/view/shell/Drawer.tsx | 56 +++--- src/view/shell/NavSignupCard.tsx | 19 +- src/view/shell/index.tsx | 39 ++-- src/view/shell/index.web.tsx | 30 ++-- 23 files changed, 519 insertions(+), 478 deletions(-) create mode 100644 src/components/AppLanguageDropdown.tsx create mode 100644 src/components/AppLanguageDropdown.web.tsx create mode 100644 src/components/dialogs/Signin.tsx delete mode 100644 src/view/com/auth/HomeLoggedOutCTA.tsx diff --git a/src/Navigation.tsx b/src/Navigation.tsx index 070c57960d..99c0ebf3c3 100644 --- a/src/Navigation.tsx +++ b/src/Navigation.tsx @@ -193,7 +193,7 @@ function commonScreens(Stack: typeof HomeTab, unreadCountLabel?: string) { ProfileFeedScreen} - options={{title: title(msg`Feed`), requireAuth: true}} + options={{title: title(msg`Feed`)}} /> - HomeScreen} - options={{requireAuth: true}} - /> + HomeScreen} /> {commonScreens(HomeTab)} ) @@ -371,11 +367,7 @@ function FeedsTabNavigator() { animationDuration: 250, contentStyle: pal.view, }}> - FeedsScreen} - options={{requireAuth: true}} - /> + FeedsScreen} /> {commonScreens(FeedsTab as typeof HomeTab)} ) @@ -451,7 +443,7 @@ const FlatNavigator = () => { HomeScreen} - options={{title: title(msg`Home`), requireAuth: true}} + options={{title: title(msg`Home`)}} /> { FeedsScreen} - options={{title: title(msg`Feeds`), requireAuth: true}} + options={{title: title(msg`Feeds`)}} /> [0]) => { + if (!value) return + if (sanitizedLang !== value) { + setLangPrefs.setAppLanguage(sanitizeAppLanguageSetting(value)) + } + }, + [sanitizedLang, setLangPrefs], + ) + + return ( + + Boolean(l.code2)).map(l => ({ + label: l.name, + value: l.code2, + key: l.code2, + }))} + useNativeAndroidPickerStyle={false} + style={{ + inputAndroid: { + color: t.atoms.text_contrast_medium.color, + fontSize: 16, + paddingRight: 12 + 4, + }, + inputIOS: { + color: t.atoms.text.color, + fontSize: 16, + paddingRight: 12 + 4, + }, + }} + /> + + + + + + ) +} diff --git a/src/components/AppLanguageDropdown.web.tsx b/src/components/AppLanguageDropdown.web.tsx new file mode 100644 index 0000000000..302a30ca66 --- /dev/null +++ b/src/components/AppLanguageDropdown.web.tsx @@ -0,0 +1,62 @@ +import React from 'react' +import {View} from 'react-native' + +import {sanitizeAppLanguageSetting} from '#/locale/helpers' +import {APP_LANGUAGES} from '#/locale/languages' +import {useLanguagePrefs, useLanguagePrefsApi} from '#/state/preferences' +import {atoms as a, useTheme} from '#/alf' +import {ChevronBottom_Stroke2_Corner0_Rounded as ChevronDown} from '#/components/icons/Chevron' +import {Text} from '#/components/Typography' + +export function AppLanguageDropdown() { + const t = useTheme() + + const langPrefs = useLanguagePrefs() + const setLangPrefs = useLanguagePrefsApi() + + const sanitizedLang = sanitizeAppLanguageSetting(langPrefs.appLanguage) + + const onChangeAppLanguage = React.useCallback( + (ev: React.ChangeEvent) => { + const value = ev.target.value + + if (!value) return + if (sanitizedLang !== value) { + setLangPrefs.setAppLanguage(sanitizeAppLanguageSetting(value)) + } + }, + [sanitizedLang, setLangPrefs], + ) + + return ( + + + {APP_LANGUAGES.find(l => l.code2 === sanitizedLang)?.name} + + + + + + ) +} diff --git a/src/components/dialogs/Context.tsx b/src/components/dialogs/Context.tsx index 87bd5c2ed7..c9dff9a999 100644 --- a/src/components/dialogs/Context.tsx +++ b/src/components/dialogs/Context.tsx @@ -6,10 +6,12 @@ type Control = Dialog.DialogOuterProps['control'] type ControlsContext = { mutedWordsDialogControl: Control + signinDialogControl: Control } const ControlsContext = React.createContext({ mutedWordsDialogControl: {} as Control, + signinDialogControl: {} as Control, }) export function useGlobalDialogsControlContext() { @@ -18,9 +20,10 @@ export function useGlobalDialogsControlContext() { export function Provider({children}: React.PropsWithChildren<{}>) { const mutedWordsDialogControl = Dialog.useDialogControl() + const signinDialogControl = Dialog.useDialogControl() const ctx = React.useMemo( - () => ({mutedWordsDialogControl}), - [mutedWordsDialogControl], + () => ({mutedWordsDialogControl, signinDialogControl}), + [mutedWordsDialogControl, signinDialogControl], ) return ( diff --git a/src/components/dialogs/Signin.tsx b/src/components/dialogs/Signin.tsx new file mode 100644 index 0000000000..488eb5c73a --- /dev/null +++ b/src/components/dialogs/Signin.tsx @@ -0,0 +1,99 @@ +import React from 'react' +import {View} from 'react-native' +import {msg, Trans} from '@lingui/macro' +import {useLingui} from '@lingui/react' + +import {isNative} from '#/platform/detection' +import {useLoggedOutViewControls} from '#/state/shell/logged-out' +import {useCloseAllActiveElements} from '#/state/util' +import {Logo} from '#/view/icons/Logo' +import {Logotype} from '#/view/icons/Logotype' +import {atoms as a, useBreakpoints, useTheme} from '#/alf' +import {Button, ButtonText} from '#/components/Button' +import * as Dialog from '#/components/Dialog' +import {useGlobalDialogsControlContext} from '#/components/dialogs/Context' +import {Text} from '#/components/Typography' + +export function SigninDialog() { + const {signinDialogControl: control} = useGlobalDialogsControlContext() + return ( + + + + + ) +} + +function SigninDialogInner({}: {control: Dialog.DialogOuterProps['control']}) { + const t = useTheme() + const {_} = useLingui() + const {gtMobile} = useBreakpoints() + const {requestSwitchToAccount} = useLoggedOutViewControls() + const closeAllActiveElements = useCloseAllActiveElements() + + const showSignIn = React.useCallback(() => { + closeAllActiveElements() + requestSwitchToAccount({requestedAccount: 'none'}) + }, [requestSwitchToAccount, closeAllActiveElements]) + + const showCreateAccount = React.useCallback(() => { + closeAllActiveElements() + requestSwitchToAccount({requestedAccount: 'new'}) + }, [requestSwitchToAccount, closeAllActiveElements]) + + return ( + + + + + + + + + + + + Sign in or create your account to join the conversation! + + + + + + + + + + {isNative && } + + + + + ) +} diff --git a/src/screens/Profile/Header/ProfileHeaderLabeler.tsx b/src/screens/Profile/Header/ProfileHeaderLabeler.tsx index d0fd5e20bd..b9145822c9 100644 --- a/src/screens/Profile/Header/ProfileHeaderLabeler.tsx +++ b/src/screens/Profile/Header/ProfileHeaderLabeler.tsx @@ -18,7 +18,7 @@ import {useModalControls} from '#/state/modals' import {useLabelerSubscriptionMutation} from '#/state/queries/labeler' import {useLikeMutation, useUnlikeMutation} from '#/state/queries/like' import {usePreferencesQuery} from '#/state/queries/preferences' -import {useSession} from '#/state/session' +import {useRequireAuth, useSession} from '#/state/session' import {useAnalytics} from 'lib/analytics/analytics' import {useHaptics} from 'lib/haptics' import {useProfileShadow} from 'state/cache/profile-shadow' @@ -64,6 +64,7 @@ let ProfileHeaderLabeler = ({ const {currentAccount, hasSession} = useSession() const {openModal} = useModalControls() const {track} = useAnalytics() + const requireAuth = useRequireAuth() const playHaptic = useHaptics() const cantSubscribePrompt = Prompt.usePromptControl() const isSelf = currentAccount?.did === profile.did @@ -125,27 +126,32 @@ let ProfileHeaderLabeler = ({ }) }, [track, openModal, profile]) - const onPressSubscribe = React.useCallback(async () => { - if (!canSubscribe) { - cantSubscribePrompt.open() - return - } - try { - await toggleSubscription({ - did: profile.did, - subscribe: !isSubscribed, - }) - } catch (e: any) { - // setSubscriptionError(e.message) - logger.error(`Failed to subscribe to labeler`, {message: e.message}) - } - }, [ - toggleSubscription, - isSubscribed, - profile, - canSubscribe, - cantSubscribePrompt, - ]) + const onPressSubscribe = React.useCallback( + () => + requireAuth(async () => { + if (!canSubscribe) { + cantSubscribePrompt.open() + return + } + try { + await toggleSubscription({ + did: profile.did, + subscribe: !isSubscribed, + }) + } catch (e: any) { + // setSubscriptionError(e.message) + logger.error(`Failed to subscribe to labeler`, {message: e.message}) + } + }), + [ + requireAuth, + toggleSubscription, + isSubscribed, + profile, + canSubscribe, + cantSubscribePrompt, + ], + ) const isMe = React.useMemo( () => currentAccount?.did === profile.did, @@ -184,7 +190,6 @@ let ProfileHeaderLabeler = ({ ? _(msg`Unsubscribe from this labeler`) : _(msg`Subscribe to this labeler`) } - disabled={!hasSession} onPress={onPressSubscribe}> {state => ( void}) { - - + + + Having trouble?{' '} - + Contact support diff --git a/src/state/queries/feed.ts b/src/state/queries/feed.ts index c56912491a..0d3de89697 100644 --- a/src/state/queries/feed.ts +++ b/src/state/queries/feed.ts @@ -17,7 +17,7 @@ import {sanitizeDisplayName} from '#/lib/strings/display-names' import {sanitizeHandle} from '#/lib/strings/handles' import {STALE} from '#/state/queries' import {usePreferencesQuery} from '#/state/queries/preferences' -import {getAgent} from '#/state/session' +import {getAgent, useSession} from '#/state/session' import {router} from '#/routes' export type FeedSourceFeedInfo = { @@ -216,17 +216,38 @@ const FOLLOWING_FEED_STUB: FeedSourceInfo = { likeCount: 0, likeUri: '', } +const DISCOVER_FEED_STUB: FeedSourceInfo = { + type: 'feed', + displayName: 'Discover', + uri: '', + route: { + href: '/', + name: 'Home', + params: {}, + }, + cid: '', + avatar: '', + description: new RichText({text: ''}), + creatorDid: '', + creatorHandle: '', + likeCount: 0, + likeUri: '', +} const pinnedFeedInfosQueryKeyRoot = 'pinnedFeedsInfos' export function usePinnedFeedsInfos() { + const {hasSession} = useSession() const {data: preferences, isLoading: isLoadingPrefs} = usePreferencesQuery() const pinnedUris = preferences?.feeds?.pinned ?? [] return useQuery({ staleTime: STALE.INFINITY, enabled: !isLoadingPrefs, - queryKey: [pinnedFeedInfosQueryKeyRoot, pinnedUris.join(',')], + queryKey: [ + pinnedFeedInfosQueryKeyRoot, + (hasSession ? 'authed:' : 'unauthed:') + pinnedUris.join(','), + ], queryFn: async () => { let resolved = new Map() @@ -264,7 +285,7 @@ export function usePinnedFeedsInfos() { ) // The returned result will have the original order. - const result = [FOLLOWING_FEED_STUB] + const result = [hasSession ? FOLLOWING_FEED_STUB : DISCOVER_FEED_STUB] await Promise.allSettled([feedsPromise, ...listsPromises]) for (let pinnedUri of pinnedUris) { if (resolved.has(pinnedUri)) { diff --git a/src/state/session/index.tsx b/src/state/session/index.tsx index 5c7cc15916..b88181ebda 100644 --- a/src/state/session/index.tsx +++ b/src/state/session/index.tsx @@ -15,8 +15,8 @@ import {logger} from '#/logger' import {isWeb} from '#/platform/detection' import * as persisted from '#/state/persisted' import {PUBLIC_BSKY_AGENT} from '#/state/queries' -import {useLoggedOutViewControls} from '#/state/shell/logged-out' import {useCloseAllActiveElements} from '#/state/util' +import {useGlobalDialogsControlContext} from '#/components/dialogs/Context' import {IS_DEV} from '#/env' import {emitSessionDropped} from '../events' import {readLabelers} from './agent-config' @@ -702,8 +702,8 @@ export function useSessionApi() { export function useRequireAuth() { const {hasSession} = useSession() - const {setShowLoggedOut} = useLoggedOutViewControls() const closeAll = useCloseAllActiveElements() + const {signinDialogControl} = useGlobalDialogsControlContext() return React.useCallback( (fn: () => void) => { @@ -711,10 +711,10 @@ export function useRequireAuth() { fn() } else { closeAll() - setShowLoggedOut(true) + signinDialogControl.open() } }, - [hasSession, setShowLoggedOut, closeAll], + [hasSession, signinDialogControl, closeAll], ) } diff --git a/src/view/com/auth/HomeLoggedOutCTA.tsx b/src/view/com/auth/HomeLoggedOutCTA.tsx deleted file mode 100644 index 4c8c35da73..0000000000 --- a/src/view/com/auth/HomeLoggedOutCTA.tsx +++ /dev/null @@ -1,170 +0,0 @@ -import React from 'react' -import {StyleSheet, TouchableOpacity, View} from 'react-native' -import {msg, Trans} from '@lingui/macro' -import {useLingui} from '@lingui/react' - -import {usePalette} from '#/lib/hooks/usePalette' -import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' -import {colors, s} from '#/lib/styles' -import {useLoggedOutViewControls} from '#/state/shell/logged-out' -import {TextLink} from '../util/Link' -import {Text} from '../util/text/Text' -import {ScrollView} from '../util/Views' - -export function HomeLoggedOutCTA() { - const pal = usePalette('default') - const {_} = useLingui() - const {isMobile} = useWebMediaQueries() - const {requestSwitchToAccount} = useLoggedOutViewControls() - - const showCreateAccount = React.useCallback(() => { - requestSwitchToAccount({requestedAccount: 'new'}) - }, [requestSwitchToAccount]) - - const showSignIn = React.useCallback(() => { - requestSwitchToAccount({requestedAccount: 'none'}) - }, [requestSwitchToAccount]) - - return ( - - - - Bluesky - - - See what's next - - - - - - Create a new account - - - - - Sign in - - - - - - - - - - - ) -} - -const styles = StyleSheet.create({ - container: { - height: '100%', - }, - hero: { - justifyContent: 'center', - paddingTop: 100, - paddingBottom: 30, - }, - heroMobile: { - paddingBottom: 50, - }, - title: { - textAlign: 'center', - fontSize: 68, - fontWeight: 'bold', - }, - subtitle: { - textAlign: 'center', - fontSize: 48, - fontWeight: 'bold', - }, - subtitleMobile: { - fontSize: 42, - }, - btnsDesktop: { - flexDirection: 'row', - justifyContent: 'center', - gap: 20, - marginHorizontal: 20, - }, - btn: { - borderRadius: 32, - width: 230, - paddingVertical: 12, - marginBottom: 20, - }, - btnMobile: { - flex: 1, - width: 'auto', - marginHorizontal: 20, - paddingVertical: 16, - }, - btnLabel: { - textAlign: 'center', - fontSize: 18, - }, - btnLabelMobile: { - textAlign: 'center', - fontSize: 21, - }, - - footer: { - flexDirection: 'row', - gap: 20, - justifyContent: 'center', - }, - footerLink: {}, -}) diff --git a/src/view/com/auth/SplashScreen.tsx b/src/view/com/auth/SplashScreen.tsx index 763b01dfa1..8eac1ab82f 100644 --- a/src/view/com/auth/SplashScreen.tsx +++ b/src/view/com/auth/SplashScreen.tsx @@ -1,19 +1,15 @@ import React from 'react' import {View} from 'react-native' -import RNPickerSelect, {PickerSelectProps} from 'react-native-picker-select' import {useSafeAreaInsets} from 'react-native-safe-area-context' import {msg, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' -import {sanitizeAppLanguageSetting} from '#/locale/helpers' -import {APP_LANGUAGES} from '#/locale/languages' -import {useLanguagePrefs, useLanguagePrefsApi} from '#/state/preferences' import {Logo} from '#/view/icons/Logo' import {Logotype} from '#/view/icons/Logotype' import {ErrorBoundary} from 'view/com/util/ErrorBoundary' import {atoms as a, useTheme} from '#/alf' +import {AppLanguageDropdown} from '#/components/AppLanguageDropdown' import {Button, ButtonText} from '#/components/Button' -import {ChevronBottom_Stroke2_Corner0_Rounded as ChevronDown} from '#/components/icons/Chevron' import {Text} from '#/components/Typography' import {CenteredView} from '../util/Views' @@ -27,22 +23,8 @@ export const SplashScreen = ({ const t = useTheme() const {_} = useLingui() - const langPrefs = useLanguagePrefs() - const setLangPrefs = useLanguagePrefsApi() const insets = useSafeAreaInsets() - const sanitizedLang = sanitizeAppLanguageSetting(langPrefs.appLanguage) - - const onChangeAppLanguage = React.useCallback( - (value: Parameters[0]) => { - if (!value) return - if (sanitizedLang !== value) { - setLangPrefs.setAppLanguage(sanitizeAppLanguageSetting(value)) - } - }, - [sanitizedLang, setLangPrefs], - ) - return ( @@ -99,43 +81,7 @@ export const SplashScreen = ({ a.justify_center, a.align_center, ]}> - - Boolean(l.code2)).map(l => ({ - label: l.name, - value: l.code2, - key: l.code2, - }))} - useNativeAndroidPickerStyle={false} - style={{ - inputAndroid: { - color: t.atoms.text_contrast_medium.color, - fontSize: 16, - paddingRight: 12 + 4, - }, - inputIOS: { - color: t.atoms.text.color, - fontSize: 16, - paddingRight: 12 + 4, - }, - }} - /> - - - - - + diff --git a/src/view/com/auth/SplashScreen.web.tsx b/src/view/com/auth/SplashScreen.web.tsx index 7a2ee16cf3..f905e1e8d5 100644 --- a/src/view/com/auth/SplashScreen.web.tsx +++ b/src/view/com/auth/SplashScreen.web.tsx @@ -4,16 +4,13 @@ import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' import {msg, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' -import {sanitizeAppLanguageSetting} from '#/locale/helpers' -import {APP_LANGUAGES} from '#/locale/languages' -import {useLanguagePrefs, useLanguagePrefsApi} from '#/state/preferences' import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' import {Logo} from '#/view/icons/Logo' import {Logotype} from '#/view/icons/Logotype' import {ErrorBoundary} from 'view/com/util/ErrorBoundary' import {atoms as a, useTheme} from '#/alf' +import {AppLanguageDropdown} from '#/components/AppLanguageDropdown' import {Button, ButtonText} from '#/components/Button' -import {ChevronBottom_Stroke2_Corner0_Rounded as ChevronDown} from '#/components/icons/Chevron' import {InlineLinkText} from '#/components/Link' import {Text} from '#/components/Typography' import {CenteredView} from '../util/Views' @@ -131,23 +128,6 @@ export const SplashScreen = ({ function Footer() { const t = useTheme() - const langPrefs = useLanguagePrefs() - const setLangPrefs = useLanguagePrefsApi() - - const sanitizedLang = sanitizeAppLanguageSetting(langPrefs.appLanguage) - - const onChangeAppLanguage = React.useCallback( - (ev: React.ChangeEvent) => { - const value = ev.target.value - - if (!value) return - if (sanitizedLang !== value) { - setLangPrefs.setAppLanguage(sanitizeAppLanguageSetting(value)) - } - }, - [sanitizedLang, setLangPrefs], - ) - return ( - - - {APP_LANGUAGES.find(l => l.code2 === sanitizedLang)?.name} - - - - - + ) } diff --git a/src/view/com/home/HomeHeaderLayout.web.tsx b/src/view/com/home/HomeHeaderLayout.web.tsx index 9818b56f6f..644d4cab6c 100644 --- a/src/view/com/home/HomeHeaderLayout.web.tsx +++ b/src/view/com/home/HomeHeaderLayout.web.tsx @@ -1,20 +1,22 @@ import React from 'react' import {StyleSheet, View} from 'react-native' import Animated from 'react-native-reanimated' -import {usePalette} from 'lib/hooks/usePalette' -import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' -import {HomeHeaderLayoutMobile} from './HomeHeaderLayoutMobile' -import {Logo} from '#/view/icons/Logo' -import {Link} from '../util/Link' import { FontAwesomeIcon, FontAwesomeIconStyle, } from '@fortawesome/react-native-fontawesome' -import {useLingui} from '@lingui/react' import {msg} from '@lingui/macro' +import {useLingui} from '@lingui/react' + import {CogIcon} from '#/lib/icons' -import {useMinimalShellMode} from 'lib/hooks/useMinimalShellMode' +import {useSession} from '#/state/session' import {useShellLayout} from '#/state/shell/shell-layout' +import {useMinimalShellMode} from 'lib/hooks/useMinimalShellMode' +import {usePalette} from 'lib/hooks/usePalette' +import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' +import {Logo} from '#/view/icons/Logo' +import {Link} from '../util/Link' +import {HomeHeaderLayoutMobile} from './HomeHeaderLayoutMobile' export function HomeHeaderLayout(props: { children: React.ReactNode @@ -38,32 +40,35 @@ function HomeHeaderLayoutDesktopAndTablet({ const pal = usePalette('default') const {headerMinimalShellTransform} = useMinimalShellMode() const {headerHeight} = useShellLayout() + const {hasSession} = useSession() const {_} = useLingui() return ( <> - - - - - - - - - + {hasSession && ( + + + + + + + + + + )} {tabBarAnchor} { diff --git a/src/view/com/home/HomeHeaderLayoutMobile.tsx b/src/view/com/home/HomeHeaderLayoutMobile.tsx index d7b7231c60..78fa9af865 100644 --- a/src/view/com/home/HomeHeaderLayoutMobile.tsx +++ b/src/view/com/home/HomeHeaderLayoutMobile.tsx @@ -1,23 +1,24 @@ import React from 'react' import {StyleSheet, TouchableOpacity, View} from 'react-native' -import {usePalette} from 'lib/hooks/usePalette' -import {Link} from '../util/Link' +import Animated from 'react-native-reanimated' import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' import {FontAwesomeIconStyle} from '@fortawesome/react-native-fontawesome' -import {HITSLOP_10} from 'lib/constants' -import Animated from 'react-native-reanimated' import {msg} from '@lingui/macro' import {useLingui} from '@lingui/react' -import {useMinimalShellMode} from 'lib/hooks/useMinimalShellMode' + +import {useSession} from '#/state/session' import {useSetDrawerOpen} from '#/state/shell/drawer-open' import {useShellLayout} from '#/state/shell/shell-layout' +import {HITSLOP_10} from 'lib/constants' +import {useMinimalShellMode} from 'lib/hooks/useMinimalShellMode' +import {usePalette} from 'lib/hooks/usePalette' import {isWeb} from 'platform/detection' import {Logo} from '#/view/icons/Logo' - -import {IS_DEV} from '#/env' import {atoms} from '#/alf' -import {Link as Link2} from '#/components/Link' import {ColorPalette_Stroke2_Corner0_Rounded as ColorPalette} from '#/components/icons/ColorPalette' +import {Link as Link2} from '#/components/Link' +import {IS_DEV} from '#/env' +import {Link} from '../util/Link' export function HomeHeaderLayoutMobile({ children, @@ -30,6 +31,7 @@ export function HomeHeaderLayoutMobile({ const setDrawerOpen = useSetDrawerOpen() const {headerHeight} = useShellLayout() const {headerMinimalShellTransform} = useMinimalShellMode() + const {hasSession} = useSession() const onPressAvi = React.useCallback(() => { setDrawerOpen(true) @@ -76,18 +78,20 @@ export function HomeHeaderLayoutMobile({ )} - - - + {hasSession && ( + + + + )} {children} diff --git a/src/view/screens/Feeds.tsx b/src/view/screens/Feeds.tsx index 2e3bf08db5..e64ab08df2 100644 --- a/src/view/screens/Feeds.tsx +++ b/src/view/screens/Feeds.tsx @@ -1,52 +1,53 @@ import React from 'react' import { ActivityIndicator, - StyleSheet, - View, type FlatList, Pressable, + StyleSheet, + View, } from 'react-native' import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' import {FontAwesomeIconStyle} from '@fortawesome/react-native-fontawesome' -import {ViewHeader} from 'view/com/util/ViewHeader' -import {FAB} from 'view/com/util/fab/FAB' -import {Link} from 'view/com/util/Link' -import {NativeStackScreenProps, FeedsTabNavigatorParams} from 'lib/routes/types' -import {usePalette} from 'lib/hooks/usePalette' -import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' -import {ComposeIcon2, CogIcon, MagnifyingGlassIcon2} from 'lib/icons' -import {s} from 'lib/styles' -import {atoms as a, useTheme} from '#/alf' -import {SearchInput, SearchInputRef} from 'view/com/util/forms/SearchInput' -import {UserAvatar} from 'view/com/util/UserAvatar' -import { - LoadingPlaceholder, - FeedFeedLoadingPlaceholder, -} from 'view/com/util/LoadingPlaceholder' -import {ErrorMessage} from 'view/com/util/error/ErrorMessage' -import debounce from 'lodash.debounce' -import {Text} from 'view/com/util/text/Text' -import {List} from 'view/com/util/List' -import {useFocusEffect} from '@react-navigation/native' -import {FeedSourceCard} from 'view/com/feeds/FeedSourceCard' -import {Trans, msg} from '@lingui/macro' +import {msg, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' -import {useSetMinimalShellMode} from '#/state/shell' -import {usePreferencesQuery} from '#/state/queries/preferences' +import {useFocusEffect} from '@react-navigation/native' +import debounce from 'lodash.debounce' + +import {isNative, isWeb} from '#/platform/detection' import { + getAvatarTypeFromUri, useFeedSourceInfoQuery, useGetPopularFeedsQuery, useSearchPopularFeedsMutation, - getAvatarTypeFromUri, } from '#/state/queries/feed' -import {cleanError} from 'lib/strings/errors' -import {useComposerControls} from '#/state/shell/composer' +import {usePreferencesQuery} from '#/state/queries/preferences' import {useSession} from '#/state/session' -import {isNative, isWeb} from '#/platform/detection' +import {useSetMinimalShellMode} from '#/state/shell' +import {useComposerControls} from '#/state/shell/composer' import {HITSLOP_10} from 'lib/constants' +import {usePalette} from 'lib/hooks/usePalette' +import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' +import {CogIcon, ComposeIcon2, MagnifyingGlassIcon2} from 'lib/icons' +import {FeedsTabNavigatorParams, NativeStackScreenProps} from 'lib/routes/types' +import {cleanError} from 'lib/strings/errors' +import {s} from 'lib/styles' +import {FeedSourceCard} from 'view/com/feeds/FeedSourceCard' +import {ErrorMessage} from 'view/com/util/error/ErrorMessage' +import {FAB} from 'view/com/util/fab/FAB' +import {SearchInput, SearchInputRef} from 'view/com/util/forms/SearchInput' +import {Link} from 'view/com/util/Link' +import {List} from 'view/com/util/List' +import { + FeedFeedLoadingPlaceholder, + LoadingPlaceholder, +} from 'view/com/util/LoadingPlaceholder' +import {Text} from 'view/com/util/text/Text' +import {UserAvatar} from 'view/com/util/UserAvatar' +import {ViewHeader} from 'view/com/util/ViewHeader' +import {atoms as a, useTheme} from '#/alf' import {IconCircle} from '#/components/IconCircle' -import {ListSparkle_Stroke2_Corner0_Rounded} from '#/components/icons/ListSparkle' import {ListMagnifyingGlass_Stroke2_Corner0_Rounded} from '#/components/icons/ListMagnifyingGlass' +import {ListSparkle_Stroke2_Corner0_Rounded} from '#/components/icons/ListSparkle' type Props = NativeStackScreenProps @@ -100,6 +101,22 @@ type FlatlistSlice = key: string } +// HACK +// the protocol doesn't yet tell us which feeds are personalized +// this list is used to filter out feed recommendations from logged out users +// for the ones we know need it +// -prf +const KNOWN_AUTHED_ONLY_FEEDS = [ + 'at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.feed.generator/with-friends', // popular with friends, by bsky.app + 'at://did:plc:tenurhgjptubkk5zf5qhi3og/app.bsky.feed.generator/mutuals', // mutuals, by skyfeed + 'at://did:plc:tenurhgjptubkk5zf5qhi3og/app.bsky.feed.generator/only-posts', // only posts, by skyfeed + 'at://did:plc:wzsilnxf24ehtmmc3gssy5bu/app.bsky.feed.generator/mentions', // mentions, by flicknow + 'at://did:plc:q6gjnaw2blty4crticxkmujt/app.bsky.feed.generator/bangers', // my bangers, by jaz + 'at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.feed.generator/mutuals', // mutuals, by bluesky + 'at://did:plc:q6gjnaw2blty4crticxkmujt/app.bsky.feed.generator/my-followers', // followers, by jaz + 'at://did:plc:vpkhqolt662uhesyj6nxm7ys/app.bsky.feed.generator/followpics', // the gram, by why +] + export function FeedsScreen(_props: Props) { const pal = usePalette('default') const {openComposer} = useComposerControls() @@ -299,7 +316,15 @@ export function FeedsScreen(_props: Props) { for (const page of popularFeeds.pages || []) { slices = slices.concat( page.feeds - .filter(feed => !preferences?.feeds?.saved.includes(feed.uri)) + .filter(feed => { + if ( + !hasSession && + KNOWN_AUTHED_ONLY_FEEDS.includes(feed.uri) + ) { + return false + } + return !preferences?.feeds?.saved.includes(feed.uri) + }) .map(feed => ({ key: `popularFeed:${feed.uri}`, type: 'popularFeed', diff --git a/src/view/screens/Home.tsx b/src/view/screens/Home.tsx index 39bdac669c..7a2a88265b 100644 --- a/src/view/screens/Home.tsx +++ b/src/view/screens/Home.tsx @@ -2,6 +2,7 @@ import React from 'react' import {ActivityIndicator, AppState, StyleSheet, View} from 'react-native' import {useFocusEffect} from '@react-navigation/native' +import {PROD_DEFAULT_FEED} from '#/lib/constants' import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback' import {useSetTitle} from '#/lib/hooks/useSetTitle' import {logEvent, LogEvents, useGate} from '#/lib/statsig/statsig' @@ -19,7 +20,6 @@ import {Pager, PagerRef, RenderTabBarFnProps} from 'view/com/pager/Pager' import {CustomFeedEmptyState} from 'view/com/posts/CustomFeedEmptyState' import {FollowingEmptyState} from 'view/com/posts/FollowingEmptyState' import {FollowingEndOfFeed} from 'view/com/posts/FollowingEndOfFeed' -import {HomeLoggedOutCTA} from '../com/auth/HomeLoggedOutCTA' import {HomeHeader} from '../com/home/HomeHeader' type Props = NativeStackScreenProps @@ -231,7 +231,12 @@ function HomeScreenReady({ onPageSelected={onPageSelected} onPageScrollStateChanged={onPageScrollStateChanged} renderTabBar={renderTabBar}> - + ) } diff --git a/src/view/screens/Profile.tsx b/src/view/screens/Profile.tsx index c391f80508..f71e1330ef 100644 --- a/src/view/screens/Profile.tsx +++ b/src/view/screens/Profile.tsx @@ -184,8 +184,7 @@ function ProfileScreenLoaded({ const showRepliesTab = hasSession const showMediaTab = !hasLabeler const showLikesTab = isMe - const showFeedsTab = - hasSession && (isMe || (profile.associated?.feedgens || 0) > 0) + const showFeedsTab = isMe || (profile.associated?.feedgens || 0) > 0 const showListsTab = hasSession && (isMe || (profile.associated?.lists || 0) > 0) diff --git a/src/view/screens/Settings/index.tsx b/src/view/screens/Settings/index.tsx index 8a7fa5e714..b97faafad1 100644 --- a/src/view/screens/Settings/index.tsx +++ b/src/view/screens/Settings/index.tsx @@ -71,6 +71,7 @@ import {UserAvatar} from 'view/com/util/UserAvatar' import {ScrollView} from 'view/com/util/Views' import {useDialogControl} from '#/components/Dialog' import {BirthDateSettingsDialog} from '#/components/dialogs/BirthDateSettings' +import {navigate, resetToTab} from '#/Navigation' import {ExportCarDialog} from './ExportCarDialog' function SettingsAccountCard({account}: {account: SessionAccount}) { @@ -104,7 +105,14 @@ function SettingsAccountCard({account}: {account: SessionAccount}) { { - logout('Settings') + if (isNative) { + logout('Settings') + resetToTab('HomeTab') + } else { + navigate('Home').then(() => { + logout('Settings') + }) + } }} accessibilityRole="button" accessibilityLabel={_(msg`Sign out`)} diff --git a/src/view/shell/Drawer.tsx b/src/view/shell/Drawer.tsx index 1bf5647f66..a7342179d4 100644 --- a/src/view/shell/Drawer.tsx +++ b/src/view/shell/Drawer.tsx @@ -9,49 +9,49 @@ import { View, ViewStyle, } from 'react-native' -import {useNavigation, StackActions} from '@react-navigation/native' import { FontAwesomeIcon, FontAwesomeIconStyle, } from '@fortawesome/react-native-fontawesome' -import {s, colors} from 'lib/styles' +import {msg, Trans} from '@lingui/macro' +import {useLingui} from '@lingui/react' +import {StackActions, useNavigation} from '@react-navigation/native' + +import {emitSoftReset} from '#/state/events' +import {useUnreadNotifications} from '#/state/queries/notifications/unread' +import {useProfileQuery} from '#/state/queries/profile' +import {SessionAccount, useSession} from '#/state/session' +import {useSetDrawerOpen} from '#/state/shell' +import {useAnalytics} from 'lib/analytics/analytics' import {FEEDBACK_FORM_URL, HELP_DESK_URL} from 'lib/constants' +import {useNavigationTabState} from 'lib/hooks/useNavigationTabState' +import {usePalette} from 'lib/hooks/usePalette' import { - HomeIcon, - HomeIconSolid, BellIcon, BellIconSolid, - UserIcon, CogIcon, + HandIcon, + HashtagIcon, + HomeIcon, + HomeIconSolid, + ListIcon, MagnifyingGlassIcon2, MagnifyingGlassIcon2Solid, + UserIcon, UserIconSolid, - HashtagIcon, - ListIcon, - HandIcon, } from 'lib/icons' -import {UserAvatar} from 'view/com/util/UserAvatar' -import {Text} from 'view/com/util/text/Text' -import {useTheme} from 'lib/ThemeContext' -import {usePalette} from 'lib/hooks/usePalette' -import {useAnalytics} from 'lib/analytics/analytics' -import {pluralize} from 'lib/strings/helpers' import {getTabState, TabState} from 'lib/routes/helpers' import {NavigationProp} from 'lib/routes/types' -import {useNavigationTabState} from 'lib/hooks/useNavigationTabState' +import {pluralize} from 'lib/strings/helpers' +import {colors, s} from 'lib/styles' +import {useTheme} from 'lib/ThemeContext' import {isWeb} from 'platform/detection' -import {formatCountShortOnly} from 'view/com/util/numeric/format' -import {Trans, msg} from '@lingui/macro' -import {useLingui} from '@lingui/react' -import {useSetDrawerOpen} from '#/state/shell' -import {useSession, SessionAccount} from '#/state/session' -import {useProfileQuery} from '#/state/queries/profile' -import {useUnreadNotifications} from '#/state/queries/notifications/unread' -import {emitSoftReset} from '#/state/events' import {NavSignupCard} from '#/view/shell/NavSignupCard' -import {TextLink} from '../com/util/Link' - +import {formatCountShortOnly} from 'view/com/util/numeric/format' +import {Text} from 'view/com/util/text/Text' +import {UserAvatar} from 'view/com/util/UserAvatar' import {useTheme as useAlfTheme} from '#/alf' +import {TextLink} from '../com/util/Link' let DrawerProfileCard = ({ account, @@ -246,7 +246,11 @@ let DrawerContent = ({}: {}): React.ReactNode => { ) : ( - + <> + + + + )} diff --git a/src/view/shell/NavSignupCard.tsx b/src/view/shell/NavSignupCard.tsx index 83d1414984..aa807f0cc6 100644 --- a/src/view/shell/NavSignupCard.tsx +++ b/src/view/shell/NavSignupCard.tsx @@ -3,13 +3,16 @@ import {View} from 'react-native' import {msg, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' -import {s} from 'lib/styles' -import {usePalette} from 'lib/hooks/usePalette' -import {Text} from '#/view/com/util/text/Text' -import {Button} from '#/view/com/util/forms/Button' import {useLoggedOutViewControls} from '#/state/shell/logged-out' import {useCloseAllActiveElements} from '#/state/util' +import {usePalette} from 'lib/hooks/usePalette' +import {s} from 'lib/styles' +import {Button} from '#/view/com/util/forms/Button' +import {Text} from '#/view/com/util/text/Text' import {Logo} from '#/view/icons/Logo' +import {atoms as a} from '#/alf' +import {AppLanguageDropdown} from '#/components/AppLanguageDropdown' +import {Link} from '#/components/Link' let NavSignupCard = ({}: {}): React.ReactNode => { const {_} = useLingui() @@ -35,7 +38,9 @@ let NavSignupCard = ({}: {}): React.ReactNode => { paddingTop: 6, marginBottom: 24, }}> - + + + @@ -62,6 +67,10 @@ let NavSignupCard = ({}: {}): React.ReactNode => { + + + + ) } diff --git a/src/view/shell/index.tsx b/src/view/shell/index.tsx index f29183095a..c554112ed5 100644 --- a/src/view/shell/index.tsx +++ b/src/view/shell/index.tsx @@ -1,37 +1,39 @@ import React from 'react' -import {StatusBar} from 'expo-status-bar' import { + BackHandler, DimensionValue, StyleSheet, useWindowDimensions, View, - BackHandler, } from 'react-native' -import {useSafeAreaInsets} from 'react-native-safe-area-context' import {Drawer} from 'react-native-drawer-layout' +import Animated from 'react-native-reanimated' +import {useSafeAreaInsets} from 'react-native-safe-area-context' +import {StatusBar} from 'expo-status-bar' import {useNavigationState} from '@react-navigation/native' -import {ModalsContainer} from 'view/com/modals/Modal' -import {Lightbox} from 'view/com/lightbox/Lightbox' -import {ErrorBoundary} from 'view/com/util/ErrorBoundary' -import {DrawerContent} from './Drawer' -import {Composer} from './Composer' -import {useTheme} from 'lib/ThemeContext' -import {usePalette} from 'lib/hooks/usePalette' -import {RoutesContainer, TabsNavigator} from '../../Navigation' -import {isStateAtTabRoot} from 'lib/routes/helpers' + +import {useSession} from '#/state/session' import { useIsDrawerOpen, - useSetDrawerOpen, useIsDrawerSwipeDisabled, + useSetDrawerOpen, } from '#/state/shell' -import {isAndroid} from 'platform/detection' -import {useSession} from '#/state/session' import {useCloseAnyActiveElement} from '#/state/util' +import {usePalette} from 'lib/hooks/usePalette' import * as notifications from 'lib/notifications/notifications' -import {Outlet as PortalOutlet} from '#/components/Portal' -import {MutedWordsDialog} from '#/components/dialogs/MutedWords' +import {isStateAtTabRoot} from 'lib/routes/helpers' +import {useTheme} from 'lib/ThemeContext' +import {isAndroid} from 'platform/detection' import {useDialogStateContext} from 'state/dialogs' -import Animated from 'react-native-reanimated' +import {Lightbox} from 'view/com/lightbox/Lightbox' +import {ModalsContainer} from 'view/com/modals/Modal' +import {ErrorBoundary} from 'view/com/util/ErrorBoundary' +import {MutedWordsDialog} from '#/components/dialogs/MutedWords' +import {SigninDialog} from '#/components/dialogs/Signin' +import {Outlet as PortalOutlet} from '#/components/Portal' +import {RoutesContainer, TabsNavigator} from '../../Navigation' +import {Composer} from './Composer' +import {DrawerContent} from './Drawer' function ShellInner() { const isDrawerOpen = useIsDrawerOpen() @@ -101,6 +103,7 @@ function ShellInner() { + diff --git a/src/view/shell/index.web.tsx b/src/view/shell/index.web.tsx index 02993ac462..51fb4a0a11 100644 --- a/src/view/shell/index.web.tsx +++ b/src/view/shell/index.web.tsx @@ -1,24 +1,25 @@ import React, {useEffect} from 'react' -import {View, StyleSheet, TouchableOpacity} from 'react-native' -import {useNavigation} from '@react-navigation/native' +import {StyleSheet, TouchableOpacity, View} from 'react-native' import {msg} from '@lingui/macro' import {useLingui} from '@lingui/react' +import {useNavigation} from '@react-navigation/native' -import {ErrorBoundary} from '../com/util/ErrorBoundary' -import {Lightbox} from '../com/lightbox/Lightbox' -import {ModalsContainer} from '../com/modals/Modal' -import {Composer} from './Composer.web' -import {useColorSchemeStyle} from 'lib/hooks/useColorSchemeStyle' -import {s, colors} from 'lib/styles' -import {RoutesContainer, FlatNavigator} from '../../Navigation' -import {DrawerContent} from './Drawer' -import {useWebMediaQueries} from '../../lib/hooks/useWebMediaQueries' -import {NavigationProp} from 'lib/routes/types' +import {useWebBodyScrollLock} from '#/lib/hooks/useWebBodyScrollLock' import {useIsDrawerOpen, useSetDrawerOpen} from '#/state/shell' import {useCloseAllActiveElements} from '#/state/util' -import {useWebBodyScrollLock} from '#/lib/hooks/useWebBodyScrollLock' -import {Outlet as PortalOutlet} from '#/components/Portal' +import {useColorSchemeStyle} from 'lib/hooks/useColorSchemeStyle' +import {NavigationProp} from 'lib/routes/types' +import {colors, s} from 'lib/styles' import {MutedWordsDialog} from '#/components/dialogs/MutedWords' +import {SigninDialog} from '#/components/dialogs/Signin' +import {Outlet as PortalOutlet} from '#/components/Portal' +import {useWebMediaQueries} from '../../lib/hooks/useWebMediaQueries' +import {FlatNavigator, RoutesContainer} from '../../Navigation' +import {Lightbox} from '../com/lightbox/Lightbox' +import {ModalsContainer} from '../com/modals/Modal' +import {ErrorBoundary} from '../com/util/ErrorBoundary' +import {Composer} from './Composer.web' +import {DrawerContent} from './Drawer' function ShellInner() { const isDrawerOpen = useIsDrawerOpen() @@ -45,6 +46,7 @@ function ShellInner() { + From 4fab3c42f9b4c96b68b8779ab2e5334071887448 Mon Sep 17 00:00:00 2001 From: Paul Frazee Date: Fri, 12 Apr 2024 14:43:55 -0700 Subject: [PATCH 017/167] Remove report post option from PWI (#3510) --- src/view/com/util/forms/PostDropdownBtn.tsx | 46 +++++++++++---------- 1 file changed, 25 insertions(+), 21 deletions(-) diff --git a/src/view/com/util/forms/PostDropdownBtn.tsx b/src/view/com/util/forms/PostDropdownBtn.tsx index 959e0f692e..04dfa203a1 100644 --- a/src/view/com/util/forms/PostDropdownBtn.tsx +++ b/src/view/com/util/forms/PostDropdownBtn.tsx @@ -283,29 +283,33 @@ let PostDropdownBtn = ({ )} - + {hasSession && ( + <> + - - {!isAuthor && ( - reportDialogControl.open()}> - {_(msg`Report post`)} - - - )} + + {!isAuthor && ( + reportDialogControl.open()}> + {_(msg`Report post`)} + + + )} - {isAuthor && ( - - {_(msg`Delete post`)} - - - )} - + {isAuthor && ( + + {_(msg`Delete post`)} + + + )} + + + )} From 7b02b5d716e172f30191fabb30fd2e943e594542 Mon Sep 17 00:00:00 2001 From: Minseo Lee Date: Sat, 13 Apr 2024 06:45:13 +0900 Subject: [PATCH 018/167] Update Korean localization (#3491) * Update messages.po * Update messages.po --- src/locale/locales/ko/messages.po | 629 +++++++++++++++--------------- 1 file changed, 307 insertions(+), 322 deletions(-) diff --git a/src/locale/locales/ko/messages.po b/src/locale/locales/ko/messages.po index 48c7a235bd..572b64a4f1 100644 --- a/src/locale/locales/ko/messages.po +++ b/src/locale/locales/ko/messages.po @@ -54,7 +54,7 @@ msgid "⚠Invalid Handle" msgstr "⚠잘못된 핸들" #: src/view/com/util/ViewHeader.tsx:89 -#: src/view/screens/Search/Search.tsx:649 +#: src/view/screens/Search/Search.tsx:739 msgid "Access navigation links and settings" msgstr "탐색 링크 및 설정으로 이동합니다" @@ -63,7 +63,7 @@ msgid "Access profile and other navigation links" msgstr "프로필 및 기타 탐색 링크로 이동합니다" #: src/view/com/modals/EditImage.tsx:300 -#: src/view/screens/Settings/index.tsx:470 +#: src/view/screens/Settings/index.tsx:413 msgid "Accessibility" msgstr "접근성" @@ -72,8 +72,8 @@ msgid "account" msgstr "계정" #: src/screens/Login/LoginForm.tsx:144 -#: src/view/screens/Settings/index.tsx:327 -#: src/view/screens/Settings/index.tsx:743 +#: src/view/screens/Settings/index.tsx:322 +#: src/view/screens/Settings/index.tsx:699 msgid "Account" msgstr "계정" @@ -123,7 +123,7 @@ msgstr "계정 언뮤트됨" #: src/view/com/auth/onboarding/RecommendedFeedsItem.tsx:150 #: src/view/com/modals/ListAddRemoveUsers.tsx:268 #: src/view/com/modals/UserAddRemoveLists.tsx:219 -#: src/view/screens/ProfileList.tsx:827 +#: src/view/screens/ProfileList.tsx:829 msgid "Add" msgstr "추가" @@ -131,13 +131,13 @@ msgstr "추가" msgid "Add a content warning" msgstr "콘텐츠 경고 추가" -#: src/view/screens/ProfileList.tsx:817 +#: src/view/screens/ProfileList.tsx:819 msgid "Add a user to this list" msgstr "이 리스트에 사용자 추가" #: src/components/dialogs/SwitchAccount.tsx:55 -#: src/view/screens/Settings/index.tsx:402 -#: src/view/screens/Settings/index.tsx:411 +#: src/view/screens/Settings/index.tsx:397 +#: src/view/screens/Settings/index.tsx:406 msgid "Add account" msgstr "계정 추가" @@ -210,7 +210,7 @@ msgid "Adult content is disabled." msgstr "성인 콘텐츠가 비활성화되어 있습니다." #: src/screens/Moderation/index.tsx:375 -#: src/view/screens/Settings/index.tsx:684 +#: src/view/screens/Settings/index.tsx:627 msgid "Advanced" msgstr "고급" @@ -287,13 +287,13 @@ msgstr "앱 비밀번호 이름에는 문자, 숫자, 공백, 대시, 밑줄만 msgid "App Password names must be at least 4 characters long." msgstr "앱 비밀번호 이름은 4자 이상이어야 합니다." -#: src/view/screens/Settings/index.tsx:695 +#: src/view/screens/Settings/index.tsx:638 msgid "App password settings" msgstr "앱 비밀번호 설정" #: src/Navigation.tsx:251 #: src/view/screens/AppPasswords.tsx:189 -#: src/view/screens/Settings/index.tsx:704 +#: src/view/screens/Settings/index.tsx:647 msgid "App Passwords" msgstr "앱 비밀번호" @@ -310,7 +310,7 @@ msgstr "\"{0}\" 라벨 이의신청" msgid "Appeal submitted." msgstr "이의신청 제출함" -#: src/view/screens/Settings/index.tsx:485 +#: src/view/screens/Settings/index.tsx:428 msgid "Appearance" msgstr "모양" @@ -366,7 +366,7 @@ msgstr "뒤로" msgid "Based on your interest in {interestsText}" msgstr "{interestsText}에 대한 관심사 기반" -#: src/view/screens/Settings/index.tsx:542 +#: src/view/screens/Settings/index.tsx:485 msgid "Basics" msgstr "기본" @@ -374,7 +374,7 @@ msgstr "기본" msgid "Birthday" msgstr "생년월일" -#: src/view/screens/Settings/index.tsx:359 +#: src/view/screens/Settings/index.tsx:354 msgid "Birthday:" msgstr "생년월일:" @@ -392,16 +392,16 @@ msgstr "계정 차단" msgid "Block Account?" msgstr "계정을 차단하시겠습니까?" -#: src/view/screens/ProfileList.tsx:530 +#: src/view/screens/ProfileList.tsx:532 msgid "Block accounts" msgstr "계정 차단" -#: src/view/screens/ProfileList.tsx:478 -#: src/view/screens/ProfileList.tsx:634 +#: src/view/screens/ProfileList.tsx:480 +#: src/view/screens/ProfileList.tsx:636 msgid "Block list" msgstr "리스트 차단" -#: src/view/screens/ProfileList.tsx:629 +#: src/view/screens/ProfileList.tsx:631 msgid "Block these accounts?" msgstr "이 계정들을 차단하시겠습니까?" @@ -431,11 +431,11 @@ msgstr "차단한 계정은 내 스레드에 답글을 달거나 나를 멘션 msgid "Blocked post." msgstr "차단된 게시물." -#: src/screens/Profile/Sections/Labels.tsx:152 +#: src/screens/Profile/Sections/Labels.tsx:163 msgid "Blocking does not prevent this labeler from placing labels on your account." msgstr "차단하더라도 이 라벨러가 내 계정에 라벨을 붙이는 것을 막지는 못합니다." -#: src/view/screens/ProfileList.tsx:631 +#: src/view/screens/ProfileList.tsx:633 msgid "Blocking is public. Blocked accounts cannot reply in your threads, mention you, or otherwise interact with you." msgstr "차단 목록은 공개됩니다. 차단한 계정은 내 스레드에 답글을 달거나 나를 멘션하거나 기타 다른 방식으로 나와 상호작용할 수 없습니다." @@ -489,10 +489,6 @@ msgstr "이미지 흐리게 및 피드에서 필터링" msgid "Books" msgstr "책" -#: src/view/screens/Settings/index.tsx:893 -#~ msgid "Build version {0} {1}" -#~ msgstr "빌드 버전 {0} {1}" - #: src/view/com/auth/HomeLoggedOutCTA.tsx:92 #: src/view/com/auth/SplashScreen.web.tsx:166 msgid "Business" @@ -552,7 +548,7 @@ msgstr "글자, 숫자, 공백, 대시, 밑줄만 포함할 수 있습니다. #: src/view/com/modals/Repost.tsx:88 #: src/view/com/modals/VerifyEmail.tsx:247 #: src/view/com/modals/VerifyEmail.tsx:253 -#: src/view/screens/Search/Search.tsx:718 +#: src/view/screens/Search/Search.tsx:808 #: src/view/shell/desktop/Search.tsx:239 msgid "Cancel" msgstr "취소" @@ -598,17 +594,17 @@ msgstr "연결된 웹사이트를 여는 것을 취소합니다" msgid "Change" msgstr "변경" -#: src/view/screens/Settings/index.tsx:353 +#: src/view/screens/Settings/index.tsx:348 msgctxt "action" msgid "Change" msgstr "변경" -#: src/view/screens/Settings/index.tsx:716 +#: src/view/screens/Settings/index.tsx:659 msgid "Change handle" msgstr "핸들 변경" #: src/view/com/modals/ChangeHandle.tsx:162 -#: src/view/screens/Settings/index.tsx:727 +#: src/view/screens/Settings/index.tsx:670 msgid "Change Handle" msgstr "핸들 변경" @@ -616,12 +612,12 @@ msgstr "핸들 변경" msgid "Change my email" msgstr "내 이메일 변경하기" -#: src/view/screens/Settings/index.tsx:754 +#: src/view/screens/Settings/index.tsx:710 msgid "Change password" msgstr "비밀번호 변경" #: src/view/com/modals/ChangePassword.tsx:141 -#: src/view/screens/Settings/index.tsx:765 +#: src/view/screens/Settings/index.tsx:721 msgid "Change Password" msgstr "비밀번호 변경" @@ -675,32 +671,32 @@ msgstr "기본 피드 선택" msgid "Choose your password" msgstr "비밀번호를 입력하세요" -#: src/view/screens/Settings/index.tsx:868 +#: src/view/screens/Settings/index.tsx:824 msgid "Clear all legacy storage data" msgstr "모든 레거시 스토리지 데이터 지우기" -#: src/view/screens/Settings/index.tsx:871 +#: src/view/screens/Settings/index.tsx:827 msgid "Clear all legacy storage data (restart after this)" msgstr "모든 레거시 스토리지 데이터 지우기 (이후 다시 시작)" -#: src/view/screens/Settings/index.tsx:880 +#: src/view/screens/Settings/index.tsx:836 msgid "Clear all storage data" msgstr "모든 스토리지 데이터 지우기" -#: src/view/screens/Settings/index.tsx:883 +#: src/view/screens/Settings/index.tsx:839 msgid "Clear all storage data (restart after this)" msgstr "모든 스토리지 데이터 지우기 (이후 다시 시작)" #: src/view/com/util/forms/SearchInput.tsx:88 -#: src/view/screens/Search/Search.tsx:699 +#: src/view/screens/Search/Search.tsx:789 msgid "Clear search query" msgstr "검색어 지우기" -#: src/view/screens/Settings/index.tsx:869 +#: src/view/screens/Settings/index.tsx:825 msgid "Clears all legacy storage data" msgstr "모든 레거시 스토리지 데이터를 지웁니다" -#: src/view/screens/Settings/index.tsx:881 +#: src/view/screens/Settings/index.tsx:837 msgid "Clears all storage data" msgstr "모든 스토리지 데이터를 지웁니다" @@ -939,7 +935,7 @@ msgstr "요리" msgid "Copied" msgstr "복사됨" -#: src/view/screens/Settings/index.tsx:251 +#: src/view/screens/Settings/index.tsx:246 msgid "Copied build version to clipboard" msgstr "빌드 버전 클립보드에 복사됨" @@ -962,7 +958,7 @@ msgstr "복사" msgid "Copy {0}" msgstr "{0} 복사" -#: src/view/screens/ProfileList.tsx:388 +#: src/view/screens/ProfileList.tsx:390 msgid "Copy link to list" msgstr "리스트 링크 복사" @@ -985,7 +981,7 @@ msgstr "저작권 정책" msgid "Could not load feed" msgstr "피드를 불러올 수 없습니다" -#: src/view/screens/ProfileList.tsx:907 +#: src/view/screens/ProfileList.tsx:909 msgid "Could not load list" msgstr "리스트를 불러올 수 없습니다" @@ -995,7 +991,7 @@ msgstr "리스트를 불러올 수 없습니다" msgid "Create a new account" msgstr "새 계정 만들기" -#: src/view/screens/Settings/index.tsx:403 +#: src/view/screens/Settings/index.tsx:398 msgid "Create a new Bluesky account" msgstr "새 Bluesky 계정을 만듭니다" @@ -1013,7 +1009,7 @@ msgstr "앱 비밀번호 만들기" msgid "Create new account" msgstr "새 계정 만들기" -#: src/components/ReportDialog/SelectReportOptionView.tsx:93 +#: src/components/ReportDialog/SelectReportOptionView.tsx:94 msgid "Create report for {0}" msgstr "{0}에 대한 신고 작성하기" @@ -1047,8 +1043,8 @@ msgstr "커뮤니티에서 구축한 맞춤 피드는 새로운 경험을 제공 msgid "Customize media from external sites." msgstr "외부 사이트 미디어를 사용자 지정합니다." -#: src/view/screens/Settings/index.tsx:504 -#: src/view/screens/Settings/index.tsx:530 +#: src/view/screens/Settings/index.tsx:447 +#: src/view/screens/Settings/index.tsx:473 msgid "Dark" msgstr "어두움" @@ -1056,7 +1052,7 @@ msgstr "어두움" msgid "Dark mode" msgstr "어두운 모드" -#: src/view/screens/Settings/index.tsx:517 +#: src/view/screens/Settings/index.tsx:460 msgid "Dark Theme" msgstr "어두운 테마" @@ -1064,7 +1060,7 @@ msgstr "어두운 테마" msgid "Date of birth" msgstr "생년월일" -#: src/view/screens/Settings/index.tsx:841 +#: src/view/screens/Settings/index.tsx:797 msgid "Debug Moderation" msgstr "검토 디버그" @@ -1074,11 +1070,11 @@ msgstr "디버그 패널" #: src/view/com/util/forms/PostDropdownBtn.tsx:319 #: src/view/screens/AppPasswords.tsx:268 -#: src/view/screens/ProfileList.tsx:613 +#: src/view/screens/ProfileList.tsx:615 msgid "Delete" msgstr "삭제" -#: src/view/screens/Settings/index.tsx:796 +#: src/view/screens/Settings/index.tsx:752 msgid "Delete account" msgstr "계정 삭제" @@ -1094,7 +1090,7 @@ msgstr "앱 비밀번호 삭제" msgid "Delete app password?" msgstr "앱 비밀번호를 삭제하시겠습니까?" -#: src/view/screens/ProfileList.tsx:415 +#: src/view/screens/ProfileList.tsx:417 msgid "Delete List" msgstr "리스트 삭제" @@ -1102,7 +1098,7 @@ msgstr "리스트 삭제" msgid "Delete my account" msgstr "내 계정 삭제" -#: src/view/screens/Settings/index.tsx:808 +#: src/view/screens/Settings/index.tsx:764 msgid "Delete My Account…" msgstr "내 계정 삭제…" @@ -1111,7 +1107,7 @@ msgstr "내 계정 삭제…" msgid "Delete post" msgstr "게시물 삭제" -#: src/view/screens/ProfileList.tsx:608 +#: src/view/screens/ProfileList.tsx:610 msgid "Delete this list?" msgstr "이 리스트를 삭제하시겠습니까?" @@ -1138,10 +1134,18 @@ msgstr "설명" msgid "Did you want to say anything?" msgstr "하고 싶은 말이 있나요?" -#: src/view/screens/Settings/index.tsx:523 +#: src/view/screens/Settings/index.tsx:466 msgid "Dim" msgstr "어둑함" +#: src/view/screens/Settings/index.tsx:689 +msgid "Disable haptics" +msgstr "햅틱 끄기" + +#: src/view/screens/Settings/index.tsx:689 +msgid "Disable vibrations" +msgstr "진동 끄기" + #: src/lib/moderation/useLabelBehaviorDescription.ts:32 #: src/lib/moderation/useLabelBehaviorDescription.ts:42 #: src/lib/moderation/useLabelBehaviorDescription.ts:68 @@ -1302,7 +1306,7 @@ msgstr "아바타 편집" msgid "Edit image" msgstr "이미지 편집" -#: src/view/screens/ProfileList.tsx:403 +#: src/view/screens/ProfileList.tsx:405 msgid "Edit list details" msgstr "리스트 세부 정보 편집" @@ -1312,7 +1316,7 @@ msgstr "검토 리스트 편집" #: src/Navigation.tsx:256 #: src/view/screens/Feeds.tsx:434 -#: src/view/screens/SavedFeeds.tsx:84 +#: src/view/screens/SavedFeeds.tsx:85 msgid "Edit My Feeds" msgstr "내 피드 편집" @@ -1320,12 +1324,12 @@ msgstr "내 피드 편집" msgid "Edit my profile" msgstr "내 프로필 편집" -#: src/screens/Profile/Header/ProfileHeaderLabeler.tsx:171 +#: src/screens/Profile/Header/ProfileHeaderLabeler.tsx:172 #: src/screens/Profile/Header/ProfileHeaderStandard.tsx:168 msgid "Edit profile" msgstr "프로필 편집" -#: src/screens/Profile/Header/ProfileHeaderLabeler.tsx:174 +#: src/screens/Profile/Header/ProfileHeaderLabeler.tsx:175 #: src/screens/Profile/Header/ProfileHeaderStandard.tsx:171 msgid "Edit Profile" msgstr "프로필 편집" @@ -1373,13 +1377,13 @@ msgstr "이메일 변경됨" msgid "Email verified" msgstr "이메일 확인됨" -#: src/view/screens/Settings/index.tsx:331 +#: src/view/screens/Settings/index.tsx:326 msgid "Email:" msgstr "이메일:" #: src/components/dialogs/EmbedConsent.tsx:101 msgid "Enable {0} only" -msgstr "{0}만 사용" +msgstr "{0}에서만 사용" #: src/screens/Moderation/index.tsx:329 msgid "Enable adult content" @@ -1397,11 +1401,7 @@ msgstr "피드에서 성인 콘텐츠 사용" #: src/components/dialogs/EmbedConsent.tsx:82 #: src/components/dialogs/EmbedConsent.tsx:89 msgid "Enable external media" -msgstr "" - -#: src/view/com/modals/EmbedConsent.tsx:97 -#~ msgid "Enable External Media" -#~ msgstr "외부 미디어 사용" +msgstr "외부 미디어 사용" #: src/view/screens/PreferencesExternalEmbeds.tsx:75 msgid "Enable media players for" @@ -1413,13 +1413,13 @@ msgstr "내가 팔로우하는 사람들 간의 답글만 표시합니다." #: src/components/dialogs/EmbedConsent.tsx:94 msgid "Enable this source only" -msgstr "" +msgstr "이 소스에서만 사용" #: src/screens/Moderation/index.tsx:339 msgid "Enabled" msgstr "활성화됨" -#: src/screens/Profile/Sections/Feed.tsx:84 +#: src/screens/Profile/Sections/Feed.tsx:100 msgid "End of feed" msgstr "피드 끝" @@ -1477,7 +1477,7 @@ msgstr "사용자 이름 및 비밀번호 입력" msgid "Error receiving captcha response." msgstr "캡차 응답을 수신하는 동안 오류가 발생했습니다." -#: src/view/screens/Search/Search.tsx:111 +#: src/view/screens/Search/Search.tsx:112 msgid "Error:" msgstr "오류:" @@ -1527,12 +1527,12 @@ msgstr "노골적이거나 불쾌감을 줄 수 있는 미디어." msgid "Explicit sexual images." msgstr "노골적인 성적 이미지." -#: src/view/screens/Settings/index.tsx:777 +#: src/view/screens/Settings/index.tsx:733 msgid "Export my data" msgstr "내 데이터 내보내기" #: src/view/screens/Settings/ExportCarDialog.tsx:44 -#: src/view/screens/Settings/index.tsx:788 +#: src/view/screens/Settings/index.tsx:744 msgid "Export My Data" msgstr "내 데이터 내보내기" @@ -1548,11 +1548,11 @@ msgstr "외부 미디어는 웹사이트가 나와 내 기기에 대한 정보 #: src/Navigation.tsx:275 #: src/view/screens/PreferencesExternalEmbeds.tsx:52 -#: src/view/screens/Settings/index.tsx:677 +#: src/view/screens/Settings/index.tsx:620 msgid "External Media Preferences" msgstr "외부 미디어 설정" -#: src/view/screens/Settings/index.tsx:668 +#: src/view/screens/Settings/index.tsx:611 msgid "External media settings" msgstr "외부 미디어 설정" @@ -1598,8 +1598,8 @@ msgstr "피드백" #: src/Navigation.tsx:464 #: src/view/screens/Feeds.tsx:419 #: src/view/screens/Feeds.tsx:524 -#: src/view/screens/Profile.tsx:194 -#: src/view/shell/bottom-bar/BottomBar.tsx:191 +#: src/view/screens/Profile.tsx:200 +#: src/view/shell/bottom-bar/BottomBar.tsx:192 #: src/view/shell/desktop/LeftNav.tsx:346 #: src/view/shell/Drawer.tsx:479 #: src/view/shell/Drawer.tsx:480 @@ -1610,7 +1610,7 @@ msgstr "피드" msgid "Feeds are created by users to curate content. Choose some feeds that you find interesting." msgstr "피드는 콘텐츠를 큐레이션하기 위해 사용자에 의해 만들어집니다. 관심 있는 피드를 선택하세요." -#: src/view/screens/SavedFeeds.tsx:156 +#: src/view/screens/SavedFeeds.tsx:157 msgid "Feeds are custom algorithms that users build with a little coding expertise. <0/> for more information." msgstr "피드는 사용자가 약간의 코딩 전문 지식만으로 구축할 수 있는 맞춤 알고리즘입니다. <0/>에서 자세한 내용을 확인하세요." @@ -1636,11 +1636,11 @@ msgstr "마무리 중" msgid "Find accounts to follow" msgstr "팔로우할 계정 찾아보기" -#: src/view/screens/Search/Search.tsx:442 +#: src/view/screens/Search/Search.tsx:532 msgid "Find users on Bluesky" msgstr "Bluesky에서 사용자 찾기" -#: src/view/screens/Search/Search.tsx:440 +#: src/view/screens/Search/Search.tsx:530 msgid "Find users with the search tool on the right" msgstr "오른쪽의 검색 도구로 사용자 찾기" @@ -1703,7 +1703,7 @@ msgstr "모두 팔로우" #: src/view/com/post-thread/PostThreadFollowBtn.tsx:144 msgid "Follow Back" -msgstr "" +msgstr "맞팔로우" #: src/screens/Onboarding/StepSuggestedAccounts/index.tsx:182 msgid "Follow selected accounts and continue to the next step" @@ -1745,7 +1745,7 @@ msgstr "팔로우 중" msgid "Following {0}" msgstr "{0} 님을 팔로우했습니다" -#: src/view/screens/Settings/index.tsx:553 +#: src/view/screens/Settings/index.tsx:496 msgid "Following feed preferences" msgstr "팔로우 중 피드 설정" @@ -1753,7 +1753,7 @@ msgstr "팔로우 중 피드 설정" #: src/view/com/home/HomeHeaderLayout.web.tsx:50 #: src/view/com/home/HomeHeaderLayoutMobile.tsx:84 #: src/view/screens/PreferencesFollowingFeed.tsx:104 -#: src/view/screens/Settings/index.tsx:562 +#: src/view/screens/Settings/index.tsx:505 msgid "Following Feed Preferences" msgstr "팔로우 중 피드 설정" @@ -1823,7 +1823,7 @@ msgstr "명백한 법률 또는 서비스 이용약관 위반 행위" #: src/view/com/auth/LoggedOut.tsx:83 #: src/view/screens/NotFound.tsx:55 #: src/view/screens/ProfileFeed.tsx:112 -#: src/view/screens/ProfileList.tsx:916 +#: src/view/screens/ProfileList.tsx:918 #: src/view/shell/desktop/LeftNav.tsx:108 msgid "Go back" msgstr "뒤로" @@ -1833,12 +1833,12 @@ msgstr "뒤로" #: src/screens/Profile/ErrorState.tsx:66 #: src/view/screens/NotFound.tsx:54 #: src/view/screens/ProfileFeed.tsx:117 -#: src/view/screens/ProfileList.tsx:921 +#: src/view/screens/ProfileList.tsx:923 msgid "Go Back" msgstr "뒤로" #: src/components/ReportDialog/SelectReportOptionView.tsx:73 -#: src/components/ReportDialog/SubmitView.tsx:104 +#: src/components/ReportDialog/SubmitView.tsx:102 #: src/screens/Onboarding/Layout.tsx:102 #: src/screens/Onboarding/Layout.tsx:191 #: src/screens/Signup/index.tsx:173 @@ -1853,7 +1853,7 @@ msgstr "홈으로 이동" msgid "Go Home" msgstr "홈으로 이동" -#: src/view/screens/Search/Search.tsx:749 +#: src/view/screens/Search/Search.tsx:839 #: src/view/shell/desktop/Search.tsx:263 msgid "Go to @{queryMaybeHandle}" msgstr "@{queryMaybeHandle}(으)로 이동" @@ -1973,7 +1973,7 @@ msgid "Hmmmm, we couldn't load that moderation service." msgstr "검토 서비스를 불러올 수 없습니다." #: src/Navigation.tsx:454 -#: src/view/shell/bottom-bar/BottomBar.tsx:147 +#: src/view/shell/bottom-bar/BottomBar.tsx:148 #: src/view/shell/desktop/LeftNav.tsx:310 #: src/view/shell/Drawer.tsx:401 #: src/view/shell/Drawer.tsx:402 @@ -2019,7 +2019,7 @@ msgstr "아무것도 선택하지 않으면 모든 연령대에 적합하다는 msgid "If you are not yet an adult according to the laws of your country, your parent or legal guardian must read these Terms on your behalf." msgstr "해당 국가의 법률에 따라 아직 성인이 아닌 경우, 부모 또는 법적 보호자가 대신 이 약관을 읽어야 합니다." -#: src/view/screens/ProfileList.tsx:610 +#: src/view/screens/ProfileList.tsx:612 msgid "If you delete this list, you won't be able to recover it." msgstr "이 리스트를 삭제하면 다시 복구할 수 없습니다." @@ -2140,11 +2140,11 @@ msgstr "{0} 님이 라벨 지정함." msgid "Labeled by the author." msgstr "작성자가 라벨 지정함." -#: src/view/screens/Profile.tsx:188 +#: src/view/screens/Profile.tsx:194 msgid "Labels" msgstr "라벨" -#: src/screens/Profile/Sections/Labels.tsx:142 +#: src/screens/Profile/Sections/Labels.tsx:153 msgid "Labels are annotations on users and content. They can be used to hide, warn, and categorize the network." msgstr "라벨은 사용자 및 콘텐츠에 대한 주석입니다. 네트워크를 숨기고, 경고하고, 분류하는 데 사용할 수 있습니다." @@ -2164,7 +2164,7 @@ msgstr "내 콘텐츠의 라벨" msgid "Language selection" msgstr "언어 선택" -#: src/view/screens/Settings/index.tsx:614 +#: src/view/screens/Settings/index.tsx:557 msgid "Language settings" msgstr "언어 설정" @@ -2173,10 +2173,14 @@ msgstr "언어 설정" msgid "Language Settings" msgstr "언어 설정" -#: src/view/screens/Settings/index.tsx:623 +#: src/view/screens/Settings/index.tsx:566 msgid "Languages" msgstr "언어" +#: src/view/screens/Search/Search.tsx:380 +msgid "Latest" +msgstr "최신" + #: src/components/moderation/ScreenHider.tsx:136 msgid "Learn More" msgstr "더 알아보기" @@ -2211,7 +2215,7 @@ msgstr "Bluesky 떠나기" msgid "left to go." msgstr "명 남았습니다." -#: src/view/screens/Settings/index.tsx:296 +#: src/view/screens/Settings/index.tsx:291 msgid "Legacy storage cleared, you need to restart the app now." msgstr "레거시 스토리지가 지워졌으며 지금 앱을 다시 시작해야 합니다." @@ -2224,16 +2228,16 @@ msgstr "비밀번호를 재설정해 봅시다!" msgid "Let's go!" msgstr "출발!" -#: src/view/screens/Settings/index.tsx:498 +#: src/view/screens/Settings/index.tsx:441 msgid "Light" msgstr "밝음" -#: src/view/com/util/post-ctrls/PostCtrls.tsx:195 +#: src/view/com/util/post-ctrls/PostCtrls.tsx:197 msgid "Like" msgstr "좋아요" -#: src/screens/Profile/Header/ProfileHeaderLabeler.tsx:258 -#: src/view/screens/ProfileFeed.tsx:573 +#: src/screens/Profile/Header/ProfileHeaderLabeler.tsx:259 +#: src/view/screens/ProfileFeed.tsx:585 msgid "Like this feed" msgstr "이 피드에 좋아요 표시" @@ -2257,9 +2261,9 @@ msgstr "{0}명의 사용자가 좋아함" msgid "Liked by {count} {0}" msgstr "{count}명의 사용자가 좋아함" -#: src/screens/Profile/Header/ProfileHeaderLabeler.tsx:278 -#: src/screens/Profile/Header/ProfileHeaderLabeler.tsx:292 -#: src/view/screens/ProfileFeed.tsx:588 +#: src/screens/Profile/Header/ProfileHeaderLabeler.tsx:279 +#: src/screens/Profile/Header/ProfileHeaderLabeler.tsx:293 +#: src/view/screens/ProfileFeed.tsx:600 msgid "Liked by {likeCount} {0}" msgstr "{likeCount}명의 사용자가 좋아함" @@ -2271,7 +2275,7 @@ msgstr "님이 내 맞춤 피드를 좋아합니다" msgid "liked your post" msgstr "님이 내 게시물을 좋아합니다" -#: src/view/screens/Profile.tsx:193 +#: src/view/screens/Profile.tsx:199 msgid "Likes" msgstr "좋아요" @@ -2287,7 +2291,7 @@ msgstr "리스트" msgid "List Avatar" msgstr "리스트 아바타" -#: src/view/screens/ProfileList.tsx:311 +#: src/view/screens/ProfileList.tsx:313 msgid "List blocked" msgstr "리스트 차단됨" @@ -2295,11 +2299,11 @@ msgstr "리스트 차단됨" msgid "List by {0}" msgstr "{0} 님의 리스트" -#: src/view/screens/ProfileList.tsx:355 +#: src/view/screens/ProfileList.tsx:357 msgid "List deleted" msgstr "리스트 삭제됨" -#: src/view/screens/ProfileList.tsx:283 +#: src/view/screens/ProfileList.tsx:285 msgid "List muted" msgstr "리스트 뮤트됨" @@ -2307,17 +2311,17 @@ msgstr "리스트 뮤트됨" msgid "List Name" msgstr "리스트 이름" -#: src/view/screens/ProfileList.tsx:325 +#: src/view/screens/ProfileList.tsx:327 msgid "List unblocked" msgstr "리스트 차단 해제됨" -#: src/view/screens/ProfileList.tsx:297 +#: src/view/screens/ProfileList.tsx:299 msgid "List unmuted" msgstr "리스트 언뮤트됨" #: src/Navigation.tsx:114 -#: src/view/screens/Profile.tsx:189 #: src/view/screens/Profile.tsx:195 +#: src/view/screens/Profile.tsx:201 #: src/view/shell/desktop/LeftNav.tsx:383 #: src/view/shell/Drawer.tsx:495 #: src/view/shell/Drawer.tsx:496 @@ -2328,10 +2332,10 @@ msgstr "리스트" msgid "Load new notifications" msgstr "새 알림 불러오기" -#: src/screens/Profile/Sections/Feed.tsx:70 +#: src/screens/Profile/Sections/Feed.tsx:86 #: src/view/com/feeds/FeedPage.tsx:138 -#: src/view/screens/ProfileFeed.tsx:496 -#: src/view/screens/ProfileList.tsx:695 +#: src/view/screens/ProfileFeed.tsx:507 +#: src/view/screens/ProfileList.tsx:697 msgid "Load new posts" msgstr "새 게시물 불러오기" @@ -2370,7 +2374,7 @@ msgstr "이곳이 당신이 가고자 하는 곳인지 확인하세요!" msgid "Manage your muted words and tags" msgstr "뮤트한 단어 및 태그 관리" -#: src/view/screens/Profile.tsx:192 +#: src/view/screens/Profile.tsx:198 msgid "Media" msgstr "미디어" @@ -2383,7 +2387,7 @@ msgid "Mentioned users" msgstr "멘션한 사용자" #: src/view/com/util/ViewHeader.tsx:87 -#: src/view/screens/Search/Search.tsx:648 +#: src/view/screens/Search/Search.tsx:738 msgid "Menu" msgstr "메뉴" @@ -2397,7 +2401,7 @@ msgstr "오해의 소지가 있는 계정" #: src/Navigation.tsx:119 #: src/screens/Moderation/index.tsx:104 -#: src/view/screens/Settings/index.tsx:645 +#: src/view/screens/Settings/index.tsx:588 #: src/view/shell/desktop/LeftNav.tsx:401 #: src/view/shell/Drawer.tsx:514 #: src/view/shell/Drawer.tsx:515 @@ -2413,13 +2417,13 @@ msgstr "검토 세부 정보" msgid "Moderation list by {0}" msgstr "{0} 님의 검토 리스트" -#: src/view/screens/ProfileList.tsx:789 +#: src/view/screens/ProfileList.tsx:791 msgid "Moderation list by <0/>" msgstr "<0/> 님의 검토 리스트" #: src/view/com/lists/ListCard.tsx:91 #: src/view/com/modals/UserAddRemoveLists.tsx:204 -#: src/view/screens/ProfileList.tsx:787 +#: src/view/screens/ProfileList.tsx:789 msgid "Moderation list by you" msgstr "내 검토 리스트" @@ -2440,7 +2444,7 @@ msgstr "검토 리스트" msgid "Moderation Lists" msgstr "검토 리스트" -#: src/view/screens/Settings/index.tsx:639 +#: src/view/screens/Settings/index.tsx:582 msgid "Moderation settings" msgstr "검토 설정" @@ -2465,7 +2469,7 @@ msgstr "더 보기" msgid "More feeds" msgstr "피드 더 보기" -#: src/view/screens/ProfileList.tsx:599 +#: src/view/screens/ProfileList.tsx:601 msgid "More options" msgstr "옵션 더 보기" @@ -2486,7 +2490,7 @@ msgstr "{truncatedTag} 뮤트" msgid "Mute Account" msgstr "계정 뮤트" -#: src/view/screens/ProfileList.tsx:518 +#: src/view/screens/ProfileList.tsx:520 msgid "Mute accounts" msgstr "계정 뮤트" @@ -2502,12 +2506,12 @@ msgstr "태그에서만 뮤트" msgid "Mute in text & tags" msgstr "글 및 태그에서 뮤트" -#: src/view/screens/ProfileList.tsx:461 -#: src/view/screens/ProfileList.tsx:624 +#: src/view/screens/ProfileList.tsx:463 +#: src/view/screens/ProfileList.tsx:626 msgid "Mute list" msgstr "리스트 뮤트" -#: src/view/screens/ProfileList.tsx:619 +#: src/view/screens/ProfileList.tsx:621 msgid "Mute these accounts?" msgstr "이 계정들을 뮤트하시겠습니까?" @@ -2554,7 +2558,7 @@ msgstr "\"{0}\" 님이 뮤트함" msgid "Muted words & tags" msgstr "뮤트한 단어 및 태그" -#: src/view/screens/ProfileList.tsx:621 +#: src/view/screens/ProfileList.tsx:623 msgid "Muting is private. Muted accounts can interact with you, but you will not see their posts or receive notifications from them." msgstr "뮤트 목록은 비공개입니다. 뮤트한 계정은 나와 상호작용할 수 있지만 해당 계정의 게시물을 보거나 해당 계정으로부터 알림을 받을 수 없습니다." @@ -2571,11 +2575,11 @@ msgstr "내 피드" msgid "My Profile" msgstr "내 프로필" -#: src/view/screens/Settings/index.tsx:596 +#: src/view/screens/Settings/index.tsx:539 msgid "My saved feeds" msgstr "내 저장된 피드" -#: src/view/screens/Settings/index.tsx:602 +#: src/view/screens/Settings/index.tsx:545 msgid "My Saved Feeds" msgstr "내 저장된 피드" @@ -2599,7 +2603,7 @@ msgid "Nature" msgstr "자연" #: src/screens/Login/ForgotPasswordForm.tsx:173 -#: src/screens/Login/LoginForm.tsx:254 +#: src/screens/Login/LoginForm.tsx:255 #: src/view/com/modals/ChangePassword.tsx:168 msgid "Navigates to the next screen" msgstr "다음 화면으로 이동합니다" @@ -2608,15 +2612,10 @@ msgstr "다음 화면으로 이동합니다" msgid "Navigates to your profile" msgstr "내 프로필로 이동합니다" -#: src/components/ReportDialog/SelectReportOptionView.tsx:122 +#: src/components/ReportDialog/SelectReportOptionView.tsx:123 msgid "Need to report a copyright violation?" msgstr "저작권 위반을 신고해야 하나요?" -#: src/view/com/modals/EmbedConsent.tsx:107 -#: src/view/com/modals/EmbedConsent.tsx:123 -#~ msgid "Never load embeds from {0}" -#~ msgstr "{0}에서 임베드를 불러오지 않습니다" - #: src/view/com/auth/onboarding/WelcomeDesktop.tsx:72 #: src/view/com/auth/onboarding/WelcomeMobile.tsx:74 msgid "Never lose access to your followers and data." @@ -2658,10 +2657,10 @@ msgstr "새 게시물" #: src/view/screens/Feeds.tsx:555 #: src/view/screens/Notifications.tsx:168 -#: src/view/screens/Profile.tsx:452 -#: src/view/screens/ProfileFeed.tsx:434 -#: src/view/screens/ProfileList.tsx:199 -#: src/view/screens/ProfileList.tsx:227 +#: src/view/screens/Profile.tsx:481 +#: src/view/screens/ProfileFeed.tsx:445 +#: src/view/screens/ProfileList.tsx:200 +#: src/view/screens/ProfileList.tsx:228 #: src/view/shell/desktop/LeftNav.tsx:252 msgid "New post" msgstr "새 게시물" @@ -2685,8 +2684,8 @@ msgstr "뉴스" #: src/screens/Login/ForgotPasswordForm.tsx:143 #: src/screens/Login/ForgotPasswordForm.tsx:150 -#: src/screens/Login/LoginForm.tsx:253 -#: src/screens/Login/LoginForm.tsx:260 +#: src/screens/Login/LoginForm.tsx:254 +#: src/screens/Login/LoginForm.tsx:261 #: src/screens/Login/SetNewPasswordForm.tsx:174 #: src/screens/Login/SetNewPasswordForm.tsx:180 #: src/screens/Signup/index.tsx:205 @@ -2714,8 +2713,8 @@ msgstr "다음 이미지" msgid "No" msgstr "아니요" -#: src/view/screens/ProfileFeed.tsx:562 -#: src/view/screens/ProfileList.tsx:769 +#: src/view/screens/ProfileFeed.tsx:574 +#: src/view/screens/ProfileList.tsx:771 msgid "No description" msgstr "설명 없음" @@ -2749,8 +2748,8 @@ msgid "No results found for \"{query}\"" msgstr "\"{query}\"에 대한 결과를 찾을 수 없습니다" #: src/view/com/modals/ListAddRemoveUsers.tsx:127 -#: src/view/screens/Search/Search.tsx:283 -#: src/view/screens/Search/Search.tsx:311 +#: src/view/screens/Search/Search.tsx:292 +#: src/view/screens/Search/Search.tsx:330 msgid "No results found for {query}" msgstr "{query}에 대한 결과를 찾을 수 없습니다" @@ -2777,7 +2776,7 @@ msgid "Not Applicable." msgstr "해당 없음." #: src/Navigation.tsx:109 -#: src/view/screens/Profile.tsx:99 +#: src/view/screens/Profile.tsx:101 msgid "Not Found" msgstr "찾을 수 없음" @@ -2788,7 +2787,7 @@ msgstr "나중에 하기" #: src/view/com/profile/ProfileMenu.tsx:368 #: src/view/com/util/forms/PostDropdownBtn.tsx:342 -#: src/view/com/util/post-ctrls/PostCtrls.tsx:246 +#: src/view/com/util/post-ctrls/PostCtrls.tsx:248 msgid "Note about sharing" msgstr "공유 관련 참고 사항" @@ -2799,7 +2798,7 @@ msgstr "참고: Bluesky는 개방형 공개 네트워크입니다. 이 설정은 #: src/Navigation.tsx:469 #: src/view/screens/Notifications.tsx:124 #: src/view/screens/Notifications.tsx:148 -#: src/view/shell/bottom-bar/BottomBar.tsx:215 +#: src/view/shell/bottom-bar/BottomBar.tsx:216 #: src/view/shell/desktop/LeftNav.tsx:365 #: src/view/shell/Drawer.tsx:438 #: src/view/shell/Drawer.tsx:439 @@ -2812,11 +2811,7 @@ msgstr "노출" #: src/lib/moderation/useReportOptions.ts:71 msgid "Nudity or adult content not labeled as such" -msgstr "" - -#: src/lib/moderation/useReportOptions.ts:71 -#~ msgid "Nudity or pornography not labeled as such" -#~ msgstr "누드 또는 음란물로 설정되지 않은 콘텐츠" +msgstr "누드 또는 성인 콘텐츠로 설정되지 않은 콘텐츠" #: src/screens/Signup/index.tsx:142 msgid "of" @@ -2835,7 +2830,7 @@ msgid "Oh no! Something went wrong." msgstr "이런! 뭔가 잘못되었습니다." #: src/screens/Onboarding/StepModeration/AdultContentEnabledPref.tsx:126 -#: src/screens/Profile/Header/ProfileHeaderLabeler.tsx:327 +#: src/screens/Profile/Header/ProfileHeaderLabeler.tsx:328 msgid "OK" msgstr "확인" @@ -2847,7 +2842,7 @@ msgstr "확인" msgid "Oldest replies first" msgstr "오래된 순" -#: src/view/screens/Settings/index.tsx:244 +#: src/view/screens/Settings/index.tsx:239 msgid "Onboarding reset" msgstr "온보딩 재설정" @@ -2869,7 +2864,7 @@ msgstr "이런, 뭔가 잘못되었습니다!" #: src/components/Lists.tsx:170 #: src/view/screens/AppPasswords.tsx:67 -#: src/view/screens/Profile.tsx:99 +#: src/view/screens/Profile.tsx:101 msgid "Oops!" msgstr "이런!" @@ -2882,11 +2877,11 @@ msgstr "공개성" msgid "Open emoji picker" msgstr "이모티콘 선택기 열기" -#: src/view/screens/ProfileFeed.tsx:300 +#: src/view/screens/ProfileFeed.tsx:311 msgid "Open feed options menu" msgstr "피드 옵션 메뉴 열기" -#: src/view/screens/Settings/index.tsx:734 +#: src/view/screens/Settings/index.tsx:677 msgid "Open links with in-app browser" msgstr "링크를 인앱 브라우저로 열기" @@ -2902,12 +2897,12 @@ msgstr "내비게이션 열기" msgid "Open post options menu" msgstr "게시물 옵션 메뉴 열기" -#: src/view/screens/Settings/index.tsx:828 -#: src/view/screens/Settings/index.tsx:838 +#: src/view/screens/Settings/index.tsx:784 +#: src/view/screens/Settings/index.tsx:794 msgid "Open storybook page" msgstr "스토리북 페이지 열기" -#: src/view/screens/Settings/index.tsx:816 +#: src/view/screens/Settings/index.tsx:772 msgid "Open system log" msgstr "시스템 로그 열기" @@ -2931,7 +2926,7 @@ msgstr "기기에서 카메라를 엽니다" msgid "Opens composer" msgstr "답글 작성 상자를 엽니다" -#: src/view/screens/Settings/index.tsx:615 +#: src/view/screens/Settings/index.tsx:558 msgid "Opens configurable language settings" msgstr "구성 가능한 언어 설정을 엽니다" @@ -2939,7 +2934,7 @@ msgstr "구성 가능한 언어 설정을 엽니다" msgid "Opens device photo gallery" msgstr "기기의 사진 갤러리를 엽니다" -#: src/view/screens/Settings/index.tsx:669 +#: src/view/screens/Settings/index.tsx:612 msgid "Opens external embeds settings" msgstr "외부 임베드 설정을 엽니다" @@ -2959,23 +2954,23 @@ msgstr "존재하는 Bluesky 계정에 로그인하는 플로를 엽니다" msgid "Opens list of invite codes" msgstr "초대 코드 목록을 엽니다" -#: src/view/screens/Settings/index.tsx:798 +#: src/view/screens/Settings/index.tsx:754 msgid "Opens modal for account deletion confirmation. Requires email code" msgstr "계정 삭제 확인을 위한 대화 상자를 엽니다. 이메일 코드가 필요합니다" -#: src/view/screens/Settings/index.tsx:756 +#: src/view/screens/Settings/index.tsx:712 msgid "Opens modal for changing your Bluesky password" msgstr "Bluesky 비밀번호 변경을 위한 대화 상자를 엽니다" -#: src/view/screens/Settings/index.tsx:718 +#: src/view/screens/Settings/index.tsx:661 msgid "Opens modal for choosing a new Bluesky handle" msgstr "새로운 Bluesky 핸들을 선택하기 위한 대화 상자를 엽니다" -#: src/view/screens/Settings/index.tsx:779 +#: src/view/screens/Settings/index.tsx:735 msgid "Opens modal for downloading your Bluesky account data (repository)" msgstr "Bluesky 계정 데이터(저장소)를 다운로드하기 위한 대화 상자를 엽니다" -#: src/view/screens/Settings/index.tsx:968 +#: src/view/screens/Settings/index.tsx:924 msgid "Opens modal for email verification" msgstr "이메일 인증을 위한 대화 상자를 엽니다" @@ -2983,7 +2978,7 @@ msgstr "이메일 인증을 위한 대화 상자를 엽니다" msgid "Opens modal for using custom domain" msgstr "사용자 지정 도메인을 사용하기 위한 대화 상자를 엽니다" -#: src/view/screens/Settings/index.tsx:640 +#: src/view/screens/Settings/index.tsx:583 msgid "Opens moderation settings" msgstr "검토 설정을 엽니다" @@ -2996,15 +2991,15 @@ msgstr "비밀번호 재설정 양식을 엽니다" msgid "Opens screen to edit Saved Feeds" msgstr "저장된 피드를 편집할 수 있는 화면을 엽니다" -#: src/view/screens/Settings/index.tsx:597 +#: src/view/screens/Settings/index.tsx:540 msgid "Opens screen with all saved feeds" msgstr "모든 저장된 피드 화면을 엽니다" -#: src/view/screens/Settings/index.tsx:696 +#: src/view/screens/Settings/index.tsx:639 msgid "Opens the app password settings" msgstr "비밀번호 설정을 엽니다" -#: src/view/screens/Settings/index.tsx:554 +#: src/view/screens/Settings/index.tsx:497 msgid "Opens the Following feed preferences" msgstr "팔로우 중 피드 설정을 엽니다" @@ -3012,16 +3007,16 @@ msgstr "팔로우 중 피드 설정을 엽니다" msgid "Opens the linked website" msgstr "연결된 웹사이트를 엽니다" -#: src/view/screens/Settings/index.tsx:829 -#: src/view/screens/Settings/index.tsx:839 +#: src/view/screens/Settings/index.tsx:785 +#: src/view/screens/Settings/index.tsx:795 msgid "Opens the storybook page" msgstr "스토리북 페이지를 엽니다" -#: src/view/screens/Settings/index.tsx:817 +#: src/view/screens/Settings/index.tsx:773 msgid "Opens the system log page" msgstr "시스템 로그 페이지를 엽니다" -#: src/view/screens/Settings/index.tsx:575 +#: src/view/screens/Settings/index.tsx:518 msgid "Opens the threads preferences" msgstr "스레드 설정을 엽니다" @@ -3029,7 +3024,7 @@ msgstr "스레드 설정을 엽니다" msgid "Option {0} of {numItems}" msgstr "{numItems}개 중 {0}번째 옵션" -#: src/components/ReportDialog/SubmitView.tsx:162 +#: src/components/ReportDialog/SubmitView.tsx:160 msgid "Optionally provide additional information below:" msgstr "선택 사항으로 아래에 추가 정보를 입력하세요:" @@ -3077,6 +3072,11 @@ msgstr "비밀번호 변경됨" msgid "Password updated!" msgstr "비밀번호 변경됨" +#: src/view/screens/Search/Search.tsx:390 +#: src/view/screens/Search/Search.tsx:399 +msgid "People" +msgstr "사람들" + #: src/Navigation.tsx:164 msgid "People followed by @{0}" msgstr "@{0} 님이 팔로우한 사람들" @@ -3101,16 +3101,16 @@ msgstr "반려동물" msgid "Pictures meant for adults." msgstr "성인용 사진." -#: src/view/screens/ProfileFeed.tsx:292 -#: src/view/screens/ProfileList.tsx:563 +#: src/view/screens/ProfileFeed.tsx:303 +#: src/view/screens/ProfileList.tsx:565 msgid "Pin to home" msgstr "홈에 고정" -#: src/view/screens/ProfileFeed.tsx:295 +#: src/view/screens/ProfileFeed.tsx:306 msgid "Pin to Home" msgstr "홈에 고정" -#: src/view/screens/SavedFeeds.tsx:88 +#: src/view/screens/SavedFeeds.tsx:89 msgid "Pinned Feeds" msgstr "고정된 피드" @@ -3183,10 +3183,6 @@ msgstr "정치" msgid "Porn" msgstr "음란물" -#: src/lib/moderation/useGlobalLabelStrings.ts:34 -#~ msgid "Pornography" -#~ msgstr "음란물" - #: src/view/com/composer/Composer.tsx:367 #: src/view/com/composer/Composer.tsx:375 msgctxt "action" @@ -3243,7 +3239,8 @@ msgstr "게시물을 찾을 수 없음" msgid "posts" msgstr "게시물" -#: src/view/screens/Profile.tsx:190 +#: src/view/screens/Profile.tsx:196 +#: src/view/screens/Search/Search.tsx:410 msgid "Posts" msgstr "게시물" @@ -3259,7 +3256,7 @@ msgstr "게시물 숨겨짐" msgid "Potentially Misleading Link" msgstr "오해의 소지가 있는 링크" -#: src/components/forms/HostingProvider.tsx:45 +#: src/components/forms/HostingProvider.tsx:46 msgid "Press to change hosting provider" msgstr "호스팅 제공자를 변경하려면 누릅니다" @@ -3281,7 +3278,7 @@ msgstr "주 언어" msgid "Prioritize Your Follows" msgstr "내 팔로우 먼저 표시" -#: src/view/screens/Settings/index.tsx:652 +#: src/view/screens/Settings/index.tsx:595 #: src/view/shell/desktop/RightNav.tsx:72 msgid "Privacy" msgstr "개인정보" @@ -3289,7 +3286,7 @@ msgstr "개인정보" #: src/Navigation.tsx:231 #: src/screens/Signup/StepInfo/Policies.tsx:56 #: src/view/screens/PrivacyPolicy.tsx:29 -#: src/view/screens/Settings/index.tsx:923 +#: src/view/screens/Settings/index.tsx:879 #: src/view/shell/Drawer.tsx:265 msgid "Privacy Policy" msgstr "개인정보 처리방침" @@ -3299,11 +3296,11 @@ msgid "Processing..." msgstr "처리 중…" #: src/view/screens/DebugMod.tsx:888 -#: src/view/screens/Profile.tsx:342 +#: src/view/screens/Profile.tsx:362 msgid "profile" msgstr "프로필" -#: src/view/shell/bottom-bar/BottomBar.tsx:260 +#: src/view/shell/bottom-bar/BottomBar.tsx:261 #: src/view/shell/desktop/LeftNav.tsx:419 #: src/view/shell/Drawer.tsx:70 #: src/view/shell/Drawer.tsx:549 @@ -3315,7 +3312,7 @@ msgstr "프로필" msgid "Profile updated" msgstr "프로필 업데이트됨" -#: src/view/screens/Settings/index.tsx:981 +#: src/view/screens/Settings/index.tsx:937 msgid "Protect your account by verifying your email." msgstr "이메일을 인증하여 계정을 보호하세요." @@ -3361,7 +3358,7 @@ msgstr "무작위" msgid "Ratios" msgstr "비율" -#: src/view/screens/Search/Search.tsx:777 +#: src/view/screens/Search/Search.tsx:867 msgid "Recent Searches" msgstr "최근 검색" @@ -3404,8 +3401,8 @@ msgstr "피드를 제거하시겠습니까?" #: src/view/com/feeds/FeedSourceCard.tsx:173 #: src/view/com/feeds/FeedSourceCard.tsx:233 -#: src/view/screens/ProfileFeed.tsx:335 -#: src/view/screens/ProfileFeed.tsx:341 +#: src/view/screens/ProfileFeed.tsx:346 +#: src/view/screens/ProfileFeed.tsx:352 msgid "Remove from my feeds" msgstr "내 피드에서 제거" @@ -3442,7 +3439,7 @@ msgstr "리스트에서 제거됨" msgid "Removed from my feeds" msgstr "내 피드에서 제거됨" -#: src/view/screens/ProfileFeed.tsx:209 +#: src/view/screens/ProfileFeed.tsx:210 msgid "Removed from your feeds" msgstr "내 피드에서 제거됨" @@ -3450,7 +3447,7 @@ msgstr "내 피드에서 제거됨" msgid "Removes default thumbnail from {0}" msgstr "{0}에서 기본 미리보기 이미지를 제거합니다" -#: src/view/screens/Profile.tsx:191 +#: src/view/screens/Profile.tsx:197 msgid "Replies" msgstr "답글" @@ -3482,12 +3479,12 @@ msgstr "계정 신고" msgid "Report dialog" msgstr "신고 대화 상자" -#: src/view/screens/ProfileFeed.tsx:352 -#: src/view/screens/ProfileFeed.tsx:354 +#: src/view/screens/ProfileFeed.tsx:363 +#: src/view/screens/ProfileFeed.tsx:365 msgid "Report feed" msgstr "피드 신고" -#: src/view/screens/ProfileList.tsx:429 +#: src/view/screens/ProfileList.tsx:431 msgid "Report List" msgstr "리스트 신고" @@ -3563,7 +3560,7 @@ msgstr "변경 요청" msgid "Request Code" msgstr "코드 요청" -#: src/view/screens/Settings/index.tsx:475 +#: src/view/screens/Settings/index.tsx:418 msgid "Require alt text before posting" msgstr "게시하기 전 대체 텍스트 필수" @@ -3579,8 +3576,8 @@ msgstr "재설정 코드" msgid "Reset Code" msgstr "재설정 코드" -#: src/view/screens/Settings/index.tsx:858 -#: src/view/screens/Settings/index.tsx:861 +#: src/view/screens/Settings/index.tsx:814 +#: src/view/screens/Settings/index.tsx:817 msgid "Reset onboarding state" msgstr "온보딩 상태 초기화" @@ -3588,16 +3585,16 @@ msgstr "온보딩 상태 초기화" msgid "Reset password" msgstr "비밀번호 재설정" -#: src/view/screens/Settings/index.tsx:848 -#: src/view/screens/Settings/index.tsx:851 +#: src/view/screens/Settings/index.tsx:804 +#: src/view/screens/Settings/index.tsx:807 msgid "Reset preferences state" msgstr "설정 상태 초기화" -#: src/view/screens/Settings/index.tsx:859 +#: src/view/screens/Settings/index.tsx:815 msgid "Resets the onboarding state" msgstr "온보딩 상태 초기화" -#: src/view/screens/Settings/index.tsx:849 +#: src/view/screens/Settings/index.tsx:805 msgid "Resets the preferences state" msgstr "설정 상태 초기화" @@ -3623,7 +3620,7 @@ msgid "Retry" msgstr "다시 시도" #: src/components/Error.tsx:86 -#: src/view/screens/ProfileList.tsx:917 +#: src/view/screens/ProfileList.tsx:919 msgid "Return to previous page" msgstr "이전 페이지로 돌아갑니다" @@ -3669,12 +3666,12 @@ msgstr "핸들 변경 저장" msgid "Save image crop" msgstr "이미지 자르기 저장" -#: src/view/screens/ProfileFeed.tsx:336 -#: src/view/screens/ProfileFeed.tsx:342 +#: src/view/screens/ProfileFeed.tsx:347 +#: src/view/screens/ProfileFeed.tsx:353 msgid "Save to my feeds" msgstr "내 피드에 저장" -#: src/view/screens/SavedFeeds.tsx:122 +#: src/view/screens/SavedFeeds.tsx:123 msgid "Saved Feeds" msgstr "저장된 피드" @@ -3682,7 +3679,7 @@ msgstr "저장된 피드" msgid "Saved to your camera roll." msgstr "내 앨범에 저장됨" -#: src/view/screens/ProfileFeed.tsx:213 +#: src/view/screens/ProfileFeed.tsx:214 msgid "Saved to your feeds" msgstr "내 피드에 저장됨" @@ -3702,7 +3699,7 @@ msgstr "이미지 자르기 설정을 저장합니다" msgid "Science" msgstr "과학" -#: src/view/screens/ProfileList.tsx:873 +#: src/view/screens/ProfileList.tsx:875 msgid "Scroll to top" msgstr "맨 위로 스크롤" @@ -3711,10 +3708,10 @@ msgstr "맨 위로 스크롤" #: src/view/com/modals/ListAddRemoveUsers.tsx:75 #: src/view/com/util/forms/SearchInput.tsx:67 #: src/view/com/util/forms/SearchInput.tsx:79 -#: src/view/screens/Search/Search.tsx:421 -#: src/view/screens/Search/Search.tsx:670 -#: src/view/screens/Search/Search.tsx:688 -#: src/view/shell/bottom-bar/BottomBar.tsx:169 +#: src/view/screens/Search/Search.tsx:511 +#: src/view/screens/Search/Search.tsx:760 +#: src/view/screens/Search/Search.tsx:778 +#: src/view/shell/bottom-bar/BottomBar.tsx:170 #: src/view/shell/desktop/LeftNav.tsx:328 #: src/view/shell/desktop/Search.tsx:215 #: src/view/shell/desktop/Search.tsx:224 @@ -3723,7 +3720,7 @@ msgstr "맨 위로 스크롤" msgid "Search" msgstr "검색" -#: src/view/screens/Search/Search.tsx:737 +#: src/view/screens/Search/Search.tsx:827 #: src/view/shell/desktop/Search.tsx:256 msgid "Search for \"{query}\"" msgstr "\"{query}\"에 대한 검색 결과" @@ -3762,7 +3759,7 @@ msgstr "<0>{displayTag} 게시물 보기" msgid "See <0>{displayTag} posts by this user" msgstr "이 사용자의 <0>{displayTag} 게시물 보기" -#: src/view/screens/SavedFeeds.tsx:163 +#: src/view/screens/SavedFeeds.tsx:164 msgid "See this guide" msgstr "이 가이드" @@ -3798,7 +3795,7 @@ msgstr "{numItems}개 중 {i}번째 옵션을 선택합니다" msgid "Select some accounts below to follow" msgstr "아래에서 팔로우할 계정을 선택하세요" -#: src/components/ReportDialog/SubmitView.tsx:135 +#: src/components/ReportDialog/SubmitView.tsx:133 msgid "Select the moderation service(s) to report to" msgstr "신고할 검토 서비스를 선택하세요." @@ -3861,8 +3858,8 @@ msgstr "이메일 보내기" msgid "Send feedback" msgstr "피드백 보내기" -#: src/components/ReportDialog/SubmitView.tsx:214 -#: src/components/ReportDialog/SubmitView.tsx:218 +#: src/components/ReportDialog/SubmitView.tsx:213 +#: src/components/ReportDialog/SubmitView.tsx:217 msgid "Send report" msgstr "신고 보내기" @@ -3914,23 +3911,23 @@ msgstr "계정 설정하기" msgid "Sets Bluesky username" msgstr "Bluesky 사용자 이름을 설정합니다" -#: src/view/screens/Settings/index.tsx:507 +#: src/view/screens/Settings/index.tsx:450 msgid "Sets color theme to dark" msgstr "색상 테마를 어두움으로 설정합니다" -#: src/view/screens/Settings/index.tsx:500 +#: src/view/screens/Settings/index.tsx:443 msgid "Sets color theme to light" msgstr "색상 테마를 밝음으로 설정합니다" -#: src/view/screens/Settings/index.tsx:494 +#: src/view/screens/Settings/index.tsx:437 msgid "Sets color theme to system setting" msgstr "색상 테마를 시스템 설정에 맞춥니다" -#: src/view/screens/Settings/index.tsx:533 +#: src/view/screens/Settings/index.tsx:476 msgid "Sets dark theme to the dark theme" msgstr "어두운 테마를 완전히 어둡게 설정합니다" -#: src/view/screens/Settings/index.tsx:526 +#: src/view/screens/Settings/index.tsx:469 msgid "Sets dark theme to the dim theme" msgstr "어두운 테마를 살짝 밝게 설정합니다" @@ -3951,7 +3948,7 @@ msgid "Sets image aspect ratio to wide" msgstr "이미지 비율을 가로로 길게 설정합니다" #: src/Navigation.tsx:139 -#: src/view/screens/Settings/index.tsx:313 +#: src/view/screens/Settings/index.tsx:308 #: src/view/shell/desktop/LeftNav.tsx:437 #: src/view/shell/Drawer.tsx:570 #: src/view/shell/Drawer.tsx:571 @@ -3975,36 +3972,36 @@ msgstr "공유" #: src/view/com/profile/ProfileMenu.tsx:224 #: src/view/com/util/forms/PostDropdownBtn.tsx:228 #: src/view/com/util/forms/PostDropdownBtn.tsx:237 -#: src/view/com/util/post-ctrls/PostCtrls.tsx:235 -#: src/view/screens/ProfileList.tsx:388 +#: src/view/com/util/post-ctrls/PostCtrls.tsx:237 +#: src/view/screens/ProfileList.tsx:390 msgid "Share" msgstr "공유" #: src/view/com/profile/ProfileMenu.tsx:373 #: src/view/com/util/forms/PostDropdownBtn.tsx:347 -#: src/view/com/util/post-ctrls/PostCtrls.tsx:251 +#: src/view/com/util/post-ctrls/PostCtrls.tsx:253 msgid "Share anyway" msgstr "무시하고 공유" -#: src/view/screens/ProfileFeed.tsx:362 -#: src/view/screens/ProfileFeed.tsx:364 +#: src/view/screens/ProfileFeed.tsx:373 +#: src/view/screens/ProfileFeed.tsx:375 msgid "Share feed" msgstr "피드 공유" #: src/view/com/modals/LinkWarning.tsx:89 #: src/view/com/modals/LinkWarning.tsx:95 msgid "Share Link" -msgstr "" +msgstr "링크 공유" #: src/view/com/modals/LinkWarning.tsx:92 msgid "Shares the linked website" -msgstr "" +msgstr "연결된 웹사이트를 공유합니다" #: src/components/moderation/ContentHider.tsx:115 #: src/components/moderation/LabelPreference.tsx:136 #: src/components/moderation/PostHider.tsx:107 #: src/screens/Onboarding/StepModeration/ModerationOption.tsx:54 -#: src/view/screens/Settings/index.tsx:363 +#: src/view/screens/Settings/index.tsx:358 msgid "Show" msgstr "표시" @@ -4026,10 +4023,6 @@ msgstr "배지 표시" msgid "Show badge and filter from feeds" msgstr "배지 표시 및 피드에서 필터링" -#: src/view/com/modals/EmbedConsent.tsx:87 -#~ msgid "Show embeds from {0}" -#~ msgstr "{0} 임베드 표시" - #: src/screens/Profile/Header/ProfileHeaderStandard.tsx:200 msgid "Show follows similar to {0}" msgstr "{0} 님과 비슷한 팔로우 표시" @@ -4118,9 +4111,9 @@ msgstr "피드에 {0} 님의 게시물을 표시합니다" #: src/view/com/auth/SplashScreen.tsx:90 #: src/view/com/auth/SplashScreen.web.tsx:110 #: src/view/com/auth/SplashScreen.web.tsx:119 -#: src/view/shell/bottom-bar/BottomBar.tsx:300 #: src/view/shell/bottom-bar/BottomBar.tsx:301 -#: src/view/shell/bottom-bar/BottomBar.tsx:303 +#: src/view/shell/bottom-bar/BottomBar.tsx:302 +#: src/view/shell/bottom-bar/BottomBar.tsx:304 #: src/view/shell/bottom-bar/BottomBarWeb.tsx:178 #: src/view/shell/bottom-bar/BottomBarWeb.tsx:179 #: src/view/shell/bottom-bar/BottomBarWeb.tsx:181 @@ -4130,12 +4123,6 @@ msgstr "피드에 {0} 님의 게시물을 표시합니다" msgid "Sign in" msgstr "로그인" -#: src/view/com/auth/HomeLoggedOutCTA.tsx:82 -#: src/view/com/auth/SplashScreen.tsx:90 -#: src/view/com/auth/SplashScreen.web.tsx:118 -#~ msgid "Sign In" -#~ msgstr "로그인" - #: src/components/AccountList.tsx:109 msgid "Sign in as {0}" msgstr "{0}(으)로 로그인" @@ -4144,14 +4131,14 @@ msgstr "{0}(으)로 로그인" msgid "Sign in as..." msgstr "로그인" -#: src/view/screens/Settings/index.tsx:107 #: src/view/screens/Settings/index.tsx:110 +#: src/view/screens/Settings/index.tsx:113 msgid "Sign out" msgstr "로그아웃" -#: src/view/shell/bottom-bar/BottomBar.tsx:290 #: src/view/shell/bottom-bar/BottomBar.tsx:291 -#: src/view/shell/bottom-bar/BottomBar.tsx:293 +#: src/view/shell/bottom-bar/BottomBar.tsx:292 +#: src/view/shell/bottom-bar/BottomBar.tsx:294 #: src/view/shell/bottom-bar/BottomBarWeb.tsx:168 #: src/view/shell/bottom-bar/BottomBarWeb.tsx:169 #: src/view/shell/bottom-bar/BottomBarWeb.tsx:171 @@ -4170,7 +4157,7 @@ msgstr "가입 또는 로그인하여 대화에 참여하세요" msgid "Sign-in Required" msgstr "로그인 필요" -#: src/view/screens/Settings/index.tsx:374 +#: src/view/screens/Settings/index.tsx:369 msgid "Signed in as" msgstr "로그인한 계정" @@ -4178,10 +4165,6 @@ msgstr "로그인한 계정" msgid "Signed in as @{0}" msgstr "@{0}(으)로 로그인했습니다" -#: src/view/com/modals/SwitchAccount.tsx:71 -#~ msgid "Signs {0} out of Bluesky" -#~ msgstr "Bluesky에서 {0}을(를) 로그아웃합니다" - #: src/screens/Onboarding/StepInterests/index.tsx:239 #: src/screens/Onboarding/StepSuggestedAccounts/index.tsx:203 #: src/view/com/auth/onboarding/WelcomeMobile.tsx:35 @@ -4198,7 +4181,7 @@ msgstr "소프트웨어 개발" #: src/components/ReportDialog/index.tsx:59 #: src/screens/Moderation/index.tsx:114 -#: src/screens/Profile/Sections/Labels.tsx:76 +#: src/screens/Profile/Sections/Labels.tsx:87 msgid "Something went wrong, please try again." msgstr "뭔가 잘못되었습니다. 다시 시도해 주세요." @@ -4234,7 +4217,7 @@ msgstr "스포츠" msgid "Square" msgstr "정사각형" -#: src/view/screens/Settings/index.tsx:903 +#: src/view/screens/Settings/index.tsx:859 msgid "Status page" msgstr "상태 페이지" @@ -4242,12 +4225,12 @@ msgstr "상태 페이지" msgid "Step" msgstr "" -#: src/view/screens/Settings/index.tsx:292 +#: src/view/screens/Settings/index.tsx:287 msgid "Storage cleared, you need to restart the app now." msgstr "스토리지가 지워졌으며 지금 앱을 다시 시작해야 합니다." #: src/Navigation.tsx:211 -#: src/view/screens/Settings/index.tsx:831 +#: src/view/screens/Settings/index.tsx:787 msgid "Storybook" msgstr "스토리북" @@ -4256,15 +4239,15 @@ msgstr "스토리북" msgid "Submit" msgstr "확인" -#: src/view/screens/ProfileList.tsx:590 +#: src/view/screens/ProfileList.tsx:592 msgid "Subscribe" msgstr "구독" -#: src/screens/Profile/Sections/Labels.tsx:180 +#: src/screens/Profile/Sections/Labels.tsx:191 msgid "Subscribe to @{0} to use these labels:" msgstr "이 라벨을 사용하려면 @{0} 님을 구독하세요:" -#: src/screens/Profile/Header/ProfileHeaderLabeler.tsx:221 +#: src/screens/Profile/Header/ProfileHeaderLabeler.tsx:222 msgid "Subscribe to Labeler" msgstr "라벨러 구독" @@ -4273,15 +4256,15 @@ msgstr "라벨러 구독" msgid "Subscribe to the {0} feed" msgstr "{0} 피드 구독하기" -#: src/screens/Profile/Header/ProfileHeaderLabeler.tsx:184 +#: src/screens/Profile/Header/ProfileHeaderLabeler.tsx:185 msgid "Subscribe to this labeler" msgstr "이 라벨러 구독하기" -#: src/view/screens/ProfileList.tsx:586 +#: src/view/screens/ProfileList.tsx:588 msgid "Subscribe to this list" msgstr "이 리스트 구독하기" -#: src/view/screens/Search/Search.tsx:376 +#: src/view/screens/Search/Search.tsx:466 msgid "Suggested Follows" msgstr "팔로우 추천" @@ -4304,19 +4287,19 @@ msgstr "지원" msgid "Switch Account" msgstr "계정 전환" -#: src/view/screens/Settings/index.tsx:139 +#: src/view/screens/Settings/index.tsx:142 msgid "Switch to {0}" msgstr "{0}(으)로 전환" -#: src/view/screens/Settings/index.tsx:140 +#: src/view/screens/Settings/index.tsx:143 msgid "Switches the account you are logged in to" msgstr "로그인한 계정을 전환합니다" -#: src/view/screens/Settings/index.tsx:491 +#: src/view/screens/Settings/index.tsx:434 msgid "System" msgstr "시스템" -#: src/view/screens/Settings/index.tsx:819 +#: src/view/screens/Settings/index.tsx:775 msgid "System log" msgstr "시스템 로그" @@ -4346,7 +4329,7 @@ msgstr "이용약관" #: src/Navigation.tsx:236 #: src/screens/Signup/StepInfo/Policies.tsx:49 -#: src/view/screens/Settings/index.tsx:917 +#: src/view/screens/Settings/index.tsx:873 #: src/view/screens/TermsOfService.tsx:29 #: src/view/shell/Drawer.tsx:259 msgid "Terms of Service" @@ -4366,7 +4349,7 @@ msgstr "글" msgid "Text input field" msgstr "텍스트 입력 필드" -#: src/components/ReportDialog/SubmitView.tsx:78 +#: src/components/ReportDialog/SubmitView.tsx:76 msgid "Thank you. Your report has been sent." msgstr "감사합니다. 신고를 전송했습니다." @@ -4428,8 +4411,8 @@ msgstr "서비스 이용약관을 다음으로 이동했습니다:" msgid "There are many feeds to try:" msgstr "시도해 볼 만한 피드:" -#: src/screens/Profile/Header/ProfileHeaderLabeler.tsx:112 -#: src/view/screens/ProfileFeed.tsx:544 +#: src/screens/Profile/Header/ProfileHeaderLabeler.tsx:113 +#: src/view/screens/ProfileFeed.tsx:556 msgid "There was an an issue contacting the server, please check your internet connection and try again." msgstr "서버에 연결하는 동안 문제가 발생했습니다. 인터넷 연결을 확인한 후 다시 시도하세요." @@ -4437,15 +4420,15 @@ msgstr "서버에 연결하는 동안 문제가 발생했습니다. 인터넷 msgid "There was an an issue removing this feed. Please check your internet connection and try again." msgstr "이 피드를 삭제하는 동안 문제가 발생했습니다. 인터넷 연결을 확인한 후 다시 시도하세요." -#: src/view/screens/ProfileFeed.tsx:218 +#: src/view/screens/ProfileFeed.tsx:219 msgid "There was an an issue updating your feeds, please check your internet connection and try again." msgstr "피드를 업데이트하는 동안 문제가 발생했습니다. 인터넷 연결을 확인한 후 다시 시도하세요." -#: src/view/screens/ProfileFeed.tsx:245 -#: src/view/screens/ProfileList.tsx:275 -#: src/view/screens/SavedFeeds.tsx:209 -#: src/view/screens/SavedFeeds.tsx:231 -#: src/view/screens/SavedFeeds.tsx:252 +#: src/view/screens/ProfileFeed.tsx:247 +#: src/view/screens/ProfileList.tsx:277 +#: src/view/screens/SavedFeeds.tsx:211 +#: src/view/screens/SavedFeeds.tsx:241 +#: src/view/screens/SavedFeeds.tsx:262 msgid "There was an issue contacting the server" msgstr "서버에 연결하는 동안 문제가 발생했습니다" @@ -4468,12 +4451,12 @@ msgstr "게시물을 가져오는 동안 문제가 발생했습니다. 이곳을 msgid "There was an issue fetching the list. Tap here to try again." msgstr "리스트를 가져오는 동안 문제가 발생했습니다. 이곳을 탭하여 다시 시도하세요." -#: src/view/com/feeds/ProfileFeedgens.tsx:148 -#: src/view/com/lists/ProfileLists.tsx:155 +#: src/view/com/feeds/ProfileFeedgens.tsx:156 +#: src/view/com/lists/ProfileLists.tsx:163 msgid "There was an issue fetching your lists. Tap here to try again." msgstr "리스트를 가져오는 동안 문제가 발생했습니다. 이곳을 탭하여 다시 시도하세요." -#: src/components/ReportDialog/SubmitView.tsx:83 +#: src/components/ReportDialog/SubmitView.tsx:81 msgid "There was an issue sending your report. Please check your internet connection." msgstr "신고를 전송하는 동안 문제가 발생했습니다. 인터넷 연결을 확인해 주세요." @@ -4499,10 +4482,10 @@ msgstr "앱 비밀번호를 가져오는 동안 문제가 발생했습니다" msgid "There was an issue! {0}" msgstr "문제가 발생했습니다! {0}" -#: src/view/screens/ProfileList.tsx:288 -#: src/view/screens/ProfileList.tsx:302 -#: src/view/screens/ProfileList.tsx:316 -#: src/view/screens/ProfileList.tsx:330 +#: src/view/screens/ProfileList.tsx:290 +#: src/view/screens/ProfileList.tsx:304 +#: src/view/screens/ProfileList.tsx:318 +#: src/view/screens/ProfileList.tsx:332 msgid "There was an issue. Please check your internet connection and try again." msgstr "문제가 발생했습니다. 인터넷 연결을 확인한 후 다시 시도하세요." @@ -4559,9 +4542,9 @@ msgstr "이 기능은 베타 버전입니다. 저장소 내보내기에 대한 msgid "This feed is currently receiving high traffic and is temporarily unavailable. Please try again later." msgstr "이 피드는 현재 트래픽이 많아 일시적으로 사용할 수 없습니다. 나중에 다시 시도해 주세요." -#: src/screens/Profile/Sections/Feed.tsx:50 -#: src/view/screens/ProfileFeed.tsx:477 -#: src/view/screens/ProfileList.tsx:675 +#: src/screens/Profile/Sections/Feed.tsx:59 +#: src/view/screens/ProfileFeed.tsx:488 +#: src/view/screens/ProfileList.tsx:677 msgid "This feed is empty!" msgstr "이 피드는 비어 있습니다." @@ -4581,7 +4564,7 @@ msgstr "이는 이메일을 변경하거나 비밀번호를 재설정해야 할 msgid "This label was applied by {0}." msgstr "이 라벨은 {0}이(가) 적용했습니다." -#: src/screens/Profile/Sections/Labels.tsx:167 +#: src/screens/Profile/Sections/Labels.tsx:178 msgid "This labeler hasn't declared what labels it publishes, and may not be active." msgstr "이 라벨러는 라벨을 게시하지 않았으며 활성화되어 있지 않을 수 있습니다." @@ -4589,7 +4572,7 @@ msgstr "이 라벨러는 라벨을 게시하지 않았으며 활성화되어 있 msgid "This link is taking you to the following website:" msgstr "이 링크를 클릭하면 다음 웹사이트로 이동합니다:" -#: src/view/screens/ProfileList.tsx:853 +#: src/view/screens/ProfileList.tsx:855 msgid "This list is empty!" msgstr "이 리스트는 비어 있습니다." @@ -4606,7 +4589,7 @@ msgid "This post has been deleted." msgstr "이 게시물은 삭제되었습니다." #: src/view/com/util/forms/PostDropdownBtn.tsx:344 -#: src/view/com/util/post-ctrls/PostCtrls.tsx:248 +#: src/view/com/util/post-ctrls/PostCtrls.tsx:250 msgid "This post is only visible to logged-in users. It won't be visible to people who aren't logged in." msgstr "이 게시물은 로그인한 사용자에게만 표시됩니다. 로그인하지 않은 사용자에게는 표시되지 않습니다." @@ -4659,12 +4642,12 @@ msgstr "이 경고는 미디어가 첨부된 게시물에만 사용할 수 있 msgid "This will delete {0} from your muted words. You can always add it back later." msgstr "뮤트한 단어에서 {0}이(가) 삭제됩니다. 나중에 언제든지 다시 추가할 수 있습니다." -#: src/view/screens/Settings/index.tsx:574 +#: src/view/screens/Settings/index.tsx:517 msgid "Thread preferences" msgstr "스레드 설정" #: src/view/screens/PreferencesThreads.tsx:53 -#: src/view/screens/Settings/index.tsx:584 +#: src/view/screens/Settings/index.tsx:527 msgid "Thread Preferences" msgstr "스레드 설정" @@ -4692,6 +4675,10 @@ msgstr "드롭다운 열기 및 닫기" msgid "Toggle to enable or disable adult content" msgstr "성인 콘텐츠 활성화 또는 비활성화 전환" +#: src/view/screens/Search/Search.tsx:370 +msgid "Top" +msgstr "인기" + #: src/view/com/modals/EditImage.tsx:272 msgid "Transformations" msgstr "변형" @@ -4712,11 +4699,11 @@ msgstr "다시 시도" msgid "Type:" msgstr "유형:" -#: src/view/screens/ProfileList.tsx:478 +#: src/view/screens/ProfileList.tsx:480 msgid "Un-block list" msgstr "리스트 차단 해제" -#: src/view/screens/ProfileList.tsx:461 +#: src/view/screens/ProfileList.tsx:463 msgid "Un-mute list" msgstr "리스트 언뮤트" @@ -4732,7 +4719,7 @@ msgstr "서비스에 연결할 수 없습니다. 인터넷 연결을 확인하 #: src/screens/Profile/Header/ProfileHeaderStandard.tsx:181 #: src/screens/Profile/Header/ProfileHeaderStandard.tsx:287 #: src/view/com/profile/ProfileMenu.tsx:361 -#: src/view/screens/ProfileList.tsx:572 +#: src/view/screens/ProfileList.tsx:574 msgid "Unblock" msgstr "차단 해제" @@ -4777,16 +4764,16 @@ msgstr "{0} 님을 언팔로우" msgid "Unfollow Account" msgstr "계정 언팔로우" -#: src/view/com/util/post-ctrls/PostCtrls.tsx:195 +#: src/view/com/util/post-ctrls/PostCtrls.tsx:197 msgid "Unlike" msgstr "좋아요 취소" -#: src/view/screens/ProfileFeed.tsx:573 +#: src/view/screens/ProfileFeed.tsx:585 msgid "Unlike this feed" msgstr "이 피드 좋아요 취소" #: src/components/TagMenu/index.tsx:249 -#: src/view/screens/ProfileList.tsx:579 +#: src/view/screens/ProfileList.tsx:581 msgid "Unmute" msgstr "언뮤트" @@ -4808,24 +4795,24 @@ msgstr "모든 {tag} 게시물 언뮤트" msgid "Unmute thread" msgstr "스레드 언뮤트" -#: src/view/screens/ProfileFeed.tsx:295 -#: src/view/screens/ProfileList.tsx:563 +#: src/view/screens/ProfileFeed.tsx:306 +#: src/view/screens/ProfileList.tsx:565 msgid "Unpin" msgstr "고정 해제" -#: src/view/screens/ProfileFeed.tsx:292 +#: src/view/screens/ProfileFeed.tsx:303 msgid "Unpin from home" msgstr "홈에서 고정 해제" -#: src/view/screens/ProfileList.tsx:444 +#: src/view/screens/ProfileList.tsx:446 msgid "Unpin moderation list" msgstr "검토 리스트 고정 해제" -#: src/screens/Profile/Header/ProfileHeaderLabeler.tsx:219 +#: src/screens/Profile/Header/ProfileHeaderLabeler.tsx:220 msgid "Unsubscribe" msgstr "구독 취소" -#: src/screens/Profile/Header/ProfileHeaderLabeler.tsx:183 +#: src/screens/Profile/Header/ProfileHeaderLabeler.tsx:184 msgid "Unsubscribe from this labeler" msgstr "이 라벨러 구독 취소하기" @@ -4932,13 +4919,13 @@ msgstr "나를 차단한 사용자" msgid "User list by {0}" msgstr "{0} 님의 사용자 리스트" -#: src/view/screens/ProfileList.tsx:777 +#: src/view/screens/ProfileList.tsx:779 msgid "User list by <0/>" msgstr "<0/> 님의 사용자 리스트" #: src/view/com/lists/ListCard.tsx:83 #: src/view/com/modals/UserAddRemoveLists.tsx:196 -#: src/view/screens/ProfileList.tsx:775 +#: src/view/screens/ProfileList.tsx:777 msgid "User list by you" msgstr "내 사용자 리스트" @@ -4958,7 +4945,9 @@ msgstr "사용자 리스트" msgid "Username or email address" msgstr "사용자 이름 또는 이메일 주소" -#: src/view/screens/ProfileList.tsx:811 +#: src/view/screens/ProfileList.tsx:813 +#: src/view/screens/Search/Search.tsx:416 +#: src/view/screens/Search/Search.tsx:425 msgid "Users" msgstr "사용자" @@ -4982,15 +4971,15 @@ msgstr "값:" msgid "Verify {0}" msgstr "{0} 확인" -#: src/view/screens/Settings/index.tsx:942 +#: src/view/screens/Settings/index.tsx:898 msgid "Verify email" msgstr "이메일 인증" -#: src/view/screens/Settings/index.tsx:967 +#: src/view/screens/Settings/index.tsx:923 msgid "Verify my email" msgstr "내 이메일 인증하기" -#: src/view/screens/Settings/index.tsx:976 +#: src/view/screens/Settings/index.tsx:932 msgid "Verify My Email" msgstr "내 이메일 인증하기" @@ -5003,7 +4992,7 @@ msgstr "새 이메일 인증" msgid "Verify Your Email" msgstr "이메일 인증하기" -#: src/view/screens/Settings/index.tsx:893 +#: src/view/screens/Settings/index.tsx:849 msgid "Version {0}" msgstr "버전 {0}" @@ -5019,11 +5008,11 @@ msgstr "{0} 님의 아바타를 봅니다" msgid "View debug entry" msgstr "디버그 항목 보기" -#: src/components/ReportDialog/SelectReportOptionView.tsx:131 +#: src/components/ReportDialog/SelectReportOptionView.tsx:132 msgid "View details" msgstr "세부 정보 보기" -#: src/components/ReportDialog/SelectReportOptionView.tsx:126 +#: src/components/ReportDialog/SelectReportOptionView.tsx:127 msgid "View details for reporting a copyright violation" msgstr "저작권 위반 신고에 대한 세부 정보 보기" @@ -5047,7 +5036,7 @@ msgstr "아바타 보기" msgid "View the labeling service provided by @{0}" msgstr "{0} 님이 제공하는 라벨링 서비스 보기" -#: src/view/screens/ProfileFeed.tsx:585 +#: src/view/screens/ProfileFeed.tsx:597 msgid "View users who like this feed" msgstr "이 피드를 좋아하는 사용자 보기" @@ -5071,10 +5060,6 @@ msgstr "콘텐츠 경고" msgid "Warn content and filter from feeds" msgstr "콘텐츠 경고 및 피드에서 필터링" -#: src/screens/Onboarding/StepAlgoFeeds/index.tsx:140 -#~ msgid "We also think you'll like \"For You\" by Skygaze:" -#~ msgstr "Skygaze의 \"For You\"를 사용해 볼 수도 있습니다:" - #: src/screens/Hashtag.tsx:133 msgid "We couldn't find any results for that hashtag." msgstr "해당 해시태그에 대한 결과를 찾을 수 없습니다." @@ -5123,7 +5108,7 @@ msgstr "이를 통해 사용자 환경을 맞춤 설정할 수 있습니다." msgid "We're so excited to have you join us!" msgstr "함께하게 되어 정말 기뻐요!" -#: src/view/screens/ProfileList.tsx:89 +#: src/view/screens/ProfileList.tsx:90 msgid "We're sorry, but we were unable to resolve this list. If this persists, please contact the list creator, @{handleOrDid}." msgstr "죄송하지만 이 리스트를 불러올 수 없습니다. 이 문제가 계속되면 리스트 작성자인 @{handleOrDid}에게 문의하세요." @@ -5131,7 +5116,7 @@ msgstr "죄송하지만 이 리스트를 불러올 수 없습니다. 이 문제 msgid "We're sorry, but we weren't able to load your muted words at this time. Please try again." msgstr "죄송하지만 현재 뮤트한 단어를 불러올 수 없습니다. 다시 시도해 주세요." -#: src/view/screens/Search/Search.tsx:256 +#: src/view/screens/Search/Search.tsx:265 msgid "We're sorry, but your search could not be completed. Please try again in a few minutes." msgstr "죄송하지만 검색을 완료할 수 없습니다. 몇 분 후에 다시 시도해 주세요." @@ -5140,7 +5125,7 @@ msgstr "죄송하지만 검색을 완료할 수 없습니다. 몇 분 후에 다 msgid "We're sorry! We can't find the page you were looking for." msgstr "죄송합니다. 페이지를 찾을 수 없습니다." -#: src/screens/Profile/Header/ProfileHeaderLabeler.tsx:321 +#: src/screens/Profile/Header/ProfileHeaderLabeler.tsx:322 msgid "We're sorry! You can only subscribe to ten labelers, and you've reached your limit of ten." msgstr "죄송합니다. 라벨러는 10개까지만 구독할 수 있으며 10개에 도달했습니다." @@ -5248,7 +5233,7 @@ msgstr "팔로워가 없습니다." msgid "You don't have any invite codes yet! We'll send you some when you've been on Bluesky for a little longer." msgstr "아직 초대 코드가 없습니다! Bluesky를 좀 더 오래 사용하신 후에 보내드리겠습니다." -#: src/view/screens/SavedFeeds.tsx:102 +#: src/view/screens/SavedFeeds.tsx:103 msgid "You don't have any pinned feeds." msgstr "고정된 피드가 없습니다." @@ -5256,7 +5241,7 @@ msgstr "고정된 피드가 없습니다." msgid "You don't have any saved feeds!" msgstr "저장된 피드가 없습니다!" -#: src/view/screens/SavedFeeds.tsx:135 +#: src/view/screens/SavedFeeds.tsx:136 msgid "You don't have any saved feeds." msgstr "저장된 피드가 없습니다." @@ -5294,12 +5279,12 @@ msgstr "내가 이 계정을 뮤트했습니다." msgid "You have muted this user" msgstr "내가 이 사용자를 뮤트했습니다" -#: src/view/com/feeds/ProfileFeedgens.tsx:136 +#: src/view/com/feeds/ProfileFeedgens.tsx:144 msgid "You have no feeds." msgstr "피드가 없습니다." #: src/view/com/lists/MyLists.tsx:89 -#: src/view/com/lists/ProfileLists.tsx:140 +#: src/view/com/lists/ProfileLists.tsx:148 msgid "You have no lists." msgstr "리스트가 없습니다." @@ -5331,7 +5316,7 @@ msgstr "가입하려면 만 13세 이상이어야 합니다." msgid "You must be 18 years or older to enable adult content" msgstr "성인 콘텐츠를 사용하려면 만 18세 이상이어야 합니다." -#: src/components/ReportDialog/SubmitView.tsx:205 +#: src/components/ReportDialog/SubmitView.tsx:203 msgid "You must select at least one labeler for a report" msgstr "신고하려면 하나 이상의 라벨을 선택해야 합니다." @@ -5438,7 +5423,7 @@ msgstr "게시물을 게시했습니다" msgid "Your posts, likes, and blocks are public. Mutes are private." msgstr "게시물, 좋아요, 차단 목록은 공개됩니다. 뮤트 목록은 공개되지 않습니다." -#: src/view/screens/Settings/index.tsx:125 +#: src/view/screens/Settings/index.tsx:128 msgid "Your profile" msgstr "내 프로필" From a306fbfca343f5729119f716dd8dbcbed3225b67 Mon Sep 17 00:00:00 2001 From: Frudrax Cheng Date: Sat, 13 Apr 2024 05:46:15 +0800 Subject: [PATCH 019/167] Update zh-TW Localization (#3478) * Update messages.po * Fix typo * Update messages.po * Remove superseded strings --- src/locale/locales/zh-TW/messages.po | 1264 ++++++-------------------- 1 file changed, 300 insertions(+), 964 deletions(-) diff --git a/src/locale/locales/zh-TW/messages.po b/src/locale/locales/zh-TW/messages.po index 484428079f..da6b060b37 100644 --- a/src/locale/locales/zh-TW/messages.po +++ b/src/locale/locales/zh-TW/messages.po @@ -1,6 +1,6 @@ msgid "" msgstr "" -"POT-Creation-Date: 2024-03-20 15:50+0800\n" +"POT-Creation-Date: 2024-04-12 11:00+0800\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" @@ -17,28 +17,10 @@ msgstr "" msgid "(no email)" msgstr "(沒有郵件)" -#: src/view/shell/desktop/RightNav.tsx:168 -#~ msgid "{0, plural, one {# invite code available} other {# invite codes available}}" -#~ msgstr "{0} 個可用的邀請碼" - #: src/screens/Profile/Header/Metrics.tsx:44 msgid "{following} following" msgstr "{following} 個跟隨中" -#: src/view/shell/desktop/RightNav.tsx:151 -#~ msgid "{invitesAvailable, plural, one {Invite codes: # available} other {Invite codes: # available}}" -#~ msgstr "可用的邀請碼:{invitesAvailable} 個" - -#: src/view/screens/Settings.tsx:435 -#: src/view/shell/Drawer.tsx:664 -#~ msgid "{invitesAvailable} invite code available" -#~ msgstr "{invitesAvailable} 個可用的邀請碼" - -#: src/view/screens/Settings.tsx:437 -#: src/view/shell/Drawer.tsx:666 -#~ msgid "{invitesAvailable} invite codes available" -#~ msgstr "{invitesAvailable} 個可用的邀請碼" - #: src/view/shell/Drawer.tsx:443 msgid "{numUnreadNotifications} unread" msgstr "{numUnreadNotifications} 個未讀" @@ -49,7 +31,7 @@ msgstr "<0/> 個成員" #: src/view/shell/Drawer.tsx:97 msgid "<0>{0} following" -msgstr "" +msgstr "<0>{0} 個跟隨中" #: src/screens/Profile/Header/Metrics.tsx:45 msgid "<0>{following} <1>following" @@ -71,14 +53,6 @@ msgstr "<0>歡迎來到<1>Bluesky" msgid "⚠Invalid Handle" msgstr "⚠無效的帳號代碼" -#: src/view/com/util/moderation/LabelInfo.tsx:45 -#~ msgid "A content warning has been applied to this {0}." -#~ msgstr "內容警告已套用到這個{0}。" - -#: src/lib/hooks/useOTAUpdate.ts:16 -#~ msgid "A new version of the app is available. Please update to continue using the app." -#~ msgstr "新版本應用程式已發佈,請更新以繼續使用。" - #: src/view/com/util/ViewHeader.tsx:89 #: src/view/screens/Search/Search.tsx:649 msgid "Access navigation links and settings" @@ -179,15 +153,6 @@ msgstr "新增替代文字" msgid "Add App Password" msgstr "新增應用程式專用密碼" -#: src/view/com/modals/report/InputIssueDetails.tsx:41 -#: src/view/com/modals/report/Modal.tsx:191 -#~ msgid "Add details" -#~ msgstr "新增細節" - -#: src/view/com/modals/report/Modal.tsx:194 -#~ msgid "Add details to report" -#~ msgstr "補充回報詳細內容" - #: src/view/com/composer/Composer.tsx:467 msgid "Add link card" msgstr "新增連結卡片" @@ -198,11 +163,11 @@ msgstr "新增連結卡片:" #: src/components/dialogs/MutedWords.tsx:157 msgid "Add mute word for configured settings" -msgstr "" +msgstr "在設定中新增靜音字詞" #: src/components/dialogs/MutedWords.tsx:86 msgid "Add muted words and tags" -msgstr "" +msgstr "新增靜音字詞及標籤" #: src/view/com/modals/ChangeHandle.tsx:416 msgid "Add the following DNS record to your domain:" @@ -240,13 +205,9 @@ msgstr "調整回覆要在你的訊息流顯示所需的最低喜歡數。" msgid "Adult Content" msgstr "成人內容" -#: src/view/com/modals/ContentFilteringSettings.tsx:141 -#~ msgid "Adult content can only be enabled via the Web at <0/>." -#~ msgstr "成人內容只能在網頁上<0/>啟用。" - #: src/components/moderation/LabelPreference.tsx:242 msgid "Adult content is disabled." -msgstr "" +msgstr "成人內容已停用" #: src/screens/Moderation/index.tsx:375 #: src/view/screens/Settings/index.tsx:684 @@ -288,7 +249,7 @@ msgstr "一封電子郵件已發送至先前填寫的電子郵件地址 {0}。 #: src/lib/moderation/useReportOptions.ts:26 msgid "An issue not included in these options" -msgstr "" +msgstr "這些選項中沒有包括的問題" #: src/view/com/profile/FollowButton.tsx:35 #: src/view/com/profile/FollowButton.tsx:45 @@ -308,7 +269,7 @@ msgstr "動物" #: src/lib/moderation/useReportOptions.ts:31 msgid "Anti-Social Behavior" -msgstr "" +msgstr "反社會行為" #: src/view/screens/LanguageSettings.tsx:95 msgid "App Language" @@ -330,10 +291,6 @@ msgstr "應用程式專用密碼名稱必須至少為 4 個字元。" msgid "App password settings" msgstr "應用程式專用密碼設定" -#: src/view/screens/Settings.tsx:650 -#~ msgid "App passwords" -#~ msgstr "應用程式專用密碼" - #: src/Navigation.tsx:251 #: src/view/screens/AppPasswords.tsx:189 #: src/view/screens/Settings/index.tsx:704 @@ -343,32 +300,15 @@ msgstr "應用程式專用密碼" #: src/components/moderation/LabelsOnMeDialog.tsx:133 #: src/components/moderation/LabelsOnMeDialog.tsx:136 msgid "Appeal" -msgstr "" +msgstr "申訴" #: src/components/moderation/LabelsOnMeDialog.tsx:201 msgid "Appeal \"{0}\" label" -msgstr "" - -#: src/view/com/util/forms/PostDropdownBtn.tsx:337 -#: src/view/com/util/forms/PostDropdownBtn.tsx:346 -#~ msgid "Appeal content warning" -#~ msgstr "申訴內容警告" - -#: src/view/com/modals/AppealLabel.tsx:65 -#~ msgid "Appeal Content Warning" -#~ msgstr "申訴內容警告" +msgstr "申訴標籤 \"{0}\"" #: src/components/moderation/LabelsOnMeDialog.tsx:192 msgid "Appeal submitted." -msgstr "" - -#: src/view/com/util/moderation/LabelInfo.tsx:52 -#~ msgid "Appeal this decision" -#~ msgstr "對此決定提出申訴" - -#: src/view/com/util/moderation/LabelInfo.tsx:56 -#~ msgid "Appeal this decision." -#~ msgstr "對此決定提出申訴。" +msgstr "申訴已提交。" #: src/view/screens/Settings/index.tsx:485 msgid "Appearance" @@ -380,7 +320,7 @@ msgstr "你確定要刪除這個應用程式專用密碼「{name}」嗎?" #: src/view/com/feeds/FeedSourceCard.tsx:280 msgid "Are you sure you want to remove {0} from your feeds?" -msgstr "" +msgstr "你確定要從你的訊息流中移除 {0} 嗎?" #: src/view/com/composer/Composer.tsx:509 msgid "Are you sure you'd like to discard this draft?" @@ -390,10 +330,6 @@ msgstr "你確定要捨棄此草稿嗎?" msgid "Are you sure?" msgstr "你確定嗎?" -#: src/view/com/util/forms/PostDropdownBtn.tsx:322 -#~ msgid "Are you sure? This cannot be undone." -#~ msgstr "你確定嗎?此操作無法撤銷。" - #: src/view/com/composer/select-language/SuggestedLanguage.tsx:60 msgid "Are you writing in <0>{0}?" msgstr "你正在使用 <0>{0} 書寫嗎?" @@ -408,7 +344,7 @@ msgstr "藝術作品或非情色的裸露。" #: src/screens/Signup/StepHandle.tsx:118 msgid "At least 3 characters" -msgstr "" +msgstr "至少 3 個字元" #: src/components/moderation/LabelsOnMeDialog.tsx:246 #: src/components/moderation/LabelsOnMeDialog.tsx:247 @@ -426,11 +362,6 @@ msgstr "" msgid "Back" msgstr "返回" -#: src/view/com/post-thread/PostThread.tsx:480 -#~ msgctxt "action" -#~ msgid "Back" -#~ msgstr "返回" - #: src/screens/Onboarding/StepSuggestedAccounts/index.tsx:144 msgid "Based on your interest in {interestsText}" msgstr "因為你對 {interestsText} 感興趣" @@ -474,10 +405,6 @@ msgstr "封鎖列表" msgid "Block these accounts?" msgstr "封鎖這些帳號?" -#: src/view/screens/ProfileList.tsx:320 -#~ msgid "Block this List" -#~ msgstr "封鎖此列表" - #: src/view/com/lists/ListCard.tsx:110 #: src/view/com/util/post-embeds/QuoteEmbed.tsx:55 msgid "Blocked" @@ -506,7 +433,7 @@ msgstr "已封鎖貼文。" #: src/screens/Profile/Sections/Labels.tsx:152 msgid "Blocking does not prevent this labeler from placing labels on your account." -msgstr "" +msgstr "封鎖並不能阻止此標記者在你的帳戶上標記標籤。" #: src/view/screens/ProfileList.tsx:631 msgid "Blocking is public. Blocked accounts cannot reply in your threads, mention you, or otherwise interact with you." @@ -514,7 +441,7 @@ msgstr "封鎖是公開的。被封鎖的帳號無法在你的貼文中回覆、 #: src/view/com/profile/ProfileMenu.tsx:353 msgid "Blocking will not prevent labels from being applied on your account, but it will stop this account from replying in your threads or interacting with you." -msgstr "" +msgstr "封鎖不會阻止標籤套用在你的帳戶上,但它會阻止此帳戶在你的討論串中回覆或與你進行互動。" #: src/view/com/auth/HomeLoggedOutCTA.tsx:98 #: src/view/com/auth/SplashScreen.web.tsx:169 @@ -546,43 +473,27 @@ msgstr "Bluesky 保持開放。" msgid "Bluesky is public." msgstr "Bluesky 為公眾而生。" -#: src/view/com/modals/Waitlist.tsx:70 -#~ msgid "Bluesky uses invites to build a healthier community. If you don't know anybody with an invite, you can sign up for the waitlist and we'll send one soon." -#~ msgstr "Bluesky 使用邀請制來打造更健康的社群環境。如果你不認識擁有邀請碼的人,你可以先填寫並加入候補清單,我們會儘快審核並發送邀請碼。" - #: src/screens/Moderation/index.tsx:533 msgid "Bluesky will not show your profile and posts to logged-out users. Other apps may not honor this request. This does not make your account private." msgstr "Bluesky 不會向未登入的使用者顯示你的個人資料和貼文。但其他應用可能不會遵照此請求,這無法確保你的帳號隱私。" -#: src/view/com/modals/ServerInput.tsx:78 -#~ msgid "Bluesky.Social" -#~ msgstr "Bluesky.Social" - #: src/lib/moderation/useLabelBehaviorDescription.ts:53 msgid "Blur images" -msgstr "" +msgstr "模糊圖片" #: src/lib/moderation/useLabelBehaviorDescription.ts:51 msgid "Blur images and filter from feeds" -msgstr "" +msgstr "從訊息流中模糊圖片並過濾" #: src/screens/Onboarding/index.tsx:33 msgid "Books" msgstr "書籍" -#: src/view/screens/Settings/index.tsx:893 -#~ msgid "Build version {0} {1}" -#~ msgstr "建構版本號 {0} {1}" - #: src/view/com/auth/HomeLoggedOutCTA.tsx:92 #: src/view/com/auth/SplashScreen.web.tsx:166 msgid "Business" msgstr "商務" -#: src/view/com/modals/ServerInput.tsx:115 -#~ msgid "Button disabled. Input custom domain to proceed." -#~ msgstr "按鈕已停用。請輸入自訂網域以繼續。" - #: src/view/com/profile/ProfileSubpageHeader.tsx:157 msgid "by —" msgstr "來自 —" @@ -601,7 +512,7 @@ msgstr "來自 <0/>" #: src/screens/Signup/StepInfo/Policies.tsx:74 msgid "By creating an account you agree to the {els}." -msgstr "" +msgstr 建立帳戶即表示你同意 {els}。" #: src/view/com/profile/ProfileSubpageHeader.tsx:159 msgid "by you" @@ -675,13 +586,9 @@ msgstr "取消引用貼文" msgid "Cancel search" msgstr "取消搜尋" -#: src/view/com/modals/Waitlist.tsx:136 -#~ msgid "Cancel waitlist signup" -#~ msgstr "取消候補清單註冊" - #: src/view/com/modals/LinkWarning.tsx:106 msgid "Cancels opening the linked website" -msgstr "" +msgstr "取消開啟連結的網站" #: src/view/com/modals/VerifyEmail.tsx:152 msgid "Change" @@ -718,10 +625,6 @@ msgstr "變更密碼" msgid "Change post language to {0}" msgstr "變更貼文的發佈語言至 {0}" -#: src/view/screens/Settings/index.tsx:733 -#~ msgid "Change your Bluesky password" -#~ msgstr "變更你的 Bluesky 密碼" - #: src/view/com/modals/ChangeEmail.tsx:109 msgid "Change Your Email" msgstr "變更你的電子郵件地址" @@ -747,10 +650,6 @@ msgstr "查看寄送至你電子郵件地址的確認郵件,然後在下方輸 msgid "Choose \"Everybody\" or \"Nobody\"" msgstr "選擇「所有人」或「沒有人」" -#: src/view/screens/Settings/index.tsx:697 -#~ msgid "Choose a new Bluesky username or create" -#~ msgstr "選擇一個新的 Bluesky 使用者名稱或重新建立" - #: src/view/com/auth/server-input/index.tsx:79 msgid "Choose Service" msgstr "選擇服務" @@ -795,11 +694,11 @@ msgstr "清除搜尋記錄" #: src/view/screens/Settings/index.tsx:869 msgid "Clears all legacy storage data" -msgstr "" +msgstr "清除所有舊儲存資料" #: src/view/screens/Settings/index.tsx:881 msgid "Clears all storage data" -msgstr "" +msgstr "清除所有資料" #: src/view/screens/Support.tsx:40 msgid "click here" @@ -807,11 +706,11 @@ msgstr "點擊這裡" #: src/components/TagMenu/index.web.tsx:138 msgid "Click here to open tag menu for {tag}" -msgstr "" +msgstr "點擊這裡開啟 {tag} 的標籤選單" #: src/components/RichText.tsx:192 msgid "Click here to open tag menu for #{tag}" -msgstr "" +msgstr "點擊這裡開啟 #{tag} 的標籤選單" #: src/screens/Onboarding/index.tsx:35 msgid "Climate" @@ -850,7 +749,7 @@ msgstr "關閉導覽頁腳" #: src/components/Menu/index.tsx:207 #: src/components/TagMenu/index.tsx:262 msgid "Close this dialog" -msgstr "" +msgstr "關閉此對話框" #: src/view/shell/index.web.tsx:56 msgid "Closes bottom navigation bar" @@ -907,11 +806,11 @@ msgstr "調整類別的內容過濾設定:{0}" #: src/components/moderation/LabelPreference.tsx:81 msgid "Configure content filtering setting for category: {name}" -msgstr "" +msgstr "為 {name} 分類配置內容過濾設定" #: src/components/moderation/LabelPreference.tsx:244 msgid "Configured in <0>moderation settings." -msgstr "" +msgstr "在<0>限制設定中進行配置" #: src/components/Prompt.tsx:153 #: src/components/Prompt.tsx:156 @@ -923,12 +822,6 @@ msgstr "" msgid "Confirm" msgstr "確認" -#: src/view/com/modals/Confirm.tsx:75 -#: src/view/com/modals/Confirm.tsx:78 -#~ msgctxt "action" -#~ msgid "Confirm" -#~ msgstr "確認" - #: src/view/com/modals/ChangeEmail.tsx:193 #: src/view/com/modals/ChangeEmail.tsx:195 msgid "Confirm Change" @@ -942,17 +835,13 @@ msgstr "確認內容語言設定" msgid "Confirm delete account" msgstr "確認刪除帳號" -#: src/view/com/modals/ContentFilteringSettings.tsx:156 -#~ msgid "Confirm your age to enable adult content." -#~ msgstr "確認你的年齡以顯示成人內容。" - #: src/screens/Moderation/index.tsx:301 msgid "Confirm your age:" -msgstr "" +msgstr "確認你的年齡:" #: src/screens/Moderation/index.tsx:292 msgid "Confirm your birthdate" -msgstr "" +msgstr "確認你的出生日期" #: src/view/com/modals/ChangeEmail.tsx:157 #: src/view/com/modals/DeleteAccount.tsx:175 @@ -961,10 +850,6 @@ msgstr "" msgid "Confirmation code" msgstr "驗證碼" -#: src/view/com/modals/Waitlist.tsx:120 -#~ msgid "Confirms signing up {email} to the waitlist" -#~ msgstr "確認將 {email} 註冊到候補列表" - #: src/screens/Login/LoginForm.tsx:248 msgid "Connecting..." msgstr "連線中…" @@ -975,19 +860,11 @@ msgstr "聯絡支援" #: src/components/moderation/LabelsOnMe.tsx:42 msgid "content" -msgstr "" +msgstr "內容" #: src/lib/moderation/useGlobalLabelStrings.ts:18 msgid "Content Blocked" -msgstr "" - -#: src/view/screens/Moderation.tsx:83 -#~ msgid "Content filtering" -#~ msgstr "內容過濾" - -#: src/view/com/modals/ContentFilteringSettings.tsx:44 -#~ msgid "Content Filtering" -#~ msgstr "內容過濾" +msgstr "已封鎖內容" #: src/screens/Moderation/index.tsx:285 msgid "Content filters" @@ -1016,7 +893,7 @@ msgstr "內容警告" #: src/components/Menu/index.web.tsx:84 msgid "Context menu backdrop, click to close the menu." -msgstr "" +msgstr "上下文菜單背景,點擊以關閉菜單" #: src/screens/Onboarding/StepAlgoFeeds/index.tsx:161 #: src/screens/Onboarding/StepFollowingFeed.tsx:154 @@ -1031,7 +908,7 @@ msgstr "繼續" #: src/components/AccountList.tsx:108 msgid "Continue as {0} (currently signed in)" -msgstr "" +msgstr "以 {0} 繼續 (目前已登入)" #: src/screens/Onboarding/StepFollowingFeed.tsx:151 #: src/screens/Onboarding/StepInterests/index.tsx:249 @@ -1090,10 +967,6 @@ msgstr "複製列表連結" msgid "Copy link to post" msgstr "複製貼文連結" -#: src/view/com/profile/ProfileHeader.tsx:295 -#~ msgid "Copy link to profile" -#~ msgstr "複製個人資料連結" - #: src/view/com/util/forms/PostDropdownBtn.tsx:220 #: src/view/com/util/forms/PostDropdownBtn.tsx:222 msgid "Copy post text" @@ -1112,10 +985,6 @@ msgstr "無法載入訊息流" msgid "Could not load list" msgstr "無法載入列表" -#: src/view/com/auth/create/Step2.tsx:91 -#~ msgid "Country" -#~ msgstr "國家" - #: src/view/com/auth/HomeLoggedOutCTA.tsx:65 #: src/view/com/auth/SplashScreen.tsx:75 #: src/view/com/auth/SplashScreen.web.tsx:104 @@ -1142,20 +1011,12 @@ msgstr "建立新帳號" #: src/components/ReportDialog/SelectReportOptionView.tsx:93 msgid "Create report for {0}" -msgstr "" +msgstr "建立 {0} 的檢舉" #: src/view/screens/AppPasswords.tsx:246 msgid "Created {0}" msgstr "{0} 已建立" -#: src/view/screens/ProfileFeed.tsx:616 -#~ msgid "Created by <0/>" -#~ msgstr "由 <0/> 建立" - -#: src/view/screens/ProfileFeed.tsx:614 -#~ msgid "Created by you" -#~ msgstr "由你建立" - #: src/view/com/composer/Composer.tsx:469 msgid "Creates a card with a thumbnail. The card links to {url}" msgstr "建立帶有縮圖的卡片。該卡片連結到 {url}" @@ -1182,14 +1043,10 @@ msgstr "由社群打造的自訂訊息流帶來新鮮體驗,協助你找到所 msgid "Customize media from external sites." msgstr "自訂外部網站的媒體。" -#: src/view/screens/Settings.tsx:687 -#~ msgid "Danger Zone" -#~ msgstr "危險區域" - #: src/view/screens/Settings/index.tsx:504 #: src/view/screens/Settings/index.tsx:530 msgid "Dark" -msgstr "深黑" +msgstr "深色" #: src/view/screens/Debug.tsx:63 msgid "Dark mode" @@ -1201,11 +1058,11 @@ msgstr "深色主題" #: src/screens/Signup/StepInfo/index.tsx:132 msgid "Date of birth" -msgstr "" +msgstr "出生日期" #: src/view/screens/Settings/index.tsx:841 msgid "Debug Moderation" -msgstr "" +msgstr "限制除錯" #: src/view/screens/Debug.tsx:83 msgid "Debug panel" @@ -1241,10 +1098,6 @@ msgstr "刪除列表" msgid "Delete my account" msgstr "刪除我的帳號" -#: src/view/screens/Settings.tsx:706 -#~ msgid "Delete my account…" -#~ msgstr "刪除我的帳號…" - #: src/view/screens/Settings/index.tsx:808 msgid "Delete My Account…" msgstr "刪除我的帳號…" @@ -1256,7 +1109,7 @@ msgstr "刪除貼文" #: src/view/screens/ProfileList.tsx:608 msgid "Delete this list?" -msgstr "" +msgstr "刪除此列表?" #: src/view/com/util/forms/PostDropdownBtn.tsx:314 msgid "Delete this post?" @@ -1277,10 +1130,6 @@ msgstr "已刪除貼文。" msgid "Description" msgstr "描述" -#: src/view/screens/Settings.tsx:760 -#~ msgid "Developer Tools" -#~ msgstr "開發者工具" - #: src/view/com/composer/Composer.tsx:218 msgid "Did you want to say anything?" msgstr "有什麼想說的嗎?" @@ -1294,16 +1143,12 @@ msgstr "暗淡" #: src/lib/moderation/useLabelBehaviorDescription.ts:68 #: src/screens/Moderation/index.tsx:341 msgid "Disabled" -msgstr "" +msgstr "停用" #: src/view/com/composer/Composer.tsx:511 msgid "Discard" msgstr "捨棄" -#: src/view/com/composer/Composer.tsx:145 -#~ msgid "Discard draft" -#~ msgstr "捨棄草稿" - #: src/view/com/composer/Composer.tsx:508 msgid "Discard draft?" msgstr "捨棄草稿?" @@ -1318,10 +1163,6 @@ msgstr "鼓勵應用程式不要向未登入使用者顯示我的帳號" msgid "Discover new custom feeds" msgstr "探索新的自訂訊息流" -#: src/view/screens/Feeds.tsx:473 -#~ msgid "Discover new feeds" -#~ msgstr "探索新的訊息流" - #: src/view/screens/Feeds.tsx:689 msgid "Discover New Feeds" msgstr "探索新的訊息流" @@ -1336,28 +1177,24 @@ msgstr "顯示名稱" #: src/view/com/modals/ChangeHandle.tsx:397 msgid "DNS Panel" -msgstr "" +msgstr "DNS 控制台" #: src/lib/moderation/useGlobalLabelStrings.ts:39 msgid "Does not include nudity." -msgstr "" +msgstr "不包含裸露内容。" #: src/screens/Signup/StepHandle.tsx:104 msgid "Doesn't begin or end with a hyphen" -msgstr "" +msgstr "不以連字符開頭或結尾" #: src/view/com/modals/ChangeHandle.tsx:481 msgid "Domain Value" -msgstr "" +msgstr "網域設定值" #: src/view/com/modals/ChangeHandle.tsx:488 msgid "Domain verified!" msgstr "網域已驗證!" -#: src/view/com/auth/create/Step1.tsx:170 -#~ msgid "Don't have an invite code?" -#~ msgstr "沒有邀請碼?" - #: src/components/dialogs/BirthDateSettings.tsx:119 #: src/components/dialogs/BirthDateSettings.tsx:125 #: src/components/forms/DateField/index.tsx:74 @@ -1393,14 +1230,6 @@ msgstr "完成" msgid "Done{extraText}" msgstr "完成{extraText}" -#: src/view/com/auth/login/ChooseAccountForm.tsx:46 -#~ msgid "Double tap to sign in" -#~ msgstr "雙擊以登入" - -#: src/view/screens/Settings/index.tsx:755 -#~ msgid "Download Bluesky account data (repository)" -#~ msgstr "下載 Bluesky 帳號資料(存放庫)" - #: src/view/screens/Settings/ExportCarDialog.tsx:59 #: src/view/screens/Settings/ExportCarDialog.tsx:63 msgid "Download CAR file" @@ -1416,7 +1245,7 @@ msgstr "受 Apple 政策限制,成人內容只能在完成註冊後在網頁 #: src/view/com/modals/ChangeHandle.tsx:258 msgid "e.g. alice" -msgstr "" +msgstr "例如:alice" #: src/view/com/modals/EditProfile.tsx:186 msgid "e.g. Alice Roberts" @@ -1424,7 +1253,7 @@ msgstr "例如:張藍天" #: src/view/com/modals/ChangeHandle.tsx:380 msgid "e.g. alice.com" -msgstr "" +msgstr "例如:alice.com" #: src/view/com/modals/EditProfile.tsx:204 msgid "e.g. Artist, dog-lover, and avid reader." @@ -1432,7 +1261,7 @@ msgstr "例如:藝術家、愛狗人士和狂熱讀者。" #: src/lib/moderation/useGlobalLabelStrings.ts:43 msgid "E.g. artistic nudes." -msgstr "" +msgstr "例如:藝術裸露。" #: src/view/com/modals/CreateOrEditList.tsx:284 msgid "e.g. Great Posters" @@ -1462,7 +1291,7 @@ msgstr "編輯" #: src/view/com/util/UserAvatar.tsx:299 #: src/view/com/util/UserBanner.tsx:85 msgid "Edit avatar" -msgstr "" +msgstr "編輯頭像" #: src/view/com/composer/photos/Gallery.tsx:144 #: src/view/com/modals/EditImage.tsx:208 @@ -1564,11 +1393,7 @@ msgstr "允許在你的訊息流中出現成人內容" #: src/components/dialogs/EmbedConsent.tsx:82 #: src/components/dialogs/EmbedConsent.tsx:89 msgid "Enable external media" -msgstr "" - -#: src/view/com/modals/EmbedConsent.tsx:97 -#~ msgid "Enable External Media" -#~ msgstr "啟用外部媒體" +msgstr "啟用外部媒體" #: src/view/screens/PreferencesExternalEmbeds.tsx:75 msgid "Enable media players for" @@ -1580,7 +1405,7 @@ msgstr "啟用此設定來只顯示你跟隨的人之間的回覆。" #: src/components/dialogs/EmbedConsent.tsx:94 msgid "Enable this source only" -msgstr "" +msgstr "僅啟用此來源" #: src/screens/Moderation/index.tsx:339 msgid "Enabled" @@ -1596,12 +1421,12 @@ msgstr "輸入此應用程式專用密碼的名稱" #: src/screens/Login/SetNewPasswordForm.tsx:139 msgid "Enter a password" -msgstr "" +msgstr "輸入密碼" #: src/components/dialogs/MutedWords.tsx:99 #: src/components/dialogs/MutedWords.tsx:100 msgid "Enter a word or tag" -msgstr "" +msgstr "輸入詞彙或標籤" #: src/view/com/modals/VerifyEmail.tsx:105 msgid "Enter Confirmation Code" @@ -1623,10 +1448,6 @@ msgstr "輸入你用於建立帳號的電子郵件。我們將向你發送重設 msgid "Enter your birth date" msgstr "輸入你的出生日期" -#: src/view/com/modals/Waitlist.tsx:78 -#~ msgid "Enter your email" -#~ msgstr "輸入你的電子郵件地址" - #: src/screens/Login/ForgotPasswordForm.tsx:105 #: src/screens/Signup/StepInfo/index.tsx:91 msgid "Enter your email address" @@ -1640,10 +1461,6 @@ msgstr "請在上方輸入你的新電子郵件地址" msgid "Enter your new email address below." msgstr "請在下方輸入你的新電子郵件地址。" -#: src/view/com/auth/create/Step2.tsx:188 -#~ msgid "Enter your phone number" -#~ msgstr "輸入你的手機號碼" - #: src/screens/Login/index.tsx:101 msgid "Enter your username and password" msgstr "輸入你的使用者名稱和密碼" @@ -1662,11 +1479,11 @@ msgstr "所有人" #: src/lib/moderation/useReportOptions.ts:66 msgid "Excessive mentions or replies" -msgstr "" +msgstr "過多的提及或回覆" #: src/view/com/modals/DeleteAccount.tsx:230 msgid "Exits account deletion process" -msgstr "" +msgstr "離開帐户删除流程" #: src/view/com/modals/ChangeHandle.tsx:151 msgid "Exits handle change process" @@ -1674,7 +1491,7 @@ msgstr "離開修改帳號代碼流程" #: src/view/com/modals/crop-image/CropImage.web.tsx:136 msgid "Exits image cropping process" -msgstr "" +msgstr "離開圖片裁剪流程" #: src/view/com/lightbox/Lightbox.web.tsx:130 msgid "Exits image view" @@ -1685,10 +1502,6 @@ msgstr "離開圖片檢視器" msgid "Exits inputting search query" msgstr "離開搜尋字詞輸入" -#: src/view/com/modals/Waitlist.tsx:138 -#~ msgid "Exits signing up for waitlist with {email}" -#~ msgstr "將 {email} 從候補列表中移除" - #: src/view/com/lightbox/Lightbox.web.tsx:183 msgid "Expand alt text" msgstr "展開替代文字" @@ -1700,11 +1513,11 @@ msgstr "展開或摺疊你要回覆的完整貼文" #: src/lib/moderation/useGlobalLabelStrings.ts:47 msgid "Explicit or potentially disturbing media." -msgstr "" +msgstr "露骨或可能令人不安的媒體內容。" #: src/lib/moderation/useGlobalLabelStrings.ts:35 msgid "Explicit sexual images." -msgstr "" +msgstr "露骨的情色內容圖片。" #: src/view/screens/Settings/index.tsx:777 msgid "Export my data" @@ -1729,7 +1542,7 @@ msgstr "外部媒體可能允許網站收集有關你和你裝置的信息。在 #: src/view/screens/PreferencesExternalEmbeds.tsx:52 #: src/view/screens/Settings/index.tsx:677 msgid "External Media Preferences" -msgstr "外部媒體偏好設定" +msgstr "外部媒體設定偏好" #: src/view/screens/Settings/index.tsx:668 msgid "External media settings" @@ -1755,7 +1568,7 @@ msgstr "無法載入推薦訊息流" #: src/view/com/lightbox/Lightbox.tsx:83 msgid "Failed to save image: {0}" -msgstr "" +msgstr "無法儲存圖片:{0}" #: src/Navigation.tsx:196 msgid "Feed" @@ -1769,10 +1582,6 @@ msgstr "{0} 建立的訊息流" msgid "Feed offline" msgstr "訊息流已離線" -#: src/view/com/feeds/FeedPage.tsx:143 -#~ msgid "Feed Preferences" -#~ msgstr "訊息流偏好設定" - #: src/view/shell/desktop/RightNav.tsx:61 #: src/view/shell/Drawer.tsx:314 msgid "Feedback" @@ -1789,10 +1598,6 @@ msgstr "意見回饋" msgid "Feeds" msgstr "訊息流" -#: src/screens/Onboarding/StepAlgoFeeds/index.tsx:106 -#~ msgid "Feeds are created by users and organizations. They offer you varied experiences and suggest content you may like using algorithms." -#~ msgstr "訊息流由使用者和組織建立,結合演算法為你推薦可能喜歡的內容,可為你帶來不一樣的體驗。" - #: src/view/com/auth/onboarding/RecommendedFeeds.tsx:57 msgid "Feeds are created by users to curate content. Choose some feeds that you find interesting." msgstr "訊息流由使用者建立並管理。選擇一些你覺得有趣的訊息流。" @@ -1807,11 +1612,11 @@ msgstr "訊息流也可以圍繞某些話題!" #: src/view/com/modals/ChangeHandle.tsx:481 msgid "File Contents" -msgstr "" +msgstr "檔案內容" #: src/lib/moderation/useLabelBehaviorDescription.ts:66 msgid "Filter from feeds" -msgstr "" +msgstr "從訊息流中篩選" #: src/screens/Onboarding/StepFinished.tsx:155 msgid "Finalizing" @@ -1837,11 +1642,7 @@ msgstr "正在尋找相似的帳號…" #: src/view/screens/PreferencesFollowingFeed.tsx:111 msgid "Fine-tune the content you see on your Following feed." -msgstr "" - -#: src/view/screens/PreferencesHomeFeed.tsx:111 -#~ msgid "Fine-tune the content you see on your home screen." -#~ msgstr "調整你在首頁上所看到的內容。" +msgstr "調整你在跟隨訊息流上所看到的內容。" #: src/view/screens/PreferencesThreads.tsx:60 msgid "Fine-tune the discussion threads." @@ -1894,7 +1695,7 @@ msgstr "跟隨所有" #: src/view/com/post-thread/PostThreadFollowBtn.tsx:144 msgid "Follow Back" -msgstr "" +msgstr "回追蹤" #: src/screens/Onboarding/StepSuggestedAccounts/index.tsx:182 msgid "Follow selected accounts and continue to the next step" @@ -1938,7 +1739,7 @@ msgstr "跟隨中:{0}" #: src/view/screens/Settings/index.tsx:553 msgid "Following feed preferences" -msgstr "" +msgstr "跟隨訊息流設定偏好" #: src/Navigation.tsx:262 #: src/view/com/home/HomeHeaderLayout.web.tsx:50 @@ -1946,7 +1747,7 @@ msgstr "" #: src/view/screens/PreferencesFollowingFeed.tsx:104 #: src/view/screens/Settings/index.tsx:562 msgid "Following Feed Preferences" -msgstr "" +msgstr "跟隨訊息流設定偏好" #: src/screens/Profile/Header/Handle.tsx:24 msgid "Follows you" @@ -1968,14 +1769,6 @@ msgstr "為了保護你的帳號安全,我們需要將驗證碼發送到你的 msgid "For security reasons, you won't be able to view this again. If you lose this password, you'll need to generate a new one." msgstr "為了保護你的帳號安全,你將無法再次查看此內容。如果你丟失了此密碼,你將需要產生一個新密碼。" -#: src/view/com/auth/login/LoginForm.tsx:244 -#~ msgid "Forgot" -#~ msgstr "忘記" - -#: src/view/com/auth/login/LoginForm.tsx:241 -#~ msgid "Forgot password" -#~ msgstr "忘記密碼" - #: src/screens/Login/index.tsx:129 #: src/screens/Login/index.tsx:144 msgid "Forgot Password" @@ -1983,20 +1776,20 @@ msgstr "忘記密碼" #: src/screens/Login/LoginForm.tsx:201 msgid "Forgot password?" -msgstr "" +msgstr "忘記密碼?" #: src/screens/Login/LoginForm.tsx:212 msgid "Forgot?" -msgstr "" +msgstr "忘記?" #: src/lib/moderation/useReportOptions.ts:52 msgid "Frequently Posts Unwanted Content" -msgstr "" +msgstr "經常發佈無關內容" #: src/screens/Hashtag.tsx:109 #: src/screens/Hashtag.tsx:149 msgid "From @{sanitizedAuthor}" -msgstr "" +msgstr "來自 @{sanitizedAuthor}" #: src/view/com/posts/FeedItem.tsx:179 msgctxt "from-feed" @@ -2014,7 +1807,7 @@ msgstr "開始" #: src/lib/moderation/useReportOptions.ts:37 msgid "Glaring violations of law or terms of service" -msgstr "" +msgstr "明顯違反法律或服務條款" #: src/components/moderation/ScreenHider.tsx:151 #: src/components/moderation/ScreenHider.tsx:160 @@ -2046,11 +1839,11 @@ msgstr "返回上一步" #: src/view/screens/NotFound.tsx:55 msgid "Go home" -msgstr "" +msgstr "前往首頁" #: src/view/screens/NotFound.tsx:54 msgid "Go Home" -msgstr "" +msgstr "前往首頁" #: src/view/screens/Search/Search.tsx:749 #: src/view/shell/desktop/Search.tsx:263 @@ -2064,7 +1857,7 @@ msgstr "前往下一步" #: src/lib/moderation/useGlobalLabelStrings.ts:46 msgid "Graphic Media" -msgstr "" +msgstr "平面媒體" #: src/view/com/modals/ChangeHandle.tsx:266 msgid "Handle" @@ -2072,19 +1865,15 @@ msgstr "帳號代碼" #: src/lib/moderation/useReportOptions.ts:32 msgid "Harassment, trolling, or intolerance" -msgstr "" +msgstr "騷擾、惡作劇或其他無法容忍的行為" #: src/Navigation.tsx:282 msgid "Hashtag" -msgstr "" - -#: src/components/RichText.tsx:188 -#~ msgid "Hashtag: {tag}" -#~ msgstr "" +msgstr "標籤" #: src/components/RichText.tsx:191 msgid "Hashtag: #{tag}" -msgstr "" +msgstr "標籤:#{tag}" #: src/screens/Signup/index.tsx:217 msgid "Having trouble?" @@ -2105,7 +1894,7 @@ msgstr "這裡有一些熱門的話題訊息流。跟隨的訊息流數量沒有 #: src/screens/Onboarding/StepTopicalFeeds.tsx:84 msgid "Here are some topical feeds based on your interests: {interestsText}. You can choose to follow as many as you like." -msgstr "這裡有一些根據您的興趣({interestsText})所推薦的熱門的話題訊息流。跟隨的訊息流數量沒有限制。" +msgstr "這裡有一些根據你的興趣({interestsText})所推薦的熱門的話題訊息流。跟隨的訊息流數量沒有限制。" #: src/view/com/modals/AddAppPasswords.tsx:154 msgid "Here is your app password." @@ -2147,10 +1936,6 @@ msgstr "隱藏這則貼文?" msgid "Hide user list" msgstr "隱藏使用者列表" -#: src/view/com/profile/ProfileHeader.tsx:487 -#~ msgid "Hides posts from {0} in your feed" -#~ msgstr "在你的訂閱中隱藏來自 {0} 的貼文" - #: src/view/com/posts/FeedErrorMessage.tsx:111 msgid "Hmm, some kind of issue occurred when contacting the feed server. Please let the feed owner know about this issue." msgstr "唔,與訊息流伺服器連線時發生了某種問題。請告訴該訊息流的擁有者這個問題。" @@ -2173,11 +1958,11 @@ msgstr "唔,我們無法找到這個訊息流,它可能已被刪除。" #: src/screens/Moderation/index.tsx:59 msgid "Hmmmm, it seems we're having trouble loading this data. See below for more details. If this issue persists, please contact us." -msgstr "" +msgstr "唔,看起來我們在載入這些資料時遇到了問題,詳情請參閱下方。如果問題持續存在,請聯絡我們。" #: src/screens/Profile/ErrorState.tsx:31 msgid "Hmmmm, we couldn't load that moderation service." -msgstr "" +msgstr "唔,我們無法載入該限制服務" #: src/Navigation.tsx:454 #: src/view/shell/bottom-bar/BottomBar.tsx:147 @@ -2187,16 +1972,9 @@ msgstr "" msgid "Home" msgstr "首頁" -#: src/Navigation.tsx:247 -#: src/view/com/pager/FeedsTabBarMobile.tsx:123 -#: src/view/screens/PreferencesHomeFeed.tsx:104 -#: src/view/screens/Settings/index.tsx:543 -#~ msgid "Home Feed Preferences" -#~ msgstr "首頁訊息流偏好" - #: src/view/com/modals/ChangeHandle.tsx:420 msgid "Host:" -msgstr "" +msgstr "主機:" #: src/screens/Login/ForgotPasswordForm.tsx:89 #: src/screens/Login/LoginForm.tsx:134 @@ -2231,15 +2009,15 @@ msgstr "若不勾選,則預設為全年齡向。" #: src/screens/Signup/StepInfo/Policies.tsx:83 msgid "If you are not yet an adult according to the laws of your country, your parent or legal guardian must read these Terms on your behalf." -msgstr "" +msgstr "如果根據你所在國家的法律,你尚未成年,則你的父母或法定監護人必須代表你閱讀這些條款。" #: src/view/screens/ProfileList.tsx:610 msgid "If you delete this list, you won't be able to recover it." -msgstr "" +msgstr "如果刪除這個列表,你將無法恢復它。" #: src/view/com/util/forms/PostDropdownBtn.tsx:316 msgid "If you remove this post, you won't be able to recover it." -msgstr "" +msgstr "如果刪除這則貼文,你將無法恢復它。" #: src/view/com/modals/ChangePassword.tsx:148 msgid "If you want to change your password, we will send you a code to verify that this is your account." @@ -2247,7 +2025,7 @@ msgstr "如果你想更改密碼,我們將向你發送一個驗證碼以確認 #: src/lib/moderation/useReportOptions.ts:36 msgid "Illegal and Urgent" -msgstr "" +msgstr "違法" #: src/view/com/util/images/Gallery.tsx:38 msgid "Image" @@ -2257,14 +2035,9 @@ msgstr "圖片" msgid "Image alt text" msgstr "圖片替代文字" -#: src/view/com/util/UserAvatar.tsx:311 -#: src/view/com/util/UserBanner.tsx:118 -#~ msgid "Image options" -#~ msgstr "圖片選項" - #: src/lib/moderation/useReportOptions.ts:47 msgid "Impersonation or false claims about identity or affiliation" -msgstr "" +msgstr "冒充或虛假聲明身份或隸屬關係" #: src/screens/Login/SetNewPasswordForm.tsx:127 msgid "Input code sent to your email for password reset" @@ -2274,14 +2047,6 @@ msgstr "輸入發送到你電子郵件地址的重設碼以重設密碼" msgid "Input confirmation code for account deletion" msgstr "輸入刪除帳號的驗證碼" -#: src/view/com/auth/create/Step1.tsx:177 -#~ msgid "Input email for Bluesky account" -#~ msgstr "輸入 Bluesky 帳號的電子郵件地址" - -#: src/view/com/auth/create/Step1.tsx:151 -#~ msgid "Input invite code to proceed" -#~ msgstr "輸入邀請碼以繼續" - #: src/view/com/modals/AddAppPasswords.tsx:181 msgid "Input name for app password" msgstr "輸入應用程式專用密碼名稱" @@ -2294,10 +2059,6 @@ msgstr "輸入新密碼" msgid "Input password for account deletion" msgstr "輸入密碼以刪除帳號" -#: src/view/com/auth/create/Step2.tsx:196 -#~ msgid "Input phone number for SMS verification" -#~ msgstr "輸入手機號碼進行簡訊驗證" - #: src/screens/Login/LoginForm.tsx:195 msgid "Input the password tied to {identifier}" msgstr "輸入與 {identifier} 關聯的密碼" @@ -2306,21 +2067,13 @@ msgstr "輸入與 {identifier} 關聯的密碼" msgid "Input the username or email address you used at signup" msgstr "輸入註冊時使用的使用者名稱或電子郵件地址" -#: src/view/com/auth/create/Step2.tsx:271 -#~ msgid "Input the verification code we have texted to you" -#~ msgstr "輸入我們發送到你手機的驗證碼" - -#: src/view/com/modals/Waitlist.tsx:90 -#~ msgid "Input your email to get on the Bluesky waitlist" -#~ msgstr "輸入你的電子郵件地址以加入 Bluesky 候補列表" - #: src/screens/Login/LoginForm.tsx:194 msgid "Input your password" msgstr "輸入你的密碼" #: src/view/com/modals/ChangeHandle.tsx:389 msgid "Input your preferred hosting provider" -msgstr "" +msgstr "輸入你的托管服務提供商" #: src/screens/Signup/StepHandle.tsx:62 msgid "Input your user handle" @@ -2334,10 +2087,6 @@ msgstr "無效或不支援的貼文紀錄" msgid "Invalid username or password" msgstr "使用者名稱或密碼無效" -#: src/view/screens/Settings.tsx:411 -#~ msgid "Invite" -#~ msgstr "邀請" - #: src/view/com/modals/InviteCodes.tsx:94 msgid "Invite a Friend" msgstr "邀請朋友" @@ -2354,10 +2103,6 @@ msgstr "邀請碼無效。請檢查你輸入的內容是否正確,然後重試 msgid "Invite codes: {0} available" msgstr "邀請碼:{0} 個可用" -#: src/view/shell/Drawer.tsx:645 -#~ msgid "Invite codes: {invitesAvailable} available" -#~ msgstr "邀請碼:{invitesAvailable} 個可用" - #: src/view/com/modals/InviteCodes.tsx:170 msgid "Invite codes: 1 available" msgstr "邀請碼:1 個可用" @@ -2371,54 +2116,41 @@ msgstr "它會即時顯示你所跟隨的人發佈的貼文。" msgid "Jobs" msgstr "工作" -#: src/view/com/modals/Waitlist.tsx:67 -#~ msgid "Join the waitlist" -#~ msgstr "加入候補列表" - -#: src/view/com/auth/create/Step1.tsx:174 -#: src/view/com/auth/create/Step1.tsx:178 -#~ msgid "Join the waitlist." -#~ msgstr "加入候補列表。" - -#: src/view/com/modals/Waitlist.tsx:128 -#~ msgid "Join Waitlist" -#~ msgstr "加入候補列表" - #: src/screens/Onboarding/index.tsx:24 msgid "Journalism" msgstr "新聞學" #: src/components/moderation/LabelsOnMe.tsx:59 msgid "label has been placed on this {labelTarget}" -msgstr "" +msgstr "此標籤已放置於 {labelTarget} 上" #: src/components/moderation/ContentHider.tsx:144 msgid "Labeled by {0}." -msgstr "" +msgstr "由 {0} 標註。" #: src/components/moderation/ContentHider.tsx:142 msgid "Labeled by the author." -msgstr "" +msgstr "由作者標註。" #: src/view/screens/Profile.tsx:188 msgid "Labels" -msgstr "" +msgstr "標籤" #: src/screens/Profile/Sections/Labels.tsx:142 msgid "Labels are annotations on users and content. They can be used to hide, warn, and categorize the network." -msgstr "" +msgstr "標籤是對使用者和內容的標註,可用於隱藏、警告和對網路進行分類。" #: src/components/moderation/LabelsOnMe.tsx:61 msgid "labels have been placed on this {labelTarget}" -msgstr "" +msgstr "此標籤已放置於 {labelTarget} 上" #: src/components/moderation/LabelsOnMeDialog.tsx:62 msgid "Labels on your account" -msgstr "" +msgstr "你帳戶上的標籤" #: src/components/moderation/LabelsOnMeDialog.tsx:64 msgid "Labels on your content" -msgstr "" +msgstr "你內容上的標籤" #: src/view/com/composer/select-language/SelectLangBtn.tsx:104 msgid "Language selection" @@ -2437,14 +2169,6 @@ msgstr "語言設定" msgid "Languages" msgstr "語言" -#: src/view/com/auth/create/StepHeader.tsx:20 -#~ msgid "Last step!" -#~ msgstr "最後一步!" - -#: src/view/com/util/moderation/ContentHider.tsx:103 -#~ msgid "Learn more" -#~ msgstr "瞭解詳情" - #: src/components/moderation/ScreenHider.tsx:136 msgid "Learn More" msgstr "瞭解詳情" @@ -2452,7 +2176,7 @@ msgstr "瞭解詳情" #: src/components/moderation/ContentHider.tsx:65 #: src/components/moderation/ContentHider.tsx:128 msgid "Learn more about the moderation applied to this content." -msgstr "" +msgstr "詳細了解套用於此內容的限制。" #: src/components/moderation/PostHider.tsx:85 #: src/components/moderation/ScreenHider.tsx:125 @@ -2492,11 +2216,6 @@ msgstr "讓我們來重設你的密碼吧!" msgid "Let's go!" msgstr "讓我們開始吧!" -#: src/view/com/util/UserAvatar.tsx:248 -#: src/view/com/util/UserBanner.tsx:62 -#~ msgid "Library" -#~ msgstr "圖片庫" - #: src/view/screens/Settings/index.tsx:498 msgid "Light" msgstr "亮色" @@ -2528,7 +2247,7 @@ msgstr "{0} 個 {1} 喜歡" #: src/components/LabelingServiceCard/index.tsx:72 msgid "Liked by {count} {0}" -msgstr "" +msgstr "{count} 個 {0} 喜歡" #: src/screens/Profile/Header/ProfileHeaderLabeler.tsx:278 #: src/screens/Profile/Header/ProfileHeaderLabeler.tsx:292 @@ -2597,11 +2316,6 @@ msgstr "解除靜音列表" msgid "Lists" msgstr "列表" -#: src/view/com/post-thread/PostThread.tsx:333 -#: src/view/com/post-thread/PostThread.tsx:341 -#~ msgid "Load more posts" -#~ msgstr "載入更多貼文" - #: src/view/screens/Notifications.tsx:159 msgid "Load new notifications" msgstr "載入新的通知" @@ -2617,10 +2331,6 @@ msgstr "載入新的貼文" msgid "Loading..." msgstr "載入中…" -#: src/view/com/modals/ServerInput.tsx:50 -#~ msgid "Local dev server" -#~ msgstr "本地開發伺服器" - #: src/Navigation.tsx:221 msgid "Log" msgstr "日誌" @@ -2642,7 +2352,7 @@ msgstr "登入未列出的帳號" #: src/screens/Login/SetNewPasswordForm.tsx:116 msgid "Looks like XXXXX-XXXXX" -msgstr "" +msgstr "看起來像是 XXXXX-XXXXX" #: src/view/com/modals/LinkWarning.tsx:79 msgid "Make sure this is where you intend to go!" @@ -2650,15 +2360,7 @@ msgstr "請確認這是你想要去的的地方!" #: src/components/dialogs/MutedWords.tsx:82 msgid "Manage your muted words and tags" -msgstr "" - -#: src/view/com/auth/create/Step2.tsx:118 -#~ msgid "May not be longer than 253 characters" -#~ msgstr "" - -#: src/view/com/auth/create/Step2.tsx:109 -#~ msgid "May only contain letters and numbers" -#~ msgstr "" +msgstr "管理你靜音的文字和標籤" #: src/view/screens/Profile.tsx:192 msgid "Media" @@ -2683,7 +2385,7 @@ msgstr "來自伺服器的訊息:{0}" #: src/lib/moderation/useReportOptions.ts:45 msgid "Misleading Account" -msgstr "" +msgstr "誤導性帳戶" #: src/Navigation.tsx:119 #: src/screens/Moderation/index.tsx:104 @@ -2696,7 +2398,7 @@ msgstr "限制" #: src/components/moderation/ModerationDetailsDialog.tsx:112 msgid "Moderation details" -msgstr "" +msgstr "限制詳情" #: src/view/com/lists/ListCard.tsx:93 #: src/view/com/modals/UserAddRemoveLists.tsx:206 @@ -2736,11 +2438,11 @@ msgstr "限制設定" #: src/Navigation.tsx:216 msgid "Moderation states" -msgstr "" +msgstr "限制狀態" #: src/screens/Moderation/index.tsx:215 msgid "Moderation tools" -msgstr "" +msgstr "限制工具" #: src/components/moderation/ModerationDetailsDialog.tsx:48 #: src/lib/moderation/useModerationCauseDescription.ts:40 @@ -2749,7 +2451,7 @@ msgstr "限制選擇對內容設定一般警告。" #: src/view/com/post-thread/PostThreadItem.tsx:541 msgid "More" -msgstr "" +msgstr "更多" #: src/view/shell/desktop/Feeds.tsx:65 msgid "More feeds" @@ -2759,25 +2461,17 @@ msgstr "更多訊息流" msgid "More options" msgstr "更多選項" -#: src/view/com/util/forms/PostDropdownBtn.tsx:315 -#~ msgid "More post options" -#~ msgstr "更多貼文選項" - #: src/view/screens/PreferencesThreads.tsx:82 msgid "Most-liked replies first" msgstr "最多按喜歡數優先" -#: src/view/com/auth/create/Step2.tsx:122 -#~ msgid "Must be at least 3 characters" -#~ msgstr "" - #: src/components/TagMenu/index.tsx:249 msgid "Mute" -msgstr "" +msgstr "靜音" #: src/components/TagMenu/index.web.tsx:105 msgid "Mute {truncatedTag}" -msgstr "" +msgstr "靜音 {truncatedTag}" #: src/view/com/profile/ProfileMenu.tsx:279 #: src/view/com/profile/ProfileMenu.tsx:286 @@ -2790,19 +2484,15 @@ msgstr "靜音帳號" #: src/components/TagMenu/index.tsx:209 msgid "Mute all {displayTag} posts" -msgstr "" - -#: src/components/TagMenu/index.tsx:211 -#~ msgid "Mute all {tag} posts" -#~ msgstr "" +msgstr "將所有 {displayTag} 貼文靜音" #: src/components/dialogs/MutedWords.tsx:148 msgid "Mute in tags only" -msgstr "" +msgstr "僅在標籤中靜音" #: src/components/dialogs/MutedWords.tsx:133 msgid "Mute in text & tags" -msgstr "" +msgstr "在文字和標籤中靜音" #: src/view/screens/ProfileList.tsx:461 #: src/view/screens/ProfileList.tsx:624 @@ -2813,10 +2503,6 @@ msgstr "靜音列表" msgid "Mute these accounts?" msgstr "靜音這些帳號?" -#: src/view/screens/ProfileList.tsx:279 -#~ msgid "Mute this List" -#~ msgstr "靜音這個列表" - #: src/components/dialogs/MutedWords.tsx:126 msgid "Mute this word in post text and tags" msgstr "在帖子文本和话题标签中隐藏该词" @@ -2833,7 +2519,7 @@ msgstr "靜音對話串" #: src/view/com/util/forms/PostDropdownBtn.tsx:267 #: src/view/com/util/forms/PostDropdownBtn.tsx:269 msgid "Mute words & tags" -msgstr "" +msgstr "靜音文字和標籤" #: src/view/com/lists/ListCard.tsx:102 msgid "Muted" @@ -2854,11 +2540,11 @@ msgstr "已靜音的帳號將不會在你的通知或時間線中顯示,被靜 #: src/lib/moderation/useModerationCauseDescription.ts:85 msgid "Muted by \"{0}\"" -msgstr "" +msgstr "被\"{0}\"靜音" #: src/screens/Moderation/index.tsx:231 msgid "Muted words & tags" -msgstr "" +msgstr "已靜音文字和標籤" #: src/view/screens/ProfileList.tsx:621 msgid "Muting is private. Muted accounts can interact with you, but you will not see their posts or receive notifications from them." @@ -2885,10 +2571,6 @@ msgstr "我儲存的訊息流" msgid "My Saved Feeds" msgstr "我儲存的訊息流" -#: src/view/com/auth/server-input/index.tsx:118 -#~ msgid "my-server.com" -#~ msgstr "my-server.com" - #: src/view/com/modals/AddAppPasswords.tsx:180 #: src/view/com/modals/CreateOrEditList.tsx:291 msgid "Name" @@ -2902,7 +2584,7 @@ msgstr "名稱是必填項" #: src/lib/moderation/useReportOptions.ts:78 #: src/lib/moderation/useReportOptions.ts:86 msgid "Name or Description Violates Community Standards" -msgstr "" +msgstr "名稱或描述違反社群標準" #: src/screens/Onboarding/index.tsx:25 msgid "Nature" @@ -2920,12 +2602,7 @@ msgstr "切換到你的個人檔案" #: src/components/ReportDialog/SelectReportOptionView.tsx:122 msgid "Need to report a copyright violation?" -msgstr "" - -#: src/view/com/modals/EmbedConsent.tsx:107 -#: src/view/com/modals/EmbedConsent.tsx:123 -#~ msgid "Never load embeds from {0}" -#~ msgstr "永不載入來自 {0} 的嵌入內容" +msgstr "需要檢舉侵權嗎?" #: src/view/com/auth/onboarding/WelcomeDesktop.tsx:72 #: src/view/com/auth/onboarding/WelcomeMobile.tsx:74 @@ -2936,13 +2613,9 @@ msgstr "永遠不會失去對你的跟隨者和資料的存取權。" msgid "Never lose access to your followers or data." msgstr "永遠不會失去對你的跟隨者或資料的存取權。" -#: src/components/dialogs/MutedWords.tsx:293 -#~ msgid "Nevermind" -#~ msgstr "" - #: src/view/com/modals/ChangeHandle.tsx:519 msgid "Nevermind, create a handle for me" -msgstr "" +msgstr "沒關係,為我創建一個帳號代碼" #: src/view/screens/Lists.tsx:76 msgctxt "action" @@ -3035,7 +2708,7 @@ msgstr "沒有描述" #: src/view/com/modals/ChangeHandle.tsx:405 msgid "No DNS Panel" -msgstr "" +msgstr "無 DNS 控制台" #: src/screens/Profile/Header/ProfileHeaderStandard.tsx:118 msgid "No longer following {0}" @@ -3043,7 +2716,7 @@ msgstr "不再跟隨 {0}" #: src/screens/Signup/StepHandle.tsx:114 msgid "No longer than 253 characters" -msgstr "" +msgstr "不超過 253 個字符" #: src/view/com/notifications/Feed.tsx:109 msgid "No notifications yet!" @@ -3080,11 +2753,11 @@ msgstr "沒有人" #: src/components/LikedByList.tsx:79 #: src/components/LikesDialog.tsx:99 msgid "Nobody has liked this yet. Maybe you should be the first!" -msgstr "" +msgstr "還沒有人喜歡這個,也許你應該成為第一個!" #: src/lib/moderation/useGlobalLabelStrings.ts:42 msgid "Non-sexual Nudity" -msgstr "" +msgstr "非情色內容裸體" #: src/view/com/modals/SelfLabel.tsx:135 msgid "Not Applicable." @@ -3104,7 +2777,7 @@ msgstr "暫時不需要" #: src/view/com/util/forms/PostDropdownBtn.tsx:342 #: src/view/com/util/post-ctrls/PostCtrls.tsx:246 msgid "Note about sharing" -msgstr "" +msgstr "關於分享的注意事項" #: src/screens/Moderation/index.tsx:540 msgid "Note: Bluesky is an open and public network. This setting only limits the visibility of your content on the Bluesky app and website, and other apps may not respect this setting. Your content may still be shown to logged-out users by other apps and websites." @@ -3126,19 +2799,15 @@ msgstr "裸露" #: src/lib/moderation/useReportOptions.ts:71 msgid "Nudity or adult content not labeled as such" -msgstr "" - -#: src/lib/moderation/useReportOptions.ts:71 -#~ msgid "Nudity or pornography not labeled as such" -#~ msgstr "" +msgstr "未貼上此類標籤的裸露或成人內容" #: src/screens/Signup/index.tsx:142 msgid "of" -msgstr "" +msgstr "of" #: src/lib/moderation/useLabelBehaviorDescription.ts:11 msgid "Off" -msgstr "" +msgstr "顯示" #: src/view/com/util/ErrorBoundary.tsx:49 msgid "Oh no!" @@ -3151,7 +2820,7 @@ msgstr "糟糕!發生了一些錯誤。" #: src/screens/Onboarding/StepModeration/AdultContentEnabledPref.tsx:126 #: src/screens/Profile/Header/ProfileHeaderLabeler.tsx:327 msgid "OK" -msgstr "" +msgstr "好的" #: src/screens/Login/PasswordUpdatedForm.tsx:44 msgid "Okay" @@ -3175,11 +2844,11 @@ msgstr "只有 {0} 可以回覆。" #: src/screens/Signup/StepHandle.tsx:97 msgid "Only contains letters, numbers, and hyphens" -msgstr "" +msgstr "只包含字母、數字和連字符" #: src/components/Lists.tsx:75 msgid "Oops, something went wrong!" -msgstr "" +msgstr "糟糕,發生了錯誤!" #: src/components/Lists.tsx:170 #: src/view/screens/AppPasswords.tsx:67 @@ -3191,10 +2860,6 @@ msgstr "糟糕!" msgid "Open" msgstr "開啟" -#: src/view/screens/Moderation.tsx:75 -#~ msgid "Open content filtering settings" -#~ msgstr "" - #: src/view/com/composer/Composer.tsx:491 #: src/view/com/composer/Composer.tsx:492 msgid "Open emoji picker" @@ -3202,7 +2867,7 @@ msgstr "開啟表情符號選擇器" #: src/view/screens/ProfileFeed.tsx:300 msgid "Open feed options menu" -msgstr "" +msgstr "開啟訊息流選項選單" #: src/view/screens/Settings/index.tsx:734 msgid "Open links with in-app browser" @@ -3210,11 +2875,7 @@ msgstr "在內建瀏覽器中開啟連結" #: src/screens/Moderation/index.tsx:227 msgid "Open muted words and tags settings" -msgstr "" - -#: src/view/screens/Moderation.tsx:92 -#~ msgid "Open muted words settings" -#~ msgstr "打开隐藏词设置" +msgstr "開啟靜音文字和標籤設定" #: src/view/com/home/HomeHeaderLayoutMobile.tsx:50 msgid "Open navigation" @@ -3222,7 +2883,7 @@ msgstr "開啟導覽" #: src/view/com/util/forms/PostDropdownBtn.tsx:183 msgid "Open post options menu" -msgstr "" +msgstr "開啟貼文選項選單" #: src/view/screens/Settings/index.tsx:828 #: src/view/screens/Settings/index.tsx:838 @@ -3231,7 +2892,7 @@ msgstr "開啟故事書頁面" #: src/view/screens/Settings/index.tsx:816 msgid "Open system log" -msgstr "" +msgstr "開啟系統日誌" #: src/view/com/util/forms/DropdownButton.tsx:154 msgid "Opens {numItems} options" @@ -3261,10 +2922,6 @@ msgstr "開啟可以更改的語言設定" msgid "Opens device photo gallery" msgstr "開啟裝置相簿" -#: src/view/com/profile/ProfileHeader.tsx:420 -#~ msgid "Opens editor for profile display name, avatar, background image, and description" -#~ msgstr "開啟個人資料(如名稱、頭貼、背景圖片、描述等)編輯器" - #: src/view/screens/Settings/index.tsx:669 msgid "Opens external embeds settings" msgstr "開啟外部嵌入設定" @@ -3273,25 +2930,13 @@ msgstr "開啟外部嵌入設定" #: src/view/com/auth/SplashScreen.tsx:68 #: src/view/com/auth/SplashScreen.web.tsx:97 msgid "Opens flow to create a new Bluesky account" -msgstr "" +msgstr "開始流程以建立新的 Bluesky 帳戶" #: src/view/com/auth/HomeLoggedOutCTA.tsx:75 #: src/view/com/auth/SplashScreen.tsx:83 #: src/view/com/auth/SplashScreen.web.tsx:112 msgid "Opens flow to sign into your existing Bluesky account" -msgstr "" - -#: src/view/com/profile/ProfileHeader.tsx:575 -#~ msgid "Opens followers list" -#~ msgstr "開啟跟隨者列表" - -#: src/view/com/profile/ProfileHeader.tsx:594 -#~ msgid "Opens following list" -#~ msgstr "開啟正在跟隨列表" - -#: src/view/screens/Settings.tsx:412 -#~ msgid "Opens invite code list" -#~ msgstr "開啟邀請碼列表" +msgstr "開啟流程以登入你現有的 Bluesky 帳戶" #: src/view/com/modals/InviteCodes.tsx:173 msgid "Opens list of invite codes" @@ -3299,27 +2944,23 @@ msgstr "開啟邀請碼列表" #: src/view/screens/Settings/index.tsx:798 msgid "Opens modal for account deletion confirmation. Requires email code" -msgstr "" - -#: src/view/screens/Settings/index.tsx:774 -#~ msgid "Opens modal for account deletion confirmation. Requires email code." -#~ msgstr "開啟用於帳號刪除確認的彈窗。需要電子郵件驗證碼。" +msgstr "開啟用於帳號刪除確認的彈窗。需要電子郵件驗證碼。" #: src/view/screens/Settings/index.tsx:756 msgid "Opens modal for changing your Bluesky password" -msgstr "" +msgstr "開啟用於修改你 Bluesky 密碼的彈窗" #: src/view/screens/Settings/index.tsx:718 msgid "Opens modal for choosing a new Bluesky handle" -msgstr "" +msgstr "開啟用於創建新 Bluesky 帳號代碼的彈窗" #: src/view/screens/Settings/index.tsx:779 msgid "Opens modal for downloading your Bluesky account data (repository)" -msgstr "" +msgstr "開啟用於下載 Bluesky 帳戶數據(存儲庫)的彈窗" #: src/view/screens/Settings/index.tsx:968 msgid "Opens modal for email verification" -msgstr "" +msgstr "開啟用於驗證電子郵件的彈窗" #: src/view/com/modals/ChangeHandle.tsx:282 msgid "Opens modal for using custom domain" @@ -3344,23 +2985,15 @@ msgstr "開啟包含所有已儲存訊息流的畫面" #: src/view/screens/Settings/index.tsx:696 msgid "Opens the app password settings" -msgstr "" - -#: src/view/screens/Settings/index.tsx:676 -#~ msgid "Opens the app password settings page" -#~ msgstr "開啟應用程式專用密碼設定頁面" +msgstr "開啟應用程式專用密碼設定的畫面" #: src/view/screens/Settings/index.tsx:554 msgid "Opens the Following feed preferences" -msgstr "" - -#: src/view/screens/Settings/index.tsx:535 -#~ msgid "Opens the home feed preferences" -#~ msgstr "開啟首頁訊息流設定偏好" +msgstr "開啟跟隨訊息流設定偏好" #: src/view/com/modals/LinkWarning.tsx:93 msgid "Opens the linked website" -msgstr "" +msgstr "開啟已連結的網站" #: src/view/screens/Settings/index.tsx:829 #: src/view/screens/Settings/index.tsx:839 @@ -3381,7 +3014,7 @@ msgstr "{0} 選項,共 {numItems} 個" #: src/components/ReportDialog/SubmitView.tsx:162 msgid "Optionally provide additional information below:" -msgstr "" +msgstr "以下是可選提供的额外信息:" #: src/view/com/modals/Threadgate.tsx:89 msgid "Or combine these options:" @@ -3389,16 +3022,12 @@ msgstr "或者選擇組合這些選項:" #: src/lib/moderation/useReportOptions.ts:25 msgid "Other" -msgstr "" +msgstr "其他" #: src/components/AccountList.tsx:73 msgid "Other account" msgstr "其他帳號" -#: src/view/com/modals/ServerInput.tsx:88 -#~ msgid "Other service" -#~ msgstr "其他服務" - #: src/view/com/composer/select-language/SelectLangBtn.tsx:91 msgid "Other..." msgstr "其他…" @@ -3421,7 +3050,7 @@ msgstr "密碼" #: src/view/com/modals/ChangePassword.tsx:142 msgid "Password Changed" -msgstr "" +msgstr "密碼已更改" #: src/screens/Login/index.tsx:157 msgid "Password updated" @@ -3451,10 +3080,6 @@ msgstr "相機的存取權限已被拒絕,請在系統設定中啟用。" msgid "Pets" msgstr "寵物" -#: src/view/com/auth/create/Step2.tsx:183 -#~ msgid "Phone number" -#~ msgstr "手機號碼" - #: src/view/com/modals/SelfLabel.tsx:121 msgid "Pictures meant for adults." msgstr "適合成年人的圖像。" @@ -3466,7 +3091,7 @@ msgstr "固定到首頁" #: src/view/screens/ProfileFeed.tsx:295 msgid "Pin to Home" -msgstr "" +msgstr "固定到首頁" #: src/view/screens/SavedFeeds.tsx:88 msgid "Pinned Feeds" @@ -3505,25 +3130,13 @@ msgstr "更改前請先確認你的電子郵件地址。這是電子郵件更新 msgid "Please enter a name for your app password. All spaces is not allowed." msgstr "請輸入應用程式專用密碼的名稱。所有空格均不允許使用。" -#: src/view/com/auth/create/Step2.tsx:206 -#~ msgid "Please enter a phone number that can receive SMS text messages." -#~ msgstr "請輸入可以接收簡訊的手機號碼。" - #: src/view/com/modals/AddAppPasswords.tsx:146 msgid "Please enter a unique name for this App Password or use our randomly generated one." msgstr "請輸入此應用程式專用密碼的唯一名稱,或使用我們提供的隨機生成名稱。" #: src/components/dialogs/MutedWords.tsx:67 msgid "Please enter a valid word, tag, or phrase to mute" -msgstr "" - -#: src/view/com/auth/create/state.ts:170 -#~ msgid "Please enter the code you received by SMS." -#~ msgstr "請輸入你收到的簡訊驗證碼。" - -#: src/view/com/auth/create/Step2.tsx:282 -#~ msgid "Please enter the verification code sent to {phoneNumberFormatted}." -#~ msgstr "請輸入發送到 {phoneNumberFormatted} 的驗證碼。" +msgstr "請輸入有效的詞語或標籤進行靜音" #: src/screens/Signup/state.ts:220 msgid "Please enter your email." @@ -3535,12 +3148,7 @@ msgstr "請輸入你的密碼:" #: src/components/moderation/LabelsOnMeDialog.tsx:221 msgid "Please explain why you think this label was incorrectly applied by {0}" -msgstr "" - -#: src/view/com/modals/AppealLabel.tsx:72 -#: src/view/com/modals/AppealLabel.tsx:75 -#~ msgid "Please tell us why you think this content warning was incorrectly applied!" -#~ msgstr "請告訴我們你認為這個內容警告標示有誤的原因!" +msgstr "請解釋你認為 {0} 不正確套用此標籤的原因" #: src/view/com/modals/VerifyEmail.tsx:101 msgid "Please Verify Your Email" @@ -3558,10 +3166,6 @@ msgstr "政治" msgid "Porn" msgstr "情色內容" -#: src/lib/moderation/useGlobalLabelStrings.ts:34 -#~ msgid "Pornography" -#~ msgstr "" - #: src/view/com/composer/Composer.tsx:367 #: src/view/com/composer/Composer.tsx:375 msgctxt "action" @@ -3594,12 +3198,12 @@ msgstr "貼文已隱藏" #: src/components/moderation/ModerationDetailsDialog.tsx:97 #: src/lib/moderation/useModerationCauseDescription.ts:99 msgid "Post Hidden by Muted Word" -msgstr "" +msgstr "貼文因靜音詞彙設定而被靜音" #: src/components/moderation/ModerationDetailsDialog.tsx:100 #: src/lib/moderation/useModerationCauseDescription.ts:108 msgid "Post Hidden by You" -msgstr "" +msgstr "你靜音了這則貼文" #: src/view/com/composer/select-language/SelectLangBtn.tsx:87 msgid "Post language" @@ -3624,7 +3228,7 @@ msgstr "貼文" #: src/components/dialogs/MutedWords.tsx:89 msgid "Posts can be muted based on their text, their tags, or both." -msgstr "" +msgstr "貼文可以根据所包含的文字和標籤来设定静音。" #: src/view/com/posts/FeedErrorMessage.tsx:64 msgid "Posts hidden" @@ -3636,13 +3240,13 @@ msgstr "潛在誤導性連結" #: src/components/forms/HostingProvider.tsx:45 msgid "Press to change hosting provider" -msgstr "" +msgstr "按下以更改主機提供商" #: src/components/Error.tsx:74 #: src/components/Lists.tsx:80 #: src/screens/Signup/index.tsx:186 msgid "Press to retry" -msgstr "" +msgstr "按下以重試" #: src/view/com/lightbox/Lightbox.web.tsx:150 msgid "Previous image" @@ -3738,7 +3342,7 @@ msgstr "比率" #: src/view/screens/Search/Search.tsx:777 msgid "Recent Searches" -msgstr "" +msgstr "最近的搜尋結果" #: src/view/com/auth/onboarding/RecommendedFeeds.tsx:116 msgid "Recommended Feeds" @@ -3757,21 +3361,17 @@ msgstr "推薦的使用者" msgid "Remove" msgstr "移除" -#: src/view/com/feeds/FeedSourceCard.tsx:108 -#~ msgid "Remove {0} from my feeds?" -#~ msgstr "將 {0} 從我的訊息流移除?" - #: src/view/com/util/AccountDropdownBtn.tsx:22 msgid "Remove account" msgstr "刪除帳號" #: src/view/com/util/UserAvatar.tsx:358 msgid "Remove Avatar" -msgstr "" +msgstr "刪除頭像" #: src/view/com/util/UserBanner.tsx:148 msgid "Remove Banner" -msgstr "" +msgstr "刪除橫幅圖片" #: src/view/com/posts/FeedErrorMessage.tsx:160 msgid "Remove feed" @@ -3779,7 +3379,7 @@ msgstr "刪除訊息流" #: src/view/com/posts/FeedErrorMessage.tsx:201 msgid "Remove feed?" -msgstr "" +msgstr "刪除訊息流?" #: src/view/com/feeds/FeedSourceCard.tsx:173 #: src/view/com/feeds/FeedSourceCard.tsx:233 @@ -3790,7 +3390,7 @@ msgstr "從我的訊息流中刪除" #: src/view/com/feeds/FeedSourceCard.tsx:278 msgid "Remove from my feeds?" -msgstr "" +msgstr "從我的訊息流中刪除?" #: src/view/com/composer/photos/Gallery.tsx:167 msgid "Remove image" @@ -3802,23 +3402,15 @@ msgstr "刪除圖片預覽" #: src/components/dialogs/MutedWords.tsx:329 msgid "Remove mute word from your list" -msgstr "" +msgstr "從你的列表中移除靜音詞" #: src/view/com/modals/Repost.tsx:48 msgid "Remove repost" msgstr "刪除轉發" -#: src/view/com/feeds/FeedSourceCard.tsx:175 -#~ msgid "Remove this feed from my feeds?" -#~ msgstr "將這個訊息流從我的訊息流列表中刪除?" - #: src/view/com/posts/FeedErrorMessage.tsx:202 msgid "Remove this feed from your saved feeds" -msgstr "" - -#: src/view/com/posts/FeedErrorMessage.tsx:132 -#~ msgid "Remove this feed from your saved feeds?" -#~ msgstr "將這個訊息流從儲存的訊息流列表中刪除?" +msgstr "將這個訊息流從儲存的訊息流列表中刪除" #: src/view/com/modals/ListAddRemoveUsers.tsx:199 #: src/view/com/modals/UserAddRemoveLists.tsx:152 @@ -3831,7 +3423,7 @@ msgstr "從我的訊息流中刪除" #: src/view/screens/ProfileFeed.tsx:209 msgid "Removed from your feeds" -msgstr "" +msgstr "從你的訊息流中刪除" #: src/view/com/composer/ExternalEmbed.tsx:71 msgid "Removes default thumbnail from {0}" @@ -3860,10 +3452,6 @@ msgctxt "description" msgid "Reply to <0/>" msgstr "回覆 <0/>" -#: src/view/com/modals/report/Modal.tsx:166 -#~ msgid "Report {collectionName}" -#~ msgstr "檢舉 {collectionName}" - #: src/view/com/profile/ProfileMenu.tsx:319 #: src/view/com/profile/ProfileMenu.tsx:322 msgid "Report Account" @@ -3871,7 +3459,7 @@ msgstr "檢舉帳號" #: src/components/ReportDialog/index.tsx:49 msgid "Report dialog" -msgstr "" +msgstr "檢舉頁" #: src/view/screens/ProfileFeed.tsx:352 #: src/view/screens/ProfileFeed.tsx:354 @@ -3889,23 +3477,23 @@ msgstr "檢舉貼文" #: src/components/ReportDialog/SelectReportOptionView.tsx:42 msgid "Report this content" -msgstr "" +msgstr "檢舉這個內容" #: src/components/ReportDialog/SelectReportOptionView.tsx:55 msgid "Report this feed" -msgstr "" +msgstr "檢舉這個訊息流" #: src/components/ReportDialog/SelectReportOptionView.tsx:52 msgid "Report this list" -msgstr "" +msgstr "檢舉這個列表" #: src/components/ReportDialog/SelectReportOptionView.tsx:49 msgid "Report this post" -msgstr "" +msgstr "檢舉這則貼文" #: src/components/ReportDialog/SelectReportOptionView.tsx:46 msgid "Report this user" -msgstr "" +msgstr "檢舉這個使用者" #: src/view/com/modals/Repost.tsx:44 #: src/view/com/modals/Repost.tsx:49 @@ -3949,10 +3537,6 @@ msgstr "轉發這條貼文" msgid "Request Change" msgstr "請求變更" -#: src/view/com/auth/create/Step2.tsx:219 -#~ msgid "Request code" -#~ msgstr "請求碼" - #: src/view/com/modals/ChangePassword.tsx:241 #: src/view/com/modals/ChangePassword.tsx:243 msgid "Request Code" @@ -3974,10 +3558,6 @@ msgstr "重設碼" msgid "Reset Code" msgstr "重設碼" -#: src/view/screens/Settings/index.tsx:824 -#~ msgid "Reset onboarding" -#~ msgstr "重設初始設定進行狀態" - #: src/view/screens/Settings/index.tsx:858 #: src/view/screens/Settings/index.tsx:861 msgid "Reset onboarding state" @@ -3987,14 +3567,10 @@ msgstr "重設初始設定進行狀態" msgid "Reset password" msgstr "重設密碼" -#: src/view/screens/Settings/index.tsx:814 -#~ msgid "Reset preferences" -#~ msgstr "重設偏好設定" - #: src/view/screens/Settings/index.tsx:848 #: src/view/screens/Settings/index.tsx:851 msgid "Reset preferences state" -msgstr "重設偏好設定狀態" +msgstr "重設設定偏好狀態" #: src/view/screens/Settings/index.tsx:859 msgid "Resets the onboarding state" @@ -4002,7 +3578,7 @@ msgstr "重設初始設定狀態" #: src/view/screens/Settings/index.tsx:849 msgid "Resets the preferences state" -msgstr "重設偏好設定狀態" +msgstr "重設設定偏好狀態" #: src/screens/Login/LoginForm.tsx:235 msgid "Retries login" @@ -4025,10 +3601,6 @@ msgstr "重試上次出錯的操作" msgid "Retry" msgstr "重試" -#: src/view/com/auth/create/Step2.tsx:247 -#~ msgid "Retry." -#~ msgstr "重試。" - #: src/components/Error.tsx:86 #: src/view/screens/ProfileList.tsx:917 msgid "Return to previous page" @@ -4036,16 +3608,12 @@ msgstr "返回上一頁" #: src/view/screens/NotFound.tsx:59 msgid "Returns to home page" -msgstr "" +msgstr "返回首頁" #: src/view/screens/NotFound.tsx:58 #: src/view/screens/ProfileFeed.tsx:113 msgid "Returns to previous page" -msgstr "" - -#: src/view/shell/desktop/RightNav.tsx:55 -#~ msgid "SANDBOX. Posts and accounts are not permanent." -#~ msgstr "沙盒模式。貼文和帳號不會永久儲存。" +msgstr "返回上一頁" #: src/components/dialogs/BirthDateSettings.tsx:125 #: src/view/com/modals/ChangeHandle.tsx:174 @@ -4066,7 +3634,7 @@ msgstr "儲存替代文字" #: src/components/dialogs/BirthDateSettings.tsx:119 msgid "Save birthday" -msgstr "" +msgstr "儲存生日" #: src/view/com/modals/EditProfile.tsx:233 msgid "Save Changes" @@ -4083,7 +3651,7 @@ msgstr "儲存圖片裁剪" #: src/view/screens/ProfileFeed.tsx:336 #: src/view/screens/ProfileFeed.tsx:342 msgid "Save to my feeds" -msgstr "" +msgstr "儲存到我的訊息流" #: src/view/screens/SavedFeeds.tsx:122 msgid "Saved Feeds" @@ -4091,11 +3659,11 @@ msgstr "已儲存訊息流" #: src/view/com/lightbox/Lightbox.tsx:81 msgid "Saved to your camera roll." -msgstr "" +msgstr "儲存到你的相機膠卷。" #: src/view/screens/ProfileFeed.tsx:213 msgid "Saved to your feeds" -msgstr "" +msgstr "儲存到你的訊息流" #: src/view/com/modals/EditProfile.tsx:226 msgid "Saves any changes to your profile" @@ -4107,7 +3675,7 @@ msgstr "儲存帳號代碼更改至 {handle}" #: src/view/com/modals/crop-image/CropImage.web.tsx:146 msgid "Saves image crop settings" -msgstr "" +msgstr "保存圖片裁剪設定" #: src/screens/Onboarding/index.tsx:36 msgid "Science" @@ -4141,19 +3709,11 @@ msgstr "搜尋「{query}」" #: src/components/TagMenu/index.tsx:145 msgid "Search for all posts by @{authorHandle} with tag {displayTag}" -msgstr "" - -#: src/components/TagMenu/index.tsx:145 -#~ msgid "Search for all posts by @{authorHandle} with tag {tag}" -#~ msgstr "" +msgstr "搜尋所有由 @{authorHandle} 發佈並具有標籤 {displayTag} 的貼文" #: src/components/TagMenu/index.tsx:94 msgid "Search for all posts with tag {displayTag}" -msgstr "" - -#: src/components/TagMenu/index.tsx:90 -#~ msgid "Search for all posts with tag {tag}" -#~ msgstr "" +msgstr "搜尋所有具有標籤 {displayTag} 的貼文" #: src/view/com/auth/LoggedOut.tsx:105 #: src/view/com/auth/LoggedOut.tsx:106 @@ -4167,27 +3727,19 @@ msgstr "所需的安全步驟" #: src/components/TagMenu/index.web.tsx:66 msgid "See {truncatedTag} posts" -msgstr "" +msgstr "查看 {truncatedTag} 的貼文" #: src/components/TagMenu/index.web.tsx:83 msgid "See {truncatedTag} posts by user" -msgstr "" +msgstr "查看使用者的 {truncatedTag} 貼文" #: src/components/TagMenu/index.tsx:128 msgid "See <0>{displayTag} posts" -msgstr "" +msgstr "查看 <0>{displayTag} 的貼文" #: src/components/TagMenu/index.tsx:187 msgid "See <0>{displayTag} posts by this user" -msgstr "" - -#: src/components/TagMenu/index.tsx:128 -#~ msgid "See <0>{tag} posts" -#~ msgstr "" - -#: src/components/TagMenu/index.tsx:189 -#~ msgid "See <0>{tag} posts by this user" -#~ msgstr "" +msgstr "查看這個使用者的 <0>{displayTag} 貼文" #: src/view/screens/SavedFeeds.tsx:163 msgid "See this guide" @@ -4203,11 +3755,7 @@ msgstr "選擇 {item}" #: src/screens/Login/ChooseAccountForm.tsx:61 msgid "Select account" -msgstr "" - -#: src/view/com/modals/ServerInput.tsx:75 -#~ msgid "Select Bluesky Social" -#~ msgstr "選擇 Bluesky Social" +msgstr "選擇帳號" #: src/screens/Login/index.tsx:120 msgid "Select from an existing account" @@ -4215,28 +3763,23 @@ msgstr "從現有帳號中選擇" #: src/view/screens/LanguageSettings.tsx:299 msgid "Select languages" -msgstr "" +msgstr "選擇語言" #: src/components/ReportDialog/SelectLabelerView.tsx:30 msgid "Select moderator" -msgstr "" +msgstr "選擇限制服務提供者" #: src/view/com/util/Selector.tsx:107 msgid "Select option {i} of {numItems}" msgstr "選擇 {numItems} 個項目中的第 {i} 項" -#: src/view/com/auth/create/Step1.tsx:96 -#: src/view/com/auth/login/LoginForm.tsx:153 -#~ msgid "Select service" -#~ msgstr "選擇服務" - #: src/screens/Onboarding/StepSuggestedAccounts/index.tsx:52 msgid "Select some accounts below to follow" msgstr "在下面選擇一些要跟隨的帳號" #: src/components/ReportDialog/SubmitView.tsx:135 msgid "Select the moderation service(s) to report to" -msgstr "" +msgstr "選擇要檢舉的限制服務提供者" #: src/view/com/auth/server-input/index.tsx:82 msgid "Select the service that hosts your data." @@ -4254,26 +3797,18 @@ msgstr "選擇你想看到(或不想看到)的內容,剩下的由我們來 msgid "Select which languages you want your subscribed feeds to include. If none are selected, all languages will be shown." msgstr "選擇你希望訂閱訊息流中所包含的語言。未選擇任何語言時會預設顯示所有語言。" -#: src/view/screens/LanguageSettings.tsx:98 -#~ msgid "Select your app language for the default text to display in the app" -#~ msgstr "選擇應用程式中顯示預設文字的語言" - #: src/view/screens/LanguageSettings.tsx:98 msgid "Select your app language for the default text to display in the app." -msgstr "" +msgstr "選擇你應用程式中要顯示的默認文字的語言。" #: src/screens/Signup/StepInfo/index.tsx:133 msgid "Select your date of birth" -msgstr "" +msgstr "選擇你的出生日期" #: src/screens/Onboarding/StepInterests/index.tsx:200 msgid "Select your interests from the options below" msgstr "下面選擇你感興趣的選項" -#: src/view/com/auth/create/Step2.tsx:155 -#~ msgid "Select your phone's country" -#~ msgstr "選擇你的電話區號" - #: src/view/screens/LanguageSettings.tsx:190 msgid "Select your preferred language for translations in your feed." msgstr "選擇你在訂閱訊息流中希望進行翻譯的目標語言偏好。" @@ -4308,15 +3843,11 @@ msgstr "提交意見" #: src/components/ReportDialog/SubmitView.tsx:214 #: src/components/ReportDialog/SubmitView.tsx:218 msgid "Send report" -msgstr "提交舉報" - -#: src/view/com/modals/report/SendReportButton.tsx:45 -#~ msgid "Send Report" -#~ msgstr "提交舉報" +msgstr "提交檢舉" #: src/components/ReportDialog/SelectLabelerView.tsx:44 msgid "Send report to {0}" -msgstr "" +msgstr "將檢舉提交至 {0}" #: src/view/com/modals/DeleteAccount.tsx:132 msgid "Sends email with confirmation code for account deletion" @@ -4326,48 +3857,14 @@ msgstr "發送包含帳號刪除確認碼的電子郵件" msgid "Server address" msgstr "伺服器地址" -#: src/view/com/modals/ContentFilteringSettings.tsx:311 -#~ msgid "Set {value} for {labelGroup} content moderation policy" -#~ msgstr "將 {labelGroup} 內容審核政策設為 {value}" - -#: src/view/com/modals/ContentFilteringSettings.tsx:160 -#: src/view/com/modals/ContentFilteringSettings.tsx:179 -#~ msgctxt "action" -#~ msgid "Set Age" -#~ msgstr "設定年齡" - #: src/screens/Moderation/index.tsx:304 msgid "Set birthdate" -msgstr "" - -#: src/view/screens/Settings/index.tsx:488 -#~ msgid "Set color theme to dark" -#~ msgstr "設定主題為深色模式" - -#: src/view/screens/Settings/index.tsx:481 -#~ msgid "Set color theme to light" -#~ msgstr "設定主題為亮色模式" - -#: src/view/screens/Settings/index.tsx:475 -#~ msgid "Set color theme to system setting" -#~ msgstr "設定主題跟隨系統設定" - -#: src/view/screens/Settings/index.tsx:514 -#~ msgid "Set dark theme to the dark theme" -#~ msgstr "設定深色模式至深黑" - -#: src/view/screens/Settings/index.tsx:507 -#~ msgid "Set dark theme to the dim theme" -#~ msgstr "設定深色模式至暗淡" +msgstr "設定生日" #: src/screens/Login/SetNewPasswordForm.tsx:102 msgid "Set new password" msgstr "設定新密碼" -#: src/view/com/auth/create/Step1.tsx:202 -#~ msgid "Set password" -#~ msgstr "設定密碼" - #: src/view/screens/PreferencesFollowingFeed.tsx:225 msgid "Set this setting to \"No\" to hide all quote posts from your feed. Reposts will still be visible." msgstr "將此設定項設為「關」會隱藏來自訂閱訊息流的所有引用貼文。轉發仍將可見。" @@ -4384,13 +3881,9 @@ msgstr "將此設定項設為「關」以隱藏來自訂閱訊息流的所有轉 msgid "Set this setting to \"Yes\" to show replies in a threaded view. This is an experimental feature." msgstr "將此設定項設為「開」以在分層視圖中顯示回覆。這是一個實驗性功能。" -#: src/view/screens/PreferencesHomeFeed.tsx:261 -#~ msgid "Set this setting to \"Yes\" to show samples of your saved feeds in your following feed. This is an experimental feature." -#~ msgstr "將此設定項設為「開」以在跟隨訊息流中顯示已儲存訊息流的樣本。這是一個實驗性功能。" - #: src/view/screens/PreferencesFollowingFeed.tsx:261 msgid "Set this setting to \"Yes\" to show samples of your saved feeds in your Following feed. This is an experimental feature." -msgstr "" +msgstr "將此設定為「是」以在你的追蹤訊息流中顯示你保存的訊息流。這是一個實驗性功能。" #: src/screens/Onboarding/Layout.tsx:48 msgid "Set up your account" @@ -4402,48 +3895,39 @@ msgstr "設定 Bluesky 使用者名稱" #: src/view/screens/Settings/index.tsx:507 msgid "Sets color theme to dark" -msgstr "" +msgstr "將色彩主題設定為深色" #: src/view/screens/Settings/index.tsx:500 msgid "Sets color theme to light" -msgstr "" +msgstr "將色彩主題設定為亮色" #: src/view/screens/Settings/index.tsx:494 msgid "Sets color theme to system setting" -msgstr "" +msgstr "將色彩主題設定為跟隨系統設定" #: src/view/screens/Settings/index.tsx:533 msgid "Sets dark theme to the dark theme" -msgstr "" +msgstr "將深色主題設定為深色" #: src/view/screens/Settings/index.tsx:526 msgid "Sets dark theme to the dim theme" -msgstr "" +msgstr "將深色主題設定為暗淡" #: src/screens/Login/ForgotPasswordForm.tsx:113 msgid "Sets email for password reset" msgstr "設定用於重設密碼的電子郵件" -#: src/view/com/auth/login/ForgotPasswordForm.tsx:122 -#~ msgid "Sets hosting provider for password reset" -#~ msgstr "設定用於密碼重設的主機提供商資訊" - #: src/view/com/modals/crop-image/CropImage.web.tsx:124 msgid "Sets image aspect ratio to square" -msgstr "" +msgstr "將圖片寬高比設定為正方形" #: src/view/com/modals/crop-image/CropImage.web.tsx:114 msgid "Sets image aspect ratio to tall" -msgstr "" +msgstr "將圖像的寬高比設定為高" #: src/view/com/modals/crop-image/CropImage.web.tsx:104 msgid "Sets image aspect ratio to wide" -msgstr "" - -#: src/view/com/auth/create/Step1.tsx:97 -#: src/view/com/auth/login/LoginForm.tsx:154 -#~ msgid "Sets server for the Bluesky client" -#~ msgstr "設定 Bluesky 用戶端的伺服器" +msgstr "將圖像的寬高比設定為寬" #: src/Navigation.tsx:139 #: src/view/screens/Settings/index.tsx:313 @@ -4459,7 +3943,7 @@ msgstr "性行為或性暗示裸露。" #: src/lib/moderation/useGlobalLabelStrings.ts:38 msgid "Sexually Suggestive" -msgstr "" +msgstr "性暗示" #: src/view/com/lightbox/Lightbox.tsx:141 msgctxt "action" @@ -4479,7 +3963,7 @@ msgstr "分享" #: src/view/com/util/forms/PostDropdownBtn.tsx:347 #: src/view/com/util/post-ctrls/PostCtrls.tsx:251 msgid "Share anyway" -msgstr "" +msgstr "仍然分享" #: src/view/screens/ProfileFeed.tsx:362 #: src/view/screens/ProfileFeed.tsx:364 @@ -4489,11 +3973,11 @@ msgstr "分享訊息流" #: src/view/com/modals/LinkWarning.tsx:89 #: src/view/com/modals/LinkWarning.tsx:95 msgid "Share Link" -msgstr "" +msgstr "分享連結" #: src/view/com/modals/LinkWarning.tsx:92 msgid "Shares the linked website" -msgstr "" +msgstr "分享連結的網站" #: src/components/moderation/ContentHider.tsx:115 #: src/components/moderation/LabelPreference.tsx:136 @@ -4515,15 +3999,11 @@ msgstr "仍然顯示" #: src/lib/moderation/useLabelBehaviorDescription.ts:27 #: src/lib/moderation/useLabelBehaviorDescription.ts:63 msgid "Show badge" -msgstr "" +msgstr "顯示徽章" #: src/lib/moderation/useLabelBehaviorDescription.ts:61 msgid "Show badge and filter from feeds" -msgstr "" - -#: src/view/com/modals/EmbedConsent.tsx:87 -#~ msgid "Show embeds from {0}" -#~ msgstr "顯示來自 {0} 的嵌入內容" +msgstr "顯示徽章並從訊息流中篩選" #: src/screens/Profile/Header/ProfileHeaderStandard.tsx:200 msgid "Show follows similar to {0}" @@ -4594,15 +4074,11 @@ msgstr "顯示使用者" #: src/lib/moderation/useLabelBehaviorDescription.ts:58 msgid "Show warning" -msgstr "" +msgstr "顯示警告" #: src/lib/moderation/useLabelBehaviorDescription.ts:56 msgid "Show warning and filter from feeds" -msgstr "" - -#: src/view/com/profile/ProfileHeader.tsx:462 -#~ msgid "Shows a list of users similar to this user." -#~ msgstr "顯示與該使用者相似的使用者列表。" +msgstr "顯示警告並從訊息流中篩選" #: src/view/com/post-thread/PostThreadFollowBtn.tsx:130 msgid "Shows posts from {0} in your feed" @@ -4629,12 +4105,6 @@ msgstr "在你的訊息流中顯示來自 {0} 的貼文" msgid "Sign in" msgstr "登入" -#: src/view/com/auth/HomeLoggedOutCTA.tsx:82 -#: src/view/com/auth/SplashScreen.tsx:86 -#: src/view/com/auth/SplashScreen.web.tsx:91 -#~ msgid "Sign In" -#~ msgstr "登入" - #: src/components/AccountList.tsx:109 msgid "Sign in as {0}" msgstr "以 {0} 登入" @@ -4643,10 +4113,6 @@ msgstr "以 {0} 登入" msgid "Sign in as..." msgstr "登入為…" -#: src/view/com/auth/login/LoginForm.tsx:140 -#~ msgid "Sign into" -#~ msgstr "登入到" - #: src/view/screens/Settings/index.tsx:107 #: src/view/screens/Settings/index.tsx:110 msgid "Sign out" @@ -4681,10 +4147,6 @@ msgstr "登入身分" msgid "Signed in as @{0}" msgstr "以 @{0} 身分登入" -#: src/view/com/modals/SwitchAccount.tsx:70 -#~ msgid "Signs {0} out of Bluesky" -#~ msgstr "從 {0} 登出 Bluesky" - #: src/screens/Onboarding/StepInterests/index.tsx:239 #: src/screens/Onboarding/StepSuggestedAccounts/index.tsx:203 #: src/view/com/auth/onboarding/WelcomeMobile.tsx:35 @@ -4695,31 +4157,15 @@ msgstr "跳過" msgid "Skip this flow" msgstr "跳過此流程" -#: src/view/com/auth/create/Step2.tsx:82 -#~ msgid "SMS verification" -#~ msgstr "簡訊驗證" - #: src/screens/Onboarding/index.tsx:40 msgid "Software Dev" msgstr "軟體開發" -#: src/view/com/modals/ProfilePreview.tsx:62 -#~ msgid "Something went wrong and we're not sure what." -#~ msgstr "發生了一些問題,我們不確定是什麼原因。" - #: src/components/ReportDialog/index.tsx:59 #: src/screens/Moderation/index.tsx:114 #: src/screens/Profile/Sections/Labels.tsx:76 msgid "Something went wrong, please try again." -msgstr "" - -#: src/components/Lists.tsx:203 -#~ msgid "Something went wrong!" -#~ msgstr "發生了一些問題!" - -#: src/view/com/modals/Waitlist.tsx:51 -#~ msgid "Something went wrong. Check your email and try again." -#~ msgstr "發生了一些問題。請檢查你的電子郵件,然後重試。" +msgstr "發生了一些問題,請重試。" #: src/App.native.tsx:66 msgid "Sorry! Your session expired. Please log in again." @@ -4735,15 +4181,15 @@ msgstr "對同一貼文的回覆進行排序:" #: src/components/moderation/LabelsOnMeDialog.tsx:146 msgid "Source:" -msgstr "" +msgstr "來源:" #: src/lib/moderation/useReportOptions.ts:65 msgid "Spam" -msgstr "" +msgstr "垃圾訊息" #: src/lib/moderation/useReportOptions.ts:53 msgid "Spam; excessive mentions or replies" -msgstr "" +msgstr "垃圾訊息;過多的提及或回复" #: src/screens/Onboarding/index.tsx:30 msgid "Sports" @@ -4753,21 +4199,13 @@ msgstr "運動" msgid "Square" msgstr "方塊" -#: src/view/com/modals/ServerInput.tsx:62 -#~ msgid "Staging" -#~ msgstr "臨時" - #: src/view/screens/Settings/index.tsx:903 msgid "Status page" msgstr "狀態頁" #: src/screens/Signup/index.tsx:142 msgid "Step" -msgstr "" - -#: src/view/com/auth/create/StepHeader.tsx:22 -#~ msgid "Step {0} of {numSteps}" -#~ msgstr "第 {0} 步,共 {numSteps} 步" +msgstr "Step" #: src/view/screens/Settings/index.tsx:292 msgid "Storage cleared, you need to restart the app now." @@ -4789,11 +4227,11 @@ msgstr "訂閱" #: src/screens/Profile/Sections/Labels.tsx:180 msgid "Subscribe to @{0} to use these labels:" -msgstr "" +msgstr "訂閱 @{0} 以使用這些標籤:" #: src/screens/Profile/Header/ProfileHeaderLabeler.tsx:221 msgid "Subscribe to Labeler" -msgstr "" +msgstr "訂閱標籤者" #: src/screens/Onboarding/StepAlgoFeeds/FeedCard.tsx:172 #: src/screens/Onboarding/StepAlgoFeeds/FeedCard.tsx:307 @@ -4802,7 +4240,7 @@ msgstr "訂閱 {0} 訊息流" #: src/screens/Profile/Header/ProfileHeaderLabeler.tsx:184 msgid "Subscribe to this labeler" -msgstr "" +msgstr "訂閱這個標籤者" #: src/view/screens/ProfileList.tsx:586 msgid "Subscribe to this list" @@ -4826,10 +4264,6 @@ msgstr "建議" msgid "Support" msgstr "支援" -#: src/view/com/modals/ProfilePreview.tsx:110 -#~ msgid "Swipe up to see more" -#~ msgstr "向上滑動查看更多" - #: src/components/dialogs/SwitchAccount.tsx:46 #: src/components/dialogs/SwitchAccount.tsx:49 msgid "Switch Account" @@ -4853,15 +4287,11 @@ msgstr "系統日誌" #: src/components/dialogs/MutedWords.tsx:323 msgid "tag" -msgstr "" +msgstr "標籤" #: src/components/TagMenu/index.tsx:78 msgid "Tag menu: {displayTag}" -msgstr "" - -#: src/components/TagMenu/index.tsx:74 -#~ msgid "Tag menu: {tag}" -#~ msgstr "" +msgstr "標籤選單:{displayTag}" #: src/view/com/modals/crop-image/CropImage.web.tsx:113 msgid "Tall" @@ -4891,7 +4321,7 @@ msgstr "服務條款" #: src/lib/moderation/useReportOptions.ts:79 #: src/lib/moderation/useReportOptions.ts:87 msgid "Terms used violate community standards" -msgstr "" +msgstr "所使用的文字違反了社群標準" #: src/components/dialogs/MutedWords.tsx:323 msgid "text" @@ -4903,11 +4333,11 @@ msgstr "文字輸入框" #: src/components/ReportDialog/SubmitView.tsx:78 msgid "Thank you. Your report has been sent." -msgstr "" +msgstr "謝謝,你的檢舉已提交。" #: src/view/com/modals/ChangeHandle.tsx:465 msgid "That contains the following:" -msgstr "" +msgstr "其中包含以下內容:" #: src/screens/Signup/index.tsx:84 msgid "That handle is already taken." @@ -4920,7 +4350,7 @@ msgstr "解除封鎖後,該帳號將能夠與你互動。" #: src/components/moderation/ModerationDetailsDialog.tsx:127 msgid "the author" -msgstr "" +msgstr "作者" #: src/view/screens/CommunityGuidelines.tsx:36 msgid "The Community Guidelines have been moved to <0/>" @@ -4932,11 +4362,11 @@ msgstr "版權政策已移動到 <0/>" #: src/components/moderation/LabelsOnMeDialog.tsx:48 msgid "The following labels were applied to your account." -msgstr "" +msgstr "以下標籤已套用到你的帳戶。" #: src/components/moderation/LabelsOnMeDialog.tsx:49 msgid "The following labels were applied to your content." -msgstr "" +msgstr "以下標籤已套用到你的內容。" #: src/screens/Onboarding/Layout.tsx:58 msgid "The following steps will help customize your Bluesky experience." @@ -4945,7 +4375,7 @@ msgstr "以下步驟將幫助自訂你的 Bluesky 體驗。" #: src/view/com/post-thread/PostThread.tsx:153 #: src/view/com/post-thread/PostThread.tsx:165 msgid "The post may have been deleted." -msgstr "此貼文可能已被刪除。" +msgstr "這則貼文可能已被刪除。" #: src/view/screens/PrivacyPolicy.tsx:33 msgid "The Privacy Policy has been moved to <0/>" @@ -5010,11 +4440,11 @@ msgstr "取得列表時發生問題,點擊這裡重試。" #: src/components/ReportDialog/SubmitView.tsx:83 msgid "There was an issue sending your report. Please check your internet connection." -msgstr "" +msgstr "提交你的檢舉時出現問題,請檢查你的網路連線。" #: src/screens/Onboarding/StepModeration/AdultContentEnabledPref.tsx:65 msgid "There was an issue syncing your preferences with the server" -msgstr "與伺服器同步偏好設定時發生問題" +msgstr "與伺服器同步設定偏好時發生問題" #: src/view/screens/AppPasswords.tsx:68 msgid "There was an issue with fetching your app passwords" @@ -5049,10 +4479,6 @@ msgstr "應用程式中發生了意外問題。請告訴我們是否發生在你 msgid "There's been a rush of new users to Bluesky! We'll activate your account as soon as we can." msgstr "Bluesky 迎來了大量新使用者!我們將儘快啟用你的帳號。" -#: src/view/com/auth/create/Step2.tsx:55 -#~ msgid "There's something wrong with this number. Please choose your country and enter your full phone number!" -#~ msgstr "電話號碼有誤,請選擇區號並輸入完整的電話號碼!" - #: src/screens/Onboarding/StepSuggestedAccounts/index.tsx:146 msgid "These are popular accounts you might like:" msgstr "這裡是一些受歡迎的帳號,你可能會喜歡:" @@ -5067,15 +4493,15 @@ msgstr "此帳號要求使用者登入後才能查看其個人資料。" #: src/components/moderation/LabelsOnMeDialog.tsx:204 msgid "This appeal will be sent to <0>{0}." -msgstr "" +msgstr "此申訴將被提交至 <0>{0}。" #: src/lib/moderation/useGlobalLabelStrings.ts:19 msgid "This content has been hidden by the moderators." -msgstr "" +msgstr "此內容已被限制提供者隱藏。" #: src/lib/moderation/useGlobalLabelStrings.ts:24 msgid "This content has received a general warning from moderators." -msgstr "" +msgstr "此內容已套用限制提供者所設定的一般警告。" #: src/components/dialogs/EmbedConsent.tsx:64 msgid "This content is hosted by {0}. Do you want to enable external media?" @@ -5090,13 +4516,9 @@ msgstr "由於其中一個使用者封鎖了另一個使用者,無法查看此 msgid "This content is not viewable without a Bluesky account." msgstr "沒有 Bluesky 帳號,無法查看此內容。" -#: src/view/screens/Settings/ExportCarDialog.tsx:75 -#~ msgid "This feature is in beta. You can read more about repository exports in <0>this blogpost." -#~ msgstr "此功能目前為測試版本。您可以在<0>這篇部落格文章中了解更多有關匯出存放庫的資訊" - #: src/view/screens/Settings/ExportCarDialog.tsx:75 msgid "This feature is in beta. You can read more about repository exports in <0>this blogpost." -msgstr "" +msgstr "此功能目前為測試版本。你可以在<0>這篇部落格文章中了解更多有關資訊。" #: src/view/com/posts/FeedErrorMessage.tsx:114 msgid "This feed is currently receiving high traffic and is temporarily unavailable. Please try again later." @@ -5122,11 +4544,11 @@ msgstr "這很重要,以防你將來需要更改電子郵件地址或重設密 #: src/components/moderation/ModerationDetailsDialog.tsx:124 msgid "This label was applied by {0}." -msgstr "" +msgstr "此標籤是由 {0} 套用的。" #: src/screens/Profile/Sections/Labels.tsx:167 msgid "This labeler hasn't declared what labels it publishes, and may not be active." -msgstr "" +msgstr "此標籤者尚未宣告它發佈的標籤,可能不活躍。" #: src/view/com/modals/LinkWarning.tsx:72 msgid "This link is taking you to the following website:" @@ -5138,7 +4560,7 @@ msgstr "此列表為空!" #: src/screens/Profile/ErrorState.tsx:40 msgid "This moderation service is unavailable. See below for more details. If this issue persists, contact us." -msgstr "" +msgstr "此限制服務暫時無法使用,詳情請見下文。如果問題持續存在,請與我們聯絡。" #: src/view/com/modals/AddAppPasswords.tsx:107 msgid "This name is already in use" @@ -5146,32 +4568,32 @@ msgstr "此名稱已被使用" #: src/view/com/post-thread/PostThreadItem.tsx:125 msgid "This post has been deleted." -msgstr "此貼文已被刪除。" +msgstr "這則貼文已被刪除。" #: src/view/com/util/forms/PostDropdownBtn.tsx:344 #: src/view/com/util/post-ctrls/PostCtrls.tsx:248 msgid "This post is only visible to logged-in users. It won't be visible to people who aren't logged in." -msgstr "" +msgstr "這則貼文僅對登入使用者可見。 未登入的人將看不到它。" #: src/view/com/util/forms/PostDropdownBtn.tsx:326 msgid "This post will be hidden from feeds." -msgstr "" +msgstr "這則貼文將從訊息流中被隱藏。" #: src/view/com/profile/ProfileMenu.tsx:370 msgid "This profile is only visible to logged-in users. It won't be visible to people who aren't logged in." -msgstr "" +msgstr "此個人資料僅對登入使用者可見。 未登入的人將看不到它。" #: src/screens/Signup/StepInfo/Policies.tsx:37 msgid "This service has not provided terms of service or a privacy policy." -msgstr "" +msgstr "此服務尚未提供服務條款或隱私政策。" #: src/view/com/modals/ChangeHandle.tsx:445 msgid "This should create a domain record at:" -msgstr "" +msgstr "這應該在以下位置創建一個域記錄:" #: src/view/com/profile/ProfileFollowers.tsx:87 msgid "This user doesn't have any followers." -msgstr "" +msgstr "此使用者沒有任何追隨者。" #: src/components/moderation/ModerationDetailsDialog.tsx:72 #: src/lib/moderation/useModerationCauseDescription.ts:68 @@ -5180,31 +4602,19 @@ msgstr "此使用者已封鎖你,你無法查看他們的內容。" #: src/lib/moderation/useGlobalLabelStrings.ts:30 msgid "This user has requested that their content only be shown to signed-in users." -msgstr "" - -#: src/view/com/modals/ModerationDetails.tsx:42 -#~ msgid "This user is included in the <0/> list which you have blocked." -#~ msgstr "此使用者包含在你已封鎖的 <0/> 列表中。" - -#: src/view/com/modals/ModerationDetails.tsx:74 -#~ msgid "This user is included in the <0/> list which you have muted." -#~ msgstr "此使用者包含在你已靜音的 <0/> 列表中。" +msgstr "此用戶要求僅將其內容顯示給已登錄的用戶。" #: src/components/moderation/ModerationDetailsDialog.tsx:55 msgid "This user is included in the <0>{0} list which you have blocked." -msgstr "" +msgstr "此使用者包含在你已封鎖的 <0>{0} 列表中。" #: src/components/moderation/ModerationDetailsDialog.tsx:84 msgid "This user is included in the <0>{0} list which you have muted." -msgstr "" - -#: src/view/com/modals/ModerationDetails.tsx:74 -#~ msgid "This user is included the <0/> list which you have muted." -#~ msgstr "此使用者包含在你已靜音的 <0/> 列表中。" +msgstr "此使用者包含在你已靜音的 <0>{0} 列表中。" #: src/view/com/profile/ProfileFollows.tsx:87 msgid "This user isn't following anyone." -msgstr "" +msgstr "此使用者未跟隨任何人。" #: src/view/com/modals/SelfLabel.tsx:137 msgid "This warning is only available for posts with media attached." @@ -5212,15 +4622,11 @@ msgstr "此警告僅適用於附帶媒體的貼文。" #: src/components/dialogs/MutedWords.tsx:283 msgid "This will delete {0} from your muted words. You can always add it back later." -msgstr "" - -#: src/view/com/util/forms/PostDropdownBtn.tsx:282 -#~ msgid "This will hide this post from your feeds." -#~ msgstr "這將在你的訊息流中隱藏此貼文。" +msgstr "這將從你的靜音詞中刪除 {0},你隨時可以在稍後添加回來。" #: src/view/screens/Settings/index.tsx:574 msgid "Thread preferences" -msgstr "" +msgstr "對話串偏好" #: src/view/screens/PreferencesThreads.tsx:53 #: src/view/screens/Settings/index.tsx:584 @@ -5237,11 +4643,11 @@ msgstr "對話串偏好" #: src/components/ReportDialog/SelectLabelerView.tsx:33 msgid "To whom would you like to send this report?" -msgstr "" +msgstr "你希望向誰提交此檢舉?" #: src/components/dialogs/MutedWords.tsx:112 msgid "Toggle between muted word options." -msgstr "" +msgstr "在靜音詞選項之間切換。" #: src/view/com/util/forms/DropdownButton.tsx:246 msgid "Toggle dropdown" @@ -5249,7 +4655,7 @@ msgstr "切換下拉式選單" #: src/screens/Moderation/index.tsx:332 msgid "Toggle to enable or disable adult content" -msgstr "" +msgstr "切換以啟用或禁用成人內容" #: src/view/com/modals/EditImage.tsx:272 msgid "Transformations" @@ -5269,7 +4675,7 @@ msgstr "重試" #: src/view/com/modals/ChangeHandle.tsx:428 msgid "Type:" -msgstr "" +msgstr "類型:" #: src/view/screens/ProfileList.tsx:478 msgid "Un-block list" @@ -5308,7 +4714,7 @@ msgstr "取消封鎖" #: src/screens/Profile/Header/ProfileHeaderStandard.tsx:281 #: src/view/com/profile/ProfileMenu.tsx:343 msgid "Unblock Account?" -msgstr "" +msgstr "取消封鎖?" #: src/view/com/modals/Repost.tsx:43 #: src/view/com/modals/Repost.tsx:56 @@ -5320,7 +4726,7 @@ msgstr "取消轉發" #: src/view/com/auth/onboarding/RecommendedFollowsItem.tsx:141 #: src/view/com/profile/ProfileHeaderSuggestedFollows.tsx:246 msgid "Unfollow" -msgstr "" +msgstr "取消跟隨" #: src/view/com/profile/FollowButton.tsx:60 msgctxt "action" @@ -5334,11 +4740,7 @@ msgstr "取消跟隨 {0}" #: src/view/com/profile/ProfileMenu.tsx:241 #: src/view/com/profile/ProfileMenu.tsx:251 msgid "Unfollow Account" -msgstr "" - -#: src/view/com/auth/create/state.ts:262 -#~ msgid "Unfortunately, you do not meet the requirements to create an account." -#~ msgstr "很遺憾,你不符合建立帳號的要求。" +msgstr "取消跟隨" #: src/view/com/util/post-ctrls/PostCtrls.tsx:195 msgid "Unlike" @@ -5346,7 +4748,7 @@ msgstr "取消喜歡" #: src/view/screens/ProfileFeed.tsx:573 msgid "Unlike this feed" -msgstr "" +msgstr "取消喜歡這個訊息流" #: src/components/TagMenu/index.tsx:249 #: src/view/screens/ProfileList.tsx:579 @@ -5355,7 +4757,7 @@ msgstr "取消靜音" #: src/components/TagMenu/index.web.tsx:104 msgid "Unmute {truncatedTag}" -msgstr "" +msgstr "取消靜音 {truncatedTag}" #: src/view/com/profile/ProfileMenu.tsx:278 #: src/view/com/profile/ProfileMenu.tsx:284 @@ -5364,11 +4766,7 @@ msgstr "取消靜音帳號" #: src/components/TagMenu/index.tsx:208 msgid "Unmute all {displayTag} posts" -msgstr "" - -#: src/components/TagMenu/index.tsx:210 -#~ msgid "Unmute all {tag} posts" -#~ msgstr "" +msgstr "取消對所有 {displayTag} 貼文的靜音" #: src/view/com/util/forms/PostDropdownBtn.tsx:251 #: src/view/com/util/forms/PostDropdownBtn.tsx:256 @@ -5382,39 +4780,31 @@ msgstr "取消固定" #: src/view/screens/ProfileFeed.tsx:292 msgid "Unpin from home" -msgstr "" +msgstr "取消固定在首頁" #: src/view/screens/ProfileList.tsx:444 msgid "Unpin moderation list" msgstr "取消固定限制列表" -#: src/view/screens/ProfileFeed.tsx:346 -#~ msgid "Unsave" -#~ msgstr "取消儲存" - #: src/screens/Profile/Header/ProfileHeaderLabeler.tsx:219 msgid "Unsubscribe" -msgstr "" +msgstr "取消訂閱" #: src/screens/Profile/Header/ProfileHeaderLabeler.tsx:183 msgid "Unsubscribe from this labeler" -msgstr "" +msgstr "取消訂閱這個標籤者" #: src/lib/moderation/useReportOptions.ts:70 msgid "Unwanted Sexual Content" -msgstr "" +msgstr "無關情色內容" #: src/view/com/modals/UserAddRemoveLists.tsx:70 msgid "Update {displayName} in Lists" msgstr "更新列表中的 {displayName}" -#: src/lib/hooks/useOTAUpdate.ts:15 -#~ msgid "Update Available" -#~ msgstr "更新可用" - #: src/view/com/modals/ChangeHandle.tsx:508 msgid "Update to {handle}" -msgstr "" +msgstr "更新至 {handle}" #: src/screens/Login/SetNewPasswordForm.tsx:186 msgid "Updating..." @@ -5429,31 +4819,31 @@ msgstr "上傳文字檔案至:" #: src/view/com/util/UserBanner.tsx:116 #: src/view/com/util/UserBanner.tsx:119 msgid "Upload from Camera" -msgstr "" +msgstr "從相機上傳" #: src/view/com/util/UserAvatar.tsx:343 #: src/view/com/util/UserBanner.tsx:133 msgid "Upload from Files" -msgstr "" +msgstr "從檔案上傳" #: src/view/com/util/UserAvatar.tsx:337 #: src/view/com/util/UserAvatar.tsx:341 #: src/view/com/util/UserBanner.tsx:127 #: src/view/com/util/UserBanner.tsx:131 msgid "Upload from Library" -msgstr "" +msgstr "從圖庫上傳" #: src/view/com/modals/ChangeHandle.tsx:408 msgid "Use a file on your server" -msgstr "" +msgstr "使用伺服器上的檔案" #: src/view/screens/AppPasswords.tsx:197 msgid "Use app passwords to login to other Bluesky clients without giving full access to your account or password." -msgstr "使用應用程式專用密碼登入到其他 Bluesky 用戶端,而無需提供你的帳號或密碼。" +msgstr "使用應用程式專用密碼登入到其他 Bluesky 使用者端,而無需提供你的帳號或密碼。" #: src/view/com/modals/ChangeHandle.tsx:517 msgid "Use bsky.social as hosting provider" -msgstr "" +msgstr "使用 bsky.social 作為主機提供商" #: src/view/com/modals/ChangeHandle.tsx:516 msgid "Use default provider" @@ -5471,16 +4861,12 @@ msgstr "使用我的預設瀏覽器" #: src/view/com/modals/ChangeHandle.tsx:400 msgid "Use the DNS panel" -msgstr "" +msgstr "使用 DNS 控制台" #: src/view/com/modals/AddAppPasswords.tsx:156 msgid "Use this to sign into the other app along with your handle." msgstr "使用這個和你的帳號代碼一起登入其他應用程式。" -#: src/view/com/modals/ServerInput.tsx:105 -#~ msgid "Use your domain as your Bluesky client service provider" -#~ msgstr "將你的網域用作 Bluesky 用戶端服務提供商" - #: src/view/com/modals/InviteCodes.tsx:201 msgid "Used by:" msgstr "使用者:" @@ -5492,7 +4878,7 @@ msgstr "使用者被封鎖" #: src/lib/moderation/useModerationCauseDescription.ts:48 msgid "User Blocked by \"{0}\"" -msgstr "" +msgstr "使用者被\"{0}\"封鎖" #: src/components/moderation/ModerationDetailsDialog.tsx:53 msgid "User Blocked by List" @@ -5500,16 +4886,12 @@ msgstr "使用者被列表封鎖" #: src/lib/moderation/useModerationCauseDescription.ts:66 msgid "User Blocking You" -msgstr "" +msgstr "使用者封鎖了你" #: src/components/moderation/ModerationDetailsDialog.tsx:70 msgid "User Blocks You" msgstr "使用者封鎖了你" -#: src/view/com/auth/create/Step2.tsx:79 -#~ msgid "User handle" -#~ msgstr "帳號代碼" - #: src/view/com/lists/ListCard.tsx:85 #: src/view/com/modals/UserAddRemoveLists.tsx:198 msgid "User list by {0}" @@ -5555,19 +4937,15 @@ msgstr "「{0}」中的使用者" #: src/components/LikesDialog.tsx:85 msgid "Users that have liked this content or profile" -msgstr "" +msgstr "喜歡此內容或個人資料的使用者" #: src/view/com/modals/ChangeHandle.tsx:436 msgid "Value:" -msgstr "" - -#: src/view/com/auth/create/Step2.tsx:243 -#~ msgid "Verification code" -#~ msgstr "驗證碼" +msgstr "值:" #: src/view/com/modals/ChangeHandle.tsx:509 msgid "Verify {0}" -msgstr "" +msgstr "驗證 {0}" #: src/view/screens/Settings/index.tsx:942 msgid "Verify email" @@ -5592,7 +4970,7 @@ msgstr "驗證你的電子郵件" #: src/view/screens/Settings/index.tsx:893 msgid "Version {0}" -msgstr "" +msgstr "版本 {0}" #: src/screens/Onboarding/index.tsx:42 msgid "Video Games" @@ -5608,11 +4986,11 @@ msgstr "查看除錯項目" #: src/components/ReportDialog/SelectReportOptionView.tsx:131 msgid "View details" -msgstr "" +msgstr "查看詳細信息" #: src/components/ReportDialog/SelectReportOptionView.tsx:126 msgid "View details for reporting a copyright violation" -msgstr "" +msgstr "查看詳細信息以檢舉侵權" #: src/view/com/posts/FeedSlice.tsx:99 msgid "View full thread" @@ -5620,7 +4998,7 @@ msgstr "查看整個對話串" #: src/components/moderation/LabelsOnMe.tsx:51 msgid "View information about these labels" -msgstr "" +msgstr "查看有關這些標籤的信息" #: src/view/com/posts/FeedErrorMessage.tsx:166 msgid "View profile" @@ -5632,11 +5010,11 @@ msgstr "查看頭像" #: src/components/LabelingServiceCard/index.tsx:140 msgid "View the labeling service provided by @{0}" -msgstr "" +msgstr "查看由 @{0} 提供的標籤服務" #: src/view/screens/ProfileFeed.tsx:585 msgid "View users who like this feed" -msgstr "" +msgstr "查看喜歡此訊息流的使用者" #: src/view/com/modals/LinkWarning.tsx:89 #: src/view/com/modals/LinkWarning.tsx:95 @@ -5652,19 +5030,15 @@ msgstr "警告" #: src/lib/moderation/useLabelBehaviorDescription.ts:48 msgid "Warn content" -msgstr "" +msgstr "警告內容" #: src/lib/moderation/useLabelBehaviorDescription.ts:46 msgid "Warn content and filter from feeds" -msgstr "" - -#: src/screens/Onboarding/StepAlgoFeeds/index.tsx:134 -#~ msgid "We also think you'll like \"For You\" by Skygaze:" -#~ msgstr "我們認為你還會喜歡 Skygaze 維護的「For You」:" +msgstr "警告內容並從訊息流中過濾" #: src/screens/Hashtag.tsx:133 msgid "We couldn't find any results for that hashtag." -msgstr "" +msgstr "我們找不到任何與該標籤相關的結果。" #: src/screens/Deactivated.tsx:133 msgid "We estimate {estimatedTime} until your account is ready." @@ -5680,7 +5054,7 @@ msgstr "你已看完了你跟隨的貼文。這是 <0/> 的最新貼文。" #: src/components/dialogs/MutedWords.tsx:203 msgid "We recommend avoiding common words that appear in many posts, since it can result in no posts being shown." -msgstr "" +msgstr "我們建議避免使用出現在許多貼文中的常用詞語,因為這可能導致沒有貼文可顯示。" #: src/screens/Onboarding/StepAlgoFeeds/index.tsx:125 msgid "We recommend our \"Discover\" feed:" @@ -5688,11 +5062,11 @@ msgstr "我們推薦我們的「Discover」訊息流:" #: src/components/dialogs/BirthDateSettings.tsx:52 msgid "We were unable to load your birth date preferences. Please try again." -msgstr "" +msgstr "我們無法加載你的出生日期設定偏好,請再試一次。" #: src/screens/Moderation/index.tsx:385 msgid "We were unable to load your configured labelers at this time." -msgstr "" +msgstr "我們目前無法加載你已配置的標籤者。" #: src/screens/Onboarding/StepInterests/index.tsx:137 msgid "We weren't able to connect. Please try again to continue setting up your account. If it continues to fail, you can skip this flow." @@ -5702,10 +5076,6 @@ msgstr "我們無法連線到網際網路,請重試以繼續設定你的帳號 msgid "We will let you know when your account is ready." msgstr "我們會在你的帳號準備好時通知你。" -#: src/view/com/modals/AppealLabel.tsx:48 -#~ msgid "We'll look into your appeal promptly." -#~ msgstr "我們將迅速審查你的申訴。" - #: src/screens/Onboarding/StepInterests/index.tsx:142 msgid "We'll use this to help customize your experience." msgstr "我們將使用這些資訊來幫助定制你的體驗。" @@ -5720,7 +5090,7 @@ msgstr "很抱歉,我們無法解析此列表。如果問題持續發生,請 #: src/components/dialogs/MutedWords.tsx:229 msgid "We're sorry, but we weren't able to load your muted words at this time. Please try again." -msgstr "" +msgstr "很抱歉,我們目前無法加載你的靜音詞。請稍後再試。" #: src/view/screens/Search/Search.tsx:256 msgid "We're sorry, but your search could not be completed. Please try again in a few minutes." @@ -5733,7 +5103,7 @@ msgstr "很抱歉!我們找不到你正在尋找的頁面。" #: src/screens/Profile/Header/ProfileHeaderLabeler.tsx:321 msgid "We're sorry! You can only subscribe to ten labelers, and you've reached your limit of ten." -msgstr "" +msgstr "抱歉!你只能訂閱十個標籤者,你已達到十個的限制。" #: src/view/com/auth/onboarding/WelcomeMobile.tsx:48 msgid "Welcome to <0>Bluesky" @@ -5743,10 +5113,6 @@ msgstr "歡迎來到 <0>Bluesky" msgid "What are your interests?" msgstr "你感興趣的是什麼?" -#: src/view/com/modals/report/Modal.tsx:169 -#~ msgid "What is the issue with this {collectionName}?" -#~ msgstr "這個 {collectionName} 有什麼問題?" - #: src/view/com/auth/SplashScreen.tsx:58 #: src/view/com/auth/SplashScreen.web.tsx:84 #: src/view/com/composer/Composer.tsx:296 @@ -5768,23 +5134,23 @@ msgstr "誰可以回覆" #: src/components/ReportDialog/SelectReportOptionView.tsx:43 msgid "Why should this content be reviewed?" -msgstr "" +msgstr "為什麼應該審查這個內容?" #: src/components/ReportDialog/SelectReportOptionView.tsx:56 msgid "Why should this feed be reviewed?" -msgstr "" +msgstr "為什麼應該審查這個訊息流?" #: src/components/ReportDialog/SelectReportOptionView.tsx:53 msgid "Why should this list be reviewed?" -msgstr "" +msgstr "為什麼應該審查這個列表?" #: src/components/ReportDialog/SelectReportOptionView.tsx:50 msgid "Why should this post be reviewed?" -msgstr "" +msgstr "為什麼應該審查這則貼文?" #: src/components/ReportDialog/SelectReportOptionView.tsx:47 msgid "Why should this user be reviewed?" -msgstr "" +msgstr "為什麼應該審查這個使用者?" #: src/view/com/modals/crop-image/CropImage.web.tsx:103 msgid "Wide" @@ -5803,10 +5169,6 @@ msgstr "撰寫你的回覆" msgid "Writers" msgstr "作家" -#: src/view/com/auth/create/Step2.tsx:263 -#~ msgid "XXXXXX" -#~ msgstr "XXXXXX" - #: src/view/com/composer/select-language/SuggestedLanguage.tsx:77 #: src/view/screens/PreferencesFollowingFeed.tsx:129 #: src/view/screens/PreferencesFollowingFeed.tsx:201 @@ -5823,7 +5185,7 @@ msgstr "輪到你了。" #: src/view/com/profile/ProfileFollows.tsx:86 msgid "You are not following anyone." -msgstr "" +msgstr "你沒有跟隨任何人。" #: src/view/com/posts/FollowingEmptyState.tsx:67 #: src/view/com/posts/FollowingEndOfFeed.tsx:68 @@ -5841,7 +5203,7 @@ msgstr "你現在可以使用新密碼登入。" #: src/view/com/profile/ProfileFollowers.tsx:86 msgid "You do not have any followers." -msgstr "" +msgstr "你沒有任何跟隨者。" #: src/view/com/modals/InviteCodes.tsx:67 msgid "You don't have any invite codes yet! We'll send you some when you've been on Bluesky for a little longer." @@ -5878,24 +5240,20 @@ msgstr "你輸入的邀請碼無效。它應該長得像這樣 XXXXX-XXXXX。" #: src/lib/moderation/useModerationCauseDescription.ts:109 msgid "You have hidden this post" -msgstr "" +msgstr "你已隱藏這則貼文" #: src/components/moderation/ModerationDetailsDialog.tsx:101 msgid "You have hidden this post." -msgstr "" +msgstr "你已隱藏這則貼文。" #: src/components/moderation/ModerationDetailsDialog.tsx:94 #: src/lib/moderation/useModerationCauseDescription.ts:92 msgid "You have muted this account." -msgstr "" +msgstr "你已隱藏這個帳號。" #: src/lib/moderation/useModerationCauseDescription.ts:86 msgid "You have muted this user" -msgstr "" - -#: src/view/com/modals/ModerationDetails.tsx:87 -#~ msgid "You have muted this user." -#~ msgstr "你已將這個使用者靜音。" +msgstr "你已隱藏這個使用者。" #: src/view/com/feeds/ProfileFeedgens.tsx:136 msgid "You have no feeds." @@ -5908,11 +5266,7 @@ msgstr "你沒有列表。" #: src/view/screens/ModerationBlockedAccounts.tsx:132 msgid "You have not blocked any accounts yet. To block an account, go to their profile and select \"Block account\" from the menu on their account." -msgstr "" - -#: src/view/screens/ModerationBlockedAccounts.tsx:132 -#~ msgid "You have not blocked any accounts yet. To block an account, go to their profile and selected \"Block account\" from the menu on their account." -#~ msgstr "你還沒有封鎖任何帳號。要封鎖帳號,請轉到其個人資料並在其帳號上的選單中選擇「封鎖帳號」。" +msgstr "你還沒有封鎖任何帳號。要封鎖帳號,請轉到其個人資料並在其帳號上的選單中選擇「封鎖帳號」。" #: src/view/screens/AppPasswords.tsx:89 msgid "You have not created any app passwords yet. You can create one by pressing the button below." @@ -5920,11 +5274,7 @@ msgstr "你還沒有建立任何應用程式專用密碼,如你想建立一個 #: src/view/screens/ModerationMutedAccounts.tsx:131 msgid "You have not muted any accounts yet. To mute an account, go to their profile and select \"Mute account\" from the menu on their account." -msgstr "" - -#: src/view/screens/ModerationMutedAccounts.tsx:131 -#~ msgid "You have not muted any accounts yet. To mute an account, go to their profile and selected \"Mute account\" from the menu on their account." -#~ msgstr "你還沒有靜音任何帳號。要靜音帳號,請轉到其個人資料並在其帳號上的選單中選擇「靜音帳號」。" +msgstr "你還沒有靜音任何帳號。要靜音帳號,請轉到其個人資料並在其帳號上的選單中選擇「靜音帳號」。" #: src/components/dialogs/MutedWords.tsx:249 msgid "You haven't muted any words or tags yet" @@ -5932,15 +5282,11 @@ msgstr "你还没有隐藏任何词或话题标签" #: src/components/moderation/LabelsOnMeDialog.tsx:68 msgid "You may appeal these labels if you feel they were placed in error." -msgstr "" +msgstr "如果你覺得這些標籤是錯誤的,你可以申訴這些標籤。" #: src/screens/Signup/StepInfo/Policies.tsx:79 msgid "You must be 13 years of age or older to sign up." -msgstr "" - -#: src/view/com/modals/ContentFilteringSettings.tsx:175 -#~ msgid "You must be 18 or older to enable adult content." -#~ msgstr "你必須年滿 18 歲才能啟用成人內容。" +msgstr "你必須年滿 13 歲才能註冊。" #: src/screens/Onboarding/StepModeration/AdultContentEnabledPref.tsx:110 msgid "You must be 18 years or older to enable adult content" @@ -5948,7 +5294,7 @@ msgstr "你必須年滿 18 歲才能啟用成人內容" #: src/components/ReportDialog/SubmitView.tsx:205 msgid "You must select at least one labeler for a report" -msgstr "" +msgstr "你必須選擇至少一個標籤者來提交檢舉" #: src/view/com/util/forms/PostDropdownBtn.tsx:144 msgid "You will no longer receive notifications for this thread" @@ -5979,7 +5325,7 @@ msgstr "你已設定完成!" #: src/components/moderation/ModerationDetailsDialog.tsx:98 #: src/lib/moderation/useModerationCauseDescription.ts:101 msgid "You've chosen to hide a word or tag within this post." -msgstr "" +msgstr "您選擇在這則貼文中隱藏詞彙或標籤。" #: src/view/com/posts/FollowingEndOfFeed.tsx:48 msgid "You've reached the end of your feed! Find some more accounts to follow." @@ -6015,10 +5361,6 @@ msgstr "你的預設訊息流為「跟隨」" msgid "Your email appears to be invalid." msgstr "你的電子郵件地址似乎無效。" -#: src/view/com/modals/Waitlist.tsx:109 -#~ msgid "Your email has been saved! We'll be in touch soon." -#~ msgstr "你的電子郵件地址已儲存!我們將很快聯繫你。" - #: src/view/com/modals/ChangeEmail.tsx:125 msgid "Your email has been updated but not verified. As a next step, please verify your new email." msgstr "你的電子郵件地址已更新但尚未驗證。作為下一步,請驗證你的新電子郵件地址。" @@ -6039,15 +5381,9 @@ msgstr "你的完整帳號代碼將修改為" msgid "Your full handle will be <0>@{0}" msgstr "你的完整帳號代碼將修改為 <0>@{0}" -#: src/view/screens/Settings.tsx:430 -#: src/view/shell/desktop/RightNav.tsx:137 -#: src/view/shell/Drawer.tsx:660 -#~ msgid "Your invite codes are hidden when logged in using an App Password" -#~ msgstr "在使用應用程式專用密碼登入時,你的邀請碼將被隱藏" - #: src/components/dialogs/MutedWords.tsx:220 msgid "Your muted words" -msgstr "" +msgstr "你的靜音詞" #: src/view/com/modals/ChangePassword.tsx:157 msgid "Your password has been changed successfully!" From 5aed7db208a3eca95e08b477b29b9b8923a2ead5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ivan=20Be=C3=A0?= Date: Fri, 12 Apr 2024 23:46:39 +0200 Subject: [PATCH 020/167] Update Catalan messages.po (#3476) * Update messages.po new lines translated * Update messages.po change {nom} by {name} * Update messages.po update changes by @jordimas --- src/locale/locales/ca/messages.po | 52 +++++++++++++++---------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/src/locale/locales/ca/messages.po b/src/locale/locales/ca/messages.po index 7945fe8b59..9c0d4cc180 100644 --- a/src/locale/locales/ca/messages.po +++ b/src/locale/locales/ca/messages.po @@ -426,7 +426,7 @@ msgstr "Nuesa artística o no eròtica." #: src/screens/Signup/StepHandle.tsx:118 msgid "At least 3 characters" -msgstr "" +msgstr "Almenys 3 caràcters" #: src/components/moderation/LabelsOnMeDialog.tsx:246 #: src/components/moderation/LabelsOnMeDialog.tsx:247 @@ -929,7 +929,7 @@ msgstr "Configura els filtres de continguts per la categoria: {0}" #: src/components/moderation/LabelPreference.tsx:81 msgid "Configure content filtering setting for category: {name}" -msgstr "" +msgstr "Configura els filtres de continguts per la categoria: {name}" #: src/components/moderation/LabelPreference.tsx:244 msgid "Configured in <0>moderation settings." @@ -1053,7 +1053,7 @@ msgstr "Continua" #: src/components/AccountList.tsx:108 msgid "Continue as {0} (currently signed in)" -msgstr "" +msgstr "Continua com a {0} (sessió actual)" #: src/screens/Onboarding/StepFollowingFeed.tsx:151 #: src/screens/Onboarding/StepInterests/index.tsx:249 @@ -1223,7 +1223,7 @@ msgstr "Tema fosc" #: src/screens/Signup/StepInfo/index.tsx:132 msgid "Date of birth" -msgstr "" +msgstr "Data de naixement" #: src/view/screens/Settings/index.tsx:841 msgid "Debug Moderation" @@ -1370,7 +1370,7 @@ msgstr "No inclou nuesa." #: src/screens/Signup/StepHandle.tsx:104 msgid "Doesn't begin or end with a hyphen" -msgstr "" +msgstr "No comença ni acaba amb un guionet" #: src/view/com/modals/ChangeHandle.tsx:481 msgid "Domain Value" @@ -1590,7 +1590,7 @@ msgstr "Habilita veure el contingut per adults als teus canals" #: src/components/dialogs/EmbedConsent.tsx:82 #: src/components/dialogs/EmbedConsent.tsx:89 msgid "Enable external media" -msgstr "" +msgstr "Habilita els continguts externs" #: src/view/com/modals/EmbedConsent.tsx:97 #~ msgid "Enable External Media" @@ -1606,7 +1606,7 @@ msgstr "Activa aquesta opció per a veure només les respostes entre els comptes #: src/components/dialogs/EmbedConsent.tsx:94 msgid "Enable this source only" -msgstr "" +msgstr "Habilita només per aquesta font" #: src/screens/Moderation/index.tsx:339 msgid "Enabled" @@ -1622,7 +1622,7 @@ msgstr "Posa un nom a aquesta contrasenya d'aplicació" #: src/screens/Login/SetNewPasswordForm.tsx:139 msgid "Enter a password" -msgstr "" +msgstr "Introdueix una contrasenya" #: src/components/dialogs/MutedWords.tsx:99 #: src/components/dialogs/MutedWords.tsx:100 @@ -1920,7 +1920,7 @@ msgstr "Segueix-los a tots" #: src/view/com/post-thread/PostThreadFollowBtn.tsx:144 msgid "Follow Back" -msgstr "" +msgstr "Segueix" #: src/screens/Onboarding/StepSuggestedAccounts/index.tsx:182 msgid "Follow selected accounts and continue to the next step" @@ -2013,11 +2013,11 @@ msgstr "He oblidat la contrasenya" #: src/screens/Login/LoginForm.tsx:201 msgid "Forgot password?" -msgstr "" +msgstr "Has oblidat la contrasenya?" #: src/screens/Login/LoginForm.tsx:212 msgid "Forgot?" -msgstr "" +msgstr "Oblidada?" #: src/lib/moderation/useReportOptions.ts:52 msgid "Frequently Posts Unwanted Content" @@ -2684,7 +2684,7 @@ msgstr "Accedeix a un compte que no està llistat" #: src/screens/Login/SetNewPasswordForm.tsx:116 msgid "Looks like XXXXX-XXXXX" -msgstr "" +msgstr "Té l'aspecte XXXXX-XXXXX" #: src/view/com/modals/LinkWarning.tsx:79 msgid "Make sure this is where you intend to go!" @@ -3093,7 +3093,7 @@ msgstr "Ja no segueixes a {0}" #: src/screens/Signup/StepHandle.tsx:114 msgid "No longer than 253 characters" -msgstr "" +msgstr "No pot tenir més de 253 caràcters" #: src/view/com/notifications/Feed.tsx:109 msgid "No notifications yet!" @@ -3176,7 +3176,7 @@ msgstr "Nuesa" #: src/lib/moderation/useReportOptions.ts:71 msgid "Nudity or adult content not labeled as such" -msgstr "" +msgstr "Nuesa o contingut per adults no etiquetat com a tal" #: src/lib/moderation/useReportOptions.ts:71 #~ msgid "Nudity or pornography not labeled as such" @@ -3184,7 +3184,7 @@ msgstr "" #: src/screens/Signup/index.tsx:142 msgid "of" -msgstr "" +msgstr "de" #: src/lib/moderation/useLabelBehaviorDescription.ts:11 msgid "Off" @@ -3225,7 +3225,7 @@ msgstr "Només {0} poden respondre." #: src/screens/Signup/StepHandle.tsx:97 msgid "Only contains letters, numbers, and hyphens" -msgstr "" +msgstr "Només pot tenir lletres, nombres i guionets" #: src/components/Lists.tsx:75 msgid "Oops, something went wrong!" @@ -3695,7 +3695,7 @@ msgstr "Enllaç potencialment enganyós" #: src/components/forms/HostingProvider.tsx:45 msgid "Press to change hosting provider" -msgstr "" +msgstr "Prem per canviar el proveïdor d'allotjament" #: src/components/Error.tsx:74 #: src/components/Lists.tsx:80 @@ -3934,7 +3934,7 @@ msgstr "Informa del compte" #: src/components/ReportDialog/index.tsx:49 msgid "Report dialog" -msgstr "" +msgstr "Diàleg de l'informe" #: src/view/screens/ProfileFeed.tsx:352 #: src/view/screens/ProfileFeed.tsx:354 @@ -4274,7 +4274,7 @@ msgstr "Selecciona {item}" #: src/screens/Login/ChooseAccountForm.tsx:61 msgid "Select account" -msgstr "" +msgstr "Selecciona el compte" #: src/view/com/modals/ServerInput.tsx:75 #~ msgid "Select Bluesky Social" @@ -4335,7 +4335,7 @@ msgstr "Selecciona l'idioma de l'aplicació perquè el text predeterminat es mos #: src/screens/Signup/StepInfo/index.tsx:133 msgid "Select your date of birth" -msgstr "" +msgstr "Selecciona la teva data de naixement" #: src/screens/Onboarding/StepInterests/index.tsx:200 msgid "Select your interests from the options below" @@ -4564,11 +4564,11 @@ msgstr "Comparteix el canal" #: src/view/com/modals/LinkWarning.tsx:89 #: src/view/com/modals/LinkWarning.tsx:95 msgid "Share Link" -msgstr "" +msgstr "Comparteix l'enllaç" #: src/view/com/modals/LinkWarning.tsx:92 msgid "Shares the linked website" -msgstr "" +msgstr "Comparteix la web enllaçada" #: src/components/moderation/ContentHider.tsx:115 #: src/components/moderation/LabelPreference.tsx:136 @@ -4838,7 +4838,7 @@ msgstr "Pàgina d'estat" #: src/screens/Signup/index.tsx:142 msgid "Step" -msgstr "" +msgstr "Pas" #: src/view/com/auth/create/StepHeader.tsx:22 #~ msgid "Step {0} of {numSteps}" @@ -5682,7 +5682,7 @@ msgstr "Verifica el teu correu" #: src/view/screens/Settings/index.tsx:893 msgid "Version {0}" -msgstr "" +msgstr "Versió {0}" #: src/screens/Onboarding/index.tsx:42 msgid "Video Games" @@ -6029,11 +6029,11 @@ msgstr "Encara no has silenciat cap paraula ni etiqueta" #: src/components/moderation/LabelsOnMeDialog.tsx:68 msgid "You may appeal these labels if you feel they were placed in error." -msgstr "Pots apel·lar aquestes etiquetes si creus que s'han col·locat per error," +msgstr "Pots apel·lar aquestes etiquetes si creus que s'han col·locat per error." #: src/screens/Signup/StepInfo/Policies.tsx:79 msgid "You must be 13 years of age or older to sign up." -msgstr "" +msgstr "Has de tenir 13 anys o més per registrar-te" #: src/view/com/modals/ContentFilteringSettings.tsx:175 #~ msgid "You must be 18 or older to enable adult content." From f6dc216110d1ab5b4387957b6ee8cc5fd272af9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gild=C3=A1sio=20Filho?= Date: Fri, 12 Apr 2024 18:47:16 -0300 Subject: [PATCH 021/167] Update pt-BR localization to latest version (#3472) * Update messages.po * Update messages.po --- src/locale/locales/pt-BR/messages.po | 574 ++------------------------- 1 file changed, 28 insertions(+), 546 deletions(-) diff --git a/src/locale/locales/pt-BR/messages.po b/src/locale/locales/pt-BR/messages.po index 6fcc18894a..83c497ea92 100644 --- a/src/locale/locales/pt-BR/messages.po +++ b/src/locale/locales/pt-BR/messages.po @@ -8,7 +8,7 @@ msgstr "" "Language: pt-BR\n" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" -"PO-Revision-Date: 2024-03-22 11:51\n" +"PO-Revision-Date: 2024-04-10 18:15\n" "Last-Translator: gildaswise\n" "Language-Team: maisondasilva, MightyLoggor, gildaswise, gleydson, faeriarum\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" @@ -53,14 +53,6 @@ msgstr "<0>Bem-vindo ao<1>Bluesky" msgid "⚠Invalid Handle" msgstr "⚠Usuário Inválido" -#: src/view/com/util/moderation/LabelInfo.tsx:45 -#~ msgid "A content warning has been applied to this {0}." -#~ msgstr "Um aviso de conteúdo foi aplicado a este {0}." - -#: src/lib/hooks/useOTAUpdate.ts:16 -#~ msgid "A new version of the app is available. Please update to continue using the app." -#~ msgstr "Uma nova versão do aplicativo está disponível. Por favor, atualize para continuar usando o aplicativo." - #: src/view/com/util/ViewHeader.tsx:89 #: src/view/screens/Search/Search.tsx:649 msgid "Access navigation links and settings" @@ -161,15 +153,6 @@ msgstr "Adicionar texto alternativo" msgid "Add App Password" msgstr "Adicionar Senha de Aplicativo" -#: src/view/com/modals/report/InputIssueDetails.tsx:41 -#: src/view/com/modals/report/Modal.tsx:191 -#~ msgid "Add details" -#~ msgstr "Adicionar detalhes" - -#: src/view/com/modals/report/Modal.tsx:194 -#~ msgid "Add details to report" -#~ msgstr "Adicionar detalhes à denúncia" - #: src/view/com/composer/Composer.tsx:467 msgid "Add link card" msgstr "Adicionar prévia de link" @@ -222,10 +205,6 @@ msgstr "Ajuste o número de curtidas para que uma resposta apareça no seu feed. msgid "Adult Content" msgstr "Conteúdo Adulto" -#: src/view/com/modals/ContentFilteringSettings.tsx:141 -#~ msgid "Adult content can only be enabled via the Web at <0/>." -#~ msgstr "Conteúdo adulto só pode ser habilitado no site: <0/>." - #: src/components/moderation/LabelPreference.tsx:242 msgid "Adult content is disabled." msgstr "O conteúdo adulto está desabilitado." @@ -312,10 +291,6 @@ msgstr "O nome da Senha de Aplicativo precisa ter no mínimo 4 caracteres." msgid "App password settings" msgstr "Configurações de Senha de Aplicativo" -#: src/view/screens/Settings.tsx:650 -#~ msgid "App passwords" -#~ msgstr "Senhas de aplicativos" - #: src/Navigation.tsx:251 #: src/view/screens/AppPasswords.tsx:189 #: src/view/screens/Settings/index.tsx:704 @@ -331,27 +306,10 @@ msgstr "Contestar" msgid "Appeal \"{0}\" label" msgstr "Contestar rótulo \"{0}\"" -#: src/view/com/util/forms/PostDropdownBtn.tsx:337 -#: src/view/com/util/forms/PostDropdownBtn.tsx:346 -#~ msgid "Appeal content warning" -#~ msgstr "Contestar aviso de conteúdo" - -#: src/view/com/modals/AppealLabel.tsx:65 -#~ msgid "Appeal Content Warning" -#~ msgstr "Contestar aviso de conteúdo" - #: src/components/moderation/LabelsOnMeDialog.tsx:192 msgid "Appeal submitted." msgstr "Contestação enviada." -#: src/view/com/util/moderation/LabelInfo.tsx:52 -#~ msgid "Appeal this decision" -#~ msgstr "Contestar esta decisão" - -#: src/view/com/util/moderation/LabelInfo.tsx:56 -#~ msgid "Appeal this decision." -#~ msgstr "Contestar esta decisão." - #: src/view/screens/Settings/index.tsx:485 msgid "Appearance" msgstr "Aparência" @@ -372,10 +330,6 @@ msgstr "Tem certeza que deseja descartar este rascunho?" msgid "Are you sure?" msgstr "Tem certeza?" -#: src/view/com/util/forms/PostDropdownBtn.tsx:322 -#~ msgid "Are you sure? This cannot be undone." -#~ msgstr "Tem certeza? Esta ação não poderá ser desfeita." - #: src/view/com/composer/select-language/SuggestedLanguage.tsx:60 msgid "Are you writing in <0>{0}?" msgstr "Você está escrevendo em <0>{0}?" @@ -390,7 +344,7 @@ msgstr "Nudez artística ou não erótica." #: src/screens/Signup/StepHandle.tsx:118 msgid "At least 3 characters" -msgstr "" +msgstr "No mínimo 3 caracteres" #: src/components/moderation/LabelsOnMeDialog.tsx:246 #: src/components/moderation/LabelsOnMeDialog.tsx:247 @@ -408,11 +362,6 @@ msgstr "" msgid "Back" msgstr "Voltar" -#: src/view/com/post-thread/PostThread.tsx:480 -#~ msgctxt "action" -#~ msgid "Back" -#~ msgstr "Voltar" - #: src/screens/Onboarding/StepSuggestedAccounts/index.tsx:144 msgid "Based on your interest in {interestsText}" msgstr "Com base no seu interesse em {interestsText}" @@ -456,10 +405,6 @@ msgstr "Lista de bloqueio" msgid "Block these accounts?" msgstr "Bloquear estas contas?" -#: src/view/screens/ProfileList.tsx:320 -#~ msgid "Block this List" -#~ msgstr "Bloquear esta Lista" - #: src/view/com/lists/ListCard.tsx:110 #: src/view/com/util/post-embeds/QuoteEmbed.tsx:55 msgid "Blocked" @@ -528,10 +473,6 @@ msgstr "Bluesky é aberto." msgid "Bluesky is public." msgstr "Bluesky é público." -#: src/view/com/modals/Waitlist.tsx:70 -#~ msgid "Bluesky uses invites to build a healthier community. If you don't know anybody with an invite, you can sign up for the waitlist and we'll send one soon." -#~ msgstr "O Bluesky usa convites para criar uma comunidade mais saudável. Se você não conhece ninguém que tenha um convite, inscreva-se na lista de espera e em breve enviaremos um para você." - #: src/screens/Moderation/index.tsx:533 msgid "Bluesky will not show your profile and posts to logged-out users. Other apps may not honor this request. This does not make your account private." msgstr "O Bluesky não mostrará seu perfil e publicações para usuários desconectados. Outros aplicativos podem não honrar esta solicitação. Isso não torna a sua conta privada." @@ -548,10 +489,6 @@ msgstr "Desfocar imagens e filtrar dos feeds" msgid "Books" msgstr "Livros" -#: src/view/screens/Settings/index.tsx:893 -#~ msgid "Build version {0} {1}" -#~ msgstr "Versão {0} {1}" - #: src/view/com/auth/HomeLoggedOutCTA.tsx:92 #: src/view/com/auth/SplashScreen.web.tsx:166 msgid "Business" @@ -649,10 +586,6 @@ msgstr "Cancelar citação" msgid "Cancel search" msgstr "Cancelar busca" -#: src/view/com/modals/Waitlist.tsx:136 -#~ msgid "Cancel waitlist signup" -#~ msgstr "Cancelar inscrição na lista de espera" - #: src/view/com/modals/LinkWarning.tsx:106 msgid "Cancels opening the linked website" msgstr "Cancela a abertura do link" @@ -692,10 +625,6 @@ msgstr "Alterar Senha" msgid "Change post language to {0}" msgstr "Trocar idioma do post para {0}" -#: src/view/screens/Settings/index.tsx:733 -#~ msgid "Change your Bluesky password" -#~ msgstr "Alterar sua senha do Bluesky" - #: src/view/com/modals/ChangeEmail.tsx:109 msgid "Change Your Email" msgstr "Altere o Seu Email" @@ -721,10 +650,6 @@ msgstr "Verifique em sua caixa de entrada um e-mail com o código de confirmaç msgid "Choose \"Everybody\" or \"Nobody\"" msgstr "Escolha \"Todos\" ou \"Ninguém\"" -#: src/view/screens/Settings/index.tsx:697 -#~ msgid "Choose a new Bluesky username or create" -#~ msgstr "Crie ou escolha um novo usuário no Bluesky" - #: src/view/com/auth/server-input/index.tsx:79 msgid "Choose Service" msgstr "Escolher Serviço" @@ -881,7 +806,7 @@ msgstr "Configure o filtro de conteúdo por categoria: {0}" #: src/components/moderation/LabelPreference.tsx:81 msgid "Configure content filtering setting for category: {name}" -msgstr "" +msgstr "Configure o filtro de conteúdo por categoria: {name}" #: src/components/moderation/LabelPreference.tsx:244 msgid "Configured in <0>moderation settings." @@ -897,12 +822,6 @@ msgstr "Configure no <0>painel de moderação." msgid "Confirm" msgstr "Confirmar" -#: src/view/com/modals/Confirm.tsx:75 -#: src/view/com/modals/Confirm.tsx:78 -#~ msgctxt "action" -#~ msgid "Confirm" -#~ msgstr "Confirmar" - #: src/view/com/modals/ChangeEmail.tsx:193 #: src/view/com/modals/ChangeEmail.tsx:195 msgid "Confirm Change" @@ -916,10 +835,6 @@ msgstr "Confirmar configurações de idioma de conteúdo" msgid "Confirm delete account" msgstr "Confirmar a exclusão da conta" -#: src/view/com/modals/ContentFilteringSettings.tsx:156 -#~ msgid "Confirm your age to enable adult content." -#~ msgstr "Confirme sua idade para habilitar conteúdo adulto." - #: src/screens/Moderation/index.tsx:301 msgid "Confirm your age:" msgstr "Confirme sua idade:" @@ -935,10 +850,6 @@ msgstr "Confirme sua data de nascimento" msgid "Confirmation code" msgstr "Código de confirmação" -#: src/view/com/modals/Waitlist.tsx:120 -#~ msgid "Confirms signing up {email} to the waitlist" -#~ msgstr "Confirma adição de {email} à lista de espera" - #: src/screens/Login/LoginForm.tsx:248 msgid "Connecting..." msgstr "Conectando..." @@ -955,14 +866,6 @@ msgstr "conteúdo" msgid "Content Blocked" msgstr "Conteúdo bloqueado" -#: src/view/screens/Moderation.tsx:83 -#~ msgid "Content filtering" -#~ msgstr "Filtragem do conteúdo" - -#: src/view/com/modals/ContentFilteringSettings.tsx:44 -#~ msgid "Content Filtering" -#~ msgstr "Filtragem do Conteúdo" - #: src/screens/Moderation/index.tsx:285 msgid "Content filters" msgstr "Filtros de conteúdo" @@ -1005,7 +908,7 @@ msgstr "Continuar" #: src/components/AccountList.tsx:108 msgid "Continue as {0} (currently signed in)" -msgstr "" +msgstr "Continuar como {0} (já conectado)" #: src/screens/Onboarding/StepFollowingFeed.tsx:151 #: src/screens/Onboarding/StepInterests/index.tsx:249 @@ -1064,10 +967,6 @@ msgstr "Copiar link da lista" msgid "Copy link to post" msgstr "Copiar link do post" -#: src/view/com/profile/ProfileHeader.tsx:295 -#~ msgid "Copy link to profile" -#~ msgstr "Copiar link do perfil" - #: src/view/com/util/forms/PostDropdownBtn.tsx:220 #: src/view/com/util/forms/PostDropdownBtn.tsx:222 msgid "Copy post text" @@ -1118,14 +1017,6 @@ msgstr "Criar denúncia para {0}" msgid "Created {0}" msgstr "{0} criada" -#: src/view/screens/ProfileFeed.tsx:616 -#~ msgid "Created by <0/>" -#~ msgstr "Criado por <0/>" - -#: src/view/screens/ProfileFeed.tsx:614 -#~ msgid "Created by you" -#~ msgstr "Criado por você" - #: src/view/com/composer/Composer.tsx:469 msgid "Creates a card with a thumbnail. The card links to {url}" msgstr "Cria uma prévia com miniatura. A prévia faz um link para {url}" @@ -1167,7 +1058,7 @@ msgstr "Modo Escuro" #: src/screens/Signup/StepInfo/index.tsx:132 msgid "Date of birth" -msgstr "" +msgstr "Data de nascimento" #: src/view/screens/Settings/index.tsx:841 msgid "Debug Moderation" @@ -1239,10 +1130,6 @@ msgstr "Post excluído." msgid "Description" msgstr "Descrição" -#: src/view/screens/Settings.tsx:760 -#~ msgid "Developer Tools" -#~ msgstr "Ferramentas de Desenvolvedor" - #: src/view/com/composer/Composer.tsx:218 msgid "Did you want to say anything?" msgstr "Você gostaria de dizer alguma coisa?" @@ -1262,10 +1149,6 @@ msgstr "Desabilitado" msgid "Discard" msgstr "Descartar" -#: src/view/com/composer/Composer.tsx:145 -#~ msgid "Discard draft" -#~ msgstr "Descartar rascunho" - #: src/view/com/composer/Composer.tsx:508 msgid "Discard draft?" msgstr "Descartar rascunho?" @@ -1302,7 +1185,7 @@ msgstr "Não inclui nudez." #: src/screens/Signup/StepHandle.tsx:104 msgid "Doesn't begin or end with a hyphen" -msgstr "" +msgstr "Não começa ou termina com um hífen" #: src/view/com/modals/ChangeHandle.tsx:481 msgid "Domain Value" @@ -1312,10 +1195,6 @@ msgstr "Domínio" msgid "Domain verified!" msgstr "Domínio verificado!" -#: src/view/com/auth/create/Step1.tsx:170 -#~ msgid "Don't have an invite code?" -#~ msgstr "Não possui um convite?" - #: src/components/dialogs/BirthDateSettings.tsx:119 #: src/components/dialogs/BirthDateSettings.tsx:125 #: src/components/forms/DateField/index.tsx:74 @@ -1351,14 +1230,6 @@ msgstr "Feito" msgid "Done{extraText}" msgstr "Feito{extraText}" -#: src/view/com/auth/login/ChooseAccountForm.tsx:46 -#~ msgid "Double tap to sign in" -#~ msgstr "Toque duas vezes para logar" - -#: src/view/screens/Settings/index.tsx:755 -#~ msgid "Download Bluesky account data (repository)" -#~ msgstr "Baixar os dados da minha conta Bluesky (repositório)" - #: src/view/screens/Settings/ExportCarDialog.tsx:59 #: src/view/screens/Settings/ExportCarDialog.tsx:63 msgid "Download CAR file" @@ -1522,11 +1393,7 @@ msgstr "Habilitar conteúdo adulto nos feeds" #: src/components/dialogs/EmbedConsent.tsx:82 #: src/components/dialogs/EmbedConsent.tsx:89 msgid "Enable external media" -msgstr "" - -#: src/view/com/modals/EmbedConsent.tsx:97 -#~ msgid "Enable External Media" -#~ msgstr "Habilitar Mídia Externa" +msgstr "Habilitar mídia externa" #: src/view/screens/PreferencesExternalEmbeds.tsx:75 msgid "Enable media players for" @@ -1538,7 +1405,7 @@ msgstr "Ative esta configuração para ver respostas apenas entre as pessoas que #: src/components/dialogs/EmbedConsent.tsx:94 msgid "Enable this source only" -msgstr "" +msgstr "Habilitar mídia somente para este site" #: src/screens/Moderation/index.tsx:339 msgid "Enabled" @@ -1554,7 +1421,7 @@ msgstr "Insira um nome para esta Senha de Aplicativo" #: src/screens/Login/SetNewPasswordForm.tsx:139 msgid "Enter a password" -msgstr "" +msgstr "Insira uma senha" #: src/components/dialogs/MutedWords.tsx:99 #: src/components/dialogs/MutedWords.tsx:100 @@ -1581,10 +1448,6 @@ msgstr "Digite o e-mail que você usou para criar a sua conta. Nós lhe enviarem msgid "Enter your birth date" msgstr "Insira seu aniversário" -#: src/view/com/modals/Waitlist.tsx:78 -#~ msgid "Enter your email" -#~ msgstr "Digite seu e-mail" - #: src/screens/Login/ForgotPasswordForm.tsx:105 #: src/screens/Signup/StepInfo/index.tsx:91 msgid "Enter your email address" @@ -1639,10 +1502,6 @@ msgstr "Sair do visualizador de imagem" msgid "Exits inputting search query" msgstr "Sair da busca" -#: src/view/com/modals/Waitlist.tsx:138 -#~ msgid "Exits signing up for waitlist with {email}" -#~ msgstr "Desistir de entrar na lista de espera" - #: src/view/com/lightbox/Lightbox.web.tsx:183 msgid "Expand alt text" msgstr "Expandir texto alternativo" @@ -1723,10 +1582,6 @@ msgstr "Feed por {0}" msgid "Feed offline" msgstr "Feed offline" -#: src/view/com/feeds/FeedPage.tsx:143 -#~ msgid "Feed Preferences" -#~ msgstr "Preferências de Feeds" - #: src/view/shell/desktop/RightNav.tsx:61 #: src/view/shell/Drawer.tsx:314 msgid "Feedback" @@ -1840,7 +1695,7 @@ msgstr "Seguir Todas" #: src/view/com/post-thread/PostThreadFollowBtn.tsx:144 msgid "Follow Back" -msgstr "" +msgstr "Seguir De Volta" #: src/screens/Onboarding/StepSuggestedAccounts/index.tsx:182 msgid "Follow selected accounts and continue to the next step" @@ -1914,14 +1769,6 @@ msgstr "Por motivos de segurança, precisamos enviar um código de confirmação msgid "For security reasons, you won't be able to view this again. If you lose this password, you'll need to generate a new one." msgstr "Por motivos de segurança, você não poderá ver esta senha novamente. Se você perder esta senha, terá que gerar uma nova." -#: src/view/com/auth/login/LoginForm.tsx:244 -#~ msgid "Forgot" -#~ msgstr "Esqueci" - -#: src/view/com/auth/login/LoginForm.tsx:241 -#~ msgid "Forgot password" -#~ msgstr "Esqueci a senha" - #: src/screens/Login/index.tsx:129 #: src/screens/Login/index.tsx:144 msgid "Forgot Password" @@ -1929,11 +1776,11 @@ msgstr "Esqueci a Senha" #: src/screens/Login/LoginForm.tsx:201 msgid "Forgot password?" -msgstr "" +msgstr "Esqueceu a senha?" #: src/screens/Login/LoginForm.tsx:212 msgid "Forgot?" -msgstr "" +msgstr "Esqueceu?" #: src/lib/moderation/useReportOptions.ts:52 msgid "Frequently Posts Unwanted Content" @@ -2024,10 +1871,6 @@ msgstr "Assédio, intolerância ou \"trollagem\"" msgid "Hashtag" msgstr "Hashtag" -#: src/components/RichText.tsx:188 -#~ msgid "Hashtag: {tag}" -#~ msgstr "Hashtag: {tag}" - #: src/components/RichText.tsx:191 msgid "Hashtag: #{tag}" msgstr "Hashtag: #{tag}" @@ -2093,10 +1936,6 @@ msgstr "Ocultar este post?" msgid "Hide user list" msgstr "Ocultar lista de usuários" -#: src/view/com/profile/ProfileHeader.tsx:487 -#~ msgid "Hides posts from {0} in your feed" -#~ msgstr "Esconder posts de {0} no seu feed" - #: src/view/com/posts/FeedErrorMessage.tsx:111 msgid "Hmm, some kind of issue occurred when contacting the feed server. Please let the feed owner know about this issue." msgstr "Hmm, ocorreu algum problema ao entrar em contato com o servidor deste feed. Por favor, avise o criador do feed sobre este problema." @@ -2133,13 +1972,6 @@ msgstr "Hmmmm, não foi possível carregar este serviço de moderação." msgid "Home" msgstr "Página Inicial" -#: src/Navigation.tsx:247 -#: src/view/com/pager/FeedsTabBarMobile.tsx:123 -#: src/view/screens/PreferencesHomeFeed.tsx:104 -#: src/view/screens/Settings/index.tsx:543 -#~ msgid "Home Feed Preferences" -#~ msgstr "Preferências da Página Inicial" - #: src/view/com/modals/ChangeHandle.tsx:420 msgid "Host:" msgstr "Host:" @@ -2203,11 +2035,6 @@ msgstr "Imagem" msgid "Image alt text" msgstr "Texto alternativo da imagem" -#: src/view/com/util/UserAvatar.tsx:311 -#: src/view/com/util/UserBanner.tsx:118 -#~ msgid "Image options" -#~ msgstr "Opções de imagem" - #: src/lib/moderation/useReportOptions.ts:47 msgid "Impersonation or false claims about identity or affiliation" msgstr "Falsificação de identidade ou alegações falsas sobre identidade ou filiação" @@ -2220,14 +2047,6 @@ msgstr "Insira o código enviado para o seu e-mail para redefinir sua senha" msgid "Input confirmation code for account deletion" msgstr "Insira o código de confirmação para excluir sua conta" -#: src/view/com/auth/create/Step1.tsx:177 -#~ msgid "Input email for Bluesky account" -#~ msgstr "Insira o e-mail para a sua conta do Bluesky" - -#: src/view/com/auth/create/Step1.tsx:151 -#~ msgid "Input invite code to proceed" -#~ msgstr "Insira o convite para continuar" - #: src/view/com/modals/AddAppPasswords.tsx:181 msgid "Input name for app password" msgstr "Insira um nome para a senha de aplicativo" @@ -2248,10 +2067,6 @@ msgstr "Insira a senha da conta {identifier}" msgid "Input the username or email address you used at signup" msgstr "Insira o usuário ou e-mail que você cadastrou" -#: src/view/com/modals/Waitlist.tsx:90 -#~ msgid "Input your email to get on the Bluesky waitlist" -#~ msgstr "Insira seu e-mail para entrar na lista de espera do Bluesky" - #: src/screens/Login/LoginForm.tsx:194 msgid "Input your password" msgstr "Insira sua senha" @@ -2301,19 +2116,6 @@ msgstr "Mostra os posts de quem você segue conforme acontecem." msgid "Jobs" msgstr "Carreiras" -#: src/view/com/modals/Waitlist.tsx:67 -#~ msgid "Join the waitlist" -#~ msgstr "Junte-se à lista de espera" - -#: src/view/com/auth/create/Step1.tsx:174 -#: src/view/com/auth/create/Step1.tsx:178 -#~ msgid "Join the waitlist." -#~ msgstr "Junte-se à lista de espera." - -#: src/view/com/modals/Waitlist.tsx:128 -#~ msgid "Join Waitlist" -#~ msgstr "Junte-se à Lista de Espera" - #: src/screens/Onboarding/index.tsx:24 msgid "Journalism" msgstr "Jornalismo" @@ -2367,14 +2169,6 @@ msgstr "Configurações de Idiomas" msgid "Languages" msgstr "Idiomas" -#: src/view/com/auth/create/StepHeader.tsx:20 -#~ msgid "Last step!" -#~ msgstr "Último passo!" - -#: src/view/com/util/moderation/ContentHider.tsx:103 -#~ msgid "Learn more" -#~ msgstr "Saiba mais" - #: src/components/moderation/ScreenHider.tsx:136 msgid "Learn More" msgstr "Saiba Mais" @@ -2422,11 +2216,6 @@ msgstr "Vamos redefinir sua senha!" msgid "Let's go!" msgstr "Vamos lá!" -#: src/view/com/util/UserAvatar.tsx:248 -#: src/view/com/util/UserBanner.tsx:62 -#~ msgid "Library" -#~ msgstr "Biblioteca" - #: src/view/screens/Settings/index.tsx:498 msgid "Light" msgstr "Claro" @@ -2527,11 +2316,6 @@ msgstr "Lista dessilenciada" msgid "Lists" msgstr "Listas" -#: src/view/com/post-thread/PostThread.tsx:333 -#: src/view/com/post-thread/PostThread.tsx:341 -#~ msgid "Load more posts" -#~ msgstr "Carregar mais posts" - #: src/view/screens/Notifications.tsx:159 msgid "Load new notifications" msgstr "Carregar novas notificações" @@ -2547,10 +2331,6 @@ msgstr "Carregar novos posts" msgid "Loading..." msgstr "Carregando..." -#: src/view/com/modals/ServerInput.tsx:50 -#~ msgid "Local dev server" -#~ msgstr "Servidor de desenvolvimento local" - #: src/Navigation.tsx:221 msgid "Log" msgstr "Registros" @@ -2572,7 +2352,7 @@ msgstr "Fazer login em uma conta que não está listada" #: src/screens/Login/SetNewPasswordForm.tsx:116 msgid "Looks like XXXXX-XXXXX" -msgstr "" +msgstr "Tem esse formato: XXXXX-XXXXX" #: src/view/com/modals/LinkWarning.tsx:79 msgid "Make sure this is where you intend to go!" @@ -2582,14 +2362,6 @@ msgstr "Certifique-se de onde está indo!" msgid "Manage your muted words and tags" msgstr "Gerencie suas palavras/tags silenciadas" -#: src/view/com/auth/create/Step2.tsx:118 -#~ msgid "May not be longer than 253 characters" -#~ msgstr "Não pode ter mais que 253 caracteres" - -#: src/view/com/auth/create/Step2.tsx:109 -#~ msgid "May only contain letters and numbers" -#~ msgstr "Só pode conter letras e números" - #: src/view/screens/Profile.tsx:192 msgid "Media" msgstr "Mídia" @@ -2689,18 +2461,10 @@ msgstr "Mais feeds" msgid "More options" msgstr "Mais opções" -#: src/view/com/util/forms/PostDropdownBtn.tsx:315 -#~ msgid "More post options" -#~ msgstr "Mais opções do post" - #: src/view/screens/PreferencesThreads.tsx:82 msgid "Most-liked replies first" msgstr "Respostas mais curtidas primeiro" -#: src/view/com/auth/create/Step2.tsx:122 -#~ msgid "Must be at least 3 characters" -#~ msgstr "Deve ter no mínimo 3 caracteres" - #: src/components/TagMenu/index.tsx:249 msgid "Mute" msgstr "Silenciar" @@ -2722,13 +2486,9 @@ msgstr "Silenciar contas" msgid "Mute all {displayTag} posts" msgstr "Silenciar posts com {displayTag}" -#: src/components/TagMenu/index.tsx:211 -#~ msgid "Mute all {tag} posts" -#~ msgstr "Silenciar posts com {tag}" - #: src/components/dialogs/MutedWords.tsx:148 msgid "Mute in tags only" -msgstr "Silenciar apenas as tags" +msgstr "Silenciar apenas tags" #: src/components/dialogs/MutedWords.tsx:133 msgid "Mute in text & tags" @@ -2737,16 +2497,12 @@ msgstr "Silenciar texto e tags" #: src/view/screens/ProfileList.tsx:461 #: src/view/screens/ProfileList.tsx:624 msgid "Mute list" -msgstr "Lista de moderação" +msgstr "Silenciar lista" #: src/view/screens/ProfileList.tsx:619 msgid "Mute these accounts?" msgstr "Silenciar estas contas?" -#: src/view/screens/ProfileList.tsx:279 -#~ msgid "Mute this List" -#~ msgstr "Silenciar esta lista" - #: src/components/dialogs/MutedWords.tsx:126 msgid "Mute this word in post text and tags" msgstr "Silenciar esta palavra no conteúdo de um post e tags" @@ -2815,10 +2571,6 @@ msgstr "Meus feeds salvos" msgid "My Saved Feeds" msgstr "Meus Feeds Salvos" -#: src/view/com/auth/server-input/index.tsx:118 -#~ msgid "my-server.com" -#~ msgstr "meu-servidor.com.br" - #: src/view/com/modals/AddAppPasswords.tsx:180 #: src/view/com/modals/CreateOrEditList.tsx:291 msgid "Name" @@ -2852,11 +2604,6 @@ msgstr "Navega para seu perfil" msgid "Need to report a copyright violation?" msgstr "Precisa denunciar uma violação de copyright?" -#: src/view/com/modals/EmbedConsent.tsx:107 -#: src/view/com/modals/EmbedConsent.tsx:123 -#~ msgid "Never load embeds from {0}" -#~ msgstr "Nunca carregar anexos de {0}" - #: src/view/com/auth/onboarding/WelcomeDesktop.tsx:72 #: src/view/com/auth/onboarding/WelcomeMobile.tsx:74 msgid "Never lose access to your followers and data." @@ -2866,10 +2613,6 @@ msgstr "Nunca perca o acesso aos seus seguidores e dados." msgid "Never lose access to your followers or data." msgstr "Nunca perca o acesso aos seus seguidores ou dados." -#: src/components/dialogs/MutedWords.tsx:293 -#~ msgid "Nevermind" -#~ msgstr "Deixa pra lá" - #: src/view/com/modals/ChangeHandle.tsx:519 msgid "Nevermind, create a handle for me" msgstr "Deixa pra lá, crie um usuário pra mim" @@ -2973,7 +2716,7 @@ msgstr "Você não está mais seguindo {0}" #: src/screens/Signup/StepHandle.tsx:114 msgid "No longer than 253 characters" -msgstr "" +msgstr "No máximo 253 caracteres" #: src/view/com/notifications/Feed.tsx:109 msgid "No notifications yet!" @@ -3056,15 +2799,11 @@ msgstr "Nudez" #: src/lib/moderation/useReportOptions.ts:71 msgid "Nudity or adult content not labeled as such" -msgstr "" - -#: src/lib/moderation/useReportOptions.ts:71 -#~ msgid "Nudity or pornography not labeled as such" -#~ msgstr "Nudez ou pornografia sem aviso aplicado" +msgstr "Nudez ou pornografia sem aviso aplicado" #: src/screens/Signup/index.tsx:142 msgid "of" -msgstr "" +msgstr "de" #: src/lib/moderation/useLabelBehaviorDescription.ts:11 msgid "Off" @@ -3105,7 +2844,7 @@ msgstr "Apenas {0} pode responder." #: src/screens/Signup/StepHandle.tsx:97 msgid "Only contains letters, numbers, and hyphens" -msgstr "" +msgstr "Contém apenas letras, números e hífens" #: src/components/Lists.tsx:75 msgid "Oops, something went wrong!" @@ -3121,10 +2860,6 @@ msgstr "Opa!" msgid "Open" msgstr "Abrir" -#: src/view/screens/Moderation.tsx:75 -#~ msgid "Open content filtering settings" -#~ msgstr "Abrir configurações de filtro" - #: src/view/com/composer/Composer.tsx:491 #: src/view/com/composer/Composer.tsx:492 msgid "Open emoji picker" @@ -3142,10 +2877,6 @@ msgstr "Abrir links no navegador interno" msgid "Open muted words and tags settings" msgstr "Abrir opções de palavras/tags silenciadas" -#: src/view/screens/Moderation.tsx:92 -#~ msgid "Open muted words settings" -#~ msgstr "Abrir configurações das palavras silenciadas" - #: src/view/com/home/HomeHeaderLayoutMobile.tsx:50 msgid "Open navigation" msgstr "Abrir navegação" @@ -3191,10 +2922,6 @@ msgstr "Abre definições de idioma configuráveis" msgid "Opens device photo gallery" msgstr "Abre a galeria de fotos do dispositivo" -#: src/view/com/profile/ProfileHeader.tsx:420 -#~ msgid "Opens editor for profile display name, avatar, background image, and description" -#~ msgstr "Abre o editor de nome, avatar, banner e descrição do perfil" - #: src/view/screens/Settings/index.tsx:669 msgid "Opens external embeds settings" msgstr "Abre as configurações de anexos externos" @@ -3211,14 +2938,6 @@ msgstr "Abre o fluxo de criação de conta do Bluesky" msgid "Opens flow to sign into your existing Bluesky account" msgstr "Abre o fluxo de entrar na sua conta do Bluesky" -#: src/view/com/profile/ProfileHeader.tsx:575 -#~ msgid "Opens followers list" -#~ msgstr "Abre lista de seguidores" - -#: src/view/com/profile/ProfileHeader.tsx:594 -#~ msgid "Opens following list" -#~ msgstr "Abre lista de seguidos" - #: src/view/com/modals/InviteCodes.tsx:173 msgid "Opens list of invite codes" msgstr "Abre a lista de códigos de convite" @@ -3419,14 +3138,6 @@ msgstr "Por favor, insira um nome único para esta Senha de Aplicativo ou use no msgid "Please enter a valid word, tag, or phrase to mute" msgstr "Por favor, insira uma palavra, tag ou frase para silenciar" -#: src/view/com/auth/create/state.ts:170 -#~ msgid "Please enter the code you received by SMS." -#~ msgstr "Por favor, digite o código recebido via SMS." - -#: src/view/com/auth/create/Step2.tsx:282 -#~ msgid "Please enter the verification code sent to {phoneNumberFormatted}." -#~ msgstr "Por favor, digite o código de verificação enviado para {phoneNumberFormatted}." - #: src/screens/Signup/state.ts:220 msgid "Please enter your email." msgstr "Por favor, digite o seu e-mail." @@ -3439,11 +3150,6 @@ msgstr "Por favor, digite sua senha também:" msgid "Please explain why you think this label was incorrectly applied by {0}" msgstr "Por favor, explique por que você acha que este rótulo foi aplicado incorrentamente por {0}" -#: src/view/com/modals/AppealLabel.tsx:72 -#: src/view/com/modals/AppealLabel.tsx:75 -#~ msgid "Please tell us why you think this content warning was incorrectly applied!" -#~ msgstr "Por favor, diga-nos por que você acha que este aviso de conteúdo foi aplicado incorretamente!" - #: src/view/com/modals/VerifyEmail.tsx:101 msgid "Please Verify Your Email" msgstr "Por favor, verifique seu e-mail" @@ -3460,10 +3166,6 @@ msgstr "Política" msgid "Porn" msgstr "Pornografia" -#: src/lib/moderation/useGlobalLabelStrings.ts:34 -#~ msgid "Pornography" -#~ msgstr "Pornografia" - #: src/view/com/composer/Composer.tsx:367 #: src/view/com/composer/Composer.tsx:375 msgctxt "action" @@ -3538,7 +3240,7 @@ msgstr "Link Potencialmente Enganoso" #: src/components/forms/HostingProvider.tsx:45 msgid "Press to change hosting provider" -msgstr "" +msgstr "Trocar de provedor de hospedagem" #: src/components/Error.tsx:74 #: src/components/Lists.tsx:80 @@ -3659,10 +3361,6 @@ msgstr "Usuários Recomendados" msgid "Remove" msgstr "Remover" -#: src/view/com/feeds/FeedSourceCard.tsx:108 -#~ msgid "Remove {0} from my feeds?" -#~ msgstr "Remover {0} dos meus feeds?" - #: src/view/com/util/AccountDropdownBtn.tsx:22 msgid "Remove account" msgstr "Remover conta" @@ -3710,18 +3408,10 @@ msgstr "Remover palavra silenciada da lista" msgid "Remove repost" msgstr "Desfazer repost" -#: src/view/com/feeds/FeedSourceCard.tsx:175 -#~ msgid "Remove this feed from my feeds?" -#~ msgstr "Remover este feed dos meus feeds?" - #: src/view/com/posts/FeedErrorMessage.tsx:202 msgid "Remove this feed from your saved feeds" msgstr "Remover este feed dos feeds salvos" -#: src/view/com/posts/FeedErrorMessage.tsx:132 -#~ msgid "Remove this feed from your saved feeds?" -#~ msgstr "Remover este feed dos feeds salvos?" - #: src/view/com/modals/ListAddRemoveUsers.tsx:199 #: src/view/com/modals/UserAddRemoveLists.tsx:152 msgid "Removed from list" @@ -3762,10 +3452,6 @@ msgctxt "description" msgid "Reply to <0/>" msgstr "Responder <0/>" -#: src/view/com/modals/report/Modal.tsx:166 -#~ msgid "Report {collectionName}" -#~ msgstr "Denunciar {collectionName}" - #: src/view/com/profile/ProfileMenu.tsx:319 #: src/view/com/profile/ProfileMenu.tsx:322 msgid "Report Account" @@ -3773,7 +3459,7 @@ msgstr "Denunciar Conta" #: src/components/ReportDialog/index.tsx:49 msgid "Report dialog" -msgstr "" +msgstr "Janela de denúncia" #: src/view/screens/ProfileFeed.tsx:352 #: src/view/screens/ProfileFeed.tsx:354 @@ -3872,10 +3558,6 @@ msgstr "Código de redefinição" msgid "Reset Code" msgstr "Código de Redefinição" -#: src/view/screens/Settings/index.tsx:824 -#~ msgid "Reset onboarding" -#~ msgstr "Redefinir tutoriais" - #: src/view/screens/Settings/index.tsx:858 #: src/view/screens/Settings/index.tsx:861 msgid "Reset onboarding state" @@ -3885,10 +3567,6 @@ msgstr "Redefinir tutoriais" msgid "Reset password" msgstr "Redefinir senha" -#: src/view/screens/Settings/index.tsx:814 -#~ msgid "Reset preferences" -#~ msgstr "Redefinir configurações" - #: src/view/screens/Settings/index.tsx:848 #: src/view/screens/Settings/index.tsx:851 msgid "Reset preferences state" @@ -3923,10 +3601,6 @@ msgstr "Tenta a última ação, que deu erro" msgid "Retry" msgstr "Tente novamente" -#: src/view/com/auth/create/Step2.tsx:247 -#~ msgid "Retry." -#~ msgstr "Tentar novamente." - #: src/components/Error.tsx:86 #: src/view/screens/ProfileList.tsx:917 msgid "Return to previous page" @@ -4037,18 +3711,10 @@ msgstr "Pesquisar por \"{query}\"" msgid "Search for all posts by @{authorHandle} with tag {displayTag}" msgstr "Pesquisar por posts de @{authorHandle} com a tag {displayTag}" -#: src/components/TagMenu/index.tsx:145 -#~ msgid "Search for all posts by @{authorHandle} with tag {tag}" -#~ msgstr "Pesquisar por posts de @{authorHandle} com a tag {tag}" - #: src/components/TagMenu/index.tsx:94 msgid "Search for all posts with tag {displayTag}" msgstr "Pesquisar por posts com a tag {displayTag}" -#: src/components/TagMenu/index.tsx:90 -#~ msgid "Search for all posts with tag {tag}" -#~ msgstr "Pesquisar por posts com a tag {tag}" - #: src/view/com/auth/LoggedOut.tsx:105 #: src/view/com/auth/LoggedOut.tsx:106 #: src/view/com/modals/ListAddRemoveUsers.tsx:70 @@ -4075,14 +3741,6 @@ msgstr "Ver posts com <0>{displayTag}" msgid "See <0>{displayTag} posts by this user" msgstr "Ver posts com <0>{displayTag} deste usuário" -#: src/components/TagMenu/index.tsx:128 -#~ msgid "See <0>{tag} posts" -#~ msgstr "Ver posts com <0>{tag}" - -#: src/components/TagMenu/index.tsx:189 -#~ msgid "See <0>{tag} posts by this user" -#~ msgstr "Ver posts com <0>{tag} deste usuário" - #: src/view/screens/SavedFeeds.tsx:163 msgid "See this guide" msgstr "Veja o guia" @@ -4097,7 +3755,7 @@ msgstr "Selecionar {item}" #: src/screens/Login/ChooseAccountForm.tsx:61 msgid "Select account" -msgstr "" +msgstr "Selecione uma conta" #: src/screens/Login/index.tsx:120 msgid "Select from an existing account" @@ -4115,11 +3773,6 @@ msgstr "Selecionar moderador" msgid "Select option {i} of {numItems}" msgstr "Seleciona opção {i} de {numItems}" -#: src/view/com/auth/create/Step1.tsx:96 -#: src/view/com/auth/login/LoginForm.tsx:153 -#~ msgid "Select service" -#~ msgstr "Selecionar serviço" - #: src/screens/Onboarding/StepSuggestedAccounts/index.tsx:52 msgid "Select some accounts below to follow" msgstr "Selecione algumas contas para seguir" @@ -4144,17 +3797,13 @@ msgstr "Selecione o que você quer (ou não) ver, e cuidaremos do resto." msgid "Select which languages you want your subscribed feeds to include. If none are selected, all languages will be shown." msgstr "Selecione quais idiomas você deseja ver nos seus feeds. Se nenhum for selecionado, todos os idiomas serão exibidos." -#: src/view/screens/LanguageSettings.tsx:98 -#~ msgid "Select your app language for the default text to display in the app" -#~ msgstr "Selecione o idioma do seu aplicativo" - #: src/view/screens/LanguageSettings.tsx:98 msgid "Select your app language for the default text to display in the app." msgstr "Selecione o idioma do seu aplicativo" #: src/screens/Signup/StepInfo/index.tsx:133 msgid "Select your date of birth" -msgstr "" +msgstr "Selecione sua data de nascimento" #: src/screens/Onboarding/StepInterests/index.tsx:200 msgid "Select your interests from the options below" @@ -4196,10 +3845,6 @@ msgstr "Enviar comentários" msgid "Send report" msgstr "Denunciar" -#: src/view/com/modals/report/SendReportButton.tsx:45 -#~ msgid "Send Report" -#~ msgstr "Denunciar" - #: src/components/ReportDialog/SelectLabelerView.tsx:44 msgid "Send report to {0}" msgstr "Denunciar via {0}" @@ -4212,48 +3857,14 @@ msgstr "Envia o e-mail com o código de confirmação para excluir a conta" msgid "Server address" msgstr "URL do servidor" -#: src/view/com/modals/ContentFilteringSettings.tsx:311 -#~ msgid "Set {value} for {labelGroup} content moderation policy" -#~ msgstr "Definir {value} para o filtro de moderação {labelGroup}" - -#: src/view/com/modals/ContentFilteringSettings.tsx:160 -#: src/view/com/modals/ContentFilteringSettings.tsx:179 -#~ msgctxt "action" -#~ msgid "Set Age" -#~ msgstr "Definir Idade" - #: src/screens/Moderation/index.tsx:304 msgid "Set birthdate" msgstr "Definir data de nascimento" -#: src/view/screens/Settings/index.tsx:488 -#~ msgid "Set color theme to dark" -#~ msgstr "Definir o tema de cor para escuro" - -#: src/view/screens/Settings/index.tsx:481 -#~ msgid "Set color theme to light" -#~ msgstr "Definir o tema de cor para claro" - -#: src/view/screens/Settings/index.tsx:475 -#~ msgid "Set color theme to system setting" -#~ msgstr "Definir o tema para acompanhar o sistema" - -#: src/view/screens/Settings/index.tsx:514 -#~ msgid "Set dark theme to the dark theme" -#~ msgstr "Definir o tema escuro para o padrão" - -#: src/view/screens/Settings/index.tsx:507 -#~ msgid "Set dark theme to the dim theme" -#~ msgstr "Definir o tema escuro para a versão menos escura" - #: src/screens/Login/SetNewPasswordForm.tsx:102 msgid "Set new password" msgstr "Definir uma nova senha" -#: src/view/com/auth/create/Step1.tsx:202 -#~ msgid "Set password" -#~ msgstr "Definir senha" - #: src/view/screens/PreferencesFollowingFeed.tsx:225 msgid "Set this setting to \"No\" to hide all quote posts from your feed. Reposts will still be visible." msgstr "Defina esta configuração como \"Não\" para ocultar todas as citações do seu feed. Reposts ainda serão visíveis." @@ -4306,10 +3917,6 @@ msgstr "Define o tema escuro para o menos escuro" msgid "Sets email for password reset" msgstr "Configura o e-mail para recuperação de senha" -#: src/view/com/auth/login/ForgotPasswordForm.tsx:122 -#~ msgid "Sets hosting provider for password reset" -#~ msgstr "Configura o provedor de hospedagem para recuperação de senha" - #: src/view/com/modals/crop-image/CropImage.web.tsx:124 msgid "Sets image aspect ratio to square" msgstr "Define a proporção da imagem para quadrada" @@ -4322,11 +3929,6 @@ msgstr "Define a proporção da imagem para alta" msgid "Sets image aspect ratio to wide" msgstr "Define a proporção da imagem para comprida" -#: src/view/com/auth/create/Step1.tsx:97 -#: src/view/com/auth/login/LoginForm.tsx:154 -#~ msgid "Sets server for the Bluesky client" -#~ msgstr "Configura o servidor para o cliente do Bluesky" - #: src/Navigation.tsx:139 #: src/view/screens/Settings/index.tsx:313 #: src/view/shell/desktop/LeftNav.tsx:437 @@ -4371,11 +3973,11 @@ msgstr "Compartilhar feed" #: src/view/com/modals/LinkWarning.tsx:89 #: src/view/com/modals/LinkWarning.tsx:95 msgid "Share Link" -msgstr "" +msgstr "Compartilhar Link" #: src/view/com/modals/LinkWarning.tsx:92 msgid "Shares the linked website" -msgstr "" +msgstr "Compartilha o link" #: src/components/moderation/ContentHider.tsx:115 #: src/components/moderation/LabelPreference.tsx:136 @@ -4403,10 +4005,6 @@ msgstr "Mostrar rótulo" msgid "Show badge and filter from feeds" msgstr "Mostrar rótulo e filtrar dos feeds" -#: src/view/com/modals/EmbedConsent.tsx:87 -#~ msgid "Show embeds from {0}" -#~ msgstr "Mostrar anexos de {0}" - #: src/screens/Profile/Header/ProfileHeaderStandard.tsx:200 msgid "Show follows similar to {0}" msgstr "Mostrar usuários parecidos com {0}" @@ -4482,10 +4080,6 @@ msgstr "Mostrar aviso" msgid "Show warning and filter from feeds" msgstr "Mostrar aviso e filtrar dos feeds" -#: src/view/com/profile/ProfileHeader.tsx:462 -#~ msgid "Shows a list of users similar to this user." -#~ msgstr "Mostra uma lista de usuários parecidos com este" - #: src/view/com/post-thread/PostThreadFollowBtn.tsx:130 msgid "Shows posts from {0} in your feed" msgstr "Mostra posts de {0} no seu feed" @@ -4511,12 +4105,6 @@ msgstr "Mostra posts de {0} no seu feed" msgid "Sign in" msgstr "Fazer login" -#: src/view/com/auth/HomeLoggedOutCTA.tsx:82 -#: src/view/com/auth/SplashScreen.tsx:86 -#: src/view/com/auth/SplashScreen.web.tsx:91 -#~ msgid "Sign In" -#~ msgstr "Fazer Login" - #: src/components/AccountList.tsx:109 msgid "Sign in as {0}" msgstr "Fazer login como {0}" @@ -4525,10 +4113,6 @@ msgstr "Fazer login como {0}" msgid "Sign in as..." msgstr "Fazer login como..." -#: src/view/com/auth/login/LoginForm.tsx:140 -#~ msgid "Sign into" -#~ msgstr "Fazer login" - #: src/view/screens/Settings/index.tsx:107 #: src/view/screens/Settings/index.tsx:110 msgid "Sign out" @@ -4563,10 +4147,6 @@ msgstr "Entrou como" msgid "Signed in as @{0}" msgstr "autenticado como @{0}" -#: src/view/com/modals/SwitchAccount.tsx:70 -#~ msgid "Signs {0} out of Bluesky" -#~ msgstr "Desloga a conta {0}" - #: src/screens/Onboarding/StepInterests/index.tsx:239 #: src/screens/Onboarding/StepSuggestedAccounts/index.tsx:203 #: src/view/com/auth/onboarding/WelcomeMobile.tsx:35 @@ -4581,24 +4161,12 @@ msgstr "Pular" msgid "Software Dev" msgstr "Desenvolvimento de software" -#: src/view/com/modals/ProfilePreview.tsx:62 -#~ msgid "Something went wrong and we're not sure what." -#~ msgstr "Algo deu errado e meio que não sabemos o que houve." - #: src/components/ReportDialog/index.tsx:59 #: src/screens/Moderation/index.tsx:114 #: src/screens/Profile/Sections/Labels.tsx:76 msgid "Something went wrong, please try again." msgstr "Algo deu errado. Por favor, tente novamente." -#: src/components/Lists.tsx:203 -#~ msgid "Something went wrong!" -#~ msgstr "Algo deu errado!" - -#: src/view/com/modals/Waitlist.tsx:51 -#~ msgid "Something went wrong. Check your email and try again." -#~ msgstr "Algo deu errado. Verifique seu e-mail e tente novamente." - #: src/App.native.tsx:66 msgid "Sorry! Your session expired. Please log in again." msgstr "Opa! Sua sessão expirou. Por favor, entre novamente." @@ -4637,11 +4205,7 @@ msgstr "Página de status" #: src/screens/Signup/index.tsx:142 msgid "Step" -msgstr "" - -#: src/view/com/auth/create/StepHeader.tsx:22 -#~ msgid "Step {0} of {numSteps}" -#~ msgstr "Passo {0} de {numSteps}" +msgstr "Passo" #: src/view/screens/Settings/index.tsx:292 msgid "Storage cleared, you need to restart the app now." @@ -4729,10 +4293,6 @@ msgstr "tag" msgid "Tag menu: {displayTag}" msgstr "Menu da tag: {displayTag}" -#: src/components/TagMenu/index.tsx:74 -#~ msgid "Tag menu: {tag}" -#~ msgstr "Menu da tag: {tag}" - #: src/view/com/modals/crop-image/CropImage.web.tsx:113 msgid "Tall" msgstr "Alto" @@ -4956,10 +4516,6 @@ msgstr "Este conteúdo não está disponível porque um dos usuários bloqueou o msgid "This content is not viewable without a Bluesky account." msgstr "Este conteúdo não é visível sem uma conta do Bluesky." -#: src/view/screens/Settings/ExportCarDialog.tsx:75 -#~ msgid "This feature is in beta. You can read more about repository exports in <0>this blogpost." -#~ msgstr "Esta funcionalidade está em beta. Você pode ler mais sobre exportação de repositórios <0>neste post do nosso blog." - #: src/view/screens/Settings/ExportCarDialog.tsx:75 msgid "This feature is in beta. You can read more about repository exports in <0>this blogpost." msgstr "Esta funcionalidade está em beta. Você pode ler mais sobre exportação de repositórios <0>neste post do nosso blog." @@ -5048,14 +4604,6 @@ msgstr "Este usuário te bloqueou. Você não pode ver este conteúdo." msgid "This user has requested that their content only be shown to signed-in users." msgstr "Este usuário requisitou que seu conteúdo só seja visível para usuários autenticados." -#: src/view/com/modals/ModerationDetails.tsx:42 -#~ msgid "This user is included in the <0/> list which you have blocked." -#~ msgstr "Este usuário está incluído na lista <0/>, que você bloqueou." - -#: src/view/com/modals/ModerationDetails.tsx:74 -#~ msgid "This user is included in the <0/> list which you have muted." -#~ msgstr "Este usuário está incluído na lista <0/>, que você silenciou." - #: src/components/moderation/ModerationDetailsDialog.tsx:55 msgid "This user is included in the <0>{0} list which you have blocked." msgstr "Este usuário está incluído na lista <0>{0}, que você bloqueou." @@ -5076,10 +4624,6 @@ msgstr "Este aviso só está disponível para publicações com mídia anexada." msgid "This will delete {0} from your muted words. You can always add it back later." msgstr "Isso removerá {0} das suas palavras silenciadas. Você pode adicioná-la novamente depois." -#: src/view/com/util/forms/PostDropdownBtn.tsx:282 -#~ msgid "This will hide this post from your feeds." -#~ msgstr "Isso ocultará este post de seus feeds." - #: src/view/screens/Settings/index.tsx:574 msgid "Thread preferences" msgstr "Preferências das Threads" @@ -5198,10 +4742,6 @@ msgstr "Deixar de seguir {0}" msgid "Unfollow Account" msgstr "Deixar de seguir" -#: src/view/com/auth/create/state.ts:262 -#~ msgid "Unfortunately, you do not meet the requirements to create an account." -#~ msgstr "Infelizmente, você não atende aos requisitos para criar uma conta." - #: src/view/com/util/post-ctrls/PostCtrls.tsx:195 msgid "Unlike" msgstr "Descurtir" @@ -5228,10 +4768,6 @@ msgstr "Dessilenciar conta" msgid "Unmute all {displayTag} posts" msgstr "Dessilenciar posts com {displayTag}" -#: src/components/TagMenu/index.tsx:210 -#~ msgid "Unmute all {tag} posts" -#~ msgstr "Dessilenciar posts com {tag}" - #: src/view/com/util/forms/PostDropdownBtn.tsx:251 #: src/view/com/util/forms/PostDropdownBtn.tsx:256 msgid "Unmute thread" @@ -5250,10 +4786,6 @@ msgstr "Desafixar da tela inicial" msgid "Unpin moderation list" msgstr "Desafixar lista de moderação" -#: src/view/screens/ProfileFeed.tsx:346 -#~ msgid "Unsave" -#~ msgstr "Remover" - #: src/screens/Profile/Header/ProfileHeaderLabeler.tsx:219 msgid "Unsubscribe" msgstr "Desinscrever-se" @@ -5270,10 +4802,6 @@ msgstr "Conteúdo Sexual Indesejado" msgid "Update {displayName} in Lists" msgstr "Atualizar {displayName} nas Listas" -#: src/lib/hooks/useOTAUpdate.ts:15 -#~ msgid "Update Available" -#~ msgstr "Atualização Disponível" - #: src/view/com/modals/ChangeHandle.tsx:508 msgid "Update to {handle}" msgstr "Alterar para {handle}" @@ -5364,10 +4892,6 @@ msgstr "Usuário Bloqueia Você" msgid "User Blocks You" msgstr "Este Usuário Te Bloqueou" -#: src/view/com/auth/create/Step2.tsx:79 -#~ msgid "User handle" -#~ msgstr "Usuário" - #: src/view/com/lists/ListCard.tsx:85 #: src/view/com/modals/UserAddRemoveLists.tsx:198 msgid "User list by {0}" @@ -5446,7 +4970,7 @@ msgstr "Verificar Seu E-mail" #: src/view/screens/Settings/index.tsx:893 msgid "Version {0}" -msgstr "" +msgstr "Versão {0}" #: src/screens/Onboarding/index.tsx:42 msgid "Video Games" @@ -5512,10 +5036,6 @@ msgstr "Avisar" msgid "Warn content and filter from feeds" msgstr "Avisar e filtrar dos feeds" -#: src/screens/Onboarding/StepAlgoFeeds/index.tsx:134 -#~ msgid "We also think you'll like \"For You\" by Skygaze:" -#~ msgstr "Também recomendamos o \"For You\", do Skygaze:" - #: src/screens/Hashtag.tsx:133 msgid "We couldn't find any results for that hashtag." msgstr "Não encontramos nenhum post com esta hashtag." @@ -5532,10 +5052,6 @@ msgstr "Esperamos que você se divirta. Lembre-se, o Bluesky é:" msgid "We ran out of posts from your follows. Here's the latest from <0/>." msgstr "Não temos mais posts de quem você segue. Aqui estão os mais novos de <0/>." -#: src/screens/Onboarding/StepAlgoFeeds/index.tsx:118 -#~ msgid "We recommend \"For You\" by Skygaze:" -#~ msgstr "Recomendamos o \"Para você\", do Skygaze:" - #: src/components/dialogs/MutedWords.tsx:203 msgid "We recommend avoiding common words that appear in many posts, since it can result in no posts being shown." msgstr "Não recomendamos utilizar palavras comuns que aparecem em muitos posts, já que isso pode resultar em filtrar todos eles." @@ -5560,10 +5076,6 @@ msgstr "Não conseguimos conectar. Por favor, tente novamente para continuar con msgid "We will let you know when your account is ready." msgstr "Avisaremos quando sua conta estiver pronta." -#: src/view/com/modals/AppealLabel.tsx:48 -#~ msgid "We'll look into your appeal promptly." -#~ msgstr "Avaliaremos sua contestação o quanto antes." - #: src/screens/Onboarding/StepInterests/index.tsx:142 msgid "We'll use this to help customize your experience." msgstr "Usaremos isto para customizar a sua experiência." @@ -5601,10 +5113,6 @@ msgstr "Bem-vindo ao <0>Bluesky" msgid "What are your interests?" msgstr "Do que você gosta?" -#: src/view/com/modals/report/Modal.tsx:169 -#~ msgid "What is the issue with this {collectionName}?" -#~ msgstr "Qual é o problema com este {collectionName}?" - #: src/view/com/auth/SplashScreen.tsx:58 #: src/view/com/auth/SplashScreen.web.tsx:84 #: src/view/com/composer/Composer.tsx:296 @@ -5747,10 +5255,6 @@ msgstr "Você silenciou esta conta." msgid "You have muted this user" msgstr "Você silenciou este usuário." -#: src/view/com/modals/ModerationDetails.tsx:87 -#~ msgid "You have muted this user." -#~ msgstr "Você silenciou este usuário." - #: src/view/com/feeds/ProfileFeedgens.tsx:136 msgid "You have no feeds." msgstr "Você não tem feeds." @@ -5764,10 +5268,6 @@ msgstr "Você não tem listas." msgid "You have not blocked any accounts yet. To block an account, go to their profile and select \"Block account\" from the menu on their account." msgstr "Você ainda não bloqueou nenhuma conta. Para bloquear uma conta, acesse um perfil e selecione \"Bloquear conta\" no menu." -#: src/view/screens/ModerationBlockedAccounts.tsx:132 -#~ msgid "You have not blocked any accounts yet. To block an account, go to their profile and selected \"Block account\" from the menu on their account." -#~ msgstr "Você ainda não bloqueou nenhuma conta. Para bloquear uma conta, acesse um perfil e selecione \"Bloquear conta\" no menu." - #: src/view/screens/AppPasswords.tsx:89 msgid "You have not created any app passwords yet. You can create one by pressing the button below." msgstr "Você ainda não criou nenhuma senha de aplicativo. Você pode criar uma pressionando o botão abaixo." @@ -5776,10 +5276,6 @@ msgstr "Você ainda não criou nenhuma senha de aplicativo. Você pode criar uma msgid "You have not muted any accounts yet. To mute an account, go to their profile and select \"Mute account\" from the menu on their account." msgstr "Você ainda não silenciou nenhuma conta. Para silenciar uma conta, acesse um perfil e selecione \"Silenciar conta\" no menu." -#: src/view/screens/ModerationMutedAccounts.tsx:131 -#~ msgid "You have not muted any accounts yet. To mute an account, go to their profile and selected \"Mute account\" from the menu on their account." -#~ msgstr "Você ainda não silenciou nenhuma conta. Para silenciar uma conta, acesse um perfil e selecione \"Silenciar conta\" no menu." - #: src/components/dialogs/MutedWords.tsx:249 msgid "You haven't muted any words or tags yet" msgstr "Você não silenciou nenhuma palavra ou tag ainda" @@ -5790,11 +5286,7 @@ msgstr "Você pode contestar estes rótulos se você acha que estão errados." #: src/screens/Signup/StepInfo/Policies.tsx:79 msgid "You must be 13 years of age or older to sign up." -msgstr "" - -#: src/view/com/modals/ContentFilteringSettings.tsx:175 -#~ msgid "You must be 18 or older to enable adult content." -#~ msgstr "Você precisa ser maior de idade para habilitar conteúdo adulto." +msgstr "Você precisa ter no mínimo 13 anos de idade para se cadastrar." #: src/screens/Onboarding/StepModeration/AdultContentEnabledPref.tsx:110 msgid "You must be 18 years or older to enable adult content" @@ -5869,10 +5361,6 @@ msgstr "Seu feed inicial é o \"Seguindo\"" msgid "Your email appears to be invalid." msgstr "Seu e-mail parece ser inválido." -#: src/view/com/modals/Waitlist.tsx:109 -#~ msgid "Your email has been saved! We'll be in touch soon." -#~ msgstr "Seu e-mail foi salvo! Logo entraremos em contato." - #: src/view/com/modals/ChangeEmail.tsx:125 msgid "Your email has been updated but not verified. As a next step, please verify your new email." msgstr "Seu e-mail foi atualizado mas não foi verificado. Como próximo passo, por favor verifique seu novo e-mail." @@ -5893,12 +5381,6 @@ msgstr "Seu identificador completo será" msgid "Your full handle will be <0>@{0}" msgstr "Seu usuário completo será <0>@{0}" -#: src/view/screens/Settings.tsx:430 -#: src/view/shell/desktop/RightNav.tsx:137 -#: src/view/shell/Drawer.tsx:660 -#~ msgid "Your invite codes are hidden when logged in using an App Password" -#~ msgstr "Seus códigos de convite estão ocultos quando conectado com uma Senha do Aplicativo" - #: src/components/dialogs/MutedWords.tsx:220 msgid "Your muted words" msgstr "Suas palavras silenciadas" From aa1d4d4e266495957137394557336155ac43c08f Mon Sep 17 00:00:00 2001 From: imbstt <83777889+imbstt@users.noreply.github.com> Date: Fri, 12 Apr 2024 23:48:08 +0200 Subject: [PATCH 022/167] Update German translations (#3466) * Change occurrences of "likt" to "liked" * Improve existing German translations * Translate new strings for German translation * Apply suggestions from code review Co-authored-by: Felix Siebeneicker --------- Co-authored-by: Felix Siebeneicker --- src/locale/locales/de/messages.po | 180 +++++++++++++++--------------- 1 file changed, 90 insertions(+), 90 deletions(-) diff --git a/src/locale/locales/de/messages.po b/src/locale/locales/de/messages.po index 28fd2256b6..8674b2a8d5 100644 --- a/src/locale/locales/de/messages.po +++ b/src/locale/locales/de/messages.po @@ -100,11 +100,11 @@ msgstr "Konto stummgeschaltet" #: src/components/moderation/ModerationDetailsDialog.tsx:93 #: src/lib/moderation/useModerationCauseDescription.ts:91 msgid "Account Muted" -msgstr "Konto Stummgeschaltet" +msgstr "Konto stummgeschaltet" #: src/components/moderation/ModerationDetailsDialog.tsx:82 msgid "Account Muted by List" -msgstr "Konto stummgeschaltet nach Liste" +msgstr "Konto stummgeschaltet gemäß Liste" #: src/view/com/util/AccountDropdownBtn.tsx:41 msgid "Account options" @@ -121,11 +121,11 @@ msgstr "Konto entblockiert" #: src/view/com/profile/ProfileMenu.tsx:166 msgid "Account unfollowed" -msgstr "" +msgstr "Konto entfolgt" #: src/view/com/profile/ProfileMenu.tsx:102 msgid "Account unmuted" -msgstr "Konto Stummschaltung aufgehoben" +msgstr "Stummschaltung für Konto aufgehoben" #: src/components/dialogs/MutedWords.tsx:164 #: src/view/com/auth/onboarding/RecommendedFeedsItem.tsx:150 @@ -270,7 +270,7 @@ msgstr "Eine E-Mail wurde an deine vorherige Adresse {0} gesendet. Sie enthält #: src/lib/moderation/useReportOptions.ts:26 msgid "An issue not included in these options" -msgstr "" +msgstr "Ein Problem, das hier nicht aufgelistet ist" #: src/view/com/profile/FollowButton.tsx:35 #: src/view/com/profile/FollowButton.tsx:45 @@ -290,7 +290,7 @@ msgstr "Tiere" #: src/lib/moderation/useReportOptions.ts:31 msgid "Anti-Social Behavior" -msgstr "" +msgstr "Asoziales Verhalten" #: src/view/screens/LanguageSettings.tsx:95 msgid "App Language" @@ -325,7 +325,7 @@ msgstr "" #: src/components/moderation/LabelsOnMeDialog.tsx:201 msgid "Appeal \"{0}\" label" -msgstr "" +msgstr "Kennzeichnung \"{0}\" anfechten" #: src/view/com/util/forms/PostDropdownBtn.tsx:337 #: src/view/com/util/forms/PostDropdownBtn.tsx:346 @@ -338,7 +338,7 @@ msgstr "" #: src/components/moderation/LabelsOnMeDialog.tsx:192 msgid "Appeal submitted." -msgstr "" +msgstr "Anfechtung abgeschickt." #: src/view/com/util/moderation/LabelInfo.tsx:52 #~ msgid "Appeal this decision" @@ -358,7 +358,7 @@ msgstr "Bist du sicher, dass du das App-Passwort \"{name}\" löschen möchtest?" #: src/view/com/feeds/FeedSourceCard.tsx:280 msgid "Are you sure you want to remove {0} from your feeds?" -msgstr "" +msgstr "Bist du sicher, dass du {0} von deinen Feeds entfernen möchtest?" #: src/view/com/composer/Composer.tsx:509 msgid "Are you sure you'd like to discard this draft?" @@ -386,7 +386,7 @@ msgstr "Künstlerische oder nicht-erotische Nacktheit." #: src/screens/Signup/StepHandle.tsx:118 msgid "At least 3 characters" -msgstr "" +msgstr "Mindestens 3 Zeichen" #: src/components/moderation/LabelsOnMeDialog.tsx:246 #: src/components/moderation/LabelsOnMeDialog.tsx:247 @@ -428,7 +428,7 @@ msgstr "Geburtstag:" #: src/screens/Profile/Header/ProfileHeaderStandard.tsx:287 #: src/view/com/profile/ProfileMenu.tsx:361 msgid "Block" -msgstr "" +msgstr "Blockieren" #: src/view/com/profile/ProfileMenu.tsx:300 #: src/view/com/profile/ProfileMenu.tsx:307 @@ -437,7 +437,7 @@ msgstr "Konto blockieren" #: src/view/com/profile/ProfileMenu.tsx:344 msgid "Block Account?" -msgstr "" +msgstr "Konto blockieren?" #: src/view/screens/ProfileList.tsx:530 msgid "Block accounts" @@ -480,19 +480,19 @@ msgstr "Blockierte Konten können nicht in deinen Threads antworten, dich erwäh #: src/view/com/post-thread/PostThread.tsx:313 msgid "Blocked post." -msgstr "Gesperrter Beitrag." +msgstr "Blockierter Beitrag." #: src/screens/Profile/Sections/Labels.tsx:152 msgid "Blocking does not prevent this labeler from placing labels on your account." -msgstr "" +msgstr "Blockieren hindert diesen Kennzeichnungsdienst nicht daran, Kennzeichnungen zu deinem Konto hinzuzufügen." #: src/view/screens/ProfileList.tsx:631 msgid "Blocking is public. Blocked accounts cannot reply in your threads, mention you, or otherwise interact with you." -msgstr "Die Sperrung ist öffentlich. Gesperrte Konten können nicht in deinen Threads antworten, dich erwähnen oder anderweitig mit dir interagieren." +msgstr "Die Blockierung ist öffentlich. Blockierte Konten können nicht in deinen Threads antworten, dich erwähnen oder anderweitig mit dir interagieren." #: src/view/com/profile/ProfileMenu.tsx:353 msgid "Blocking will not prevent labels from being applied on your account, but it will stop this account from replying in your threads or interacting with you." -msgstr "" +msgstr "Blockieren verhindert nicht, dass Kennzeichnungen zu deinem Konto hinzugefügt werden, verhindert aber, dass dieses Konto in deinen Threads antworten oder interagieren kann." #: src/view/com/auth/HomeLoggedOutCTA.tsx:98 #: src/view/com/auth/SplashScreen.web.tsx:169 @@ -530,11 +530,11 @@ msgstr "Bluesky zeigt dein Profil und deine Beiträge nicht für abgemeldete Nut #: src/lib/moderation/useLabelBehaviorDescription.ts:53 msgid "Blur images" -msgstr "" +msgstr "Bilder verwischen" #: src/lib/moderation/useLabelBehaviorDescription.ts:51 msgid "Blur images and filter from feeds" -msgstr "" +msgstr "Bilder verwischen und aus Feeds herausfiltern" #: src/screens/Onboarding/index.tsx:33 msgid "Books" @@ -559,7 +559,7 @@ msgstr "von {0}" #: src/components/LabelingServiceCard/index.tsx:57 msgid "By {0}" -msgstr "" +msgstr "Von {0}" #: src/view/com/profile/ProfileSubpageHeader.tsx:161 msgid "by <0/>" @@ -567,7 +567,7 @@ msgstr "von <0/>" #: src/screens/Signup/StepInfo/Policies.tsx:74 msgid "By creating an account you agree to the {els}." -msgstr "" +msgstr "Mit dem Erstellen des Kontos akzeptierst du die {els}." #: src/view/com/profile/ProfileSubpageHeader.tsx:159 msgid "by you" @@ -869,11 +869,11 @@ msgstr "Inhaltsfilterungseinstellung der Kategorie {0} konfigurieren" #: src/components/moderation/LabelPreference.tsx:81 msgid "Configure content filtering setting for category: {name}" -msgstr "" +msgstr "Konfiguriere die Inhaltsfilterung für die Kategorie: {name}" #: src/components/moderation/LabelPreference.tsx:244 msgid "Configured in <0>moderation settings." -msgstr "" +msgstr "Konfiguriert in <0>Moderationseinstellungen" #: src/components/Prompt.tsx:153 #: src/components/Prompt.tsx:156 @@ -910,11 +910,11 @@ msgstr "Bestätige das Löschen des Kontos" #: src/screens/Moderation/index.tsx:301 msgid "Confirm your age:" -msgstr "" +msgstr "Bestätige dein Alter:" #: src/screens/Moderation/index.tsx:292 msgid "Confirm your birthdate" -msgstr "" +msgstr "Bestätige dein Geburtsdatum" #: src/view/com/modals/ChangeEmail.tsx:157 #: src/view/com/modals/DeleteAccount.tsx:175 @@ -937,7 +937,7 @@ msgstr "" #: src/lib/moderation/useGlobalLabelStrings.ts:18 msgid "Content Blocked" -msgstr "" +msgstr "Inhalt blockiert" #: src/view/screens/Moderation.tsx:83 #~ msgid "Content filtering" @@ -949,7 +949,7 @@ msgstr "" #: src/screens/Moderation/index.tsx:285 msgid "Content filters" -msgstr "" +msgstr "Inhaltsfilterung" #: src/view/com/modals/lang-settings/ContentLanguagesSettings.tsx:74 #: src/view/screens/LanguageSettings.tsx:278 @@ -974,7 +974,7 @@ msgstr "Inhaltswarnungen" #: src/components/Menu/index.web.tsx:84 msgid "Context menu backdrop, click to close the menu." -msgstr "" +msgstr "Hintergrund des Kontextmenüs, klicken, um das Menü zu schließen" #: src/screens/Onboarding/StepAlgoFeeds/index.tsx:161 #: src/screens/Onboarding/StepFollowingFeed.tsx:154 @@ -989,7 +989,7 @@ msgstr "Fortfahren" #: src/components/AccountList.tsx:108 msgid "Continue as {0} (currently signed in)" -msgstr "" +msgstr "Fortfahren mit {0} (aktuell angemeldet)" #: src/screens/Onboarding/StepFollowingFeed.tsx:151 #: src/screens/Onboarding/StepInterests/index.tsx:249 @@ -1037,7 +1037,7 @@ msgstr "Kopieren" #: src/view/com/modals/ChangeHandle.tsx:480 msgid "Copy {0}" -msgstr "" +msgstr "{} kopieren" #: src/view/screens/ProfileList.tsx:388 msgid "Copy link to list" @@ -1096,7 +1096,7 @@ msgstr "Neues Konto erstellen" #: src/components/ReportDialog/SelectReportOptionView.tsx:93 msgid "Create report for {0}" -msgstr "" +msgstr "Meldung für {0} erstellen" #: src/view/screens/AppPasswords.tsx:246 msgid "Created {0}" @@ -1165,7 +1165,7 @@ msgstr "Debug-Panel" #: src/view/screens/AppPasswords.tsx:268 #: src/view/screens/ProfileList.tsx:613 msgid "Delete" -msgstr "" +msgstr "Löschen" #: src/view/screens/Settings/index.tsx:796 msgid "Delete account" @@ -1181,7 +1181,7 @@ msgstr "App-Passwort löschen" #: src/view/screens/AppPasswords.tsx:263 msgid "Delete app password?" -msgstr "" +msgstr "App-Passwort löschen?" #: src/view/screens/ProfileList.tsx:415 msgid "Delete List" @@ -1202,7 +1202,7 @@ msgstr "Beitrag löschen" #: src/view/screens/ProfileList.tsx:608 msgid "Delete this list?" -msgstr "" +msgstr "Diese Liste löschen?" #: src/view/com/util/forms/PostDropdownBtn.tsx:314 msgid "Delete this post?" @@ -1236,7 +1236,7 @@ msgstr "Dimmen" #: src/lib/moderation/useLabelBehaviorDescription.ts:68 #: src/screens/Moderation/index.tsx:341 msgid "Disabled" -msgstr "" +msgstr "Deaktiviert" #: src/view/com/composer/Composer.tsx:511 msgid "Discard" @@ -1248,7 +1248,7 @@ msgstr "Verwerfen" #: src/view/com/composer/Composer.tsx:508 msgid "Discard draft?" -msgstr "" +msgstr "Entwurf löschen?" #: src/screens/Moderation/index.tsx:518 #: src/screens/Moderation/index.tsx:522 @@ -1278,11 +1278,11 @@ msgstr "" #: src/lib/moderation/useGlobalLabelStrings.ts:39 msgid "Does not include nudity." -msgstr "" +msgstr "Beinhaltet keine Nacktheit." #: src/screens/Signup/StepHandle.tsx:104 msgid "Doesn't begin or end with a hyphen" -msgstr "" +msgstr "Beginnt oder endet nicht mit einem Bindestrich" #: src/view/com/modals/ChangeHandle.tsx:481 msgid "Domain Value" @@ -1350,7 +1350,7 @@ msgstr "Aufgrund der Apple-Richtlinien können Inhalte für Erwachsene erst nach #: src/view/com/modals/ChangeHandle.tsx:258 msgid "e.g. alice" -msgstr "" +msgstr "z.B. alice" #: src/view/com/modals/EditProfile.tsx:186 msgid "e.g. Alice Roberts" @@ -1358,7 +1358,7 @@ msgstr "z.B. Alice Roberts" #: src/view/com/modals/ChangeHandle.tsx:380 msgid "e.g. alice.com" -msgstr "" +msgstr "z.B. alice.com" #: src/view/com/modals/EditProfile.tsx:204 msgid "e.g. Artist, dog-lover, and avid reader." @@ -1366,11 +1366,11 @@ msgstr "z.B. Künstlerin, Hundeliebhaberin und begeisterte Leserin." #: src/lib/moderation/useGlobalLabelStrings.ts:43 msgid "E.g. artistic nudes." -msgstr "" +msgstr "Z.B. künstlerische Nacktheit" #: src/view/com/modals/CreateOrEditList.tsx:284 msgid "e.g. Great Posters" -msgstr "z.B. Große Poster" +msgstr "z.B. Großartige Poster" #: src/view/com/modals/CreateOrEditList.tsx:285 msgid "e.g. Spammers" @@ -1396,7 +1396,7 @@ msgstr "Bearbeiten" #: src/view/com/util/UserAvatar.tsx:299 #: src/view/com/util/UserBanner.tsx:85 msgid "Edit avatar" -msgstr "" +msgstr "Avatar bearbeiten" #: src/view/com/composer/photos/Gallery.tsx:144 #: src/view/com/modals/EditImage.tsx:208 @@ -1484,7 +1484,7 @@ msgstr "Nur {0} aktivieren" #: src/screens/Moderation/index.tsx:329 msgid "Enable adult content" -msgstr "" +msgstr "Inhalte für Erwachsene aktivieren" #: src/screens/Onboarding/StepModeration/AdultContentEnabledPref.tsx:94 msgid "Enable Adult Content" @@ -1498,7 +1498,7 @@ msgstr "Aktiviere Inhalte für Erwachsene in deinen Feeds" #: src/components/dialogs/EmbedConsent.tsx:82 #: src/components/dialogs/EmbedConsent.tsx:89 msgid "Enable external media" -msgstr "" +msgstr "Externe Medien aktivieren" #: src/view/com/modals/EmbedConsent.tsx:97 #~ msgid "Enable External Media" @@ -1514,11 +1514,11 @@ msgstr "Aktiviere diese Einstellung, um nur Antworten von Personen zu sehen, den #: src/components/dialogs/EmbedConsent.tsx:94 msgid "Enable this source only" -msgstr "" +msgstr "Nur von dieser Seite erlauben" #: src/screens/Moderation/index.tsx:339 msgid "Enabled" -msgstr "" +msgstr "Aktiviert" #: src/screens/Profile/Sections/Feed.tsx:84 msgid "End of feed" @@ -1530,7 +1530,7 @@ msgstr "Gebe einen Namen für dieses App-Passwort ein" #: src/screens/Login/SetNewPasswordForm.tsx:139 msgid "Enter a password" -msgstr "" +msgstr "Gib ein Passwort ein" #: src/components/dialogs/MutedWords.tsx:99 #: src/components/dialogs/MutedWords.tsx:100 @@ -1588,28 +1588,28 @@ msgstr "Alle" #: src/lib/moderation/useReportOptions.ts:66 msgid "Excessive mentions or replies" -msgstr "" +msgstr "Übermäßig viele Erwähnungen oder Antworten" #: src/view/com/modals/DeleteAccount.tsx:230 msgid "Exits account deletion process" -msgstr "" +msgstr "Verlässt den Vorgang der Accountlöschung" #: src/view/com/modals/ChangeHandle.tsx:151 msgid "Exits handle change process" -msgstr "Beendet den Prozess des Handle-Wechsels" +msgstr "Verlässt den Vorgang des Handle-Wechsels" #: src/view/com/modals/crop-image/CropImage.web.tsx:136 msgid "Exits image cropping process" -msgstr "" +msgstr "Verlässt den Vorgang des Bildzuschneidens" #: src/view/com/lightbox/Lightbox.web.tsx:130 msgid "Exits image view" -msgstr "Beendet die Bildansicht" +msgstr "Verlässt die Bildansicht" #: src/view/com/modals/ListAddRemoveUsers.tsx:88 #: src/view/shell/desktop/Search.tsx:236 msgid "Exits inputting search query" -msgstr "Beendet die Eingabe der Suchanfrage" +msgstr "Verlässt die Eingabe der Suchanfrage" #: src/view/com/lightbox/Lightbox.web.tsx:183 msgid "Expand alt text" @@ -1677,7 +1677,7 @@ msgstr "Empfohlene Feeds konnten nicht geladen werden" #: src/view/com/lightbox/Lightbox.tsx:83 msgid "Failed to save image: {0}" -msgstr "" +msgstr "Das Speichern des Bildes ist fehlgeschlagen: {0}" #: src/Navigation.tsx:196 msgid "Feed" @@ -1721,11 +1721,11 @@ msgstr "Die Feeds können auch auf einem Thema basieren!" #: src/view/com/modals/ChangeHandle.tsx:481 msgid "File Contents" -msgstr "" +msgstr "Dateiinhalt" #: src/lib/moderation/useLabelBehaviorDescription.ts:66 msgid "Filter from feeds" -msgstr "" +msgstr "Aus Feeds filtern" #: src/screens/Onboarding/StepFinished.tsx:155 msgid "Finalizing" @@ -1796,7 +1796,7 @@ msgstr "{0} folgen" #: src/view/com/profile/ProfileMenu.tsx:242 #: src/view/com/profile/ProfileMenu.tsx:253 msgid "Follow Account" -msgstr "" +msgstr "Accounts folgen" #: src/screens/Onboarding/StepSuggestedAccounts/index.tsx:187 msgid "Follow All" @@ -1804,7 +1804,7 @@ msgstr "Allen folgen" #: src/view/com/post-thread/PostThreadFollowBtn.tsx:144 msgid "Follow Back" -msgstr "" +msgstr "Zurückfolgen" #: src/screens/Onboarding/StepSuggestedAccounts/index.tsx:182 msgid "Follow selected accounts and continue to the next step" @@ -1893,15 +1893,15 @@ msgstr "Passwort vergessen" #: src/screens/Login/LoginForm.tsx:201 msgid "Forgot password?" -msgstr "" +msgstr "Passwort vergessen?" #: src/screens/Login/LoginForm.tsx:212 msgid "Forgot?" -msgstr "" +msgstr "Vergessen?" #: src/lib/moderation/useReportOptions.ts:52 msgid "Frequently Posts Unwanted Content" -msgstr "" +msgstr "Postet oft unerwünschte Inhalte" #: src/screens/Hashtag.tsx:109 #: src/screens/Hashtag.tsx:149 @@ -1924,7 +1924,7 @@ msgstr "Los geht's" #: src/lib/moderation/useReportOptions.ts:37 msgid "Glaring violations of law or terms of service" -msgstr "" +msgstr "Eklatante Verstöße gegen Gesetze oder Nutzungsbedingungen" #: src/components/moderation/ScreenHider.tsx:151 #: src/components/moderation/ScreenHider.tsx:160 @@ -2134,11 +2134,11 @@ msgstr "" #: src/view/screens/ProfileList.tsx:610 msgid "If you delete this list, you won't be able to recover it." -msgstr "" +msgstr "Wenn du diese Liste löschst, kannst du sie nicht wiederherstellen." #: src/view/com/util/forms/PostDropdownBtn.tsx:316 msgid "If you remove this post, you won't be able to recover it." -msgstr "" +msgstr "Wenn du diesen Post löschst, kannst du ihn nicht wiederherstellen." #: src/view/com/modals/ChangePassword.tsx:148 msgid "If you want to change your password, we will send you a code to verify that this is your account." @@ -2146,7 +2146,7 @@ msgstr "Wenn du dein Passwort ändern möchtest, senden wir dir einen Code, um z #: src/lib/moderation/useReportOptions.ts:36 msgid "Illegal and Urgent" -msgstr "" +msgstr "Illegal und dringend" #: src/view/com/util/images/Gallery.tsx:38 msgid "Image" @@ -2380,35 +2380,35 @@ msgstr "Diesen Feed liken" #: src/Navigation.tsx:201 #: src/Navigation.tsx:206 msgid "Liked by" -msgstr "Gelikt von" +msgstr "Geliked von" #: src/screens/Profile/ProfileLabelerLikedBy.tsx:29 #: src/view/screens/PostLikedBy.tsx:27 #: src/view/screens/ProfileFeedLikedBy.tsx:27 msgid "Liked By" -msgstr "Gelikt von" +msgstr "Geliked von" #: src/view/com/feeds/FeedSourceCard.tsx:268 msgid "Liked by {0} {1}" -msgstr "Von {0} {1} gelikt" +msgstr "Von {0} {1} geliked" #: src/components/LabelingServiceCard/index.tsx:72 msgid "Liked by {count} {0}" -msgstr "" +msgstr "Geliked von {count} {0}" #: src/screens/Profile/Header/ProfileHeaderLabeler.tsx:278 #: src/screens/Profile/Header/ProfileHeaderLabeler.tsx:292 #: src/view/screens/ProfileFeed.tsx:588 msgid "Liked by {likeCount} {0}" -msgstr "Von {likeCount} {0} gelikt" +msgstr "Von {likeCount} {0} geliked" #: src/view/com/notifications/FeedItem.tsx:174 msgid "liked your custom feed" -msgstr "hat deinen benutzerdefinierten Feed gelikt" +msgstr "hat deinen benutzerdefinierten Feed geliked" #: src/view/com/notifications/FeedItem.tsx:159 msgid "liked your post" -msgstr "hat deinen Beitrag gelikt" +msgstr "hat deinen Beitrag geliked" #: src/view/screens/Profile.tsx:193 msgid "Likes" @@ -2424,7 +2424,7 @@ msgstr "Liste" #: src/view/com/modals/CreateOrEditList.tsx:262 msgid "List Avatar" -msgstr "Avatar auflisten" +msgstr "Listenbild" #: src/view/screens/ProfileList.tsx:311 msgid "List blocked" @@ -2504,7 +2504,7 @@ msgstr "Anmeldung bei einem Konto, das nicht aufgelistet ist" #: src/screens/Login/SetNewPasswordForm.tsx:116 msgid "Looks like XXXXX-XXXXX" -msgstr "" +msgstr "Im Format XXXXX-XXXXX" #: src/view/com/modals/LinkWarning.tsx:79 msgid "Make sure this is where you intend to go!" @@ -2545,7 +2545,7 @@ msgstr "Nachricht vom Server: {0}" #: src/lib/moderation/useReportOptions.ts:45 msgid "Misleading Account" -msgstr "" +msgstr "Irreführender Account" #: src/Navigation.tsx:119 #: src/screens/Moderation/index.tsx:104 @@ -2602,7 +2602,7 @@ msgstr "" #: src/screens/Moderation/index.tsx:215 msgid "Moderation tools" -msgstr "" +msgstr "Moderationswerkzeuge" #: src/components/moderation/ModerationDetailsDialog.tsx:48 #: src/lib/moderation/useModerationCauseDescription.ts:40 @@ -2611,7 +2611,7 @@ msgstr "Der Moderator hat beschlossen, eine allgemeine Warnung vor dem Inhalt au #: src/view/com/post-thread/PostThreadItem.tsx:541 msgid "More" -msgstr "" +msgstr "Mehr" #: src/view/shell/desktop/Feeds.tsx:65 msgid "More feeds" @@ -2708,7 +2708,7 @@ msgstr "Bei stummgeschalteten Konten werden dazugehörige Beiträge aus deinem F #: src/lib/moderation/useModerationCauseDescription.ts:85 msgid "Muted by \"{0}\"" -msgstr "" +msgstr "Stummgeschaltet über \"{0}\"" #: src/screens/Moderation/index.tsx:231 msgid "Muted words & tags" @@ -2733,7 +2733,7 @@ msgstr "Mein Profil" #: src/view/screens/Settings/index.tsx:596 msgid "My saved feeds" -msgstr "" +msgstr "Meine gespeicherten Feeds" #: src/view/screens/Settings/index.tsx:602 msgid "My Saved Feeds" @@ -2897,7 +2897,7 @@ msgstr "{0} wird nicht mehr gefolgt" #: src/screens/Signup/StepHandle.tsx:114 msgid "No longer than 253 characters" -msgstr "" +msgstr "Nicht länger als 253 Zeichen" #: src/view/com/notifications/Feed.tsx:109 msgid "No notifications yet!" @@ -2938,7 +2938,7 @@ msgstr "" #: src/lib/moderation/useGlobalLabelStrings.ts:42 msgid "Non-sexual Nudity" -msgstr "" +msgstr "Nicht-sexuelle Nacktheit" #: src/view/com/modals/SelfLabel.tsx:135 msgid "Not Applicable." @@ -2988,11 +2988,11 @@ msgstr "" #: src/screens/Signup/index.tsx:142 msgid "of" -msgstr "" +msgstr "von" #: src/lib/moderation/useLabelBehaviorDescription.ts:11 msgid "Off" -msgstr "" +msgstr "Aus" #: src/view/com/util/ErrorBoundary.tsx:49 msgid "Oh no!" @@ -3005,7 +3005,7 @@ msgstr "Oh nein, da ist etwas schief gelaufen." #: src/screens/Onboarding/StepModeration/AdultContentEnabledPref.tsx:126 #: src/screens/Profile/Header/ProfileHeaderLabeler.tsx:327 msgid "OK" -msgstr "" +msgstr "OK" #: src/screens/Login/PasswordUpdatedForm.tsx:44 msgid "Okay" @@ -3029,7 +3029,7 @@ msgstr "Nur {0} kann antworten." #: src/screens/Signup/StepHandle.tsx:97 msgid "Only contains letters, numbers, and hyphens" -msgstr "" +msgstr "Enthält nur Buchstaben, Nummern und Bindestriche" #: src/components/Lists.tsx:75 msgid "Oops, something went wrong!" @@ -3064,7 +3064,7 @@ msgstr "Links mit In-App-Browser öffnen" #: src/screens/Moderation/index.tsx:227 msgid "Open muted words and tags settings" -msgstr "" +msgstr "Einstellungen für stummgeschaltete Wörter und Tags öffnen" #: src/view/screens/Moderation.tsx:92 #~ msgid "Open muted words settings" @@ -3097,7 +3097,7 @@ msgstr "Öffnet zusätzliche Details für einen Debug-Eintrag" #: src/view/com/notifications/FeedItem.tsx:353 msgid "Opens an expanded list of users in this notification" -msgstr "Öffnet eine erweiterte Liste der Benutzer in dieser Meldung" +msgstr "Öffnet eine erweiterte Liste der Benutzer in dieser Mitteilung" #: src/view/com/composer/photos/OpenCameraBtn.tsx:78 msgid "Opens camera on device" @@ -3127,13 +3127,13 @@ msgstr "Öffnet die Einstellungen für externe eingebettete Medien" #: src/view/com/auth/SplashScreen.tsx:68 #: src/view/com/auth/SplashScreen.web.tsx:97 msgid "Opens flow to create a new Bluesky account" -msgstr "" +msgstr "Öffnet den Vorgang, einen neuen Bluesky account anzulegen" #: src/view/com/auth/HomeLoggedOutCTA.tsx:75 #: src/view/com/auth/SplashScreen.tsx:83 #: src/view/com/auth/SplashScreen.web.tsx:112 msgid "Opens flow to sign into your existing Bluesky account" -msgstr "" +msgstr "Öffnet den Vorgang, sich mit einen bestehenden Bluesky Account anzumelden" #: src/view/com/profile/ProfileHeader.tsx:575 #~ msgid "Opens followers list" @@ -5346,7 +5346,7 @@ msgstr "Videospiele" #: src/screens/Profile/Header/Shell.tsx:107 msgid "View {0}'s avatar" -msgstr "Avatar {0} ansehen" +msgstr "Avatar von {0} ansehen" #: src/view/screens/Log.tsx:52 msgid "View debug entry" From 1f587ea4b66a6680d8d1fe06b1705994165973d5 Mon Sep 17 00:00:00 2001 From: surfdude29 <149612116+surfdude29@users.noreply.github.com> Date: Fri, 12 Apr 2024 23:49:36 +0200 Subject: [PATCH 023/167] Remove obsolete strings from `en` message catalog (#3462) --- src/locale/locales/en/messages.po | 697 ------------------------------ 1 file changed, 697 deletions(-) diff --git a/src/locale/locales/en/messages.po b/src/locale/locales/en/messages.po index 8a88ef3ad1..f3b9b1977a 100644 --- a/src/locale/locales/en/messages.po +++ b/src/locale/locales/en/messages.po @@ -17,28 +17,10 @@ msgstr "" msgid "(no email)" msgstr "" -#: src/view/shell/desktop/RightNav.tsx:168 -#~ msgid "{0, plural, one {# invite code available} other {# invite codes available}}" -#~ msgstr "" - #: src/screens/Profile/Header/Metrics.tsx:44 msgid "{following} following" msgstr "" -#: src/view/shell/desktop/RightNav.tsx:151 -#~ msgid "{invitesAvailable, plural, one {Invite codes: # available} other {Invite codes: # available}}" -#~ msgstr "" - -#: src/view/screens/Settings.tsx:435 -#: src/view/shell/Drawer.tsx:664 -#~ msgid "{invitesAvailable} invite code available" -#~ msgstr "" - -#: src/view/screens/Settings.tsx:437 -#: src/view/shell/Drawer.tsx:666 -#~ msgid "{invitesAvailable} invite codes available" -#~ msgstr "" - #: src/view/shell/Drawer.tsx:443 msgid "{numUnreadNotifications} unread" msgstr "" @@ -71,14 +53,6 @@ msgstr "" msgid "⚠Invalid Handle" msgstr "" -#: src/view/com/util/moderation/LabelInfo.tsx:45 -#~ msgid "A content warning has been applied to this {0}." -#~ msgstr "" - -#: src/lib/hooks/useOTAUpdate.ts:16 -#~ msgid "A new version of the app is available. Please update to continue using the app." -#~ msgstr "" - #: src/view/com/util/ViewHeader.tsx:89 #: src/view/screens/Search/Search.tsx:649 msgid "Access navigation links and settings" @@ -179,15 +153,6 @@ msgstr "" msgid "Add App Password" msgstr "" -#: src/view/com/modals/report/InputIssueDetails.tsx:41 -#: src/view/com/modals/report/Modal.tsx:191 -#~ msgid "Add details" -#~ msgstr "" - -#: src/view/com/modals/report/Modal.tsx:194 -#~ msgid "Add details to report" -#~ msgstr "" - #: src/view/com/composer/Composer.tsx:467 msgid "Add link card" msgstr "" @@ -240,14 +205,6 @@ msgstr "" msgid "Adult Content" msgstr "" -#: src/view/com/modals/ContentFilteringSettings.tsx:141 -#~ msgid "Adult content can only be enabled via the Web at <0/>." -#~ msgstr "" - -#: src/screens/Onboarding/StepModeration/AdultContentEnabledPref.tsx:78 -#~ msgid "Adult content can only be enabled via the Web at <0>bsky.app." -#~ msgstr "" - #: src/components/moderation/LabelPreference.tsx:242 msgid "Adult content is disabled." msgstr "" @@ -334,10 +291,6 @@ msgstr "" msgid "App password settings" msgstr "" -#: src/view/screens/Settings.tsx:650 -#~ msgid "App passwords" -#~ msgstr "" - #: src/Navigation.tsx:251 #: src/view/screens/AppPasswords.tsx:189 #: src/view/screens/Settings/index.tsx:704 @@ -353,27 +306,10 @@ msgstr "" msgid "Appeal \"{0}\" label" msgstr "" -#: src/view/com/util/forms/PostDropdownBtn.tsx:337 -#: src/view/com/util/forms/PostDropdownBtn.tsx:346 -#~ msgid "Appeal content warning" -#~ msgstr "" - -#: src/view/com/modals/AppealLabel.tsx:65 -#~ msgid "Appeal Content Warning" -#~ msgstr "" - #: src/components/moderation/LabelsOnMeDialog.tsx:192 msgid "Appeal submitted." msgstr "" -#: src/view/com/util/moderation/LabelInfo.tsx:52 -#~ msgid "Appeal this decision" -#~ msgstr "" - -#: src/view/com/util/moderation/LabelInfo.tsx:56 -#~ msgid "Appeal this decision." -#~ msgstr "" - #: src/view/screens/Settings/index.tsx:485 msgid "Appearance" msgstr "" @@ -394,10 +330,6 @@ msgstr "" msgid "Are you sure?" msgstr "" -#: src/view/com/util/forms/PostDropdownBtn.tsx:322 -#~ msgid "Are you sure? This cannot be undone." -#~ msgstr "" - #: src/view/com/composer/select-language/SuggestedLanguage.tsx:60 msgid "Are you writing in <0>{0}?" msgstr "" @@ -430,11 +362,6 @@ msgstr "" msgid "Back" msgstr "" -#: src/view/com/post-thread/PostThread.tsx:480 -#~ msgctxt "action" -#~ msgid "Back" -#~ msgstr "" - #: src/screens/Onboarding/StepSuggestedAccounts/index.tsx:144 msgid "Based on your interest in {interestsText}" msgstr "" @@ -478,10 +405,6 @@ msgstr "" msgid "Block these accounts?" msgstr "" -#: src/view/screens/ProfileList.tsx:320 -#~ msgid "Block this List" -#~ msgstr "" - #: src/view/com/lists/ListCard.tsx:110 #: src/view/com/util/post-embeds/QuoteEmbed.tsx:55 msgid "Blocked" @@ -550,18 +473,10 @@ msgstr "" msgid "Bluesky is public." msgstr "" -#: src/view/com/modals/Waitlist.tsx:70 -#~ msgid "Bluesky uses invites to build a healthier community. If you don't know anybody with an invite, you can sign up for the waitlist and we'll send one soon." -#~ msgstr "" - #: src/screens/Moderation/index.tsx:533 msgid "Bluesky will not show your profile and posts to logged-out users. Other apps may not honor this request. This does not make your account private." msgstr "" -#: src/view/com/modals/ServerInput.tsx:78 -#~ msgid "Bluesky.Social" -#~ msgstr "" - #: src/lib/moderation/useLabelBehaviorDescription.ts:53 msgid "Blur images" msgstr "" @@ -574,19 +489,11 @@ msgstr "" msgid "Books" msgstr "" -#: src/view/screens/Settings/index.tsx:893 -#~ msgid "Build version {0} {1}" -#~ msgstr "" - #: src/view/com/auth/HomeLoggedOutCTA.tsx:92 #: src/view/com/auth/SplashScreen.web.tsx:166 msgid "Business" msgstr "" -#: src/view/com/modals/ServerInput.tsx:115 -#~ msgid "Button disabled. Input custom domain to proceed." -#~ msgstr "" - #: src/view/com/profile/ProfileSubpageHeader.tsx:157 msgid "by —" msgstr "" @@ -679,10 +586,6 @@ msgstr "" msgid "Cancel search" msgstr "" -#: src/view/com/modals/Waitlist.tsx:136 -#~ msgid "Cancel waitlist signup" -#~ msgstr "" - #: src/view/com/modals/LinkWarning.tsx:106 msgid "Cancels opening the linked website" msgstr "" @@ -722,10 +625,6 @@ msgstr "" msgid "Change post language to {0}" msgstr "" -#: src/view/screens/Settings/index.tsx:733 -#~ msgid "Change your Bluesky password" -#~ msgstr "" - #: src/view/com/modals/ChangeEmail.tsx:109 msgid "Change Your Email" msgstr "" @@ -751,10 +650,6 @@ msgstr "" msgid "Choose \"Everybody\" or \"Nobody\"" msgstr "" -#: src/view/screens/Settings/index.tsx:697 -#~ msgid "Choose a new Bluesky username or create" -#~ msgstr "" - #: src/view/com/auth/server-input/index.tsx:79 msgid "Choose Service" msgstr "" @@ -768,10 +663,6 @@ msgstr "" msgid "Choose the algorithms that power your experience with custom feeds." msgstr "" -#: src/screens/Onboarding/StepAlgoFeeds/index.tsx:103 -#~ msgid "Choose your algorithmic feeds" -#~ msgstr "" - #: src/screens/Onboarding/StepAlgoFeeds/index.tsx:104 msgid "Choose your main feeds" msgstr "" @@ -931,12 +822,6 @@ msgstr "" msgid "Confirm" msgstr "" -#: src/view/com/modals/Confirm.tsx:75 -#: src/view/com/modals/Confirm.tsx:78 -#~ msgctxt "action" -#~ msgid "Confirm" -#~ msgstr "" - #: src/view/com/modals/ChangeEmail.tsx:193 #: src/view/com/modals/ChangeEmail.tsx:195 msgid "Confirm Change" @@ -950,10 +835,6 @@ msgstr "" msgid "Confirm delete account" msgstr "" -#: src/view/com/modals/ContentFilteringSettings.tsx:156 -#~ msgid "Confirm your age to enable adult content." -#~ msgstr "" - #: src/screens/Moderation/index.tsx:301 msgid "Confirm your age:" msgstr "" @@ -969,10 +850,6 @@ msgstr "" msgid "Confirmation code" msgstr "" -#: src/view/com/modals/Waitlist.tsx:120 -#~ msgid "Confirms signing up {email} to the waitlist" -#~ msgstr "" - #: src/screens/Login/LoginForm.tsx:248 msgid "Connecting..." msgstr "" @@ -989,14 +866,6 @@ msgstr "" msgid "Content Blocked" msgstr "" -#: src/view/screens/Moderation.tsx:83 -#~ msgid "Content filtering" -#~ msgstr "" - -#: src/view/com/modals/ContentFilteringSettings.tsx:44 -#~ msgid "Content Filtering" -#~ msgstr "" - #: src/screens/Moderation/index.tsx:285 msgid "Content filters" msgstr "" @@ -1098,10 +967,6 @@ msgstr "" msgid "Copy link to post" msgstr "" -#: src/view/com/profile/ProfileHeader.tsx:295 -#~ msgid "Copy link to profile" -#~ msgstr "" - #: src/view/com/util/forms/PostDropdownBtn.tsx:220 #: src/view/com/util/forms/PostDropdownBtn.tsx:222 msgid "Copy post text" @@ -1120,10 +985,6 @@ msgstr "" msgid "Could not load list" msgstr "" -#: src/view/com/auth/create/Step2.tsx:91 -#~ msgid "Country" -#~ msgstr "" - #: src/view/com/auth/HomeLoggedOutCTA.tsx:65 #: src/view/com/auth/SplashScreen.tsx:75 #: src/view/com/auth/SplashScreen.web.tsx:104 @@ -1156,14 +1017,6 @@ msgstr "" msgid "Created {0}" msgstr "" -#: src/view/screens/ProfileFeed.tsx:616 -#~ msgid "Created by <0/>" -#~ msgstr "" - -#: src/view/screens/ProfileFeed.tsx:614 -#~ msgid "Created by you" -#~ msgstr "" - #: src/view/com/composer/Composer.tsx:469 msgid "Creates a card with a thumbnail. The card links to {url}" msgstr "" @@ -1190,10 +1043,6 @@ msgstr "" msgid "Customize media from external sites." msgstr "" -#: src/view/screens/Settings.tsx:687 -#~ msgid "Danger Zone" -#~ msgstr "" - #: src/view/screens/Settings/index.tsx:504 #: src/view/screens/Settings/index.tsx:530 msgid "Dark" @@ -1249,10 +1098,6 @@ msgstr "" msgid "Delete my account" msgstr "" -#: src/view/screens/Settings.tsx:706 -#~ msgid "Delete my account…" -#~ msgstr "" - #: src/view/screens/Settings/index.tsx:808 msgid "Delete My Account…" msgstr "" @@ -1285,10 +1130,6 @@ msgstr "" msgid "Description" msgstr "" -#: src/view/screens/Settings.tsx:760 -#~ msgid "Developer Tools" -#~ msgstr "" - #: src/view/com/composer/Composer.tsx:218 msgid "Did you want to say anything?" msgstr "" @@ -1308,10 +1149,6 @@ msgstr "" msgid "Discard" msgstr "" -#: src/view/com/composer/Composer.tsx:145 -#~ msgid "Discard draft" -#~ msgstr "" - #: src/view/com/composer/Composer.tsx:508 msgid "Discard draft?" msgstr "" @@ -1326,10 +1163,6 @@ msgstr "" msgid "Discover new custom feeds" msgstr "" -#: src/view/screens/Feeds.tsx:473 -#~ msgid "Discover new feeds" -#~ msgstr "" - #: src/view/screens/Feeds.tsx:689 msgid "Discover New Feeds" msgstr "" @@ -1362,10 +1195,6 @@ msgstr "" msgid "Domain verified!" msgstr "" -#: src/view/com/auth/create/Step1.tsx:170 -#~ msgid "Don't have an invite code?" -#~ msgstr "" - #: src/components/dialogs/BirthDateSettings.tsx:119 #: src/components/dialogs/BirthDateSettings.tsx:125 #: src/components/forms/DateField/index.tsx:74 @@ -1401,14 +1230,6 @@ msgstr "" msgid "Done{extraText}" msgstr "" -#: src/view/com/auth/login/ChooseAccountForm.tsx:46 -#~ msgid "Double tap to sign in" -#~ msgstr "" - -#: src/view/screens/Settings/index.tsx:755 -#~ msgid "Download Bluesky account data (repository)" -#~ msgstr "" - #: src/view/screens/Settings/ExportCarDialog.tsx:59 #: src/view/screens/Settings/ExportCarDialog.tsx:63 msgid "Download CAR file" @@ -1574,10 +1395,6 @@ msgstr "" msgid "Enable external media" msgstr "" -#: src/view/com/modals/EmbedConsent.tsx:97 -#~ msgid "Enable External Media" -#~ msgstr "" - #: src/view/screens/PreferencesExternalEmbeds.tsx:75 msgid "Enable media players for" msgstr "" @@ -1631,10 +1448,6 @@ msgstr "" msgid "Enter your birth date" msgstr "" -#: src/view/com/modals/Waitlist.tsx:78 -#~ msgid "Enter your email" -#~ msgstr "" - #: src/screens/Login/ForgotPasswordForm.tsx:105 #: src/screens/Signup/StepInfo/index.tsx:91 msgid "Enter your email address" @@ -1648,10 +1461,6 @@ msgstr "" msgid "Enter your new email address below." msgstr "" -#: src/view/com/auth/create/Step2.tsx:188 -#~ msgid "Enter your phone number" -#~ msgstr "" - #: src/screens/Login/index.tsx:101 msgid "Enter your username and password" msgstr "" @@ -1693,10 +1502,6 @@ msgstr "" msgid "Exits inputting search query" msgstr "" -#: src/view/com/modals/Waitlist.tsx:138 -#~ msgid "Exits signing up for waitlist with {email}" -#~ msgstr "" - #: src/view/com/lightbox/Lightbox.web.tsx:183 msgid "Expand alt text" msgstr "" @@ -1777,10 +1582,6 @@ msgstr "" msgid "Feed offline" msgstr "" -#: src/view/com/feeds/FeedPage.tsx:143 -#~ msgid "Feed Preferences" -#~ msgstr "" - #: src/view/shell/desktop/RightNav.tsx:61 #: src/view/shell/Drawer.tsx:314 msgid "Feedback" @@ -1797,14 +1598,6 @@ msgstr "" msgid "Feeds" msgstr "" -#: src/screens/Onboarding/StepAlgoFeeds/index.tsx:106 -#~ msgid "Feeds are created by users and can give you entirely new experiences." -#~ msgstr "" - -#: src/screens/Onboarding/StepAlgoFeeds/index.tsx:106 -#~ msgid "Feeds are created by users and organizations. They offer you varied experiences and suggest content you may like using algorithms." -#~ msgstr "" - #: src/view/com/auth/onboarding/RecommendedFeeds.tsx:57 msgid "Feeds are created by users to curate content. Choose some feeds that you find interesting." msgstr "" @@ -1851,10 +1644,6 @@ msgstr "" msgid "Fine-tune the content you see on your Following feed." msgstr "" -#: src/view/screens/PreferencesHomeFeed.tsx:111 -#~ msgid "Fine-tune the content you see on your home screen." -#~ msgstr "" - #: src/view/screens/PreferencesThreads.tsx:60 msgid "Fine-tune the discussion threads." msgstr "" @@ -1980,14 +1769,6 @@ msgstr "" msgid "For security reasons, you won't be able to view this again. If you lose this password, you'll need to generate a new one." msgstr "" -#: src/view/com/auth/login/LoginForm.tsx:244 -#~ msgid "Forgot" -#~ msgstr "" - -#: src/view/com/auth/login/LoginForm.tsx:241 -#~ msgid "Forgot password" -#~ msgstr "" - #: src/screens/Login/index.tsx:129 #: src/screens/Login/index.tsx:144 msgid "Forgot Password" @@ -2090,10 +1871,6 @@ msgstr "" msgid "Hashtag" msgstr "" -#: src/components/RichText.tsx:188 -#~ msgid "Hashtag: {tag}" -#~ msgstr "" - #: src/components/RichText.tsx:191 msgid "Hashtag: #{tag}" msgstr "" @@ -2159,10 +1936,6 @@ msgstr "" msgid "Hide user list" msgstr "" -#: src/view/com/profile/ProfileHeader.tsx:487 -#~ msgid "Hides posts from {0} in your feed" -#~ msgstr "" - #: src/view/com/posts/FeedErrorMessage.tsx:111 msgid "Hmm, some kind of issue occurred when contacting the feed server. Please let the feed owner know about this issue." msgstr "" @@ -2199,13 +1972,6 @@ msgstr "" msgid "Home" msgstr "" -#: src/Navigation.tsx:247 -#: src/view/com/pager/FeedsTabBarMobile.tsx:123 -#: src/view/screens/PreferencesHomeFeed.tsx:104 -#: src/view/screens/Settings/index.tsx:543 -#~ msgid "Home Feed Preferences" -#~ msgstr "" - #: src/view/com/modals/ChangeHandle.tsx:420 msgid "Host:" msgstr "" @@ -2269,11 +2035,6 @@ msgstr "" msgid "Image alt text" msgstr "" -#: src/view/com/util/UserAvatar.tsx:311 -#: src/view/com/util/UserBanner.tsx:118 -#~ msgid "Image options" -#~ msgstr "" - #: src/lib/moderation/useReportOptions.ts:47 msgid "Impersonation or false claims about identity or affiliation" msgstr "" @@ -2286,14 +2047,6 @@ msgstr "" msgid "Input confirmation code for account deletion" msgstr "" -#: src/view/com/auth/create/Step1.tsx:177 -#~ msgid "Input email for Bluesky account" -#~ msgstr "" - -#: src/view/com/auth/create/Step1.tsx:151 -#~ msgid "Input invite code to proceed" -#~ msgstr "" - #: src/view/com/modals/AddAppPasswords.tsx:181 msgid "Input name for app password" msgstr "" @@ -2306,10 +2059,6 @@ msgstr "" msgid "Input password for account deletion" msgstr "" -#: src/view/com/auth/create/Step2.tsx:196 -#~ msgid "Input phone number for SMS verification" -#~ msgstr "" - #: src/screens/Login/LoginForm.tsx:195 msgid "Input the password tied to {identifier}" msgstr "" @@ -2318,14 +2067,6 @@ msgstr "" msgid "Input the username or email address you used at signup" msgstr "" -#: src/view/com/auth/create/Step2.tsx:271 -#~ msgid "Input the verification code we have texted to you" -#~ msgstr "" - -#: src/view/com/modals/Waitlist.tsx:90 -#~ msgid "Input your email to get on the Bluesky waitlist" -#~ msgstr "" - #: src/screens/Login/LoginForm.tsx:194 msgid "Input your password" msgstr "" @@ -2346,10 +2087,6 @@ msgstr "" msgid "Invalid username or password" msgstr "" -#: src/view/screens/Settings.tsx:411 -#~ msgid "Invite" -#~ msgstr "" - #: src/view/com/modals/InviteCodes.tsx:94 msgid "Invite a Friend" msgstr "" @@ -2366,10 +2103,6 @@ msgstr "" msgid "Invite codes: {0} available" msgstr "" -#: src/view/shell/Drawer.tsx:645 -#~ msgid "Invite codes: {invitesAvailable} available" -#~ msgstr "" - #: src/view/com/modals/InviteCodes.tsx:170 msgid "Invite codes: 1 available" msgstr "" @@ -2383,19 +2116,6 @@ msgstr "" msgid "Jobs" msgstr "" -#: src/view/com/modals/Waitlist.tsx:67 -#~ msgid "Join the waitlist" -#~ msgstr "" - -#: src/view/com/auth/create/Step1.tsx:174 -#: src/view/com/auth/create/Step1.tsx:178 -#~ msgid "Join the waitlist." -#~ msgstr "" - -#: src/view/com/modals/Waitlist.tsx:128 -#~ msgid "Join Waitlist" -#~ msgstr "" - #: src/screens/Onboarding/index.tsx:24 msgid "Journalism" msgstr "" @@ -2449,14 +2169,6 @@ msgstr "" msgid "Languages" msgstr "" -#: src/view/com/auth/create/StepHeader.tsx:20 -#~ msgid "Last step!" -#~ msgstr "" - -#: src/view/com/util/moderation/ContentHider.tsx:103 -#~ msgid "Learn more" -#~ msgstr "" - #: src/components/moderation/ScreenHider.tsx:136 msgid "Learn More" msgstr "" @@ -2504,11 +2216,6 @@ msgstr "" msgid "Let's go!" msgstr "" -#: src/view/com/util/UserAvatar.tsx:248 -#: src/view/com/util/UserBanner.tsx:62 -#~ msgid "Library" -#~ msgstr "" - #: src/view/screens/Settings/index.tsx:498 msgid "Light" msgstr "" @@ -2609,11 +2316,6 @@ msgstr "" msgid "Lists" msgstr "" -#: src/view/com/post-thread/PostThread.tsx:333 -#: src/view/com/post-thread/PostThread.tsx:341 -#~ msgid "Load more posts" -#~ msgstr "" - #: src/view/screens/Notifications.tsx:159 msgid "Load new notifications" msgstr "" @@ -2629,10 +2331,6 @@ msgstr "" msgid "Loading..." msgstr "" -#: src/view/com/modals/ServerInput.tsx:50 -#~ msgid "Local dev server" -#~ msgstr "" - #: src/Navigation.tsx:221 msgid "Log" msgstr "" @@ -2664,14 +2362,6 @@ msgstr "" msgid "Manage your muted words and tags" msgstr "" -#: src/view/com/auth/create/Step2.tsx:118 -#~ msgid "May not be longer than 253 characters" -#~ msgstr "" - -#: src/view/com/auth/create/Step2.tsx:109 -#~ msgid "May only contain letters and numbers" -#~ msgstr "" - #: src/view/screens/Profile.tsx:192 msgid "Media" msgstr "" @@ -2771,18 +2461,10 @@ msgstr "" msgid "More options" msgstr "" -#: src/view/com/util/forms/PostDropdownBtn.tsx:315 -#~ msgid "More post options" -#~ msgstr "" - #: src/view/screens/PreferencesThreads.tsx:82 msgid "Most-liked replies first" msgstr "" -#: src/view/com/auth/create/Step2.tsx:122 -#~ msgid "Must be at least 3 characters" -#~ msgstr "" - #: src/components/TagMenu/index.tsx:249 msgid "Mute" msgstr "" @@ -2804,10 +2486,6 @@ msgstr "" msgid "Mute all {displayTag} posts" msgstr "" -#: src/components/TagMenu/index.tsx:211 -#~ msgid "Mute all {tag} posts" -#~ msgstr "" - #: src/components/dialogs/MutedWords.tsx:148 msgid "Mute in tags only" msgstr "" @@ -2825,10 +2503,6 @@ msgstr "" msgid "Mute these accounts?" msgstr "" -#: src/view/screens/ProfileList.tsx:279 -#~ msgid "Mute this List" -#~ msgstr "" - #: src/components/dialogs/MutedWords.tsx:126 msgid "Mute this word in post text and tags" msgstr "" @@ -2897,10 +2571,6 @@ msgstr "" msgid "My Saved Feeds" msgstr "" -#: src/view/com/auth/server-input/index.tsx:118 -#~ msgid "my-server.com" -#~ msgstr "" - #: src/view/com/modals/AddAppPasswords.tsx:180 #: src/view/com/modals/CreateOrEditList.tsx:291 msgid "Name" @@ -2934,11 +2604,6 @@ msgstr "" msgid "Need to report a copyright violation?" msgstr "" -#: src/view/com/modals/EmbedConsent.tsx:107 -#: src/view/com/modals/EmbedConsent.tsx:123 -#~ msgid "Never load embeds from {0}" -#~ msgstr "" - #: src/view/com/auth/onboarding/WelcomeDesktop.tsx:72 #: src/view/com/auth/onboarding/WelcomeMobile.tsx:74 msgid "Never lose access to your followers and data." @@ -2948,10 +2613,6 @@ msgstr "" msgid "Never lose access to your followers or data." msgstr "" -#: src/components/dialogs/MutedWords.tsx:293 -#~ msgid "Nevermind" -#~ msgstr "" - #: src/view/com/modals/ChangeHandle.tsx:519 msgid "Nevermind, create a handle for me" msgstr "" @@ -3140,10 +2801,6 @@ msgstr "" msgid "Nudity or adult content not labeled as such" msgstr "" -#: src/lib/moderation/useReportOptions.ts:71 -#~ msgid "Nudity or pornography not labeled as such" -#~ msgstr "" - #: src/screens/Signup/index.tsx:142 msgid "of" msgstr "" @@ -3203,10 +2860,6 @@ msgstr "" msgid "Open" msgstr "" -#: src/view/screens/Moderation.tsx:75 -#~ msgid "Open content filtering settings" -#~ msgstr "" - #: src/view/com/composer/Composer.tsx:491 #: src/view/com/composer/Composer.tsx:492 msgid "Open emoji picker" @@ -3224,10 +2877,6 @@ msgstr "" msgid "Open muted words and tags settings" msgstr "" -#: src/view/screens/Moderation.tsx:92 -#~ msgid "Open muted words settings" -#~ msgstr "" - #: src/view/com/home/HomeHeaderLayoutMobile.tsx:50 msgid "Open navigation" msgstr "" @@ -3273,10 +2922,6 @@ msgstr "" msgid "Opens device photo gallery" msgstr "" -#: src/view/com/profile/ProfileHeader.tsx:420 -#~ msgid "Opens editor for profile display name, avatar, background image, and description" -#~ msgstr "" - #: src/view/screens/Settings/index.tsx:669 msgid "Opens external embeds settings" msgstr "" @@ -3293,18 +2938,6 @@ msgstr "" msgid "Opens flow to sign into your existing Bluesky account" msgstr "" -#: src/view/com/profile/ProfileHeader.tsx:575 -#~ msgid "Opens followers list" -#~ msgstr "" - -#: src/view/com/profile/ProfileHeader.tsx:594 -#~ msgid "Opens following list" -#~ msgstr "" - -#: src/view/screens/Settings.tsx:412 -#~ msgid "Opens invite code list" -#~ msgstr "" - #: src/view/com/modals/InviteCodes.tsx:173 msgid "Opens list of invite codes" msgstr "" @@ -3313,10 +2946,6 @@ msgstr "" msgid "Opens modal for account deletion confirmation. Requires email code" msgstr "" -#: src/view/screens/Settings/index.tsx:774 -#~ msgid "Opens modal for account deletion confirmation. Requires email code." -#~ msgstr "" - #: src/view/screens/Settings/index.tsx:756 msgid "Opens modal for changing your Bluesky password" msgstr "" @@ -3358,18 +2987,10 @@ msgstr "" msgid "Opens the app password settings" msgstr "" -#: src/view/screens/Settings/index.tsx:676 -#~ msgid "Opens the app password settings page" -#~ msgstr "" - #: src/view/screens/Settings/index.tsx:554 msgid "Opens the Following feed preferences" msgstr "" -#: src/view/screens/Settings/index.tsx:535 -#~ msgid "Opens the home feed preferences" -#~ msgstr "" - #: src/view/com/modals/LinkWarning.tsx:93 msgid "Opens the linked website" msgstr "" @@ -3407,10 +3028,6 @@ msgstr "" msgid "Other account" msgstr "" -#: src/view/com/modals/ServerInput.tsx:88 -#~ msgid "Other service" -#~ msgstr "" - #: src/view/com/composer/select-language/SelectLangBtn.tsx:91 msgid "Other..." msgstr "" @@ -3463,10 +3080,6 @@ msgstr "" msgid "Pets" msgstr "" -#: src/view/com/auth/create/Step2.tsx:183 -#~ msgid "Phone number" -#~ msgstr "" - #: src/view/com/modals/SelfLabel.tsx:121 msgid "Pictures meant for adults." msgstr "" @@ -3517,10 +3130,6 @@ msgstr "" msgid "Please enter a name for your app password. All spaces is not allowed." msgstr "" -#: src/view/com/auth/create/Step2.tsx:206 -#~ msgid "Please enter a phone number that can receive SMS text messages." -#~ msgstr "" - #: src/view/com/modals/AddAppPasswords.tsx:146 msgid "Please enter a unique name for this App Password or use our randomly generated one." msgstr "" @@ -3529,14 +3138,6 @@ msgstr "" msgid "Please enter a valid word, tag, or phrase to mute" msgstr "" -#: src/view/com/auth/create/state.ts:170 -#~ msgid "Please enter the code you received by SMS." -#~ msgstr "" - -#: src/view/com/auth/create/Step2.tsx:282 -#~ msgid "Please enter the verification code sent to {phoneNumberFormatted}." -#~ msgstr "" - #: src/screens/Signup/state.ts:220 msgid "Please enter your email." msgstr "" @@ -3549,16 +3150,6 @@ msgstr "" msgid "Please explain why you think this label was incorrectly applied by {0}" msgstr "" -#: src/view/com/modals/AppealLabel.tsx:72 -#: src/view/com/modals/AppealLabel.tsx:75 -#~ msgid "Please tell us why you think this content warning was incorrectly applied!" -#~ msgstr "" - -#: src/view/com/modals/AppealLabel.tsx:72 -#: src/view/com/modals/AppealLabel.tsx:75 -#~ msgid "Please tell us why you think this decision was incorrect." -#~ msgstr "" - #: src/view/com/modals/VerifyEmail.tsx:101 msgid "Please Verify Your Email" msgstr "" @@ -3575,10 +3166,6 @@ msgstr "" msgid "Porn" msgstr "" -#: src/lib/moderation/useGlobalLabelStrings.ts:34 -#~ msgid "Pornography" -#~ msgstr "" - #: src/view/com/composer/Composer.tsx:367 #: src/view/com/composer/Composer.tsx:375 msgctxt "action" @@ -3774,10 +3361,6 @@ msgstr "" msgid "Remove" msgstr "" -#: src/view/com/feeds/FeedSourceCard.tsx:108 -#~ msgid "Remove {0} from my feeds?" -#~ msgstr "" - #: src/view/com/util/AccountDropdownBtn.tsx:22 msgid "Remove account" msgstr "" @@ -3825,18 +3408,10 @@ msgstr "" msgid "Remove repost" msgstr "" -#: src/view/com/feeds/FeedSourceCard.tsx:175 -#~ msgid "Remove this feed from my feeds?" -#~ msgstr "" - #: src/view/com/posts/FeedErrorMessage.tsx:202 msgid "Remove this feed from your saved feeds" msgstr "" -#: src/view/com/posts/FeedErrorMessage.tsx:132 -#~ msgid "Remove this feed from your saved feeds?" -#~ msgstr "" - #: src/view/com/modals/ListAddRemoveUsers.tsx:199 #: src/view/com/modals/UserAddRemoveLists.tsx:152 msgid "Removed from list" @@ -3877,10 +3452,6 @@ msgctxt "description" msgid "Reply to <0/>" msgstr "" -#: src/view/com/modals/report/Modal.tsx:166 -#~ msgid "Report {collectionName}" -#~ msgstr "" - #: src/view/com/profile/ProfileMenu.tsx:319 #: src/view/com/profile/ProfileMenu.tsx:322 msgid "Report Account" @@ -3966,10 +3537,6 @@ msgstr "" msgid "Request Change" msgstr "" -#: src/view/com/auth/create/Step2.tsx:219 -#~ msgid "Request code" -#~ msgstr "" - #: src/view/com/modals/ChangePassword.tsx:241 #: src/view/com/modals/ChangePassword.tsx:243 msgid "Request Code" @@ -3991,10 +3558,6 @@ msgstr "" msgid "Reset Code" msgstr "" -#: src/view/screens/Settings/index.tsx:824 -#~ msgid "Reset onboarding" -#~ msgstr "" - #: src/view/screens/Settings/index.tsx:858 #: src/view/screens/Settings/index.tsx:861 msgid "Reset onboarding state" @@ -4004,10 +3567,6 @@ msgstr "" msgid "Reset password" msgstr "" -#: src/view/screens/Settings/index.tsx:814 -#~ msgid "Reset preferences" -#~ msgstr "" - #: src/view/screens/Settings/index.tsx:848 #: src/view/screens/Settings/index.tsx:851 msgid "Reset preferences state" @@ -4042,10 +3601,6 @@ msgstr "" msgid "Retry" msgstr "" -#: src/view/com/auth/create/Step2.tsx:247 -#~ msgid "Retry." -#~ msgstr "" - #: src/components/Error.tsx:86 #: src/view/screens/ProfileList.tsx:917 msgid "Return to previous page" @@ -4060,10 +3615,6 @@ msgstr "" msgid "Returns to previous page" msgstr "" -#: src/view/shell/desktop/RightNav.tsx:55 -#~ msgid "SANDBOX. Posts and accounts are not permanent." -#~ msgstr "" - #: src/components/dialogs/BirthDateSettings.tsx:125 #: src/view/com/modals/ChangeHandle.tsx:174 #: src/view/com/modals/CreateOrEditList.tsx:338 @@ -4160,18 +3711,10 @@ msgstr "" msgid "Search for all posts by @{authorHandle} with tag {displayTag}" msgstr "" -#: src/components/TagMenu/index.tsx:145 -#~ msgid "Search for all posts by @{authorHandle} with tag {tag}" -#~ msgstr "" - #: src/components/TagMenu/index.tsx:94 msgid "Search for all posts with tag {displayTag}" msgstr "" -#: src/components/TagMenu/index.tsx:90 -#~ msgid "Search for all posts with tag {tag}" -#~ msgstr "" - #: src/view/com/auth/LoggedOut.tsx:105 #: src/view/com/auth/LoggedOut.tsx:106 #: src/view/com/modals/ListAddRemoveUsers.tsx:70 @@ -4198,14 +3741,6 @@ msgstr "" msgid "See <0>{displayTag} posts by this user" msgstr "" -#: src/components/TagMenu/index.tsx:128 -#~ msgid "See <0>{tag} posts" -#~ msgstr "" - -#: src/components/TagMenu/index.tsx:189 -#~ msgid "See <0>{tag} posts by this user" -#~ msgstr "" - #: src/view/screens/SavedFeeds.tsx:163 msgid "See this guide" msgstr "" @@ -4222,10 +3757,6 @@ msgstr "" msgid "Select account" msgstr "" -#: src/view/com/modals/ServerInput.tsx:75 -#~ msgid "Select Bluesky Social" -#~ msgstr "" - #: src/screens/Login/index.tsx:120 msgid "Select from an existing account" msgstr "" @@ -4242,11 +3773,6 @@ msgstr "" msgid "Select option {i} of {numItems}" msgstr "" -#: src/view/com/auth/create/Step1.tsx:96 -#: src/view/com/auth/login/LoginForm.tsx:153 -#~ msgid "Select service" -#~ msgstr "" - #: src/screens/Onboarding/StepSuggestedAccounts/index.tsx:52 msgid "Select some accounts below to follow" msgstr "" @@ -4259,10 +3785,6 @@ msgstr "" msgid "Select the service that hosts your data." msgstr "" -#: src/screens/Onboarding/StepModeration/index.tsx:49 -#~ msgid "Select the types of content that you want to see (or not see), and we'll handle the rest." -#~ msgstr "" - #: src/screens/Onboarding/StepTopicalFeeds.tsx:100 msgid "Select topical feeds to follow from the list below" msgstr "" @@ -4275,10 +3797,6 @@ msgstr "" msgid "Select which languages you want your subscribed feeds to include. If none are selected, all languages will be shown." msgstr "" -#: src/view/screens/LanguageSettings.tsx:98 -#~ msgid "Select your app language for the default text to display in the app" -#~ msgstr "" - #: src/view/screens/LanguageSettings.tsx:98 msgid "Select your app language for the default text to display in the app." msgstr "" @@ -4291,10 +3809,6 @@ msgstr "" msgid "Select your interests from the options below" msgstr "" -#: src/view/com/auth/create/Step2.tsx:155 -#~ msgid "Select your phone's country" -#~ msgstr "" - #: src/view/screens/LanguageSettings.tsx:190 msgid "Select your preferred language for translations in your feed." msgstr "" @@ -4331,10 +3845,6 @@ msgstr "" msgid "Send report" msgstr "" -#: src/view/com/modals/report/SendReportButton.tsx:45 -#~ msgid "Send Report" -#~ msgstr "" - #: src/components/ReportDialog/SelectLabelerView.tsx:44 msgid "Send report to {0}" msgstr "" @@ -4347,48 +3857,14 @@ msgstr "" msgid "Server address" msgstr "" -#: src/view/com/modals/ContentFilteringSettings.tsx:311 -#~ msgid "Set {value} for {labelGroup} content moderation policy" -#~ msgstr "" - -#: src/view/com/modals/ContentFilteringSettings.tsx:160 -#: src/view/com/modals/ContentFilteringSettings.tsx:179 -#~ msgctxt "action" -#~ msgid "Set Age" -#~ msgstr "" - #: src/screens/Moderation/index.tsx:304 msgid "Set birthdate" msgstr "" -#: src/view/screens/Settings/index.tsx:488 -#~ msgid "Set color theme to dark" -#~ msgstr "" - -#: src/view/screens/Settings/index.tsx:481 -#~ msgid "Set color theme to light" -#~ msgstr "" - -#: src/view/screens/Settings/index.tsx:475 -#~ msgid "Set color theme to system setting" -#~ msgstr "" - -#: src/view/screens/Settings/index.tsx:514 -#~ msgid "Set dark theme to the dark theme" -#~ msgstr "" - -#: src/view/screens/Settings/index.tsx:507 -#~ msgid "Set dark theme to the dim theme" -#~ msgstr "" - #: src/screens/Login/SetNewPasswordForm.tsx:102 msgid "Set new password" msgstr "" -#: src/view/com/auth/create/Step1.tsx:202 -#~ msgid "Set password" -#~ msgstr "" - #: src/view/screens/PreferencesFollowingFeed.tsx:225 msgid "Set this setting to \"No\" to hide all quote posts from your feed. Reposts will still be visible." msgstr "" @@ -4405,10 +3881,6 @@ msgstr "" msgid "Set this setting to \"Yes\" to show replies in a threaded view. This is an experimental feature." msgstr "" -#: src/view/screens/PreferencesHomeFeed.tsx:261 -#~ msgid "Set this setting to \"Yes\" to show samples of your saved feeds in your following feed. This is an experimental feature." -#~ msgstr "" - #: src/view/screens/PreferencesFollowingFeed.tsx:261 msgid "Set this setting to \"Yes\" to show samples of your saved feeds in your Following feed. This is an experimental feature." msgstr "" @@ -4445,10 +3917,6 @@ msgstr "" msgid "Sets email for password reset" msgstr "" -#: src/view/com/auth/login/ForgotPasswordForm.tsx:122 -#~ msgid "Sets hosting provider for password reset" -#~ msgstr "" - #: src/view/com/modals/crop-image/CropImage.web.tsx:124 msgid "Sets image aspect ratio to square" msgstr "" @@ -4461,11 +3929,6 @@ msgstr "" msgid "Sets image aspect ratio to wide" msgstr "" -#: src/view/com/auth/create/Step1.tsx:97 -#: src/view/com/auth/login/LoginForm.tsx:154 -#~ msgid "Sets server for the Bluesky client" -#~ msgstr "" - #: src/Navigation.tsx:139 #: src/view/screens/Settings/index.tsx:313 #: src/view/shell/desktop/LeftNav.tsx:437 @@ -4542,10 +4005,6 @@ msgstr "" msgid "Show badge and filter from feeds" msgstr "" -#: src/view/com/modals/EmbedConsent.tsx:87 -#~ msgid "Show embeds from {0}" -#~ msgstr "" - #: src/screens/Profile/Header/ProfileHeaderStandard.tsx:200 msgid "Show follows similar to {0}" msgstr "" @@ -4621,10 +4080,6 @@ msgstr "" msgid "Show warning and filter from feeds" msgstr "" -#: src/view/com/profile/ProfileHeader.tsx:462 -#~ msgid "Shows a list of users similar to this user." -#~ msgstr "" - #: src/view/com/post-thread/PostThreadFollowBtn.tsx:130 msgid "Shows posts from {0} in your feed" msgstr "" @@ -4650,12 +4105,6 @@ msgstr "" msgid "Sign in" msgstr "" -#: src/view/com/auth/HomeLoggedOutCTA.tsx:82 -#: src/view/com/auth/SplashScreen.tsx:86 -#: src/view/com/auth/SplashScreen.web.tsx:91 -#~ msgid "Sign In" -#~ msgstr "" - #: src/components/AccountList.tsx:109 msgid "Sign in as {0}" msgstr "" @@ -4664,10 +4113,6 @@ msgstr "" msgid "Sign in as..." msgstr "" -#: src/view/com/auth/login/LoginForm.tsx:140 -#~ msgid "Sign into" -#~ msgstr "" - #: src/view/screens/Settings/index.tsx:107 #: src/view/screens/Settings/index.tsx:110 msgid "Sign out" @@ -4702,10 +4147,6 @@ msgstr "" msgid "Signed in as @{0}" msgstr "" -#: src/view/com/modals/SwitchAccount.tsx:70 -#~ msgid "Signs {0} out of Bluesky" -#~ msgstr "" - #: src/screens/Onboarding/StepInterests/index.tsx:239 #: src/screens/Onboarding/StepSuggestedAccounts/index.tsx:203 #: src/view/com/auth/onboarding/WelcomeMobile.tsx:35 @@ -4716,32 +4157,16 @@ msgstr "" msgid "Skip this flow" msgstr "" -#: src/view/com/auth/create/Step2.tsx:82 -#~ msgid "SMS verification" -#~ msgstr "" - #: src/screens/Onboarding/index.tsx:40 msgid "Software Dev" msgstr "" -#: src/view/com/modals/ProfilePreview.tsx:62 -#~ msgid "Something went wrong and we're not sure what." -#~ msgstr "" - #: src/components/ReportDialog/index.tsx:59 #: src/screens/Moderation/index.tsx:114 #: src/screens/Profile/Sections/Labels.tsx:76 msgid "Something went wrong, please try again." msgstr "" -#: src/components/Lists.tsx:203 -#~ msgid "Something went wrong!" -#~ msgstr "" - -#: src/view/com/modals/Waitlist.tsx:51 -#~ msgid "Something went wrong. Check your email and try again." -#~ msgstr "" - #: src/App.native.tsx:66 msgid "Sorry! Your session expired. Please log in again." msgstr "" @@ -4774,10 +4199,6 @@ msgstr "" msgid "Square" msgstr "" -#: src/view/com/modals/ServerInput.tsx:62 -#~ msgid "Staging" -#~ msgstr "" - #: src/view/screens/Settings/index.tsx:903 msgid "Status page" msgstr "" @@ -4786,10 +4207,6 @@ msgstr "" msgid "Step" msgstr "" -#: src/view/com/auth/create/StepHeader.tsx:22 -#~ msgid "Step {0} of {numSteps}" -#~ msgstr "" - #: src/view/screens/Settings/index.tsx:292 msgid "Storage cleared, you need to restart the app now." msgstr "" @@ -4847,10 +4264,6 @@ msgstr "" msgid "Support" msgstr "" -#: src/view/com/modals/ProfilePreview.tsx:110 -#~ msgid "Swipe up to see more" -#~ msgstr "" - #: src/components/dialogs/SwitchAccount.tsx:46 #: src/components/dialogs/SwitchAccount.tsx:49 msgid "Switch Account" @@ -4880,10 +4293,6 @@ msgstr "" msgid "Tag menu: {displayTag}" msgstr "" -#: src/components/TagMenu/index.tsx:74 -#~ msgid "Tag menu: {tag}" -#~ msgstr "" - #: src/view/com/modals/crop-image/CropImage.web.tsx:113 msgid "Tall" msgstr "" @@ -5070,10 +4479,6 @@ msgstr "" msgid "There's been a rush of new users to Bluesky! We'll activate your account as soon as we can." msgstr "" -#: src/view/com/auth/create/Step2.tsx:55 -#~ msgid "There's something wrong with this number. Please choose your country and enter your full phone number!" -#~ msgstr "" - #: src/screens/Onboarding/StepSuggestedAccounts/index.tsx:146 msgid "These are popular accounts you might like:" msgstr "" @@ -5111,10 +4516,6 @@ msgstr "" msgid "This content is not viewable without a Bluesky account." msgstr "" -#: src/view/screens/Settings/ExportCarDialog.tsx:75 -#~ msgid "This feature is in beta. You can read more about repository exports in <0>this blogpost." -#~ msgstr "" - #: src/view/screens/Settings/ExportCarDialog.tsx:75 msgid "This feature is in beta. You can read more about repository exports in <0>this blogpost." msgstr "" @@ -5203,14 +4604,6 @@ msgstr "" msgid "This user has requested that their content only be shown to signed-in users." msgstr "" -#: src/view/com/modals/ModerationDetails.tsx:42 -#~ msgid "This user is included in the <0/> list which you have blocked." -#~ msgstr "" - -#: src/view/com/modals/ModerationDetails.tsx:74 -#~ msgid "This user is included in the <0/> list which you have muted." -#~ msgstr "" - #: src/components/moderation/ModerationDetailsDialog.tsx:55 msgid "This user is included in the <0>{0} list which you have blocked." msgstr "" @@ -5219,10 +4612,6 @@ msgstr "" msgid "This user is included in the <0>{0} list which you have muted." msgstr "" -#: src/view/com/modals/ModerationDetails.tsx:74 -#~ msgid "This user is included the <0/> list which you have muted." -#~ msgstr "" - #: src/view/com/profile/ProfileFollows.tsx:87 msgid "This user isn't following anyone." msgstr "" @@ -5235,10 +4624,6 @@ msgstr "" msgid "This will delete {0} from your muted words. You can always add it back later." msgstr "" -#: src/view/com/util/forms/PostDropdownBtn.tsx:282 -#~ msgid "This will hide this post from your feeds." -#~ msgstr "" - #: src/view/screens/Settings/index.tsx:574 msgid "Thread preferences" msgstr "" @@ -5357,10 +4742,6 @@ msgstr "" msgid "Unfollow Account" msgstr "" -#: src/view/com/auth/create/state.ts:262 -#~ msgid "Unfortunately, you do not meet the requirements to create an account." -#~ msgstr "" - #: src/view/com/util/post-ctrls/PostCtrls.tsx:195 msgid "Unlike" msgstr "" @@ -5387,10 +4768,6 @@ msgstr "" msgid "Unmute all {displayTag} posts" msgstr "" -#: src/components/TagMenu/index.tsx:210 -#~ msgid "Unmute all {tag} posts" -#~ msgstr "" - #: src/view/com/util/forms/PostDropdownBtn.tsx:251 #: src/view/com/util/forms/PostDropdownBtn.tsx:256 msgid "Unmute thread" @@ -5409,10 +4786,6 @@ msgstr "" msgid "Unpin moderation list" msgstr "" -#: src/view/screens/ProfileFeed.tsx:346 -#~ msgid "Unsave" -#~ msgstr "" - #: src/screens/Profile/Header/ProfileHeaderLabeler.tsx:219 msgid "Unsubscribe" msgstr "" @@ -5429,10 +4802,6 @@ msgstr "" msgid "Update {displayName} in Lists" msgstr "" -#: src/lib/hooks/useOTAUpdate.ts:15 -#~ msgid "Update Available" -#~ msgstr "" - #: src/view/com/modals/ChangeHandle.tsx:508 msgid "Update to {handle}" msgstr "" @@ -5498,10 +4867,6 @@ msgstr "" msgid "Use this to sign into the other app along with your handle." msgstr "" -#: src/view/com/modals/ServerInput.tsx:105 -#~ msgid "Use your domain as your Bluesky client service provider" -#~ msgstr "" - #: src/view/com/modals/InviteCodes.tsx:201 msgid "Used by:" msgstr "" @@ -5527,10 +4892,6 @@ msgstr "" msgid "User Blocks You" msgstr "" -#: src/view/com/auth/create/Step2.tsx:79 -#~ msgid "User handle" -#~ msgstr "" - #: src/view/com/lists/ListCard.tsx:85 #: src/view/com/modals/UserAddRemoveLists.tsx:198 msgid "User list by {0}" @@ -5582,10 +4943,6 @@ msgstr "" msgid "Value:" msgstr "" -#: src/view/com/auth/create/Step2.tsx:243 -#~ msgid "Verification code" -#~ msgstr "" - #: src/view/com/modals/ChangeHandle.tsx:509 msgid "Verify {0}" msgstr "" @@ -5679,10 +5036,6 @@ msgstr "" msgid "Warn content and filter from feeds" msgstr "" -#: src/screens/Onboarding/StepAlgoFeeds/index.tsx:134 -#~ msgid "We also think you'll like \"For You\" by Skygaze:" -#~ msgstr "" - #: src/screens/Hashtag.tsx:133 msgid "We couldn't find any results for that hashtag." msgstr "" @@ -5699,10 +5052,6 @@ msgstr "" msgid "We ran out of posts from your follows. Here's the latest from <0/>." msgstr "" -#: src/screens/Onboarding/StepAlgoFeeds/index.tsx:118 -#~ msgid "We recommend \"For You\" by Skygaze:" -#~ msgstr "" - #: src/components/dialogs/MutedWords.tsx:203 msgid "We recommend avoiding common words that appear in many posts, since it can result in no posts being shown." msgstr "" @@ -5727,10 +5076,6 @@ msgstr "" msgid "We will let you know when your account is ready." msgstr "" -#: src/view/com/modals/AppealLabel.tsx:48 -#~ msgid "We'll look into your appeal promptly." -#~ msgstr "" - #: src/screens/Onboarding/StepInterests/index.tsx:142 msgid "We'll use this to help customize your experience." msgstr "" @@ -5768,10 +5113,6 @@ msgstr "" msgid "What are your interests?" msgstr "" -#: src/view/com/modals/report/Modal.tsx:169 -#~ msgid "What is the issue with this {collectionName}?" -#~ msgstr "" - #: src/view/com/auth/SplashScreen.tsx:58 #: src/view/com/auth/SplashScreen.web.tsx:84 #: src/view/com/composer/Composer.tsx:296 @@ -5828,10 +5169,6 @@ msgstr "" msgid "Writers" msgstr "" -#: src/view/com/auth/create/Step2.tsx:263 -#~ msgid "XXXXXX" -#~ msgstr "" - #: src/view/com/composer/select-language/SuggestedLanguage.tsx:77 #: src/view/screens/PreferencesFollowingFeed.tsx:129 #: src/view/screens/PreferencesFollowingFeed.tsx:201 @@ -5842,10 +5179,6 @@ msgstr "" msgid "Yes" msgstr "" -#: src/screens/Onboarding/StepModeration/index.tsx:46 -#~ msgid "You are in control" -#~ msgstr "" - #: src/screens/Deactivated.tsx:130 msgid "You are in line." msgstr "" @@ -5859,10 +5192,6 @@ msgstr "" msgid "You can also discover new Custom Feeds to follow." msgstr "" -#: src/screens/Onboarding/StepAlgoFeeds/index.tsx:123 -#~ msgid "You can also try our \"Discover\" algorithm:" -#~ msgstr "" - #: src/screens/Onboarding/StepFollowingFeed.tsx:143 msgid "You can change these settings later." msgstr "" @@ -5926,10 +5255,6 @@ msgstr "" msgid "You have muted this user" msgstr "" -#: src/view/com/modals/ModerationDetails.tsx:87 -#~ msgid "You have muted this user." -#~ msgstr "" - #: src/view/com/feeds/ProfileFeedgens.tsx:136 msgid "You have no feeds." msgstr "" @@ -5943,10 +5268,6 @@ msgstr "" msgid "You have not blocked any accounts yet. To block an account, go to their profile and select \"Block account\" from the menu on their account." msgstr "" -#: src/view/screens/ModerationBlockedAccounts.tsx:132 -#~ msgid "You have not blocked any accounts yet. To block an account, go to their profile and selected \"Block account\" from the menu on their account." -#~ msgstr "" - #: src/view/screens/AppPasswords.tsx:89 msgid "You have not created any app passwords yet. You can create one by pressing the button below." msgstr "" @@ -5955,10 +5276,6 @@ msgstr "" msgid "You have not muted any accounts yet. To mute an account, go to their profile and select \"Mute account\" from the menu on their account." msgstr "" -#: src/view/screens/ModerationMutedAccounts.tsx:131 -#~ msgid "You have not muted any accounts yet. To mute an account, go to their profile and selected \"Mute account\" from the menu on their account." -#~ msgstr "" - #: src/components/dialogs/MutedWords.tsx:249 msgid "You haven't muted any words or tags yet" msgstr "" @@ -5971,10 +5288,6 @@ msgstr "" msgid "You must be 13 years of age or older to sign up." msgstr "" -#: src/view/com/modals/ContentFilteringSettings.tsx:175 -#~ msgid "You must be 18 or older to enable adult content." -#~ msgstr "" - #: src/screens/Onboarding/StepModeration/AdultContentEnabledPref.tsx:110 msgid "You must be 18 years or older to enable adult content" msgstr "" @@ -6048,10 +5361,6 @@ msgstr "" msgid "Your email appears to be invalid." msgstr "" -#: src/view/com/modals/Waitlist.tsx:109 -#~ msgid "Your email has been saved! We'll be in touch soon." -#~ msgstr "" - #: src/view/com/modals/ChangeEmail.tsx:125 msgid "Your email has been updated but not verified. As a next step, please verify your new email." msgstr "" @@ -6072,12 +5381,6 @@ msgstr "" msgid "Your full handle will be <0>@{0}" msgstr "" -#: src/view/screens/Settings.tsx:430 -#: src/view/shell/desktop/RightNav.tsx:137 -#: src/view/shell/Drawer.tsx:660 -#~ msgid "Your invite codes are hidden when logged in using an App Password" -#~ msgstr "" - #: src/components/dialogs/MutedWords.tsx:220 msgid "Your muted words" msgstr "" From f91aa37c6bd900bdc4eec1095c9ecd83da2f13f2 Mon Sep 17 00:00:00 2001 From: Hailey Date: Fri, 12 Apr 2024 14:51:53 -0700 Subject: [PATCH 024/167] Enable updates for `production` behind `receive_updates` gate (#3496) * add gate type * gate the updates * enable updates in `production` * web placeholder for `useOTAUpdates()` * update comment --- app.config.js | 16 +++++--- src/App.native.tsx | 2 - src/lib/hooks/useOTAUpdates.ts | 66 +++++++++++++++--------------- src/lib/hooks/useOTAUpdates.web.ts | 1 + src/lib/statsig/gates.ts | 1 + src/view/screens/Home.tsx | 3 ++ 6 files changed, 49 insertions(+), 40 deletions(-) create mode 100644 src/lib/hooks/useOTAUpdates.web.ts diff --git a/app.config.js b/app.config.js index 9036d5e331..40feed40c5 100644 --- a/app.config.js +++ b/app.config.js @@ -42,8 +42,14 @@ module.exports = function (config) { const IS_DEV = process.env.EXPO_PUBLIC_ENV === 'development' const IS_TESTFLIGHT = process.env.EXPO_PUBLIC_ENV === 'testflight' + const IS_PRODUCTION = process.env.EXPO_PUBLIC_ENV === 'production' - const UPDATES_CHANNEL = IS_TESTFLIGHT ? 'testflight' : 'production' + const UPDATES_CHANNEL = IS_TESTFLIGHT + ? 'testflight' + : IS_PRODUCTION + ? 'production' + : undefined + const UPDATES_ENABLED = !!UPDATES_CHANNEL return { expo: { @@ -126,14 +132,12 @@ module.exports = function (config) { }, updates: { url: 'https://updates.bsky.app/manifest', - // TODO Eventually we want to enable this for all environments, but for now it will only be used for - // TestFlight builds - enabled: IS_TESTFLIGHT, + enabled: UPDATES_ENABLED, fallbackToCacheTimeout: 30000, - codeSigningCertificate: IS_TESTFLIGHT + codeSigningCertificate: UPDATES_ENABLED ? './code-signing/certificate.pem' : undefined, - codeSigningMetadata: IS_TESTFLIGHT + codeSigningMetadata: UPDATES_ENABLED ? { keyid: 'main', alg: 'rsa-v1_5-sha256', diff --git a/src/App.native.tsx b/src/App.native.tsx index 9abe4a559d..ede587c899 100644 --- a/src/App.native.tsx +++ b/src/App.native.tsx @@ -19,7 +19,6 @@ import {init as initPersistedState} from '#/state/persisted' import * as persisted from '#/state/persisted' import {Provider as LabelDefsProvider} from '#/state/preferences/label-defs' import {useIntentHandler} from 'lib/hooks/useIntentHandler' -import {useOTAUpdates} from 'lib/hooks/useOTAUpdates' import {useNotificationsListener} from 'lib/notifications/notifications' import {QueryProvider} from 'lib/react-query' import {s} from 'lib/styles' @@ -58,7 +57,6 @@ function InnerApp() { const {_} = useLingui() useIntentHandler() - useOTAUpdates() // init useEffect(() => { diff --git a/src/lib/hooks/useOTAUpdates.ts b/src/lib/hooks/useOTAUpdates.ts index 51fd18aa04..70905c1373 100644 --- a/src/lib/hooks/useOTAUpdates.ts +++ b/src/lib/hooks/useOTAUpdates.ts @@ -12,6 +12,7 @@ import { import {logger} from '#/logger' import {IS_TESTFLIGHT} from 'lib/app-info' +import {useGate} from 'lib/statsig/statsig' import {isIOS} from 'platform/detection' const MINIMUM_MINIMIZE_TIME = 15 * 60e3 @@ -30,6 +31,9 @@ async function setExtraParams() { } export function useOTAUpdates() { + const shouldReceiveUpdates = + useGate('receive_updates') && isEnabled && !__DEV__ + const appState = React.useRef('active') const lastMinimize = React.useRef(0) const ranInitialCheck = React.useRef(false) @@ -51,61 +55,59 @@ export function useOTAUpdates() { logger.debug('No update available.') } } catch (e) { - logger.warn('OTA Update Error', {error: `${e}`}) + logger.error('OTA Update Error', {error: `${e}`}) } }, 10e3) }, []) - const onIsTestFlight = React.useCallback(() => { - setTimeout(async () => { - try { - await setExtraParams() + const onIsTestFlight = React.useCallback(async () => { + try { + await setExtraParams() - const res = await checkForUpdateAsync() - if (res.isAvailable) { - await fetchUpdateAsync() + const res = await checkForUpdateAsync() + if (res.isAvailable) { + await fetchUpdateAsync() - Alert.alert( - 'Update Available', - 'A new version of the app is available. Relaunch now?', - [ - { - text: 'No', - style: 'cancel', + Alert.alert( + 'Update Available', + 'A new version of the app is available. Relaunch now?', + [ + { + text: 'No', + style: 'cancel', + }, + { + text: 'Relaunch', + style: 'default', + onPress: async () => { + await reloadAsync() }, - { - text: 'Relaunch', - style: 'default', - onPress: async () => { - await reloadAsync() - }, - }, - ], - ) - } - } catch (e: any) { - // No need to handle + }, + ], + ) } - }, 3e3) + } catch (e: any) { + logger.error('Internal OTA Update Error', {error: `${e}`}) + } }, []) React.useEffect(() => { + // We use this setTimeout to allow Statsig to initialize before we check for an update // For Testflight users, we can prompt the user to update immediately whenever there's an available update. This // is suspect however with the Apple App Store guidelines, so we don't want to prompt production users to update // immediately. if (IS_TESTFLIGHT) { onIsTestFlight() return - } else if (!isEnabled || __DEV__ || ranInitialCheck.current) { - // Development client shouldn't check for updates at all, so we skip that here. + } else if (!shouldReceiveUpdates || ranInitialCheck.current) { return } setCheckTimeout() ranInitialCheck.current = true - }, [onIsTestFlight, setCheckTimeout]) + }, [onIsTestFlight, setCheckTimeout, shouldReceiveUpdates]) - // After the app has been minimized for 30 minutes, we want to either A. install an update if one has become available + // After the app has been minimized for 15 minutes, we want to either A. install an update if one has become available // or B check for an update again. React.useEffect(() => { if (!isEnabled) return diff --git a/src/lib/hooks/useOTAUpdates.web.ts b/src/lib/hooks/useOTAUpdates.web.ts new file mode 100644 index 0000000000..1baf4894ee --- /dev/null +++ b/src/lib/hooks/useOTAUpdates.web.ts @@ -0,0 +1 @@ +export function useOTAUpdates() {} diff --git a/src/lib/statsig/gates.ts b/src/lib/statsig/gates.ts index acf0b2aff2..81f3f19d56 100644 --- a/src/lib/statsig/gates.ts +++ b/src/lib/statsig/gates.ts @@ -5,6 +5,7 @@ export type Gate = | 'disable_poll_on_discover' | 'new_profile_scroll_component' | 'new_search' + | 'receive_updates' | 'show_follow_back_label' | 'start_session_with_following' | 'use_new_suggestions_endpoint' diff --git a/src/view/screens/Home.tsx b/src/view/screens/Home.tsx index 7a2a88265b..b55053af0a 100644 --- a/src/view/screens/Home.tsx +++ b/src/view/screens/Home.tsx @@ -14,6 +14,7 @@ import {UsePreferencesQueryResponse} from '#/state/queries/preferences/types' import {useSession} from '#/state/session' import {useSetDrawerSwipeDisabled, useSetMinimalShellMode} from '#/state/shell' import {useSelectedFeed, useSetSelectedFeed} from '#/state/shell/selected-feed' +import {useOTAUpdates} from 'lib/hooks/useOTAUpdates' import {HomeTabNavigatorParams, NativeStackScreenProps} from 'lib/routes/types' import {FeedPage} from 'view/com/feeds/FeedPage' import {Pager, PagerRef, RenderTabBarFnProps} from 'view/com/pager/Pager' @@ -51,6 +52,8 @@ function HomeScreenReady({ preferences: UsePreferencesQueryResponse pinnedFeedInfos: FeedSourceInfo[] }) { + useOTAUpdates() + const allFeeds = React.useMemo(() => { const feeds: FeedDescriptor[] = [] feeds.push('home') From 1f61109cfa8307cbbceea604b1daec7486dd3393 Mon Sep 17 00:00:00 2001 From: Eric Bailey Date: Fri, 12 Apr 2024 17:01:32 -0500 Subject: [PATCH 025/167] Profile card hover preview (#3508) * feat: initial user card hover * feat: flesh it out some more * fix: initialize middlewares once * chore: remove floating-ui react-native * chore: clean up * Update moderation apis, fix lint * Refactor profile hover card to alf * Clean up * Debounce, fix positioning when loading * Fix going away * Close on all link presses * Tweak styles * Disable on mobile web * cleanup some of the changes pt. 1 * cleanup some of the changes pt. 2 * cleanup some of the changes pt. 3 * cleanup some of the changes pt. 4 * Re-revert files * Fix handle presentation * Don't follow yourself, silly * Collapsed notifications group * ProfileCard * Tree view replies * Suggested follows * Fix hover-back-on-card edge case * Moar --------- Co-authored-by: Mary Co-authored-by: Hailey --- package.json | 2 + src/components/ProfileHoverCard/index.tsx | 5 + src/components/ProfileHoverCard/index.web.tsx | 290 ++++++++++++++++++ src/components/ProfileHoverCard/types.ts | 6 + src/components/RichText.tsx | 10 +- src/components/hooks/useFollowMethods.ts | 60 ++++ src/components/hooks/useRichText.ts | 33 ++ src/lib/statsig/events.ts | 2 + src/screens/Profile/Header/Handle.tsx | 7 +- src/state/queries/profile.ts | 4 +- src/view/com/notifications/FeedItem.tsx | 105 ++++--- src/view/com/profile/ProfileCard.tsx | 37 ++- .../profile/ProfileHeaderSuggestedFollows.tsx | 32 +- src/view/com/util/PostMeta.tsx | 23 +- src/view/com/util/UserAvatar.tsx | 48 +-- src/view/com/util/UserPreviewLink.tsx | 31 -- yarn.lock | 27 ++ 17 files changed, 576 insertions(+), 146 deletions(-) create mode 100644 src/components/ProfileHoverCard/index.tsx create mode 100644 src/components/ProfileHoverCard/index.web.tsx create mode 100644 src/components/ProfileHoverCard/types.ts create mode 100644 src/components/hooks/useFollowMethods.ts create mode 100644 src/components/hooks/useRichText.ts delete mode 100644 src/view/com/util/UserPreviewLink.tsx diff --git a/package.json b/package.json index 85db718dc1..8b99b70cce 100644 --- a/package.json +++ b/package.json @@ -57,6 +57,8 @@ "@emoji-mart/react": "^1.1.1", "@expo/html-elements": "^0.4.2", "@expo/webpack-config": "^19.0.0", + "@floating-ui/dom": "^1.6.3", + "@floating-ui/react-dom": "^2.0.8", "@fortawesome/fontawesome-svg-core": "^6.1.1", "@fortawesome/free-regular-svg-icons": "^6.1.1", "@fortawesome/free-solid-svg-icons": "^6.1.1", diff --git a/src/components/ProfileHoverCard/index.tsx b/src/components/ProfileHoverCard/index.tsx new file mode 100644 index 0000000000..980336ee4a --- /dev/null +++ b/src/components/ProfileHoverCard/index.tsx @@ -0,0 +1,5 @@ +import {ProfileHoverCardProps} from './types' + +export function ProfileHoverCard({children}: ProfileHoverCardProps) { + return children +} diff --git a/src/components/ProfileHoverCard/index.web.tsx b/src/components/ProfileHoverCard/index.web.tsx new file mode 100644 index 0000000000..cfb8cf2fc4 --- /dev/null +++ b/src/components/ProfileHoverCard/index.web.tsx @@ -0,0 +1,290 @@ +import React from 'react' +import {View} from 'react-native' +import Animated, {FadeIn, FadeOut} from 'react-native-reanimated' +import {AppBskyActorDefs, moderateProfile, ModerationOpts} from '@atproto/api' +import {flip, offset, shift, size, useFloating} from '@floating-ui/react-dom' +import {msg, Trans} from '@lingui/macro' +import {useLingui} from '@lingui/react' + +import {makeProfileLink} from '#/lib/routes/links' +import {sanitizeDisplayName} from '#/lib/strings/display-names' +import {sanitizeHandle} from '#/lib/strings/handles' +import {pluralize} from '#/lib/strings/helpers' +import {useModerationOpts} from '#/state/queries/preferences' +import {usePrefetchProfileQuery, useProfileQuery} from '#/state/queries/profile' +import {useSession} from '#/state/session' +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' +import {atoms as a, useTheme} from '#/alf' +import {Button, ButtonIcon, ButtonText} from '#/components/Button' +import {useFollowMethods} from '#/components/hooks/useFollowMethods' +import {useRichText} from '#/components/hooks/useRichText' +import {Check_Stroke2_Corner0_Rounded as Check} from '#/components/icons/Check' +import {PlusLarge_Stroke2_Corner0_Rounded as Plus} from '#/components/icons/Plus' +import {InlineLinkText, Link} from '#/components/Link' +import {Loader} from '#/components/Loader' +import {Portal} from '#/components/Portal' +import {RichText} from '#/components/RichText' +import {Text} from '#/components/Typography' +import {ProfileHoverCardProps} from './types' + +const floatingMiddlewares = [ + offset(4), + flip({padding: 16}), + shift({padding: 16}), + size({ + padding: 16, + apply({availableWidth, availableHeight, elements}) { + Object.assign(elements.floating.style, { + maxWidth: `${availableWidth}px`, + maxHeight: `${availableHeight}px`, + }) + }, + }), +] + +const isTouchDevice = 'ontouchstart' in window || navigator.maxTouchPoints > 0 + +export function ProfileHoverCard(props: ProfileHoverCardProps) { + return isTouchDevice ? props.children : +} + +export function ProfileHoverCardInner(props: ProfileHoverCardProps) { + const [hovered, setHovered] = React.useState(false) + const {refs, floatingStyles} = useFloating({ + middleware: floatingMiddlewares, + }) + const prefetchProfileQuery = usePrefetchProfileQuery() + + const prefetchedProfile = React.useRef(false) + const targetHovered = React.useRef(false) + const cardHovered = React.useRef(false) + const targetClicked = React.useRef(false) + + const onPointerEnterTarget = React.useCallback(() => { + targetHovered.current = true + + if (prefetchedProfile.current) { + // if we're navigating + if (targetClicked.current) return + setHovered(true) + } else { + prefetchProfileQuery(props.did).then(() => { + if (targetHovered.current) { + setHovered(true) + } + prefetchedProfile.current = true + }) + } + }, [props.did, prefetchProfileQuery]) + const onPointerEnterCard = React.useCallback(() => { + cardHovered.current = true + // if we're navigating + if (targetClicked.current) return + setHovered(true) + }, []) + const onPointerLeaveTarget = React.useCallback(() => { + targetHovered.current = false + setTimeout(() => { + if (cardHovered.current) return + setHovered(false) + }, 100) + }, []) + const onPointerLeaveCard = React.useCallback(() => { + cardHovered.current = false + setTimeout(() => { + if (targetHovered.current) return + setHovered(false) + }, 100) + }, []) + const onClickTarget = React.useCallback(() => { + targetClicked.current = true + setHovered(false) + }, []) + const hide = React.useCallback(() => { + setHovered(false) + }, []) + + return ( +
+ {props.children} + + {hovered && ( + + +
+ +
+
+
+ )} +
+ ) +} + +function Card({did, hide}: {did: string; hide: () => void}) { + const t = useTheme() + + const profile = useProfileQuery({did}) + const moderationOpts = useModerationOpts() + + const data = profile.data + + return ( + + {data && moderationOpts ? ( + + ) : ( + + + + )} + + ) +} + +function Inner({ + profile, + moderationOpts, + hide, +}: { + profile: AppBskyActorDefs.ProfileViewDetailed + moderationOpts: ModerationOpts + hide: () => void +}) { + const t = useTheme() + const {_} = useLingui() + const {currentAccount} = useSession() + const moderation = React.useMemo( + () => moderateProfile(profile, moderationOpts), + [profile, moderationOpts], + ) + const [descriptionRT] = useRichText(profile.description ?? '') + const profileShadow = useProfileShadow(profile) + const {follow, unfollow} = useFollowMethods({ + profile: profileShadow, + logContext: 'ProfileHoverCard', + }) + const blockHide = profile.viewer?.blocking || profile.viewer?.blockedBy + const following = formatCount(profile.followsCount || 0) + const followers = formatCount(profile.followersCount || 0) + const pluralizedFollowers = pluralize(profile.followersCount || 0, 'follower') + const profileURL = makeProfileLink({ + did: profile.did, + handle: profile.handle, + }) + const isMe = React.useMemo( + () => currentAccount?.did === profile.did, + [currentAccount, profile], + ) + + return ( + + + + + + + {!isMe && ( + + )} + + + + + + {sanitizeDisplayName( + profile.displayName || sanitizeHandle(profile.handle), + moderation.ui('displayName'), + )} + + + + + + + {!blockHide && ( + <> + + + + {followers} + + {pluralizedFollowers} + + + + + + {following} + following + + + + + {profile.description?.trim() && !moderation.ui('profileView').blur ? ( + + + + ) : undefined} + + )} + + ) +} diff --git a/src/components/ProfileHoverCard/types.ts b/src/components/ProfileHoverCard/types.ts new file mode 100644 index 0000000000..4e70df5f0d --- /dev/null +++ b/src/components/ProfileHoverCard/types.ts @@ -0,0 +1,6 @@ +import React from 'react' + +export type ProfileHoverCardProps = { + children: React.ReactElement + did: string +} diff --git a/src/components/RichText.tsx b/src/components/RichText.tsx index 5cfa0b24f9..17f36c1418 100644 --- a/src/components/RichText.tsx +++ b/src/components/RichText.tsx @@ -7,7 +7,7 @@ import {toShortUrl} from '#/lib/strings/url-helpers' import {isNative} from '#/platform/detection' import {atoms as a, flatten, native, TextStyleProp, useTheme, web} from '#/alf' import {useInteractionState} from '#/components/hooks/useInteractionState' -import {InlineLinkText} from '#/components/Link' +import {InlineLinkText, LinkProps} from '#/components/Link' import {TagMenu, useTagMenuControl} from '#/components/TagMenu' import {Text, TextProps} from '#/components/Typography' @@ -22,6 +22,7 @@ export function RichText({ selectable, enableTags = false, authorHandle, + onLinkPress, }: TextStyleProp & Pick & { value: RichTextAPI | string @@ -30,6 +31,7 @@ export function RichText({ disableLinks?: boolean enableTags?: boolean authorHandle?: string + onLinkPress?: LinkProps['onPress'] }) { const richText = React.useMemo( () => @@ -90,7 +92,8 @@ export function RichText({ to={`/profile/${mention.did}`} style={[...styles, {pointerEvents: 'auto'}]} // @ts-ignore TODO - dataSet={WORD_WRAP}> + dataSet={WORD_WRAP} + onPress={onLinkPress}> {segment.text} , ) @@ -106,7 +109,8 @@ export function RichText({ style={[...styles, {pointerEvents: 'auto'}]} // @ts-ignore TODO dataSet={WORD_WRAP} - shareOnLongPress> + shareOnLongPress + onPress={onLinkPress}> {toShortUrl(segment.text)} , ) diff --git a/src/components/hooks/useFollowMethods.ts b/src/components/hooks/useFollowMethods.ts new file mode 100644 index 0000000000..1e91a1f38a --- /dev/null +++ b/src/components/hooks/useFollowMethods.ts @@ -0,0 +1,60 @@ +import React from 'react' +import {AppBskyActorDefs} from '@atproto/api' +import {msg} from '@lingui/macro' +import {useLingui} from '@lingui/react' + +import {LogEvents} from '#/lib/statsig/statsig' +import {logger} from '#/logger' +import {Shadow} from '#/state/cache/types' +import {useProfileFollowMutationQueue} from '#/state/queries/profile' +import {useRequireAuth} from '#/state/session' +import * as Toast from '#/view/com/util/Toast' + +export function useFollowMethods({ + profile, + logContext, +}: { + profile: Shadow + logContext: LogEvents['profile:follow']['logContext'] & + LogEvents['profile:unfollow']['logContext'] +}) { + const {_} = useLingui() + const requireAuth = useRequireAuth() + const [queueFollow, queueUnfollow] = useProfileFollowMutationQueue( + profile, + logContext, + ) + + const follow = React.useCallback(() => { + requireAuth(async () => { + try { + await queueFollow() + } catch (e: any) { + logger.error(`useFollowMethods: failed to follow`, {message: String(e)}) + if (e?.name !== 'AbortError') { + Toast.show(_(msg`An issue occurred, please try again.`)) + } + } + }) + }, [_, queueFollow, requireAuth]) + + const unfollow = React.useCallback(() => { + requireAuth(async () => { + try { + await queueUnfollow() + } catch (e: any) { + logger.error(`useFollowMethods: failed to unfollow`, { + message: String(e), + }) + if (e?.name !== 'AbortError') { + Toast.show(_(msg`An issue occurred, please try again.`)) + } + } + }) + }, [_, queueUnfollow, requireAuth]) + + return { + follow, + unfollow, + } +} diff --git a/src/components/hooks/useRichText.ts b/src/components/hooks/useRichText.ts new file mode 100644 index 0000000000..e363ae5a93 --- /dev/null +++ b/src/components/hooks/useRichText.ts @@ -0,0 +1,33 @@ +import React from 'react' +import {RichText as RichTextAPI} from '@atproto/api' + +import {getAgent} from '#/state/session' + +export function useRichText(text: string): [RichTextAPI, boolean] { + const [prevText, setPrevText] = React.useState(text) + const [rawRT, setRawRT] = React.useState(() => new RichTextAPI({text})) + const [resolvedRT, setResolvedRT] = React.useState(null) + if (text !== prevText) { + setPrevText(text) + setRawRT(new RichTextAPI({text})) + setResolvedRT(null) + // This will queue an immediate re-render + } + React.useEffect(() => { + let ignore = false + async function resolveRTFacets() { + // new each time + const resolvedRT = new RichTextAPI({text}) + await resolvedRT.detectFacets(getAgent()) + if (!ignore) { + setResolvedRT(resolvedRT) + } + } + resolveRTFacets() + return () => { + ignore = true + } + }, [text]) + const isResolving = resolvedRT === null + return [resolvedRT ?? rawRT, isResolving] +} diff --git a/src/lib/statsig/events.ts b/src/lib/statsig/events.ts index 3d650b8b73..1231c5de5d 100644 --- a/src/lib/statsig/events.ts +++ b/src/lib/statsig/events.ts @@ -99,6 +99,7 @@ export type LogEvents = { | 'ProfileHeader' | 'ProfileHeaderSuggestedFollows' | 'ProfileMenu' + | 'ProfileHoverCard' } 'profile:unfollow': { logContext: @@ -108,5 +109,6 @@ export type LogEvents = { | 'ProfileHeader' | 'ProfileHeaderSuggestedFollows' | 'ProfileMenu' + | 'ProfileHoverCard' } } diff --git a/src/screens/Profile/Header/Handle.tsx b/src/screens/Profile/Header/Handle.tsx index fd1cbe5333..9ab24fbbed 100644 --- a/src/screens/Profile/Header/Handle.tsx +++ b/src/screens/Profile/Header/Handle.tsx @@ -1,10 +1,10 @@ import React from 'react' import {View} from 'react-native' import {AppBskyActorDefs} from '@atproto/api' -import {isInvalidHandle} from 'lib/strings/handles' -import {Shadow} from '#/state/cache/types' import {Trans} from '@lingui/macro' +import {Shadow} from '#/state/cache/types' +import {isInvalidHandle} from 'lib/strings/handles' import {atoms as a, useTheme, web} from '#/alf' import {Text} from '#/components/Typography' @@ -26,6 +26,7 @@ export function ProfileHeaderHandle({
) : undefined} {invalidHandle ? ⚠Invalid Handle : `@${profile.handle}`} diff --git a/src/state/queries/profile.ts b/src/state/queries/profile.ts index a962fecff7..7842d53d4d 100644 --- a/src/state/queries/profile.ts +++ b/src/state/queries/profile.ts @@ -90,8 +90,8 @@ export function useProfilesQuery({handles}: {handles: string[]}) { export function usePrefetchProfileQuery() { const queryClient = useQueryClient() const prefetchProfileQuery = useCallback( - (did: string) => { - queryClient.prefetchQuery({ + async (did: string) => { + await queryClient.prefetchQuery({ queryKey: RQKEY(did), queryFn: async () => { const res = await getAgent().getProfile({actor: did || ''}) diff --git a/src/view/com/notifications/FeedItem.tsx b/src/view/com/notifications/FeedItem.tsx index 78b1677c3d..e1dae6659f 100644 --- a/src/view/com/notifications/FeedItem.tsx +++ b/src/view/com/notifications/FeedItem.tsx @@ -1,20 +1,20 @@ -import React, {memo, useMemo, useState, useEffect} from 'react' +import React, {memo, useEffect, useMemo, useState} from 'react' import { Animated, - TouchableOpacity, Pressable, StyleSheet, + TouchableOpacity, View, } from 'react-native' import { + AppBskyActorDefs, AppBskyEmbedImages, + AppBskyEmbedRecordWithMedia, AppBskyFeedDefs, AppBskyFeedPost, - ModerationOpts, - ModerationDecision, moderateProfile, - AppBskyEmbedRecordWithMedia, - AppBskyActorDefs, + ModerationDecision, + ModerationOpts, } from '@atproto/api' import {AtUri} from '@atproto/api' import { @@ -22,28 +22,30 @@ import { FontAwesomeIconStyle, Props, } from '@fortawesome/react-native-fontawesome' +import {msg, Trans} from '@lingui/macro' +import {useLingui} from '@lingui/react' + import {FeedNotification} from '#/state/queries/notifications/feed' -import {s, colors} from 'lib/styles' -import {niceDate} from 'lib/strings/time' +import {useAnimatedValue} from 'lib/hooks/useAnimatedValue' +import {usePalette} from 'lib/hooks/usePalette' +import {HeartIconSolid} from 'lib/icons' +import {makeProfileLink} from 'lib/routes/links' import {sanitizeDisplayName} from 'lib/strings/display-names' import {sanitizeHandle} from 'lib/strings/handles' import {pluralize} from 'lib/strings/helpers' -import {HeartIconSolid} from 'lib/icons' -import {Text} from '../util/text/Text' -import {UserAvatar, PreviewableUserAvatar} from '../util/UserAvatar' -import {UserPreviewLink} from '../util/UserPreviewLink' -import {ImageHorzList} from '../util/images/ImageHorzList' -import {Post} from '../post/Post' -import {Link, TextLink} from '../util/Link' -import {usePalette} from 'lib/hooks/usePalette' -import {useAnimatedValue} from 'lib/hooks/useAnimatedValue' -import {formatCount} from '../util/numeric/format' -import {makeProfileLink} from 'lib/routes/links' -import {TimeElapsed} from '../util/TimeElapsed' +import {niceDate} from 'lib/strings/time' +import {colors, s} from 'lib/styles' import {isWeb} from 'platform/detection' -import {Trans, msg} from '@lingui/macro' -import {useLingui} from '@lingui/react' +import {Link as NewLink} from '#/components/Link' +import {ProfileHoverCard} from '#/components/ProfileHoverCard' import {FeedSourceCard} from '../feeds/FeedSourceCard' +import {Post} from '../post/Post' +import {ImageHorzList} from '../util/images/ImageHorzList' +import {Link, TextLink} from '../util/Link' +import {formatCount} from '../util/numeric/format' +import {Text} from '../util/text/Text' +import {TimeElapsed} from '../util/TimeElapsed' +import {PreviewableUserAvatar, UserAvatar} from '../util/UserAvatar' const MAX_AUTHORS = 5 @@ -356,8 +358,10 @@ function CondensedAuthorsList({ {authors.slice(0, MAX_AUTHORS).map(author => ( - {authors.map(author => ( - - - - - - - {sanitizeDisplayName(author.displayName || author.handle)} -   - - {sanitizeHandle(author.handle)} + label={_(msg`See profile`)} + to={makeProfileLink({ + did: author.did, + handle: author.handle, + })}> + + + + + + + + + {sanitizeDisplayName(author.displayName || author.handle)} +   + + {sanitizeHandle(author.handle)} + - + - + ))} ) diff --git a/src/view/com/profile/ProfileCard.tsx b/src/view/com/profile/ProfileCard.tsx index 235139fff0..e6df5f6d0e 100644 --- a/src/view/com/profile/ProfileCard.tsx +++ b/src/view/com/profile/ProfileCard.tsx @@ -6,22 +6,23 @@ import { ModerationCause, ModerationDecision, } from '@atproto/api' -import {Link} from '../util/Link' -import {Text} from '../util/text/Text' -import {UserAvatar} from '../util/UserAvatar' -import {s} from 'lib/styles' -import {usePalette} from 'lib/hooks/usePalette' -import {FollowButton} from './FollowButton' -import {sanitizeDisplayName} from 'lib/strings/display-names' -import {sanitizeHandle} from 'lib/strings/handles' -import {makeProfileLink} from 'lib/routes/links' -import {getModerationCauseKey, isJustAMute} from 'lib/moderation' +import {Trans} from '@lingui/macro' + +import {useModerationCauseDescription} from '#/lib/moderation/useModerationCauseDescription' +import {useProfileShadow} from '#/state/cache/profile-shadow' import {Shadow} from '#/state/cache/types' import {useModerationOpts} from '#/state/queries/preferences' -import {useProfileShadow} from '#/state/cache/profile-shadow' import {useSession} from '#/state/session' -import {Trans} from '@lingui/macro' -import {useModerationCauseDescription} from '#/lib/moderation/useModerationCauseDescription' +import {usePalette} from 'lib/hooks/usePalette' +import {getModerationCauseKey, isJustAMute} from 'lib/moderation' +import {makeProfileLink} from 'lib/routes/links' +import {sanitizeDisplayName} from 'lib/strings/display-names' +import {sanitizeHandle} from 'lib/strings/handles' +import {s} from 'lib/styles' +import {Link} from '../util/Link' +import {Text} from '../util/text/Text' +import {PreviewableUserAvatar} from '../util/UserAvatar' +import {FollowButton} from './FollowButton' export function ProfileCard({ testID, @@ -76,8 +77,10 @@ export function ProfileCard({ anchorNoUnderline> - ( - diff --git a/src/view/com/profile/ProfileHeaderSuggestedFollows.tsx b/src/view/com/profile/ProfileHeaderSuggestedFollows.tsx index 3602cdb9a8..cf35885cd2 100644 --- a/src/view/com/profile/ProfileHeaderSuggestedFollows.tsx +++ b/src/view/com/profile/ProfileHeaderSuggestedFollows.tsx @@ -1,28 +1,28 @@ import React from 'react' -import {View, StyleSheet, Pressable, ScrollView} from 'react-native' +import {Pressable, ScrollView, StyleSheet, View} from 'react-native' import {AppBskyActorDefs, moderateProfile} from '@atproto/api' import { FontAwesomeIcon, FontAwesomeIconStyle, } from '@fortawesome/react-native-fontawesome' +import {msg, Trans} from '@lingui/macro' +import {useLingui} from '@lingui/react' -import * as Toast from '../util/Toast' +import {useProfileShadow} from '#/state/cache/profile-shadow' +import {useModerationOpts} from '#/state/queries/preferences' +import {useProfileFollowMutationQueue} from '#/state/queries/profile' +import {useSuggestedFollowsByActorQuery} from '#/state/queries/suggested-follows' +import {useAnalytics} from 'lib/analytics/analytics' import {usePalette} from 'lib/hooks/usePalette' -import {Text} from 'view/com/util/text/Text' -import {UserAvatar} from 'view/com/util/UserAvatar' -import {Button} from 'view/com/util/forms/Button' +import {makeProfileLink} from 'lib/routes/links' import {sanitizeDisplayName} from 'lib/strings/display-names' import {sanitizeHandle} from 'lib/strings/handles' -import {makeProfileLink} from 'lib/routes/links' -import {Link} from 'view/com/util/Link' -import {useAnalytics} from 'lib/analytics/analytics' import {isWeb} from 'platform/detection' -import {useModerationOpts} from '#/state/queries/preferences' -import {useSuggestedFollowsByActorQuery} from '#/state/queries/suggested-follows' -import {useProfileShadow} from '#/state/cache/profile-shadow' -import {useProfileFollowMutationQueue} from '#/state/queries/profile' -import {useLingui} from '@lingui/react' -import {Trans, msg} from '@lingui/macro' +import {Button} from 'view/com/util/forms/Button' +import {Link} from 'view/com/util/Link' +import {Text} from 'view/com/util/text/Text' +import {PreviewableUserAvatar} from 'view/com/util/UserAvatar' +import * as Toast from '../util/Toast' const OUTER_PADDING = 10 const INNER_PADDING = 14 @@ -218,8 +218,10 @@ function SuggestedFollow({ backgroundColor: pal.view.backgroundColor, }, ]}> - diff --git a/src/view/com/util/PostMeta.tsx b/src/view/com/util/PostMeta.tsx index 529fc54e01..b37c69448e 100644 --- a/src/view/com/util/PostMeta.tsx +++ b/src/view/com/util/PostMeta.tsx @@ -1,18 +1,19 @@ import React, {memo} from 'react' import {StyleProp, StyleSheet, TextStyle, View, ViewStyle} from 'react-native' -import {Text} from './text/Text' -import {TextLinkOnWebOnly} from './Link' -import {niceDate} from 'lib/strings/time' +import {AppBskyActorDefs, ModerationDecision, ModerationUI} from '@atproto/api' + +import {usePrefetchProfileQuery} from '#/state/queries/profile' import {usePalette} from 'lib/hooks/usePalette' -import {TypographyVariant} from 'lib/ThemeContext' -import {UserAvatar} from './UserAvatar' +import {makeProfileLink} from 'lib/routes/links' import {sanitizeDisplayName} from 'lib/strings/display-names' import {sanitizeHandle} from 'lib/strings/handles' +import {niceDate} from 'lib/strings/time' +import {TypographyVariant} from 'lib/ThemeContext' import {isAndroid, isWeb} from 'platform/detection' +import {TextLinkOnWebOnly} from './Link' +import {Text} from './text/Text' import {TimeElapsed} from './TimeElapsed' -import {makeProfileLink} from 'lib/routes/links' -import {AppBskyActorDefs, ModerationDecision, ModerationUI} from '@atproto/api' -import {usePrefetchProfileQuery} from '#/state/queries/profile' +import {PreviewableUserAvatar} from './UserAvatar' interface PostMetaOpts { author: AppBskyActorDefs.ProfileViewBasic @@ -38,9 +39,11 @@ let PostMeta = (opts: PostMetaOpts): React.ReactNode => { {opts.showAvatar && ( - diff --git a/src/view/com/util/UserAvatar.tsx b/src/view/com/util/UserAvatar.tsx index 4beedbd5b4..89aa56b736 100644 --- a/src/view/com/util/UserAvatar.tsx +++ b/src/view/com/util/UserAvatar.tsx @@ -1,30 +1,32 @@ import React, {memo, useMemo} from 'react' import {Image, StyleSheet, TouchableOpacity, View} from 'react-native' -import Svg, {Circle, Rect, Path} from 'react-native-svg' import {Image as RNImage} from 'react-native-image-crop-picker' -import {useLingui} from '@lingui/react' -import {msg, Trans} from '@lingui/macro' -import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' +import Svg, {Circle, Path, Rect} from 'react-native-svg' import {ModerationUI} from '@atproto/api' +import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' +import {msg, Trans} from '@lingui/macro' +import {useLingui} from '@lingui/react' -import {HighPriorityImage} from 'view/com/util/images/Image' -import {openCamera, openCropper, openPicker} from '../../../lib/media/picker' -import { - usePhotoLibraryPermission, - useCameraPermission, -} from 'lib/hooks/usePermissions' -import {colors} from 'lib/styles' import {usePalette} from 'lib/hooks/usePalette' -import {isWeb, isAndroid, isNative} from 'platform/detection' -import {UserPreviewLink} from './UserPreviewLink' -import * as Menu from '#/components/Menu' import { - Camera_Stroke2_Corner0_Rounded as Camera, + useCameraPermission, + usePhotoLibraryPermission, +} from 'lib/hooks/usePermissions' +import {makeProfileLink} from 'lib/routes/links' +import {colors} from 'lib/styles' +import {isAndroid, isNative, isWeb} from 'platform/detection' +import {HighPriorityImage} from 'view/com/util/images/Image' +import {tokens, useTheme} from '#/alf' +import { Camera_Filled_Stroke2_Corner0_Rounded as CameraFilled, + Camera_Stroke2_Corner0_Rounded as Camera, } from '#/components/icons/Camera' import {StreamingLive_Stroke2_Corner0_Rounded as Library} from '#/components/icons/StreamingLive' import {Trash_Stroke2_Corner0_Rounded as Trash} from '#/components/icons/Trash' -import {useTheme, tokens} from '#/alf' +import {Link} from '#/components/Link' +import * as Menu from '#/components/Menu' +import {ProfileHoverCard} from '#/components/ProfileHoverCard' +import {openCamera, openCropper, openPicker} from '../../../lib/media/picker' export type UserAvatarType = 'user' | 'algo' | 'list' | 'labeler' @@ -372,10 +374,18 @@ export {EditableUserAvatar} let PreviewableUserAvatar = ( props: PreviewableUserAvatarProps, ): React.ReactNode => { + const {_} = useLingui() return ( - - - + + + + + ) } PreviewableUserAvatar = memo(PreviewableUserAvatar) diff --git a/src/view/com/util/UserPreviewLink.tsx b/src/view/com/util/UserPreviewLink.tsx deleted file mode 100644 index a2c46afc01..0000000000 --- a/src/view/com/util/UserPreviewLink.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import React from 'react' -import {StyleProp, ViewStyle} from 'react-native' -import {Link} from './Link' -import {isWeb} from 'platform/detection' -import {makeProfileLink} from 'lib/routes/links' -import {usePrefetchProfileQuery} from '#/state/queries/profile' - -interface UserPreviewLinkProps { - did: string - handle: string - style?: StyleProp -} -export function UserPreviewLink( - props: React.PropsWithChildren, -) { - const prefetchProfileQuery = usePrefetchProfileQuery() - return ( - { - if (isWeb) { - prefetchProfileQuery(props.did) - } - }} - href={makeProfileLink(props)} - title={props.handle} - asAnchor - style={props.style}> - {props.children} - - ) -} diff --git a/yarn.lock b/yarn.lock index 1a61c8b037..39bfc6a206 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3511,6 +3511,13 @@ resolved "https://registry.yarnpkg.com/@flatten-js/interval-tree/-/interval-tree-1.1.2.tgz#fcc891da48bc230392884be01c26fe8c625702e8" integrity sha512-OwLoV9E/XM6b7bes2rSFnGNjyRy7vcoIHFTnmBR2WAaZTf0Fe4EX4GdA65vU1KgFAasti7iRSg2dZfYd1Zt00Q== +"@floating-ui/core@^1.0.0": + version "1.6.0" + resolved "https://registry.yarnpkg.com/@floating-ui/core/-/core-1.6.0.tgz#fa41b87812a16bf123122bf945946bae3fdf7fc1" + integrity sha512-PcF++MykgmTj3CIyOQbKA/hDzOAiqI3mhuoN44WRCopIs1sgoDoU4oty4Jtqaj/y3oDU6fnVSm4QG0a3t5i0+g== + dependencies: + "@floating-ui/utils" "^0.2.1" + "@floating-ui/core@^1.4.1": version "1.4.1" resolved "https://registry.yarnpkg.com/@floating-ui/core/-/core-1.4.1.tgz#0d633f4b76052668afb932492ac452f7ebe97f17" @@ -3526,6 +3533,14 @@ "@floating-ui/core" "^1.4.1" "@floating-ui/utils" "^0.1.1" +"@floating-ui/dom@^1.6.1", "@floating-ui/dom@^1.6.3": + version "1.6.3" + resolved "https://registry.yarnpkg.com/@floating-ui/dom/-/dom-1.6.3.tgz#954e46c1dd3ad48e49db9ada7218b0985cee75ef" + integrity sha512-RnDthu3mzPlQ31Ss/BTwQ1zjzIhr3lk1gZB1OC56h/1vEtaXkESrOqL5fQVMfXpwGtRwX+YsZBdyHtJMQnkArw== + dependencies: + "@floating-ui/core" "^1.0.0" + "@floating-ui/utils" "^0.2.0" + "@floating-ui/react-dom@^2.0.0": version "2.0.1" resolved "https://registry.yarnpkg.com/@floating-ui/react-dom/-/react-dom-2.0.1.tgz#7972a4fc488a8c746cded3cfe603b6057c308a91" @@ -3533,11 +3548,23 @@ dependencies: "@floating-ui/dom" "^1.3.0" +"@floating-ui/react-dom@^2.0.8": + version "2.0.8" + resolved "https://registry.yarnpkg.com/@floating-ui/react-dom/-/react-dom-2.0.8.tgz#afc24f9756d1b433e1fe0d047c24bd4d9cefaa5d" + integrity sha512-HOdqOt3R3OGeTKidaLvJKcgg75S6tibQ3Tif4eyd91QnIJWr0NLvoXFpJA/j8HqkFSL68GDca9AuyWEHlhyClw== + dependencies: + "@floating-ui/dom" "^1.6.1" + "@floating-ui/utils@^0.1.1": version "0.1.1" resolved "https://registry.yarnpkg.com/@floating-ui/utils/-/utils-0.1.1.tgz#1a5b1959a528e374e8037c4396c3e825d6cf4a83" integrity sha512-m0G6wlnhm/AX0H12IOWtK8gASEMffnX08RtKkCgTdHb9JpHKGloI7icFfLg9ZmQeavcvR0PKmzxClyuFPSjKWw== +"@floating-ui/utils@^0.2.0", "@floating-ui/utils@^0.2.1": + version "0.2.1" + resolved "https://registry.yarnpkg.com/@floating-ui/utils/-/utils-0.2.1.tgz#16308cea045f0fc777b6ff20a9f25474dd8293d2" + integrity sha512-9TANp6GPoMtYzQdt54kfAyMmz1+osLlXdg2ENroU7zzrtflTLrrC/lgrIfaSe+Wu0b89GKccT7vxXA0MoAIO+Q== + "@fortawesome/fontawesome-common-types@6.4.2": version "6.4.2" resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.4.2.tgz#1766039cad33f8ad87f9467b98e0d18fbc8f01c5" From 6218eb0eeac2ccab33c56fda97a52837edd58694 Mon Sep 17 00:00:00 2001 From: Eric Bailey Date: Fri, 12 Apr 2024 17:19:58 -0500 Subject: [PATCH 026/167] Zhuzh sign in dialog (#3512) --- src/components/dialogs/Signin.tsx | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/components/dialogs/Signin.tsx b/src/components/dialogs/Signin.tsx index 488eb5c73a..b9c939e94b 100644 --- a/src/components/dialogs/Signin.tsx +++ b/src/components/dialogs/Signin.tsx @@ -45,7 +45,7 @@ function SigninDialogInner({}: {control: Dialog.DialogOuterProps['control']}) { - + - + Sign in or create your account to join the conversation! From c3821fdc311fe7ddebede427715892d3a1e53716 Mon Sep 17 00:00:00 2001 From: Hailey Date: Fri, 12 Apr 2024 15:22:09 -0700 Subject: [PATCH 027/167] Remove vertical scrollbars from views on native (#3429) * remove vertical scrollbars * add to a few missing lists * gate this change * use `hide_vertical_scroll_indicators` * fix gate lint * fix bool --- src/lib/statsig/gates.ts | 1 + .../com/auth/onboarding/RecommendedFeeds.tsx | 20 ++++++------ .../auth/onboarding/RecommendedFollows.tsx | 28 +++++++++-------- src/view/com/util/List.tsx | 13 +++++--- src/view/com/util/Views.jsx | 16 +++++++++- .../screens/ModerationBlockedAccounts.tsx | 31 ++++++++++++------- src/view/screens/ModerationMutedAccounts.tsx | 30 +++++++++++------- 7 files changed, 88 insertions(+), 51 deletions(-) diff --git a/src/lib/statsig/gates.ts b/src/lib/statsig/gates.ts index 81f3f19d56..314799f288 100644 --- a/src/lib/statsig/gates.ts +++ b/src/lib/statsig/gates.ts @@ -3,6 +3,7 @@ export type Gate = | 'autoexpand_suggestions_on_profile_follow' | 'disable_min_shell_on_foregrounding' | 'disable_poll_on_discover' + | 'hide_vertical_scroll_indicators' | 'new_profile_scroll_component' | 'new_search' | 'receive_updates' diff --git a/src/view/com/auth/onboarding/RecommendedFeeds.tsx b/src/view/com/auth/onboarding/RecommendedFeeds.tsx index d3318bffd8..95f8502f81 100644 --- a/src/view/com/auth/onboarding/RecommendedFeeds.tsx +++ b/src/view/com/auth/onboarding/RecommendedFeeds.tsx @@ -1,18 +1,19 @@ import React from 'react' import {ActivityIndicator, FlatList, StyleSheet, View} from 'react-native' import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' -import {TabletOrDesktop, Mobile} from 'view/com/util/layouts/Breakpoints' +import {msg, Trans} from '@lingui/macro' +import {useLingui} from '@lingui/react' + +import {useSuggestedFeedsQuery} from '#/state/queries/suggested-feeds' +import {usePalette} from 'lib/hooks/usePalette' +import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' +import {ErrorMessage} from 'view/com/util/error/ErrorMessage' +import {Button} from 'view/com/util/forms/Button' +import {Mobile, TabletOrDesktop} from 'view/com/util/layouts/Breakpoints' +import {TitleColumnLayout} from 'view/com/util/layouts/TitleColumnLayout' import {Text} from 'view/com/util/text/Text' import {ViewHeader} from 'view/com/util/ViewHeader' -import {TitleColumnLayout} from 'view/com/util/layouts/TitleColumnLayout' -import {Button} from 'view/com/util/forms/Button' import {RecommendedFeedsItem} from './RecommendedFeedsItem' -import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' -import {usePalette} from 'lib/hooks/usePalette' -import {ErrorMessage} from 'view/com/util/error/ErrorMessage' -import {Trans, msg} from '@lingui/macro' -import {useLingui} from '@lingui/react' -import {useSuggestedFeedsQuery} from '#/state/queries/suggested-feeds' type Props = { next: () => void @@ -130,6 +131,7 @@ export function RecommendedFeeds({next}: Props) { renderItem={({item}) => } keyExtractor={item => item.uri} style={{flex: 1}} + showsVerticalScrollIndicator={false} /> ) : isLoading ? ( diff --git a/src/view/com/auth/onboarding/RecommendedFollows.tsx b/src/view/com/auth/onboarding/RecommendedFollows.tsx index d275f6c90e..a840f949e4 100644 --- a/src/view/com/auth/onboarding/RecommendedFollows.tsx +++ b/src/view/com/auth/onboarding/RecommendedFollows.tsx @@ -1,21 +1,22 @@ import React from 'react' import {ActivityIndicator, FlatList, StyleSheet, View} from 'react-native' -import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' import {AppBskyActorDefs, moderateProfile} from '@atproto/api' -import {TabletOrDesktop, Mobile} from 'view/com/util/layouts/Breakpoints' -import {Text} from 'view/com/util/text/Text' -import {ViewHeader} from 'view/com/util/ViewHeader' -import {TitleColumnLayout} from 'view/com/util/layouts/TitleColumnLayout' -import {Button} from 'view/com/util/forms/Button' -import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' -import {usePalette} from 'lib/hooks/usePalette' -import {RecommendedFollowsItem} from './RecommendedFollowsItem' +import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' +import {msg, Trans} from '@lingui/macro' +import {useLingui} from '@lingui/react' + +import {logger} from '#/logger' +import {useModerationOpts} from '#/state/queries/preferences' import {useSuggestedFollowsQuery} from '#/state/queries/suggested-follows' import {useGetSuggestedFollowersByActor} from '#/state/queries/suggested-follows' -import {useModerationOpts} from '#/state/queries/preferences' -import {logger} from '#/logger' -import {Trans, msg} from '@lingui/macro' -import {useLingui} from '@lingui/react' +import {usePalette} from 'lib/hooks/usePalette' +import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' +import {Button} from 'view/com/util/forms/Button' +import {Mobile, TabletOrDesktop} from 'view/com/util/layouts/Breakpoints' +import {TitleColumnLayout} from 'view/com/util/layouts/TitleColumnLayout' +import {Text} from 'view/com/util/text/Text' +import {ViewHeader} from 'view/com/util/ViewHeader' +import {RecommendedFollowsItem} from './RecommendedFollowsItem' type Props = { next: () => void @@ -202,6 +203,7 @@ export function RecommendedFollows({next}: Props) { )} keyExtractor={item => item.did} style={{flex: 1}} + showsVerticalScrollIndicator={false} /> )} + + ) +} + +function toShareUrl(path: string) { + return `https://bsky.app${path}` +} + +/** + * Based on a snippet of code from React, which itself was based on the escape-html library. + * Copyright (c) Meta Platforms, Inc. and affiliates + * Copyright (c) 2012-2013 TJ Holowaychuk + * Copyright (c) 2015 Andreas Lubbe + * Copyright (c) 2015 Tiancheng "Timothy" Gu + * Licensed as MIT. + */ +const matchHtmlRegExp = /["'&<>]/ +function escapeHtml(string: string) { + const str = String(string) + const match = matchHtmlRegExp.exec(str) + if (!match) { + return str + } + let escape + let html = '' + let index + let lastIndex = 0 + for (index = match.index; index < str.length; index++) { + switch (str.charCodeAt(index)) { + case 34: // " + escape = '"' + break + case 38: // & + escape = '&' + break + case 39: // ' + escape = ''' + break + case 60: // < + escape = '<' + break + case 62: // > + escape = '>' + break + default: + continue + } + if (lastIndex !== index) { + html += str.slice(lastIndex, index) + } + lastIndex = index + 1 + html += escape + } + return lastIndex !== index ? html + str.slice(lastIndex, index) : html +} diff --git a/bskyembed/src/main.tsx b/bskyembed/src/screens/post.tsx similarity index 85% rename from bskyembed/src/main.tsx rename to bskyembed/src/screens/post.tsx index 895675434c..76c921540e 100644 --- a/bskyembed/src/main.tsx +++ b/bskyembed/src/screens/post.tsx @@ -1,27 +1,27 @@ -import './index.css' +import '../index.css' import {AppBskyFeedDefs, BskyAgent} from '@atproto/api' import {h, render} from 'preact' -import logo from '../assets/logo.svg' -import {Container} from './container' -import {Link} from './link' -import {Post} from './post' -import {getRkey} from './utils' +import logo from '../../assets/logo.svg' +import {Container} from '../components/container' +import {Link} from '../components/link' +import {Post} from '../components/post' +import {getRkey} from '../utils' const root = document.getElementById('app') if (!root) throw new Error('No root element') -const searchParams = new URLSearchParams(window.location.search) - const agent = new BskyAgent({ service: 'https://public.api.bsky.app', }) -const uri = searchParams.get('uri') +const uri = `at://${window.location.pathname.slice('/embed/'.length)}` + +console.log(uri) if (!uri) { - throw new Error('No uri in query string') + throw new Error('No uri in path') } agent diff --git a/bskyembed/src/utils.ts b/bskyembed/src/utils.ts index 3408fcd97a..1f6fd5061c 100644 --- a/bskyembed/src/utils.ts +++ b/bskyembed/src/utils.ts @@ -1,3 +1,5 @@ +import {AtUri} from '@atproto/api' + export function niceDate(date: number | string | Date) { const d = new Date(date) return `${d.toLocaleDateString('en-us', { @@ -11,5 +13,6 @@ export function niceDate(date: number | string | Date) { } export function getRkey({uri}: {uri: string}): string { - return uri.split('/').pop() as string + const at = new AtUri(uri) + return at.rkey } diff --git a/bskyembed/tsconfig.snippet.json b/bskyembed/tsconfig.snippet.json new file mode 100644 index 0000000000..a6b6071dd6 --- /dev/null +++ b/bskyembed/tsconfig.snippet.json @@ -0,0 +1,10 @@ + +{ + "compilerOptions": { + "target": "ES5", + "lib": ["DOM", "DOM.Iterable", "ESNext"], + "strict": true, + "outDir": "dist" + }, + "include": ["snippet"], +} diff --git a/bskyembed/vite.config.ts b/bskyembed/vite.config.ts index 8d0b920713..9acc9d5ee4 100644 --- a/bskyembed/vite.config.ts +++ b/bskyembed/vite.config.ts @@ -1,3 +1,5 @@ +import {resolve} from 'node:path' + import preact from '@preact/preset-vite' import legacy from '@vitejs/plugin-legacy' import type {UserConfig} from 'vite' @@ -12,7 +14,13 @@ const config: UserConfig = { }), ], build: { - assetsDir: 'static/embed/assets', + assetsDir: 'static', + rollupOptions: { + input: { + index: resolve(__dirname, 'index.html'), + post: resolve(__dirname, 'post.html'), + }, + }, }, } diff --git a/bskyweb/.gitignore b/bskyweb/.gitignore index 1d945e1dab..ace9fbf51d 100644 --- a/bskyweb/.gitignore +++ b/bskyweb/.gitignore @@ -9,6 +9,10 @@ static/js/*.js static/js/*.map static/js/*.js.LICENSE.txt templates/scripts.html +templates/*-embed.html +static/embed/*.html +static/embed/assets/*.js +static/embed/assets/*.css # Don't ignore this file !.gitignore From 4c966e5d6d1cbafe7a41d58268ffcb2cee31abe8 Mon Sep 17 00:00:00 2001 From: Samuel Newman Date: Sat, 13 Apr 2024 05:13:53 +0100 Subject: [PATCH 033/167] [Embeds] "Embed post" post dropdown option (#3513) * add embed option to post dropdown menu * put embed post button behind a gate * increase line height in dialog * add gate to gate name union * hide embed button if PWI optout * Ungate embed button * Escape HTML, align implementations * Make dialog conditionally rendered * Memoize EmbedDialog * Render dialog lazily --------- Co-authored-by: Dan Abramov --- .../codeBrackets_stroke2_corner0_rounded.svg | 1 + bskyembed/src/screens/landing.tsx | 6 +- src/components/dialogs/Embed.tsx | 191 ++++++++++++++++++ src/components/icons/CodeBrackets.tsx | 5 + src/lib/constants.ts | 2 + src/view/com/util/forms/PostDropdownBtn.tsx | 31 ++- src/view/com/util/post-ctrls/PostCtrls.tsx | 1 + 7 files changed, 233 insertions(+), 4 deletions(-) create mode 100644 assets/icons/codeBrackets_stroke2_corner0_rounded.svg create mode 100644 src/components/dialogs/Embed.tsx create mode 100644 src/components/icons/CodeBrackets.tsx diff --git a/assets/icons/codeBrackets_stroke2_corner0_rounded.svg b/assets/icons/codeBrackets_stroke2_corner0_rounded.svg new file mode 100644 index 0000000000..0cc239210e --- /dev/null +++ b/assets/icons/codeBrackets_stroke2_corner0_rounded.svg @@ -0,0 +1 @@ + diff --git a/bskyembed/src/screens/landing.tsx b/bskyembed/src/screens/landing.tsx index 88e84ffb67..7c8ef28108 100644 --- a/bskyembed/src/screens/landing.tsx +++ b/bskyembed/src/screens/landing.tsx @@ -159,6 +159,7 @@ function Snippet({thread}: {thread: AppBskyFeedDefs.ThreadViewPost}) { return '' } + const lang = record.langs && record.langs.length > 0 ? record.langs[0] : '' const profileHref = toShareUrl( ['/profile', thread.post.author.did].join('/'), ) @@ -167,10 +168,9 @@ function Snippet({thread}: {thread: AppBskyFeedDefs.ThreadViewPost}) { ['/profile', thread.post.author.did, 'post', urip.rkey].join('/'), ) - const lang = record.langs ? record.langs[0] : '' - // x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x - // DO NOT ADD ANY NEW INTERPOLATIOONS BELOW WITHOUT ESCAPING THEM! + // DO NOT ADD ANY NEW INTERPOLATIONS BELOW WITHOUT ESCAPING THEM! + // Also, keep this code synced with the app code in Embed.tsx. // x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x return `

${escapeHtml(record.text)}${ + record.embed + ? `

[image or embed]` + : '' + }

— ${escapeHtml( + postAuthor.displayName || postAuthor.handle, + )} (@${escapeHtml( + postAuthor.handle, + )}) ${escapeHtml( + niceDate(timestamp), + )}
` + }, [postUri, postCid, record, timestamp, postAuthor]) + + return ( + + + + Embed post + + + + Embed this post in your website. Simply copy the following snippet + and paste it into the HTML code of your website. + + + + + + + + + + + + + + ) +} + +/** + * Based on a snippet of code from React, which itself was based on the escape-html library. + * Copyright (c) Meta Platforms, Inc. and affiliates + * Copyright (c) 2012-2013 TJ Holowaychuk + * Copyright (c) 2015 Andreas Lubbe + * Copyright (c) 2015 Tiancheng "Timothy" Gu + * Licensed as MIT. + */ +const matchHtmlRegExp = /["'&<>]/ +function escapeHtml(string: string) { + const str = String(string) + const match = matchHtmlRegExp.exec(str) + if (!match) { + return str + } + let escape + let html = '' + let index + let lastIndex = 0 + for (index = match.index; index < str.length; index++) { + switch (str.charCodeAt(index)) { + case 34: // " + escape = '"' + break + case 38: // & + escape = '&' + break + case 39: // ' + escape = ''' + break + case 60: // < + escape = '<' + break + case 62: // > + escape = '>' + break + default: + continue + } + if (lastIndex !== index) { + html += str.slice(lastIndex, index) + } + lastIndex = index + 1 + html += escape + } + return lastIndex !== index ? html + str.slice(lastIndex, index) : html +} diff --git a/src/components/icons/CodeBrackets.tsx b/src/components/icons/CodeBrackets.tsx new file mode 100644 index 0000000000..59d5fca900 --- /dev/null +++ b/src/components/icons/CodeBrackets.tsx @@ -0,0 +1,5 @@ +import {createSinglePathSVG} from './TEMPLATE' + +export const CodeBrackets_Stroke2_Corner0_Rounded = createSinglePathSVG({ + path: 'M14.242 3.03a1 1 0 0 1 .728 1.213l-4 16a1 1 0 1 1-1.94-.485l4-16a1 1 0 0 1 1.213-.728ZM6.707 7.293a1 1 0 0 1 0 1.414L3.414 12l3.293 3.293a1 1 0 1 1-1.414 1.414l-4-4a1 1 0 0 1 0-1.414l4-4a1 1 0 0 1 1.414 0Zm10.586 0a1 1 0 0 1 1.414 0l4 4a1 1 0 0 1 0 1.414l-4 4a1 1 0 1 1-1.414-1.414L20.586 12l-3.293-3.293a1 1 0 0 1 0-1.414Z', +}) diff --git a/src/lib/constants.ts b/src/lib/constants.ts index 401c39362b..bb49387c4c 100644 --- a/src/lib/constants.ts +++ b/src/lib/constants.ts @@ -7,6 +7,8 @@ export const BSKY_SERVICE = 'https://bsky.social' export const DEFAULT_SERVICE = BSKY_SERVICE const HELP_DESK_LANG = 'en-us' export const HELP_DESK_URL = `https://blueskyweb.zendesk.com/hc/${HELP_DESK_LANG}` +export const EMBED_SERVICE = 'https://embed.bsky.app' +export const EMBED_SCRIPT = `${EMBED_SERVICE}/static/embed.js` const BASE_FEEDBACK_FORM_URL = `${HELP_DESK_URL}/requests/new` export function FEEDBACK_FORM_URL({ diff --git a/src/view/com/util/forms/PostDropdownBtn.tsx b/src/view/com/util/forms/PostDropdownBtn.tsx index 04dfa203a1..31032396f3 100644 --- a/src/view/com/util/forms/PostDropdownBtn.tsx +++ b/src/view/com/util/forms/PostDropdownBtn.tsx @@ -28,12 +28,14 @@ import {getCurrentRoute} from 'lib/routes/helpers' import {shareUrl} from 'lib/sharing' import {toShareUrl} from 'lib/strings/url-helpers' import {useTheme} from 'lib/ThemeContext' -import {atoms as a, useTheme as useAlf} from '#/alf' +import {atoms as a, useBreakpoints, useTheme as useAlf} from '#/alf' import {useDialogControl} from '#/components/Dialog' import {useGlobalDialogsControlContext} from '#/components/dialogs/Context' +import {EmbedDialog} from '#/components/dialogs/Embed' import {ArrowOutOfBox_Stroke2_Corner0_Rounded as Share} from '#/components/icons/ArrowOutOfBox' import {BubbleQuestion_Stroke2_Corner0_Rounded as Translate} from '#/components/icons/Bubble' import {Clipboard_Stroke2_Corner2_Rounded as ClipboardIcon} from '#/components/icons/Clipboard' +import {CodeBrackets_Stroke2_Corner0_Rounded as CodeBrackets} from '#/components/icons/CodeBrackets' import {EyeSlash_Stroke2_Corner0_Rounded as EyeSlash} from '#/components/icons/EyeSlash' import {Filter_Stroke2_Corner0_Rounded as Filter} from '#/components/icons/Filter' import {Mute_Stroke2_Corner0_Rounded as Mute} from '#/components/icons/Mute' @@ -55,6 +57,7 @@ let PostDropdownBtn = ({ richText, style, hitSlop, + timestamp, }: { testID: string postAuthor: AppBskyActorDefs.ProfileViewBasic @@ -64,10 +67,12 @@ let PostDropdownBtn = ({ richText: RichTextAPI style?: StyleProp hitSlop?: PressableProps['hitSlop'] + timestamp: string }): React.ReactNode => { const {hasSession, currentAccount} = useSession() const theme = useTheme() const alf = useAlf() + const {gtMobile} = useBreakpoints() const {_} = useLingui() const defaultCtrlColor = theme.palette.default.postCtrl const langPrefs = useLanguagePrefs() @@ -83,6 +88,7 @@ let PostDropdownBtn = ({ const deletePromptControl = useDialogControl() const hidePromptControl = useDialogControl() const loggedOutWarningPromptControl = useDialogControl() + const embedPostControl = useDialogControl() const rootUri = record.reply?.root?.uri || postUri const isThreadMuted = mutedThreads.includes(rootUri) @@ -177,6 +183,8 @@ let PostDropdownBtn = ({ shareUrl(url) }, [href]) + const canEmbed = isWeb && gtMobile && !shouldShowLoggedOutWarning + return ( @@ -238,6 +246,16 @@ let PostDropdownBtn = ({ + + {canEmbed && ( + + {_(msg`Embed post`)} + + + )} {hasSession && ( @@ -350,6 +368,17 @@ let PostDropdownBtn = ({ onConfirm={onSharePost} confirmButtonCta={_(msg`Share anyway`)} /> + + {canEmbed && ( + + )} ) } diff --git a/src/view/com/util/post-ctrls/PostCtrls.tsx b/src/view/com/util/post-ctrls/PostCtrls.tsx index cd4a363730..cb50ee6dc3 100644 --- a/src/view/com/util/post-ctrls/PostCtrls.tsx +++ b/src/view/com/util/post-ctrls/PostCtrls.tsx @@ -264,6 +264,7 @@ let PostCtrls = ({ richText={richText} style={styles.btnPad} hitSlop={big ? HITSLOP_20 : HITSLOP_10} + timestamp={post.indexedAt} />
From a845587e1f0b74b087b0c59d1cdc8e6c5feaf98f Mon Sep 17 00:00:00 2001 From: dan Date: Sat, 13 Apr 2024 05:50:09 +0100 Subject: [PATCH 034/167] [Embeds] Show error for users with PWI flag on landing (#3524) --- bskyembed/src/screens/landing.tsx | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/bskyembed/src/screens/landing.tsx b/bskyembed/src/screens/landing.tsx index 7c8ef28108..06b455981d 100644 --- a/bskyembed/src/screens/landing.tsx +++ b/bskyembed/src/screens/landing.tsx @@ -86,7 +86,14 @@ function LandingPage() { if (!AppBskyFeedDefs.isThreadViewPost(data.thread)) { throw new Error('Post not found') } - + const pwiOptOut = !!data.thread.post.author.labels?.find( + label => label.val === '!no-unauthenticated', + ) + if (pwiOptOut) { + throw new Error( + 'The author of this post has requested their posts not be displayed on external sites.', + ) + } setThread(data.thread) } catch (err) { console.error(err) @@ -113,25 +120,15 @@ function LandingPage() { className="border rounded-lg py-3 w-full max-w-[600px] px-4" placeholder={DEFAULT_POST} /> -

{error}

{uri && !error && thread && } - - {thread ? ( - - ) : ( - - - - -
- + {!error && thread && } + {error && ( +

{error}

)}
From f5bb348bf51df6f6d35eb23cdf771c184d77fec4 Mon Sep 17 00:00:00 2001 From: Eric Bailey Date: Sat, 13 Apr 2024 00:13:53 -0500 Subject: [PATCH 035/167] Profile hovers (#3518) * Add hover card for mentions * Reposted by * Fix key * Add to composer reply to --- src/components/ProfileHoverCard/index.web.tsx | 5 +- src/components/ProfileHoverCard/types.ts | 1 + src/components/RichText.tsx | 22 +++--- src/view/com/composer/ComposerReplyTo.tsx | 18 +++-- src/view/com/posts/FeedItem.tsx | 67 ++++++++++--------- 5 files changed, 64 insertions(+), 49 deletions(-) diff --git a/src/components/ProfileHoverCard/index.web.tsx b/src/components/ProfileHoverCard/index.web.tsx index 0d62a52a39..d0e1b58ee4 100644 --- a/src/components/ProfileHoverCard/index.web.tsx +++ b/src/components/ProfileHoverCard/index.web.tsx @@ -116,7 +116,10 @@ export function ProfileHoverCardInner(props: ProfileHoverCardProps) { ref={refs.setReference} onPointerEnter={onPointerEnterTarget} onPointerLeave={onPointerLeaveTarget} - onMouseUp={onClickTarget}> + onMouseUp={onClickTarget} + style={{ + display: props.inline ? 'inline' : 'block', + }}> {props.children} {hovered && ( diff --git a/src/components/ProfileHoverCard/types.ts b/src/components/ProfileHoverCard/types.ts index 4e70df5f0d..a62279c96c 100644 --- a/src/components/ProfileHoverCard/types.ts +++ b/src/components/ProfileHoverCard/types.ts @@ -3,4 +3,5 @@ import React from 'react' export type ProfileHoverCardProps = { children: React.ReactElement did: string + inline?: boolean } diff --git a/src/components/RichText.tsx b/src/components/RichText.tsx index 17f36c1418..82cdda1076 100644 --- a/src/components/RichText.tsx +++ b/src/components/RichText.tsx @@ -8,6 +8,7 @@ import {isNative} from '#/platform/detection' import {atoms as a, flatten, native, TextStyleProp, useTheme, web} from '#/alf' import {useInteractionState} from '#/components/hooks/useInteractionState' import {InlineLinkText, LinkProps} from '#/components/Link' +import {ProfileHoverCard} from '#/components/ProfileHoverCard' import {TagMenu, useTagMenuControl} from '#/components/TagMenu' import {Text, TextProps} from '#/components/Typography' @@ -86,16 +87,17 @@ export function RichText({ !disableLinks ) { els.push( - - {segment.text} - , + + + {segment.text} + + , ) } else if (link && AppBskyRichtextFacet.validateLink(link).success) { if (disableLinks) { diff --git a/src/view/com/composer/ComposerReplyTo.tsx b/src/view/com/composer/ComposerReplyTo.tsx index 0c1b87d04d..24a2373f5c 100644 --- a/src/view/com/composer/ComposerReplyTo.tsx +++ b/src/view/com/composer/ComposerReplyTo.tsx @@ -1,21 +1,22 @@ import React from 'react' import {LayoutAnimation, Pressable, StyleSheet, View} from 'react-native' import {Image} from 'expo-image' -import {useLingui} from '@lingui/react' -import {msg} from '@lingui/macro' import { AppBskyEmbedImages, AppBskyEmbedRecord, AppBskyEmbedRecordWithMedia, AppBskyFeedPost, } from '@atproto/api' -import {ComposerOptsPostRef} from 'state/shell/composer' +import {msg} from '@lingui/macro' +import {useLingui} from '@lingui/react' + import {usePalette} from 'lib/hooks/usePalette' import {sanitizeDisplayName} from 'lib/strings/display-names' import {sanitizeHandle} from 'lib/strings/handles' -import {UserAvatar} from 'view/com/util/UserAvatar' -import {Text} from 'view/com/util/text/Text' +import {ComposerOptsPostRef} from 'state/shell/composer' import {QuoteEmbed} from 'view/com/util/post-embeds/QuoteEmbed' +import {Text} from 'view/com/util/text/Text' +import {PreviewableUserAvatar} from 'view/com/util/UserAvatar' export function ComposerReplyTo({replyTo}: {replyTo: ComposerOptsPostRef}) { const pal = usePalette('default') @@ -83,9 +84,11 @@ export function ComposerReplyTo({replyTo}: {replyTo: ComposerOptsPostRef}) { accessibilityHint={_( msg`Expand or collapse the full post you are replying to`, )}> - @@ -216,6 +219,7 @@ function ComposerReplyToImages({ const styles = StyleSheet.create({ replyToLayout: { flexDirection: 'row', + alignItems: 'flex-start', borderTopWidth: 1, paddingTop: 16, paddingBottom: 16, diff --git a/src/view/com/posts/FeedItem.tsx b/src/view/com/posts/FeedItem.tsx index 0fbcc4a13c..cc403b7742 100644 --- a/src/view/com/posts/FeedItem.tsx +++ b/src/view/com/posts/FeedItem.tsx @@ -11,31 +11,33 @@ import { FontAwesomeIcon, FontAwesomeIconStyle, } from '@fortawesome/react-native-fontawesome' -import {ReasonFeedSource, isReasonFeedSource} from 'lib/api/feed/types' -import {Link, TextLinkOnWebOnly, TextLink} from '../util/Link' -import {Text} from '../util/text/Text' -import {UserInfoText} from '../util/UserInfoText' -import {PostMeta} from '../util/PostMeta' -import {PostCtrls} from '../util/post-ctrls/PostCtrls' -import {PostEmbeds} from '../util/post-embeds' -import {ContentHider} from '#/components/moderation/ContentHider' -import {PostAlerts} from '../../../components/moderation/PostAlerts' -import {LabelsOnMyPost} from '../../../components/moderation/LabelsOnMe' -import {RichText} from '#/components/RichText' -import {PreviewableUserAvatar} from '../util/UserAvatar' -import {s} from 'lib/styles' +import {msg, Trans} from '@lingui/macro' +import {useLingui} from '@lingui/react' + +import {POST_TOMBSTONE, Shadow, usePostShadow} from '#/state/cache/post-shadow' +import {useComposerControls} from '#/state/shell/composer' +import {isReasonFeedSource, ReasonFeedSource} from 'lib/api/feed/types' +import {MAX_POST_LINES} from 'lib/constants' import {usePalette} from 'lib/hooks/usePalette' +import {makeProfileLink} from 'lib/routes/links' import {sanitizeDisplayName} from 'lib/strings/display-names' import {sanitizeHandle} from 'lib/strings/handles' -import {makeProfileLink} from 'lib/routes/links' -import {MAX_POST_LINES} from 'lib/constants' import {countLines} from 'lib/strings/helpers' -import {useComposerControls} from '#/state/shell/composer' -import {Shadow, usePostShadow, POST_TOMBSTONE} from '#/state/cache/post-shadow' -import {FeedNameText} from '../util/FeedInfoText' -import {Trans, msg} from '@lingui/macro' -import {useLingui} from '@lingui/react' +import {s} from 'lib/styles' import {atoms as a} from '#/alf' +import {ContentHider} from '#/components/moderation/ContentHider' +import {ProfileHoverCard} from '#/components/ProfileHoverCard' +import {RichText} from '#/components/RichText' +import {LabelsOnMyPost} from '../../../components/moderation/LabelsOnMe' +import {PostAlerts} from '../../../components/moderation/PostAlerts' +import {FeedNameText} from '../util/FeedInfoText' +import {Link, TextLink, TextLinkOnWebOnly} from '../util/Link' +import {PostCtrls} from '../util/post-ctrls/PostCtrls' +import {PostEmbeds} from '../util/post-embeds' +import {PostMeta} from '../util/PostMeta' +import {Text} from '../util/text/Text' +import {PreviewableUserAvatar} from '../util/UserAvatar' +import {UserInfoText} from '../util/UserInfoText' export function FeedItem({ post, @@ -213,17 +215,20 @@ let FeedItemInner = ({ numberOfLines={1}> Reposted by{' '} - + + + From 826f6b043ca73f3cc459fbac62ae6de5f82e362b Mon Sep 17 00:00:00 2001 From: Hailey Date: Sat, 13 Apr 2024 03:18:18 -0700 Subject: [PATCH 036/167] Moderate content in embeds (#3525) * move info to its own file * Revert "move info to its own file" This reverts commit 1d45a2f4034f50cbe9cb25070f954042cdf9127a. * better way * all cases * pass labelInfo to ImageEmbed * blur avatars * add back as string * one more as string * external embed * add back as string again --- bskyembed/src/components/embed.tsx | 70 +++++++++++++++++++++++++----- bskyembed/src/components/post.tsx | 17 +++++--- bskyembed/src/labels.ts | 21 +++++++++ 3 files changed, 91 insertions(+), 17 deletions(-) create mode 100644 bskyembed/src/labels.ts diff --git a/bskyembed/src/components/embed.tsx b/bskyembed/src/components/embed.tsx index 2f9f6b3cdb..d880199652 100644 --- a/bskyembed/src/components/embed.tsx +++ b/bskyembed/src/components/embed.tsx @@ -9,23 +9,33 @@ import { AppBskyLabelerDefs, } from '@atproto/api' import {ComponentChildren, h} from 'preact' +import {useMemo} from 'preact/hooks' import infoIcon from '../../assets/circleInfo_stroke2_corner0_rounded.svg' +import {CONTENT_LABELS, labelsToInfo} from '../labels' import {getRkey} from '../utils' import {Link} from './link' -export function Embed({content}: {content: AppBskyFeedDefs.PostView['embed']}) { +export function Embed({ + content, + labels, +}: { + content: AppBskyFeedDefs.PostView['embed'] + labels: AppBskyFeedDefs.PostView['labels'] +}) { + const labelInfo = useMemo(() => labelsToInfo(labels), [labels]) + if (!content) return null try { // Case 1: Image if (AppBskyEmbedImages.isView(content)) { - return + return } // Case 2: External link if (AppBskyEmbedExternal.isView(content)) { - return + return } // Case 3: Record (quote or linked post) @@ -50,15 +60,22 @@ export function Embed({content}: {content: AppBskyFeedDefs.PostView['embed']}) { if (AppBskyFeedPost.isRecord(record.value)) { text = record.value.text } + + const isAuthorLabeled = record.author.labels?.some(label => + CONTENT_LABELS.includes(label.val), + ) + return (
- +
+ +

{record.author.displayName} @@ -74,7 +91,11 @@ export function Embed({content}: {content: AppBskyFeedDefs.PostView['embed']}) { return false }) .map(embed => ( - + ))} ) @@ -137,15 +158,19 @@ export function Embed({content}: {content: AppBskyFeedDefs.PostView['embed']}) { } // Case 4: Record with media - if (AppBskyEmbedRecordWithMedia.isView(content)) { + if ( + AppBskyEmbedRecordWithMedia.isView(content) && + AppBskyEmbedRecord.isViewRecord(content.record.record) + ) { return (

- +
) @@ -168,7 +193,17 @@ function Info({children}: {children: ComponentChildren}) { ) } -function ImageEmbed({content}: {content: AppBskyEmbedImages.View}) { +function ImageEmbed({ + content, + labelInfo, +}: { + content: AppBskyEmbedImages.View + labelInfo?: string +}) { + if (labelInfo) { + return {labelInfo} + } + switch (content.images.length) { case 1: return ( @@ -229,7 +264,13 @@ function ImageEmbed({content}: {content: AppBskyEmbedImages.View}) { } } -function ExternalEmbed({content}: {content: AppBskyEmbedExternal.View}) { +function ExternalEmbed({ + content, + labelInfo, +}: { + content: AppBskyEmbedExternal.View + labelInfo?: string +}) { function toNiceDomain(url: string): string { try { const urlp = new URL(url) @@ -238,6 +279,11 @@ function ExternalEmbed({content}: {content: AppBskyEmbedExternal.View}) { return url } } + + if (labelInfo) { + return {labelInfo} + } + return ( + CONTENT_LABELS.includes(label.val), + ) + let record: AppBskyFeedPost.Record | null = null if (AppBskyFeedPost.isRecord(post.record)) { record = post.record @@ -28,10 +33,12 @@ export function Post({thread}: Props) {
- +
+ +
- +
From acbadc610bc373752cf19e17ea48d1921dd5315e Mon Sep 17 00:00:00 2001 From: Samuel Newman Date: Sat, 13 Apr 2024 11:42:23 +0100 Subject: [PATCH 038/167] add hideRecord prop (#3527) --- bskyembed/src/components/embed.tsx | 34 ++++++++++++++++++------------ 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/bskyembed/src/components/embed.tsx b/bskyembed/src/components/embed.tsx index d880199652..4457defce4 100644 --- a/bskyembed/src/components/embed.tsx +++ b/bskyembed/src/components/embed.tsx @@ -19,9 +19,11 @@ import {Link} from './link' export function Embed({ content, labels, + hideRecord, }: { content: AppBskyFeedDefs.PostView['embed'] labels: AppBskyFeedDefs.PostView['labels'] + hideRecord?: boolean }) { const labelInfo = useMemo(() => labelsToInfo(labels), [labels]) @@ -40,6 +42,10 @@ export function Embed({ // Case 3: Record (quote or linked post) if (AppBskyEmbedRecord.isView(content)) { + if (hideRecord) { + return null + } + const record = content.record // Case 3.1: Post @@ -84,19 +90,14 @@ export function Embed({

{text &&

{text}

} - {record.embeds - ?.filter(embed => { - if (AppBskyEmbedImages.isView(embed)) return true - if (AppBskyEmbedExternal.isView(embed)) return true - return false - }) - .map(embed => ( - - ))} + {record.embeds?.map(embed => ( + + ))} ) } @@ -164,13 +165,18 @@ export function Embed({ ) { return (
- +
) From c3f75330ed995121f7eb7cde435396741493b7b3 Mon Sep 17 00:00:00 2001 From: dan Date: Sat, 13 Apr 2024 11:46:16 +0100 Subject: [PATCH 039/167] More obvious click area (#3528) --- bskyembed/src/components/post.tsx | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/bskyembed/src/components/post.tsx b/bskyembed/src/components/post.tsx index d0eaf228ac..3f2c745bdd 100644 --- a/bskyembed/src/components/post.tsx +++ b/bskyembed/src/components/post.tsx @@ -31,7 +31,7 @@ export function Post({thread}: Props) { return (
-
+
-
+
@@ -52,6 +52,7 @@ export function Post({thread}: Props) {

@{post.author.handle}

+
@@ -60,12 +61,14 @@ export function Post({thread}: Props) {
- -
+ + + +
{!!post.likeCount && (
From 1390b1dc9e35aafda328877c46e90860c6268453 Mon Sep 17 00:00:00 2001 From: dan Date: Sat, 13 Apr 2024 12:09:49 +0100 Subject: [PATCH 040/167] [Statsig] Send ref source (#3531) * [Statsig] Send ref source * Add is web check * Fix types --- src/lib/statsig/statsig.tsx | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/lib/statsig/statsig.tsx b/src/lib/statsig/statsig.tsx index 7513b945c6..3d2dc13092 100644 --- a/src/lib/statsig/statsig.tsx +++ b/src/lib/statsig/statsig.tsx @@ -9,11 +9,20 @@ import { } from 'statsig-react-native-expo' import {logger} from '#/logger' +import {isWeb} from '#/platform/detection' import {IS_TESTFLIGHT} from 'lib/app-info' import {useSession} from '../../state/session' import {LogEvents} from './events' import {Gate} from './gates' +let refSrc: string | undefined +let refUrl: string | undefined +if (isWeb && typeof window !== 'undefined') { + const params = new URLSearchParams(window.location.search) + refSrc = params.get('ref_src') ?? undefined + refUrl = params.get('ref_url') ?? undefined +} + export type {LogEvents} const statsigOptions = { @@ -97,6 +106,8 @@ function toStatsigUser(did: string | undefined) { userID, platform: Platform.OS, custom: { + refSrc, + refUrl, // Need to specify here too for gating. platform: Platform.OS, }, From 9fb20915e890be0993c15ad19b59105b78cf8f12 Mon Sep 17 00:00:00 2001 From: dan Date: Sat, 13 Apr 2024 12:19:21 +0100 Subject: [PATCH 041/167] [Embed] Don't reuse DOM when changing embed (#3530) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Don't reuse DOM when changing embed * add skeleton loading state 💀 * autoselect text --------- Co-authored-by: Samuel Newman --- bskyembed/src/components/container.tsx | 6 +-- bskyembed/src/screens/landing.tsx | 51 +++++++++++++++++++++----- 2 files changed, 44 insertions(+), 13 deletions(-) diff --git a/bskyembed/src/components/container.tsx b/bskyembed/src/components/container.tsx index a96addc8ce..5b1b2b7fb4 100644 --- a/bskyembed/src/components/container.tsx +++ b/bskyembed/src/components/container.tsx @@ -8,7 +8,7 @@ export function Container({ href, }: { children: ComponentChildren - href: string + href?: string }) { const ref = useRef(null) const prevHeight = useRef(0) @@ -39,7 +39,7 @@ export function Container({ ref={ref} className="w-full bg-white hover:bg-neutral-50 relative transition-colors max-w-[600px] min-w-[300px] flex border rounded-xl" onClick={() => { - if (ref.current) { + if (ref.current && href) { // forwardRef requires preact/compat - let's keep it simple // to keep the bundle size down const anchor = ref.current.querySelector('a') @@ -48,7 +48,7 @@ export function Container({ } } }}> - + {href && }
{children}
) diff --git a/bskyembed/src/screens/landing.tsx b/bskyembed/src/screens/landing.tsx index f10100baa3..0c55089357 100644 --- a/bskyembed/src/screens/landing.tsx +++ b/bskyembed/src/screens/landing.tsx @@ -1,7 +1,7 @@ import '../index.css' import {AppBskyFeedDefs, AppBskyFeedPost, AtUri, BskyAgent} from '@atproto/api' -import {Fragment, h, render} from 'preact' +import {h, render} from 'preact' import {useEffect, useMemo, useRef, useState} from 'preact/hooks' import arrowBottom from '../../assets/arrowBottom_stroke2_corner0_rounded.svg' @@ -30,6 +30,7 @@ render(, root) function LandingPage() { const [uri, setUri] = useState('') const [error, setError] = useState(null) + const [loading, setLoading] = useState(false) const [thread, setThread] = useState( null, ) @@ -37,6 +38,8 @@ function LandingPage() { useEffect(() => { void (async () => { setError(null) + setThread(null) + setLoading(true) try { let atUri = DEFAULT_URI @@ -98,6 +101,8 @@ function LandingPage() { } catch (err) { console.error(err) setError(err instanceof Error ? err.message : 'Invalid Bluesky URL') + } finally { + setLoading(false) } })() }, [uri]) @@ -122,19 +127,42 @@ function LandingPage() { -
- {uri && !error && thread && } - {!error && thread && } - {error && ( -
-

{error}

-
- )} -
+ {loading ? ( + + ) : ( +
+ {!error && thread && uri && } + {!error && thread && } + {error && ( +
+

{error}

+
+ )} +
+ )} ) } +function Skeleton() { + return ( + +
+
+
+
+
+
+
+
+
+
+
+
+ + ) +} + function Snippet({thread}: {thread: AppBskyFeedDefs.ThreadViewPost}) { const ref = useRef(null) const [copied, setCopied] = useState(false) @@ -195,6 +223,9 @@ function Snippet({thread}: {thread: AppBskyFeedDefs.ThreadViewPost}) { className="border rounded-lg py-3 w-full px-4" readOnly autoFocus + onFocus={() => { + ref.current?.select() + }} />
From 7543f72b778804b5293bb04505da01e8cc2bace1 Mon Sep 17 00:00:00 2001 From: Hailey Date: Sat, 13 Apr 2024 16:55:49 -0700 Subject: [PATCH 052/167] Remove extra wrapper on notification user links (#3548) --- src/view/com/notifications/FeedItem.tsx | 47 ++++++++++++------------- 1 file changed, 23 insertions(+), 24 deletions(-) diff --git a/src/view/com/notifications/FeedItem.tsx b/src/view/com/notifications/FeedItem.tsx index e1dae6659f..3c9c64061a 100644 --- a/src/view/com/notifications/FeedItem.tsx +++ b/src/view/com/notifications/FeedItem.tsx @@ -420,31 +420,30 @@ function ExpandedAuthorsList({ to={makeProfileLink({ did: author.did, handle: author.handle, - })}> - - - - - - - - - {sanitizeDisplayName(author.displayName || author.handle)} -   - - {sanitizeHandle(author.handle)} - + })} + style={styles.expandedAuthor}> + + + + + + + + {sanitizeDisplayName(author.displayName || author.handle)} +   + + {sanitizeHandle(author.handle)} - + ))} From 3b9c5ceeb3c5944e5de7b3fdec9be83d2a3da2f1 Mon Sep 17 00:00:00 2001 From: Hailey Date: Sat, 13 Apr 2024 17:02:32 -0700 Subject: [PATCH 053/167] Cache DID and profile basic on profile card presses (#3523) * cache profiles add onPress back rm log cache profile and did when pressing profile card * minimal diff --- src/view/com/profile/ProfileCard.tsx | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/view/com/profile/ProfileCard.tsx b/src/view/com/profile/ProfileCard.tsx index e6df5f6d0e..b52573a018 100644 --- a/src/view/com/profile/ProfileCard.tsx +++ b/src/view/com/profile/ProfileCard.tsx @@ -1,4 +1,4 @@ -import * as React from 'react' +import React from 'react' import {StyleProp, StyleSheet, View, ViewStyle} from 'react-native' import { AppBskyActorDefs, @@ -7,6 +7,7 @@ import { ModerationDecision, } from '@atproto/api' import {Trans} from '@lingui/macro' +import {useQueryClient} from '@tanstack/react-query' import {useModerationCauseDescription} from '#/lib/moderation/useModerationCauseDescription' import {useProfileShadow} from '#/state/cache/profile-shadow' @@ -19,6 +20,8 @@ import {makeProfileLink} from 'lib/routes/links' import {sanitizeDisplayName} from 'lib/strings/display-names' import {sanitizeHandle} from 'lib/strings/handles' import {s} from 'lib/styles' +import {profileBasicQueryKey as RQKEY_PROFILE_BASIC} from 'state/queries/profile' +import {RQKEY as RQKEY_URI} from 'state/queries/resolve-uri' import {Link} from '../util/Link' import {Text} from '../util/text/Text' import {PreviewableUserAvatar} from '../util/UserAvatar' @@ -47,10 +50,19 @@ export function ProfileCard({ onPress?: () => void style?: StyleProp }) { + const queryClient = useQueryClient() const pal = usePalette('default') const profile = useProfileShadow(profileUnshadowed) const moderationOpts = useModerationOpts() const isLabeler = profile?.associated?.labeler + + const onBeforePress = React.useCallback(() => { + onPress?.() + + queryClient.setQueryData(RQKEY_URI(profile.handle), profile.did) + queryClient.setQueryData(RQKEY_PROFILE_BASIC(profile.did), profile) + }, [onPress, profile, queryClient]) + if (!moderationOpts) { return null } @@ -72,8 +84,8 @@ export function ProfileCard({ ]} href={makeProfileLink(profile)} title={profile.handle} - onBeforePress={onPress} asAnchor + onBeforePress={onBeforePress} anchorNoUnderline> From cb3f246822516bfca5b77d0f291bc24b27b4c48b Mon Sep 17 00:00:00 2001 From: Nick Manos Date: Sat, 13 Apr 2024 20:02:44 -0400 Subject: [PATCH 054/167] Fix Android in-app browser closing when switching apps (#3546) --- src/state/preferences/in-app-browser.tsx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/state/preferences/in-app-browser.tsx b/src/state/preferences/in-app-browser.tsx index 2398f1f812..73c4bbbe78 100644 --- a/src/state/preferences/in-app-browser.tsx +++ b/src/state/preferences/in-app-browser.tsx @@ -1,15 +1,16 @@ import React from 'react' -import * as persisted from '#/state/persisted' import {Linking} from 'react-native' import * as WebBrowser from 'expo-web-browser' + import {isNative} from '#/platform/detection' -import {useModalControls} from '../modals' +import * as persisted from '#/state/persisted' import {usePalette} from 'lib/hooks/usePalette' import { + createBskyAppAbsoluteUrl, isBskyRSSUrl, isRelativeUrl, - createBskyAppAbsoluteUrl, } from 'lib/strings/url-helpers' +import {useModalControls} from '../modals' type StateContext = persisted.Schema['useInAppBrowser'] type SetContext = (v: persisted.Schema['useInAppBrowser']) => void @@ -78,6 +79,7 @@ export function useOpenLink() { presentationStyle: WebBrowser.WebBrowserPresentationStyle.FULL_SCREEN, toolbarColor: pal.colors.backgroundLight, + createTask: false, }) return } From 23056daa292905b0019565dc0c42870037ed98fe Mon Sep 17 00:00:00 2001 From: Mary <148872143+mary-ext@users.noreply.github.com> Date: Sun, 14 Apr 2024 07:13:05 +0700 Subject: [PATCH 055/167] fix: only close drawer if directly tapping backdrop (#3534) --- src/view/shell/index.web.tsx | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/view/shell/index.web.tsx b/src/view/shell/index.web.tsx index 51fb4a0a11..9dab23671f 100644 --- a/src/view/shell/index.web.tsx +++ b/src/view/shell/index.web.tsx @@ -1,5 +1,5 @@ import React, {useEffect} from 'react' -import {StyleSheet, TouchableOpacity, View} from 'react-native' +import {StyleSheet, TouchableWithoutFeedback, View} from 'react-native' import {msg} from '@lingui/macro' import {useLingui} from '@lingui/react' import {useNavigation} from '@react-navigation/native' @@ -51,15 +51,21 @@ function ShellInner() { {!isDesktop && isDrawerOpen && ( - setDrawerOpen(false)} - style={styles.drawerMask} + { + // Only close if press happens outside of the drawer + if (ev.target === ev.currentTarget) { + setDrawerOpen(false) + } + }} accessibilityLabel={_(msg`Close navigation footer`)} accessibilityHint={_(msg`Closes bottom navigation bar`)}> - - + + + + - + )} ) From 0b43d728e4b95fe2f8085b8d01e34963f8663c0d Mon Sep 17 00:00:00 2001 From: Paul Frazee Date: Sat, 13 Apr 2024 19:49:52 -0700 Subject: [PATCH 056/167] Improve the language behaviors around the PWI (#3545) * Handle leftnav overflow with longer languages' copy * Update the language dropdown to set ALL language prefs * Add hackfix to language cachebusting on PWI * Reset feeds on language change --- src/components/AppLanguageDropdown.tsx | 10 ++- src/components/AppLanguageDropdown.web.tsx | 10 ++- src/lib/api/feed/custom.ts | 81 +++++++++++++++++++--- src/state/preferences/languages.tsx | 8 ++- src/state/queries/post-feed.ts | 8 +++ src/view/shell/NavSignupCard.tsx | 8 ++- 6 files changed, 111 insertions(+), 14 deletions(-) diff --git a/src/components/AppLanguageDropdown.tsx b/src/components/AppLanguageDropdown.tsx index dea9e66fbf..02cd0ce2d4 100644 --- a/src/components/AppLanguageDropdown.tsx +++ b/src/components/AppLanguageDropdown.tsx @@ -1,16 +1,19 @@ import React from 'react' import {View} from 'react-native' import RNPickerSelect, {PickerSelectProps} from 'react-native-picker-select' +import {useQueryClient} from '@tanstack/react-query' import {sanitizeAppLanguageSetting} from '#/locale/helpers' import {APP_LANGUAGES} from '#/locale/languages' import {useLanguagePrefs, useLanguagePrefsApi} from '#/state/preferences' +import {resetPostsFeedQueries} from '#/state/queries/post-feed' import {atoms as a, useTheme} from '#/alf' import {ChevronBottom_Stroke2_Corner0_Rounded as ChevronDown} from '#/components/icons/Chevron' export function AppLanguageDropdown() { const t = useTheme() + const queryClient = useQueryClient() const langPrefs = useLanguagePrefs() const setLangPrefs = useLanguagePrefsApi() const sanitizedLang = sanitizeAppLanguageSetting(langPrefs.appLanguage) @@ -21,8 +24,13 @@ export function AppLanguageDropdown() { if (sanitizedLang !== value) { setLangPrefs.setAppLanguage(sanitizeAppLanguageSetting(value)) } + setLangPrefs.setPrimaryLanguage(value) + setLangPrefs.setContentLanguage(value) + + // reset feeds to refetch content + resetPostsFeedQueries(queryClient) }, - [sanitizedLang, setLangPrefs], + [sanitizedLang, setLangPrefs, queryClient], ) return ( diff --git a/src/components/AppLanguageDropdown.web.tsx b/src/components/AppLanguageDropdown.web.tsx index 8052a4ef3e..aea1b2b900 100644 --- a/src/components/AppLanguageDropdown.web.tsx +++ b/src/components/AppLanguageDropdown.web.tsx @@ -1,9 +1,11 @@ import React from 'react' import {View} from 'react-native' +import {useQueryClient} from '@tanstack/react-query' import {sanitizeAppLanguageSetting} from '#/locale/helpers' import {APP_LANGUAGES} from '#/locale/languages' import {useLanguagePrefs, useLanguagePrefsApi} from '#/state/preferences' +import {resetPostsFeedQueries} from '#/state/queries/post-feed' import {atoms as a, useTheme} from '#/alf' import {ChevronBottom_Stroke2_Corner0_Rounded as ChevronDown} from '#/components/icons/Chevron' import {Text} from '#/components/Typography' @@ -11,6 +13,7 @@ import {Text} from '#/components/Typography' export function AppLanguageDropdown() { const t = useTheme() + const queryClient = useQueryClient() const langPrefs = useLanguagePrefs() const setLangPrefs = useLanguagePrefsApi() @@ -24,8 +27,13 @@ export function AppLanguageDropdown() { if (sanitizedLang !== value) { setLangPrefs.setAppLanguage(sanitizeAppLanguageSetting(value)) } + setLangPrefs.setPrimaryLanguage(value) + setLangPrefs.setContentLanguage(value) + + // reset feeds to refetch content + resetPostsFeedQueries(queryClient) }, - [sanitizedLang, setLangPrefs], + [sanitizedLang, setLangPrefs, queryClient], ) return ( diff --git a/src/lib/api/feed/custom.ts b/src/lib/api/feed/custom.ts index 41c5367e57..bd30d58acb 100644 --- a/src/lib/api/feed/custom.ts +++ b/src/lib/api/feed/custom.ts @@ -1,10 +1,12 @@ import { AppBskyFeedDefs, AppBskyFeedGetFeed as GetCustomFeed, + AtpAgent, } from '@atproto/api' -import {FeedAPI, FeedAPIResponse} from './types' -import {getAgent} from '#/state/session' + import {getContentLanguages} from '#/state/preferences/languages' +import {getAgent} from '#/state/session' +import {FeedAPI, FeedAPIResponse} from './types' export class CustomFeedAPI implements FeedAPI { constructor(public params: GetCustomFeed.QueryParams) {} @@ -29,14 +31,17 @@ export class CustomFeedAPI implements FeedAPI { limit: number }): Promise { const contentLangs = getContentLanguages().join(',') - const res = await getAgent().app.bsky.feed.getFeed( - { - ...this.params, - cursor, - limit, - }, - {headers: {'Accept-Language': contentLangs}}, - ) + const agent = getAgent() + const res = agent.session + ? await getAgent().app.bsky.feed.getFeed( + { + ...this.params, + cursor, + limit, + }, + {headers: {'Accept-Language': contentLangs}}, + ) + : await loggedOutFetch({...this.params, cursor, limit}) if (res.success) { // NOTE // some custom feeds fail to enforce the pagination limit @@ -55,3 +60,59 @@ export class CustomFeedAPI implements FeedAPI { } } } + +// HACK +// we want feeds to give language-specific results immediately when a +// logged-out user changes their language. this comes with two problems: +// 1. not all languages have content, and +// 2. our public caching layer isnt correctly busting against the accept-language header +// for now we handle both of these with a manual workaround +// -prf +async function loggedOutFetch({ + feed, + limit, + cursor, +}: { + feed: string + limit: number + cursor?: string +}) { + let contentLangs = getContentLanguages().join(',') + + // manually construct fetch call so we can add the `lang` cache-busting param + let res = await AtpAgent.fetch!( + `https://api.bsky.app/xrpc/app.bsky.feed.getFeed?feed=${feed}${ + cursor ? `&cursor=${cursor}` : '' + }&limit=${limit}&lang=${contentLangs}`, + 'GET', + {'Accept-Language': contentLangs}, + undefined, + ) + if (res.body?.feed?.length) { + return { + success: true, + data: res.body, + } + } + + // no data, try again with language headers removed + res = await AtpAgent.fetch!( + `https://api.bsky.app/xrpc/app.bsky.feed.getFeed?feed=${feed}${ + cursor ? `&cursor=${cursor}` : '' + }&limit=${limit}`, + 'GET', + {'Accept-Language': ''}, + undefined, + ) + if (res.body?.feed?.length) { + return { + success: true, + data: res.body, + } + } + + return { + success: false, + data: {feed: []}, + } +} diff --git a/src/state/preferences/languages.tsx b/src/state/preferences/languages.tsx index df774c05e2..b7494c1f93 100644 --- a/src/state/preferences/languages.tsx +++ b/src/state/preferences/languages.tsx @@ -1,6 +1,7 @@ import React from 'react' -import * as persisted from '#/state/persisted' + import {AppLanguage} from '#/locale/languages' +import * as persisted from '#/state/persisted' type SetStateCb = ( s: persisted.Schema['languagePrefs'], @@ -9,6 +10,7 @@ type StateContext = persisted.Schema['languagePrefs'] type ApiContext = { setPrimaryLanguage: (code2: string) => void setPostLanguage: (commaSeparatedLangCodes: string) => void + setContentLanguage: (code2: string) => void toggleContentLanguage: (code2: string) => void togglePostLanguage: (code2: string) => void savePostLanguageToHistory: () => void @@ -21,6 +23,7 @@ const stateContext = React.createContext( const apiContext = React.createContext({ setPrimaryLanguage: (_: string) => {}, setPostLanguage: (_: string) => {}, + setContentLanguage: (_: string) => {}, toggleContentLanguage: (_: string) => {}, togglePostLanguage: (_: string) => {}, savePostLanguageToHistory: () => {}, @@ -53,6 +56,9 @@ export function Provider({children}: React.PropsWithChildren<{}>) { setPostLanguage(commaSeparatedLangCodes: string) { setStateWrapped(s => ({...s, postLanguage: commaSeparatedLangCodes})) }, + setContentLanguage(code2: string) { + setStateWrapped(s => ({...s, contentLanguages: [code2]})) + }, toggleContentLanguage(code2: string) { setStateWrapped(s => { const exists = s.contentLanguages.includes(code2) diff --git a/src/state/queries/post-feed.ts b/src/state/queries/post-feed.ts index ee22bac691..3453a77648 100644 --- a/src/state/queries/post-feed.ts +++ b/src/state/queries/post-feed.ts @@ -459,6 +459,14 @@ function assertSomePostsPassModeration(feed: AppBskyFeedDefs.FeedViewPost[]) { } } +export function resetPostsFeedQueries(queryClient: QueryClient, timeout = 0) { + setTimeout(() => { + queryClient.resetQueries({ + predicate: query => query.queryKey[0] === RQKEY_ROOT, + }) + }, timeout) +} + export function resetProfilePostsQueries( queryClient: QueryClient, did: string, diff --git a/src/view/shell/NavSignupCard.tsx b/src/view/shell/NavSignupCard.tsx index aa807f0cc6..12bfa7ea05 100644 --- a/src/view/shell/NavSignupCard.tsx +++ b/src/view/shell/NavSignupCard.tsx @@ -48,7 +48,13 @@ let NavSignupCard = ({}: {}): React.ReactNode => { - + ) : ( + ) : null} + @@ -586,7 +613,7 @@ const styles = StyleSheet.create({ }, bottomBar: { flexDirection: 'row', - paddingVertical: 10, + paddingVertical: 4, paddingLeft: 15, paddingRight: 20, alignItems: 'center', diff --git a/src/view/com/composer/photos/OpenCameraBtn.tsx b/src/view/com/composer/photos/OpenCameraBtn.tsx index 4353704d57..8f9152e34d 100644 --- a/src/view/com/composer/photos/OpenCameraBtn.tsx +++ b/src/view/com/composer/photos/OpenCameraBtn.tsx @@ -1,32 +1,31 @@ import React, {useCallback} from 'react' -import {TouchableOpacity, StyleSheet} from 'react-native' import * as MediaLibrary from 'expo-media-library' -import { - FontAwesomeIcon, - FontAwesomeIconStyle, -} from '@fortawesome/react-native-fontawesome' -import {usePalette} from 'lib/hooks/usePalette' -import {useAnalytics} from 'lib/analytics/analytics' -import {openCamera} from 'lib/media/picker' -import {useCameraPermission} from 'lib/hooks/usePermissions' -import {HITSLOP_10, POST_IMG_MAX} from 'lib/constants' -import {GalleryModel} from 'state/models/media/gallery' -import {isMobileWeb, isNative} from 'platform/detection' -import {logger} from '#/logger' -import {useLingui} from '@lingui/react' import {msg} from '@lingui/macro' +import {useLingui} from '@lingui/react' + +import {useAnalytics} from '#/lib/analytics/analytics' +import {POST_IMG_MAX} from '#/lib/constants' +import {useCameraPermission} from '#/lib/hooks/usePermissions' +import {openCamera} from '#/lib/media/picker' +import {logger} from '#/logger' +import {isMobileWeb, isNative} from '#/platform/detection' +import {GalleryModel} from '#/state/models/media/gallery' +import {atoms as a, useTheme} from '#/alf' +import {Button} from '#/components/Button' +import {Camera_Stroke2_Corner0_Rounded as Camera} from '#/components/icons/Camera' type Props = { gallery: GalleryModel + disabled?: boolean } -export function OpenCameraBtn({gallery}: Props) { - const pal = usePalette('default') +export function OpenCameraBtn({gallery, disabled}: Props) { const {track} = useAnalytics() const {_} = useLingui() const {requestCameraAccessIfNeeded} = useCameraPermission() const [mediaPermissionRes, requestMediaPermission] = MediaLibrary.usePermissions() + const t = useTheme() const onPressTakePicture = useCallback(async () => { track('Composer:CameraOpened') @@ -68,25 +67,17 @@ export function OpenCameraBtn({gallery}: Props) { } return ( - - - + label={_(msg`Camera`)} + accessibilityHint={_(msg`Opens camera on device`)} + style={a.p_sm} + variant="ghost" + shape="round" + color="primary" + disabled={disabled}> + + ) } - -const styles = StyleSheet.create({ - button: { - paddingHorizontal: 15, - }, -}) diff --git a/src/view/com/composer/photos/SelectGifBtn.tsx b/src/view/com/composer/photos/SelectGifBtn.tsx new file mode 100644 index 0000000000..31310fdc1d --- /dev/null +++ b/src/view/com/composer/photos/SelectGifBtn.tsx @@ -0,0 +1,53 @@ +import React, {useCallback} from 'react' +import {Keyboard} from 'react-native' +import {msg} from '@lingui/macro' +import {useLingui} from '@lingui/react' + +import {logEvent} from '#/lib/statsig/statsig' +import {Gif} from '#/state/queries/giphy' +import {atoms as a, useTheme} from '#/alf' +import {Button} from '#/components/Button' +import {useDialogControl} from '#/components/Dialog' +import {GifSelectDialog} from '#/components/dialogs/GifSelect' +import {GifSquare_Stroke2_Corner0_Rounded as GifIcon} from '#/components/icons/Gif' + +type Props = { + onClose: () => void + onSelectGif: (gif: Gif) => void + disabled?: boolean +} + +export function SelectGifBtn({onClose, onSelectGif, disabled}: Props) { + const {_} = useLingui() + const control = useDialogControl() + const t = useTheme() + + const onPressSelectGif = useCallback(async () => { + logEvent('composer:gif:open', {}) + Keyboard.dismiss() + control.open() + }, [control]) + + return ( + <> + + + + + ) +} diff --git a/src/view/com/composer/photos/SelectPhotoBtn.tsx b/src/view/com/composer/photos/SelectPhotoBtn.tsx index f7fa9502d6..747653fc8d 100644 --- a/src/view/com/composer/photos/SelectPhotoBtn.tsx +++ b/src/view/com/composer/photos/SelectPhotoBtn.tsx @@ -1,27 +1,26 @@ +/* eslint-disable react-native-a11y/has-valid-accessibility-ignores-invert-colors */ import React, {useCallback} from 'react' -import {TouchableOpacity, StyleSheet} from 'react-native' -import { - FontAwesomeIcon, - FontAwesomeIconStyle, -} from '@fortawesome/react-native-fontawesome' -import {usePalette} from 'lib/hooks/usePalette' -import {useAnalytics} from 'lib/analytics/analytics' -import {usePhotoLibraryPermission} from 'lib/hooks/usePermissions' -import {GalleryModel} from 'state/models/media/gallery' -import {HITSLOP_10} from 'lib/constants' -import {isNative} from 'platform/detection' -import {useLingui} from '@lingui/react' import {msg} from '@lingui/macro' +import {useLingui} from '@lingui/react' + +import {useAnalytics} from '#/lib/analytics/analytics' +import {usePhotoLibraryPermission} from '#/lib/hooks/usePermissions' +import {isNative} from '#/platform/detection' +import {GalleryModel} from '#/state/models/media/gallery' +import {atoms as a, useTheme} from '#/alf' +import {Button} from '#/components/Button' +import {Image_Stroke2_Corner0_Rounded as Image} from '#/components/icons/Image' type Props = { gallery: GalleryModel + disabled?: boolean } -export function SelectPhotoBtn({gallery}: Props) { - const pal = usePalette('default') +export function SelectPhotoBtn({gallery, disabled}: Props) { const {track} = useAnalytics() const {_} = useLingui() const {requestPhotoAccessIfNeeded} = usePhotoLibraryPermission() + const t = useTheme() const onPressSelectPhotos = useCallback(async () => { track('Composer:GalleryOpened') @@ -34,25 +33,17 @@ export function SelectPhotoBtn({gallery}: Props) { }, [track, requestPhotoAccessIfNeeded, gallery]) return ( - - - + label={_(msg`Gallery`)} + accessibilityHint={_(msg`Opens device photo gallery`)} + style={a.p_sm} + variant="ghost" + shape="round" + color="primary" + disabled={disabled}> + + ) } - -const styles = StyleSheet.create({ - button: { - paddingHorizontal: 15, - }, -}) diff --git a/src/view/screens/Storybook/Buttons.tsx b/src/view/screens/Storybook/Buttons.tsx index cae8ec3144..b532b0dd16 100644 --- a/src/view/screens/Storybook/Buttons.tsx +++ b/src/view/screens/Storybook/Buttons.tsx @@ -9,7 +9,7 @@ import { ButtonText, ButtonVariant, } from '#/components/Button' -import {ArrowTopRight_Stroke2_Corner0_Rounded as ArrowTopRight} from '#/components/icons/ArrowTopRight' +import {ArrowTopRight_Stroke2_Corner0_Rounded as ArrowTopRight} from '#/components/icons/Arrow' import {ChevronLeft_Stroke2_Corner0_Rounded as ChevronLeft} from '#/components/icons/Chevron' import {Globe_Stroke2_Corner0_Rounded as Globe} from '#/components/icons/Globe' import {H1} from '#/components/Typography' diff --git a/src/view/screens/Storybook/Icons.tsx b/src/view/screens/Storybook/Icons.tsx index 9d7dc0aa8a..bff1fdc9b7 100644 --- a/src/view/screens/Storybook/Icons.tsx +++ b/src/view/screens/Storybook/Icons.tsx @@ -2,11 +2,11 @@ import React from 'react' import {View} from 'react-native' import {atoms as a, useTheme} from '#/alf' -import {H1} from '#/components/Typography' -import {Globe_Stroke2_Corner0_Rounded as Globe} from '#/components/icons/Globe' -import {ArrowTopRight_Stroke2_Corner0_Rounded as ArrowTopRight} from '#/components/icons/ArrowTopRight' +import {ArrowTopRight_Stroke2_Corner0_Rounded as ArrowTopRight} from '#/components/icons/Arrow' import {CalendarDays_Stroke2_Corner0_Rounded as CalendarDays} from '#/components/icons/CalendarDays' +import {Globe_Stroke2_Corner0_Rounded as Globe} from '#/components/icons/Globe' import {Loader} from '#/components/Loader' +import {H1} from '#/components/Typography' export function Icons() { const t = useTheme() From c91f065be5d3f9b34431cee7113367b085e84030 Mon Sep 17 00:00:00 2001 From: Hailey Date: Thu, 18 Apr 2024 21:32:25 -0700 Subject: [PATCH 095/167] add dimensions to data (#3616) * add dimensions to data * keep alt text * put it in the right url * just send the original height and width instead --- src/view/com/composer/Composer.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/view/com/composer/Composer.tsx b/src/view/com/composer/Composer.tsx index f0f630dd46..77bec9bff8 100644 --- a/src/view/com/composer/Composer.tsx +++ b/src/view/com/composer/Composer.tsx @@ -333,7 +333,7 @@ export const ComposePost = observer(function ComposePost({ const onSelectGif = useCallback( (gif: Gif) => setExtLink({ - uri: gif.url, + uri: `${gif.url}?hh=${gif.images.original.height}&ww=${gif.images.original.width}`, isLoading: true, meta: { url: gif.url, From f709fbcbddde49a812197d79758482b6497be8d2 Mon Sep 17 00:00:00 2001 From: Hailey Date: Thu, 18 Apr 2024 21:38:54 -0700 Subject: [PATCH 096/167] align center post meta in threaded (#3615) * align center post meta in threaded * put `displayNameStyle` in correct place * maybe? * with mobile padding too? --- src/view/com/post-thread/PostThreadItem.tsx | 8 +++++++- src/view/com/util/PostMeta.tsx | 6 ++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/view/com/post-thread/PostThreadItem.tsx b/src/view/com/post-thread/PostThreadItem.tsx index 089714c727..ddcf1a1306 100644 --- a/src/view/com/post-thread/PostThreadItem.tsx +++ b/src/view/com/post-thread/PostThreadItem.tsx @@ -27,6 +27,7 @@ import {sanitizeHandle} from 'lib/strings/handles' import {countLines, pluralize} from 'lib/strings/helpers' import {niceDate} from 'lib/strings/time' import {s} from 'lib/styles' +import {isWeb} from 'platform/detection' import {useSession} from 'state/session' import {PostThreadFollowBtn} from 'view/com/post-thread/PostThreadFollowBtn' import {atoms as a} from '#/alf' @@ -478,7 +479,12 @@ let PostThreadItemLoaded = ({ avatarSize={28} displayNameType="md-bold" displayNameStyle={isThreadedChild && s.ml2} - style={isThreadedChild && s.mb2} + style={ + isThreadedChild && { + alignItems: 'center', + paddingBottom: isWeb ? 5 : 2, + } + } /> { /> )} - + Date: Fri, 19 Apr 2024 15:23:47 +0100 Subject: [PATCH 097/167] [Statsig] Update experiments (#3617) --- src/lib/statsig/gates.ts | 10 ++++----- .../Profile/Header/ProfileHeaderStandard.tsx | 2 +- src/state/shell/selected-feed.tsx | 2 +- src/view/com/feeds/FeedPage.tsx | 22 +++++++------------ .../com/post-thread/PostThreadFollowBtn.tsx | 2 +- src/view/screens/Home.tsx | 18 +++++++++++---- 6 files changed, 30 insertions(+), 26 deletions(-) diff --git a/src/lib/statsig/gates.ts b/src/lib/statsig/gates.ts index 314799f288..d540cde10b 100644 --- a/src/lib/statsig/gates.ts +++ b/src/lib/statsig/gates.ts @@ -1,12 +1,12 @@ export type Gate = // Keep this alphabetic please. - | 'autoexpand_suggestions_on_profile_follow' - | 'disable_min_shell_on_foregrounding' - | 'disable_poll_on_discover' + | 'autoexpand_suggestions_on_profile_follow_v2' + | 'disable_min_shell_on_foregrounding_v2' + | 'disable_poll_on_discover_v2' | 'hide_vertical_scroll_indicators' | 'new_profile_scroll_component' | 'new_search' | 'receive_updates' - | 'show_follow_back_label' - | 'start_session_with_following' + | 'show_follow_back_label_v2' + | 'start_session_with_following_v2' | 'use_new_suggestions_endpoint' diff --git a/src/screens/Profile/Header/ProfileHeaderStandard.tsx b/src/screens/Profile/Header/ProfileHeaderStandard.tsx index 9e03613263..7c52bcbda7 100644 --- a/src/screens/Profile/Header/ProfileHeaderStandard.tsx +++ b/src/screens/Profile/Header/ProfileHeaderStandard.tsx @@ -94,7 +94,7 @@ let ProfileHeaderStandard = ({ )}`, ), ) - if (isWeb && gate('autoexpand_suggestions_on_profile_follow')) { + if (isWeb && gate('autoexpand_suggestions_on_profile_follow_v2')) { setShowSuggestedFollows(true) } } catch (e: any) { diff --git a/src/state/shell/selected-feed.tsx b/src/state/shell/selected-feed.tsx index dca3445f33..df50b3952f 100644 --- a/src/state/shell/selected-feed.tsx +++ b/src/state/shell/selected-feed.tsx @@ -27,7 +27,7 @@ function getInitialFeed(gate: (gateName: Gate) => boolean) { return feedFromSession } } - if (!gate('start_session_with_following')) { + if (!gate('start_session_with_following_v2')) { const feedFromPersisted = persisted.get('lastSelectedHomeFeed') if (feedFromPersisted) { // Fall back to the last chosen one across all tabs. diff --git a/src/view/com/feeds/FeedPage.tsx b/src/view/com/feeds/FeedPage.tsx index 2b8fde632c..4ebf64da9a 100644 --- a/src/view/com/feeds/FeedPage.tsx +++ b/src/view/com/feeds/FeedPage.tsx @@ -104,17 +104,11 @@ export function FeedPage({ }) }, [scrollToTop, feed, queryClient, setHasNew]) - let feedPollInterval - if ( - feed === // Discover - 'feedgen|at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.feed.generator/whats-hot' && - // TODO: This gate check is still too early. Move it to where the polling happens. - gate('disable_poll_on_discover') - ) { - feedPollInterval = undefined - } else { - feedPollInterval = POLL_FREQ - } + const isDiscoverFeed = + feed === + 'feedgen|at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.feed.generator/whats-hot' + const adjustedHasNew = + hasNew && !(isDiscoverFeed && gate('disable_poll_on_discover_v2')) return ( @@ -124,7 +118,7 @@ export function FeedPage({ enabled={isPageFocused} feed={feed} feedParams={feedParams} - pollInterval={feedPollInterval} + pollInterval={POLL_FREQ} disablePoll={hasNew} scrollElRef={scrollElRef} onScrolledDownChange={setIsScrolledDown} @@ -134,11 +128,11 @@ export function FeedPage({ headerOffset={headerOffset} /> - {(isScrolledDown || hasNew) && ( + {(isScrolledDown || adjustedHasNew) && ( )} diff --git a/src/view/com/post-thread/PostThreadFollowBtn.tsx b/src/view/com/post-thread/PostThreadFollowBtn.tsx index 7c9a544515..1f70f41c4a 100644 --- a/src/view/com/post-thread/PostThreadFollowBtn.tsx +++ b/src/view/com/post-thread/PostThreadFollowBtn.tsx @@ -140,7 +140,7 @@ function PostThreadFollowBtnLoaded({ style={[!isFollowing ? palInverted.text : pal.text, s.bold]} numberOfLines={1}> {!isFollowing ? ( - isFollowedBy && gate('show_follow_back_label') ? ( + isFollowedBy && gate('show_follow_back_label_v2') ? ( Follow Back ) : ( Follow diff --git a/src/view/screens/Home.tsx b/src/view/screens/Home.tsx index fbaa49a321..3eaa1b8757 100644 --- a/src/view/screens/Home.tsx +++ b/src/view/screens/Home.tsx @@ -5,6 +5,7 @@ import {useFocusEffect} from '@react-navigation/native' import {PROD_DEFAULT_FEED} from '#/lib/constants' import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback' import {useSetTitle} from '#/lib/hooks/useSetTitle' +import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' import {logEvent, LogEvents, useGate} from '#/lib/statsig/statsig' import {emitSoftReset} from '#/state/events' import {FeedSourceInfo, usePinnedFeedsInfos} from '#/state/queries/feed' @@ -12,7 +13,11 @@ import {FeedDescriptor, FeedParams} from '#/state/queries/post-feed' import {usePreferencesQuery} from '#/state/queries/preferences' import {UsePreferencesQueryResponse} from '#/state/queries/preferences/types' import {useSession} from '#/state/session' -import {useSetDrawerSwipeDisabled, useSetMinimalShellMode} from '#/state/shell' +import { + useMinimalShellMode, + useSetDrawerSwipeDisabled, + useSetMinimalShellMode, +} from '#/state/shell' import {useSelectedFeed, useSetSelectedFeed} from '#/state/shell/selected-feed' import {useOTAUpdates} from 'lib/hooks/useOTAUpdates' import {HomeTabNavigatorParams, NativeStackScreenProps} from 'lib/routes/types' @@ -112,11 +117,16 @@ function HomeScreenReady({ ) const gate = useGate() + const mode = useMinimalShellMode() + const {isMobile} = useWebMediaQueries() React.useEffect(() => { const listener = AppState.addEventListener('change', nextAppState => { if (nextAppState === 'active') { - // TODO: Check if minimal shell is on before logging an exposure. - if (gate('disable_min_shell_on_foregrounding')) { + if ( + isMobile && + mode.value === 1 && + gate('disable_min_shell_on_foregrounding_v2') + ) { setMinimalShellMode(false) } } @@ -124,7 +134,7 @@ function HomeScreenReady({ return () => { listener.remove() } - }, [setMinimalShellMode, gate]) + }, [setMinimalShellMode, mode, isMobile, gate]) const onPageSelected = React.useCallback( (index: number) => { From c42a557417960a8eed7eb132c24790f4a94cff37 Mon Sep 17 00:00:00 2001 From: dan Date: Fri, 19 Apr 2024 15:51:41 +0100 Subject: [PATCH 098/167] [Statsig] Send locale info (#3621) --- src/lib/statsig/statsig.tsx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/lib/statsig/statsig.tsx b/src/lib/statsig/statsig.tsx index c43d2bf8a3..2e3fdfd5cb 100644 --- a/src/lib/statsig/statsig.tsx +++ b/src/lib/statsig/statsig.tsx @@ -6,6 +6,7 @@ import {Statsig, StatsigProvider} from 'statsig-react-native-expo' import {logger} from '#/logger' import {isWeb} from '#/platform/detection' +import * as persisted from '#/state/persisted' import {IS_TESTFLIGHT} from 'lib/app-info' import {useSession} from '../../state/session' import {timeout} from '../async/timeout' @@ -23,6 +24,8 @@ type StatsigUser = { platform: 'ios' | 'android' | 'web' refSrc: string refUrl: string + appLanguage: string + contentLanguages: string[] } } @@ -132,6 +135,7 @@ function toStatsigUser(did: string | undefined): StatsigUser { if (did) { userID = sha256(did) } + const languagePrefs = persisted.get('languagePrefs') return { userID, platform: Platform.OS as 'ios' | 'android' | 'web', @@ -139,6 +143,8 @@ function toStatsigUser(did: string | undefined): StatsigUser { refSrc, refUrl, platform: Platform.OS as 'ios' | 'android' | 'web', + appLanguage: languagePrefs.appLanguage, + contentLanguages: languagePrefs.contentLanguages, }, } } From ade2ea6172a71d60bd7ce6aed97d09dbddb352d0 Mon Sep 17 00:00:00 2001 From: dan Date: Fri, 19 Apr 2024 21:58:18 +0100 Subject: [PATCH 099/167] Ungate Top/Latest search (#3627) --- src/lib/statsig/gates.ts | 1 - src/view/screens/Search/Search.tsx | 105 +++++++++++------------------ 2 files changed, 38 insertions(+), 68 deletions(-) diff --git a/src/lib/statsig/gates.ts b/src/lib/statsig/gates.ts index d540cde10b..301746fb21 100644 --- a/src/lib/statsig/gates.ts +++ b/src/lib/statsig/gates.ts @@ -5,7 +5,6 @@ export type Gate = | 'disable_poll_on_discover_v2' | 'hide_vertical_scroll_indicators' | 'new_profile_scroll_component' - | 'new_search' | 'receive_updates' | 'show_follow_back_label_v2' | 'start_session_with_following_v2' diff --git a/src/view/screens/Search/Search.tsx b/src/view/screens/Search/Search.tsx index 0b11ff767b..36e780c77c 100644 --- a/src/view/screens/Search/Search.tsx +++ b/src/view/screens/Search/Search.tsx @@ -407,7 +407,6 @@ export function SearchScreenInner({ const {isDesktop} = useWebMediaQueries() const [activeTab, setActiveTab] = React.useState(0) const {_} = useLingui() - const gate = useGate() const onPageSelected = React.useCallback( (index: number) => { @@ -420,74 +419,46 @@ export function SearchScreenInner({ const sections = React.useMemo(() => { if (!query) return [] - if (gate('new_search')) { - if (hasSession) { - return [ - { - title: _(msg`Top`), - component: ( - - ), - }, - { - title: _(msg`Latest`), - component: ( - - ), - }, - { - title: _(msg`People`), - component: ( - - ), - }, - ] - } else { - return [ - { - title: _(msg`People`), - component: ( - - ), - }, - ] - } + if (hasSession) { + return [ + { + title: _(msg`Top`), + component: ( + + ), + }, + { + title: _(msg`Latest`), + component: ( + + ), + }, + { + title: _(msg`People`), + component: ( + + ), + }, + ] } else { - if (hasSession) { - return [ - { - title: _(msg`Posts`), - component: ( - - ), - }, - { - title: _(msg`Users`), - component: ( - - ), - }, - ] - } else { - return [ - { - title: _(msg`Users`), - component: ( - - ), - }, - ] - } + return [ + { + title: _(msg`People`), + component: ( + + ), + }, + ] } - }, [hasSession, gate, _, query, activeTab]) + }, [hasSession, _, query, activeTab]) if (hasSession) { return query ? ( From 8b33ffdfb5ca606708c8104ecad4fa5430268483 Mon Sep 17 00:00:00 2001 From: Samuel Newman Date: Fri, 19 Apr 2024 22:10:37 +0100 Subject: [PATCH 100/167] Add disable autoplay preference and group related settings into a dedicated page (#3626) * add autoplay preference * group accessibility settings into a dedicated page * fix gray background on web * Put a11y first --------- Co-authored-by: Dan Abramov --- bskyweb/cmd/bskyweb/server.go | 1 + src/Navigation.tsx | 9 ++ src/lib/routes/types.ts | 1 + src/routes.ts | 1 + src/state/persisted/schema.ts | 2 + src/state/preferences/autoplay.tsx | 42 +++++++ src/state/preferences/index.tsx | 11 +- src/view/icons/index.tsx | 75 ++++++------ src/view/screens/AccessibilitySettings.tsx | 132 +++++++++++++++++++++ src/view/screens/Settings/index.tsx | 71 +++++------ 10 files changed, 263 insertions(+), 82 deletions(-) create mode 100644 src/state/preferences/autoplay.tsx create mode 100644 src/view/screens/AccessibilitySettings.tsx diff --git a/bskyweb/cmd/bskyweb/server.go b/bskyweb/cmd/bskyweb/server.go index 17d6210148..cc2ed54264 100644 --- a/bskyweb/cmd/bskyweb/server.go +++ b/bskyweb/cmd/bskyweb/server.go @@ -190,6 +190,7 @@ func serve(cctx *cli.Context) error { e.GET("/settings/saved-feeds", server.WebGeneric) e.GET("/settings/threads", server.WebGeneric) e.GET("/settings/external-embeds", server.WebGeneric) + e.GET("/settings/accessibility", server.WebGeneric) e.GET("/sys/debug", server.WebGeneric) e.GET("/sys/debug-mod", server.WebGeneric) e.GET("/sys/log", server.WebGeneric) diff --git a/src/Navigation.tsx b/src/Navigation.tsx index 99c0ebf3c3..363875dcb6 100644 --- a/src/Navigation.tsx +++ b/src/Navigation.tsx @@ -53,6 +53,7 @@ import { setEmailConfirmationRequested, shouldRequestEmailConfirmation, } from './state/shell/reminders' +import {AccessibilitySettingsScreen} from './view/screens/AccessibilitySettings' import {CommunityGuidelinesScreen} from './view/screens/CommunityGuidelines' import {CopyrightPolicyScreen} from './view/screens/CopyrightPolicy' import {DebugModScreen} from './view/screens/DebugMod' @@ -276,6 +277,14 @@ function commonScreens(Stack: typeof HomeTab, unreadCountLabel?: string) { requireAuth: true, }} /> + AccessibilitySettingsScreen} + options={{ + title: title(msg`Accessibility Settings`), + requireAuth: true, + }} + /> HashtagScreen} diff --git a/src/lib/routes/types.ts b/src/lib/routes/types.ts index 95af2f237d..ac5cb0bce0 100644 --- a/src/lib/routes/types.ts +++ b/src/lib/routes/types.ts @@ -35,6 +35,7 @@ export type CommonNavigatorParams = { PreferencesFollowingFeed: undefined PreferencesThreads: undefined PreferencesExternalEmbeds: undefined + AccessibilitySettings: undefined Search: {q?: string} Hashtag: {tag: string; author?: string} } diff --git a/src/routes.ts b/src/routes.ts index f6f3729475..f27839698f 100644 --- a/src/routes.ts +++ b/src/routes.ts @@ -29,6 +29,7 @@ export const router = new Router({ PreferencesFollowingFeed: '/settings/following-feed', PreferencesThreads: '/settings/threads', PreferencesExternalEmbeds: '/settings/external-embeds', + AccessibilitySettings: '/settings/accessibility', SavedFeeds: '/settings/saved-feeds', Support: '/support', PrivacyPolicy: '/support/privacy', diff --git a/src/state/persisted/schema.ts b/src/state/persisted/schema.ts index 67e082a95d..1b77d138bd 100644 --- a/src/state/persisted/schema.ts +++ b/src/state/persisted/schema.ts @@ -60,6 +60,7 @@ export const schema = z.object({ lastSelectedHomeFeed: z.string().optional(), pdsAddressHistory: z.array(z.string()).optional(), disableHaptics: z.boolean().optional(), + disableAutoplay: z.boolean().optional(), }) export type Schema = z.infer @@ -96,4 +97,5 @@ export const defaults: Schema = { lastSelectedHomeFeed: undefined, pdsAddressHistory: [], disableHaptics: false, + disableAutoplay: false, } diff --git a/src/state/preferences/autoplay.tsx b/src/state/preferences/autoplay.tsx new file mode 100644 index 0000000000..d5aa049f36 --- /dev/null +++ b/src/state/preferences/autoplay.tsx @@ -0,0 +1,42 @@ +import React from 'react' + +import * as persisted from '#/state/persisted' + +type StateContext = boolean +type SetContext = (v: boolean) => void + +const stateContext = React.createContext( + Boolean(persisted.defaults.disableAutoplay), +) +const setContext = React.createContext((_: boolean) => {}) + +export function Provider({children}: {children: React.ReactNode}) { + const [state, setState] = React.useState( + Boolean(persisted.get('disableAutoplay')), + ) + + const setStateWrapped = React.useCallback( + (autoplayDisabled: persisted.Schema['disableAutoplay']) => { + setState(Boolean(autoplayDisabled)) + persisted.write('disableAutoplay', autoplayDisabled) + }, + [setState], + ) + + React.useEffect(() => { + return persisted.onUpdate(() => { + setState(Boolean(persisted.get('disableAutoplay'))) + }) + }, [setStateWrapped]) + + return ( + + + {children} + + + ) +} + +export const useAutoplayDisabled = () => React.useContext(stateContext) +export const useSetAutoplayDisabled = () => React.useContext(setContext) diff --git a/src/state/preferences/index.tsx b/src/state/preferences/index.tsx index 804d0fc310..5c8fab2ad7 100644 --- a/src/state/preferences/index.tsx +++ b/src/state/preferences/index.tsx @@ -1,9 +1,10 @@ import React from 'react' -import {Provider as AltTextRequiredProvider} from '../preferences/alt-text-required' -import {Provider as HiddenPostsProvider} from '../preferences/hidden-posts' +import {Provider as AltTextRequiredProvider} from './alt-text-required' +import {Provider as AutoplayProvider} from './autoplay' import {Provider as DisableHapticsProvider} from './disable-haptics' import {Provider as ExternalEmbedsProvider} from './external-embeds-prefs' +import {Provider as HiddenPostsProvider} from './hidden-posts' import {Provider as InAppBrowserProvider} from './in-app-browser' import {Provider as LanguagesProvider} from './languages' @@ -11,6 +12,8 @@ export { useRequireAltTextEnabled, useSetRequireAltTextEnabled, } from './alt-text-required' +export {useAutoplayDisabled, useSetAutoplayDisabled} from './autoplay' +export {useHapticsDisabled, useSetHapticsDisabled} from './disable-haptics' export { useExternalEmbedsPrefs, useSetExternalEmbedPref, @@ -26,7 +29,9 @@ export function Provider({children}: React.PropsWithChildren<{}>) { - {children} + + {children} + diff --git a/src/view/icons/index.tsx b/src/view/icons/index.tsx index ede1e63355..b9af6a519c 100644 --- a/src/view/icons/index.tsx +++ b/src/view/icons/index.tsx @@ -1,63 +1,72 @@ import {library} from '@fortawesome/fontawesome-svg-core' - import {faAddressCard} from '@fortawesome/free-regular-svg-icons' +import {faBell as farBell} from '@fortawesome/free-regular-svg-icons/faBell' +import {faBookmark as farBookmark} from '@fortawesome/free-regular-svg-icons/faBookmark' +import {faCalendar as farCalendar} from '@fortawesome/free-regular-svg-icons/faCalendar' +import {faCircle} from '@fortawesome/free-regular-svg-icons/faCircle' +import {faCircleCheck as farCircleCheck} from '@fortawesome/free-regular-svg-icons/faCircleCheck' +import {faCirclePlay} from '@fortawesome/free-regular-svg-icons/faCirclePlay' +import {faCircleUser} from '@fortawesome/free-regular-svg-icons/faCircleUser' +import {faClone as farClone} from '@fortawesome/free-regular-svg-icons/faClone' +import {faComment} from '@fortawesome/free-regular-svg-icons/faComment' +import {faComments} from '@fortawesome/free-regular-svg-icons/faComments' +import {faCompass} from '@fortawesome/free-regular-svg-icons/faCompass' +import {faEyeSlash as farEyeSlash} from '@fortawesome/free-regular-svg-icons/faEyeSlash' +import {faFaceSmile} from '@fortawesome/free-regular-svg-icons/faFaceSmile' +import {faFloppyDisk} from '@fortawesome/free-regular-svg-icons/faFloppyDisk' +import {faHand as farHand} from '@fortawesome/free-regular-svg-icons/faHand' +import {faHeart} from '@fortawesome/free-regular-svg-icons/faHeart' +import {faImage as farImage} from '@fortawesome/free-regular-svg-icons/faImage' +import {faMessage} from '@fortawesome/free-regular-svg-icons/faMessage' +import {faPaste} from '@fortawesome/free-regular-svg-icons/faPaste' +import {faSquare} from '@fortawesome/free-regular-svg-icons/faSquare' +import {faSquareCheck} from '@fortawesome/free-regular-svg-icons/faSquareCheck' +import {faSquarePlus} from '@fortawesome/free-regular-svg-icons/faSquarePlus' +import {faTrashCan} from '@fortawesome/free-regular-svg-icons/faTrashCan' +import {faUser} from '@fortawesome/free-regular-svg-icons/faUser' +import {faFlask} from '@fortawesome/free-solid-svg-icons' +import {faUniversalAccess} from '@fortawesome/free-solid-svg-icons' import {faAngleDown} from '@fortawesome/free-solid-svg-icons/faAngleDown' import {faAngleLeft} from '@fortawesome/free-solid-svg-icons/faAngleLeft' import {faAngleRight} from '@fortawesome/free-solid-svg-icons/faAngleRight' import {faAngleUp} from '@fortawesome/free-solid-svg-icons/faAngleUp' +import {faArrowDown} from '@fortawesome/free-solid-svg-icons/faArrowDown' import {faArrowLeft} from '@fortawesome/free-solid-svg-icons/faArrowLeft' import {faArrowRight} from '@fortawesome/free-solid-svg-icons/faArrowRight' -import {faArrowUp} from '@fortawesome/free-solid-svg-icons/faArrowUp' -import {faArrowDown} from '@fortawesome/free-solid-svg-icons/faArrowDown' import {faArrowRightFromBracket} from '@fortawesome/free-solid-svg-icons/faArrowRightFromBracket' +import {faArrowRotateLeft} from '@fortawesome/free-solid-svg-icons/faArrowRotateLeft' +import {faArrowsRotate} from '@fortawesome/free-solid-svg-icons/faArrowsRotate' +import {faArrowTrendUp} from '@fortawesome/free-solid-svg-icons/faArrowTrendUp' +import {faArrowUp} from '@fortawesome/free-solid-svg-icons/faArrowUp' import {faArrowUpFromBracket} from '@fortawesome/free-solid-svg-icons/faArrowUpFromBracket' import {faArrowUpRightFromSquare} from '@fortawesome/free-solid-svg-icons/faArrowUpRightFromSquare' -import {faArrowRotateLeft} from '@fortawesome/free-solid-svg-icons/faArrowRotateLeft' -import {faArrowTrendUp} from '@fortawesome/free-solid-svg-icons/faArrowTrendUp' -import {faArrowsRotate} from '@fortawesome/free-solid-svg-icons/faArrowsRotate' import {faAt} from '@fortawesome/free-solid-svg-icons/faAt' -import {faBars} from '@fortawesome/free-solid-svg-icons/faBars' import {faBan} from '@fortawesome/free-solid-svg-icons/faBan' +import {faBars} from '@fortawesome/free-solid-svg-icons/faBars' import {faBell} from '@fortawesome/free-solid-svg-icons/faBell' -import {faBell as farBell} from '@fortawesome/free-regular-svg-icons/faBell' import {faBookmark} from '@fortawesome/free-solid-svg-icons/faBookmark' -import {faBookmark as farBookmark} from '@fortawesome/free-regular-svg-icons/faBookmark' -import {faCalendar as farCalendar} from '@fortawesome/free-regular-svg-icons/faCalendar' import {faCamera} from '@fortawesome/free-solid-svg-icons/faCamera' import {faCheck} from '@fortawesome/free-solid-svg-icons/faCheck' +import {faChevronDown} from '@fortawesome/free-solid-svg-icons/faChevronDown' import {faChevronRight} from '@fortawesome/free-solid-svg-icons/faChevronRight' -import {faCircle} from '@fortawesome/free-regular-svg-icons/faCircle' -import {faCircleCheck as farCircleCheck} from '@fortawesome/free-regular-svg-icons/faCircleCheck' import {faCircleCheck} from '@fortawesome/free-solid-svg-icons/faCircleCheck' import {faCircleDot} from '@fortawesome/free-solid-svg-icons/faCircleDot' import {faCircleExclamation} from '@fortawesome/free-solid-svg-icons/faCircleExclamation' -import {faCirclePlay} from '@fortawesome/free-regular-svg-icons/faCirclePlay' -import {faCircleUser} from '@fortawesome/free-regular-svg-icons/faCircleUser' import {faClone} from '@fortawesome/free-solid-svg-icons/faClone' -import {faClone as farClone} from '@fortawesome/free-regular-svg-icons/faClone' -import {faComment} from '@fortawesome/free-regular-svg-icons/faComment' import {faCommentSlash} from '@fortawesome/free-solid-svg-icons/faCommentSlash' -import {faComments} from '@fortawesome/free-regular-svg-icons/faComments' -import {faCompass} from '@fortawesome/free-regular-svg-icons/faCompass' import {faDownload} from '@fortawesome/free-solid-svg-icons/faDownload' import {faEllipsis} from '@fortawesome/free-solid-svg-icons/faEllipsis' import {faEnvelope} from '@fortawesome/free-solid-svg-icons/faEnvelope' import {faExclamation} from '@fortawesome/free-solid-svg-icons/faExclamation' import {faEye} from '@fortawesome/free-solid-svg-icons/faEye' -import {faEyeSlash as farEyeSlash} from '@fortawesome/free-regular-svg-icons/faEyeSlash' -import {faFaceSmile} from '@fortawesome/free-regular-svg-icons/faFaceSmile' +import {faFilter} from '@fortawesome/free-solid-svg-icons/faFilter' import {faFire} from '@fortawesome/free-solid-svg-icons/faFire' -import {faFlask} from '@fortawesome/free-solid-svg-icons' -import {faFloppyDisk} from '@fortawesome/free-regular-svg-icons/faFloppyDisk' import {faGear} from '@fortawesome/free-solid-svg-icons/faGear' import {faGlobe} from '@fortawesome/free-solid-svg-icons/faGlobe' import {faHand} from '@fortawesome/free-solid-svg-icons/faHand' -import {faHand as farHand} from '@fortawesome/free-regular-svg-icons/faHand' import {faHashtag} from '@fortawesome/free-solid-svg-icons/faHashtag' -import {faHeart} from '@fortawesome/free-regular-svg-icons/faHeart' import {faHeart as fasHeart} from '@fortawesome/free-solid-svg-icons/faHeart' import {faHouse} from '@fortawesome/free-solid-svg-icons/faHouse' -import {faImage as farImage} from '@fortawesome/free-regular-svg-icons/faImage' import {faImage} from '@fortawesome/free-solid-svg-icons/faImage' import {faInfo} from '@fortawesome/free-solid-svg-icons/faInfo' import {faLanguage} from '@fortawesome/free-solid-svg-icons/faLanguage' @@ -66,10 +75,8 @@ import {faList} from '@fortawesome/free-solid-svg-icons/faList' import {faListUl} from '@fortawesome/free-solid-svg-icons/faListUl' import {faLock} from '@fortawesome/free-solid-svg-icons/faLock' import {faMagnifyingGlass} from '@fortawesome/free-solid-svg-icons/faMagnifyingGlass' -import {faMessage} from '@fortawesome/free-regular-svg-icons/faMessage' import {faNoteSticky} from '@fortawesome/free-solid-svg-icons/faNoteSticky' import {faPause} from '@fortawesome/free-solid-svg-icons/faPause' -import {faPaste} from '@fortawesome/free-regular-svg-icons/faPaste' import {faPen} from '@fortawesome/free-solid-svg-icons/faPen' import {faPenNib} from '@fortawesome/free-solid-svg-icons/faPenNib' import {faPenToSquare} from '@fortawesome/free-solid-svg-icons/faPenToSquare' @@ -87,23 +94,16 @@ import {faShareFromSquare} from '@fortawesome/free-solid-svg-icons/faShareFromSq import {faShield} from '@fortawesome/free-solid-svg-icons/faShield' import {faSignal} from '@fortawesome/free-solid-svg-icons/faSignal' import {faSliders} from '@fortawesome/free-solid-svg-icons/faSliders' -import {faSquare} from '@fortawesome/free-regular-svg-icons/faSquare' -import {faSquareCheck} from '@fortawesome/free-regular-svg-icons/faSquareCheck' -import {faSquarePlus} from '@fortawesome/free-regular-svg-icons/faSquarePlus' import {faThumbtack} from '@fortawesome/free-solid-svg-icons/faThumbtack' import {faTicket} from '@fortawesome/free-solid-svg-icons/faTicket' -import {faTrashCan} from '@fortawesome/free-regular-svg-icons/faTrashCan' -import {faUser} from '@fortawesome/free-regular-svg-icons/faUser' -import {faUsers} from '@fortawesome/free-solid-svg-icons/faUsers' import {faUserCheck} from '@fortawesome/free-solid-svg-icons/faUserCheck' -import {faUserSlash} from '@fortawesome/free-solid-svg-icons/faUserSlash' import {faUserPlus} from '@fortawesome/free-solid-svg-icons/faUserPlus' -import {faUserXmark} from '@fortawesome/free-solid-svg-icons/faUserXmark' +import {faUsers} from '@fortawesome/free-solid-svg-icons/faUsers' +import {faUserSlash} from '@fortawesome/free-solid-svg-icons/faUserSlash' import {faUsersSlash} from '@fortawesome/free-solid-svg-icons/faUsersSlash' +import {faUserXmark} from '@fortawesome/free-solid-svg-icons/faUserXmark' import {faX} from '@fortawesome/free-solid-svg-icons/faX' import {faXmark} from '@fortawesome/free-solid-svg-icons/faXmark' -import {faChevronDown} from '@fortawesome/free-solid-svg-icons/faChevronDown' -import {faFilter} from '@fortawesome/free-solid-svg-icons/faFilter' library.add( faAddressCard, @@ -196,6 +196,7 @@ library.add( faSquare, faSquareCheck, faSquarePlus, + faUniversalAccess, faUser, faUsers, faUserCheck, diff --git a/src/view/screens/AccessibilitySettings.tsx b/src/view/screens/AccessibilitySettings.tsx new file mode 100644 index 0000000000..ac0d985f10 --- /dev/null +++ b/src/view/screens/AccessibilitySettings.tsx @@ -0,0 +1,132 @@ +import React from 'react' +import {StyleSheet, View} from 'react-native' +import {msg, Trans} from '@lingui/macro' +import {useLingui} from '@lingui/react' +import {useFocusEffect} from '@react-navigation/native' + +import {isNative} from '#/platform/detection' +import {useSetMinimalShellMode} from '#/state/shell' +import {useAnalytics} from 'lib/analytics/analytics' +import {usePalette} from 'lib/hooks/usePalette' +import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' +import {CommonNavigatorParams, NativeStackScreenProps} from 'lib/routes/types' +import {s} from 'lib/styles' +import { + useAutoplayDisabled, + useHapticsDisabled, + useRequireAltTextEnabled, + useSetAutoplayDisabled, + useSetHapticsDisabled, + useSetRequireAltTextEnabled, +} from 'state/preferences' +import {ToggleButton} from 'view/com/util/forms/ToggleButton' +import {SimpleViewHeader} from '../com/util/SimpleViewHeader' +import {Text} from '../com/util/text/Text' +import {ScrollView} from '../com/util/Views' + +type Props = NativeStackScreenProps< + CommonNavigatorParams, + 'AccessibilitySettings' +> +export function AccessibilitySettingsScreen({}: Props) { + const pal = usePalette('default') + const setMinimalShellMode = useSetMinimalShellMode() + const {screen} = useAnalytics() + const {isMobile} = useWebMediaQueries() + const {_} = useLingui() + + const requireAltTextEnabled = useRequireAltTextEnabled() + const setRequireAltTextEnabled = useSetRequireAltTextEnabled() + const autoplayDisabled = useAutoplayDisabled() + const setAutoplayDisabled = useSetAutoplayDisabled() + const hapticsDisabled = useHapticsDisabled() + const setHapticsDisabled = useSetHapticsDisabled() + + useFocusEffect( + React.useCallback(() => { + screen('PreferencesExternalEmbeds') + setMinimalShellMode(false) + }, [screen, setMinimalShellMode]), + ) + + return ( + + + + + Accessibility Settings + + + + + + Alt text + + + setRequireAltTextEnabled(!requireAltTextEnabled)} + /> + + + Media + + + setAutoplayDisabled(!autoplayDisabled)} + /> + + {isNative && ( + <> + + Haptics + + + setHapticsDisabled(!hapticsDisabled)} + /> + + + )} + + + ) +} + +const styles = StyleSheet.create({ + heading: { + paddingHorizontal: 18, + paddingTop: 14, + paddingBottom: 6, + }, + toggleCard: { + paddingVertical: 8, + paddingHorizontal: 6, + marginBottom: 1, + }, +}) diff --git a/src/view/screens/Settings/index.tsx b/src/view/screens/Settings/index.tsx index b97faafad1..bb38da676c 100644 --- a/src/view/screens/Settings/index.tsx +++ b/src/view/screens/Settings/index.tsx @@ -20,14 +20,10 @@ import {useLingui} from '@lingui/react' import {useFocusEffect, useNavigation} from '@react-navigation/native' import {useQueryClient} from '@tanstack/react-query' -import {isIOS, isNative} from '#/platform/detection' +import {isNative} from '#/platform/detection' import {useModalControls} from '#/state/modals' import {clearLegacyStorage} from '#/state/persisted/legacy' import {clear as clearStorage} from '#/state/persisted/store' -import { - useRequireAltTextEnabled, - useSetRequireAltTextEnabled, -} from '#/state/preferences' import { useInAppBrowser, useSetInAppBrowser, @@ -56,10 +52,6 @@ import {makeProfileLink} from 'lib/routes/links' import {CommonNavigatorParams, NativeStackScreenProps} from 'lib/routes/types' import {NavigationProp} from 'lib/routes/types' import {colors, s} from 'lib/styles' -import { - useHapticsDisabled, - useSetHapticsDisabled, -} from 'state/preferences/disable-haptics' import {AccountDropdownBtn} from 'view/com/util/AccountDropdownBtn' import {SelectableBtn} from 'view/com/util/forms/SelectableBtn' import {ToggleButton} from 'view/com/util/forms/ToggleButton' @@ -162,12 +154,8 @@ export function SettingsScreen({}: Props) { const pal = usePalette('default') const {_} = useLingui() const setMinimalShellMode = useSetMinimalShellMode() - const requireAltTextEnabled = useRequireAltTextEnabled() - const setRequireAltTextEnabled = useSetRequireAltTextEnabled() const inAppBrowserPref = useInAppBrowser() const setUseInAppBrowser = useSetInAppBrowser() - const isHapticsDisabled = useHapticsDisabled() - const setHapticsDisabled = useSetHapticsDisabled() const onboardingDispatch = useOnboardingDispatch() const navigation = useNavigation() const {isMobile} = useWebMediaQueries() @@ -282,6 +270,10 @@ export function SettingsScreen({}: Props) { navigation.navigate('SavedFeeds') }, [navigation]) + const onPressAccessibilitySettings = React.useCallback(() => { + navigation.navigate('AccessibilitySettings') + }, [navigation]) + const onPressStatusPage = React.useCallback(() => { Linking.openURL(STATUS_PAGE_URL) }, []) @@ -318,7 +310,7 @@ export function SettingsScreen({}: Props) { - - Accessibility - - - setRequireAltTextEnabled(!requireAltTextEnabled)} - /> - - - - Appearance @@ -492,6 +469,29 @@ export function SettingsScreen({}: Props) { Basics + + + + + + Accessibility + + )} - {isNative && ( - - setHapticsDisabled(!isHapticsDisabled)} - /> - - )} Account From edbb18afa44bcfee4560ac19880a0c32563b6a0e Mon Sep 17 00:00:00 2001 From: Samuel Newman Date: Fri, 19 Apr 2024 22:55:53 +0100 Subject: [PATCH 101/167] Throttle gif search by 500ms (#3622) * debounce gif search by 300ms * Throttle it instead --------- Co-authored-by: Dan Abramov --- src/components/dialogs/GifSelect.tsx | 11 +++------ src/components/hooks/useThrottledValue.ts | 27 +++++++++++++++++++++++ 2 files changed, 30 insertions(+), 8 deletions(-) create mode 100644 src/components/hooks/useThrottledValue.ts diff --git a/src/components/dialogs/GifSelect.tsx b/src/components/dialogs/GifSelect.tsx index 92e21af47d..c8897da364 100644 --- a/src/components/dialogs/GifSelect.tsx +++ b/src/components/dialogs/GifSelect.tsx @@ -1,10 +1,4 @@ -import React, { - useCallback, - useDeferredValue, - useMemo, - useRef, - useState, -} from 'react' +import React, {useCallback, useMemo, useRef, useState} from 'react' import {TextInput, View} from 'react-native' import {Image} from 'expo-image' import {msg, Trans} from '@lingui/macro' @@ -22,6 +16,7 @@ import {Gif, useGifphySearch, useGiphyTrending} from '#/state/queries/giphy' import {atoms as a, useBreakpoints, useTheme} from '#/alf' import * as Dialog from '#/components/Dialog' import * as TextField from '#/components/forms/TextField' +import {useThrottledValue} from '#/components/hooks/useThrottledValue' import {ArrowLeft_Stroke2_Corner0_Rounded as Arrow} from '#/components/icons/Arrow' import {MagnifyingGlass2_Stroke2_Corner0_Rounded as Search} from '#/components/icons/MagnifyingGlass2' import {InlineLinkText} from '#/components/Link' @@ -82,7 +77,7 @@ function GifList({ const {gtMobile} = useBreakpoints() const ref = useRef(null) const [undeferredSearch, setSearch] = useState('') - const search = useDeferredValue(undeferredSearch) + const search = useThrottledValue(undeferredSearch, 500) const isSearching = search.length > 0 diff --git a/src/components/hooks/useThrottledValue.ts b/src/components/hooks/useThrottledValue.ts new file mode 100644 index 0000000000..5764c547e4 --- /dev/null +++ b/src/components/hooks/useThrottledValue.ts @@ -0,0 +1,27 @@ +import {useEffect, useRef, useState} from 'react' + +import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback' + +export function useThrottledValue(value: T, time?: number) { + const pendingValueRef = useRef(value) + const [throttledValue, setThrottledValue] = useState(value) + + useEffect(() => { + pendingValueRef.current = value + }, [value]) + + const handleTick = useNonReactiveCallback(() => { + if (pendingValueRef.current !== throttledValue) { + setThrottledValue(pendingValueRef.current) + } + }) + + useEffect(() => { + const id = setInterval(handleTick, time) + return () => { + clearInterval(id) + } + }, [handleTick, time]) + + return throttledValue +} From 22e86c99033a55881bf02a3129b653a13bac619e Mon Sep 17 00:00:00 2001 From: Samuel Newman Date: Fri, 19 Apr 2024 23:26:04 +0100 Subject: [PATCH 102/167] fix onEndReached issue by forcing flatlist to scroll (#3623) --- src/components/Dialog/index.web.tsx | 16 ++++++++++++++-- src/components/dialogs/GifSelect.tsx | 6 +++++- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/components/Dialog/index.web.tsx b/src/components/Dialog/index.web.tsx index d00d2d832b..a086955db6 100644 --- a/src/components/Dialog/index.web.tsx +++ b/src/components/Dialog/index.web.tsx @@ -199,11 +199,23 @@ export const ScrollableInner = Inner export function InnerFlatList({ label, + style, ...props }: FlatListProps & {label: string}) { + const {gtMobile} = useBreakpoints() return ( - - + + ) } diff --git a/src/components/dialogs/GifSelect.tsx b/src/components/dialogs/GifSelect.tsx index c8897da364..baff31168b 100644 --- a/src/components/dialogs/GifSelect.tsx +++ b/src/components/dialogs/GifSelect.tsx @@ -147,7 +147,11 @@ function GifList({ From c0ca891501cbc60eb945e3235800ec1e29a15ccd Mon Sep 17 00:00:00 2001 From: Hailey Date: Fri, 19 Apr 2024 15:31:20 -0700 Subject: [PATCH 103/167] Player improvement pre-reqs (#3618) * add dims to type * save * add the dimensions to the embed info * add a new case * add a new case * limit this case to giphy * use gate * flip mp4/webp * fix tests * add new test --- __tests__/lib/string.test.ts | 56 ++++++++++++------- src/lib/statsig/gates.ts | 1 + src/lib/strings/embed-player.ts | 28 ++++++++-- .../util/post-embeds/ExternalLinkEmbed.tsx | 47 ++++++++++------ 4 files changed, 89 insertions(+), 43 deletions(-) diff --git a/__tests__/lib/string.test.ts b/__tests__/lib/string.test.ts index f003e5acc0..eeb5ae1572 100644 --- a/__tests__/lib/string.test.ts +++ b/__tests__/lib/string.test.ts @@ -1,17 +1,18 @@ import {RichText} from '@atproto/api' + +import {parseEmbedPlayerFromUrl} from 'lib/strings/embed-player' +import {cleanError} from '../../src/lib/strings/errors' +import {createFullHandle, makeValidHandle} from '../../src/lib/strings/handles' +import {enforceLen, pluralize} from '../../src/lib/strings/helpers' +import {detectLinkables} from '../../src/lib/strings/rich-text-detection' +import {shortenLinks} from '../../src/lib/strings/rich-text-manip' +import {ago} from '../../src/lib/strings/time' import { makeRecordUri, toNiceDomain, - toShortUrl, toShareUrl, + toShortUrl, } from '../../src/lib/strings/url-helpers' -import {pluralize, enforceLen} from '../../src/lib/strings/helpers' -import {ago} from '../../src/lib/strings/time' -import {detectLinkables} from '../../src/lib/strings/rich-text-detection' -import {shortenLinks} from '../../src/lib/strings/rich-text-manip' -import {makeValidHandle, createFullHandle} from '../../src/lib/strings/handles' -import {cleanError} from '../../src/lib/strings/errors' -import {parseEmbedPlayerFromUrl} from 'lib/strings/embed-player' describe('detectLinkables', () => { const inputs = [ @@ -434,6 +435,8 @@ describe('parseEmbedPlayerFromUrl', () => { 'https://giphy.com/gif/some-random-gif-name-gifId', 'https://giphy.com/gifs/', + 'https://giphy.com/gifs/39248209509382934029?hh=100&ww=100', + 'https://media.giphy.com/media/gifId/giphy.webp', 'https://media0.giphy.com/media/gifId/giphy.webp', 'https://media1.giphy.com/media/gifId/giphy.gif', @@ -621,18 +624,31 @@ describe('parseEmbedPlayerFromUrl', () => { isGif: true, hideDetails: true, metaUri: 'https://giphy.com/gifs/gifId', - playerUri: 'https://i.giphy.com/media/gifId/giphy.webp', + playerUri: 'https://i.giphy.com/media/gifId/200.webp', }, undefined, undefined, + { + type: 'giphy_gif', + source: 'giphy', + isGif: true, + hideDetails: true, + metaUri: 'https://giphy.com/gifs/39248209509382934029', + playerUri: 'https://i.giphy.com/media/39248209509382934029/200.mp4', + dimensions: { + width: 100, + height: 100, + }, + }, + { type: 'giphy_gif', source: 'giphy', isGif: true, hideDetails: true, metaUri: 'https://giphy.com/gifs/gifId', - playerUri: 'https://i.giphy.com/media/gifId/giphy.webp', + playerUri: 'https://i.giphy.com/media/gifId/200.webp', }, { type: 'giphy_gif', @@ -640,7 +656,7 @@ describe('parseEmbedPlayerFromUrl', () => { isGif: true, hideDetails: true, metaUri: 'https://giphy.com/gifs/gifId', - playerUri: 'https://i.giphy.com/media/gifId/giphy.webp', + playerUri: 'https://i.giphy.com/media/gifId/200.webp', }, { type: 'giphy_gif', @@ -648,7 +664,7 @@ describe('parseEmbedPlayerFromUrl', () => { isGif: true, hideDetails: true, metaUri: 'https://giphy.com/gifs/gifId', - playerUri: 'https://i.giphy.com/media/gifId/giphy.webp', + playerUri: 'https://i.giphy.com/media/gifId/200.webp', }, { type: 'giphy_gif', @@ -656,7 +672,7 @@ describe('parseEmbedPlayerFromUrl', () => { isGif: true, hideDetails: true, metaUri: 'https://giphy.com/gifs/gifId', - playerUri: 'https://i.giphy.com/media/gifId/giphy.webp', + playerUri: 'https://i.giphy.com/media/gifId/200.webp', }, { type: 'giphy_gif', @@ -664,7 +680,7 @@ describe('parseEmbedPlayerFromUrl', () => { isGif: true, hideDetails: true, metaUri: 'https://giphy.com/gifs/gifId', - playerUri: 'https://i.giphy.com/media/gifId/giphy.webp', + playerUri: 'https://i.giphy.com/media/gifId/200.webp', }, { type: 'giphy_gif', @@ -672,7 +688,7 @@ describe('parseEmbedPlayerFromUrl', () => { isGif: true, hideDetails: true, metaUri: 'https://giphy.com/gifs/gifId', - playerUri: 'https://i.giphy.com/media/gifId/giphy.webp', + playerUri: 'https://i.giphy.com/media/gifId/200.webp', }, undefined, undefined, @@ -684,7 +700,7 @@ describe('parseEmbedPlayerFromUrl', () => { isGif: true, hideDetails: true, metaUri: 'https://giphy.com/gifs/gifId', - playerUri: 'https://i.giphy.com/media/gifId/giphy.webp', + playerUri: 'https://i.giphy.com/media/gifId/200.webp', }, { @@ -693,7 +709,7 @@ describe('parseEmbedPlayerFromUrl', () => { isGif: true, hideDetails: true, metaUri: 'https://giphy.com/gifs/gifId', - playerUri: 'https://i.giphy.com/media/gifId/giphy.webp', + playerUri: 'https://i.giphy.com/media/gifId/200.webp', }, { type: 'giphy_gif', @@ -701,7 +717,7 @@ describe('parseEmbedPlayerFromUrl', () => { isGif: true, hideDetails: true, metaUri: 'https://giphy.com/gifs/gifId', - playerUri: 'https://i.giphy.com/media/gifId/giphy.webp', + playerUri: 'https://i.giphy.com/media/gifId/200.webp', }, { type: 'giphy_gif', @@ -709,7 +725,7 @@ describe('parseEmbedPlayerFromUrl', () => { isGif: true, hideDetails: true, metaUri: 'https://giphy.com/gifs/gifId', - playerUri: 'https://i.giphy.com/media/gifId/giphy.webp', + playerUri: 'https://i.giphy.com/media/gifId/200.webp', }, { type: 'giphy_gif', @@ -717,7 +733,7 @@ describe('parseEmbedPlayerFromUrl', () => { isGif: true, hideDetails: true, metaUri: 'https://giphy.com/gifs/gifId', - playerUri: 'https://i.giphy.com/media/gifId/giphy.webp', + playerUri: 'https://i.giphy.com/media/gifId/200.webp', }, { diff --git a/src/lib/statsig/gates.ts b/src/lib/statsig/gates.ts index 301746fb21..e42e9efe1e 100644 --- a/src/lib/statsig/gates.ts +++ b/src/lib/statsig/gates.ts @@ -4,6 +4,7 @@ export type Gate = | 'disable_min_shell_on_foregrounding_v2' | 'disable_poll_on_discover_v2' | 'hide_vertical_scroll_indicators' + | 'new_gif_player' | 'new_profile_scroll_component' | 'receive_updates' | 'show_follow_back_label_v2' diff --git a/src/lib/strings/embed-player.ts b/src/lib/strings/embed-player.ts index ee73284785..bbc58a2063 100644 --- a/src/lib/strings/embed-player.ts +++ b/src/lib/strings/embed-player.ts @@ -1,4 +1,5 @@ import {Dimensions} from 'react-native' + import {isWeb} from 'platform/detection' const {height: SCREEN_HEIGHT} = Dimensions.get('window') @@ -60,6 +61,10 @@ export interface EmbedPlayerParams { source: EmbedPlayerSource metaUri?: string hideDetails?: boolean + dimensions?: { + height: number + width: number + } } const giphyRegex = /media(?:[0-4]\.giphy\.com|\.giphy\.com)/i @@ -250,6 +255,16 @@ export function parseEmbedPlayerFromUrl( if (urlp.hostname === 'giphy.com' || urlp.hostname === 'www.giphy.com') { const [_, gifs, nameAndId] = urlp.pathname.split('/') + const h = urlp.searchParams.get('hh') + const w = urlp.searchParams.get('ww') + let dimensions + if (h && w) { + dimensions = { + height: Number(h), + width: Number(w), + } + } + /* * nameAndId is a string that consists of the name (dash separated) and the id of the gif (the last part of the name) * We want to get the id of the gif, then direct to media.giphy.com/media/{id}/giphy.webp so we can @@ -266,7 +281,10 @@ export function parseEmbedPlayerFromUrl( isGif: true, hideDetails: true, metaUri: `https://giphy.com/gifs/${gifId}`, - playerUri: `https://i.giphy.com/media/${gifId}/giphy.webp`, + playerUri: `https://i.giphy.com/media/${gifId}/${ + dimensions ? '200.mp4' : '200.webp' + }`, + dimensions, } } } @@ -287,7 +305,7 @@ export function parseEmbedPlayerFromUrl( isGif: true, hideDetails: true, metaUri: `https://giphy.com/gifs/${trackingOrId}`, - playerUri: `https://i.giphy.com/media/${trackingOrId}/giphy.webp`, + playerUri: `https://i.giphy.com/media/${trackingOrId}/200.webp`, } } else if (filename && gifFilenameRegex.test(filename)) { return { @@ -296,7 +314,7 @@ export function parseEmbedPlayerFromUrl( isGif: true, hideDetails: true, metaUri: `https://giphy.com/gifs/${idOrFilename}`, - playerUri: `https://i.giphy.com/media/${idOrFilename}/giphy.webp`, + playerUri: `https://i.giphy.com/media/${idOrFilename}/200.webp`, } } } @@ -315,7 +333,7 @@ export function parseEmbedPlayerFromUrl( isGif: true, hideDetails: true, metaUri: `https://giphy.com/gifs/${gifId}`, - playerUri: `https://i.giphy.com/media/${gifId}/giphy.webp`, + playerUri: `https://i.giphy.com/media/${gifId}/200.webp`, } } else if (mediaOrFilename) { const gifId = mediaOrFilename.split('.')[0] @@ -327,7 +345,7 @@ export function parseEmbedPlayerFromUrl( metaUri: `https://giphy.com/gifs/${gifId}`, playerUri: `https://i.giphy.com/media/${ mediaOrFilename.split('.')[0] - }/giphy.webp`, + }/200.webp`, } } } diff --git a/src/view/com/util/post-embeds/ExternalLinkEmbed.tsx b/src/view/com/util/post-embeds/ExternalLinkEmbed.tsx index aaa98a41f6..ff7c643f62 100644 --- a/src/view/com/util/post-embeds/ExternalLinkEmbed.tsx +++ b/src/view/com/util/post-embeds/ExternalLinkEmbed.tsx @@ -1,15 +1,17 @@ import React from 'react' -import {Image} from 'expo-image' -import {Text} from '../text/Text' import {StyleSheet, View} from 'react-native' +import {Image} from 'expo-image' +import {AppBskyEmbedExternal} from '@atproto/api' + import {usePalette} from 'lib/hooks/usePalette' import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' -import {AppBskyEmbedExternal} from '@atproto/api' -import {toNiceDomain} from 'lib/strings/url-helpers' +import {useGate} from 'lib/statsig/statsig' import {parseEmbedPlayerFromUrl} from 'lib/strings/embed-player' -import {ExternalPlayer} from 'view/com/util/post-embeds/ExternalPlayerEmbed' -import {ExternalGifEmbed} from 'view/com/util/post-embeds/ExternalGifEmbed' +import {toNiceDomain} from 'lib/strings/url-helpers' import {useExternalEmbedsPrefs} from 'state/preferences' +import {ExternalGifEmbed} from 'view/com/util/post-embeds/ExternalGifEmbed' +import {ExternalPlayer} from 'view/com/util/post-embeds/ExternalPlayerEmbed' +import {Text} from '../text/Text' export const ExternalLinkEmbed = ({ link, @@ -19,6 +21,7 @@ export const ExternalLinkEmbed = ({ const pal = usePalette('default') const {isMobile} = useWebMediaQueries() const externalEmbedPrefs = useExternalEmbedsPrefs() + const gate = useGate() const embedPlayerParams = React.useMemo(() => { const params = parseEmbedPlayerFromUrl(link.uri) @@ -27,6 +30,10 @@ export const ExternalLinkEmbed = ({ return params } }, [link.uri, externalEmbedPrefs]) + const isCompatibleGiphy = + embedPlayerParams?.source === 'giphy' && + embedPlayerParams.dimensions && + gate('new_gif_player') return ( @@ -37,20 +44,24 @@ export const ExternalLinkEmbed = ({ accessibilityIgnoresInvertColors /> ) : undefined} - {(embedPlayerParams?.isGif && ( + {isCompatibleGiphy ? ( + + ) : embedPlayerParams?.isGif ? ( - )) || - (embedPlayerParams && ( - - ))} + ) : embedPlayerParams ? ( + + ) : undefined} - - {toNiceDomain(link.uri)} - - {!embedPlayerParams?.isGif && ( + {!isCompatibleGiphy && ( + + {toNiceDomain(link.uri)} + + )} + + {!embedPlayerParams?.isGif && !embedPlayerParams?.dimensions && ( {link.title || link.uri} From d3c0b48da3053727dd4e02acc353f6372121d944 Mon Sep 17 00:00:00 2001 From: dan Date: Fri, 19 Apr 2024 23:37:11 +0100 Subject: [PATCH 104/167] Top/Latest for hashtags (#3625) * Split HashtagScreen into two components * Hashtag tabs * Visual fixes --- src/screens/Hashtag.tsx | 208 +++++++++++++++++++++---------- src/view/com/util/ViewHeader.tsx | 55 +++++--- 2 files changed, 184 insertions(+), 79 deletions(-) diff --git a/src/screens/Hashtag.tsx b/src/screens/Hashtag.tsx index 5388593f14..34539f5102 100644 --- a/src/screens/Hashtag.tsx +++ b/src/screens/Hashtag.tsx @@ -1,11 +1,12 @@ import React from 'react' -import {ListRenderItemInfo, Pressable} from 'react-native' +import {ListRenderItemInfo, Pressable, StyleSheet, View} from 'react-native' import {PostView} from '@atproto/api/dist/client/types/app/bsky/feed/defs' import {msg} from '@lingui/macro' import {useLingui} from '@lingui/react' import {useFocusEffect} from '@react-navigation/native' import {NativeStackScreenProps} from '@react-navigation/native-stack' +import {usePalette} from '#/lib/hooks/usePalette' import {HITSLOP_10} from 'lib/constants' import {useInitialNumToRender} from 'lib/hooks/useInitialNumToRender' import {CommonNavigatorParams} from 'lib/routes/types' @@ -13,18 +14,17 @@ import {shareUrl} from 'lib/sharing' import {cleanError} from 'lib/strings/errors' import {sanitizeHandle} from 'lib/strings/handles' import {enforceLen} from 'lib/strings/helpers' -import {isNative} from 'platform/detection' +import {isNative, isWeb} from 'platform/detection' import {useSearchPostsQuery} from 'state/queries/search-posts' -import {useSetMinimalShellMode} from 'state/shell' +import {useSetDrawerSwipeDisabled, useSetMinimalShellMode} from 'state/shell' +import {Pager} from '#/view/com/pager/Pager' +import {TabBar} from '#/view/com/pager/TabBar' +import {CenteredView} from '#/view/com/util/Views' import {Post} from 'view/com/post/Post' import {List} from 'view/com/util/List' import {ViewHeader} from 'view/com/util/ViewHeader' import {ArrowOutOfBox_Stroke2_Corner0_Rounded} from '#/components/icons/ArrowOutOfBox' -import { - ListFooter, - ListHeaderDesktop, - ListMaybePlaceholder, -} from '#/components/Lists' +import {ListFooter, ListMaybePlaceholder} from '#/components/Lists' const renderItem = ({item}: ListRenderItemInfo) => { return @@ -38,20 +38,13 @@ export default function HashtagScreen({ route, }: NativeStackScreenProps) { const {tag, author} = route.params - const setMinimalShellMode = useSetMinimalShellMode() const {_} = useLingui() - const initialNumToRender = useInitialNumToRender() - const [isPTR, setIsPTR] = React.useState(false) + const pal = usePalette('default') const fullTag = React.useMemo(() => { return `#${decodeURIComponent(tag)}` }, [tag]) - const queryParam = React.useMemo(() => { - if (!author) return fullTag - return `${fullTag} from:${sanitizeHandle(author)}` - }, [fullTag, author]) - const headerTitle = React.useMemo(() => { return enforceLen(fullTag.toLowerCase(), 24, true, 'middle') }, [fullTag]) @@ -61,27 +54,6 @@ export default function HashtagScreen({ return sanitizeHandle(author) }, [author]) - const { - data, - isFetchingNextPage, - isLoading, - isError, - error, - refetch, - fetchNextPage, - hasNextPage, - } = useSearchPostsQuery({query: queryParam}) - - const posts = React.useMemo(() => { - return data?.pages.flatMap(page => page.posts) || [] - }, [data]) - - useFocusEffect( - React.useCallback(() => { - setMinimalShellMode(false) - }, [setMinimalShellMode]), - ) - const onShare = React.useCallback(() => { const url = new URL('https://bsky.app') url.pathname = `/hashtag/${decodeURIComponent(tag)}` @@ -91,6 +63,131 @@ export default function HashtagScreen({ shareUrl(url.toString()) }, [tag, author]) + const [activeTab, setActiveTab] = React.useState(0) + const setMinimalShellMode = useSetMinimalShellMode() + const setDrawerSwipeDisabled = useSetDrawerSwipeDisabled() + + useFocusEffect( + React.useCallback(() => { + setMinimalShellMode(false) + }, [setMinimalShellMode]), + ) + + const onPageSelected = React.useCallback( + (index: number) => { + setMinimalShellMode(false) + setDrawerSwipeDisabled(index > 0) + setActiveTab(index) + }, + [setDrawerSwipeDisabled, setMinimalShellMode], + ) + + const sections = React.useMemo(() => { + return [ + { + title: _(msg`Top`), + component: ( + + ), + }, + { + title: _(msg`Latest`), + component: ( + + ), + }, + ] + }, [_, fullTag, author, activeTab]) + + return ( + <> + + ( + + + + ) + : undefined + } + /> + + ( + + section.title)} {...props} /> + + )} + initialPage={0}> + {sections.map((section, i) => ( + {section.component} + ))} + + + ) +} + +function HashtagScreenTab({ + fullTag, + author, + sort, + active, +}: { + fullTag: string + author: string | undefined + sort: 'top' | 'latest' + active: boolean +}) { + const {_} = useLingui() + const initialNumToRender = useInitialNumToRender() + const [isPTR, setIsPTR] = React.useState(false) + + const queryParam = React.useMemo(() => { + if (!author) return fullTag + return `${fullTag} from:${sanitizeHandle(author)}` + }, [fullTag, author]) + + const { + data, + isFetched, + isFetchingNextPage, + isLoading, + isError, + error, + refetch, + fetchNextPage, + hasNextPage, + } = useSearchPostsQuery({query: queryParam, sort, enabled: active}) + + const posts = React.useMemo(() => { + return data?.pages.flatMap(page => page.posts) || [] + }, [data]) + const onRefresh = React.useCallback(async () => { setIsPTR(true) await refetch() @@ -104,29 +201,9 @@ export default function HashtagScreen({ return ( <> - ( - - - - ) - : undefined - } - /> {posts.length < 1 ? ( - } ListFooterComponent={ ) } + +const styles = StyleSheet.create({ + tabBarContainer: { + // @ts-ignore web only + position: isWeb ? 'sticky' : '', + top: 0, + zIndex: 1, + }, +}) diff --git a/src/view/com/util/ViewHeader.tsx b/src/view/com/util/ViewHeader.tsx index 872e10eef0..63a2b3de39 100644 --- a/src/view/com/util/ViewHeader.tsx +++ b/src/view/com/util/ViewHeader.tsx @@ -1,19 +1,20 @@ import React from 'react' import {StyleSheet, TouchableOpacity, View} from 'react-native' -import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' -import {useNavigation} from '@react-navigation/native' -import {CenteredView} from './Views' -import {Text} from './text/Text' -import {usePalette} from 'lib/hooks/usePalette' -import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' -import {useAnalytics} from 'lib/analytics/analytics' -import {NavigationProp} from 'lib/routes/types' -import {useMinimalShellMode} from 'lib/hooks/useMinimalShellMode' import Animated from 'react-native-reanimated' -import {useSetDrawerOpen} from '#/state/shell' +import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' import {msg} from '@lingui/macro' import {useLingui} from '@lingui/react' +import {useNavigation} from '@react-navigation/native' + +import {useSetDrawerOpen} from '#/state/shell' +import {useAnalytics} from 'lib/analytics/analytics' +import {useMinimalShellMode} from 'lib/hooks/useMinimalShellMode' +import {usePalette} from 'lib/hooks/usePalette' +import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' +import {NavigationProp} from 'lib/routes/types' import {useTheme} from '#/alf' +import {Text} from './text/Text' +import {CenteredView} from './Views' const BACK_HITSLOP = {left: 20, top: 20, right: 50, bottom: 20} @@ -62,6 +63,7 @@ export function ViewHeader({ return ( @@ -136,14 +138,17 @@ export function ViewHeader({ function DesktopWebHeader({ title, + subtitle, renderButton, showBorder = true, }: { title: string + subtitle?: string renderButton?: () => JSX.Element showBorder?: boolean }) { const pal = usePalette('default') + const t = useTheme() return ( - - - {title} - + + + + {title} + + + {renderButton?.()} - {renderButton?.()} + {subtitle ? ( + + + + {subtitle} + + + + ) : null} ) } @@ -236,6 +258,9 @@ const styles = StyleSheet.create({ subtitle: { fontSize: 13, }, + subtitleDesktop: { + fontSize: 15, + }, backBtn: { width: 30, height: 30, From ce1c1e1cbed05183b42dd44e620fe1d4065bce1e Mon Sep 17 00:00:00 2001 From: dan Date: Sat, 20 Apr 2024 00:29:35 +0100 Subject: [PATCH 105/167] Search in PWI (#3628) --- src/view/screens/Search/Search.tsx | 144 +++++++++++------------------ 1 file changed, 52 insertions(+), 92 deletions(-) diff --git a/src/view/screens/Search/Search.tsx b/src/view/screens/Search/Search.tsx index 36e780c77c..099174b9b3 100644 --- a/src/view/screens/Search/Search.tsx +++ b/src/view/screens/Search/Search.tsx @@ -393,13 +393,7 @@ function SearchScreenUserResults({ ) } -export function SearchScreenInner({ - query, - primarySearch, -}: { - query?: string - primarySearch?: boolean -}) { +export function SearchScreenInner({query}: {query?: string}) { const pal = usePalette('default') const setMinimalShellMode = useSetMinimalShellMode() const setDrawerSwipeDisabled = useSetDrawerSwipeDisabled() @@ -419,86 +413,35 @@ export function SearchScreenInner({ const sections = React.useMemo(() => { if (!query) return [] - if (hasSession) { - return [ - { - title: _(msg`Top`), - component: ( - - ), - }, - { - title: _(msg`Latest`), - component: ( - - ), - }, - { - title: _(msg`People`), - component: ( - - ), - }, - ] - } else { - return [ - { - title: _(msg`People`), - component: ( - - ), - }, - ] - } - }, [hasSession, _, query, activeTab]) - - if (hasSession) { - return query ? ( - ( - - section.title)} {...props} /> - - )} - initialPage={0}> - {sections.map((section, i) => ( - {section.component} - ))} - - ) : ( - - - - Suggested Follows - - - - - - ) - } + return [ + { + title: _(msg`Top`), + component: ( + + ), + }, + { + title: _(msg`Latest`), + component: ( + + ), + }, + { + title: _(msg`People`), + component: ( + + ), + }, + ] + }, [_, query, activeTab]) return query ? ( {section.component} ))} + ) : hasSession ? ( + + + + Suggested Follows + + + + + ) : ( - {isDesktop && !primarySearch ? ( - Find users with the search tool on the right - ) : ( - Find users on Bluesky - )} + Find posts and users on Bluesky From 0e3a13b6df3c3e0c72473715be7d6eb809cae597 Mon Sep 17 00:00:00 2001 From: Matthieu Sieben Date: Mon, 22 Apr 2024 19:44:44 +0200 Subject: [PATCH 106/167] docs(build): update build instructions (#3641) --- docs/build.md | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/docs/build.md b/docs/build.md index 0eb2315098..deab91a5ba 100644 --- a/docs/build.md +++ b/docs/build.md @@ -2,7 +2,8 @@ ## App Build -- Set up your environment [using the react native instructions](https://reactnative.dev/docs/environment-setup). +- Set up your environment [using the expo instructions](https://docs.expo.dev/guides/local-app-development/). + - make sure that the JAVA_HOME points to the zulu-17 directory in your `.zshrc` or `.bashrc` file: `export JAVA_HOME=/Library/Java/JavaVirtualMachines/zulu-17.jdk/Contents/Home`. DO NOT use another JDK or you will encounter build errors. - If you're running macOS, make sure you are running the correct versions of Ruby and Cocoapods: - Check if you've installed Cocoapods through `homebrew`. If you have, remove it: - `brew info cocoapods` @@ -14,7 +15,7 @@ - `rbenv global 2.7.6` - Add `eval "$(rbenv init - zsh)"` to your `~/.zshrc` - From inside the project directory: - - `bundler install` + - `bundler install` (this will install Cocoapods) - Setup your environment [for e2e testing using detox](https://wix.github.io/Detox/docs/introduction/getting-started): - `yarn global add detox-cli` - `brew tap wix/brew` @@ -27,7 +28,7 @@ - `git clone git@github.com:bluesky-social/atproto.git` - `cd atproto` - `brew install pnpm` - - `brew install jq` + - optional: `brew install jq` - `pnpm i` - `pnpm build` - Start the docker daemon (on MacOS this entails starting the Docker Desktop app) @@ -38,11 +39,22 @@ - Xcode must be installed for this to run. - A simulator must be preconfigured in Xcode settings. - if no iOS versions are available, install the iOS runtime at `Xcode > Settings > Platforms`. + - if the simulator download keeps failing you can download it from the developer website. + - [Apple Developer](https://developer.apple.com/download/all/?q=Simulator%20Runtime) + - `xcode-select -s /Applications/Xcode.app` + - `xcodebuild -runFirstLaunch` + - `xcrun simctl runtime add "~/Downloads/iOS_17.4_Simulator_Runtime.dmg"` (adapt the path to the downloaded file) - In addition, ensure Xcode Command Line Tools are installed using `xcode-select --install`. - - Pods must be installed: - - From the project directory root: `cd ios && pod install`. - Expo will require you to configure Xcode Signing. Follow the linked instructions. Error messages in Xcode related to the signing process can be safely ignored when installing on the iOS Simulator; Expo merely requires the profile to exist in order to install the app on the Simulator. + - Make sure you do have a certificate: open Xcode > Settings > Accounts > (sign-in) > Manage Certificates > + > Apple Development > Done. + - If you still encounter issues, try `rm -rf ios` before trying to build again (`yarn ios`) - Android: `yarn android` + - Install "Android Studio" + - Make sure you have the Android SDK installed (Android Studio > Tools > Android SDK). + - In "SDK Platforms": "Android x" (where x is Android's current version). + - In "SDK Tools": "Android SDK Build-Tools" and "Android Emulator" are required. + - Add `export ANDROID_HOME=/Users//Library/Android/sdk` to your `.zshrc` or `.bashrc` (and restart your terminal). + - Setup an emulator (Android Studio > Tools > Device Manager). - Web: `yarn web` - If you are cloning or forking this repo as an open-source developer, please check the tips below as well - Run e2e tests From 243769e6577f1fc2a23dc4542bd57eab3bba45de Mon Sep 17 00:00:00 2001 From: Hailey Date: Mon, 22 Apr 2024 13:06:25 -0700 Subject: [PATCH 107/167] remove gate from updates (#3646) --- src/lib/hooks/useOTAUpdates.ts | 4 +--- src/lib/statsig/gates.ts | 1 - 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/src/lib/hooks/useOTAUpdates.ts b/src/lib/hooks/useOTAUpdates.ts index b8d331c6f1..a1692e62cc 100644 --- a/src/lib/hooks/useOTAUpdates.ts +++ b/src/lib/hooks/useOTAUpdates.ts @@ -12,7 +12,6 @@ import { import {logger} from '#/logger' import {IS_TESTFLIGHT} from 'lib/app-info' -import {useGate} from 'lib/statsig/statsig' import {isIOS} from 'platform/detection' const MINIMUM_MINIMIZE_TIME = 15 * 60e3 @@ -31,8 +30,7 @@ async function setExtraParams() { } export function useOTAUpdates() { - const gate = useGate() - const shouldReceiveUpdates = isEnabled && !__DEV__ && gate('receive_updates') + const shouldReceiveUpdates = isEnabled && !__DEV__ const appState = React.useRef('active') const lastMinimize = React.useRef(0) diff --git a/src/lib/statsig/gates.ts b/src/lib/statsig/gates.ts index e42e9efe1e..843c14f04d 100644 --- a/src/lib/statsig/gates.ts +++ b/src/lib/statsig/gates.ts @@ -6,7 +6,6 @@ export type Gate = | 'hide_vertical_scroll_indicators' | 'new_gif_player' | 'new_profile_scroll_component' - | 'receive_updates' | 'show_follow_back_label_v2' | 'start_session_with_following_v2' | 'use_new_suggestions_endpoint' From bcd88b088a43d64f34da5ec0d16dadaa74beedb3 Mon Sep 17 00:00:00 2001 From: Samuel Newman Date: Mon, 22 Apr 2024 21:24:50 +0100 Subject: [PATCH 108/167] add maxwidth to app language dropdown (#3635) --- src/components/AppLanguageDropdown.web.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/AppLanguageDropdown.web.tsx b/src/components/AppLanguageDropdown.web.tsx index c3c8575a10..a106d99663 100644 --- a/src/components/AppLanguageDropdown.web.tsx +++ b/src/components/AppLanguageDropdown.web.tsx @@ -71,6 +71,7 @@ export function AppLanguageDropdown() { color: t.atoms.text.color, background: t.atoms.bg.backgroundColor, padding: 4, + maxWidth: '100%', }}> {APP_LANGUAGES.filter(l => Boolean(l.code2)).map(l => ( ))} From f4e72cc83c431285dee454b6201377871dbed09a Mon Sep 17 00:00:00 2001 From: Samuel Newman Date: Mon, 22 Apr 2024 22:07:48 +0100 Subject: [PATCH 115/167] [GIFs] Add error boundary to GIF picker (#3643) * error boundary on gif picker * add dialog.close for web users * fix size of dialog on web * Safer coercion --------- Co-authored-by: Dan Abramov --- src/components/dialogs/GifSelect.tsx | 37 +++++++++++++++++++++++++++- src/view/com/util/ErrorBoundary.tsx | 12 ++++++--- 2 files changed, 45 insertions(+), 4 deletions(-) diff --git a/src/components/dialogs/GifSelect.tsx b/src/components/dialogs/GifSelect.tsx index baff31168b..ad4fbeadea 100644 --- a/src/components/dialogs/GifSelect.tsx +++ b/src/components/dialogs/GifSelect.tsx @@ -13,6 +13,8 @@ import { useSetExternalEmbedPref, } from '#/state/preferences' import {Gif, useGifphySearch, useGiphyTrending} from '#/state/queries/giphy' +import {ErrorScreen} from '#/view/com/util/error/ErrorScreen' +import {ErrorBoundary} from '#/view/com/util/ErrorBoundary' import {atoms as a, useBreakpoints, useTheme} from '#/alf' import * as Dialog from '#/components/Dialog' import * as TextField from '#/components/forms/TextField' @@ -54,13 +56,18 @@ export function GifSelectDialog({ break } + const renderErrorBoundary = useCallback( + (error: any) => , + [], + ) + return ( - {content} + {content} ) } @@ -357,3 +364,31 @@ function GiphyConsentPrompt({control}: {control: Dialog.DialogControlProps}) { ) } + +function DialogError({details}: {details?: string}) { + const {_} = useLingui() + const control = Dialog.useDialogContext() + + return ( + + + + + + ) +} diff --git a/src/view/com/util/ErrorBoundary.tsx b/src/view/com/util/ErrorBoundary.tsx index 22fdd606e4..dccd2bbc9b 100644 --- a/src/view/com/util/ErrorBoundary.tsx +++ b/src/view/com/util/ErrorBoundary.tsx @@ -1,12 +1,14 @@ import React, {Component, ErrorInfo, ReactNode} from 'react' +import {msg} from '@lingui/macro' +import {useLingui} from '@lingui/react' + +import {logger} from '#/logger' import {ErrorScreen} from './error/ErrorScreen' import {CenteredView} from './Views' -import {msg} from '@lingui/macro' -import {logger} from '#/logger' -import {useLingui} from '@lingui/react' interface Props { children?: ReactNode + renderError?: (error: any) => ReactNode } interface State { @@ -30,6 +32,10 @@ export class ErrorBoundary extends Component { public render() { if (this.state.hasError) { + if (this.props.renderError) { + return this.props.renderError(this.state.error) + } + return ( From 0b3cc5901996e58f8c26566c547a2ea3ea5b6e0b Mon Sep 17 00:00:00 2001 From: surfdude29 <149612116+surfdude29@users.noreply.github.com> Date: Mon, 22 Apr 2024 22:13:10 +0100 Subject: [PATCH 116/167] Update French translations (#3644) * Update French translations * Apply suggestions from code review Co-authored-by: Stanislas Signoud --------- Co-authored-by: Stanislas Signoud --- src/locale/locales/fr/messages.po | 988 +++++++++--------------------- 1 file changed, 292 insertions(+), 696 deletions(-) diff --git a/src/locale/locales/fr/messages.po b/src/locale/locales/fr/messages.po index a8402fe505..23a477fc7f 100644 --- a/src/locale/locales/fr/messages.po +++ b/src/locale/locales/fr/messages.po @@ -8,7 +8,7 @@ msgstr "" "Language: fr\n" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" -"PO-Revision-Date: 2024-03-12 09:00+0000\n" +"PO-Revision-Date: 2024-04-22 15:00+0100\n" "Last-Translator: surfdude29\n" "Language-Team: Stanislas Signoud (@signez.fr), surfdude29\n" "Plural-Forms: \n" @@ -32,11 +32,11 @@ msgstr "<0/> membres" #: src/view/shell/Drawer.tsx:97 msgid "<0>{0} following" -msgstr "" +msgstr "<0>{0} abonnements" #: src/components/ProfileHoverCard/index.web.tsx:314 msgid "<0>{followers} <1>{pluralizedFollowers}" -msgstr "" +msgstr "<0>{followers} <1>{pluralizedFollowers}" #: src/components/ProfileHoverCard/index.web.tsx:326 #: src/screens/Profile/Header/Metrics.tsx:45 @@ -59,14 +59,6 @@ msgstr "<0>Bienvenue sur<1>Bluesky" msgid "⚠Invalid Handle" msgstr "⚠Pseudo invalide" -#: src/view/com/util/moderation/LabelInfo.tsx:45 -#~ msgid "A content warning has been applied to this {0}." -#~ msgstr "Un avertissement sur le contenu a été appliqué sur ce {0}." - -#: src/lib/hooks/useOTAUpdate.ts:16 -#~ msgid "A new version of the app is available. Please update to continue using the app." -#~ msgstr "Une nouvelle version de l’application est disponible. Veuillez faire la mise à jour pour continuer à utiliser l’application." - #: src/view/com/util/ViewHeader.tsx:89 #: src/view/screens/Search/Search.tsx:796 msgid "Access navigation links and settings" @@ -83,7 +75,7 @@ msgstr "Accessibilité" #: src/components/moderation/LabelsOnMe.tsx:42 msgid "account" -msgstr "" +msgstr "compte" #: src/screens/Login/LoginForm.tsx:144 #: src/view/screens/Settings/index.tsx:330 @@ -97,7 +89,7 @@ msgstr "Compte bloqué" #: src/view/com/profile/ProfileMenu.tsx:153 msgid "Account followed" -msgstr "" +msgstr "Compte suivi" #: src/view/com/profile/ProfileMenu.tsx:113 msgid "Account muted" @@ -127,7 +119,7 @@ msgstr "Compte débloqué" #: src/view/com/profile/ProfileMenu.tsx:166 msgid "Account unfollowed" -msgstr "" +msgstr "Compte désabonné" #: src/view/com/profile/ProfileMenu.tsx:102 msgid "Account unmuted" @@ -167,15 +159,6 @@ msgstr "Ajouter un texte alt" msgid "Add App Password" msgstr "Ajouter un mot de passe d’application" -#: src/view/com/modals/report/InputIssueDetails.tsx:41 -#: src/view/com/modals/report/Modal.tsx:191 -#~ msgid "Add details" -#~ msgstr "Ajouter des détails" - -#: src/view/com/modals/report/Modal.tsx:194 -#~ msgid "Add details to report" -#~ msgstr "Ajouter des détails au rapport" - #: src/view/com/composer/Composer.tsx:467 msgid "Add link card" msgstr "Ajouter une carte de lien" @@ -228,13 +211,9 @@ msgstr "Définissez le nombre de likes qu’une réponse doit avoir pour être a msgid "Adult Content" msgstr "Contenu pour adultes" -#: src/view/com/modals/ContentFilteringSettings.tsx:141 -#~ msgid "Adult content can only be enabled via the Web at <0/>." -#~ msgstr "Le contenu pour adultes ne peut être activé que via le Web à <0/>." - #: src/components/moderation/LabelPreference.tsx:242 msgid "Adult content is disabled." -msgstr "" +msgstr "Le contenu pour adultes est désactivé." #: src/screens/Moderation/index.tsx:375 #: src/view/screens/Settings/index.tsx:635 @@ -272,11 +251,11 @@ msgstr "Un e-mail a été envoyé à {0}. Il comprend un code de confirmation qu #: src/view/com/modals/ChangeEmail.tsx:119 msgid "An email has been sent to your previous address, {0}. It includes a confirmation code which you can enter below." -msgstr "Un courriel a été envoyé à votre ancienne adresse, {0}. Il comprend un code de confirmation que vous pouvez saisir ici." +msgstr "Un e-mail a été envoyé à votre ancienne adresse, {0}. Il comprend un code de confirmation que vous pouvez saisir ici." #: src/lib/moderation/useReportOptions.ts:26 msgid "An issue not included in these options" -msgstr "" +msgstr "Un problème qui ne fait pas partie de ces options" #: src/components/hooks/useFollowMethods.ts:35 #: src/components/hooks/useFollowMethods.ts:50 @@ -298,7 +277,7 @@ msgstr "Animaux" #: src/lib/moderation/useReportOptions.ts:31 msgid "Anti-Social Behavior" -msgstr "" +msgstr "Comportement antisocial" #: src/view/screens/LanguageSettings.tsx:95 msgid "App Language" @@ -329,32 +308,15 @@ msgstr "Mots de passe d’application" #: src/components/moderation/LabelsOnMeDialog.tsx:133 #: src/components/moderation/LabelsOnMeDialog.tsx:136 msgid "Appeal" -msgstr "" +msgstr "Faire appel" #: src/components/moderation/LabelsOnMeDialog.tsx:201 msgid "Appeal \"{0}\" label" -msgstr "" - -#: src/view/com/util/forms/PostDropdownBtn.tsx:337 -#: src/view/com/util/forms/PostDropdownBtn.tsx:346 -#~ msgid "Appeal content warning" -#~ msgstr "Faire appel de l’avertissement sur le contenu" - -#: src/view/com/modals/AppealLabel.tsx:65 -#~ msgid "Appeal Content Warning" -#~ msgstr "Faire appel de l’avertissement sur le contenu" +msgstr "Faire appel de l’étiquette « {0} »" #: src/components/moderation/LabelsOnMeDialog.tsx:192 msgid "Appeal submitted." -msgstr "" - -#: src/view/com/util/moderation/LabelInfo.tsx:52 -#~ msgid "Appeal this decision" -#~ msgstr "Faire appel de cette décision" - -#: src/view/com/util/moderation/LabelInfo.tsx:56 -#~ msgid "Appeal this decision." -#~ msgstr "Faire appel de cette décision." +msgstr "Appel soumis." #: src/view/screens/Settings/index.tsx:436 msgid "Appearance" @@ -366,7 +328,7 @@ msgstr "Êtes-vous sûr de vouloir supprimer le mot de passe de l’application #: src/view/com/feeds/FeedSourceCard.tsx:280 msgid "Are you sure you want to remove {0} from your feeds?" -msgstr "" +msgstr "Êtes-vous sûr de vouloir supprimer {0} de vos fils d’actu ?" #: src/view/com/composer/Composer.tsx:509 msgid "Are you sure you'd like to discard this draft?" @@ -376,10 +338,6 @@ msgstr "Êtes-vous sûr de vouloir rejeter ce brouillon ?" msgid "Are you sure?" msgstr "Vous confirmez ?" -#: src/view/com/util/forms/PostDropdownBtn.tsx:322 -#~ msgid "Are you sure? This cannot be undone." -#~ msgstr "Vous confirmez ? Cela ne pourra pas être annulé." - #: src/view/com/composer/select-language/SuggestedLanguage.tsx:60 msgid "Are you writing in <0>{0}?" msgstr "Écrivez-vous en <0>{0} ?" @@ -394,7 +352,7 @@ msgstr "Nudité artistique ou non érotique." #: src/screens/Signup/StepHandle.tsx:119 msgid "At least 3 characters" -msgstr "" +msgstr "Au moins 3 caractères" #: src/components/moderation/LabelsOnMeDialog.tsx:246 #: src/components/moderation/LabelsOnMeDialog.tsx:247 @@ -412,11 +370,6 @@ msgstr "" msgid "Back" msgstr "Arrière" -#: src/view/com/post-thread/PostThread.tsx:480 -#~ msgctxt "action" -#~ msgid "Back" -#~ msgstr "Retour" - #: src/screens/Onboarding/StepSuggestedAccounts/index.tsx:144 msgid "Based on your interest in {interestsText}" msgstr "En fonction de votre intérêt pour {interestsText}" @@ -436,7 +389,7 @@ msgstr "Date de naissance :" #: src/screens/Profile/Header/ProfileHeaderStandard.tsx:286 #: src/view/com/profile/ProfileMenu.tsx:361 msgid "Block" -msgstr "" +msgstr "Bloquer" #: src/view/com/profile/ProfileMenu.tsx:300 #: src/view/com/profile/ProfileMenu.tsx:307 @@ -445,7 +398,7 @@ msgstr "Bloquer ce compte" #: src/view/com/profile/ProfileMenu.tsx:344 msgid "Block Account?" -msgstr "" +msgstr "Bloquer ce compte ?" #: src/view/screens/ProfileList.tsx:532 msgid "Block accounts" @@ -460,10 +413,6 @@ msgstr "Liste de blocage" msgid "Block these accounts?" msgstr "Bloquer ces comptes ?" -#: src/view/screens/ProfileList.tsx:320 -#~ msgid "Block this List" -#~ msgstr "Bloquer cette liste" - #: src/view/com/lists/ListCard.tsx:110 #: src/view/com/util/post-embeds/QuoteEmbed.tsx:58 msgid "Blocked" @@ -492,7 +441,7 @@ msgstr "Post bloqué." #: src/screens/Profile/Sections/Labels.tsx:163 msgid "Blocking does not prevent this labeler from placing labels on your account." -msgstr "" +msgstr "Le blocage n’empêche pas cet étiqueteur de placer des étiquettes sur votre compte." #: src/view/screens/ProfileList.tsx:633 msgid "Blocking is public. Blocked accounts cannot reply in your threads, mention you, or otherwise interact with you." @@ -500,7 +449,7 @@ msgstr "Le blocage est public. Les comptes bloqués ne peuvent pas répondre à #: src/view/com/profile/ProfileMenu.tsx:353 msgid "Blocking will not prevent labels from being applied on your account, but it will stop this account from replying in your threads or interacting with you." -msgstr "" +msgstr "Le blocage n’empêchera pas les étiquettes d’être appliquées à votre compte, mais il empêchera ce compte de répondre à vos discussions ou d’interagir avec vous." #: src/view/com/auth/SplashScreen.web.tsx:149 msgid "Blog" @@ -536,20 +485,16 @@ msgstr "Bluesky n’affichera pas votre profil et vos posts à des personnes non #: src/lib/moderation/useLabelBehaviorDescription.ts:53 msgid "Blur images" -msgstr "" +msgstr "Flouter les images" #: src/lib/moderation/useLabelBehaviorDescription.ts:51 msgid "Blur images and filter from feeds" -msgstr "" +msgstr "Flouter les images et les filtrer des fils d’actu" #: src/screens/Onboarding/index.tsx:33 msgid "Books" msgstr "Livres" -#: src/view/screens/Settings/index.tsx:893 -#~ msgid "Build version {0} {1}" -#~ msgstr "Version Build {0} {1}" - #: src/view/com/auth/SplashScreen.web.tsx:146 msgid "Business" msgstr "Affaires" @@ -564,7 +509,7 @@ msgstr "par {0}" #: src/components/LabelingServiceCard/index.tsx:57 msgid "By {0}" -msgstr "" +msgstr "Par {0}" #: src/view/com/profile/ProfileSubpageHeader.tsx:161 msgid "by <0/>" @@ -572,7 +517,7 @@ msgstr "par <0/>" #: src/screens/Signup/StepInfo/Policies.tsx:74 msgid "By creating an account you agree to the {els}." -msgstr "" +msgstr "En créant un compte, vous acceptez les {els}." #: src/view/com/profile/ProfileSubpageHeader.tsx:159 msgid "by you" @@ -648,11 +593,11 @@ msgstr "Annuler la recherche" #: src/view/com/modals/LinkWarning.tsx:106 msgid "Cancels opening the linked website" -msgstr "" +msgstr "Annule l’ouverture du site web lié" #: src/view/com/modals/VerifyEmail.tsx:152 msgid "Change" -msgstr "" +msgstr "Modifier" #: src/view/screens/Settings/index.tsx:356 msgctxt "action" @@ -685,10 +630,6 @@ msgstr "Modifier le mot de passe" msgid "Change post language to {0}" msgstr "Modifier la langue de post en {0}" -#: src/view/screens/Settings/index.tsx:733 -#~ msgid "Change your Bluesky password" -#~ msgstr "Changer votre mot de passe pour Bluesky" - #: src/view/com/modals/ChangeEmail.tsx:109 msgid "Change Your Email" msgstr "Modifier votre e-mail" @@ -714,10 +655,6 @@ msgstr "Consultez votre boîte de réception, vous avez du recevoir un e-mail co msgid "Choose \"Everybody\" or \"Nobody\"" msgstr "Choisir « Tout le monde » ou « Personne »" -#: src/view/screens/Settings/index.tsx:697 -#~ msgid "Choose a new Bluesky username or create" -#~ msgstr "Choisir un nouveau pseudo Bluesky ou en créer un" - #: src/view/com/auth/server-input/index.tsx:79 msgid "Choose Service" msgstr "Choisir un service" @@ -762,11 +699,11 @@ msgstr "Effacer la recherche" #: src/view/screens/Settings/index.tsx:833 msgid "Clears all legacy storage data" -msgstr "" +msgstr "Efface toutes les données de stockage existantes" #: src/view/screens/Settings/index.tsx:845 msgid "Clears all storage data" -msgstr "" +msgstr "Efface toutes les données de stockage" #: src/view/screens/Support.tsx:40 msgid "click here" @@ -874,11 +811,11 @@ msgstr "Configurer les paramètres de filtrage de contenu pour la catégorie : #: src/components/moderation/LabelPreference.tsx:81 msgid "Configure content filtering setting for category: {name}" -msgstr "" +msgstr "Configure les paramètres de filtrage de contenu pour la catégorie : {name}" #: src/components/moderation/LabelPreference.tsx:244 msgid "Configured in <0>moderation settings." -msgstr "" +msgstr "Configuré dans <0>les paramètres de modération." #: src/components/Prompt.tsx:153 #: src/components/Prompt.tsx:156 @@ -890,12 +827,6 @@ msgstr "" msgid "Confirm" msgstr "Confirmer" -#: src/view/com/modals/Confirm.tsx:75 -#: src/view/com/modals/Confirm.tsx:78 -#~ msgctxt "action" -#~ msgid "Confirm" -#~ msgstr "Confirmer" - #: src/view/com/modals/ChangeEmail.tsx:193 #: src/view/com/modals/ChangeEmail.tsx:195 msgid "Confirm Change" @@ -909,17 +840,13 @@ msgstr "Confirmer les paramètres de langue" msgid "Confirm delete account" msgstr "Confirmer la suppression du compte" -#: src/view/com/modals/ContentFilteringSettings.tsx:156 -#~ msgid "Confirm your age to enable adult content." -#~ msgstr "Confirmez votre âge pour activer le contenu pour adultes." - #: src/screens/Moderation/index.tsx:301 msgid "Confirm your age:" -msgstr "" +msgstr "Confirmez votre âge :" #: src/screens/Moderation/index.tsx:292 msgid "Confirm your birthdate" -msgstr "" +msgstr "Confirme votre date de naissance" #: src/view/com/modals/ChangeEmail.tsx:157 #: src/view/com/modals/DeleteAccount.tsx:175 @@ -938,23 +865,15 @@ msgstr "Contacter le support" #: src/components/moderation/LabelsOnMe.tsx:42 msgid "content" -msgstr "" +msgstr "contenu" #: src/lib/moderation/useGlobalLabelStrings.ts:18 msgid "Content Blocked" -msgstr "" - -#: src/view/screens/Moderation.tsx:83 -#~ msgid "Content filtering" -#~ msgstr "Filtrage du contenu" - -#: src/view/com/modals/ContentFilteringSettings.tsx:44 -#~ msgid "Content Filtering" -#~ msgstr "Filtrage du contenu" +msgstr "Contenu bloqué" #: src/screens/Moderation/index.tsx:285 msgid "Content filters" -msgstr "" +msgstr "Filtres de contenu" #: src/view/com/modals/lang-settings/ContentLanguagesSettings.tsx:74 #: src/view/screens/LanguageSettings.tsx:278 @@ -979,7 +898,7 @@ msgstr "Avertissements sur le contenu" #: src/components/Menu/index.web.tsx:84 msgid "Context menu backdrop, click to close the menu." -msgstr "" +msgstr "Menu contextuel en arrière-plan, cliquez pour fermer le menu." #: src/screens/Onboarding/StepAlgoFeeds/index.tsx:161 #: src/screens/Onboarding/StepFollowingFeed.tsx:154 @@ -994,7 +913,7 @@ msgstr "Continuer" #: src/components/AccountList.tsx:108 msgid "Continue as {0} (currently signed in)" -msgstr "" +msgstr "Continuer comme {0} (actuellement connecté)" #: src/screens/Onboarding/StepFollowingFeed.tsx:151 #: src/screens/Onboarding/StepInterests/index.tsx:249 @@ -1034,7 +953,7 @@ msgstr "Copié dans le presse-papier" #: src/components/dialogs/Embed.tsx:134 msgid "Copied!" -msgstr "" +msgstr "Copié !" #: src/view/com/modals/AddAppPasswords.tsx:190 msgid "Copies app password" @@ -1042,16 +961,16 @@ msgstr "Copie le mot de passe d’application" #: src/view/com/modals/AddAppPasswords.tsx:189 msgid "Copy" -msgstr "Copie" +msgstr "Copier" #: src/view/com/modals/ChangeHandle.tsx:480 msgid "Copy {0}" -msgstr "" +msgstr "Copier {0}" #: src/components/dialogs/Embed.tsx:120 #: src/components/dialogs/Embed.tsx:139 msgid "Copy code" -msgstr "" +msgstr "Copier ce code" #: src/view/screens/ProfileList.tsx:390 msgid "Copy link to list" @@ -1062,10 +981,6 @@ msgstr "Copier le lien vers la liste" msgid "Copy link to post" msgstr "Copier le lien vers le post" -#: src/view/com/profile/ProfileHeader.tsx:295 -#~ msgid "Copy link to profile" -#~ msgstr "Copier le lien vers le profil" - #: src/view/com/util/forms/PostDropdownBtn.tsx:228 #: src/view/com/util/forms/PostDropdownBtn.tsx:230 msgid "Copy post text" @@ -1100,7 +1015,7 @@ msgstr "Créer un compte" #: src/components/dialogs/Signin.tsx:86 #: src/components/dialogs/Signin.tsx:88 msgid "Create an account" -msgstr "" +msgstr "Créer un compte" #: src/view/com/modals/AddAppPasswords.tsx:227 msgid "Create App Password" @@ -1113,20 +1028,12 @@ msgstr "Créer un nouveau compte" #: src/components/ReportDialog/SelectReportOptionView.tsx:94 msgid "Create report for {0}" -msgstr "" +msgstr "Créer un rapport pour {0}" #: src/view/screens/AppPasswords.tsx:246 msgid "Created {0}" msgstr "{0} créé" -#: src/view/screens/ProfileFeed.tsx:616 -#~ msgid "Created by <0/>" -#~ msgstr "Créée par <0/>" - -#: src/view/screens/ProfileFeed.tsx:614 -#~ msgid "Created by you" -#~ msgstr "Créée par vous" - #: src/view/com/composer/Composer.tsx:469 msgid "Creates a card with a thumbnail. The card links to {url}" msgstr "Crée une carte avec une miniature. La carte pointe vers {url}" @@ -1168,11 +1075,11 @@ msgstr "Thème sombre" #: src/screens/Signup/StepInfo/index.tsx:134 msgid "Date of birth" -msgstr "" +msgstr "Date de naissance" #: src/view/screens/Settings/index.tsx:805 msgid "Debug Moderation" -msgstr "" +msgstr "Déboguer la modération" #: src/view/screens/Debug.tsx:83 msgid "Debug panel" @@ -1182,7 +1089,7 @@ msgstr "Panneau de débug" #: src/view/screens/AppPasswords.tsx:268 #: src/view/screens/ProfileList.tsx:615 msgid "Delete" -msgstr "" +msgstr "Supprimer" #: src/view/screens/Settings/index.tsx:760 msgid "Delete account" @@ -1198,7 +1105,7 @@ msgstr "Supprimer le mot de passe de l’appli" #: src/view/screens/AppPasswords.tsx:263 msgid "Delete app password?" -msgstr "" +msgstr "Supprimer le mot de passe de l’appli ?" #: src/view/screens/ProfileList.tsx:417 msgid "Delete List" @@ -1219,7 +1126,7 @@ msgstr "Supprimer le post" #: src/view/screens/ProfileList.tsx:610 msgid "Delete this list?" -msgstr "" +msgstr "Supprimer cette liste ?" #: src/view/com/util/forms/PostDropdownBtn.tsx:336 msgid "Delete this post?" @@ -1250,30 +1157,26 @@ msgstr "Atténué" #: src/view/screens/Settings/index.tsx:697 msgid "Disable haptics" -msgstr "" +msgstr "Désactiver l’haptique" #: src/view/screens/Settings/index.tsx:697 msgid "Disable vibrations" -msgstr "" +msgstr "Désactiver les vibrations" #: src/lib/moderation/useLabelBehaviorDescription.ts:32 #: src/lib/moderation/useLabelBehaviorDescription.ts:42 #: src/lib/moderation/useLabelBehaviorDescription.ts:68 #: src/screens/Moderation/index.tsx:341 msgid "Disabled" -msgstr "" +msgstr "Désactivé" #: src/view/com/composer/Composer.tsx:511 msgid "Discard" msgstr "Ignorer" -#: src/view/com/composer/Composer.tsx:145 -#~ msgid "Discard draft" -#~ msgstr "Ignorer le brouillon" - #: src/view/com/composer/Composer.tsx:508 msgid "Discard draft?" -msgstr "" +msgstr "Abandonner le brouillon ?" #: src/screens/Moderation/index.tsx:518 #: src/screens/Moderation/index.tsx:522 @@ -1299,19 +1202,19 @@ msgstr "Afficher le nom" #: src/view/com/modals/ChangeHandle.tsx:397 msgid "DNS Panel" -msgstr "" +msgstr "Panneau DNS" #: src/lib/moderation/useGlobalLabelStrings.ts:39 msgid "Does not include nudity." -msgstr "" +msgstr "Ne comprend pas de nudité." #: src/screens/Signup/StepHandle.tsx:105 msgid "Doesn't begin or end with a hyphen" -msgstr "" +msgstr "Ne commence pas ou ne se termine pas par un trait d’union" #: src/view/com/modals/ChangeHandle.tsx:481 msgid "Domain Value" -msgstr "" +msgstr "Valeur du domaine" #: src/view/com/modals/ChangeHandle.tsx:488 msgid "Domain verified!" @@ -1352,14 +1255,6 @@ msgstr "Terminer" msgid "Done{extraText}" msgstr "Terminé{extraText}" -#: src/view/com/auth/login/ChooseAccountForm.tsx:46 -#~ msgid "Double tap to sign in" -#~ msgstr "Tapotez deux fois pour vous connecter" - -#: src/view/screens/Settings/index.tsx:755 -#~ msgid "Download Bluesky account data (repository)" -#~ msgstr "Télécharger les données du compte Bluesky (dépôt)" - #: src/view/screens/Settings/ExportCarDialog.tsx:59 #: src/view/screens/Settings/ExportCarDialog.tsx:63 msgid "Download CAR file" @@ -1375,7 +1270,7 @@ msgstr "En raison des politiques d’Apple, le contenu pour adultes ne peut êtr #: src/view/com/modals/ChangeHandle.tsx:258 msgid "e.g. alice" -msgstr "" +msgstr "ex. alice" #: src/view/com/modals/EditProfile.tsx:186 msgid "e.g. Alice Roberts" @@ -1383,7 +1278,7 @@ msgstr "ex. Alice Dupont" #: src/view/com/modals/ChangeHandle.tsx:380 msgid "e.g. alice.com" -msgstr "" +msgstr "ex. alice.fr" #: src/view/com/modals/EditProfile.tsx:204 msgid "e.g. Artist, dog-lover, and avid reader." @@ -1391,7 +1286,7 @@ msgstr "ex. Artiste, amoureuse des chiens et lectrice passionnée." #: src/lib/moderation/useGlobalLabelStrings.ts:43 msgid "E.g. artistic nudes." -msgstr "" +msgstr "Ex. nus artistiques." #: src/view/com/modals/CreateOrEditList.tsx:284 msgid "e.g. Great Posters" @@ -1421,7 +1316,7 @@ msgstr "Modifier" #: src/view/com/util/UserAvatar.tsx:301 #: src/view/com/util/UserBanner.tsx:85 msgid "Edit avatar" -msgstr "" +msgstr "Modifier l’avatar" #: src/view/com/composer/photos/Gallery.tsx:144 #: src/view/com/modals/EditImage.tsx:208 @@ -1505,17 +1400,17 @@ msgstr "E-mail :" #: src/components/dialogs/Embed.tsx:112 msgid "Embed HTML code" -msgstr "" +msgstr "Code HTML à intégrer" #: src/components/dialogs/Embed.tsx:97 #: src/view/com/util/forms/PostDropdownBtn.tsx:253 #: src/view/com/util/forms/PostDropdownBtn.tsx:255 msgid "Embed post" -msgstr "" +msgstr "Intégrer le post" #: src/components/dialogs/Embed.tsx:101 msgid "Embed this post in your website. Simply copy the following snippet and paste it into the HTML code of your website." -msgstr "" +msgstr "Intégrez ce post à votre site web. Il suffit de copier l’extrait suivant et de le coller dans le code HTML de votre site web." #: src/components/dialogs/EmbedConsent.tsx:101 msgid "Enable {0} only" @@ -1523,7 +1418,7 @@ msgstr "Activer {0} uniquement" #: src/screens/Moderation/index.tsx:329 msgid "Enable adult content" -msgstr "" +msgstr "Activer le contenu pour adultes" #: src/screens/Onboarding/StepModeration/AdultContentEnabledPref.tsx:94 msgid "Enable Adult Content" @@ -1537,11 +1432,7 @@ msgstr "Activer le contenu pour adultes dans vos fils d’actu" #: src/components/dialogs/EmbedConsent.tsx:82 #: src/components/dialogs/EmbedConsent.tsx:89 msgid "Enable external media" -msgstr "" - -#: src/view/com/modals/EmbedConsent.tsx:97 -#~ msgid "Enable External Media" -#~ msgstr "Activer les médias externes" +msgstr "Activer les médias externes" #: src/view/screens/PreferencesExternalEmbeds.tsx:75 msgid "Enable media players for" @@ -1553,11 +1444,11 @@ msgstr "Activez ce paramètre pour ne voir que les réponses des personnes que v #: src/components/dialogs/EmbedConsent.tsx:94 msgid "Enable this source only" -msgstr "" +msgstr "Active cette source uniquement" #: src/screens/Moderation/index.tsx:339 msgid "Enabled" -msgstr "" +msgstr "Activé" #: src/screens/Profile/Sections/Feed.tsx:100 msgid "End of feed" @@ -1569,7 +1460,7 @@ msgstr "Entrer un nom pour ce mot de passe d’application" #: src/screens/Login/SetNewPasswordForm.tsx:139 msgid "Enter a password" -msgstr "" +msgstr "Saisir un mot de passe" #: src/components/dialogs/MutedWords.tsx:99 #: src/components/dialogs/MutedWords.tsx:100 @@ -1627,11 +1518,11 @@ msgstr "Tout le monde" #: src/lib/moderation/useReportOptions.ts:66 msgid "Excessive mentions or replies" -msgstr "" +msgstr "Mentions ou réponses excessives" #: src/view/com/modals/DeleteAccount.tsx:230 msgid "Exits account deletion process" -msgstr "" +msgstr "Sort du processus de suppression du compte" #: src/view/com/modals/ChangeHandle.tsx:151 msgid "Exits handle change process" @@ -1639,7 +1530,7 @@ msgstr "Sort du processus de changement de pseudo" #: src/view/com/modals/crop-image/CropImage.web.tsx:136 msgid "Exits image cropping process" -msgstr "" +msgstr "Sort du processus de recadrage de l’image" #: src/view/com/lightbox/Lightbox.web.tsx:130 msgid "Exits image view" @@ -1661,11 +1552,11 @@ msgstr "Développe ou réduit le post complet auquel vous répondez" #: src/lib/moderation/useGlobalLabelStrings.ts:47 msgid "Explicit or potentially disturbing media." -msgstr "" +msgstr "Médias explicites ou potentiellement dérangeants." #: src/lib/moderation/useGlobalLabelStrings.ts:35 msgid "Explicit sexual images." -msgstr "" +msgstr "Images sexuelles explicites." #: src/view/screens/Settings/index.tsx:741 msgid "Export my data" @@ -1716,7 +1607,7 @@ msgstr "Échec du chargement des fils d’actu recommandés" #: src/view/com/lightbox/Lightbox.tsx:83 msgid "Failed to save image: {0}" -msgstr "" +msgstr "Échec de l’enregistrement de l’image : {0}" #: src/Navigation.tsx:196 msgid "Feed" @@ -1760,11 +1651,11 @@ msgstr "Les fils d’actu peuvent également être thématiques !" #: src/view/com/modals/ChangeHandle.tsx:481 msgid "File Contents" -msgstr "" +msgstr "Contenu du fichier" #: src/lib/moderation/useLabelBehaviorDescription.ts:66 msgid "Filter from feeds" -msgstr "" +msgstr "Filtrer des fils d’actu" #: src/screens/Onboarding/StepFinished.tsx:155 msgid "Finalizing" @@ -1835,7 +1726,7 @@ msgstr "Suivre {0}" #: src/view/com/profile/ProfileMenu.tsx:242 #: src/view/com/profile/ProfileMenu.tsx:253 msgid "Follow Account" -msgstr "" +msgstr "Suivre le compte" #: src/screens/Onboarding/StepSuggestedAccounts/index.tsx:187 msgid "Follow All" @@ -1843,7 +1734,7 @@ msgstr "Suivre tous" #: src/view/com/post-thread/PostThreadFollowBtn.tsx:144 msgid "Follow Back" -msgstr "" +msgstr "Suivre en retour" #: src/screens/Onboarding/StepSuggestedAccounts/index.tsx:182 msgid "Follow selected accounts and continue to the next step" @@ -1887,7 +1778,7 @@ msgstr "Suit {0}" #: src/view/screens/Settings/index.tsx:504 msgid "Following feed preferences" -msgstr "" +msgstr "Préférences du fil d’actu « Following »" #: src/Navigation.tsx:262 #: src/view/com/home/HomeHeaderLayout.web.tsx:54 @@ -1917,14 +1808,6 @@ msgstr "Pour des raisons de sécurité, nous devrons envoyer un code de confirma msgid "For security reasons, you won't be able to view this again. If you lose this password, you'll need to generate a new one." msgstr "Pour des raisons de sécurité, vous ne pourrez plus afficher ceci. Si vous perdez ce mot de passe, vous devrez en générer un autre." -#: src/view/com/auth/login/LoginForm.tsx:244 -#~ msgid "Forgot" -#~ msgstr "Oublié" - -#: src/view/com/auth/login/LoginForm.tsx:241 -#~ msgid "Forgot password" -#~ msgstr "Mot de passe oublié" - #: src/screens/Login/index.tsx:129 #: src/screens/Login/index.tsx:144 msgid "Forgot Password" @@ -1932,15 +1815,15 @@ msgstr "Mot de passe oublié" #: src/screens/Login/LoginForm.tsx:201 msgid "Forgot password?" -msgstr "" +msgstr "Mot de passe oublié ?" #: src/screens/Login/LoginForm.tsx:212 msgid "Forgot?" -msgstr "" +msgstr "Oublié ?" #: src/lib/moderation/useReportOptions.ts:52 msgid "Frequently Posts Unwanted Content" -msgstr "" +msgstr "Publication fréquente de contenu indésirable" #: src/screens/Hashtag.tsx:109 #: src/screens/Hashtag.tsx:149 @@ -1963,7 +1846,7 @@ msgstr "C’est parti" #: src/lib/moderation/useReportOptions.ts:37 msgid "Glaring violations of law or terms of service" -msgstr "" +msgstr "Violations flagrantes de la loi ou des conditions d’utilisation" #: src/components/moderation/ScreenHider.tsx:151 #: src/components/moderation/ScreenHider.tsx:160 @@ -1995,11 +1878,11 @@ msgstr "Retour à l’étape précédente" #: src/view/screens/NotFound.tsx:55 msgid "Go home" -msgstr "" +msgstr "Accéder à l’accueil" #: src/view/screens/NotFound.tsx:54 msgid "Go Home" -msgstr "" +msgstr "Accéder à l’accueil" #: src/view/screens/Search/Search.tsx:896 #: src/view/shell/desktop/Search.tsx:263 @@ -2013,7 +1896,7 @@ msgstr "Aller à la suite" #: src/lib/moderation/useGlobalLabelStrings.ts:46 msgid "Graphic Media" -msgstr "" +msgstr "Médias crus" #: src/view/com/modals/ChangeHandle.tsx:266 msgid "Handle" @@ -2021,7 +1904,7 @@ msgstr "Pseudo" #: src/lib/moderation/useReportOptions.ts:32 msgid "Harassment, trolling, or intolerance" -msgstr "" +msgstr "Harcèlement, trolling ou intolérance" #: src/Navigation.tsx:282 msgid "Hashtag" @@ -2092,10 +1975,6 @@ msgstr "Cacher ce post ?" msgid "Hide user list" msgstr "Cacher la liste des comptes" -#: src/view/com/profile/ProfileHeader.tsx:487 -#~ msgid "Hides posts from {0} in your feed" -#~ msgstr "Masque les posts de {0} dans votre fil d’actu" - #: src/view/com/posts/FeedErrorMessage.tsx:111 msgid "Hmm, some kind of issue occurred when contacting the feed server. Please let the feed owner know about this issue." msgstr "Hmm, un problème s’est produit avec le serveur de fils d’actu. Veuillez informer la personne propriétaire du fil d’actu de ce problème." @@ -2106,11 +1985,11 @@ msgstr "Hmm, le serveur du fils d’actu semble être mal configuré. Veuillez i #: src/view/com/posts/FeedErrorMessage.tsx:105 msgid "Hmm, the feed server appears to be offline. Please let the feed owner know about this issue." -msgstr "Mmm… le serveur de fils d’actu semble être hors ligne. Veuillez informer la personne propriétaire du fil d’actu de ce problème." +msgstr "Hmm, le serveur de fils d’actu semble être hors ligne. Veuillez informer la personne propriétaire du fil d’actu de ce problème." #: src/view/com/posts/FeedErrorMessage.tsx:102 msgid "Hmm, the feed server gave a bad response. Please let the feed owner know about this issue." -msgstr "Mmm… le serveur de fils d’actu ne répond pas. Veuillez informer la personne propriétaire du fil d’actu de ce problème." +msgstr "Hmm, le serveur de fils d’actu ne répond pas. Veuillez informer la personne propriétaire du fil d’actu de ce problème." #: src/view/com/posts/FeedErrorMessage.tsx:96 msgid "Hmm, we're having trouble finding this feed. It may have been deleted." @@ -2118,11 +1997,11 @@ msgstr "Hmm, nous n’arrivons pas à trouver ce fil d’actu. Il a peut-être #: src/screens/Moderation/index.tsx:59 msgid "Hmmmm, it seems we're having trouble loading this data. See below for more details. If this issue persists, please contact us." -msgstr "" +msgstr "Hmm, il semble que nous ayons des difficultés à charger ces données. Voir ci-dessous pour plus de détails. Si le problème persiste, veuillez nous contacter." #: src/screens/Profile/ErrorState.tsx:31 msgid "Hmmmm, we couldn't load that moderation service." -msgstr "" +msgstr "Hmm, nous n’avons pas pu charger ce service de modération." #: src/Navigation.tsx:446 #: src/view/shell/bottom-bar/BottomBar.tsx:148 @@ -2134,7 +2013,7 @@ msgstr "Accueil" #: src/view/com/modals/ChangeHandle.tsx:420 msgid "Host:" -msgstr "" +msgstr "Hébergeur :" #: src/screens/Login/ForgotPasswordForm.tsx:89 #: src/screens/Login/LoginForm.tsx:134 @@ -2169,15 +2048,15 @@ msgstr "Si rien n’est sélectionné, il n’y a pas de restriction d’âge." #: src/screens/Signup/StepInfo/Policies.tsx:83 msgid "If you are not yet an adult according to the laws of your country, your parent or legal guardian must read these Terms on your behalf." -msgstr "" +msgstr "Si vous n’êtes pas encore un adulte selon les lois de votre pays, vos parents ou votre tuteur légal doivent lire ces conditions en votre nom." #: src/view/screens/ProfileList.tsx:612 msgid "If you delete this list, you won't be able to recover it." -msgstr "" +msgstr "Si vous supprimez cette liste, vous ne pourrez pas la récupérer." #: src/view/com/util/forms/PostDropdownBtn.tsx:338 msgid "If you remove this post, you won't be able to recover it." -msgstr "" +msgstr "Si vous supprimez ce post, vous ne pourrez pas le récupérer." #: src/view/com/modals/ChangePassword.tsx:148 msgid "If you want to change your password, we will send you a code to verify that this is your account." @@ -2185,7 +2064,7 @@ msgstr "Si vous souhaitez modifier votre mot de passe, nous vous enverrons un co #: src/lib/moderation/useReportOptions.ts:36 msgid "Illegal and Urgent" -msgstr "" +msgstr "Illégal et urgent" #: src/view/com/util/images/Gallery.tsx:38 msgid "Image" @@ -2195,14 +2074,9 @@ msgstr "Image" msgid "Image alt text" msgstr "Texte alt de l’image" -#: src/view/com/util/UserAvatar.tsx:311 -#: src/view/com/util/UserBanner.tsx:118 -#~ msgid "Image options" -#~ msgstr "Options d’images" - #: src/lib/moderation/useReportOptions.ts:47 msgid "Impersonation or false claims about identity or affiliation" -msgstr "" +msgstr "Usurpation d’identité ou fausses déclarations concernant l’identité ou l’affiliation" #: src/screens/Login/SetNewPasswordForm.tsx:127 msgid "Input code sent to your email for password reset" @@ -2212,14 +2086,6 @@ msgstr "Entrez le code envoyé à votre e-mail pour réinitialiser le mot de pas msgid "Input confirmation code for account deletion" msgstr "Entrez le code de confirmation pour supprimer le compte" -#: src/view/com/auth/create/Step1.tsx:177 -#~ msgid "Input email for Bluesky account" -#~ msgstr "Saisir l’email pour le compte Bluesky" - -#: src/view/com/auth/create/Step1.tsx:151 -#~ msgid "Input invite code to proceed" -#~ msgstr "Entrez le code d’invitation pour continuer" - #: src/view/com/modals/AddAppPasswords.tsx:181 msgid "Input name for app password" msgstr "Entrez le nom du mot de passe de l’appli" @@ -2246,7 +2112,7 @@ msgstr "Entrez votre mot de passe" #: src/view/com/modals/ChangeHandle.tsx:389 msgid "Input your preferred hosting provider" -msgstr "" +msgstr "Entrez votre hébergeur préféré" #: src/screens/Signup/StepHandle.tsx:63 msgid "Input your user handle" @@ -2294,35 +2160,35 @@ msgstr "Journalisme" #: src/components/moderation/LabelsOnMe.tsx:59 msgid "label has been placed on this {labelTarget}" -msgstr "" +msgstr "étiquette a été placée sur ce {labelTarget}" #: src/components/moderation/ContentHider.tsx:144 msgid "Labeled by {0}." -msgstr "" +msgstr "Étiqueté par {0}." #: src/components/moderation/ContentHider.tsx:142 msgid "Labeled by the author." -msgstr "" +msgstr "Étiqueté par l’auteur." #: src/view/screens/Profile.tsx:193 msgid "Labels" -msgstr "" +msgstr "Étiquettes" #: src/screens/Profile/Sections/Labels.tsx:153 msgid "Labels are annotations on users and content. They can be used to hide, warn, and categorize the network." -msgstr "" +msgstr "Les étiquettes sont des annotations sur les comptes et le contenu. Elles peuvent être utilisées pour masquer, avertir et catégoriser le réseau." #: src/components/moderation/LabelsOnMe.tsx:61 msgid "labels have been placed on this {labelTarget}" -msgstr "" +msgstr "étiquettes ont été placées sur ce {labelTarget}" #: src/components/moderation/LabelsOnMeDialog.tsx:62 msgid "Labels on your account" -msgstr "" +msgstr "Étiquettes sur votre compte" #: src/components/moderation/LabelsOnMeDialog.tsx:64 msgid "Labels on your content" -msgstr "" +msgstr "Étiquettes sur votre contenu" #: src/view/com/composer/select-language/SelectLangBtn.tsx:104 msgid "Language selection" @@ -2341,17 +2207,9 @@ msgstr "Paramètres linguistiques" msgid "Languages" msgstr "Langues" -#: src/view/com/auth/create/StepHeader.tsx:20 -#~ msgid "Last step!" -#~ msgstr "Dernière étape !" - #: src/view/screens/Search/Search.tsx:437 msgid "Latest" -msgstr "" - -#: src/view/com/util/moderation/ContentHider.tsx:103 -#~ msgid "Learn more" -#~ msgstr "En savoir plus" +msgstr "Dernier" #: src/components/moderation/ScreenHider.tsx:136 msgid "Learn More" @@ -2360,7 +2218,7 @@ msgstr "En savoir plus" #: src/components/moderation/ContentHider.tsx:65 #: src/components/moderation/ContentHider.tsx:128 msgid "Learn more about the moderation applied to this content." -msgstr "" +msgstr "En savoir plus sur la modération appliquée à ce contenu." #: src/components/moderation/PostHider.tsx:85 #: src/components/moderation/ScreenHider.tsx:125 @@ -2373,7 +2231,7 @@ msgstr "En savoir plus sur ce qui est public sur Bluesky." #: src/components/moderation/ContentHider.tsx:152 msgid "Learn more." -msgstr "" +msgstr "En savoir plus." #: src/view/com/modals/lang-settings/ContentLanguagesSettings.tsx:82 msgid "Leave them all unchecked to see any language." @@ -2400,11 +2258,6 @@ msgstr "Réinitialisez votre mot de passe !" msgid "Let's go!" msgstr "Allons-y !" -#: src/view/com/util/UserAvatar.tsx:248 -#: src/view/com/util/UserBanner.tsx:62 -#~ msgid "Library" -#~ msgstr "Bibliothèque" - #: src/view/screens/Settings/index.tsx:449 msgid "Light" msgstr "Clair" @@ -2436,7 +2289,7 @@ msgstr "Liké par {0} {1}" #: src/components/LabelingServiceCard/index.tsx:72 msgid "Liked by {count} {0}" -msgstr "" +msgstr "Liké par {count} {0}"" #: src/screens/Profile/Header/ProfileHeaderLabeler.tsx:284 #: src/screens/Profile/Header/ProfileHeaderLabeler.tsx:298 @@ -2505,11 +2358,6 @@ msgstr "Liste démasquée" msgid "Lists" msgstr "Listes" -#: src/view/com/post-thread/PostThread.tsx:333 -#: src/view/com/post-thread/PostThread.tsx:341 -#~ msgid "Load more posts" -#~ msgstr "Charger plus de posts" - #: src/view/screens/Notifications.tsx:159 msgid "Load new notifications" msgstr "Charger les nouvelles notifications" @@ -2546,7 +2394,7 @@ msgstr "Se connecter à un compte qui n’est pas listé" #: src/screens/Login/SetNewPasswordForm.tsx:116 msgid "Looks like XXXXX-XXXXX" -msgstr "" +msgstr "De la forme XXXXX-XXXXX" #: src/view/com/modals/LinkWarning.tsx:79 msgid "Make sure this is where you intend to go!" @@ -2556,14 +2404,6 @@ msgstr "Assurez-vous que c’est bien là que vous avez l’intention d’aller msgid "Manage your muted words and tags" msgstr "Gérer les mots et les mots-clés masqués" -#: src/view/com/auth/create/Step2.tsx:118 -#~ msgid "May not be longer than 253 characters" -#~ msgstr "Ne doit pas dépasser 253 caractères" - -#: src/view/com/auth/create/Step2.tsx:109 -#~ msgid "May only contain letters and numbers" -#~ msgstr "Ne peut contenir que des lettres et des chiffres" - #: src/view/screens/Profile.tsx:197 msgid "Media" msgstr "Média" @@ -2587,7 +2427,7 @@ msgstr "Message du serveur : {0}" #: src/lib/moderation/useReportOptions.ts:45 msgid "Misleading Account" -msgstr "" +msgstr "Compte trompeur" #: src/Navigation.tsx:119 #: src/screens/Moderation/index.tsx:104 @@ -2600,7 +2440,7 @@ msgstr "Modération" #: src/components/moderation/ModerationDetailsDialog.tsx:112 msgid "Moderation details" -msgstr "" +msgstr "Détails de la modération" #: src/view/com/lists/ListCard.tsx:93 #: src/view/com/modals/UserAddRemoveLists.tsx:206 @@ -2640,11 +2480,11 @@ msgstr "Paramètres de modération" #: src/Navigation.tsx:216 msgid "Moderation states" -msgstr "" +msgstr "États de modération" #: src/screens/Moderation/index.tsx:215 msgid "Moderation tools" -msgstr "" +msgstr "Outils de modération" #: src/components/moderation/ModerationDetailsDialog.tsx:48 #: src/lib/moderation/useModerationCauseDescription.ts:40 @@ -2653,7 +2493,7 @@ msgstr "La modération a choisi d’ajouter un avertissement général sur le co #: src/view/com/post-thread/PostThreadItem.tsx:535 msgid "More" -msgstr "" +msgstr "Plus" #: src/view/shell/desktop/Feeds.tsx:65 msgid "More feeds" @@ -2667,10 +2507,6 @@ msgstr "Plus d’options" msgid "Most-liked replies first" msgstr "Réponses les plus likées en premier" -#: src/view/com/auth/create/Step2.tsx:122 -#~ msgid "Must be at least 3 characters" -#~ msgstr "Doit comporter au moins 3 caractères" - #: src/components/TagMenu/index.tsx:249 msgid "Mute" msgstr "Masquer" @@ -2709,10 +2545,6 @@ msgstr "Masquer la liste" msgid "Mute these accounts?" msgstr "Masquer ces comptes ?" -#: src/view/screens/ProfileList.tsx:279 -#~ msgid "Mute this List" -#~ msgstr "Masquer cette liste" - #: src/components/dialogs/MutedWords.tsx:126 msgid "Mute this word in post text and tags" msgstr "Masquer ce mot dans le texte du post et les mots-clés" @@ -2750,7 +2582,7 @@ msgstr "Les comptes masqués voient leurs posts supprimés de votre fil d’actu #: src/lib/moderation/useModerationCauseDescription.ts:85 msgid "Muted by \"{0}\"" -msgstr "" +msgstr "Masqué par « {0} »" #: src/screens/Moderation/index.tsx:231 msgid "Muted words & tags" @@ -2775,16 +2607,12 @@ msgstr "Mon profil" #: src/view/screens/Settings/index.tsx:547 msgid "My saved feeds" -msgstr "" +msgstr "Mes fils d’actu enregistrés" #: src/view/screens/Settings/index.tsx:553 msgid "My Saved Feeds" msgstr "Mes fils d’actu enregistrés" -#: src/view/com/auth/server-input/index.tsx:118 -#~ msgid "my-server.com" -#~ msgstr "mon-serveur.fr" - #: src/view/com/modals/AddAppPasswords.tsx:180 #: src/view/com/modals/CreateOrEditList.tsx:291 msgid "Name" @@ -2798,7 +2626,7 @@ msgstr "Le nom est requis" #: src/lib/moderation/useReportOptions.ts:78 #: src/lib/moderation/useReportOptions.ts:86 msgid "Name or Description Violates Community Standards" -msgstr "" +msgstr "Nom ou description qui viole les normes communautaires" #: src/screens/Onboarding/index.tsx:25 msgid "Nature" @@ -2816,29 +2644,20 @@ msgstr "Navigue vers votre profil" #: src/components/ReportDialog/SelectReportOptionView.tsx:123 msgid "Need to report a copyright violation?" -msgstr "" - -#: src/view/com/modals/EmbedConsent.tsx:107 -#: src/view/com/modals/EmbedConsent.tsx:123 -#~ msgid "Never load embeds from {0}" -#~ msgstr "Ne jamais charger les contenus intégrés de {0}" +msgstr "Besoin de signaler une violation des droits d’auteur ?" #: src/view/com/auth/onboarding/WelcomeDesktop.tsx:72 #: src/view/com/auth/onboarding/WelcomeMobile.tsx:74 msgid "Never lose access to your followers and data." -msgstr "Ne perdez jamais l’accès à vos followers et à vos données." +msgstr "Ne perdez jamais l’accès à vos abonné·e·s et à vos données." #: src/screens/Onboarding/StepFinished.tsx:123 msgid "Never lose access to your followers or data." -msgstr "Ne perdez jamais l’accès à vos followers ou à vos données." - -#: src/components/dialogs/MutedWords.tsx:293 -#~ msgid "Nevermind" -#~ msgstr "Peu importe" +msgstr "Ne perdez jamais l’accès à vos abonné·e·s ou à vos données." #: src/view/com/modals/ChangeHandle.tsx:519 msgid "Nevermind, create a handle for me" -msgstr "" +msgstr "Peu importe, créez un pseudo pour moi" #: src/view/screens/Lists.tsx:76 msgctxt "action" @@ -2931,7 +2750,7 @@ msgstr "Aucune description" #: src/view/com/modals/ChangeHandle.tsx:405 msgid "No DNS Panel" -msgstr "" +msgstr "Pas de panneau DNS" #: src/screens/Profile/Header/ProfileHeaderStandard.tsx:118 msgid "No longer following {0}" @@ -2939,7 +2758,7 @@ msgstr "Ne suit plus {0}" #: src/screens/Signup/StepHandle.tsx:115 msgid "No longer than 253 characters" -msgstr "" +msgstr "Pas plus de 253 caractères" #: src/view/com/notifications/Feed.tsx:109 msgid "No notifications yet!" @@ -2976,11 +2795,11 @@ msgstr "Personne" #: src/components/LikedByList.tsx:79 #: src/components/LikesDialog.tsx:99 msgid "Nobody has liked this yet. Maybe you should be the first!" -msgstr "" +msgstr "Personne n’a encore liké. Peut-être devriez-vous ouvrir la voie !" #: src/lib/moderation/useGlobalLabelStrings.ts:42 msgid "Non-sexual Nudity" -msgstr "" +msgstr "Nudité non sexuelle" #: src/view/com/modals/SelfLabel.tsx:135 msgid "Not Applicable." @@ -3000,7 +2819,7 @@ msgstr "Pas maintenant" #: src/view/com/util/forms/PostDropdownBtn.tsx:364 #: src/view/com/util/post-ctrls/PostCtrls.tsx:248 msgid "Note about sharing" -msgstr "" +msgstr "Note sur le partage" #: src/screens/Moderation/index.tsx:540 msgid "Note: Bluesky is an open and public network. This setting only limits the visibility of your content on the Bluesky app and website, and other apps may not respect this setting. Your content may still be shown to logged-out users by other apps and websites." @@ -3022,19 +2841,15 @@ msgstr "Nudité" #: src/lib/moderation/useReportOptions.ts:71 msgid "Nudity or adult content not labeled as such" -msgstr "" - -#: src/lib/moderation/useReportOptions.ts:71 -#~ msgid "Nudity or pornography not labeled as such" -#~ msgstr "" +msgstr "Nudité ou contenu adulte non identifié comme tel" #: src/screens/Signup/index.tsx:143 msgid "of" -msgstr "" +msgstr "sur" #: src/lib/moderation/useLabelBehaviorDescription.ts:11 msgid "Off" -msgstr "" +msgstr "Éteint" #: src/view/com/util/ErrorBoundary.tsx:49 msgid "Oh no!" @@ -3047,7 +2862,7 @@ msgstr "Oh non ! Il y a eu un problème." #: src/screens/Onboarding/StepModeration/AdultContentEnabledPref.tsx:126 #: src/screens/Profile/Header/ProfileHeaderLabeler.tsx:333 msgid "OK" -msgstr "" +msgstr "OK" #: src/screens/Login/PasswordUpdatedForm.tsx:44 msgid "Okay" @@ -3071,7 +2886,7 @@ msgstr "Seul {0} peut répondre." #: src/screens/Signup/StepHandle.tsx:98 msgid "Only contains letters, numbers, and hyphens" -msgstr "" +msgstr "Ne contient que des lettres, des chiffres et des traits d’union" #: src/components/Lists.tsx:75 msgid "Oops, something went wrong!" @@ -3087,10 +2902,6 @@ msgstr "Oups !" msgid "Open" msgstr "Ouvrir" -#: src/view/screens/Moderation.tsx:75 -#~ msgid "Open content filtering settings" -#~ msgstr "Ouvrir les paramètres de filtrage de contenu" - #: src/view/com/composer/Composer.tsx:491 #: src/view/com/composer/Composer.tsx:492 msgid "Open emoji picker" @@ -3098,7 +2909,7 @@ msgstr "Ouvrir le sélecteur d’emoji" #: src/view/screens/ProfileFeed.tsx:311 msgid "Open feed options menu" -msgstr "" +msgstr "Ouvrir le menu des options de fil d’actu" #: src/view/screens/Settings/index.tsx:685 msgid "Open links with in-app browser" @@ -3106,11 +2917,7 @@ msgstr "Ouvrir des liens avec le navigateur interne à l’appli" #: src/screens/Moderation/index.tsx:227 msgid "Open muted words and tags settings" -msgstr "" - -#: src/view/screens/Moderation.tsx:92 -#~ msgid "Open muted words settings" -#~ msgstr "Ouvrir les paramètres des mots masqués" +msgstr "Ouvrir les paramètres des mots masqués et mots-clés" #: src/view/com/home/HomeHeaderLayoutMobile.tsx:52 msgid "Open navigation" @@ -3127,7 +2934,7 @@ msgstr "Ouvrir la page Storybook" #: src/view/screens/Settings/index.tsx:780 msgid "Open system log" -msgstr "" +msgstr "Ouvrir le journal du système" #: src/view/com/util/forms/DropdownButton.tsx:154 msgid "Opens {numItems} options" @@ -3157,10 +2964,6 @@ msgstr "Ouvre les paramètres linguistiques configurables" msgid "Opens device photo gallery" msgstr "Ouvre la galerie de photos de l’appareil" -#: src/view/com/profile/ProfileHeader.tsx:420 -#~ msgid "Opens editor for profile display name, avatar, background image, and description" -#~ msgstr "Ouvre l’éditeur pour le nom d’affichage du profil, l’avatar, l’image d’arrière-plan et la description" - #: src/view/screens/Settings/index.tsx:620 msgid "Opens external embeds settings" msgstr "Ouvre les paramètres d’intégration externe" @@ -3168,20 +2971,12 @@ msgstr "Ouvre les paramètres d’intégration externe" #: src/view/com/auth/SplashScreen.tsx:50 #: src/view/com/auth/SplashScreen.web.tsx:94 msgid "Opens flow to create a new Bluesky account" -msgstr "" +msgstr "Ouvre le flux de création d’un nouveau compte Bluesky" #: src/view/com/auth/SplashScreen.tsx:65 #: src/view/com/auth/SplashScreen.web.tsx:109 msgid "Opens flow to sign into your existing Bluesky account" -msgstr "" - -#: src/view/com/profile/ProfileHeader.tsx:575 -#~ msgid "Opens followers list" -#~ msgstr "Ouvre la liste des comptes abonnés" - -#: src/view/com/profile/ProfileHeader.tsx:594 -#~ msgid "Opens following list" -#~ msgstr "Ouvre la liste des abonnements" +msgstr "Ouvre le flux pour vous connecter à votre compte Bluesky existant" #: src/view/com/modals/InviteCodes.tsx:173 msgid "Opens list of invite codes" @@ -3189,27 +2984,23 @@ msgstr "Ouvre la liste des codes d’invitation" #: src/view/screens/Settings/index.tsx:762 msgid "Opens modal for account deletion confirmation. Requires email code" -msgstr "" - -#: src/view/screens/Settings/index.tsx:774 -#~ msgid "Opens modal for account deletion confirmation. Requires email code." -#~ msgstr "Ouvre la fenêtre modale pour confirmer la suppression du compte. Requiert un code e-mail." +msgstr "Ouvre la fenêtre modale pour confirmer la suppression du compte. Requiert un code e-mail." #: src/view/screens/Settings/index.tsx:720 msgid "Opens modal for changing your Bluesky password" -msgstr "" +msgstr "Ouvre une fenêtre modale pour changer le mot de passe de Bluesky" #: src/view/screens/Settings/index.tsx:669 msgid "Opens modal for choosing a new Bluesky handle" -msgstr "" +msgstr "Ouvre une fenêtre modale pour choisir un nouveau pseudo Bluesky" #: src/view/screens/Settings/index.tsx:743 msgid "Opens modal for downloading your Bluesky account data (repository)" -msgstr "" +msgstr "Ouvre une fenêtre modale pour télécharger les données du compte Bluesky (dépôt)" #: src/view/screens/Settings/index.tsx:932 msgid "Opens modal for email verification" -msgstr "" +msgstr "Ouvre une fenêtre modale pour la vérification de l’e-mail" #: src/view/com/modals/ChangeHandle.tsx:282 msgid "Opens modal for using custom domain" @@ -3234,23 +3025,15 @@ msgstr "Ouvre l’écran avec tous les fils d’actu enregistrés" #: src/view/screens/Settings/index.tsx:647 msgid "Opens the app password settings" -msgstr "" - -#: src/view/screens/Settings/index.tsx:676 -#~ msgid "Opens the app password settings page" -#~ msgstr "Ouvre la page de configuration du mot de passe" +msgstr "Ouvre les paramètres du mot de passe de l’application" #: src/view/screens/Settings/index.tsx:505 msgid "Opens the Following feed preferences" -msgstr "" - -#: src/view/screens/Settings/index.tsx:535 -#~ msgid "Opens the home feed preferences" -#~ msgstr "Ouvre les préférences du fil d’accueil" +msgstr "Ouvre les préférences du fil d’actu « Following »" #: src/view/com/modals/LinkWarning.tsx:93 msgid "Opens the linked website" -msgstr "" +msgstr "Ouvre le site web lié" #: src/view/screens/Settings/index.tsx:793 #: src/view/screens/Settings/index.tsx:803 @@ -3271,7 +3054,7 @@ msgstr "Option {0} sur {numItems}" #: src/components/ReportDialog/SubmitView.tsx:160 msgid "Optionally provide additional information below:" -msgstr "" +msgstr "Ajoutez des informations supplémentaires ci-dessous (optionnel) :" #: src/view/com/modals/Threadgate.tsx:89 msgid "Or combine these options:" @@ -3279,7 +3062,7 @@ msgstr "Ou une combinaison de ces options :" #: src/lib/moderation/useReportOptions.ts:25 msgid "Other" -msgstr "" +msgstr "Autre" #: src/components/AccountList.tsx:73 msgid "Other account" @@ -3307,7 +3090,7 @@ msgstr "Mot de passe" #: src/view/com/modals/ChangePassword.tsx:142 msgid "Password Changed" -msgstr "" +msgstr "Mot de passe modifié" #: src/screens/Login/index.tsx:157 msgid "Password updated" @@ -3320,7 +3103,7 @@ msgstr "Mot de passe mis à jour !" #: src/view/screens/Search/Search.tsx:447 #: src/view/screens/Search/Search.tsx:456 msgid "People" -msgstr "" +msgstr "Personnes" #: src/Navigation.tsx:164 msgid "People followed by @{0}" @@ -3353,7 +3136,7 @@ msgstr "Ajouter à l’accueil" #: src/view/screens/ProfileFeed.tsx:306 msgid "Pin to Home" -msgstr "" +msgstr "Ajouter à l’accueil" #: src/view/screens/SavedFeeds.tsx:89 msgid "Pinned Feeds" @@ -3410,12 +3193,7 @@ msgstr "Veuillez également entrer votre mot de passe :" #: src/components/moderation/LabelsOnMeDialog.tsx:221 msgid "Please explain why you think this label was incorrectly applied by {0}" -msgstr "" - -#: src/view/com/modals/AppealLabel.tsx:72 -#: src/view/com/modals/AppealLabel.tsx:75 -#~ msgid "Please tell us why you think this content warning was incorrectly applied!" -#~ msgstr "Dites-nous donc pourquoi vous pensez que cet avertissement de contenu a été appliqué à tort !" +msgstr "Veuillez expliquer pourquoi vous pensez que cette étiquette a été appliquée à tort par {0}" #: src/view/com/modals/VerifyEmail.tsx:101 msgid "Please Verify Your Email" @@ -3433,10 +3211,6 @@ msgstr "Politique" msgid "Porn" msgstr "Porno" -#: src/lib/moderation/useGlobalLabelStrings.ts:34 -#~ msgid "Pornography" -#~ msgstr "" - #: src/view/com/composer/Composer.tsx:367 #: src/view/com/composer/Composer.tsx:375 msgctxt "action" @@ -3469,12 +3243,12 @@ msgstr "Post caché" #: src/components/moderation/ModerationDetailsDialog.tsx:97 #: src/lib/moderation/useModerationCauseDescription.ts:99 msgid "Post Hidden by Muted Word" -msgstr "" +msgstr "Post caché par mot masqué" #: src/components/moderation/ModerationDetailsDialog.tsx:100 #: src/lib/moderation/useModerationCauseDescription.ts:108 msgid "Post Hidden by You" -msgstr "" +msgstr "Post caché par vous" #: src/view/com/composer/select-language/SelectLangBtn.tsx:87 msgid "Post language" @@ -3512,13 +3286,13 @@ msgstr "Lien potentiellement trompeur" #: src/components/forms/HostingProvider.tsx:46 msgid "Press to change hosting provider" -msgstr "" +msgstr "Appuyer pour changer d’hébergeur" #: src/components/Error.tsx:74 #: src/components/Lists.tsx:80 #: src/screens/Signup/index.tsx:187 msgid "Press to retry" -msgstr "" +msgstr "Appuyer pour réessayer" #: src/view/com/lightbox/Lightbox.web.tsx:150 msgid "Previous image" @@ -3552,7 +3326,7 @@ msgstr "Traitement…" #: src/view/screens/DebugMod.tsx:888 #: src/view/screens/Profile.tsx:361 msgid "profile" -msgstr "" +msgstr "profil" #: src/view/shell/bottom-bar/BottomBar.tsx:261 #: src/view/shell/desktop/LeftNav.tsx:419 @@ -3614,7 +3388,7 @@ msgstr "Ratios" #: src/view/screens/Search/Search.tsx:924 msgid "Recent Searches" -msgstr "" +msgstr "Recherches récentes" #: src/view/com/auth/onboarding/RecommendedFeeds.tsx:117 msgid "Recommended Feeds" @@ -3633,29 +3407,25 @@ msgstr "Comptes recommandés" msgid "Remove" msgstr "Supprimer" -#: src/view/com/feeds/FeedSourceCard.tsx:108 -#~ msgid "Remove {0} from my feeds?" -#~ msgstr "Supprimer {0} de mes fils d’actu ?" - #: src/view/com/util/AccountDropdownBtn.tsx:22 msgid "Remove account" msgstr "Supprimer compte" #: src/view/com/util/UserAvatar.tsx:360 msgid "Remove Avatar" -msgstr "" +msgstr "Supprimer l’avatar" #: src/view/com/util/UserBanner.tsx:148 msgid "Remove Banner" -msgstr "" +msgstr "Supprimer l’image d’en-tête" #: src/view/com/posts/FeedErrorMessage.tsx:160 msgid "Remove feed" -msgstr "Supprimer fil d’actu" +msgstr "Supprimer le fil d’actu" #: src/view/com/posts/FeedErrorMessage.tsx:201 msgid "Remove feed?" -msgstr "" +msgstr "Supprimer le fil d’actu ?" #: src/view/com/feeds/FeedSourceCard.tsx:173 #: src/view/com/feeds/FeedSourceCard.tsx:233 @@ -3666,7 +3436,7 @@ msgstr "Supprimer de mes fils d’actu" #: src/view/com/feeds/FeedSourceCard.tsx:278 msgid "Remove from my feeds?" -msgstr "" +msgstr "Supprimer de mes fils d’actu ?" #: src/view/com/composer/photos/Gallery.tsx:167 msgid "Remove image" @@ -3684,17 +3454,9 @@ msgstr "Supprimer le mot masqué de votre liste" msgid "Remove repost" msgstr "Supprimer le repost" -#: src/view/com/feeds/FeedSourceCard.tsx:175 -#~ msgid "Remove this feed from my feeds?" -#~ msgstr "Supprimer ce fil d’actu ?" - #: src/view/com/posts/FeedErrorMessage.tsx:202 msgid "Remove this feed from your saved feeds" -msgstr "" - -#: src/view/com/posts/FeedErrorMessage.tsx:132 -#~ msgid "Remove this feed from your saved feeds?" -#~ msgstr "Supprimer ce fil d’actu de vos fils d’actu enregistrés ?" +msgstr "Supprimer ce fil d’actu de vos fils d’actu enregistrés" #: src/view/com/modals/ListAddRemoveUsers.tsx:199 #: src/view/com/modals/UserAddRemoveLists.tsx:152 @@ -3707,7 +3469,7 @@ msgstr "Supprimé de mes fils d’actu" #: src/view/screens/ProfileFeed.tsx:210 msgid "Removed from your feeds" -msgstr "" +msgstr "Supprimé de vos fils d’actu" #: src/view/com/composer/ExternalEmbed.tsx:71 msgid "Removes default thumbnail from {0}" @@ -3736,10 +3498,6 @@ msgctxt "description" msgid "Reply to <0/>" msgstr "Réponse à <0/>" -#: src/view/com/modals/report/Modal.tsx:166 -#~ msgid "Report {collectionName}" -#~ msgstr "Signaler {collectionName}" - #: src/view/com/profile/ProfileMenu.tsx:319 #: src/view/com/profile/ProfileMenu.tsx:322 msgid "Report Account" @@ -3747,7 +3505,7 @@ msgstr "Signaler le compte" #: src/components/ReportDialog/index.tsx:49 msgid "Report dialog" -msgstr "" +msgstr "Fenêtre de dialogue de signalement" #: src/view/screens/ProfileFeed.tsx:363 #: src/view/screens/ProfileFeed.tsx:365 @@ -3765,23 +3523,23 @@ msgstr "Signaler le post" #: src/components/ReportDialog/SelectReportOptionView.tsx:42 msgid "Report this content" -msgstr "" +msgstr "Signaler ce contenu" #: src/components/ReportDialog/SelectReportOptionView.tsx:55 msgid "Report this feed" -msgstr "" +msgstr "Signaler ce fil d’actu" #: src/components/ReportDialog/SelectReportOptionView.tsx:52 msgid "Report this list" -msgstr "" +msgstr "Signaler cette liste" #: src/components/ReportDialog/SelectReportOptionView.tsx:49 msgid "Report this post" -msgstr "" +msgstr "Signaler ce post" #: src/components/ReportDialog/SelectReportOptionView.tsx:46 msgid "Report this user" -msgstr "" +msgstr "Signaler ce compte" #: src/view/com/modals/Repost.tsx:44 #: src/view/com/modals/Repost.tsx:49 @@ -3808,13 +3566,9 @@ msgstr "Republié par" msgid "Reposted by {0}" msgstr "Republié par {0}" -#: src/view/com/posts/FeedItem.tsx:214 -#~ msgid "Reposted by <0/>" -#~ msgstr "Republié par <0/>" - #: src/view/com/posts/FeedItem.tsx:216 msgid "Reposted by <0><1/>" -msgstr "" +msgstr "Republié par <0><1/>" #: src/view/com/notifications/FeedItem.tsx:168 msgid "reposted your post" @@ -3850,10 +3604,6 @@ msgstr "Réinitialiser le code" msgid "Reset Code" msgstr "Code de réinitialisation" -#: src/view/screens/Settings/index.tsx:824 -#~ msgid "Reset onboarding" -#~ msgstr "Réinitialiser le didacticiel" - #: src/view/screens/Settings/index.tsx:822 #: src/view/screens/Settings/index.tsx:825 msgid "Reset onboarding state" @@ -3863,10 +3613,6 @@ msgstr "Réinitialisation du didacticiel" msgid "Reset password" msgstr "Réinitialiser mot de passe" -#: src/view/screens/Settings/index.tsx:814 -#~ msgid "Reset preferences" -#~ msgstr "Réinitialiser les préférences" - #: src/view/screens/Settings/index.tsx:812 #: src/view/screens/Settings/index.tsx:815 msgid "Reset preferences state" @@ -3908,12 +3654,12 @@ msgstr "Retourne à la page précédente" #: src/view/screens/NotFound.tsx:59 msgid "Returns to home page" -msgstr "" +msgstr "Retour à la page d’accueil" #: src/view/screens/NotFound.tsx:58 #: src/view/screens/ProfileFeed.tsx:113 msgid "Returns to previous page" -msgstr "" +msgstr "Retour à la page précédente" #: src/components/dialogs/BirthDateSettings.tsx:125 #: src/view/com/modals/ChangeHandle.tsx:174 @@ -3934,7 +3680,7 @@ msgstr "Enregistrer le texte alt" #: src/components/dialogs/BirthDateSettings.tsx:119 msgid "Save birthday" -msgstr "" +msgstr "Enregistrer la date de naissance" #: src/view/com/modals/EditProfile.tsx:233 msgid "Save Changes" @@ -3951,7 +3697,7 @@ msgstr "Enregistrer le recadrage de l’image" #: src/view/screens/ProfileFeed.tsx:347 #: src/view/screens/ProfileFeed.tsx:353 msgid "Save to my feeds" -msgstr "" +msgstr "Enregistrer dans mes fils d’actu" #: src/view/screens/SavedFeeds.tsx:123 msgid "Saved Feeds" @@ -3959,11 +3705,11 @@ msgstr "Fils d’actu enregistrés" #: src/view/com/lightbox/Lightbox.tsx:81 msgid "Saved to your camera roll." -msgstr "" +msgstr "Enregistré dans votre photothèque" #: src/view/screens/ProfileFeed.tsx:214 msgid "Saved to your feeds" -msgstr "" +msgstr "Enregistré à mes fils d’actu" #: src/view/com/modals/EditProfile.tsx:226 msgid "Saves any changes to your profile" @@ -3975,7 +3721,7 @@ msgstr "Enregistre le changement de pseudo en {handle}" #: src/view/com/modals/crop-image/CropImage.web.tsx:146 msgid "Saves image crop settings" -msgstr "" +msgstr "Enregistre les paramètres de recadrage de l’image" #: src/screens/Onboarding/index.tsx:36 msgid "Science" @@ -4044,23 +3790,19 @@ msgstr "Voir les posts <0>{displayTag} de ce compte" #: src/view/com/notifications/FeedItem.tsx:419 #: src/view/com/util/UserAvatar.tsx:381 msgid "See profile" -msgstr "" +msgstr "Voir le profil" #: src/view/screens/SavedFeeds.tsx:164 msgid "See this guide" msgstr "Voir ce guide" -#: src/view/com/auth/HomeLoggedOutCTA.tsx:40 -#~ msgid "See what's next" -#~ msgstr "Voir la suite" - #: src/view/com/util/Selector.tsx:106 msgid "Select {item}" msgstr "Sélectionner {item}" #: src/screens/Login/ChooseAccountForm.tsx:61 msgid "Select account" -msgstr "" +msgstr "Sélectionner un compte" #: src/screens/Login/index.tsx:120 msgid "Select from an existing account" @@ -4068,28 +3810,23 @@ msgstr "Sélectionner un compte existant" #: src/view/screens/LanguageSettings.tsx:299 msgid "Select languages" -msgstr "" +msgstr "Sélectionner les langues" #: src/components/ReportDialog/SelectLabelerView.tsx:30 msgid "Select moderator" -msgstr "" +msgstr "Sélectionner une modération" #: src/view/com/util/Selector.tsx:107 msgid "Select option {i} of {numItems}" msgstr "Sélectionne l’option {i} sur {numItems}" -#: src/view/com/auth/create/Step1.tsx:96 -#: src/view/com/auth/login/LoginForm.tsx:153 -#~ msgid "Select service" -#~ msgstr "Sélectionner un service" - #: src/screens/Onboarding/StepSuggestedAccounts/index.tsx:52 msgid "Select some accounts below to follow" msgstr "Sélectionnez quelques comptes à suivre ci-dessous" #: src/components/ReportDialog/SubmitView.tsx:133 msgid "Select the moderation service(s) to report to" -msgstr "" +msgstr "Sélectionnez le(s) service(s) de modération destinataires du signalement" #: src/view/com/auth/server-input/index.tsx:82 msgid "Select the service that hosts your data." @@ -4107,17 +3844,13 @@ msgstr "Sélectionnez ce que vous voulez voir (ou ne pas voir), et nous nous occ msgid "Select which languages you want your subscribed feeds to include. If none are selected, all languages will be shown." msgstr "Sélectionnez les langues que vous souhaitez voir figurer dans les fils d’actu que vous suivez. Si aucune langue n’est sélectionnée, toutes les langues seront affichées." -#: src/view/screens/LanguageSettings.tsx:98 -#~ msgid "Select your app language for the default text to display in the app" -#~ msgstr "Sélectionnez la langue de votre application à afficher par défaut" - #: src/view/screens/LanguageSettings.tsx:98 msgid "Select your app language for the default text to display in the app." -msgstr "" +msgstr "Sélectionnez votre langue par défaut pour les textes de l’application." #: src/screens/Signup/StepInfo/index.tsx:135 msgid "Select your date of birth" -msgstr "" +msgstr "Sélectionnez votre date de naissance" #: src/screens/Onboarding/StepInterests/index.tsx:200 msgid "Select your interests from the options below" @@ -4157,15 +3890,11 @@ msgstr "Envoyer des commentaires" #: src/components/ReportDialog/SubmitView.tsx:213 #: src/components/ReportDialog/SubmitView.tsx:217 msgid "Send report" -msgstr "" - -#: src/view/com/modals/report/SendReportButton.tsx:45 -#~ msgid "Send Report" -#~ msgstr "Envoyer le rapport" +msgstr "Envoyer le rapport" #: src/components/ReportDialog/SelectLabelerView.tsx:44 msgid "Send report to {0}" -msgstr "" +msgstr "Envoyer le rapport à {0}" #: src/view/com/modals/DeleteAccount.tsx:132 msgid "Sends email with confirmation code for account deletion" @@ -4175,48 +3904,14 @@ msgstr "Envoie un e-mail avec le code de confirmation pour la suppression du com msgid "Server address" msgstr "Adresse du serveur" -#: src/view/com/modals/ContentFilteringSettings.tsx:311 -#~ msgid "Set {value} for {labelGroup} content moderation policy" -#~ msgstr "Choisis {value} pour la politique de modération de contenu {labelGroup}" - -#: src/view/com/modals/ContentFilteringSettings.tsx:160 -#: src/view/com/modals/ContentFilteringSettings.tsx:179 -#~ msgctxt "action" -#~ msgid "Set Age" -#~ msgstr "Enregistrer l’âge" - #: src/screens/Moderation/index.tsx:304 msgid "Set birthdate" -msgstr "" - -#: src/view/screens/Settings/index.tsx:488 -#~ msgid "Set color theme to dark" -#~ msgstr "Change le thème de couleur en sombre" - -#: src/view/screens/Settings/index.tsx:481 -#~ msgid "Set color theme to light" -#~ msgstr "Change le thème de couleur en clair" - -#: src/view/screens/Settings/index.tsx:475 -#~ msgid "Set color theme to system setting" -#~ msgstr "Change le thème de couleur en fonction du paramètre système" - -#: src/view/screens/Settings/index.tsx:514 -#~ msgid "Set dark theme to the dark theme" -#~ msgstr "Choisir le thème le plus sombre comme thème sombre" - -#: src/view/screens/Settings/index.tsx:507 -#~ msgid "Set dark theme to the dim theme" -#~ msgstr "Choisir le thème atténué comme thème sombre" +msgstr "Entrez votre date de naissance" #: src/screens/Login/SetNewPasswordForm.tsx:102 msgid "Set new password" msgstr "Définir un nouveau mot de passe" -#: src/view/com/auth/create/Step1.tsx:202 -#~ msgid "Set password" -#~ msgstr "Définit le mot de passe" - #: src/view/screens/PreferencesFollowingFeed.tsx:225 msgid "Set this setting to \"No\" to hide all quote posts from your feed. Reposts will still be visible." msgstr "Choisissez « Non » pour cacher toutes les citations sur votre fils d’actu. Les reposts seront toujours visibles." @@ -4247,48 +3942,39 @@ msgstr "Définit le pseudo Bluesky" #: src/view/screens/Settings/index.tsx:458 msgid "Sets color theme to dark" -msgstr "" +msgstr "Change le thème de couleur en sombre" #: src/view/screens/Settings/index.tsx:451 msgid "Sets color theme to light" -msgstr "" +msgstr "Change le thème de couleur en clair" #: src/view/screens/Settings/index.tsx:445 msgid "Sets color theme to system setting" -msgstr "" +msgstr "Change le thème de couleur en fonction du paramètre système" #: src/view/screens/Settings/index.tsx:484 msgid "Sets dark theme to the dark theme" -msgstr "" +msgstr "Change le thème sombre comme étant le plus sombre" #: src/view/screens/Settings/index.tsx:477 msgid "Sets dark theme to the dim theme" -msgstr "" +msgstr "Change le thème sombre comme étant le thème atténué" #: src/screens/Login/ForgotPasswordForm.tsx:113 msgid "Sets email for password reset" msgstr "Définit l’e-mail pour la réinitialisation du mot de passe" -#: src/view/com/auth/login/ForgotPasswordForm.tsx:122 -#~ msgid "Sets hosting provider for password reset" -#~ msgstr "Définit l’hébergeur pour la réinitialisation du mot de passe" - #: src/view/com/modals/crop-image/CropImage.web.tsx:124 msgid "Sets image aspect ratio to square" -msgstr "" +msgstr "Définit le rapport d’aspect de l’image comme étant carré" #: src/view/com/modals/crop-image/CropImage.web.tsx:114 msgid "Sets image aspect ratio to tall" -msgstr "" +msgstr "Définit le rapport d’aspect de l’image comme portrait" #: src/view/com/modals/crop-image/CropImage.web.tsx:104 msgid "Sets image aspect ratio to wide" -msgstr "" - -#: src/view/com/auth/create/Step1.tsx:97 -#: src/view/com/auth/login/LoginForm.tsx:154 -#~ msgid "Sets server for the Bluesky client" -#~ msgstr "Définit le serveur pour le client Bluesky" +msgstr "Définit le rapport d’aspect de l’image comme paysage" #: src/Navigation.tsx:139 #: src/view/screens/Settings/index.tsx:316 @@ -4304,7 +3990,7 @@ msgstr "Activité sexuelle ou nudité érotique." #: src/lib/moderation/useGlobalLabelStrings.ts:38 msgid "Sexually Suggestive" -msgstr "" +msgstr "Sexuellement suggestif" #: src/view/com/lightbox/Lightbox.tsx:141 msgctxt "action" @@ -4324,7 +4010,7 @@ msgstr "Partager" #: src/view/com/util/forms/PostDropdownBtn.tsx:369 #: src/view/com/util/post-ctrls/PostCtrls.tsx:253 msgid "Share anyway" -msgstr "" +msgstr "Partager quand même" #: src/view/screens/ProfileFeed.tsx:373 #: src/view/screens/ProfileFeed.tsx:375 @@ -4334,11 +4020,11 @@ msgstr "Partager le fil d’actu" #: src/view/com/modals/LinkWarning.tsx:89 #: src/view/com/modals/LinkWarning.tsx:95 msgid "Share Link" -msgstr "" +msgstr "Partager le lien" #: src/view/com/modals/LinkWarning.tsx:92 msgid "Shares the linked website" -msgstr "" +msgstr "Partage le site web lié" #: src/components/moderation/ContentHider.tsx:115 #: src/components/moderation/LabelPreference.tsx:136 @@ -4360,15 +4046,11 @@ msgstr "Afficher quand même" #: src/lib/moderation/useLabelBehaviorDescription.ts:27 #: src/lib/moderation/useLabelBehaviorDescription.ts:63 msgid "Show badge" -msgstr "" +msgstr "Afficher le badge" #: src/lib/moderation/useLabelBehaviorDescription.ts:61 msgid "Show badge and filter from feeds" -msgstr "" - -#: src/view/com/modals/EmbedConsent.tsx:87 -#~ msgid "Show embeds from {0}" -#~ msgstr "Afficher les intégrations de {0}" +msgstr "Afficher les badges et filtrer des fils d’actu" #: src/screens/Profile/Header/ProfileHeaderStandard.tsx:200 msgid "Show follows similar to {0}" @@ -4439,15 +4121,11 @@ msgstr "Afficher les comptes" #: src/lib/moderation/useLabelBehaviorDescription.ts:58 msgid "Show warning" -msgstr "" +msgstr "Afficher l’avertissement" #: src/lib/moderation/useLabelBehaviorDescription.ts:56 msgid "Show warning and filter from feeds" -msgstr "" - -#: src/view/com/profile/ProfileHeader.tsx:462 -#~ msgid "Shows a list of users similar to this user." -#~ msgstr "Affiche une liste de comptes similaires à ce compte." +msgstr "Afficher l’avertissement et filtrer des fils d’actu" #: src/view/com/post-thread/PostThreadFollowBtn.tsx:130 msgid "Shows posts from {0} in your feed" @@ -4474,12 +4152,6 @@ msgstr "Affiche les posts de {0} dans votre fil d’actu" msgid "Sign in" msgstr "Connexion" -#: src/view/com/auth/HomeLoggedOutCTA.tsx:82 -#: src/view/com/auth/SplashScreen.tsx:86 -#: src/view/com/auth/SplashScreen.web.tsx:91 -#~ msgid "Sign In" -#~ msgstr "Connexion" - #: src/components/AccountList.tsx:109 msgid "Sign in as {0}" msgstr "Se connecter en tant que {0}" @@ -4490,15 +4162,11 @@ msgstr "Se connecter en tant que…" #: src/components/dialogs/Signin.tsx:75 msgid "Sign in or create your account to join the conversation!" -msgstr "" - -#: src/view/com/auth/login/LoginForm.tsx:140 -#~ msgid "Sign into" -#~ msgstr "Se connecter à" +msgstr "Connectez-vous ou créez votre compte pour participer à la conversation !" #: src/components/dialogs/Signin.tsx:46 msgid "Sign into Bluesky or create a new account" -msgstr "" +msgstr "Connectez-vous à Bluesky ou créez un nouveau compte" #: src/view/screens/Settings/index.tsx:118 #: src/view/screens/Settings/index.tsx:121 @@ -4534,10 +4202,6 @@ msgstr "Connecté en tant que" msgid "Signed in as @{0}" msgstr "Connecté en tant que @{0}" -#: src/view/com/modals/SwitchAccount.tsx:70 -#~ msgid "Signs {0} out of Bluesky" -#~ msgstr "Déconnecte {0} de Bluesky" - #: src/screens/Onboarding/StepInterests/index.tsx:239 #: src/screens/Onboarding/StepSuggestedAccounts/index.tsx:203 #: src/view/com/auth/onboarding/WelcomeMobile.tsx:35 @@ -4556,11 +4220,7 @@ msgstr "Développement de logiciels" #: src/screens/Moderation/index.tsx:114 #: src/screens/Profile/Sections/Labels.tsx:87 msgid "Something went wrong, please try again." -msgstr "" - -#: src/components/Lists.tsx:203 -#~ msgid "Something went wrong!" -#~ msgstr "Quelque chose n’a pas marché !" +msgstr "Quelque chose n’a pas marché, veuillez réessayer." #: src/App.native.tsx:64 msgid "Sorry! Your session expired. Please log in again." @@ -4576,15 +4236,15 @@ msgstr "Trier les réponses au même post par :" #: src/components/moderation/LabelsOnMeDialog.tsx:146 msgid "Source:" -msgstr "" +msgstr "Source :" #: src/lib/moderation/useReportOptions.ts:65 msgid "Spam" -msgstr "" +msgstr "Spam" #: src/lib/moderation/useReportOptions.ts:53 msgid "Spam; excessive mentions or replies" -msgstr "" +msgstr "Spam ; mentions ou réponses excessives" #: src/screens/Onboarding/index.tsx:30 msgid "Sports" @@ -4600,11 +4260,7 @@ msgstr "État du service" #: src/screens/Signup/index.tsx:143 msgid "Step" -msgstr "" - -#: src/view/com/auth/create/StepHeader.tsx:22 -#~ msgid "Step {0} of {numSteps}" -#~ msgstr "Étape {0} sur {numSteps}" +msgstr "Étape" #: src/view/screens/Settings/index.tsx:295 msgid "Storage cleared, you need to restart the app now." @@ -4626,11 +4282,11 @@ msgstr "S’abonner" #: src/screens/Profile/Sections/Labels.tsx:191 msgid "Subscribe to @{0} to use these labels:" -msgstr "" +msgstr "Abonnez-vous à @{0} pour utiliser ces étiquettes :" #: src/screens/Profile/Header/ProfileHeaderLabeler.tsx:227 msgid "Subscribe to Labeler" -msgstr "" +msgstr "S’abonner à l’étiqueteur" #: src/screens/Onboarding/StepAlgoFeeds/FeedCard.tsx:172 #: src/screens/Onboarding/StepAlgoFeeds/FeedCard.tsx:307 @@ -4639,7 +4295,7 @@ msgstr "S’abonner au fil d’actu {0}" #: src/screens/Profile/Header/ProfileHeaderLabeler.tsx:191 msgid "Subscribe to this labeler" -msgstr "" +msgstr "S’abonner à cet étiqueteur" #: src/view/screens/ProfileList.tsx:588 msgid "Subscribe to this list" @@ -4720,7 +4376,7 @@ msgstr "Conditions d’utilisation" #: src/lib/moderation/useReportOptions.ts:79 #: src/lib/moderation/useReportOptions.ts:87 msgid "Terms used violate community standards" -msgstr "" +msgstr "Termes utilisés qui violent les normes de la communauté" #: src/components/dialogs/MutedWords.tsx:323 msgid "text" @@ -4732,11 +4388,11 @@ msgstr "Champ de saisie de texte" #: src/components/ReportDialog/SubmitView.tsx:76 msgid "Thank you. Your report has been sent." -msgstr "" +msgstr "Nous vous remercions. Votre rapport a été envoyé." #: src/view/com/modals/ChangeHandle.tsx:465 msgid "That contains the following:" -msgstr "" +msgstr "Qui contient les éléments suivants :" #: src/screens/Signup/index.tsx:85 msgid "That handle is already taken." @@ -4749,7 +4405,7 @@ msgstr "Ce compte pourra interagir avec vous après le déblocage." #: src/components/moderation/ModerationDetailsDialog.tsx:127 msgid "the author" -msgstr "" +msgstr "l’auteur" #: src/view/screens/CommunityGuidelines.tsx:36 msgid "The Community Guidelines have been moved to <0/>" @@ -4761,11 +4417,11 @@ msgstr "Notre politique de droits d’auteur a été déplacée vers <0/>" #: src/components/moderation/LabelsOnMeDialog.tsx:48 msgid "The following labels were applied to your account." -msgstr "" +msgstr "Les étiquettes suivantes ont été appliquées à votre compte." #: src/components/moderation/LabelsOnMeDialog.tsx:49 msgid "The following labels were applied to your content." -msgstr "" +msgstr "Les étiquettes suivantes ont été appliquées à votre contenu." #: src/screens/Onboarding/Layout.tsx:58 msgid "The following steps will help customize your Bluesky experience." @@ -4839,7 +4495,7 @@ msgstr "Il y a eu un problème lors de la récupération de vos listes. Appuyez #: src/components/ReportDialog/SubmitView.tsx:81 msgid "There was an issue sending your report. Please check your internet connection." -msgstr "" +msgstr "Il y a eu un problème lors de l’envoi de votre rapport. Veuillez vérifier votre connexion internet." #: src/screens/Onboarding/StepModeration/AdultContentEnabledPref.tsx:65 msgid "There was an issue syncing your preferences with the server" @@ -4892,15 +4548,15 @@ msgstr "Ce compte a demandé aux personnes de se connecter pour voir son profil. #: src/components/moderation/LabelsOnMeDialog.tsx:204 msgid "This appeal will be sent to <0>{0}." -msgstr "" +msgstr "Cet appel sera envoyé à <0>{0}." #: src/lib/moderation/useGlobalLabelStrings.ts:19 msgid "This content has been hidden by the moderators." -msgstr "" +msgstr "Ce contenu a été masqué par la modération." #: src/lib/moderation/useGlobalLabelStrings.ts:24 msgid "This content has received a general warning from moderators." -msgstr "" +msgstr "Ce contenu a reçu un avertissement général de la part de la modération." #: src/components/dialogs/EmbedConsent.tsx:64 msgid "This content is hosted by {0}. Do you want to enable external media?" @@ -4915,13 +4571,9 @@ msgstr "Ce contenu n’est pas disponible car l’un des comptes impliqués a bl msgid "This content is not viewable without a Bluesky account." msgstr "Ce contenu n’est pas visible sans un compte Bluesky." -#: src/view/screens/Settings/ExportCarDialog.tsx:75 -#~ msgid "This feature is in beta. You can read more about repository exports in <0>this blogpost." -#~ msgstr "Cette fonctionnalité est en version bêta. Vous pouvez en savoir plus sur les exportations de dépôts dans <0>ce blogpost." - #: src/view/screens/Settings/ExportCarDialog.tsx:75 msgid "This feature is in beta. You can read more about repository exports in <0>this blogpost." -msgstr "" +msgstr "Cette fonctionnalité est en version bêta. Vous pouvez en savoir plus sur les exportations de dépôts dans <0>ce blogpost." #: src/view/com/posts/FeedErrorMessage.tsx:114 msgid "This feed is currently receiving high traffic and is temporarily unavailable. Please try again later." @@ -4947,11 +4599,11 @@ msgstr "Ceci est important au cas où vous auriez besoin de changer d’e-mail o #: src/components/moderation/ModerationDetailsDialog.tsx:124 msgid "This label was applied by {0}." -msgstr "" +msgstr "Cette étiquette a été apposée par {0}." #: src/screens/Profile/Sections/Labels.tsx:178 msgid "This labeler hasn't declared what labels it publishes, and may not be active." -msgstr "" +msgstr "Cet étiqueteur n’a pas déclaré les étiquettes qu’il publie et peut ne pas être actif." #: src/view/com/modals/LinkWarning.tsx:72 msgid "This link is taking you to the following website:" @@ -4963,7 +4615,7 @@ msgstr "Cette liste est vide !" #: src/screens/Profile/ErrorState.tsx:40 msgid "This moderation service is unavailable. See below for more details. If this issue persists, contact us." -msgstr "" +msgstr "Ce service de modération n’est pas disponible. Voir ci-dessous pour plus de détails. Si le problème persiste, contactez-nous." #: src/view/com/modals/AddAppPasswords.tsx:107 msgid "This name is already in use" @@ -4976,27 +4628,27 @@ msgstr "Ce post a été supprimé." #: src/view/com/util/forms/PostDropdownBtn.tsx:366 #: src/view/com/util/post-ctrls/PostCtrls.tsx:250 msgid "This post is only visible to logged-in users. It won't be visible to people who aren't logged in." -msgstr "" +msgstr "Ce post n’est visible que pour les personnes connectées. Il ne sera pas visible pour les personnes qui ne sont pas connectées." #: src/view/com/util/forms/PostDropdownBtn.tsx:348 msgid "This post will be hidden from feeds." -msgstr "" +msgstr "Ce post sera masqué des fils d’actu." #: src/view/com/profile/ProfileMenu.tsx:370 msgid "This profile is only visible to logged-in users. It won't be visible to people who aren't logged in." -msgstr "" +msgstr "Ce profil n’est visible que pour les personnes connectées. Il ne sera pas visible pour les personnes qui ne sont pas connectées." #: src/screens/Signup/StepInfo/Policies.tsx:37 msgid "This service has not provided terms of service or a privacy policy." -msgstr "" +msgstr "Ce service n’a pas fourni de conditions d’utilisation ni de politique de confidentialité." #: src/view/com/modals/ChangeHandle.tsx:445 msgid "This should create a domain record at:" -msgstr "" +msgstr "Cela devrait créer un enregistrement de domaine à :" #: src/view/com/profile/ProfileFollowers.tsx:87 msgid "This user doesn't have any followers." -msgstr "" +msgstr "Ce compte n’a pas d’abonné·e·s." #: src/components/moderation/ModerationDetailsDialog.tsx:72 #: src/lib/moderation/useModerationCauseDescription.ts:68 @@ -5005,27 +4657,19 @@ msgstr "Ce compte vous a bloqué. Vous ne pouvez pas voir son contenu." #: src/lib/moderation/useGlobalLabelStrings.ts:30 msgid "This user has requested that their content only be shown to signed-in users." -msgstr "" - -#: src/view/com/modals/ModerationDetails.tsx:42 -#~ msgid "This user is included in the <0/> list which you have blocked." -#~ msgstr "Ce compte est inclus dans la liste <0/> que vous avez bloquée." - -#: src/view/com/modals/ModerationDetails.tsx:74 -#~ msgid "This user is included in the <0/> list which you have muted." -#~ msgstr "Ce compte est inclus dans la liste <0/> que vous avez masquée." +msgstr "Cette personne a demandé que son contenu ne soit affiché qu’aux personnes connectées." #: src/components/moderation/ModerationDetailsDialog.tsx:55 msgid "This user is included in the <0>{0} list which you have blocked." -msgstr "" +msgstr "Ce compte est inclus dans la liste <0>{0} que vous avez bloquée." #: src/components/moderation/ModerationDetailsDialog.tsx:84 msgid "This user is included in the <0>{0} list which you have muted." -msgstr "" +msgstr "Ce compte est inclus dans la liste <0>{0} que vous avez masquée." #: src/view/com/profile/ProfileFollows.tsx:87 msgid "This user isn't following anyone." -msgstr "" +msgstr "Ce compte ne suit personne." #: src/view/com/modals/SelfLabel.tsx:137 msgid "This warning is only available for posts with media attached." @@ -5035,13 +4679,9 @@ msgstr "Cet avertissement n’est disponible que pour les posts contenant des m msgid "This will delete {0} from your muted words. You can always add it back later." msgstr "Cela supprimera {0} de vos mots masqués. Vous pourrez toujours le réintégrer plus tard." -#: src/view/com/util/forms/PostDropdownBtn.tsx:282 -#~ msgid "This will hide this post from your feeds." -#~ msgstr "Cela va masquer ce post de vos fils d’actu." - #: src/view/screens/Settings/index.tsx:525 msgid "Thread preferences" -msgstr "" +msgstr "Préférences des fils de discussion" #: src/view/screens/PreferencesThreads.tsx:53 #: src/view/screens/Settings/index.tsx:535 @@ -5054,11 +4694,11 @@ msgstr "Mode arborescent" #: src/Navigation.tsx:269 msgid "Threads Preferences" -msgstr "Préférences de fils de discussion" +msgstr "Préférences des fils de discussion" #: src/components/ReportDialog/SelectLabelerView.tsx:33 msgid "To whom would you like to send this report?" -msgstr "" +msgstr "À qui souhaitez-vous envoyer ce rapport ?" #: src/components/dialogs/MutedWords.tsx:112 msgid "Toggle between muted word options." @@ -5070,11 +4710,11 @@ msgstr "Activer le menu déroulant" #: src/screens/Moderation/index.tsx:332 msgid "Toggle to enable or disable adult content" -msgstr "" +msgstr "Activer ou désactiver le contenu pour adultes" #: src/view/screens/Search/Search.tsx:427 msgid "Top" -msgstr "" +msgstr "Meilleur" #: src/view/com/modals/EditImage.tsx:272 msgid "Transformations" @@ -5094,7 +4734,7 @@ msgstr "Réessayer" #: src/view/com/modals/ChangeHandle.tsx:428 msgid "Type:" -msgstr "" +msgstr "Type :" #: src/view/screens/ProfileList.tsx:480 msgid "Un-block list" @@ -5133,7 +4773,7 @@ msgstr "Débloquer le compte" #: src/screens/Profile/Header/ProfileHeaderStandard.tsx:280 #: src/view/com/profile/ProfileMenu.tsx:343 msgid "Unblock Account?" -msgstr "" +msgstr "Débloquer le compte ?" #: src/view/com/modals/Repost.tsx:43 #: src/view/com/modals/Repost.tsx:56 @@ -5145,7 +4785,7 @@ msgstr "Annuler le repost" #: src/view/com/auth/onboarding/RecommendedFollowsItem.tsx:141 #: src/view/com/profile/ProfileHeaderSuggestedFollows.tsx:248 msgid "Unfollow" -msgstr "" +msgstr "Se désabonner" #: src/view/com/profile/FollowButton.tsx:60 msgctxt "action" @@ -5159,11 +4799,7 @@ msgstr "Se désabonner de {0}" #: src/view/com/profile/ProfileMenu.tsx:241 #: src/view/com/profile/ProfileMenu.tsx:251 msgid "Unfollow Account" -msgstr "" - -#: src/view/com/auth/create/state.ts:262 -#~ msgid "Unfortunately, you do not meet the requirements to create an account." -#~ msgstr "Malheureusement, vous ne remplissez pas les conditions requises pour créer un compte." +msgstr "Se désabonner du compte" #: src/view/com/util/post-ctrls/PostCtrls.tsx:197 msgid "Unlike" @@ -5171,7 +4807,7 @@ msgstr "Déliker" #: src/view/screens/ProfileFeed.tsx:585 msgid "Unlike this feed" -msgstr "" +msgstr "Déliker ce fil d’actu" #: src/components/TagMenu/index.tsx:249 #: src/view/screens/ProfileList.tsx:581 @@ -5203,39 +4839,31 @@ msgstr "Désépingler" #: src/view/screens/ProfileFeed.tsx:303 msgid "Unpin from home" -msgstr "" +msgstr "Désépingler de l’accueil" #: src/view/screens/ProfileList.tsx:446 msgid "Unpin moderation list" msgstr "Supprimer la liste de modération" -#: src/view/screens/ProfileFeed.tsx:346 -#~ msgid "Unsave" -#~ msgstr "Supprimer" - #: src/screens/Profile/Header/ProfileHeaderLabeler.tsx:225 msgid "Unsubscribe" -msgstr "" +msgstr "Se désabonner" #: src/screens/Profile/Header/ProfileHeaderLabeler.tsx:190 msgid "Unsubscribe from this labeler" -msgstr "" +msgstr "Se désabonner de cet étiqueteur" #: src/lib/moderation/useReportOptions.ts:70 msgid "Unwanted Sexual Content" -msgstr "" +msgstr "Contenu sexuel non désiré" #: src/view/com/modals/UserAddRemoveLists.tsx:70 msgid "Update {displayName} in Lists" msgstr "Mise à jour de {displayName} dans les listes" -#: src/lib/hooks/useOTAUpdate.ts:15 -#~ msgid "Update Available" -#~ msgstr "Mise à jour disponible" - #: src/view/com/modals/ChangeHandle.tsx:508 msgid "Update to {handle}" -msgstr "" +msgstr "Mettre à jour pour {handle}" #: src/screens/Login/SetNewPasswordForm.tsx:186 msgid "Updating..." @@ -5250,23 +4878,23 @@ msgstr "Envoyer un fichier texte vers :" #: src/view/com/util/UserBanner.tsx:116 #: src/view/com/util/UserBanner.tsx:119 msgid "Upload from Camera" -msgstr "" +msgstr "Envoyer à partir de l’appareil photo" #: src/view/com/util/UserAvatar.tsx:345 #: src/view/com/util/UserBanner.tsx:133 msgid "Upload from Files" -msgstr "" +msgstr "Envoyer à partir de fichiers" #: src/view/com/util/UserAvatar.tsx:339 #: src/view/com/util/UserAvatar.tsx:343 #: src/view/com/util/UserBanner.tsx:127 #: src/view/com/util/UserBanner.tsx:131 msgid "Upload from Library" -msgstr "" +msgstr "Envoyer à partir de la photothèque" #: src/view/com/modals/ChangeHandle.tsx:408 msgid "Use a file on your server" -msgstr "" +msgstr "Utiliser un fichier sur votre serveur" #: src/view/screens/AppPasswords.tsx:197 msgid "Use app passwords to login to other Bluesky clients without giving full access to your account or password." @@ -5274,7 +4902,7 @@ msgstr "Utilisez les mots de passe de l’appli pour se connecter à d’autres #: src/view/com/modals/ChangeHandle.tsx:517 msgid "Use bsky.social as hosting provider" -msgstr "" +msgstr "Utiliser bsky.social comme hébergeur" #: src/view/com/modals/ChangeHandle.tsx:516 msgid "Use default provider" @@ -5292,7 +4920,7 @@ msgstr "Utiliser mon navigateur par défaut" #: src/view/com/modals/ChangeHandle.tsx:400 msgid "Use the DNS panel" -msgstr "" +msgstr "Utiliser le panneau DNS" #: src/view/com/modals/AddAppPasswords.tsx:156 msgid "Use this to sign into the other app along with your handle." @@ -5309,7 +4937,7 @@ msgstr "Compte bloqué" #: src/lib/moderation/useModerationCauseDescription.ts:48 msgid "User Blocked by \"{0}\"" -msgstr "" +msgstr "Compte bloqué par « {0} »" #: src/components/moderation/ModerationDetailsDialog.tsx:53 msgid "User Blocked by List" @@ -5317,16 +4945,12 @@ msgstr "Compte bloqué par liste" #: src/lib/moderation/useModerationCauseDescription.ts:66 msgid "User Blocking You" -msgstr "" +msgstr "Compte qui vous bloque" #: src/components/moderation/ModerationDetailsDialog.tsx:70 msgid "User Blocks You" msgstr "Compte qui vous bloque" -#: src/view/com/auth/create/Step2.tsx:79 -#~ msgid "User handle" -#~ msgstr "Pseudo" - #: src/view/com/lists/ListCard.tsx:85 #: src/view/com/modals/UserAddRemoveLists.tsx:198 msgid "User list by {0}" @@ -5374,15 +4998,15 @@ msgstr "Comptes dans « {0} »" #: src/components/LikesDialog.tsx:85 msgid "Users that have liked this content or profile" -msgstr "" +msgstr "Comptes qui ont liké ce contenu ou ce profil" #: src/view/com/modals/ChangeHandle.tsx:436 msgid "Value:" -msgstr "" +msgstr "Valeur :" #: src/view/com/modals/ChangeHandle.tsx:509 msgid "Verify {0}" -msgstr "" +msgstr "Vérifier {0}" #: src/view/screens/Settings/index.tsx:906 msgid "Verify email" @@ -5407,7 +5031,7 @@ msgstr "Vérifiez votre e-mail" #: src/view/screens/Settings/index.tsx:857 msgid "Version {0}" -msgstr "" +msgstr "Version {0}" #: src/screens/Onboarding/index.tsx:42 msgid "Video Games" @@ -5423,11 +5047,11 @@ msgstr "Afficher l’entrée de débogage" #: src/components/ReportDialog/SelectReportOptionView.tsx:132 msgid "View details" -msgstr "" +msgstr "Voir les détails" #: src/components/ReportDialog/SelectReportOptionView.tsx:127 msgid "View details for reporting a copyright violation" -msgstr "" +msgstr "Voir les détails pour signaler une violation du droit d’auteur" #: src/view/com/posts/FeedSlice.tsx:99 msgid "View full thread" @@ -5435,7 +5059,7 @@ msgstr "Voir le fil de discussion entier" #: src/components/moderation/LabelsOnMe.tsx:51 msgid "View information about these labels" -msgstr "" +msgstr "Voir les informations sur ces étiquettes" #: src/components/ProfileHoverCard/index.web.tsx:264 #: src/components/ProfileHoverCard/index.web.tsx:293 @@ -5449,11 +5073,11 @@ msgstr "Afficher l’avatar" #: src/components/LabelingServiceCard/index.tsx:140 msgid "View the labeling service provided by @{0}" -msgstr "" +msgstr "Voir le service d’étiquetage fourni par @{0}" #: src/view/screens/ProfileFeed.tsx:597 msgid "View users who like this feed" -msgstr "" +msgstr "Voir les comptes qui a liké ce fil d’actu" #: src/view/com/modals/LinkWarning.tsx:89 #: src/view/com/modals/LinkWarning.tsx:95 @@ -5469,15 +5093,11 @@ msgstr "Avertir" #: src/lib/moderation/useLabelBehaviorDescription.ts:48 msgid "Warn content" -msgstr "" +msgstr "Avertir du contenu" #: src/lib/moderation/useLabelBehaviorDescription.ts:46 msgid "Warn content and filter from feeds" -msgstr "" - -#: src/screens/Onboarding/StepAlgoFeeds/index.tsx:134 -#~ msgid "We also think you'll like \"For You\" by Skygaze:" -#~ msgstr "Nous pensons également que vous aimerez « For You » de Skygaze :" +msgstr "Avertir du contenu et filtrer des fils d’actu" #: src/screens/Hashtag.tsx:133 msgid "We couldn't find any results for that hashtag." @@ -5505,11 +5125,11 @@ msgstr "Nous vous recommandons notre fil d’actu « Discover » :" #: src/components/dialogs/BirthDateSettings.tsx:52 msgid "We were unable to load your birth date preferences. Please try again." -msgstr "" +msgstr "Nous n’avons pas pu charger vos préférences en matière de date de naissance. Veuillez réessayer." #: src/screens/Moderation/index.tsx:385 msgid "We were unable to load your configured labelers at this time." -msgstr "" +msgstr "Nous n’avons pas pu charger vos étiqueteurs configurés pour le moment." #: src/screens/Onboarding/StepInterests/index.tsx:137 msgid "We weren't able to connect. Please try again to continue setting up your account. If it continues to fail, you can skip this flow." @@ -5519,10 +5139,6 @@ msgstr "Nous n’avons pas pu nous connecter. Veuillez réessayer pour continuer msgid "We will let you know when your account is ready." msgstr "Nous vous informerons lorsque votre compte sera prêt." -#: src/view/com/modals/AppealLabel.tsx:48 -#~ msgid "We'll look into your appeal promptly." -#~ msgstr "Nous examinerons votre appel rapidement." - #: src/screens/Onboarding/StepInterests/index.tsx:142 msgid "We'll use this to help customize your experience." msgstr "Nous utiliserons ces informations pour personnaliser votre expérience." @@ -5550,7 +5166,7 @@ msgstr "Nous sommes désolés ! La page que vous recherchez est introuvable." #: src/screens/Profile/Header/ProfileHeaderLabeler.tsx:327 msgid "We're sorry! You can only subscribe to ten labelers, and you've reached your limit of ten." -msgstr "" +msgstr "Nous sommes désolés ! Vous ne pouvez vous abonner qu’à dix étiqueteurs, et vous avez atteint votre limite de dix." #: src/view/com/auth/onboarding/WelcomeMobile.tsx:48 msgid "Welcome to <0>Bluesky" @@ -5560,10 +5176,6 @@ msgstr "Bienvenue sur <0>Bluesky" msgid "What are your interests?" msgstr "Quels sont vos centres d’intérêt ?" -#: src/view/com/modals/report/Modal.tsx:169 -#~ msgid "What is the issue with this {collectionName}?" -#~ msgstr "Quel est le problème avec cette {collectionName} ?" - #: src/view/com/auth/SplashScreen.tsx:40 #: src/view/com/auth/SplashScreen.web.tsx:81 #: src/view/com/composer/Composer.tsx:296 @@ -5585,23 +5197,23 @@ msgstr "Qui peut répondre ?" #: src/components/ReportDialog/SelectReportOptionView.tsx:43 msgid "Why should this content be reviewed?" -msgstr "" +msgstr "Pourquoi ce contenu doit-il être examiné ?" #: src/components/ReportDialog/SelectReportOptionView.tsx:56 msgid "Why should this feed be reviewed?" -msgstr "" +msgstr "Pourquoi ce fil d’actu doit-il être examiné ?" #: src/components/ReportDialog/SelectReportOptionView.tsx:53 msgid "Why should this list be reviewed?" -msgstr "" +msgstr "Pourquoi cette liste devrait-elle être examinée ?" #: src/components/ReportDialog/SelectReportOptionView.tsx:50 msgid "Why should this post be reviewed?" -msgstr "" +msgstr "Pourquoi ce post devrait-il être examiné ?" #: src/components/ReportDialog/SelectReportOptionView.tsx:47 msgid "Why should this user be reviewed?" -msgstr "" +msgstr "Pourquoi ce compte doit-il être examiné ?" #: src/view/com/modals/crop-image/CropImage.web.tsx:103 msgid "Wide" @@ -5636,7 +5248,7 @@ msgstr "Vous êtes dans la file d’attente." #: src/view/com/profile/ProfileFollows.tsx:86 msgid "You are not following anyone." -msgstr "" +msgstr "Vous ne suivez personne." #: src/view/com/posts/FollowingEmptyState.tsx:67 #: src/view/com/posts/FollowingEndOfFeed.tsx:68 @@ -5654,7 +5266,7 @@ msgstr "Vous pouvez maintenant vous connecter avec votre nouveau mot de passe." #: src/view/com/profile/ProfileFollowers.tsx:86 msgid "You do not have any followers." -msgstr "" +msgstr "Vous n’avez pas d’abonné·e·s." #: src/view/com/modals/InviteCodes.tsx:67 msgid "You don't have any invite codes yet! We'll send you some when you've been on Bluesky for a little longer." @@ -5691,24 +5303,20 @@ msgstr "Vous avez introduit un code non valide. Il devrait ressembler à XXXXX-X #: src/lib/moderation/useModerationCauseDescription.ts:109 msgid "You have hidden this post" -msgstr "" +msgstr "Vous avez caché ce post" #: src/components/moderation/ModerationDetailsDialog.tsx:101 msgid "You have hidden this post." -msgstr "" +msgstr "Vous avez caché ce post." #: src/components/moderation/ModerationDetailsDialog.tsx:94 #: src/lib/moderation/useModerationCauseDescription.ts:92 msgid "You have muted this account." -msgstr "" +msgstr "Vous avez masqué ce compte." #: src/lib/moderation/useModerationCauseDescription.ts:86 msgid "You have muted this user" -msgstr "" - -#: src/view/com/modals/ModerationDetails.tsx:87 -#~ msgid "You have muted this user." -#~ msgstr "Vous avez masqué ce compte." +msgstr "Vous avez masqué ce compte" #: src/view/com/feeds/ProfileFeedgens.tsx:144 msgid "You have no feeds." @@ -5721,11 +5329,7 @@ msgstr "Vous n’avez aucune liste." #: src/view/screens/ModerationBlockedAccounts.tsx:138 msgid "You have not blocked any accounts yet. To block an account, go to their profile and select \"Block account\" from the menu on their account." -msgstr "" - -#: src/view/screens/ModerationBlockedAccounts.tsx:132 -#~ msgid "You have not blocked any accounts yet. To block an account, go to their profile and selected \"Block account\" from the menu on their account." -#~ msgstr "Vous n’avez pas encore bloqué de comptes. Pour bloquer un compte, accédez à son profil et sélectionnez « Bloquer le compte » dans le menu de son compte." +msgstr "Vous n’avez pas encore bloqué de comptes. Pour bloquer un compte, allez sur son profil et sélectionnez « Bloquer le compte » dans le menu de son compte." #: src/view/screens/AppPasswords.tsx:89 msgid "You have not created any app passwords yet. You can create one by pressing the button below." @@ -5733,11 +5337,7 @@ msgstr "Vous n’avez encore créé aucun mot de passe pour l’appli. Vous pouv #: src/view/screens/ModerationMutedAccounts.tsx:136 msgid "You have not muted any accounts yet. To mute an account, go to their profile and select \"Mute account\" from the menu on their account." -msgstr "" - -#: src/view/screens/ModerationMutedAccounts.tsx:131 -#~ msgid "You have not muted any accounts yet. To mute an account, go to their profile and selected \"Mute account\" from the menu on their account." -#~ msgstr "Vous n’avez encore masqué aucun compte. Pour désactiver un compte, allez sur son profil et sélectionnez « Masquer le compte » dans le menu de son compte." +msgstr "Vous n’avez encore masqué aucun compte. Pour masquer un compte, allez sur son profil et sélectionnez « Masquer le compte » dans le menu de son compte." #: src/components/dialogs/MutedWords.tsx:249 msgid "You haven't muted any words or tags yet" @@ -5745,15 +5345,11 @@ msgstr "Vous n’avez pas encore masqué de mot ou de mot-clé" #: src/components/moderation/LabelsOnMeDialog.tsx:68 msgid "You may appeal these labels if you feel they were placed in error." -msgstr "" +msgstr "Vous pouvez faire appel de ces étiquettes si vous estimez qu’elles ont été apposées par erreur." #: src/screens/Signup/StepInfo/Policies.tsx:79 msgid "You must be 13 years of age or older to sign up." -msgstr "" - -#: src/view/com/modals/ContentFilteringSettings.tsx:175 -#~ msgid "You must be 18 or older to enable adult content." -#~ msgstr "Vous devez avoir 18 ans ou plus pour activer le contenu pour adultes." +msgstr "Vous devez avoir 13 ans ou plus pour vous inscrire." #: src/screens/Onboarding/StepModeration/AdultContentEnabledPref.tsx:110 msgid "You must be 18 years or older to enable adult content" @@ -5761,7 +5357,7 @@ msgstr "Vous devez avoir 18 ans ou plus pour activer le contenu pour adultes." #: src/components/ReportDialog/SubmitView.tsx:203 msgid "You must select at least one labeler for a report" -msgstr "" +msgstr "Vous devez sélectionner au moins un étiqueteur pour un rapport" #: src/view/com/util/forms/PostDropdownBtn.tsx:150 msgid "You will no longer receive notifications for this thread" @@ -5792,7 +5388,7 @@ msgstr "Vous êtes prêt à partir !" #: src/components/moderation/ModerationDetailsDialog.tsx:98 #: src/lib/moderation/useModerationCauseDescription.ts:101 msgid "You've chosen to hide a word or tag within this post." -msgstr "" +msgstr "Vous avez choisi de masquer un mot ou un mot-clé dans ce post." #: src/view/com/posts/FollowingEndOfFeed.tsx:48 msgid "You've reached the end of your feed! Find some more accounts to follow." From f49d73dd00462528bd2ff73fd51746756c12fe9d Mon Sep 17 00:00:00 2001 From: Samuel Newman Date: Mon, 22 Apr 2024 22:18:39 +0100 Subject: [PATCH 117/167] [GIFs] Reset scroll on query change (#3642) * scroll list to top on query change * dismiss keyboard on swipe list * don't need an effect --- src/components/Dialog/index.web.tsx | 12 ++++++------ src/components/dialogs/GifSelect.tsx | 17 ++++++++++++----- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/src/components/Dialog/index.web.tsx b/src/components/Dialog/index.web.tsx index a086955db6..4cb4e7570c 100644 --- a/src/components/Dialog/index.web.tsx +++ b/src/components/Dialog/index.web.tsx @@ -197,11 +197,10 @@ export function Inner({ export const ScrollableInner = Inner -export function InnerFlatList({ - label, - style, - ...props -}: FlatListProps & {label: string}) { +export const InnerFlatList = React.forwardRef< + FlatList, + FlatListProps & {label: string} +>(function InnerFlatList({label, style, ...props}, ref) { const {gtMobile} = useBreakpoints() return ( ) -} +}) export function Handle() { return null diff --git a/src/components/dialogs/GifSelect.tsx b/src/components/dialogs/GifSelect.tsx index ad4fbeadea..a8fe016d10 100644 --- a/src/components/dialogs/GifSelect.tsx +++ b/src/components/dialogs/GifSelect.tsx @@ -1,6 +1,7 @@ import React, {useCallback, useMemo, useRef, useState} from 'react' -import {TextInput, View} from 'react-native' +import {Keyboard, TextInput, View} from 'react-native' import {Image} from 'expo-image' +import {BottomSheetFlatListMethods} from '@discord/bottom-sheet' import {msg, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' @@ -82,7 +83,8 @@ function GifList({ const {_} = useLingui() const t = useTheme() const {gtMobile} = useBreakpoints() - const ref = useRef(null) + const textInputRef = useRef(null) + const listRef = useRef(null) const [undeferredSearch, setSearch] = useState('') const search = useThrottledValue(undeferredSearch, 500) @@ -133,7 +135,7 @@ function GifList({ const onGoBack = useCallback(() => { if (isSearching) { // clear the input and reset the state - ref.current?.clear() + textInputRef.current?.clear() setSearch('') } else { control.close() @@ -180,10 +182,13 @@ function GifList({ { + setSearch(text) + listRef.current?.scrollToOffset({offset: 0, animated: false}) + }} returnKeyType="search" clearButtonMode="while-editing" - inputRef={ref} + inputRef={textInputRef} maxLength={50} onKeyPress={({nativeEvent}) => { if (nativeEvent.key === 'Escape') { @@ -200,6 +205,7 @@ function GifList({ <> {gtMobile && } item.id} // @ts-expect-error web only style={isWeb && {minHeight: '100vh'}} + onScrollBeginDrag={() => Keyboard.dismiss()} ListFooterComponent={ hasData ? ( Date: Mon, 22 Apr 2024 14:46:05 -0700 Subject: [PATCH 118/167] properly close the switch account dialog (#3558) * properly close the switch account dialog * use it for switch account as well * ensure dialog is closed on unmount Revert "properly check if the ref is null" This reverts commit 8f563808a5d39389b0bc47a31e73cd147d1e7e8b. properly check if the ref is null ensure dialog is closed on unmount * Revert "ensure dialog is closed on unmount" This reverts commit a48548fd8ed53ae3eb08a0e05bb89f641c112b95. --- src/components/dialogs/SwitchAccount.tsx | 17 +++++++++-------- src/lib/hooks/useAccountSwitcher.ts | 18 ++++-------------- 2 files changed, 13 insertions(+), 22 deletions(-) diff --git a/src/components/dialogs/SwitchAccount.tsx b/src/components/dialogs/SwitchAccount.tsx index 645113d4af..55628a790d 100644 --- a/src/components/dialogs/SwitchAccount.tsx +++ b/src/components/dialogs/SwitchAccount.tsx @@ -6,7 +6,6 @@ import {useLingui} from '@lingui/react' import {useAccountSwitcher} from '#/lib/hooks/useAccountSwitcher' import {type SessionAccount, useSession} from '#/state/session' import {useLoggedOutViewControls} from '#/state/shell/logged-out' -import {useCloseAllActiveElements} from '#/state/util' import {atoms as a} from '#/alf' import * as Dialog from '#/components/Dialog' import {AccountList} from '../AccountList' @@ -21,23 +20,25 @@ export function SwitchAccountDialog({ const {currentAccount} = useSession() const {onPressSwitchAccount} = useAccountSwitcher() const {setShowLoggedOut} = useLoggedOutViewControls() - const closeAllActiveElements = useCloseAllActiveElements() const onSelectAccount = useCallback( (account: SessionAccount) => { - if (account.did === currentAccount?.did) { - control.close() + if (account.did !== currentAccount?.did) { + control.close(() => { + onPressSwitchAccount(account, 'SwitchAccount') + }) } else { - onPressSwitchAccount(account, 'SwitchAccount') + control.close() } }, [currentAccount, control, onPressSwitchAccount], ) const onPressAddAccount = useCallback(() => { - setShowLoggedOut(true) - closeAllActiveElements() - }, [setShowLoggedOut, closeAllActiveElements]) + control.close(() => { + setShowLoggedOut(true) + }) + }, [setShowLoggedOut, control]) return ( diff --git a/src/lib/hooks/useAccountSwitcher.ts b/src/lib/hooks/useAccountSwitcher.ts index eb1685a0ae..6a1cea2345 100644 --- a/src/lib/hooks/useAccountSwitcher.ts +++ b/src/lib/hooks/useAccountSwitcher.ts @@ -1,17 +1,15 @@ import {useCallback} from 'react' -import {isWeb} from '#/platform/detection' import {useAnalytics} from '#/lib/analytics/analytics' -import {useSessionApi, SessionAccount} from '#/state/session' -import * as Toast from '#/view/com/util/Toast' -import {useCloseAllActiveElements} from '#/state/util' +import {isWeb} from '#/platform/detection' +import {SessionAccount, useSessionApi} from '#/state/session' import {useLoggedOutViewControls} from '#/state/shell/logged-out' +import * as Toast from '#/view/com/util/Toast' import {LogEvents} from '../statsig/statsig' export function useAccountSwitcher() { const {track} = useAnalytics() const {selectAccount, clearCurrentAccount} = useSessionApi() - const closeAllActiveElements = useCloseAllActiveElements() const {requestSwitchToAccount} = useLoggedOutViewControls() const onPressSwitchAccount = useCallback( @@ -23,7 +21,6 @@ export function useAccountSwitcher() { try { if (account.accessJwt) { - closeAllActiveElements() if (isWeb) { // We're switching accounts, which remounts the entire app. // On mobile, this gets us Home, but on the web we also need reset the URL. @@ -37,7 +34,6 @@ export function useAccountSwitcher() { Toast.show(`Signed in as @${account.handle}`) }, 100) } else { - closeAllActiveElements() requestSwitchToAccount({requestedAccount: account.did}) Toast.show( `Please sign in as @${account.handle}`, @@ -49,13 +45,7 @@ export function useAccountSwitcher() { clearCurrentAccount() // back user out to login } }, - [ - track, - clearCurrentAccount, - selectAccount, - closeAllActiveElements, - requestSwitchToAccount, - ], + [track, clearCurrentAccount, selectAccount, requestSwitchToAccount], ) return {onPressSwitchAccount} From 76449fb6ef9b3eb327b6d059614d0da31c9d8e1f Mon Sep 17 00:00:00 2001 From: Samuel Newman Date: Mon, 22 Apr 2024 23:39:32 +0100 Subject: [PATCH 119/167] [GIFs] Replace GIPHY with Tenor (#3651) * replace GIPHY with Tenor * remove "directly" wording * replace GIPHY wording * remove log --- src/components/dialogs/GifSelect.tsx | 65 ++-- src/lib/constants.ts | 14 +- src/state/queries/giphy.ts | 280 ------------------ src/state/queries/tenor.ts | 177 +++++++++++ src/view/com/composer/Composer.tsx | 17 +- src/view/com/composer/photos/SelectGifBtn.tsx | 2 +- 6 files changed, 220 insertions(+), 335 deletions(-) delete mode 100644 src/state/queries/giphy.ts create mode 100644 src/state/queries/tenor.ts diff --git a/src/components/dialogs/GifSelect.tsx b/src/components/dialogs/GifSelect.tsx index a8fe016d10..41612aa5d9 100644 --- a/src/components/dialogs/GifSelect.tsx +++ b/src/components/dialogs/GifSelect.tsx @@ -5,7 +5,6 @@ import {BottomSheetFlatListMethods} from '@discord/bottom-sheet' import {msg, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' -import {GIPHY_PRIVACY_POLICY} from '#/lib/constants' import {logEvent} from '#/lib/statsig/statsig' import {cleanError} from '#/lib/strings/errors' import {isWeb} from '#/platform/detection' @@ -13,7 +12,11 @@ import { useExternalEmbedsPrefs, useSetExternalEmbedPref, } from '#/state/preferences' -import {Gif, useGifphySearch, useGiphyTrending} from '#/state/queries/giphy' +import { + Gif, + useFeaturedGifsQuery, + useGifSearchQuery, +} from '#/state/queries/tenor' import {ErrorScreen} from '#/view/com/util/error/ErrorScreen' import {ErrorBoundary} from '#/view/com/util/ErrorBoundary' import {atoms as a, useBreakpoints, useTheme} from '#/alf' @@ -22,7 +25,6 @@ import * as TextField from '#/components/forms/TextField' import {useThrottledValue} from '#/components/hooks/useThrottledValue' import {ArrowLeft_Stroke2_Corner0_Rounded as Arrow} from '#/components/icons/Arrow' import {MagnifyingGlass2_Stroke2_Corner0_Rounded as Search} from '#/components/icons/MagnifyingGlass2' -import {InlineLinkText} from '#/components/Link' import {Button, ButtonIcon, ButtonText} from '../Button' import {ListFooter, ListMaybePlaceholder} from '../Lists' import {Text} from '../Typography' @@ -46,14 +48,14 @@ export function GifSelectDialog({ let content = null let snapPoints - switch (externalEmbedsPrefs?.giphy) { + switch (externalEmbedsPrefs?.tenor) { case 'show': content = snapPoints = ['100%'] break case 'hide': default: - content = + content = break } @@ -90,8 +92,8 @@ function GifList({ const isSearching = search.length > 0 - const trendingQuery = useGiphyTrending() - const searchQuery = useGifphySearch(search) + const trendingQuery = useFeaturedGifsQuery() + const searchQuery = useGifSearchQuery(search) const { data, @@ -105,17 +107,7 @@ function GifList({ } = isSearching ? searchQuery : trendingQuery const flattenedData = useMemo(() => { - const uniquenessSet = new Set() - - function filter(gif: Gif) { - if (!gif) return false - if (uniquenessSet.has(gif.id)) { - return false - } - uniquenessSet.add(gif.id) - return true - } - return data?.pages.flatMap(page => page.data.filter(filter)) || [] + return data?.pages.flatMap(page => page.results) || [] }, [data]) const renderItem = useCallback( @@ -181,7 +173,7 @@ function GifList({ { setSearch(text) listRef.current?.scrollToOffset({offset: 0, animated: false}) @@ -223,12 +215,12 @@ function GifList({ emptyType="results" sideBorders={false} errorTitle={_(msg`Failed to load GIFs`)} - errorMessage={_(msg`There was an issue connecting to GIPHY.`)} + errorMessage={_(msg`There was an issue connecting to Tenor.`)} emptyMessage={ isSearching ? _(msg`No search results found for "${search}".`) : _( - msg`No trending GIFs found. There may be an issue with GIPHY.`, + msg`No featured GIFs found. There may be an issue with Tenor.`, ) } /> @@ -287,7 +279,9 @@ function GifPreview({ {aspectRatio: 1, opacity: pressed ? 0.8 : 1}, t.atoms.bg_contrast_25, ]} - source={{uri: gif.images.preview_gif.url}} + source={{ + uri: gif.media_formats.tinygif.url, + }} contentFit="cover" accessibilityLabel={gif.title} accessibilityHint="" @@ -299,61 +293,56 @@ function GifPreview({ ) } -function GiphyConsentPrompt({control}: {control: Dialog.DialogControlProps}) { +function TenorConsentPrompt({control}: {control: Dialog.DialogControlProps}) { const {_} = useLingui() const t = useTheme() const {gtMobile} = useBreakpoints() const setExternalEmbedPref = useSetExternalEmbedPref() const onShowPress = useCallback(() => { - setExternalEmbedPref('giphy', 'show') + setExternalEmbedPref('tenor', 'show') }, [setExternalEmbedPref]) const onHidePress = useCallback(() => { - setExternalEmbedPref('giphy', 'hide') + setExternalEmbedPref('tenor', 'hide') control.close() }, [control, setExternalEmbedPref]) const gtMobileWeb = gtMobile && isWeb return ( - + - Permission to use GIPHY + Permission to use Tenor - Bluesky uses GIPHY to provide the GIF selector feature. + Bluesky uses Tenor to provide the GIF selector feature. - GIPHY may collect information about you and your device. You can - find out more in their{' '} - control.close()}> - privacy policy - - . + Tenor is a third-party service that provides GIFs for use in + Bluesky. By enabling Tenor, requests will be made to Tenor's + servers to retrieve the GIFs. - - - - ) -} - function DialogError({details}: {details?: string}) { const {_} = useLingui() const control = Dialog.useDialogContext() diff --git a/src/view/screens/PreferencesExternalEmbeds.tsx b/src/view/screens/PreferencesExternalEmbeds.tsx index 1e8cedf7e2..5eec7e5077 100644 --- a/src/view/screens/PreferencesExternalEmbeds.tsx +++ b/src/view/screens/PreferencesExternalEmbeds.tsx @@ -1,25 +1,26 @@ import React from 'react' import {StyleSheet, View} from 'react-native' +import {Trans} from '@lingui/macro' import {useFocusEffect} from '@react-navigation/native' -import {NativeStackScreenProps, CommonNavigatorParams} from 'lib/routes/types' -import {s} from 'lib/styles' -import {Text} from '../com/util/text/Text' -import {usePalette} from 'lib/hooks/usePalette' -import {useAnalytics} from 'lib/analytics/analytics' -import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' + import { EmbedPlayerSource, externalEmbedLabels, } from '#/lib/strings/embed-player' import {useSetMinimalShellMode} from '#/state/shell' -import {Trans} from '@lingui/macro' -import {ScrollView} from '../com/util/Views' +import {useAnalytics} from 'lib/analytics/analytics' +import {usePalette} from 'lib/hooks/usePalette' +import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' +import {CommonNavigatorParams, NativeStackScreenProps} from 'lib/routes/types' +import {s} from 'lib/styles' import { useExternalEmbedsPrefs, useSetExternalEmbedPref, } from 'state/preferences' import {ToggleButton} from 'view/com/util/forms/ToggleButton' import {SimpleViewHeader} from '../com/util/SimpleViewHeader' +import {Text} from '../com/util/text/Text' +import {ScrollView} from '../com/util/Views' type Props = NativeStackScreenProps< CommonNavigatorParams, @@ -74,13 +75,16 @@ export function PreferencesExternalEmbeds({}: Props) { Enable media players for - {Object.entries(externalEmbedLabels).map(([key, label]) => ( - - ))} + {Object.entries(externalEmbedLabels) + // TODO: Remove special case when we disable the old integration. + .filter(([key]) => key !== 'tenor') + .map(([key, label]) => ( + + ))} ) From 49b5d420e6ae7c3c9cfd56f47248b686f5c0128a Mon Sep 17 00:00:00 2001 From: Samuel Newman Date: Tue, 23 Apr 2024 00:37:46 +0100 Subject: [PATCH 121/167] rm country param (#3653) --- src/state/queries/tenor.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/state/queries/tenor.ts b/src/state/queries/tenor.ts index 66cfcec6af..80c57479e6 100644 --- a/src/state/queries/tenor.ts +++ b/src/state/queries/tenor.ts @@ -65,10 +65,6 @@ function createTenorApi( if (locale) { params.set('locale', locale.languageTag.replace('-', '_')) - - if (locale.regionCode) { - params.set('country', locale.regionCode) - } } for (const [key, value] of Object.entries(input)) { From fe9b3f0432d36fd60e5da4ed16be87cd0470e64b Mon Sep 17 00:00:00 2001 From: dan Date: Tue, 23 Apr 2024 00:54:59 +0100 Subject: [PATCH 122/167] Ungate profile scroll fix (#3655) --- src/lib/statsig/gates.ts | 1 - src/view/screens/Profile.tsx | 24 ++++-------------------- 2 files changed, 4 insertions(+), 21 deletions(-) diff --git a/src/lib/statsig/gates.ts b/src/lib/statsig/gates.ts index 843c14f04d..c41083afba 100644 --- a/src/lib/statsig/gates.ts +++ b/src/lib/statsig/gates.ts @@ -5,7 +5,6 @@ export type Gate = | 'disable_poll_on_discover_v2' | 'hide_vertical_scroll_indicators' | 'new_gif_player' - | 'new_profile_scroll_component' | 'show_follow_back_label_v2' | 'start_session_with_following_v2' | 'use_new_suggestions_endpoint' diff --git a/src/view/screens/Profile.tsx b/src/view/screens/Profile.tsx index c7f5a6627a..9cf2352c8a 100644 --- a/src/view/screens/Profile.tsx +++ b/src/view/screens/Profile.tsx @@ -25,7 +25,6 @@ import {useAnalytics} from 'lib/analytics/analytics' import {useSetTitle} from 'lib/hooks/useSetTitle' import {ComposeIcon2} from 'lib/icons' import {CommonNavigatorParams, NativeStackScreenProps} from 'lib/routes/types' -import {useGate} from 'lib/statsig/statsig' import {combinedDisplayName} from 'lib/strings/display-names' import {isInvalidHandle} from 'lib/strings/handles' import {colors, s} from 'lib/styles' @@ -143,7 +142,6 @@ function ProfileScreenLoaded({ const setMinimalShellMode = useSetMinimalShellMode() const {openComposer} = useComposerControls() const {screen, track} = useAnalytics() - const gate = useGate() const { data: labelerInfo, error: labelerError, @@ -317,21 +315,8 @@ function ProfileScreenLoaded({ // = const renderHeader = React.useCallback(() => { - if (gate('new_profile_scroll_component')) { - return ( - - - - ) - } else { - return ( + return ( + - ) - } + + ) }, [ - gate, scrollViewTag, profile, labelerInfo, From cbb817b5b707042afefbf8ca46a7104d62349492 Mon Sep 17 00:00:00 2001 From: Hailey Date: Mon, 22 Apr 2024 18:54:15 -0700 Subject: [PATCH 123/167] GIF Viewer (#3605) * ios player autoplay after recycle remove all items from AVPlayer queue recurururururursion use managers in the view add prefetch make sure player items stay in order add controller and item managers start of the view create module, ios * android player smoother basic caching prep cache somewhat works backup other files android impl blegh lets go touchup add prefetch to js use caching * bogus testing commit * add dims to type * save * add the dimensions to the embed info * add a new case * add a new case * limit this case to giphy * use gate * Revert "bogus testing commit" This reverts commit b3c8751b71f7108de9aa843b22ded4e0249fa854. * add web player base * flip mp4/webp * basic mp4 player for web * move some stuff into `ExternalLinkEmbed` instead * use a class component for web * remove extra component * add `onPlayerStateChange` event type on web * layer properly * fix tests * add new test * about ready. native portions done, a few touch ups on web needed show placeholder on ios fix type rm log display thumbnail until video is ready to play add oncanplay, playsinline remove unused method add `isLoaded` change event release player when finished apply gc to the view cleanup logs android gc rm log automatic gc for assets make `nativeRef` private remove unnecessary `await` cleanup rev log only play on prepare whenever needed rm unused perfperfperf rm var comment + android width native height calculations rm pressable add event dispatcher on android add event dispatcher on ios * ready to test ios fix autoplay ios clean oops * autoplay on web * normalize across all platforms add check for `ALT:` separate gif embed logic to another file handle permissions requests flatten web styles normalize styles normalize styles prefetch functions pause animatable on foreground android nits one more oops idk where that code went lint rethink the usage wrap up android clear bg update gradle more android rename dir update android namespace web ios add deps use webp rm unused update types use webp on mobile * rm gate from types * remove unused event param * only start placeholder op if doesn't exist in disk cache * fix gifs animating on app resume android * remove comment * add `isLoaded` for ios * add `isLoaded` to Android * onload for web * add visual loading state * rm a log * implement isloaded for android * dialogs * replace `webpSource` with `source` * update prop name * Move to Tenor for GIFs (#3654) * update some urls * right order for dimensions * add GIF coder for ios * remove giphy check * rewrite tenor urls * remove all the unnecessary stuff for consent * rm print * rm log * check if id and filename are strings * full size playback controls * pass tests * add accessibility to gifs * use `onPlay` and `onPause` * rm unused logic for description * add accessibility label to the controls * add gif into to external embed in composer * make it optional * gif dimensions * make the jsx look nicer --------- Co-authored-by: Dan Abramov Co-authored-by: Samuel Newman --- __tests__/lib/string.test.ts | 230 +++++++++--------- .../android/build.gradle | 98 ++++++++ .../android/src/main/AndroidManifest.xml | 2 + .../AppCompatImageViewExtended.kt | 37 +++ .../ExpoBlueskyGifViewModule.kt | 54 ++++ .../expo/modules/blueskygifview/GifView.kt | 180 ++++++++++++++ .../expo-module.config.json | 9 + modules/expo-bluesky-gif-view/index.ts | 1 + .../ios/ExpoBlueskyGifView.podspec | 23 ++ .../ios/ExpoBlueskyGifViewModule.swift | 47 ++++ .../expo-bluesky-gif-view/ios/GifView.swift | 185 ++++++++++++++ modules/expo-bluesky-gif-view/ios/Util.swift | 17 ++ modules/expo-bluesky-gif-view/src/GifView.tsx | 39 +++ .../src/GifView.types.ts | 15 ++ .../expo-bluesky-gif-view/src/GifView.web.tsx | 82 +++++++ src/lib/statsig/gates.ts | 1 - src/lib/strings/embed-player.ts | 46 ++-- src/view/com/composer/Composer.tsx | 10 +- src/view/com/composer/ExternalEmbed.tsx | 70 ++++-- .../util/post-embeds/ExternalLinkEmbed.tsx | 150 +++++++----- src/view/com/util/post-embeds/GifEmbed.tsx | 140 +++++++++++ src/view/com/util/post-embeds/index.tsx | 52 +--- 22 files changed, 1223 insertions(+), 265 deletions(-) create mode 100644 modules/expo-bluesky-gif-view/android/build.gradle create mode 100644 modules/expo-bluesky-gif-view/android/src/main/AndroidManifest.xml create mode 100644 modules/expo-bluesky-gif-view/android/src/main/java/expo/modules/blueskygifview/AppCompatImageViewExtended.kt create mode 100644 modules/expo-bluesky-gif-view/android/src/main/java/expo/modules/blueskygifview/ExpoBlueskyGifViewModule.kt create mode 100644 modules/expo-bluesky-gif-view/android/src/main/java/expo/modules/blueskygifview/GifView.kt create mode 100644 modules/expo-bluesky-gif-view/expo-module.config.json create mode 100644 modules/expo-bluesky-gif-view/index.ts create mode 100644 modules/expo-bluesky-gif-view/ios/ExpoBlueskyGifView.podspec create mode 100644 modules/expo-bluesky-gif-view/ios/ExpoBlueskyGifViewModule.swift create mode 100644 modules/expo-bluesky-gif-view/ios/GifView.swift create mode 100644 modules/expo-bluesky-gif-view/ios/Util.swift create mode 100644 modules/expo-bluesky-gif-view/src/GifView.tsx create mode 100644 modules/expo-bluesky-gif-view/src/GifView.types.ts create mode 100644 modules/expo-bluesky-gif-view/src/GifView.web.tsx create mode 100644 src/view/com/util/post-embeds/GifEmbed.tsx diff --git a/__tests__/lib/string.test.ts b/__tests__/lib/string.test.ts index eeb5ae1572..03d6852494 100644 --- a/__tests__/lib/string.test.ts +++ b/__tests__/lib/string.test.ts @@ -459,6 +459,12 @@ describe('parseEmbedPlayerFromUrl', () => { 'https://tenor.com/view', 'https://tenor.com/view/gifId.gif', 'https://tenor.com/intl/view/gifId.gif', + + 'https://media.tenor.com/someID_AAAAC/someName.gif?hh=100&ww=100', + 'https://media.tenor.com/someID_AAAAC/someName.gif', + 'https://media.tenor.com/someID/someName.gif', + 'https://media.tenor.com/someID', + 'https://media.tenor.com', ] const outputs = [ @@ -628,137 +634,129 @@ describe('parseEmbedPlayerFromUrl', () => { }, undefined, undefined, - { type: 'giphy_gif', source: 'giphy', isGif: true, hideDetails: true, metaUri: 'https://giphy.com/gifs/39248209509382934029', - playerUri: 'https://i.giphy.com/media/39248209509382934029/200.mp4', + playerUri: 'https://i.giphy.com/media/39248209509382934029/200.webp', + }, + { + type: 'giphy_gif', + source: 'giphy', + isGif: true, + hideDetails: true, + metaUri: 'https://giphy.com/gifs/gifId', + playerUri: 'https://i.giphy.com/media/gifId/200.webp', + }, + { + type: 'giphy_gif', + source: 'giphy', + isGif: true, + hideDetails: true, + metaUri: 'https://giphy.com/gifs/gifId', + playerUri: 'https://i.giphy.com/media/gifId/200.webp', + }, + { + type: 'giphy_gif', + source: 'giphy', + isGif: true, + hideDetails: true, + metaUri: 'https://giphy.com/gifs/gifId', + playerUri: 'https://i.giphy.com/media/gifId/200.webp', + }, + { + type: 'giphy_gif', + source: 'giphy', + isGif: true, + hideDetails: true, + metaUri: 'https://giphy.com/gifs/gifId', + playerUri: 'https://i.giphy.com/media/gifId/200.webp', + }, + { + type: 'giphy_gif', + source: 'giphy', + isGif: true, + hideDetails: true, + metaUri: 'https://giphy.com/gifs/gifId', + playerUri: 'https://i.giphy.com/media/gifId/200.webp', + }, + { + type: 'giphy_gif', + source: 'giphy', + isGif: true, + hideDetails: true, + metaUri: 'https://giphy.com/gifs/gifId', + playerUri: 'https://i.giphy.com/media/gifId/200.webp', + }, + undefined, + undefined, + undefined, + + { + type: 'giphy_gif', + source: 'giphy', + isGif: true, + hideDetails: true, + metaUri: 'https://giphy.com/gifs/gifId', + playerUri: 'https://i.giphy.com/media/gifId/200.webp', + }, + + { + type: 'giphy_gif', + source: 'giphy', + isGif: true, + hideDetails: true, + metaUri: 'https://giphy.com/gifs/gifId', + playerUri: 'https://i.giphy.com/media/gifId/200.webp', + }, + { + type: 'giphy_gif', + source: 'giphy', + isGif: true, + hideDetails: true, + metaUri: 'https://giphy.com/gifs/gifId', + playerUri: 'https://i.giphy.com/media/gifId/200.webp', + }, + { + type: 'giphy_gif', + source: 'giphy', + isGif: true, + hideDetails: true, + metaUri: 'https://giphy.com/gifs/gifId', + playerUri: 'https://i.giphy.com/media/gifId/200.webp', + }, + { + type: 'giphy_gif', + source: 'giphy', + isGif: true, + hideDetails: true, + metaUri: 'https://giphy.com/gifs/gifId', + playerUri: 'https://i.giphy.com/media/gifId/200.webp', + }, + + undefined, + undefined, + undefined, + undefined, + undefined, + + { + type: 'tenor_gif', + source: 'tenor', + isGif: true, + hideDetails: true, + playerUri: 'https://t.gifs.bsky.app/someID_AAAAM/someName.gif', dimensions: { width: 100, height: 100, }, }, - - { - type: 'giphy_gif', - source: 'giphy', - isGif: true, - hideDetails: true, - metaUri: 'https://giphy.com/gifs/gifId', - playerUri: 'https://i.giphy.com/media/gifId/200.webp', - }, - { - type: 'giphy_gif', - source: 'giphy', - isGif: true, - hideDetails: true, - metaUri: 'https://giphy.com/gifs/gifId', - playerUri: 'https://i.giphy.com/media/gifId/200.webp', - }, - { - type: 'giphy_gif', - source: 'giphy', - isGif: true, - hideDetails: true, - metaUri: 'https://giphy.com/gifs/gifId', - playerUri: 'https://i.giphy.com/media/gifId/200.webp', - }, - { - type: 'giphy_gif', - source: 'giphy', - isGif: true, - hideDetails: true, - metaUri: 'https://giphy.com/gifs/gifId', - playerUri: 'https://i.giphy.com/media/gifId/200.webp', - }, - { - type: 'giphy_gif', - source: 'giphy', - isGif: true, - hideDetails: true, - metaUri: 'https://giphy.com/gifs/gifId', - playerUri: 'https://i.giphy.com/media/gifId/200.webp', - }, - { - type: 'giphy_gif', - source: 'giphy', - isGif: true, - hideDetails: true, - metaUri: 'https://giphy.com/gifs/gifId', - playerUri: 'https://i.giphy.com/media/gifId/200.webp', - }, undefined, undefined, undefined, - - { - type: 'giphy_gif', - source: 'giphy', - isGif: true, - hideDetails: true, - metaUri: 'https://giphy.com/gifs/gifId', - playerUri: 'https://i.giphy.com/media/gifId/200.webp', - }, - - { - type: 'giphy_gif', - source: 'giphy', - isGif: true, - hideDetails: true, - metaUri: 'https://giphy.com/gifs/gifId', - playerUri: 'https://i.giphy.com/media/gifId/200.webp', - }, - { - type: 'giphy_gif', - source: 'giphy', - isGif: true, - hideDetails: true, - metaUri: 'https://giphy.com/gifs/gifId', - playerUri: 'https://i.giphy.com/media/gifId/200.webp', - }, - { - type: 'giphy_gif', - source: 'giphy', - isGif: true, - hideDetails: true, - metaUri: 'https://giphy.com/gifs/gifId', - playerUri: 'https://i.giphy.com/media/gifId/200.webp', - }, - { - type: 'giphy_gif', - source: 'giphy', - isGif: true, - hideDetails: true, - metaUri: 'https://giphy.com/gifs/gifId', - playerUri: 'https://i.giphy.com/media/gifId/200.webp', - }, - - { - type: 'tenor_gif', - source: 'tenor', - isGif: true, - hideDetails: true, - playerUri: 'https://tenor.com/view/gifId.gif', - }, undefined, - undefined, - { - type: 'tenor_gif', - source: 'tenor', - isGif: true, - hideDetails: true, - playerUri: 'https://tenor.com/view/gifId.gif', - }, - { - type: 'tenor_gif', - source: 'tenor', - isGif: true, - hideDetails: true, - playerUri: 'https://tenor.com/intl/view/gifId.gif', - }, ] it('correctly grabs the correct id from uri', () => { diff --git a/modules/expo-bluesky-gif-view/android/build.gradle b/modules/expo-bluesky-gif-view/android/build.gradle new file mode 100644 index 0000000000..c209a35aec --- /dev/null +++ b/modules/expo-bluesky-gif-view/android/build.gradle @@ -0,0 +1,98 @@ +apply plugin: 'com.android.library' +apply plugin: 'kotlin-android' +apply plugin: 'maven-publish' + +group = 'expo.modules.blueskygifview' +version = '0.5.0' + +buildscript { + def expoModulesCorePlugin = new File(project(":expo-modules-core").projectDir.absolutePath, "ExpoModulesCorePlugin.gradle") + if (expoModulesCorePlugin.exists()) { + apply from: expoModulesCorePlugin + applyKotlinExpoModulesCorePlugin() + } + + // Simple helper that allows the root project to override versions declared by this library. + ext.safeExtGet = { prop, fallback -> + rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback + } + + // Ensures backward compatibility + ext.getKotlinVersion = { + if (ext.has("kotlinVersion")) { + ext.kotlinVersion() + } else { + ext.safeExtGet("kotlinVersion", "1.8.10") + } + } + + repositories { + mavenCentral() + } + + dependencies { + classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:${getKotlinVersion()}") + } +} + +afterEvaluate { + publishing { + publications { + release(MavenPublication) { + from components.release + } + } + repositories { + maven { + url = mavenLocal().url + } + } + } +} + +android { + compileSdkVersion safeExtGet("compileSdkVersion", 33) + + def agpVersion = com.android.Version.ANDROID_GRADLE_PLUGIN_VERSION + if (agpVersion.tokenize('.')[0].toInteger() < 8) { + compileOptions { + sourceCompatibility JavaVersion.VERSION_11 + targetCompatibility JavaVersion.VERSION_11 + } + + kotlinOptions { + jvmTarget = JavaVersion.VERSION_11.majorVersion + } + } + + namespace "expo.modules.blueskygifview" + defaultConfig { + minSdkVersion safeExtGet("minSdkVersion", 21) + targetSdkVersion safeExtGet("targetSdkVersion", 34) + versionCode 1 + versionName "0.5.0" + } + lintOptions { + abortOnError false + } + publishing { + singleVariant("release") { + withSourcesJar() + } + } +} + +repositories { + mavenCentral() +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.6.1' + def GLIDE_VERSION = "4.13.2" + + implementation project(':expo-modules-core') + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:${getKotlinVersion()}" + + // Keep glide version up to date with expo-image so that we don't have duplicate deps + implementation 'com.github.bumptech.glide:glide:4.13.2' +} diff --git a/modules/expo-bluesky-gif-view/android/src/main/AndroidManifest.xml b/modules/expo-bluesky-gif-view/android/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..bdae66c8f5 --- /dev/null +++ b/modules/expo-bluesky-gif-view/android/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + + diff --git a/modules/expo-bluesky-gif-view/android/src/main/java/expo/modules/blueskygifview/AppCompatImageViewExtended.kt b/modules/expo-bluesky-gif-view/android/src/main/java/expo/modules/blueskygifview/AppCompatImageViewExtended.kt new file mode 100644 index 0000000000..5d20848453 --- /dev/null +++ b/modules/expo-bluesky-gif-view/android/src/main/java/expo/modules/blueskygifview/AppCompatImageViewExtended.kt @@ -0,0 +1,37 @@ +package expo.modules.blueskygifview + +import android.content.Context +import android.graphics.Canvas +import android.graphics.drawable.Animatable +import androidx.appcompat.widget.AppCompatImageView + +class AppCompatImageViewExtended(context: Context, private val parent: GifView): AppCompatImageView(context) { + override fun onDraw(canvas: Canvas) { + super.onDraw(canvas) + + if (this.drawable is Animatable) { + if (!parent.isLoaded) { + parent.isLoaded = true + parent.firePlayerStateChange() + } + + if (!parent.isPlaying) { + this.pause() + } + } + } + + fun pause() { + val drawable = this.drawable + if (drawable is Animatable) { + drawable.stop() + } + } + + fun play() { + val drawable = this.drawable + if (drawable is Animatable) { + drawable.start() + } + } +} \ No newline at end of file diff --git a/modules/expo-bluesky-gif-view/android/src/main/java/expo/modules/blueskygifview/ExpoBlueskyGifViewModule.kt b/modules/expo-bluesky-gif-view/android/src/main/java/expo/modules/blueskygifview/ExpoBlueskyGifViewModule.kt new file mode 100644 index 0000000000..625e1d45f9 --- /dev/null +++ b/modules/expo-bluesky-gif-view/android/src/main/java/expo/modules/blueskygifview/ExpoBlueskyGifViewModule.kt @@ -0,0 +1,54 @@ +package expo.modules.blueskygifview + +import com.bumptech.glide.Glide +import com.bumptech.glide.load.engine.DiskCacheStrategy +import expo.modules.kotlin.modules.Module +import expo.modules.kotlin.modules.ModuleDefinition + +class ExpoBlueskyGifViewModule : Module() { + override fun definition() = ModuleDefinition { + Name("ExpoBlueskyGifView") + + AsyncFunction("prefetchAsync") { sources: List -> + val activity = appContext.currentActivity ?: return@AsyncFunction + val glide = Glide.with(activity) + + sources.forEach { source -> + glide + .download(source) + .diskCacheStrategy(DiskCacheStrategy.DATA) + .submit() + } + } + + View(GifView::class) { + Events( + "onPlayerStateChange" + ) + + Prop("source") { view: GifView, source: String -> + view.source = source + } + + Prop("placeholderSource") { view: GifView, source: String -> + view.placeholderSource = source + } + + Prop("autoplay") { view: GifView, autoplay: Boolean -> + view.autoplay = autoplay + } + + AsyncFunction("playAsync") { view: GifView -> + view.play() + } + + AsyncFunction("pauseAsync") { view: GifView -> + view.pause() + } + + AsyncFunction("toggleAsync") { view: GifView -> + view.toggle() + } + } + } +} diff --git a/modules/expo-bluesky-gif-view/android/src/main/java/expo/modules/blueskygifview/GifView.kt b/modules/expo-bluesky-gif-view/android/src/main/java/expo/modules/blueskygifview/GifView.kt new file mode 100644 index 0000000000..be5830df7a --- /dev/null +++ b/modules/expo-bluesky-gif-view/android/src/main/java/expo/modules/blueskygifview/GifView.kt @@ -0,0 +1,180 @@ +package expo.modules.blueskygifview + + +import android.content.Context +import android.graphics.Color +import android.graphics.drawable.Animatable +import android.graphics.drawable.Drawable +import com.bumptech.glide.Glide +import com.bumptech.glide.load.engine.DiskCacheStrategy +import com.bumptech.glide.load.engine.GlideException +import com.bumptech.glide.request.RequestListener +import com.bumptech.glide.request.target.Target +import expo.modules.kotlin.AppContext +import expo.modules.kotlin.exception.Exceptions +import expo.modules.kotlin.viewevent.EventDispatcher +import expo.modules.kotlin.views.ExpoView + +class GifView(context: Context, appContext: AppContext) : ExpoView(context, appContext) { + // Events + private val onPlayerStateChange by EventDispatcher() + + // Glide + private val activity = appContext.currentActivity ?: throw Exceptions.MissingActivity() + private val glide = Glide.with(activity) + val imageView = AppCompatImageViewExtended(context, this) + var isPlaying = true + var isLoaded = false + + // Requests + private var placeholderRequest: Target? = null + private var webpRequest: Target? = null + + // Props + var placeholderSource: String? = null + var source: String? = null + var autoplay: Boolean = true + set(value) { + field = value + + if (value) { + this.play() + } else { + this.pause() + } + } + + + // + + init { + this.setBackgroundColor(Color.TRANSPARENT) + + this.imageView.setBackgroundColor(Color.TRANSPARENT) + this.imageView.layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT) + + this.addView(this.imageView) + } + + override fun onAttachedToWindow() { + if (this.imageView.drawable == null || this.imageView.drawable !is Animatable) { + this.load() + } else if (this.isPlaying) { + this.imageView.play() + } + super.onAttachedToWindow() + } + + override fun onDetachedFromWindow() { + this.imageView.pause() + super.onDetachedFromWindow() + } + + // + + // + + private fun load() { + if (placeholderSource == null || source == null) { + return + } + + this.webpRequest = glide.load(source) + .diskCacheStrategy(DiskCacheStrategy.DATA) + .skipMemoryCache(false) + .listener(object: RequestListener { + override fun onResourceReady( + resource: Drawable?, + model: Any?, + target: Target?, + dataSource: com.bumptech.glide.load.DataSource?, + isFirstResource: Boolean + ): Boolean { + if (placeholderRequest != null) { + glide.clear(placeholderRequest) + } + return false + } + + override fun onLoadFailed( + e: GlideException?, + model: Any?, + target: Target?, + isFirstResource: Boolean + ): Boolean { + return true + } + }) + .into(this.imageView) + + if (this.imageView.drawable == null || this.imageView.drawable !is Animatable) { + this.placeholderRequest = glide.load(placeholderSource) + .diskCacheStrategy(DiskCacheStrategy.DATA) + // Let's not bloat the memory cache with placeholders + .skipMemoryCache(true) + .listener(object: RequestListener { + override fun onResourceReady( + resource: Drawable?, + model: Any?, + target: Target?, + dataSource: com.bumptech.glide.load.DataSource?, + isFirstResource: Boolean + ): Boolean { + // Incase this request finishes after the webp, let's just not set + // the drawable. This shouldn't happen because the request should get cancelled + if (imageView.drawable == null) { + imageView.setImageDrawable(resource) + } + return true + } + + override fun onLoadFailed( + e: GlideException?, + model: Any?, + target: Target?, + isFirstResource: Boolean + ): Boolean { + return true + } + }) + .submit() + } + } + + // + + // + + fun play() { + this.imageView.play() + this.isPlaying = true + this.firePlayerStateChange() + } + + fun pause() { + this.imageView.pause() + this.isPlaying = false + this.firePlayerStateChange() + } + + fun toggle() { + if (this.isPlaying) { + this.pause() + } else { + this.play() + } + } + + // + + // + + fun firePlayerStateChange() { + onPlayerStateChange(mapOf( + "isPlaying" to this.isPlaying, + "isLoaded" to this.isLoaded, + )) + } + + // +} diff --git a/modules/expo-bluesky-gif-view/expo-module.config.json b/modules/expo-bluesky-gif-view/expo-module.config.json new file mode 100644 index 0000000000..0756c8e24c --- /dev/null +++ b/modules/expo-bluesky-gif-view/expo-module.config.json @@ -0,0 +1,9 @@ +{ + "platforms": ["ios", "android", "web"], + "ios": { + "modules": ["ExpoBlueskyGifViewModule"] + }, + "android": { + "modules": ["expo.modules.blueskygifview.ExpoBlueskyGifViewModule"] + } +} diff --git a/modules/expo-bluesky-gif-view/index.ts b/modules/expo-bluesky-gif-view/index.ts new file mode 100644 index 0000000000..0244a54914 --- /dev/null +++ b/modules/expo-bluesky-gif-view/index.ts @@ -0,0 +1 @@ +export {GifView} from './src/GifView' diff --git a/modules/expo-bluesky-gif-view/ios/ExpoBlueskyGifView.podspec b/modules/expo-bluesky-gif-view/ios/ExpoBlueskyGifView.podspec new file mode 100644 index 0000000000..ddd0877b24 --- /dev/null +++ b/modules/expo-bluesky-gif-view/ios/ExpoBlueskyGifView.podspec @@ -0,0 +1,23 @@ +Pod::Spec.new do |s| + s.name = 'ExpoBlueskyGifView' + s.version = '1.0.0' + s.summary = 'A simple GIF player for Bluesky' + s.description = 'A simple GIF player for Bluesky' + s.author = '' + s.homepage = 'https://github.com/bluesky-social/social-app' + s.platforms = { :ios => '13.4', :tvos => '13.4' } + s.source = { git: '' } + s.static_framework = true + + s.dependency 'ExpoModulesCore' + s.dependency 'SDWebImage', '~> 5.17.0' + s.dependency 'SDWebImageWebPCoder', '~> 0.13.0' + + # Swift/Objective-C compatibility + s.pod_target_xcconfig = { + 'DEFINES_MODULE' => 'YES', + 'SWIFT_COMPILATION_MODE' => 'wholemodule' + } + + s.source_files = "**/*.{h,m,mm,swift,hpp,cpp}" +end diff --git a/modules/expo-bluesky-gif-view/ios/ExpoBlueskyGifViewModule.swift b/modules/expo-bluesky-gif-view/ios/ExpoBlueskyGifViewModule.swift new file mode 100644 index 0000000000..7c7132290d --- /dev/null +++ b/modules/expo-bluesky-gif-view/ios/ExpoBlueskyGifViewModule.swift @@ -0,0 +1,47 @@ +import ExpoModulesCore +import SDWebImage +import SDWebImageWebPCoder + +public class ExpoBlueskyGifViewModule: Module { + public func definition() -> ModuleDefinition { + Name("ExpoBlueskyGifView") + + OnCreate { + SDImageCodersManager.shared.addCoder(SDImageGIFCoder.shared) + } + + AsyncFunction("prefetchAsync") { (sources: [URL]) in + SDWebImagePrefetcher.shared.prefetchURLs(sources, context: Util.createContext(), progress: nil) + } + + View(GifView.self) { + Events( + "onPlayerStateChange" + ) + + Prop("source") { (view: GifView, prop: String) in + view.source = prop + } + + Prop("placeholderSource") { (view: GifView, prop: String) in + view.placeholderSource = prop + } + + Prop("autoplay") { (view: GifView, prop: Bool) in + view.autoplay = prop + } + + AsyncFunction("toggleAsync") { (view: GifView) in + view.toggle() + } + + AsyncFunction("playAsync") { (view: GifView) in + view.play() + } + + AsyncFunction("pauseAsync") { (view: GifView) in + view.pause() + } + } + } +} diff --git a/modules/expo-bluesky-gif-view/ios/GifView.swift b/modules/expo-bluesky-gif-view/ios/GifView.swift new file mode 100644 index 0000000000..de722d7a63 --- /dev/null +++ b/modules/expo-bluesky-gif-view/ios/GifView.swift @@ -0,0 +1,185 @@ +import ExpoModulesCore +import SDWebImage +import SDWebImageWebPCoder + +typealias SDWebImageContext = [SDWebImageContextOption: Any] + +public class GifView: ExpoView, AVPlayerViewControllerDelegate { + // Events + private let onPlayerStateChange = EventDispatcher() + + // SDWebImage + private let imageView = SDAnimatedImageView(frame: .zero) + private let imageManager = SDWebImageManager( + cache: SDImageCache.shared, + loader: SDImageLoadersManager.shared + ) + private var isPlaying = true + private var isLoaded = false + + // Requests + private var webpOperation: SDWebImageCombinedOperation? + private var placeholderOperation: SDWebImageCombinedOperation? + + // Props + var source: String? = nil + var placeholderSource: String? = nil + var autoplay = true { + didSet { + if !autoplay { + self.pause() + } else { + self.play() + } + } + } + + // MARK: - Lifecycle + + public required init(appContext: AppContext? = nil) { + super.init(appContext: appContext) + self.clipsToBounds = true + + self.imageView.autoresizingMask = [.flexibleWidth, .flexibleHeight] + self.imageView.layer.masksToBounds = false + self.imageView.backgroundColor = .clear + self.imageView.contentMode = .scaleToFill + + // We have to explicitly set this to false. If we don't, every time + // the view comes into the viewport, it will start animating again + self.imageView.autoPlayAnimatedImage = false + + self.addSubview(self.imageView) + } + + public override func willMove(toWindow newWindow: UIWindow?) { + if newWindow == nil { + // Don't cancel the placeholder operation, because we really want that to complete for + // when we scroll back up + self.webpOperation?.cancel() + self.placeholderOperation?.cancel() + } else if self.imageView.image == nil { + self.load() + } + } + + // MARK: - Loading + + private func load() { + guard let source = self.source, let placeholderSource = self.placeholderSource else { + return + } + + self.webpOperation?.cancel() + self.placeholderOperation?.cancel() + + // We only need to start an operation for the placeholder if it doesn't exist + // in the cache already. Cache key is by default the absolute URL of the image. + // See: + // https://github.com/SDWebImage/SDWebImage/blob/master/Docs/HowToUse.md#using-asynchronous-image-caching-independently + if !SDImageCache.shared.diskImageDataExists(withKey: source), + let url = URL(string: placeholderSource) + { + self.placeholderOperation = imageManager.loadImage( + with: url, + options: [.retryFailed], + context: Util.createContext(), + progress: onProgress(_:_:_:), + completed: onLoaded(_:_:_:_:_:_:) + ) + } + + if let url = URL(string: source) { + self.webpOperation = imageManager.loadImage( + with: url, + options: [.retryFailed], + context: Util.createContext(), + progress: onProgress(_:_:_:), + completed: onLoaded(_:_:_:_:_:_:) + ) + } + } + + private func setImage(_ image: UIImage) { + if self.imageView.image == nil || image.sd_isAnimated { + self.imageView.image = image + } + + if image.sd_isAnimated { + self.firePlayerStateChange() + if isPlaying { + self.imageView.startAnimating() + } + } + } + + // MARK: - Loading blocks + + private func onProgress(_ receivedSize: Int, _ expectedSize: Int, _ imageUrl: URL?) {} + + private func onLoaded( + _ image: UIImage?, + _ data: Data?, + _ error: Error?, + _ cacheType: SDImageCacheType, + _ finished: Bool, + _ imageUrl: URL? + ) { + guard finished else { + return + } + + if let placeholderSource = self.placeholderSource, + imageUrl?.absoluteString == placeholderSource, + self.imageView.image == nil, + let image = image + { + self.setImage(image) + return + } + + if let source = self.source, + imageUrl?.absoluteString == source, + // UIImage perf suckssss if the image is animated + let data = data, + let animatedImage = SDAnimatedImage(data: data) + { + self.placeholderOperation?.cancel() + self.isPlaying = self.autoplay + self.isLoaded = true + self.setImage(animatedImage) + self.firePlayerStateChange() + } + } + + // MARK: - Playback Controls + + func play() { + self.imageView.startAnimating() + self.isPlaying = true + self.firePlayerStateChange() + } + + func pause() { + self.imageView.stopAnimating() + self.isPlaying = false + self.firePlayerStateChange() + } + + func toggle() { + if self.isPlaying { + self.pause() + } else { + self.play() + } + } + + // MARK: - Util + + private func firePlayerStateChange() { + onPlayerStateChange([ + "isPlaying": self.isPlaying, + "isLoaded": self.isLoaded + ]) + } +} diff --git a/modules/expo-bluesky-gif-view/ios/Util.swift b/modules/expo-bluesky-gif-view/ios/Util.swift new file mode 100644 index 0000000000..55ed4152aa --- /dev/null +++ b/modules/expo-bluesky-gif-view/ios/Util.swift @@ -0,0 +1,17 @@ +import SDWebImage + +class Util { + static func createContext() -> SDWebImageContext { + var context = SDWebImageContext() + + // SDAnimatedImage for some reason has issues whenever loaded from memory. Instead, we + // will just use the disk. SDWebImage will manage this cache for us, so we don't need + // to worry about clearing it. + context[.originalQueryCacheType] = SDImageCacheType.disk.rawValue + context[.originalStoreCacheType] = SDImageCacheType.disk.rawValue + context[.queryCacheType] = SDImageCacheType.disk.rawValue + context[.storeCacheType] = SDImageCacheType.disk.rawValue + + return context + } +} diff --git a/modules/expo-bluesky-gif-view/src/GifView.tsx b/modules/expo-bluesky-gif-view/src/GifView.tsx new file mode 100644 index 0000000000..87258de17b --- /dev/null +++ b/modules/expo-bluesky-gif-view/src/GifView.tsx @@ -0,0 +1,39 @@ +import React from 'react' +import {requireNativeModule} from 'expo' +import {requireNativeViewManager} from 'expo-modules-core' + +import {GifViewProps} from './GifView.types' + +const NativeModule = requireNativeModule('ExpoBlueskyGifView') +const NativeView: React.ComponentType< + GifViewProps & {ref: React.RefObject} +> = requireNativeViewManager('ExpoBlueskyGifView') + +export class GifView extends React.PureComponent { + // TODO native types, should all be the same as those in this class + private nativeRef: React.RefObject = React.createRef() + + constructor(props: GifViewProps | Readonly) { + super(props) + } + + static async prefetchAsync(sources: string[]): Promise { + return await NativeModule.prefetchAsync(sources) + } + + async playAsync(): Promise { + await this.nativeRef.current.playAsync() + } + + async pauseAsync(): Promise { + await this.nativeRef.current.pauseAsync() + } + + async toggleAsync(): Promise { + await this.nativeRef.current.toggleAsync() + } + + render() { + return + } +} diff --git a/modules/expo-bluesky-gif-view/src/GifView.types.ts b/modules/expo-bluesky-gif-view/src/GifView.types.ts new file mode 100644 index 0000000000..29ec277f2b --- /dev/null +++ b/modules/expo-bluesky-gif-view/src/GifView.types.ts @@ -0,0 +1,15 @@ +import {ViewProps} from 'react-native' + +export interface GifViewStateChangeEvent { + nativeEvent: { + isPlaying: boolean + isLoaded: boolean + } +} + +export interface GifViewProps extends ViewProps { + autoplay?: boolean + source?: string + placeholderSource?: string + onPlayerStateChange?: (event: GifViewStateChangeEvent) => void +} diff --git a/modules/expo-bluesky-gif-view/src/GifView.web.tsx b/modules/expo-bluesky-gif-view/src/GifView.web.tsx new file mode 100644 index 0000000000..c197e01a1d --- /dev/null +++ b/modules/expo-bluesky-gif-view/src/GifView.web.tsx @@ -0,0 +1,82 @@ +import * as React from 'react' +import {StyleSheet} from 'react-native' + +import {GifViewProps} from './GifView.types' + +export class GifView extends React.PureComponent { + private readonly videoPlayerRef: React.RefObject = + React.createRef() + private isLoaded = false + + constructor(props: GifViewProps | Readonly) { + super(props) + } + + componentDidUpdate(prevProps: Readonly) { + if (prevProps.autoplay !== this.props.autoplay) { + if (this.props.autoplay) { + this.playAsync() + } else { + this.pauseAsync() + } + } + } + + static async prefetchAsync(_: string[]): Promise { + console.warn('prefetchAsync is not supported on web') + } + + private firePlayerStateChangeEvent = () => { + this.props.onPlayerStateChange?.({ + nativeEvent: { + isPlaying: !this.videoPlayerRef.current?.paused, + isLoaded: this.isLoaded, + }, + }) + } + + private onLoad = () => { + // Prevent multiple calls to onLoad because onCanPlay will fire after each loop + if (this.isLoaded) { + return + } + + this.isLoaded = true + this.firePlayerStateChangeEvent() + } + + async playAsync(): Promise { + this.videoPlayerRef.current?.play() + } + + async pauseAsync(): Promise { + this.videoPlayerRef.current?.pause() + } + + async toggleAsync(): Promise { + if (this.videoPlayerRef.current?.paused) { + await this.playAsync() + } else { + await this.pauseAsync() + } + } + + render() { + return ( +