From e769c07fc54069a1950465935f642cdf2bdc2113 Mon Sep 17 00:00:00 2001 From: Eric Bailey Date: Tue, 17 Sep 2024 13:55:19 -0500 Subject: [PATCH] Pre-fill alt text with 10-million card post (#5389) * Pre-fill alt text with 10-million card post (#5377) * Clean up type * Tweak alt copy * Add pt translation, fix typo --------- Co-authored-by: Calvin (cherry picked from commit 2745cba3eae2e7f6dd803bbbb805599b2a99c834) --- .../dialogs/nuxs/TenMillion/index.tsx | 57 +++++++++++++------ src/locale/locales/ca/messages.po | 2 +- src/state/models/media/gallery.ts | 14 ++--- src/state/models/media/image.ts | 25 +++++--- src/state/shell/composer/index.tsx | 2 +- 5 files changed, 68 insertions(+), 32 deletions(-) diff --git a/src/components/dialogs/nuxs/TenMillion/index.tsx b/src/components/dialogs/nuxs/TenMillion/index.tsx index 4e7a171aad..8960824094 100644 --- a/src/components/dialogs/nuxs/TenMillion/index.tsx +++ b/src/components/dialogs/nuxs/TenMillion/index.tsx @@ -197,6 +197,27 @@ export function TenMillionInner({ const isLoadingData = isProfileLoading || !moderation || !profile const isLoadingImage = !uri + const displayName = React.useMemo(() => { + if (!profile || !moderation) return '' + return sanitizeDisplayName( + profile.displayName || sanitizeHandle(profile.handle), + moderation.ui('displayName'), + ) + }, [profile, moderation]) + const handle = React.useMemo(() => { + if (!profile) return '' + return sanitizeHandle(profile.handle, '@') + }, [profile]) + const joinedDate = React.useMemo(() => { + if (!profile || !profile.createdAt) return '' + const date = i18n.date(profile.createdAt, { + month: 'short', + day: 'numeric', + year: 'numeric', + }) + return date + }, [i18n, profile]) + const error: string = React.useMemo(() => { if (profileError) { return _( @@ -235,19 +256,34 @@ export function TenMillionInner({ msg`Bluesky now has over 10 million users, and I was #${i18n.number( userNumber, )}!`, - ), // TODO + ), imageUris: [ { uri, width: WIDTH, height: HEIGHT, + altText: _( + msg`A virtual certificate with text "Celebrating 10M users on Bluesky, #${i18n.number( + userNumber, + )}, ${displayName} ${handle}, joined on ${joinedDate}"`, + ), }, ], }) }, 1e3) }) } - }, [_, i18n, control, openComposer, uri, userNumber]) + }, [ + _, + i18n, + control, + openComposer, + uri, + userNumber, + displayName, + handle, + joinedDate, + ]) const onNativeShare = React.useCallback(() => { if (uri) { control.close(() => { @@ -490,11 +526,7 @@ export function TenMillionInner({ a.leading_tight, {maxWidth: '60%'}, ]}> - {sanitizeDisplayName( - profile.displayName || - sanitizeHandle(profile.handle), - moderation.ui('displayName'), - )} + {displayName} @@ -508,7 +540,7 @@ export function TenMillionInner({ a.leading_snug, lightTheme.atoms.text_contrast_medium, ]}> - {sanitizeHandle(profile.handle, '@')} + {handle} {profile.createdAt && ( @@ -524,14 +556,7 @@ export function TenMillionInner({ a.text_right, lightTheme.atoms.text_contrast_low, ]}> - - Joined{' '} - {i18n.date(profile.createdAt, { - month: 'short', - day: 'numeric', - year: 'numeric', - })} - + Joined on {joinedDate} )} diff --git a/src/locale/locales/ca/messages.po b/src/locale/locales/ca/messages.po index 180089fd1d..bb09cd6cf9 100644 --- a/src/locale/locales/ca/messages.po +++ b/src/locale/locales/ca/messages.po @@ -75,7 +75,7 @@ msgstr "{0, plural, one {# segon} other {# segons}}" #: src/components/KnownFollowers.tsx:179 #~ msgid "{0, plural, one {and # other} other {and # others}}" -#~ msgstr "{0, plural, one {i # altre} other {i # altres}" +#~ msgstr "{0, plural, one {i # altre} other {i # altres}}" #: src/components/ProfileHoverCard/index.web.tsx:398 #: src/screens/Profile/Header/Metrics.tsx:23 diff --git a/src/state/models/media/gallery.ts b/src/state/models/media/gallery.ts index 9c8c13010d..828905002e 100644 --- a/src/state/models/media/gallery.ts +++ b/src/state/models/media/gallery.ts @@ -1,19 +1,20 @@ import {makeAutoObservable, runInAction} from 'mobx' -import {ImageModel} from './image' -import {Image as RNImage} from 'react-native-image-crop-picker' -import {openPicker} from 'lib/media/picker' + import {getImageDim} from 'lib/media/manip' +import {openPicker} from 'lib/media/picker' +import {ImageInitOptions, ImageModel} from './image' interface InitialImageUri { uri: string width: number height: number + altText?: string } export class GalleryModel { images: ImageModel[] = [] - constructor(uris?: {uri: string; width: number; height: number}[]) { + constructor(uris?: InitialImageUri[]) { makeAutoObservable(this) if (uris) { @@ -33,7 +34,7 @@ export class GalleryModel { return this.images.some(image => image.altText.trim() === '') } - *add(image_: Omit) { + *add(image_: ImageInitOptions) { if (this.size >= 4) { return } @@ -59,7 +60,6 @@ export class GalleryModel { path: uri, height, width, - mime: 'image/jpeg', } runInAction(() => { @@ -100,10 +100,10 @@ export class GalleryModel { async addFromUris(uris: InitialImageUri[]) { for (const uriObj of uris) { this.add({ - mime: 'image/jpeg', height: uriObj.height, width: uriObj.width, path: uriObj.uri, + altText: uriObj.altText, }) } } diff --git a/src/state/models/media/image.ts b/src/state/models/media/image.ts index 5c547c148f..55f6364911 100644 --- a/src/state/models/media/image.ts +++ b/src/state/models/media/image.ts @@ -1,14 +1,15 @@ import {Image as RNImage} from 'react-native-image-crop-picker' -import {makeAutoObservable, runInAction} from 'mobx' -import {POST_IMG_MAX} from 'lib/constants' import * as ImageManipulator from 'expo-image-manipulator' -import {getDataUriSize} from 'lib/media/util' -import {openCropper} from 'lib/media/picker' import {ActionCrop, FlipType, SaveFormat} from 'expo-image-manipulator' +import {makeAutoObservable, runInAction} from 'mobx' import {Position} from 'react-avatar-editor' -import {Dimensions} from 'lib/media/types' -import {isIOS} from 'platform/detection' + import {logger} from '#/logger' +import {POST_IMG_MAX} from 'lib/constants' +import {openCropper} from 'lib/media/picker' +import {Dimensions} from 'lib/media/types' +import {getDataUriSize} from 'lib/media/util' +import {isIOS} from 'platform/detection' export interface ImageManipulationAttributes { aspectRatio?: '4:3' | '1:1' | '3:4' | 'None' @@ -19,6 +20,13 @@ export interface ImageManipulationAttributes { flipVertical?: boolean } +export interface ImageInitOptions { + path: string + width: number + height: number + altText?: string +} + const MAX_IMAGE_SIZE_IN_BYTES = 976560 export class ImageModel implements Omit { @@ -41,12 +49,15 @@ export class ImageModel implements Omit { } prevAttributes: ImageManipulationAttributes = {} - constructor(image: Omit) { + constructor(image: ImageInitOptions) { makeAutoObservable(this) this.path = image.path this.width = image.width this.height = image.height + if (image.altText !== undefined) { + this.setAltText(image.altText) + } } setRatio(aspectRatio: ImageManipulationAttributes['aspectRatio']) { diff --git a/src/state/shell/composer/index.tsx b/src/state/shell/composer/index.tsx index 612388ff86..6755ec9a66 100644 --- a/src/state/shell/composer/index.tsx +++ b/src/state/shell/composer/index.tsx @@ -36,7 +36,7 @@ export interface ComposerOpts { mention?: string // handle of user to mention openEmojiPicker?: (pos: DOMRect | undefined) => void text?: string - imageUris?: {uri: string; width: number; height: number}[] + imageUris?: {uri: string; width: number; height: number; altText?: string}[] } type StateContext = ComposerOpts | undefined