diff --git a/src/lib/media/video/upload.shared.ts b/src/lib/media/video/upload.shared.ts index 1ab8439e6f..fd46e27868 100644 --- a/src/lib/media/video/upload.shared.ts +++ b/src/lib/media/video/upload.shared.ts @@ -30,7 +30,7 @@ export async function getServiceAuthToken({ return serviceAuth.token } -export async function getVideoUploadLimits(agent: BskyAgent, _: I18n['_']) { +export async function getVideoUploadLimits(agent: BskyAgent, i18n: I18n) { const token = await getServiceAuthToken({ agent, lxm: 'app.bsky.video.getUploadLimits', @@ -52,7 +52,7 @@ export async function getVideoUploadLimits(agent: BskyAgent, _: I18n['_']) { throw new UploadLimitError(limits.message) } else { throw new UploadLimitError( - _( + i18n._( msg`You have temporarily reached the limit for video uploads. Please try again later.`, ), ) diff --git a/src/lib/media/video/upload.ts b/src/lib/media/video/upload.ts index b7df3be52f..503577a76a 100644 --- a/src/lib/media/video/upload.ts +++ b/src/lib/media/video/upload.ts @@ -16,19 +16,19 @@ export async function uploadVideo({ did, setProgress, signal, - _, + i18n, }: { video: CompressedVideo agent: BskyAgent did: string setProgress: (progress: number) => void signal: AbortSignal - _: I18n['_'] + i18n: I18n }) { if (signal.aborted) { throw new AbortError() } - await getVideoUploadLimits(agent, _) + await getVideoUploadLimits(agent, i18n) const uri = createVideoEndpointUrl('/xrpc/app.bsky.video.uploadVideo', { did, @@ -69,7 +69,9 @@ export async function uploadVideo({ const responseBody = JSON.parse(res.body) as AppBskyVideoDefs.JobStatus 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) { diff --git a/src/lib/media/video/upload.web.ts b/src/lib/media/video/upload.web.ts index 2e78ec6d38..98d329a709 100644 --- a/src/lib/media/video/upload.web.ts +++ b/src/lib/media/video/upload.web.ts @@ -15,19 +15,19 @@ export async function uploadVideo({ did, setProgress, signal, - _, + i18n, }: { video: CompressedVideo agent: BskyAgent did: string setProgress: (progress: number) => void signal: AbortSignal - _: I18n['_'] + i18n: I18n }) { if (signal.aborted) { throw new AbortError() } - await getVideoUploadLimits(agent, _) + await getVideoUploadLimits(agent, i18n) const uri = createVideoEndpointUrl('/xrpc/app.bsky.video.uploadVideo', { did, @@ -70,11 +70,11 @@ export async function uploadVideo({ ) as AppBskyVideoDefs.JobStatus resolve(uploadRes) } else { - reject(new ServerError(_(msg`Failed to upload video`))) + reject(new ServerError(i18n._(msg`Failed to upload video`))) } } xhr.onerror = () => { - reject(new ServerError(_(msg`Failed to upload video`))) + reject(new ServerError(i18n._(msg`Failed to upload video`))) } xhr.open('POST', uri) xhr.setRequestHeader('Content-Type', video.mimeType) @@ -84,7 +84,7 @@ export async function uploadVideo({ ) 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) { diff --git a/src/view/com/composer/Composer.tsx b/src/view/com/composer/Composer.tsx index ceaa2fc395..f05ff232fb 100644 --- a/src/view/com/composer/Composer.tsx +++ b/src/view/com/composer/Composer.tsx @@ -54,9 +54,8 @@ import { type BskyAgent, type RichText, } from '@atproto/api' -import {msg, plural} from '@lingui/core/macro' -import {useLingui} from '@lingui/react' -import {Trans} from '@lingui/react/macro' +import {plural} from '@lingui/core/macro' +import {Trans, useLingui} from '@lingui/react/macro' import {useNavigation} from '@react-navigation/native' import {useQueryClient} from '@tanstack/react-query' @@ -197,12 +196,13 @@ export const ComposePost = ({ cancelRef?: React.RefObject }) => { const {currentAccount} = useSession() + const t = useTheme() const ax = useAnalytics() const agent = useAgent() const queryClient = useQueryClient() const currentDid = currentAccount!.did const {closeComposer} = useComposerControls() - const {_} = useLingui() + const {t: l, i18n} = useLingui() const requireAltTextEnabled = useRequireAltTextEnabled() const langPrefs = useLanguagePrefs() const setLangPrefs = useLanguagePrefsApi() @@ -313,7 +313,7 @@ export const ComposePost = ({ abortController, }, }) - processVideo( + void processVideo( asset, videoAction => { composerDispatch({ @@ -328,10 +328,10 @@ export const ComposePost = ({ agent, currentDid, abortController.signal, - _, + i18n, ) }, - [_, agent, currentDid, composerDispatch], + [i18n, agent, currentDid, composerDispatch], ) const onInitVideo = useNonReactiveCallback(() => { @@ -460,7 +460,7 @@ export const ComposePost = ({ } // Start video compression and upload - processVideo( + void processVideo( asset, videoAction => { composerDispatch({ @@ -475,7 +475,7 @@ export const ComposePost = ({ agent, currentDid, abortController.signal, - _, + i18n, ) } catch (e) { 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( @@ -558,11 +558,11 @@ export const ComposePost = ({ const getDraftSaveError = useCallback( (e: unknown): string => { 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 => { @@ -571,14 +571,12 @@ export const ComposePost = ({ ) if (tooLong) { setError( - _( - 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.'})}`, - ), + 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.'})}`, ) return false } return true - }, [composerState.thread.posts, _]) + }, [composerState.thread.posts, l]) const handleSaveDraft = useCallback(async () => { setError('') @@ -768,21 +766,21 @@ export const ComposePost = ({ const media = thread.posts[i].embed.media if (media) { 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) { - return _(msg`One or more GIFs is missing alt text.`) + return l`One or more GIFs is missing alt text.` } if ( media.type === 'video' && media.video.status !== 'error' && !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 = !missingAltError && @@ -895,11 +893,9 @@ export const ComposePost = ({ let err = cleanError(e.message) if (err.includes('not locate record')) { - err = _( - msg`We're sorry! The post you are replying to has been deleted.`, - ) + err = l`We're sorry! The post you are replying to has been deleted.` } 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) setIsPublishing(false) @@ -979,14 +975,14 @@ export const ComposePost = ({ {thread.posts.length > 1 - ? _(msg`Your posts were sent`) + ? l`Your posts were sent` : replyTo - ? _(msg`Your reply was sent`) - : _(msg`Your post was sent`)} + ? l`Your reply was sent` + : l`Your post was sent`} {postUri && ( { const {host: name, rkey} = new AtUri(postUri) navigation.navigate('PostThread', {name, rkey}) @@ -1001,7 +997,7 @@ export const ComposePost = ({ ) }, 500) }, [ - _, + l, ax, agent, thread, @@ -1026,7 +1022,7 @@ export const ComposePost = ({ // Preserves the referential identity passed to each post item. // Avoids re-rendering all posts on each keystroke. const onComposerPostPublish = useNonReactiveCallback(() => { - onPressPublish() + void onPressPublish() }) useEffect(() => { @@ -1047,7 +1043,7 @@ export const ComposePost = ({ setPublishOnUpload(false) } else if (uploadingVideos === 0) { setPublishOnUpload(false) - onPressPublish() + void onPressPublish() } } }, [thread.posts, onPressPublish, publishOnUpload]) @@ -1189,7 +1185,13 @@ export const ComposePost = ({ layout={native(LinearTransition)} onScroll={scrollHandler} contentContainerStyle={a.flex_grow} - style={a.flex_1} + style={[ + a.flex_1, + web({ + scrollbarGutter: 'stable', + scrollbarColor: `${t.palette.contrast_200} transparent`, + }), + ]} keyboardShouldPersistTaps="always" onContentSizeChange={onScrollViewContentSizeChange} onLayout={onScrollViewLayout}> @@ -1224,9 +1226,9 @@ export const ComposePost = ({ {replyTo ? ( @@ -1264,21 +1266,17 @@ export const ComposePost = ({ {allPostsWithinLimit && ( )} - + )} @@ -1320,16 +1318,16 @@ let ComposerPost = memo(function ComposerPost({ }) { const {currentAccount} = useSession() const currentDid = currentAccount!.did - const {_} = useLingui() + const {t: l} = useLingui() const {data: currentProfile} = useProfileQuery({did: currentDid}) const richtext = post.richtext const isTextOnly = !post.embed.link && !post.embed.quote && !post.embed.media const forceMinHeight = IS_WEB && isTextOnly && isActive const selectTextInputPlaceholder = isReply ? isFirstPost - ? _(msg`Write your reply`) - : _(msg`Add another post`) - : _(msg`What's up?`) + ? l`Write your reply` + : l`Add another post` + : l`What's up?` const discardPromptControl = Prompt.usePromptControl() const dispatchPost = useCallback( @@ -1369,7 +1367,7 @@ let ComposerPost = memo(function ComposerPost({ if (IS_NATIVE) return // web only const [mimeType] = uri.slice('data:'.length).split(';') if (!SUPPORTED_MIME_TYPES.includes(mimeType as SupportedMimeTypes)) { - Toast.show(_(msg`Unsupported video type: ${mimeType}`), { + Toast.show(l`Unsupported video type: ${mimeType}`, { type: 'error', }) return @@ -1384,7 +1382,7 @@ let ComposerPost = memo(function ComposerPost({ onImageAdd([res]) } }, - [post.id, onSelectVideo, onImageAdd, _], + [post.id, onSelectVideo, onImageAdd, l], ) useHideKeyboardOnBackground() @@ -1429,19 +1427,20 @@ let ComposerPost = memo(function ComposerPost({ onError={onError} onPressPublish={onPublish} accessible={true} - accessibilityLabel={_(msg`Write post`)} - accessibilityHint={_( - msg`Compose posts up to ${plural(MAX_GRAPHEME_LENGTH || 0, { + accessibilityLabel={l`Write post`} + accessibilityHint={l`Compose posts up to ${plural( + MAX_GRAPHEME_LENGTH || 0, + { other: '# characters', - })} in length`, - )} + }, + )} in length`} /> {canRemovePost && isActive && ( <>