Merge branch 'bluesky-social:main' into patch-3

This commit is contained in:
Minseo Lee 2024-02-22 09:37:09 +09:00 committed by GitHub
commit c2d87b8075
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
48 changed files with 667 additions and 581 deletions

View file

@ -12,7 +12,7 @@ import {createFullHandle} from '#/lib/strings/handles'
import {cleanError} from '#/lib/strings/errors'
import {useOnboardingDispatch} from '#/state/shell/onboarding'
import {useSessionApi} from '#/state/session'
import {DEFAULT_SERVICE, IS_PROD} from '#/lib/constants'
import {DEFAULT_SERVICE, IS_PROD_SERVICE} from '#/lib/constants'
import {
DEFAULT_PROD_FEEDS,
usePreferencesSetBirthDateMutation,
@ -147,7 +147,7 @@ export function useSubmitCreateAccount(
: undefined,
})
setBirthDate({birthDate: uiState.birthDate})
if (IS_PROD(uiState.serviceUrl)) {
if (IS_PROD_SERVICE(uiState.serviceUrl)) {
setSavedFeeds(DEFAULT_PROD_FEEDS)
}
} catch (e: any) {

View file

@ -2,7 +2,7 @@ import React from 'react'
import {View} from 'react-native'
import {useLingui} from '@lingui/react'
import {Trans, msg} from '@lingui/macro'
import {PROD_SERVICE} from 'lib/constants'
import {BSKY_SERVICE} from 'lib/constants'
import * as persisted from '#/state/persisted'
import {atoms as a, useBreakpoints, useTheme} from '#/alf'
@ -26,7 +26,7 @@ export function ServerInputDialog({
const [pdsAddressHistory, setPdsAddressHistory] = React.useState<string[]>(
persisted.get('pdsAddressHistory') || [],
)
const [fixedOption, setFixedOption] = React.useState([PROD_SERVICE])
const [fixedOption, setFixedOption] = React.useState([BSKY_SERVICE])
const [customAddress, setCustomAddress] = React.useState('')
const onClose = React.useCallback(() => {
@ -86,7 +86,7 @@ export function ServerInputDialog({
label="Preferences"
values={fixedOption}
onChange={setFixedOption}>
<ToggleButton.Button name={PROD_SERVICE} label={_(msg`Bluesky`)}>
<ToggleButton.Button name={BSKY_SERVICE} label={_(msg`Bluesky`)}>
{_(msg`Bluesky`)}
</ToggleButton.Button>
<ToggleButton.Button

View file

@ -2,7 +2,7 @@ import React from 'react'
import {Pressable, StyleProp, StyleSheet, View, ViewStyle} from 'react-native'
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
import {Text} from '../util/text/Text'
import {RichText} from '../util/text/RichText'
import {RichText} from '#/components/RichText'
import {usePalette} from 'lib/hooks/usePalette'
import {s} from 'lib/styles'
import {UserAvatar} from '../util/UserAvatar'
@ -25,6 +25,7 @@ import {
} from '#/state/queries/preferences'
import {useFeedSourceInfoQuery, FeedSourceInfo} from '#/state/queries/feed'
import {FeedLoadingPlaceholder} from '#/view/com/util/LoadingPlaceholder'
import {useTheme} from '#/alf'
export function FeedSourceCard({
feedUri,
@ -82,6 +83,7 @@ export function FeedSourceCardLoaded({
pinOnSave?: boolean
showMinimalPlaceholder?: boolean
}) {
const t = useTheme()
const pal = usePalette('default')
const {_} = useLingui()
const navigation = useNavigation<NavigationProp>()
@ -266,8 +268,8 @@ export function FeedSourceCardLoaded({
{showDescription && feed.description ? (
<RichText
style={[pal.textLight, styles.description]}
richText={feed.description}
style={[t.atoms.text_contrast_high, styles.description]}
value={feed.description}
numberOfLines={3}
/>
) : null}

View file

@ -3,7 +3,7 @@ import {StyleProp, StyleSheet, View, ViewStyle} from 'react-native'
import {AtUri, AppBskyGraphDefs, RichText} from '@atproto/api'
import {Link} from '../util/Link'
import {Text} from '../util/text/Text'
import {RichText as RichTextCom} from '../util/text/RichText'
import {RichText as RichTextCom} from '#/components/RichText'
import {UserAvatar} from '../util/UserAvatar'
import {s} from 'lib/styles'
import {usePalette} from 'lib/hooks/usePalette'
@ -12,6 +12,7 @@ import {sanitizeDisplayName} from 'lib/strings/display-names'
import {sanitizeHandle} from 'lib/strings/handles'
import {makeProfileLink} from 'lib/routes/links'
import {Trans} from '@lingui/macro'
import {atoms as a} from '#/alf'
export const ListCard = ({
testID,
@ -119,9 +120,9 @@ export const ListCard = ({
{descriptionRichText ? (
<View style={styles.details}>
<RichTextCom
style={[pal.text, s.flex1]}
style={[a.flex_1]}
numberOfLines={20}
richText={descriptionRichText}
value={descriptionRichText}
/>
</View>
) : undefined}

View file

@ -11,7 +11,7 @@ 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 '../util/text/RichText'
import {RichText} from '#/components/RichText'
import {Text} from '../util/text/Text'
import {PreviewableUserAvatar} from '../util/UserAvatar'
import {s} from 'lib/styles'
@ -44,6 +44,7 @@ import {ThreadPost} from '#/state/queries/post-thread'
import {useSession} from 'state/session'
import {WhoCanReply} from '../threadgate/WhoCanReply'
import {LoadingPlaceholder} from '../util/LoadingPlaceholder'
import {atoms as a} from '#/alf'
export function PostThreadItem({
post,
@ -326,10 +327,8 @@ let PostThreadItemLoaded = ({
styles.postTextLargeContainer,
]}>
<RichText
type="post-text-lg"
richText={richText}
lineHeight={1.3}
style={s.flex1}
value={richText}
style={[a.flex_1, a.text_xl]}
selectable
/>
</View>
@ -522,10 +521,8 @@ let PostThreadItemLoaded = ({
{richText?.text ? (
<View style={styles.postTextContainer}>
<RichText
type="post-text"
richText={richText}
style={[pal.text, s.flex1]}
lineHeight={1.3}
value={richText}
style={[a.flex_1, a.text_md]}
numberOfLines={limitLines ? MAX_POST_LINES : undefined}
/>
</View>

View file

@ -17,7 +17,7 @@ import {PostCtrls} from '../util/post-ctrls/PostCtrls'
import {ContentHider} from '../util/moderation/ContentHider'
import {PostAlerts} from '../util/moderation/PostAlerts'
import {Text} from '../util/text/Text'
import {RichText} from '../util/text/RichText'
import {RichText} from '#/components/RichText'
import {PreviewableUserAvatar} from '../util/UserAvatar'
import {s, colors} from 'lib/styles'
import {usePalette} from 'lib/hooks/usePalette'
@ -29,6 +29,7 @@ import {useComposerControls} from '#/state/shell/composer'
import {Shadow, usePostShadow, POST_TOMBSTONE} from '#/state/cache/post-shadow'
import {Trans, msg} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {atoms as a} from '#/alf'
export function Post({
post,
@ -184,11 +185,9 @@ function PostInner({
<View style={styles.postTextContainer}>
<RichText
testID="postText"
type="post-text"
richText={richText}
lineHeight={1.3}
value={richText}
numberOfLines={limitLines ? MAX_POST_LINES : undefined}
style={s.flex1}
style={[a.flex_1, a.text_md]}
/>
</View>
) : undefined}

View file

@ -20,7 +20,7 @@ import {PostCtrls} from '../util/post-ctrls/PostCtrls'
import {PostEmbeds} from '../util/post-embeds'
import {ContentHider} from '../util/moderation/ContentHider'
import {PostAlerts} from '../util/moderation/PostAlerts'
import {RichText} from '../util/text/RichText'
import {RichText} from '#/components/RichText'
import {PreviewableUserAvatar} from '../util/UserAvatar'
import {s} from 'lib/styles'
import {usePalette} from 'lib/hooks/usePalette'
@ -36,6 +36,7 @@ import {FeedNameText} from '../util/FeedInfoText'
import {useSession} from '#/state/session'
import {Trans, msg} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {atoms as a} from '#/alf'
export function FeedItem({
post,
@ -347,11 +348,9 @@ let PostContent = ({
<View style={styles.postTextContainer}>
<RichText
testID="postText"
type="post-text"
richText={richText}
lineHeight={1.3}
value={richText}
numberOfLines={limitLines ? MAX_POST_LINES : undefined}
style={s.flex1}
style={[a.flex_1, a.text_md]}
/>
</View>
) : undefined}

View file

@ -23,7 +23,7 @@ import * as Toast from '../util/Toast'
import {LoadingPlaceholder} from '../util/LoadingPlaceholder'
import {Text} from '../util/text/Text'
import {ThemedText} from '../util/text/ThemedText'
import {RichText} from '../util/text/RichText'
import {RichText} from '#/components/RichText'
import {UserAvatar} from '../util/UserAvatar'
import {UserBanner} from '../util/UserBanner'
import {ProfileHeaderAlerts} from '../util/moderation/ProfileHeaderAlerts'
@ -56,6 +56,7 @@ import {Shadow} from '#/state/cache/types'
import {useRequireAuth} from '#/state/session'
import {LabelInfo} from '../util/moderation/LabelInfo'
import {useProfileShadow} from 'state/cache/profile-shadow'
import {atoms as a} from '#/alf'
let ProfileHeaderLoading = (_props: {}): React.ReactNode => {
const pal = usePalette('default')
@ -608,12 +609,12 @@ let ProfileHeader = ({
</Text>
</View>
{descriptionRT && !moderation.profile.blur ? (
<View pointerEvents="auto">
<View pointerEvents="auto" style={[styles.description]}>
<RichText
testID="profileHeaderDescription"
style={[styles.description, pal.text]}
style={[a.text_md]}
numberOfLines={15}
richText={descriptionRT}
value={descriptionRT}
/>
</View>
) : undefined}

View file

@ -1,11 +1,14 @@
import React from 'react'
import {View} from 'react-native'
import {View, ViewStyle} from 'react-native'
/**
* This utility function captures events and stops
* them from propagating upwards.
*/
export function EventStopper({children}: React.PropsWithChildren<{}>) {
export function EventStopper({
children,
style,
}: React.PropsWithChildren<{style?: ViewStyle | ViewStyle[]}>) {
const stop = (e: any) => {
e.stopPropagation()
}
@ -15,7 +18,8 @@ export function EventStopper({children}: React.PropsWithChildren<{}>) {
onTouchEnd={stop}
// @ts-ignore web only -prf
onClick={stop}
onKeyDown={stop}>
onKeyDown={stop}
style={style}>
{children}
</View>
)

View file

@ -2,6 +2,7 @@ import React, {memo} from 'react'
import {StyleProp, View, ViewStyle} from 'react-native'
import Clipboard from '@react-native-clipboard/clipboard'
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
import {useNavigation} from '@react-navigation/native'
import {
AppBskyActorDefs,
AppBskyFeedPost,
@ -19,6 +20,8 @@ import * as Toast from '../Toast'
import {EventStopper} from '../EventStopper'
import {useModalControls} from '#/state/modals'
import {makeProfileLink} from '#/lib/routes/links'
import {CommonNavigatorParams} from '#/lib/routes/types'
import {getCurrentRoute} from 'lib/routes/helpers'
import {getTranslatorLink} from '#/locale/helpers'
import {usePostDeleteMutation} from '#/state/queries/post'
import {useMutedThreads, useToggleThreadMute} from '#/state/muted-threads'
@ -63,6 +66,7 @@ let PostDropdownBtn = ({
const hiddenPosts = useHiddenPosts()
const {hidePost} = useHiddenPostsApi()
const openLink = useOpenLink()
const navigation = useNavigation()
const rootUri = record.reply?.root?.uri || postUri
const isThreadMuted = mutedThreads.includes(rootUri)
@ -82,13 +86,38 @@ let PostDropdownBtn = ({
postDeleteMutation.mutateAsync({uri: postUri}).then(
() => {
Toast.show(_(msg`Post deleted`))
const route = getCurrentRoute(navigation.getState())
if (route.name === 'PostThread') {
const params = route.params as CommonNavigatorParams['PostThread']
if (
currentAccount &&
isAuthor &&
(params.name === currentAccount.handle ||
params.name === currentAccount.did)
) {
const currentHref = makeProfileLink(postAuthor, 'post', params.rkey)
if (currentHref === href && navigation.canGoBack()) {
navigation.goBack()
}
}
}
},
e => {
logger.error('Failed to delete post', {message: e})
Toast.show(_(msg`Failed to delete post, please try again`))
},
)
}, [postUri, postDeleteMutation, _])
}, [
navigation,
postUri,
postDeleteMutation,
postAuthor,
currentAccount,
isAuthor,
href,
_,
])
const onToggleThreadMute = React.useCallback(() => {
try {

View file

@ -21,7 +21,7 @@ import {msg} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {useNavigation} from '@react-navigation/native'
import {AppBskyEmbedExternal} from '@atproto/api'
import {EmbedPlayerParams, getPlayerHeight} from 'lib/strings/embed-player'
import {EmbedPlayerParams, getPlayerAspect} from 'lib/strings/embed-player'
import {EventStopper} from '../EventStopper'
import {isNative} from 'platform/detection'
import {NavigationProp} from 'lib/routes/types'
@ -67,14 +67,12 @@ function PlaceholderOverlay({
// This renders the webview/youtube player as a separate layer
function Player({
height,
params,
onLoad,
isPlayerActive,
}: {
isPlayerActive: boolean
params: EmbedPlayerParams
height: number
onLoad: () => void
}) {
// ensures we only load what's requested
@ -91,25 +89,21 @@ function Player({
if (!isPlayerActive) return null
return (
<View style={[styles.layer, styles.playerLayer]}>
<EventStopper>
<View style={{height, width: '100%'}}>
<WebView
javaScriptEnabled={true}
onShouldStartLoadWithRequest={onShouldStartLoadWithRequest}
mediaPlaybackRequiresUserAction={false}
allowsInlineMediaPlayback
bounces={false}
allowsFullscreenVideo
nestedScrollEnabled
source={{uri: params.playerUri}}
onLoad={onLoad}
setSupportMultipleWindows={false} // Prevent any redirects from opening a new window (ads)
style={[styles.webview, styles.topRadius]}
/>
</View>
</EventStopper>
</View>
<EventStopper style={[styles.layer, styles.playerLayer]}>
<WebView
javaScriptEnabled={true}
onShouldStartLoadWithRequest={onShouldStartLoadWithRequest}
mediaPlaybackRequiresUserAction={false}
allowsInlineMediaPlayback
bounces={false}
allowsFullscreenVideo
nestedScrollEnabled
source={{uri: params.playerUri}}
onLoad={onLoad}
style={styles.webview}
setSupportMultipleWindows={false} // Prevent any redirects from opening a new window (ads)
/>
</EventStopper>
)
}
@ -129,13 +123,16 @@ export function ExternalPlayer({
const [isPlayerActive, setPlayerActive] = React.useState(false)
const [isLoading, setIsLoading] = React.useState(true)
const [dim, setDim] = React.useState({
width: 0,
height: 0,
})
const aspect = React.useMemo(() => {
return getPlayerAspect({
type: params.type,
width: windowDims.width,
hasThumb: !!link.thumb,
})
}, [params.type, windowDims.width, link.thumb])
const viewRef = useAnimatedRef()
const frameCallback = useFrameCallback(() => {
const measurement = measure(viewRef)
if (!measurement) return
@ -180,17 +177,6 @@ export function ExternalPlayer({
}
}, [navigation, isPlayerActive, frameCallback])
// calculate height for the player and the screen size
const height = React.useMemo(
() =>
getPlayerHeight({
type: params.type,
width: dim.width,
hasThumb: !!link.thumb,
}),
[params.type, dim.width, link.thumb],
)
const onLoad = React.useCallback(() => {
setIsLoading(false)
}, [])
@ -216,32 +202,11 @@ export function ExternalPlayer({
[externalEmbedsPrefs, openModal, params.source],
)
// measure the layout to set sizing
const onLayout = React.useCallback(
(event: {nativeEvent: {layout: {width: any; height: any}}}) => {
setDim({
width: event.nativeEvent.layout.width,
height: event.nativeEvent.layout.height,
})
},
[],
)
return (
<Animated.View
ref={viewRef}
style={{height}}
collapsable={false}
onLayout={onLayout}>
<Animated.View ref={viewRef} collapsable={false} style={[aspect]}>
{link.thumb && (!isPlayerActive || isLoading) && (
<Image
style={[
{
width: dim.width,
height,
},
styles.topRadius,
]}
style={[{flex: 1}, styles.topRadius]}
source={{uri: link.thumb}}
accessibilityIgnoresInvertColors
/>
@ -251,12 +216,7 @@ export function ExternalPlayer({
isPlayerActive={isPlayerActive}
onPress={onPlayPress}
/>
<Player
isPlayerActive={isPlayerActive}
params={params}
height={height}
onLoad={onLoad}
/>
<Player isPlayerActive={isPlayerActive} params={params} onLoad={onLoad} />
</Animated.View>
)
}

View file

@ -20,7 +20,8 @@ import {PostAlerts} from '../moderation/PostAlerts'
import {makeProfileLink} from 'lib/routes/links'
import {InfoCircleIcon} from 'lib/icons'
import {Trans} from '@lingui/macro'
import {RichText} from 'view/com/util/text/RichText'
import {RichText} from '#/components/RichText'
import {atoms as a} from '#/alf'
export function MaybeQuoteEmbed({
embed,
@ -127,11 +128,10 @@ export function QuoteEmbed({
) : null}
{richText ? (
<RichText
richText={richText}
type="post-text"
style={pal.text}
value={richText}
style={[a.text_md]}
numberOfLines={20}
noLinks
disableLinks
/>
) : null}
{embed && <PostEmbeds embed={embed} moderation={{}} />}

View file

@ -10,6 +10,9 @@ import {usePalette} from 'lib/hooks/usePalette'
const WORD_WRAP = {wordWrap: 1}
/**
* @deprecated use `#/components/RichText`
*/
export function RichText({
testID,
type = 'md',

View file

@ -17,7 +17,7 @@ import {TextLink} from 'view/com/util/Link'
import {ListRef} from 'view/com/util/List'
import {Button} from 'view/com/util/forms/Button'
import {Text} from 'view/com/util/text/Text'
import {RichText} from 'view/com/util/text/RichText'
import {RichText} from '#/components/RichText'
import {LoadLatestBtn} from 'view/com/util/load-latest/LoadLatestBtn'
import {FAB} from 'view/com/util/fab/FAB'
import {EmptyState} from 'view/com/util/EmptyState'
@ -59,6 +59,7 @@ import {useComposerControls} from '#/state/shell/composer'
import {truncateAndInvalidate} from '#/state/queries/util'
import {isNative} from '#/platform/detection'
import {listenSoftReset} from '#/state/events'
import {atoms as a} from '#/alf'
const SECTION_TITLES = ['Posts', 'About']
@ -575,9 +576,8 @@ function AboutSection({
{feedInfo.description ? (
<RichText
testID="listDescription"
type="lg"
style={pal.text}
richText={feedInfo.description}
style={[a.text_md]}
value={feedInfo.description}
/>
) : (
<Text type="lg" style={[{fontStyle: 'italic'}, pal.textLight]}>

View file

@ -14,7 +14,7 @@ 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 'view/com/util/text/RichText'
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'
@ -60,6 +60,7 @@ import {
import {logger} from '#/logger'
import {useAnalytics} from '#/lib/analytics/analytics'
import {listenSoftReset} from '#/state/events'
import {atoms as a} from '#/alf'
const SECTION_TITLES_CURATE = ['Posts', 'About']
const SECTION_TITLES_MOD = ['About']
@ -742,9 +743,8 @@ const AboutSection = React.forwardRef<SectionRef, AboutSectionProps>(
{descriptionRT ? (
<RichText
testID="listDescription"
type="lg"
style={pal.text}
richText={descriptionRT}
style={[a.text_md]}
value={descriptionRT}
/>
) : (
<Text

View file

@ -76,7 +76,7 @@ export function ExportCarDialog({
This feature is in beta. You can read more about repository
exports in{' '}
<InlineLink
to="https://atproto.com/blog/repo-export"
to="https://docs.bsky.app/blog/repo-export"
style={[a.text_sm]}>
this blogpost
</InlineLink>

View file

@ -9,7 +9,8 @@ import * as Prompt from '#/components/Prompt'
import {useDialogStateControlContext} from '#/state/dialogs'
export function Dialogs() {
const control = Dialog.useDialogControl()
const scrollable = Dialog.useDialogControl()
const basic = Dialog.useDialogControl()
const prompt = Prompt.usePromptControl()
const {closeAllDialogs} = useDialogStateControlContext()
@ -20,8 +21,31 @@ export function Dialogs() {
color="secondary"
size="small"
onPress={() => {
control.open()
scrollable.open()
prompt.open()
basic.open()
}}
label="Open basic dialog">
Open all dialogs
</Button>
<Button
variant="outline"
color="secondary"
size="small"
onPress={() => {
scrollable.open()
}}
label="Open basic dialog">
Open scrollable dialog
</Button>
<Button
variant="outline"
color="secondary"
size="small"
onPress={() => {
basic.open()
}}
label="Open basic dialog">
Open basic dialog
@ -48,9 +72,18 @@ export function Dialogs() {
</Prompt.Actions>
</Prompt.Outer>
<Dialog.Outer control={basic}>
<Dialog.Handle />
<Dialog.Inner label="test">
<H3 nativeID="dialog-title">Dialog</H3>
<P nativeID="dialog-description">A basic dialog</P>
</Dialog.Inner>
</Dialog.Outer>
<Dialog.Outer
control={control}
nativeOptions={{sheet: {snapPoints: ['90%']}}}>
control={scrollable}
nativeOptions={{sheet: {snapPoints: ['100%']}}}>
<Dialog.Handle />
<Dialog.ScrollableInner
@ -77,9 +110,13 @@ export function Dialogs() {
variant="outline"
color="primary"
size="small"
onPress={() => control.close()}
onPress={() =>
scrollable.close(() => {
console.log('CLOSED')
})
}
label="Open basic dialog">
Close basic dialog
Close dialog
</Button>
</View>
</View>

View file

@ -8,7 +8,9 @@ import {RichText} from '#/components/RichText'
export function Typography() {
return (
<View style={[a.gap_md]}>
<Text style={[a.text_5xl]}>atoms.text_5xl</Text>
<Text selectable style={[a.text_5xl]}>
atoms.text_5xl
</Text>
<Text style={[a.text_4xl]}>atoms.text_4xl</Text>
<Text style={[a.text_3xl]}>atoms.text_3xl</Text>
<Text style={[a.text_2xl]}>atoms.text_2xl</Text>
@ -24,6 +26,7 @@ export function Typography() {
value={`This is rich text. It can have mentions like @bsky.app or links like https://bsky.social`}
/>
<RichText
selectable
resolveFacets
value={`This is rich text. It can have mentions like @bsky.app or links like https://bsky.social`}
style={[a.text_xl]}