diff options
Diffstat (limited to 'src/view')
-rw-r--r-- | src/view/com/composer/Composer.tsx | 15 | ||||
-rw-r--r-- | src/view/com/composer/videos/SubtitleFilePicker.tsx | 10 | ||||
-rw-r--r-- | src/view/com/profile/FollowButton.tsx | 11 | ||||
-rw-r--r-- | src/view/com/util/images/AutoSizedImage.tsx | 69 | ||||
-rw-r--r-- | src/view/com/util/post-embeds/VideoEmbedInner/TimeIndicator.tsx | 4 | ||||
-rw-r--r-- | src/view/com/util/post-embeds/VideoEmbedInner/VideoWebControls.tsx | 2 | ||||
-rw-r--r-- | src/view/screens/Profile.tsx | 11 |
7 files changed, 87 insertions, 35 deletions
diff --git a/src/view/com/composer/Composer.tsx b/src/view/com/composer/Composer.tsx index 4c7892bc0..dfdfb3ebd 100644 --- a/src/view/com/composer/Composer.tsx +++ b/src/view/com/composer/Composer.tsx @@ -59,7 +59,7 @@ import {useIsKeyboardVisible} from '#/lib/hooks/useIsKeyboardVisible' import {usePalette} from '#/lib/hooks/usePalette' import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' import {LikelyType} from '#/lib/link-meta/link-meta' -import {logEvent, useGate} from '#/lib/statsig/statsig' +import {logEvent} from '#/lib/statsig/statsig' import {cleanError} from '#/lib/strings/errors' import {insertMentionAt} from '#/lib/strings/mention-manip' import {shortenLinks} from '#/lib/strings/rich-text-manip' @@ -140,7 +140,6 @@ export const ComposePost = observer(function ComposePost({ }: Props & { cancelRef?: React.RefObject<CancelRef> }) { - const gate = useGate() const {currentAccount} = useSession() const agent = useAgent() const {data: currentProfile} = useProfileQuery({did: currentAccount!.did}) @@ -803,13 +802,11 @@ export const ComposePost = observer(function ComposePost({ ) : ( <ToolbarWrapper style={[a.flex_row, a.align_center, a.gap_xs]}> <SelectPhotoBtn gallery={gallery} disabled={!canSelectImages} /> - {gate('video_upload') && ( - <SelectVideoBtn - onSelectVideo={selectVideo} - disabled={!canSelectImages} - setError={setError} - /> - )} + <SelectVideoBtn + onSelectVideo={selectVideo} + disabled={!canSelectImages} + setError={setError} + /> <OpenCameraBtn gallery={gallery} disabled={!canSelectImages} /> <SelectGifBtn onClose={focusTextInput} diff --git a/src/view/com/composer/videos/SubtitleFilePicker.tsx b/src/view/com/composer/videos/SubtitleFilePicker.tsx index 9e0fe0aee..beb3f07a8 100644 --- a/src/view/com/composer/videos/SubtitleFilePicker.tsx +++ b/src/view/com/composer/videos/SubtitleFilePicker.tsx @@ -3,6 +3,7 @@ import {View} from 'react-native' import {msg, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' +import {logger} from '#/logger' import * as Toast from '#/view/com/util/Toast' import {atoms as a} from '#/alf' import {Button, ButtonIcon, ButtonText} from '#/components/Button' @@ -25,9 +26,16 @@ export function SubtitleFilePicker({ const handlePick = (evt: React.ChangeEvent<HTMLInputElement>) => { const selectedFile = evt.target.files?.[0] if (selectedFile) { - if (selectedFile.type === 'text/vtt') { + if ( + selectedFile.type === 'text/vtt' || + (selectedFile.type === 'text/plain' && + selectedFile.name.endsWith('.vtt')) + ) { onSelectFile(selectedFile) } else { + logger.error('Invalid subtitle file type', { + safeMessage: `File: ${selectedFile.name} (${selectedFile.type})`, + }) Toast.show(_(msg`Only WebVTT (.vtt) files are supported`)) } } diff --git a/src/view/com/profile/FollowButton.tsx b/src/view/com/profile/FollowButton.tsx index 42adea3cf..aaa5d3454 100644 --- a/src/view/com/profile/FollowButton.tsx +++ b/src/view/com/profile/FollowButton.tsx @@ -61,7 +61,7 @@ export function FollowButton({ label={_(msg({message: 'Unfollow', context: 'action'}))} /> ) - } else { + } else if (!profile.viewer.followedBy) { return ( <Button type={unfollowedType} @@ -70,5 +70,14 @@ export function FollowButton({ label={_(msg({message: 'Follow', context: 'action'}))} /> ) + } else { + return ( + <Button + type={unfollowedType} + labelStyle={labelStyle} + onPress={onPressFollow} + label={_(msg({message: 'Follow Back', context: 'action'}))} + /> + ) } } diff --git a/src/view/com/util/images/AutoSizedImage.tsx b/src/view/com/util/images/AutoSizedImage.tsx index f57ab4e3c..932a18280 100644 --- a/src/view/com/util/images/AutoSizedImage.tsx +++ b/src/view/com/util/images/AutoSizedImage.tsx @@ -10,7 +10,7 @@ import {Dimensions} from '#/lib/media/types' import {isNative} from '#/platform/detection' import {useLargeAltBadgeEnabled} from '#/state/preferences/large-alt-badge' import {atoms as a, useBreakpoints, useTheme} from '#/alf' -import {Crop_Stroke2_Corner0_Rounded as Crop} from '#/components/icons/Crop' +import {ArrowsDiagonalOut_Stroke2_Corner0_Rounded as Fullscreen} from '#/components/icons/ArrowsDiagonal' import {Text} from '#/components/Typography' export function useImageAspectRatio({ @@ -23,9 +23,10 @@ export function useImageAspectRatio({ const [raw, setAspectRatio] = React.useState<number>( dimensions ? calc(dimensions) : 1, ) + // this basically controls the width of the image const {isCropped, constrained, max} = React.useMemo(() => { - const a34 = 0.75 // max of 3:4 ratio in feeds - const constrained = Math.max(raw, a34) + const ratio = 1 / 2 // max of 1:2 ratio in feeds + const constrained = Math.max(raw, ratio) const max = Math.max(raw, 0.25) // max of 1:4 in thread const isCropped = raw < constrained return { @@ -68,14 +69,14 @@ export function ConstrainedImage({ const t = useTheme() const {gtMobile} = useBreakpoints() /** - * Computed as a % value to apply as `paddingTop` + * Computed as a % value to apply as `paddingTop`, this basically controls + * the height of the image. */ const outerAspectRatio = React.useMemo<DimensionValue>(() => { - // capped to square or shorter const ratio = isNative || !gtMobile - ? Math.min(1 / aspectRatio, 1.5) - : Math.min(1 / aspectRatio, 1) + ? Math.min(1 / aspectRatio, 16 / 9) // 9:16 bounding box + : Math.min(1 / aspectRatio, 1) // 1:1 bounding box return `${ratio * 100}%` }, [aspectRatio, gtMobile]) @@ -146,33 +147,59 @@ export function AutoSizedImage({ style={[ a.absolute, a.flex_row, - a.align_center, - a.rounded_xs, - t.atoms.bg_contrast_25, { - gap: 3, - padding: 3, bottom: a.p_xs.padding, right: a.p_xs.padding, - opacity: 0.8, + gap: 3, }, largeAlt && [ { gap: 4, - padding: 5, }, ], ]}> {isCropped && ( - <Crop - fill={t.atoms.text_contrast_high.color} - width={largeAlt ? 18 : 12} - /> + <View + style={[ + a.rounded_xs, + t.atoms.bg_contrast_25, + { + padding: 3, + opacity: 0.8, + }, + largeAlt && [ + { + padding: 5, + }, + ], + ]}> + <Fullscreen + fill={t.atoms.text_contrast_high.color} + width={largeAlt ? 18 : 12} + /> + </View> )} {hasAlt && ( - <Text style={[a.font_heavy, largeAlt ? a.text_xs : {fontSize: 8}]}> - ALT - </Text> + <View + style={[ + a.justify_center, + a.rounded_xs, + t.atoms.bg_contrast_25, + { + padding: 3, + opacity: 0.8, + }, + largeAlt && [ + { + padding: 5, + }, + ], + ]}> + <Text + style={[a.font_heavy, largeAlt ? a.text_xs : {fontSize: 8}]}> + ALT + </Text> + </View> )} </View> ) : null} diff --git a/src/view/com/util/post-embeds/VideoEmbedInner/TimeIndicator.tsx b/src/view/com/util/post-embeds/VideoEmbedInner/TimeIndicator.tsx index 6636883f1..be3f90711 100644 --- a/src/view/com/util/post-embeds/VideoEmbedInner/TimeIndicator.tsx +++ b/src/view/com/util/post-embeds/VideoEmbedInner/TimeIndicator.tsx @@ -37,11 +37,11 @@ export function TimeIndicator({time}: {time: number}) { ]}> <Text style={[ - {color: t.palette.white, fontSize: 12}, + {color: t.palette.white, fontSize: 12, fontVariant: ['tabular-nums']}, a.font_bold, {lineHeight: 1.25}, ]}> - {minutes}:{seconds} + {`${minutes}:${seconds}`} </Text> </Animated.View> ) diff --git a/src/view/com/util/post-embeds/VideoEmbedInner/VideoWebControls.tsx b/src/view/com/util/post-embeds/VideoEmbedInner/VideoWebControls.tsx index bb15db083..791025f70 100644 --- a/src/view/com/util/post-embeds/VideoEmbedInner/VideoWebControls.tsx +++ b/src/view/com/util/post-embeds/VideoEmbedInner/VideoWebControls.tsx @@ -370,7 +370,7 @@ export function Controls({ onPress={onPressPlayPause} /> <View style={a.flex_1} /> - <Text style={{color: t.palette.white}}> + <Text style={{color: t.palette.white, fontVariant: ['tabular-nums']}}> {formatTime(currentTime)} / {formatTime(duration)} </Text> {hasSubtitleTrack && ( diff --git a/src/view/screens/Profile.tsx b/src/view/screens/Profile.tsx index 37111c02e..5ef645981 100644 --- a/src/view/screens/Profile.tsx +++ b/src/view/screens/Profile.tsx @@ -41,6 +41,7 @@ import {ProfileFeedSection} from '#/screens/Profile/Sections/Feed' import {ProfileLabelsSection} from '#/screens/Profile/Sections/Labels' import {ScreenHider} from '#/components/moderation/ScreenHider' import {ProfileStarterPacks} from '#/components/StarterPack/ProfileStarterPacks' +import {navigate} from '#/Navigation' import {ExpoScrollForwarderView} from '../../../modules/expo-scroll-forwarder' import {ProfileFeedgens} from '../com/feeds/ProfileFeedgens' import {ProfileLists} from '../com/lists/ProfileLists' @@ -86,6 +87,16 @@ export function ProfileScreen({route}: Props) { } }, [resolveError, refetchDid, refetchProfile]) + // Apply hard-coded redirects as need + React.useEffect(() => { + if (resolveError) { + if (name === 'lulaoficial.bsky.social') { + console.log('Applying redirect to lula.com.br') + navigate('Profile', {name: 'lula.com.br'}) + } + } + }, [name, resolveError]) + // When we open the profile, we want to reset the posts query if we are blocked. React.useEffect(() => { if (resolvedDid && profile?.viewer?.blockedBy) { |