Stop passing _ around in composer functions (#10256)

This commit is contained in:
Samuel Newman
2026-04-15 07:39:51 -07:00
committed by GitHub
parent bcbc114189
commit ecc78efb12
5 changed files with 129 additions and 137 deletions
+2 -2
View File
@@ -30,7 +30,7 @@ export async function getServiceAuthToken({
return serviceAuth.token return serviceAuth.token
} }
export async function getVideoUploadLimits(agent: BskyAgent, _: I18n['_']) { export async function getVideoUploadLimits(agent: BskyAgent, i18n: I18n) {
const token = await getServiceAuthToken({ const token = await getServiceAuthToken({
agent, agent,
lxm: 'app.bsky.video.getUploadLimits', lxm: 'app.bsky.video.getUploadLimits',
@@ -52,7 +52,7 @@ export async function getVideoUploadLimits(agent: BskyAgent, _: I18n['_']) {
throw new UploadLimitError(limits.message) throw new UploadLimitError(limits.message)
} else { } else {
throw new UploadLimitError( throw new UploadLimitError(
_( i18n._(
msg`You have temporarily reached the limit for video uploads. Please try again later.`, msg`You have temporarily reached the limit for video uploads. Please try again later.`,
), ),
) )
+6 -4
View File
@@ -16,19 +16,19 @@ export async function uploadVideo({
did, did,
setProgress, setProgress,
signal, signal,
_, i18n,
}: { }: {
video: CompressedVideo video: CompressedVideo
agent: BskyAgent agent: BskyAgent
did: string did: string
setProgress: (progress: number) => void setProgress: (progress: number) => void
signal: AbortSignal signal: AbortSignal
_: I18n['_'] i18n: I18n
}) { }) {
if (signal.aborted) { if (signal.aborted) {
throw new AbortError() throw new AbortError()
} }
await getVideoUploadLimits(agent, _) await getVideoUploadLimits(agent, i18n)
const uri = createVideoEndpointUrl('/xrpc/app.bsky.video.uploadVideo', { const uri = createVideoEndpointUrl('/xrpc/app.bsky.video.uploadVideo', {
did, did,
@@ -69,7 +69,9 @@ export async function uploadVideo({
const responseBody = JSON.parse(res.body) as AppBskyVideoDefs.JobStatus const responseBody = JSON.parse(res.body) as AppBskyVideoDefs.JobStatus
if (!responseBody.jobId) { if (!responseBody.jobId) {
throw new ServerError(responseBody.error || _(msg`Failed to upload video`)) throw new ServerError(
responseBody.error || i18n._(msg`Failed to upload video`),
)
} }
if (signal.aborted) { if (signal.aborted) {
+6 -6
View File
@@ -15,19 +15,19 @@ export async function uploadVideo({
did, did,
setProgress, setProgress,
signal, signal,
_, i18n,
}: { }: {
video: CompressedVideo video: CompressedVideo
agent: BskyAgent agent: BskyAgent
did: string did: string
setProgress: (progress: number) => void setProgress: (progress: number) => void
signal: AbortSignal signal: AbortSignal
_: I18n['_'] i18n: I18n
}) { }) {
if (signal.aborted) { if (signal.aborted) {
throw new AbortError() throw new AbortError()
} }
await getVideoUploadLimits(agent, _) await getVideoUploadLimits(agent, i18n)
const uri = createVideoEndpointUrl('/xrpc/app.bsky.video.uploadVideo', { const uri = createVideoEndpointUrl('/xrpc/app.bsky.video.uploadVideo', {
did, did,
@@ -70,11 +70,11 @@ export async function uploadVideo({
) as AppBskyVideoDefs.JobStatus ) as AppBskyVideoDefs.JobStatus
resolve(uploadRes) resolve(uploadRes)
} else { } else {
reject(new ServerError(_(msg`Failed to upload video`))) reject(new ServerError(i18n._(msg`Failed to upload video`)))
} }
} }
xhr.onerror = () => { xhr.onerror = () => {
reject(new ServerError(_(msg`Failed to upload video`))) reject(new ServerError(i18n._(msg`Failed to upload video`)))
} }
xhr.open('POST', uri) xhr.open('POST', uri)
xhr.setRequestHeader('Content-Type', video.mimeType) xhr.setRequestHeader('Content-Type', video.mimeType)
@@ -84,7 +84,7 @@ export async function uploadVideo({
) )
if (!res.jobId) { if (!res.jobId) {
throw new ServerError(res.error || _(msg`Failed to upload video`)) throw new ServerError(res.error || i18n._(msg`Failed to upload video`))
} }
if (signal.aborted) { if (signal.aborted) {
+84 -94
View File
@@ -54,9 +54,8 @@ import {
type BskyAgent, type BskyAgent,
type RichText, type RichText,
} from '@atproto/api' } from '@atproto/api'
import {msg, plural} from '@lingui/core/macro' import {plural} from '@lingui/core/macro'
import {useLingui} from '@lingui/react' import {Trans, useLingui} from '@lingui/react/macro'
import {Trans} from '@lingui/react/macro'
import {useNavigation} from '@react-navigation/native' import {useNavigation} from '@react-navigation/native'
import {useQueryClient} from '@tanstack/react-query' import {useQueryClient} from '@tanstack/react-query'
@@ -197,12 +196,13 @@ export const ComposePost = ({
cancelRef?: React.RefObject<CancelRef | null> cancelRef?: React.RefObject<CancelRef | null>
}) => { }) => {
const {currentAccount} = useSession() const {currentAccount} = useSession()
const t = useTheme()
const ax = useAnalytics() const ax = useAnalytics()
const agent = useAgent() const agent = useAgent()
const queryClient = useQueryClient() const queryClient = useQueryClient()
const currentDid = currentAccount!.did const currentDid = currentAccount!.did
const {closeComposer} = useComposerControls() const {closeComposer} = useComposerControls()
const {_} = useLingui() const {t: l, i18n} = useLingui()
const requireAltTextEnabled = useRequireAltTextEnabled() const requireAltTextEnabled = useRequireAltTextEnabled()
const langPrefs = useLanguagePrefs() const langPrefs = useLanguagePrefs()
const setLangPrefs = useLanguagePrefsApi() const setLangPrefs = useLanguagePrefsApi()
@@ -313,7 +313,7 @@ export const ComposePost = ({
abortController, abortController,
}, },
}) })
processVideo( void processVideo(
asset, asset,
videoAction => { videoAction => {
composerDispatch({ composerDispatch({
@@ -328,10 +328,10 @@ export const ComposePost = ({
agent, agent,
currentDid, currentDid,
abortController.signal, abortController.signal,
_, i18n,
) )
}, },
[_, agent, currentDid, composerDispatch], [i18n, agent, currentDid, composerDispatch],
) )
const onInitVideo = useNonReactiveCallback(() => { const onInitVideo = useNonReactiveCallback(() => {
@@ -460,7 +460,7 @@ export const ComposePost = ({
} }
// Start video compression and upload // Start video compression and upload
processVideo( void processVideo(
asset, asset,
videoAction => { videoAction => {
composerDispatch({ composerDispatch({
@@ -475,7 +475,7 @@ export const ComposePost = ({
agent, agent,
currentDid, currentDid,
abortController.signal, abortController.signal,
_, i18n,
) )
} catch (e) { } catch (e) {
logger.error('Failed to restore video from draft', { logger.error('Failed to restore video from draft', {
@@ -484,7 +484,7 @@ export const ComposePost = ({
}) })
} }
}, },
[_, agent, currentDid, composerDispatch], [i18n, agent, currentDid, composerDispatch],
) )
const handleSelectDraft = useCallback( const handleSelectDraft = useCallback(
@@ -558,11 +558,11 @@ export const ComposePost = ({
const getDraftSaveError = useCallback( const getDraftSaveError = useCallback(
(e: unknown): string => { (e: unknown): string => {
if (e instanceof AppBskyDraftCreateDraft.DraftLimitReachedError) { if (e instanceof AppBskyDraftCreateDraft.DraftLimitReachedError) {
return _(msg`You've reached the maximum number of drafts`) return l`You've reached the maximum number of drafts`
} }
return _(msg`Failed to save draft`) return l`Failed to save draft`
}, },
[_], [l],
) )
const validateDraftTextOrError = useCallback((): boolean => { const validateDraftTextOrError = useCallback((): boolean => {
@@ -571,14 +571,12 @@ export const ComposePost = ({
) )
if (tooLong) { if (tooLong) {
setError( setError(
_( l`One or more posts are too long to save as a draft. ${plural(MAX_DRAFT_GRAPHEME_LENGTH, {one: 'The maximum number of characters is # character.', other: 'The maximum number of characters is # characters.'})}`,
msg`One or more posts are too long to save as a draft. ${plural(MAX_DRAFT_GRAPHEME_LENGTH, {one: 'The maximum number of characters is # character.', other: 'The maximum number of characters is # characters.'})}`,
),
) )
return false return false
} }
return true return true
}, [composerState.thread.posts, _]) }, [composerState.thread.posts, l])
const handleSaveDraft = useCallback(async () => { const handleSaveDraft = useCallback(async () => {
setError('') setError('')
@@ -768,21 +766,21 @@ export const ComposePost = ({
const media = thread.posts[i].embed.media const media = thread.posts[i].embed.media
if (media) { if (media) {
if (media.type === 'images' && media.images.some(img => !img.alt)) { if (media.type === 'images' && media.images.some(img => !img.alt)) {
return _(msg`One or more images is missing alt text.`) return l`One or more images is missing alt text.`
} }
if (media.type === 'gif' && !media.alt) { if (media.type === 'gif' && !media.alt) {
return _(msg`One or more GIFs is missing alt text.`) return l`One or more GIFs is missing alt text.`
} }
if ( if (
media.type === 'video' && media.type === 'video' &&
media.video.status !== 'error' && media.video.status !== 'error' &&
!media.video.altText !media.video.altText
) { ) {
return _(msg`One or more videos is missing alt text.`) return l`One or more videos is missing alt text.`
} }
} }
} }
}, [thread, requireAltTextEnabled, _]) }, [thread, requireAltTextEnabled, l])
const canPost = const canPost =
!missingAltError && !missingAltError &&
@@ -895,11 +893,9 @@ export const ComposePost = ({
let err = cleanError(e.message) let err = cleanError(e.message)
if (err.includes('not locate record')) { if (err.includes('not locate record')) {
err = _( err = l`We're sorry! The post you are replying to has been deleted.`
msg`We're sorry! The post you are replying to has been deleted.`,
)
} else if (e instanceof EmbeddingDisabledError) { } else if (e instanceof EmbeddingDisabledError) {
err = _(msg`This post's author has disabled quote posts.`) err = l`This post's author has disabled quote posts.`
} }
setError(err) setError(err)
setIsPublishing(false) setIsPublishing(false)
@@ -979,14 +975,14 @@ export const ComposePost = ({
<Toast.Icon /> <Toast.Icon />
<Toast.Text> <Toast.Text>
{thread.posts.length > 1 {thread.posts.length > 1
? _(msg`Your posts were sent`) ? l`Your posts were sent`
: replyTo : replyTo
? _(msg`Your reply was sent`) ? l`Your reply was sent`
: _(msg`Your post was sent`)} : l`Your post was sent`}
</Toast.Text> </Toast.Text>
{postUri && ( {postUri && (
<Toast.Action <Toast.Action
label={_(msg`View post`)} label={l`View post`}
onPress={() => { onPress={() => {
const {host: name, rkey} = new AtUri(postUri) const {host: name, rkey} = new AtUri(postUri)
navigation.navigate('PostThread', {name, rkey}) navigation.navigate('PostThread', {name, rkey})
@@ -1001,7 +997,7 @@ export const ComposePost = ({
) )
}, 500) }, 500)
}, [ }, [
_, l,
ax, ax,
agent, agent,
thread, thread,
@@ -1026,7 +1022,7 @@ export const ComposePost = ({
// Preserves the referential identity passed to each post item. // Preserves the referential identity passed to each post item.
// Avoids re-rendering all posts on each keystroke. // Avoids re-rendering all posts on each keystroke.
const onComposerPostPublish = useNonReactiveCallback(() => { const onComposerPostPublish = useNonReactiveCallback(() => {
onPressPublish() void onPressPublish()
}) })
useEffect(() => { useEffect(() => {
@@ -1047,7 +1043,7 @@ export const ComposePost = ({
setPublishOnUpload(false) setPublishOnUpload(false)
} else if (uploadingVideos === 0) { } else if (uploadingVideos === 0) {
setPublishOnUpload(false) setPublishOnUpload(false)
onPressPublish() void onPressPublish()
} }
} }
}, [thread.posts, onPressPublish, publishOnUpload]) }, [thread.posts, onPressPublish, publishOnUpload])
@@ -1189,7 +1185,13 @@ export const ComposePost = ({
layout={native(LinearTransition)} layout={native(LinearTransition)}
onScroll={scrollHandler} onScroll={scrollHandler}
contentContainerStyle={a.flex_grow} contentContainerStyle={a.flex_grow}
style={a.flex_1} style={[
a.flex_1,
web({
scrollbarGutter: 'stable',
scrollbarColor: `${t.palette.contrast_200} transparent`,
}),
]}
keyboardShouldPersistTaps="always" keyboardShouldPersistTaps="always"
onContentSizeChange={onScrollViewContentSizeChange} onContentSizeChange={onScrollViewContentSizeChange}
onLayout={onScrollViewLayout}> onLayout={onScrollViewLayout}>
@@ -1224,9 +1226,9 @@ export const ComposePost = ({
{replyTo ? ( {replyTo ? (
<Prompt.Basic <Prompt.Basic
control={discardPromptControl} control={discardPromptControl}
title={_(msg`Discard draft?`)} title={l`Discard draft?`}
description="" description=""
confirmButtonCta={_(msg`Discard`)} confirmButtonCta={l`Discard`}
confirmButtonColor="negative" confirmButtonColor="negative"
onConfirm={handleDiscard} onConfirm={handleDiscard}
/> />
@@ -1264,21 +1266,17 @@ export const ComposePost = ({
<Prompt.Actions> <Prompt.Actions>
{allPostsWithinLimit && ( {allPostsWithinLimit && (
<Prompt.Action <Prompt.Action
cta={ cta={composerState.draftId ? l`Save changes` : l`Save draft`}
composerState.draftId
? _(msg`Save changes`)
: _(msg`Save draft`)
}
onPress={handleSaveDraft} onPress={handleSaveDraft}
color="primary" color="primary"
/> />
)} )}
<Prompt.Action <Prompt.Action
cta={_(msg`Discard`)} cta={l`Discard`}
onPress={handleDiscard} onPress={handleDiscard}
color="negative_subtle" color="negative_subtle"
/> />
<Prompt.Cancel cta={_(msg`Keep editing`)} /> <Prompt.Cancel cta={l`Keep editing`} />
</Prompt.Actions> </Prompt.Actions>
</Prompt.Outer> </Prompt.Outer>
)} )}
@@ -1320,16 +1318,16 @@ let ComposerPost = memo(function ComposerPost({
}) { }) {
const {currentAccount} = useSession() const {currentAccount} = useSession()
const currentDid = currentAccount!.did const currentDid = currentAccount!.did
const {_} = useLingui() const {t: l} = useLingui()
const {data: currentProfile} = useProfileQuery({did: currentDid}) const {data: currentProfile} = useProfileQuery({did: currentDid})
const richtext = post.richtext const richtext = post.richtext
const isTextOnly = !post.embed.link && !post.embed.quote && !post.embed.media const isTextOnly = !post.embed.link && !post.embed.quote && !post.embed.media
const forceMinHeight = IS_WEB && isTextOnly && isActive const forceMinHeight = IS_WEB && isTextOnly && isActive
const selectTextInputPlaceholder = isReply const selectTextInputPlaceholder = isReply
? isFirstPost ? isFirstPost
? _(msg`Write your reply`) ? l`Write your reply`
: _(msg`Add another post`) : l`Add another post`
: _(msg`What's up?`) : l`What's up?`
const discardPromptControl = Prompt.usePromptControl() const discardPromptControl = Prompt.usePromptControl()
const dispatchPost = useCallback( const dispatchPost = useCallback(
@@ -1369,7 +1367,7 @@ let ComposerPost = memo(function ComposerPost({
if (IS_NATIVE) return // web only if (IS_NATIVE) return // web only
const [mimeType] = uri.slice('data:'.length).split(';') const [mimeType] = uri.slice('data:'.length).split(';')
if (!SUPPORTED_MIME_TYPES.includes(mimeType as SupportedMimeTypes)) { if (!SUPPORTED_MIME_TYPES.includes(mimeType as SupportedMimeTypes)) {
Toast.show(_(msg`Unsupported video type: ${mimeType}`), { Toast.show(l`Unsupported video type: ${mimeType}`, {
type: 'error', type: 'error',
}) })
return return
@@ -1384,7 +1382,7 @@ let ComposerPost = memo(function ComposerPost({
onImageAdd([res]) onImageAdd([res])
} }
}, },
[post.id, onSelectVideo, onImageAdd, _], [post.id, onSelectVideo, onImageAdd, l],
) )
useHideKeyboardOnBackground() useHideKeyboardOnBackground()
@@ -1429,19 +1427,20 @@ let ComposerPost = memo(function ComposerPost({
onError={onError} onError={onError}
onPressPublish={onPublish} onPressPublish={onPublish}
accessible={true} accessible={true}
accessibilityLabel={_(msg`Write post`)} accessibilityLabel={l`Write post`}
accessibilityHint={_( accessibilityHint={l`Compose posts up to ${plural(
msg`Compose posts up to ${plural(MAX_GRAPHEME_LENGTH || 0, { MAX_GRAPHEME_LENGTH || 0,
{
other: '# characters', other: '# characters',
})} in length`, },
)} )} in length`}
/> />
</View> </View>
{canRemovePost && isActive && ( {canRemovePost && isActive && (
<> <>
<Button <Button
label={_(msg`Delete post`)} label={l`Delete post`}
size="small" size="small"
color="secondary" color="secondary"
variant="ghost" variant="ghost"
@@ -1466,15 +1465,15 @@ let ComposerPost = memo(function ComposerPost({
</Button> </Button>
<Prompt.Basic <Prompt.Basic
control={discardPromptControl} control={discardPromptControl}
title={_(msg`Discard post?`)} title={l`Discard post?`}
description={_(msg`Are you sure you'd like to discard this post?`)} description={l`Are you sure you'd like to discard this post?`}
onConfirm={() => { onConfirm={() => {
dispatch({ dispatch({
type: 'remove_post', type: 'remove_post',
postId: post.id, postId: post.id,
}) })
}} }}
confirmButtonCta={_(msg`Discard`)} confirmButtonCta={l`Discard`}
confirmButtonColor="negative" confirmButtonColor="negative"
/> />
</> </>
@@ -1531,7 +1530,8 @@ function ComposerTopBar({
children?: React.ReactNode children?: React.ReactNode
}) { }) {
const t = useTheme() const t = useTheme()
const {_} = useLingui() const {t: l} = useLingui()
return ( return (
<Animated.View <Animated.View
style={topBarAnimatedStyle} style={topBarAnimatedStyle}
@@ -1544,7 +1544,7 @@ function ComposerTopBar({
IS_LIQUID_GLASS ? [a.px_lg, a.pt_lg, a.pb_md] : [a.p_sm], IS_LIQUID_GLASS ? [a.px_lg, a.pt_lg, a.pb_md] : [a.p_sm],
]}> ]}>
<Button <Button
label={_(msg`Cancel`)} label={l`Cancel`}
variant="ghost" variant="ghost"
color="primary" color="primary"
shape="default" shape="default"
@@ -1552,9 +1552,7 @@ function ComposerTopBar({
style={[{paddingLeft: 7, paddingRight: 7}]} style={[{paddingLeft: 7, paddingRight: 7}]}
hoverStyle={[a.bg_transparent, {opacity: 0.5}]} hoverStyle={[a.bg_transparent, {opacity: 0.5}]}
onPress={onCancel} onPress={onCancel}
accessibilityHint={_( accessibilityHint={l`Closes post composer and discards post draft`}>
msg`Closes post composer and discards post draft`,
)}>
<ButtonText style={[a.text_md]} maxFontSizeMultiplier={2}> <ButtonText style={[a.text_md]} maxFontSizeMultiplier={2}>
<Trans>Cancel</Trans> <Trans>Cancel</Trans>
</ButtonText> </ButtonText>
@@ -1588,35 +1586,27 @@ function ComposerTopBar({
label={ label={
isReply isReply
? isThread ? isThread
? _( ? l({
msg({
message: 'Publish replies', message: 'Publish replies',
comment: comment:
'Accessibility label for button to publish multiple replies in a thread', 'Accessibility label for button to publish multiple replies in a thread',
}), })
) : l({
: _(
msg({
message: 'Publish reply', message: 'Publish reply',
comment: comment:
'Accessibility label for button to publish a single reply', 'Accessibility label for button to publish a single reply',
}), })
)
: isThread : isThread
? _( ? l({
msg({
message: 'Publish posts', message: 'Publish posts',
comment: comment:
'Accessibility label for button to publish multiple posts in a thread', 'Accessibility label for button to publish multiple posts in a thread',
}), })
) : l({
: _(
msg({
message: 'Publish post', message: 'Publish post',
comment: comment:
'Accessibility label for button to publish a single post', 'Accessibility label for button to publish a single post',
}), })
)
} }
color="primary" color="primary"
size="small" size="small"
@@ -1851,7 +1841,7 @@ function ComposerFooter({
openGallery?: boolean openGallery?: boolean
}) { }) {
const t = useTheme() const t = useTheme()
const {_} = useLingui() const {t: l} = useLingui()
const {isMobile} = useWebMediaQueries() const {isMobile} = useWebMediaQueries()
/* /*
* Once we've allowed a certain type of asset to be selected, we don't allow * Once we've allowed a certain type of asset to be selected, we don't allow
@@ -1979,8 +1969,8 @@ function ComposerFooter({
<Button <Button
onPress={onEmojiButtonPress} onPress={onEmojiButtonPress}
style={a.p_sm} style={a.p_sm}
label={_(msg`Open emoji picker`)} label={l`Open emoji picker`}
accessibilityHint={_(msg`Opens emoji picker`)} accessibilityHint={l`Opens emoji picker`}
variant="ghost" variant="ghost"
shape="round" shape="round"
color="primary"> color="primary">
@@ -1994,7 +1984,7 @@ function ComposerFooter({
<View style={[a.flex_row, a.align_center, a.justify_between]}> <View style={[a.flex_row, a.align_center, a.justify_between]}>
{showAddButton && ( {showAddButton && (
<Button <Button
label={_(msg`Add another post to thread`)} label={l`Add another post to thread`}
onPress={onAddPost} onPress={onAddPost}
style={[a.p_sm]} style={[a.p_sm]}
variant="ghost" variant="ghost"
@@ -2276,7 +2266,7 @@ function ErrorBanner({
clearVideo: () => void clearVideo: () => void
}) { }) {
const t = useTheme() const t = useTheme()
const {_} = useLingui() const {t: l} = useLingui()
const videoError = const videoError =
videoState.status === 'error' ? videoState.error : undefined videoState.status === 'error' ? videoState.error : undefined
@@ -2311,7 +2301,7 @@ function ErrorBanner({
{error} {error}
</Text> </Text>
<Button <Button
label={_(msg`Dismiss error`)} label={l`Dismiss error`}
size="tiny" size="tiny"
color="secondary" color="secondary"
variant="ghost" variant="ghost"
@@ -2358,7 +2348,7 @@ function ToolbarWrapper({
function VideoUploadToolbar({state}: {state: VideoState}) { function VideoUploadToolbar({state}: {state: VideoState}) {
const t = useTheme() const t = useTheme()
const {_} = useLingui() const {t: l} = useLingui()
const progress = state.progress const progress = state.progress
const shouldRotate = const shouldRotate =
state.status === 'processing' && (progress === 0 || progress === 1) state.status === 'processing' && (progress === 0 || progress === 1)
@@ -2390,34 +2380,34 @@ function VideoUploadToolbar({state}: {state: VideoState}) {
switch (state.status) { switch (state.status) {
case 'compressing': case 'compressing':
if (isGif) { if (isGif) {
text = _(msg`Compressing GIF...`) text = l`Compressing GIF...`
} else { } else {
text = _(msg`Compressing video...`) text = l`Compressing video...`
} }
break break
case 'uploading': case 'uploading':
if (isGif) { if (isGif) {
text = _(msg`Uploading GIF...`) text = l`Uploading GIF...`
} else { } else {
text = _(msg`Uploading video...`) text = l`Uploading video...`
} }
break break
case 'processing': case 'processing':
if (isGif) { if (isGif) {
text = _(msg`Processing GIF...`) text = l`Processing GIF...`
} else { } else {
text = _(msg`Processing video...`) text = l`Processing video...`
} }
break break
case 'error': case 'error':
text = _(msg`Error`) text = l`Error`
wheelProgress = 100 wheelProgress = 100
break break
case 'done': case 'done':
if (isGif) { if (isGif) {
text = _(msg`GIF uploaded`) text = l`GIF uploaded`
} else { } else {
text = _(msg`Video uploaded`) text = l`Video uploaded`
} }
break break
} }
+19 -19
View File
@@ -263,7 +263,7 @@ export async function processVideo(
agent: BskyAgent, agent: BskyAgent,
did: string, did: string,
signal: AbortSignal, signal: AbortSignal,
_: I18n['_'], i18n: I18n,
) { ) {
let video: CompressedVideo | undefined let video: CompressedVideo | undefined
try { try {
@@ -274,7 +274,7 @@ export async function processVideo(
signal, signal,
}) })
} catch (e) { } catch (e) {
const message = getCompressErrorMessage(e, _) const message = getCompressErrorMessage(e, i18n)
if (message !== null) { if (message !== null) {
dispatch({ dispatch({
type: 'to_error', type: 'to_error',
@@ -297,13 +297,13 @@ export async function processVideo(
agent, agent,
did, did,
signal, signal,
_, i18n,
setProgress: p => { setProgress: p => {
dispatch({type: 'update_progress', progress: p, signal}) dispatch({type: 'update_progress', progress: p, signal})
}, },
}) })
} catch (e) { } catch (e) {
const message = getUploadErrorMessage(e, _) const message = getUploadErrorMessage(e, i18n)
if (message !== null) { if (message !== null) {
dispatch({ dispatch({
type: 'to_error', type: 'to_error',
@@ -355,7 +355,7 @@ export async function processVideo(
logger.error('Error processing video', {safeMessage: e}) logger.error('Error processing video', {safeMessage: e})
dispatch({ dispatch({
type: 'to_error', type: 'to_error',
error: _(msg`Video failed to process`), error: i18n._(msg`Video failed to process`),
signal, signal,
}) })
return // Exit async loop return // Exit async loop
@@ -387,20 +387,20 @@ export async function processVideo(
} }
} }
function getCompressErrorMessage(e: unknown, _: I18n['_']): string | null { function getCompressErrorMessage(e: unknown, i18n: I18n): string | null {
if (e instanceof AbortError) { if (e instanceof AbortError) {
return null return null
} }
if (e instanceof VideoTooLargeError) { if (e instanceof VideoTooLargeError) {
return _( return i18n._(
msg`The selected video is larger than 100 MB. Please try again with a smaller file.`, msg`The selected video is larger than 100 MB. Please try again with a smaller file.`,
) )
} }
logger.error('Error compressing video', {safeMessage: e}) logger.error('Error compressing video', {safeMessage: e})
return _(msg`An error occurred while compressing the video.`) return i18n._(msg`An error occurred while compressing the video.`)
} }
function getUploadErrorMessage(e: unknown, _: I18n['_']): string | null { function getUploadErrorMessage(e: unknown, i18n: I18n): string | null {
if (e instanceof AbortError) { if (e instanceof AbortError) {
return null return null
} }
@@ -408,38 +408,38 @@ function getUploadErrorMessage(e: unknown, _: I18n['_']): string | null {
// https://github.com/bluesky-social/tango/blob/lumi/lumi/worker/permissions.go#L77 // https://github.com/bluesky-social/tango/blob/lumi/lumi/worker/permissions.go#L77
switch (e.message) { switch (e.message) {
case 'User is not allowed to upload videos': case 'User is not allowed to upload videos':
return _(msg`You are not allowed to upload videos.`) return i18n._(msg`You are not allowed to upload videos.`)
case 'Uploading is disabled at the moment': case 'Uploading is disabled at the moment':
return _( return i18n._(
msg`Hold up! We’re gradually giving access to video, and you’re still waiting in line. Check back soon!`, msg`Hold up! We’re gradually giving access to video, and you’re still waiting in line. Check back soon!`,
) )
case "Failed to get user's upload stats": case "Failed to get user's upload stats":
return _( return i18n._(
msg`We were unable to determine if you are allowed to upload videos. Please try again.`, msg`We were unable to determine if you are allowed to upload videos. Please try again.`,
) )
case 'User has exceeded daily upload bytes limit': case 'User has exceeded daily upload bytes limit':
return _( return i18n._(
msg`You've reached your daily limit for video uploads (too many bytes)`, msg`You've reached your daily limit for video uploads (too many bytes)`,
) )
case 'User has exceeded daily upload videos limit': case 'User has exceeded daily upload videos limit':
return _( return i18n._(
msg`You've reached your daily limit for video uploads (too many videos)`, msg`You've reached your daily limit for video uploads (too many videos)`,
) )
case 'Account is not old enough to upload videos': case 'Account is not old enough to upload videos':
return _( return i18n._(
msg`Your account is not yet old enough to upload videos. Please try again later.`, msg`Your account is not yet old enough to upload videos. Please try again later.`,
) )
case 'file size (100000001 bytes) is larger than the maximum allowed size (100000000 bytes)': case 'file size (100000001 bytes) is larger than the maximum allowed size (100000000 bytes)':
return _( return i18n._(
msg`The selected video is larger than 100 MB. Please try again with a smaller file.`, msg`The selected video is larger than 100 MB. Please try again with a smaller file.`,
) )
case 'Confirm your email address to upload videos': case 'Confirm your email address to upload videos':
return _(msg`Please confirm your email address to upload videos.`) return i18n._(msg`Please confirm your email address to upload videos.`)
} }
} }
if (isNetworkError(e)) { if (isNetworkError(e)) {
return _( return i18n._(
msg`An error occurred while uploading the video. Please check your internet connection and try again.`, msg`An error occurred while uploading the video. Please check your internet connection and try again.`,
) )
} else { } else {
@@ -448,5 +448,5 @@ function getUploadErrorMessage(e: unknown, _: I18n['_']): string | null {
} }
const message = e instanceof Error ? e.message : '' const message = e instanceof Error ? e.message : ''
return _(msg`An error occurred while uploading the video. ${message}`) return i18n._(msg`An error occurred while uploading the video. ${message}`)
} }