Merge remote-tracking branch 'origin/main' into 3p-moderators
* origin/main: (69 commits) Update .po files use `showControls` to show/hide live text icon on ios (#2982) Fix dim mode unread notif color Make dim theme dim (#2966) Add handle validation to create account UI (#2959) Normalize relative day (#2874) increase timeout to 15s (#2958) use `useOpenLink` hook for links in ALF (#2975) Rename Home Feed Prefs to Following Feed Prefs (#2965) Refactor feed header components (#2964) patch react-navigation to fix history bug (#2955) Use EAS managed build number, run build/submit on GH Actions (#2841) Fix `numberOfLines` not updating on iOS 15 (#2956) Fix UITextView line height adjustment for DynamicType, always use the max width for the view (#2916) Navigate back from a deleted post's route (#2948) Add optional close callback to Dialog (#2947) Fix flash when pressing into just-created post (#2945) Last usage (#2944) Update blogpost URL in ExportCarDialog.tsx (#2939) Prefer full posts for post thread placeholder (#2943) ...
This commit is contained in:
+164
-86
@@ -1,5 +1,11 @@
|
||||
import React from 'react'
|
||||
import {ActivityIndicator, StyleSheet, View, type FlatList} from 'react-native'
|
||||
import {
|
||||
ActivityIndicator,
|
||||
StyleSheet,
|
||||
View,
|
||||
type FlatList,
|
||||
Pressable,
|
||||
} from 'react-native'
|
||||
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
||||
import {FontAwesomeIconStyle} from '@fortawesome/react-native-fontawesome'
|
||||
import {ViewHeader} from 'view/com/util/ViewHeader'
|
||||
@@ -8,9 +14,10 @@ import {Link} from 'view/com/util/Link'
|
||||
import {NativeStackScreenProps, FeedsTabNavigatorParams} from 'lib/routes/types'
|
||||
import {usePalette} from 'lib/hooks/usePalette'
|
||||
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
|
||||
import {ComposeIcon2, CogIcon} from 'lib/icons'
|
||||
import {ComposeIcon2, CogIcon, MagnifyingGlassIcon2} from 'lib/icons'
|
||||
import {s} from 'lib/styles'
|
||||
import {SearchInput} from 'view/com/util/forms/SearchInput'
|
||||
import {atoms as a, useTheme} from '#/alf'
|
||||
import {SearchInput, SearchInputRef} from 'view/com/util/forms/SearchInput'
|
||||
import {UserAvatar} from 'view/com/util/UserAvatar'
|
||||
import {
|
||||
LoadingPlaceholder,
|
||||
@@ -35,7 +42,11 @@ import {
|
||||
import {cleanError} from 'lib/strings/errors'
|
||||
import {useComposerControls} from '#/state/shell/composer'
|
||||
import {useSession} from '#/state/session'
|
||||
import {isNative} from '#/platform/detection'
|
||||
import {isNative, isWeb} from '#/platform/detection'
|
||||
import {HITSLOP_10} from 'lib/constants'
|
||||
import {IconCircle} from '#/components/IconCircle'
|
||||
import {ListSparkle_Stroke2_Corner0_Rounded} from '#/components/icons/ListSparkle'
|
||||
import {ListMagnifyingGlass_Stroke2_Corner0_Rounded} from '#/components/icons/ListMagnifyingGlass'
|
||||
|
||||
type Props = NativeStackScreenProps<FeedsTabNavigatorParams, 'Feeds'>
|
||||
|
||||
@@ -121,6 +132,7 @@ export function FeedsScreen(_props: Props) {
|
||||
} = useSearchPopularFeedsMutation()
|
||||
const {hasSession} = useSession()
|
||||
const listRef = React.useRef<FlatList>(null)
|
||||
const searchInputRef = React.useRef<SearchInputRef>(null)
|
||||
|
||||
/**
|
||||
* A search query is present. We may not have search results yet.
|
||||
@@ -207,12 +219,7 @@ export function FeedsScreen(_props: Props) {
|
||||
// pendingItems: this.rootStore.preferences.savedFeeds.length || 3,
|
||||
})
|
||||
} else {
|
||||
if (preferences?.feeds?.saved.length === 0) {
|
||||
slices.push({
|
||||
key: 'savedFeedNoResults',
|
||||
type: 'savedFeedNoResults',
|
||||
})
|
||||
} else {
|
||||
if (preferences?.feeds?.saved.length !== 0) {
|
||||
const {saved, pinned} = preferences.feeds
|
||||
|
||||
slices = slices.concat(
|
||||
@@ -330,14 +337,26 @@ export function FeedsScreen(_props: Props) {
|
||||
|
||||
const renderHeaderBtn = React.useCallback(() => {
|
||||
return (
|
||||
<Link
|
||||
href="/settings/saved-feeds"
|
||||
hitSlop={10}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={_(msg`Edit Saved Feeds`)}
|
||||
accessibilityHint={_(msg`Opens screen to edit Saved Feeds`)}>
|
||||
<CogIcon size={22} strokeWidth={2} style={pal.textLight} />
|
||||
</Link>
|
||||
<View style={styles.headerBtnGroup}>
|
||||
<Pressable
|
||||
accessibilityRole="button"
|
||||
hitSlop={HITSLOP_10}
|
||||
onPress={searchInputRef.current?.focus}>
|
||||
<MagnifyingGlassIcon2
|
||||
size={22}
|
||||
strokeWidth={2}
|
||||
style={pal.textLight}
|
||||
/>
|
||||
</Pressable>
|
||||
<Link
|
||||
href="/settings/saved-feeds"
|
||||
hitSlop={10}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={_(msg`Edit Saved Feeds`)}
|
||||
accessibilityHint={_(msg`Opens screen to edit Saved Feeds`)}>
|
||||
<CogIcon size={22} strokeWidth={2} style={pal.textLight} />
|
||||
</Link>
|
||||
</View>
|
||||
)
|
||||
}, [pal, _])
|
||||
|
||||
@@ -380,34 +399,48 @@ export function FeedsScreen(_props: Props) {
|
||||
) {
|
||||
return (
|
||||
<View style={s.p10}>
|
||||
<ActivityIndicator />
|
||||
<ActivityIndicator size="large" />
|
||||
</View>
|
||||
)
|
||||
} else if (item.type === 'savedFeedsHeader') {
|
||||
if (!isMobile) {
|
||||
return (
|
||||
<View
|
||||
style={[
|
||||
pal.view,
|
||||
styles.header,
|
||||
pal.border,
|
||||
{
|
||||
borderBottomWidth: 1,
|
||||
},
|
||||
]}>
|
||||
<Text type="title-lg" style={[pal.text, s.bold]}>
|
||||
<Trans>My Feeds</Trans>
|
||||
</Text>
|
||||
<Link
|
||||
href="/settings/saved-feeds"
|
||||
accessibilityLabel={_(msg`Edit My Feeds`)}
|
||||
accessibilityHint="">
|
||||
<CogIcon strokeWidth={1.5} style={pal.icon} size={28} />
|
||||
</Link>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
return <View />
|
||||
return (
|
||||
<>
|
||||
{!isMobile && (
|
||||
<View
|
||||
style={[
|
||||
pal.view,
|
||||
styles.header,
|
||||
pal.border,
|
||||
{
|
||||
borderBottomWidth: 1,
|
||||
},
|
||||
]}>
|
||||
<Text type="title-lg" style={[pal.text, s.bold]}>
|
||||
<Trans>Feeds</Trans>
|
||||
</Text>
|
||||
<View style={styles.headerBtnGroup}>
|
||||
<Pressable
|
||||
accessibilityRole="button"
|
||||
hitSlop={HITSLOP_10}
|
||||
onPress={searchInputRef.current?.focus}>
|
||||
<MagnifyingGlassIcon2
|
||||
size={22}
|
||||
strokeWidth={2}
|
||||
style={pal.icon}
|
||||
/>
|
||||
</Pressable>
|
||||
<Link
|
||||
href="/settings/saved-feeds"
|
||||
accessibilityLabel={_(msg`Edit My Feeds`)}
|
||||
accessibilityHint="">
|
||||
<CogIcon strokeWidth={1.5} style={pal.icon} size={28} />
|
||||
</Link>
|
||||
</View>
|
||||
</View>
|
||||
)}
|
||||
{preferences?.feeds?.saved?.length !== 0 && <FeedsSavedHeader />}
|
||||
</>
|
||||
)
|
||||
} else if (item.type === 'savedFeedNoResults') {
|
||||
return (
|
||||
<View
|
||||
@@ -425,45 +458,17 @@ export function FeedsScreen(_props: Props) {
|
||||
} else if (item.type === 'popularFeedsHeader') {
|
||||
return (
|
||||
<>
|
||||
<View
|
||||
style={[
|
||||
pal.view,
|
||||
styles.header,
|
||||
{
|
||||
// This is first in the flatlist without a session -esb
|
||||
marginTop: hasSession ? 16 : 0,
|
||||
paddingLeft: isMobile ? 12 : undefined,
|
||||
paddingRight: 10,
|
||||
paddingBottom: isMobile ? 6 : undefined,
|
||||
},
|
||||
]}>
|
||||
<Text type="title-lg" style={[pal.text, s.bold]}>
|
||||
<Trans>Discover new feeds</Trans>
|
||||
</Text>
|
||||
|
||||
{!isMobile && (
|
||||
<SearchInput
|
||||
query={query}
|
||||
onChangeQuery={onChangeQuery}
|
||||
onPressCancelSearch={onPressCancelSearch}
|
||||
onSubmitQuery={onSubmitQuery}
|
||||
setIsInputFocused={onChangeSearchFocus}
|
||||
style={{flex: 1, maxWidth: 250}}
|
||||
/>
|
||||
)}
|
||||
<FeedsAboutHeader />
|
||||
<View style={{paddingHorizontal: 12, paddingBottom: 12}}>
|
||||
<SearchInput
|
||||
ref={searchInputRef}
|
||||
query={query}
|
||||
onChangeQuery={onChangeQuery}
|
||||
onPressCancelSearch={onPressCancelSearch}
|
||||
onSubmitQuery={onSubmitQuery}
|
||||
setIsInputFocused={onChangeSearchFocus}
|
||||
/>
|
||||
</View>
|
||||
|
||||
{isMobile && (
|
||||
<View style={{paddingHorizontal: 8, paddingBottom: 10}}>
|
||||
<SearchInput
|
||||
query={query}
|
||||
onChangeQuery={onChangeQuery}
|
||||
onPressCancelSearch={onPressCancelSearch}
|
||||
onSubmitQuery={onSubmitQuery}
|
||||
setIsInputFocused={onChangeSearchFocus}
|
||||
/>
|
||||
</View>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
} else if (item.type === 'popularFeedsLoading') {
|
||||
@@ -495,15 +500,20 @@ export function FeedsScreen(_props: Props) {
|
||||
return null
|
||||
},
|
||||
[
|
||||
_,
|
||||
hasSession,
|
||||
isMobile,
|
||||
pal,
|
||||
pal.view,
|
||||
pal.border,
|
||||
pal.text,
|
||||
pal.icon,
|
||||
pal.textLight,
|
||||
_,
|
||||
preferences?.feeds?.saved?.length,
|
||||
query,
|
||||
onChangeQuery,
|
||||
onPressCancelSearch,
|
||||
onSubmitQuery,
|
||||
onChangeSearchFocus,
|
||||
hasSession,
|
||||
],
|
||||
)
|
||||
|
||||
@@ -518,8 +528,6 @@ export function FeedsScreen(_props: Props) {
|
||||
/>
|
||||
)}
|
||||
|
||||
{preferences ? <View /> : <ActivityIndicator />}
|
||||
|
||||
<List
|
||||
ref={listRef}
|
||||
style={[!isTabletOrDesktop && s.flex1, styles.list]}
|
||||
@@ -626,6 +634,71 @@ function SavedFeedLoadingPlaceholder() {
|
||||
)
|
||||
}
|
||||
|
||||
function FeedsSavedHeader() {
|
||||
const t = useTheme()
|
||||
|
||||
return (
|
||||
<View
|
||||
style={
|
||||
isWeb
|
||||
? [
|
||||
a.flex_row,
|
||||
a.px_md,
|
||||
a.py_lg,
|
||||
a.gap_md,
|
||||
a.border_b,
|
||||
t.atoms.border_contrast_low,
|
||||
]
|
||||
: [
|
||||
{flexDirection: 'row-reverse'},
|
||||
a.p_lg,
|
||||
a.gap_md,
|
||||
a.border_b,
|
||||
t.atoms.border_contrast_low,
|
||||
]
|
||||
}>
|
||||
<IconCircle icon={ListSparkle_Stroke2_Corner0_Rounded} size="lg" />
|
||||
<View style={[a.flex_1, a.gap_xs]}>
|
||||
<Text style={[a.flex_1, a.text_2xl, a.font_bold, t.atoms.text]}>
|
||||
<Trans>My Feeds</Trans>
|
||||
</Text>
|
||||
<Text style={[t.atoms.text_contrast_high]}>
|
||||
<Trans>All the feeds you've saved, right in one place.</Trans>
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
function FeedsAboutHeader() {
|
||||
const t = useTheme()
|
||||
|
||||
return (
|
||||
<View
|
||||
style={
|
||||
isWeb
|
||||
? [a.flex_row, a.px_md, a.pt_lg, a.pb_lg, a.gap_md]
|
||||
: [{flexDirection: 'row-reverse'}, a.p_lg, a.gap_md]
|
||||
}>
|
||||
<IconCircle
|
||||
icon={ListMagnifyingGlass_Stroke2_Corner0_Rounded}
|
||||
size="lg"
|
||||
/>
|
||||
<View style={[a.flex_1, a.gap_sm]}>
|
||||
<Text style={[a.flex_1, a.text_2xl, a.font_bold, t.atoms.text]}>
|
||||
<Trans>Discover New Feeds</Trans>
|
||||
</Text>
|
||||
<Text style={[t.atoms.text_contrast_high]}>
|
||||
<Trans>
|
||||
Custom feeds built by the community bring you new experiences and
|
||||
help you find the content you love.
|
||||
</Trans>
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
@@ -663,4 +736,9 @@ const styles = StyleSheet.create({
|
||||
paddingHorizontal: 4,
|
||||
paddingVertical: 2,
|
||||
},
|
||||
headerBtnGroup: {
|
||||
flexDirection: 'row',
|
||||
gap: 15,
|
||||
alignItems: 'center',
|
||||
},
|
||||
})
|
||||
|
||||
@@ -6,7 +6,7 @@ import {FeedDescriptor, FeedParams} from '#/state/queries/post-feed'
|
||||
import {FollowingEmptyState} from 'view/com/posts/FollowingEmptyState'
|
||||
import {FollowingEndOfFeed} from 'view/com/posts/FollowingEndOfFeed'
|
||||
import {CustomFeedEmptyState} from 'view/com/posts/CustomFeedEmptyState'
|
||||
import {FeedsTabBar} from '../com/pager/FeedsTabBar'
|
||||
import {HomeHeader} from '../com/home/HomeHeader'
|
||||
import {Pager, RenderTabBarFnProps, PagerRef} from 'view/com/pager/Pager'
|
||||
import {FeedPage} from 'view/com/feeds/FeedPage'
|
||||
import {HomeLoggedOutCTA} from '../com/auth/HomeLoggedOutCTA'
|
||||
@@ -118,7 +118,7 @@ function HomeScreenReady({
|
||||
const renderTabBar = React.useCallback(
|
||||
(props: RenderTabBarFnProps) => {
|
||||
return (
|
||||
<FeedsTabBar
|
||||
<HomeHeader
|
||||
key="FEEDS_TAB_BAR"
|
||||
selectedPage={props.selectedPage}
|
||||
onSelect={props.onSelect}
|
||||
|
||||
+5
-5
@@ -78,9 +78,9 @@ function RepliesThresholdInput({
|
||||
|
||||
type Props = NativeStackScreenProps<
|
||||
CommonNavigatorParams,
|
||||
'PreferencesHomeFeed'
|
||||
'PreferencesFollowingFeed'
|
||||
>
|
||||
export function PreferencesHomeFeed({navigation}: Props) {
|
||||
export function PreferencesFollowingFeed({navigation}: Props) {
|
||||
const pal = usePalette('default')
|
||||
const {_} = useLingui()
|
||||
const {isTabletOrDesktop} = useWebMediaQueries()
|
||||
@@ -101,14 +101,14 @@ export function PreferencesHomeFeed({navigation}: Props) {
|
||||
styles.container,
|
||||
isTabletOrDesktop && styles.desktopContainer,
|
||||
]}>
|
||||
<ViewHeader title={_(msg`Home Feed Preferences`)} showOnDesktop />
|
||||
<ViewHeader title={_(msg`Following Feed Preferences`)} showOnDesktop />
|
||||
<View
|
||||
style={[
|
||||
styles.titleSection,
|
||||
isTabletOrDesktop && {paddingTop: 20, paddingBottom: 20},
|
||||
]}>
|
||||
<Text type="xl" style={[pal.textLight, styles.description]}>
|
||||
<Trans>Fine-tune the content you see on your home screen.</Trans>
|
||||
<Trans>Fine-tune the content you see on your Following feed.</Trans>
|
||||
</Text>
|
||||
</View>
|
||||
|
||||
@@ -260,7 +260,7 @@ export function PreferencesHomeFeed({navigation}: Props) {
|
||||
<Text style={[pal.text, s.pb10]}>
|
||||
<Trans>
|
||||
Set this setting to "Yes" to show samples of your saved feeds in
|
||||
your following feed. This is an experimental feature.
|
||||
your Following feed. This is an experimental feature.
|
||||
</Trans>
|
||||
</Text>
|
||||
<ToggleButton
|
||||
@@ -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]}>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
import React from 'react'
|
||||
import {View} from 'react-native'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {Trans, msg} from '@lingui/macro'
|
||||
|
||||
import {atoms as a, useBreakpoints, useTheme} from '#/alf'
|
||||
import * as Dialog from '#/components/Dialog'
|
||||
import {Text, P} from '#/components/Typography'
|
||||
import {Button, ButtonText} from '#/components/Button'
|
||||
import {InlineLink, Link} from '#/components/Link'
|
||||
import {getAgent, useSession} from '#/state/session'
|
||||
|
||||
export function ExportCarDialog({
|
||||
control,
|
||||
}: {
|
||||
control: Dialog.DialogOuterProps['control']
|
||||
}) {
|
||||
const {_} = useLingui()
|
||||
const t = useTheme()
|
||||
const {gtMobile} = useBreakpoints()
|
||||
const {currentAccount} = useSession()
|
||||
|
||||
const downloadUrl = React.useMemo(() => {
|
||||
const agent = getAgent()
|
||||
if (!currentAccount || !agent.session) {
|
||||
return '' // shouldnt ever happen
|
||||
}
|
||||
// eg: https://bsky.social/xrpc/com.atproto.sync.getRepo?did=did:plc:ewvi7nxzyoun6zhxrhs64oiz
|
||||
const url = new URL(agent.pdsUrl || agent.service)
|
||||
url.pathname = '/xrpc/com.atproto.sync.getRepo'
|
||||
url.searchParams.set('did', agent.session.did)
|
||||
return url.toString()
|
||||
}, [currentAccount])
|
||||
|
||||
return (
|
||||
<Dialog.Outer control={control}>
|
||||
<Dialog.Handle />
|
||||
|
||||
<Dialog.ScrollableInner
|
||||
accessibilityDescribedBy="dialog-description"
|
||||
accessibilityLabelledBy="dialog-title">
|
||||
<View style={[a.relative, a.gap_md, a.w_full]}>
|
||||
<Text nativeID="dialog-title" style={[a.text_2xl, a.font_bold]}>
|
||||
<Trans>Export My Data</Trans>
|
||||
</Text>
|
||||
<P nativeID="dialog-description" style={[a.text_sm]}>
|
||||
<Trans>
|
||||
Your account repository, containing all public data records, can
|
||||
be downloaded as a "CAR" file. This file does not include media
|
||||
embeds, such as images, or your private data, which must be
|
||||
fetched separately.
|
||||
</Trans>
|
||||
</P>
|
||||
|
||||
<Link
|
||||
variant="solid"
|
||||
color="primary"
|
||||
size="large"
|
||||
label={_(msg`Download CAR file`)}
|
||||
to={downloadUrl}
|
||||
download="repo.car">
|
||||
<ButtonText>
|
||||
<Trans>Download CAR file</Trans>
|
||||
</ButtonText>
|
||||
</Link>
|
||||
|
||||
<P
|
||||
style={[
|
||||
a.py_xs,
|
||||
t.atoms.text_contrast_medium,
|
||||
a.text_sm,
|
||||
a.leading_snug,
|
||||
a.flex_1,
|
||||
]}>
|
||||
<Trans>
|
||||
This feature is in beta. You can read more about repository
|
||||
exports in{' '}
|
||||
<InlineLink
|
||||
to="https://docs.bsky.app/blog/repo-export"
|
||||
style={[a.text_sm]}>
|
||||
this blogpost.
|
||||
</InlineLink>
|
||||
</Trans>
|
||||
</P>
|
||||
|
||||
<View style={gtMobile && [a.flex_row, a.justify_end]}>
|
||||
<Button
|
||||
testID="doneBtn"
|
||||
variant="outline"
|
||||
color="primary"
|
||||
size={gtMobile ? 'small' : 'large'}
|
||||
onPress={() => control.close()}
|
||||
label={_(msg`Done`)}>
|
||||
{_(msg`Done`)}
|
||||
</Button>
|
||||
</View>
|
||||
|
||||
{!gtMobile && <View style={{height: 40}} />}
|
||||
</View>
|
||||
</Dialog.ScrollableInner>
|
||||
</Dialog.Outer>
|
||||
)
|
||||
}
|
||||
@@ -17,14 +17,6 @@ import {
|
||||
} from '@fortawesome/react-native-fontawesome'
|
||||
import {NativeStackScreenProps, CommonNavigatorParams} from 'lib/routes/types'
|
||||
import * as AppInfo from 'lib/app-info'
|
||||
import {s, colors} from 'lib/styles'
|
||||
import {ScrollView} from '../com/util/Views'
|
||||
import {Link, TextLink} from '../com/util/Link'
|
||||
import {Text} from '../com/util/text/Text'
|
||||
import * as Toast from '../com/util/Toast'
|
||||
import {UserAvatar} from '../com/util/UserAvatar'
|
||||
import {ToggleButton} from 'view/com/util/forms/ToggleButton'
|
||||
import {SelectableBtn} from 'view/com/util/forms/SelectableBtn'
|
||||
import {usePalette} from 'lib/hooks/usePalette'
|
||||
import {useCustomPalette} from 'lib/hooks/useCustomPalette'
|
||||
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
|
||||
@@ -34,8 +26,6 @@ import {NavigationProp} from 'lib/routes/types'
|
||||
import {HandIcon, HashtagIcon} from 'lib/icons'
|
||||
import Clipboard from '@react-native-clipboard/clipboard'
|
||||
import {makeProfileLink} from 'lib/routes/links'
|
||||
import {AccountDropdownBtn} from 'view/com/util/AccountDropdownBtn'
|
||||
import {SimpleViewHeader} from 'view/com/util/SimpleViewHeader'
|
||||
import {RQKEY as RQKEY_PROFILE} from '#/state/queries/profile'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
import {
|
||||
@@ -48,22 +38,12 @@ import {
|
||||
useRequireAltTextEnabled,
|
||||
useSetRequireAltTextEnabled,
|
||||
} from '#/state/preferences'
|
||||
import {
|
||||
useSession,
|
||||
useSessionApi,
|
||||
SessionAccount,
|
||||
getAgent,
|
||||
} from '#/state/session'
|
||||
import {useSession, useSessionApi, SessionAccount} from '#/state/session'
|
||||
import {useProfileQuery} from '#/state/queries/profile'
|
||||
import {useClearPreferencesMutation} from '#/state/queries/preferences'
|
||||
import {useInviteCodesQuery} from '#/state/queries/invites'
|
||||
// TODO import {useInviteCodesQuery} from '#/state/queries/invites'
|
||||
import {clear as clearStorage} from '#/state/persisted/store'
|
||||
import {clearLegacyStorage} from '#/state/persisted/legacy'
|
||||
|
||||
// TEMPORARY (APP-700)
|
||||
// remove after backend testing finishes
|
||||
// -prf
|
||||
import {useDebugHeaderSetting} from 'lib/api/debug-appview-proxy-header'
|
||||
import {STATUS_PAGE_URL} from 'lib/constants'
|
||||
import {Trans, msg} from '@lingui/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
@@ -75,6 +55,19 @@ import {
|
||||
useSetInAppBrowser,
|
||||
} from '#/state/preferences/in-app-browser'
|
||||
import {isNative} from '#/platform/detection'
|
||||
import {useDialogControl} from '#/components/Dialog'
|
||||
|
||||
import {s, colors} from 'lib/styles'
|
||||
import {ScrollView} from 'view/com/util/Views'
|
||||
import {Link, TextLink} from 'view/com/util/Link'
|
||||
import {Text} from 'view/com/util/text/Text'
|
||||
import * as Toast from 'view/com/util/Toast'
|
||||
import {UserAvatar} from 'view/com/util/UserAvatar'
|
||||
import {ToggleButton} from 'view/com/util/forms/ToggleButton'
|
||||
import {SelectableBtn} from 'view/com/util/forms/SelectableBtn'
|
||||
import {AccountDropdownBtn} from 'view/com/util/AccountDropdownBtn'
|
||||
import {SimpleViewHeader} from 'view/com/util/SimpleViewHeader'
|
||||
import {ExportCarDialog} from './ExportCarDialog'
|
||||
|
||||
function SettingsAccountCard({account}: {account: SessionAccount}) {
|
||||
const pal = usePalette('default')
|
||||
@@ -159,23 +152,22 @@ export function SettingsScreen({}: Props) {
|
||||
const {screen, track} = useAnalytics()
|
||||
const {openModal} = useModalControls()
|
||||
const {isSwitchingAccounts, accounts, currentAccount} = useSession()
|
||||
const [debugHeaderEnabled, toggleDebugHeader] = useDebugHeaderSetting(
|
||||
getAgent(),
|
||||
)
|
||||
const {mutate: clearPreferences} = useClearPreferencesMutation()
|
||||
const {data: invites} = useInviteCodesQuery()
|
||||
const invitesAvailable = invites?.available?.length ?? 0
|
||||
// TODO
|
||||
// const {data: invites} = useInviteCodesQuery()
|
||||
// const invitesAvailable = invites?.available?.length ?? 0
|
||||
const {setShowLoggedOut} = useLoggedOutViewControls()
|
||||
const closeAllActiveElements = useCloseAllActiveElements()
|
||||
const exportCarControl = useDialogControl()
|
||||
|
||||
const primaryBg = useCustomPalette<ViewStyle>({
|
||||
light: {backgroundColor: colors.blue0},
|
||||
dark: {backgroundColor: colors.blue6},
|
||||
})
|
||||
const primaryText = useCustomPalette<TextStyle>({
|
||||
light: {color: colors.blue3},
|
||||
dark: {color: colors.blue2},
|
||||
})
|
||||
// const primaryBg = useCustomPalette<ViewStyle>({
|
||||
// light: {backgroundColor: colors.blue0},
|
||||
// dark: {backgroundColor: colors.blue6},
|
||||
// })
|
||||
// const primaryText = useCustomPalette<TextStyle>({
|
||||
// light: {color: colors.blue3},
|
||||
// dark: {color: colors.blue2},
|
||||
// })
|
||||
|
||||
const dangerBg = useCustomPalette<ViewStyle>({
|
||||
light: {backgroundColor: colors.red1},
|
||||
@@ -214,10 +206,16 @@ export function SettingsScreen({}: Props) {
|
||||
})
|
||||
}, [track, queryClient, openModal, currentAccount])
|
||||
|
||||
const onPressExportRepository = React.useCallback(() => {
|
||||
exportCarControl.open()
|
||||
}, [exportCarControl])
|
||||
|
||||
/* TODO
|
||||
const onPressInviteCodes = React.useCallback(() => {
|
||||
track('Settings:InvitecodesButtonClicked')
|
||||
openModal({name: 'invite-codes'})
|
||||
}, [track, openModal])
|
||||
*/
|
||||
|
||||
const onPressLanguageSettings = React.useCallback(() => {
|
||||
navigation.navigate('LanguageSettings')
|
||||
@@ -243,8 +241,8 @@ export function SettingsScreen({}: Props) {
|
||||
Toast.show(_(msg`Copied build version to clipboard`))
|
||||
}, [_])
|
||||
|
||||
const openHomeFeedPreferences = React.useCallback(() => {
|
||||
navigation.navigate('PreferencesHomeFeed')
|
||||
const openFollowingFeedPreferences = React.useCallback(() => {
|
||||
navigation.navigate('PreferencesFollowingFeed')
|
||||
}, [navigation])
|
||||
|
||||
const openThreadsPreferences = React.useCallback(() => {
|
||||
@@ -282,6 +280,8 @@ export function SettingsScreen({}: Props) {
|
||||
|
||||
return (
|
||||
<View style={s.hContentRegion} testID="settingsScreen">
|
||||
<ExportCarDialog control={exportCarControl} />
|
||||
|
||||
<SimpleViewHeader
|
||||
showBackButton={isMobile}
|
||||
style={[
|
||||
@@ -395,51 +395,57 @@ export function SettingsScreen({}: Props) {
|
||||
|
||||
<View style={styles.spacer20} />
|
||||
|
||||
<Text type="xl-bold" style={[pal.text, styles.heading]}>
|
||||
<Trans>Invite a Friend</Trans>
|
||||
</Text>
|
||||
{/* TODO (
|
||||
<>
|
||||
<Text type="xl-bold" style={[pal.text, styles.heading]}>
|
||||
<Trans>Invite a Friend</Trans>
|
||||
</Text>
|
||||
|
||||
<TouchableOpacity
|
||||
testID="inviteFriendBtn"
|
||||
style={[
|
||||
styles.linkCard,
|
||||
pal.view,
|
||||
isSwitchingAccounts && styles.dimmed,
|
||||
]}
|
||||
onPress={isSwitchingAccounts ? undefined : onPressInviteCodes}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={_(msg`Invite`)}
|
||||
accessibilityHint={_(msg`Opens invite code list`)}
|
||||
disabled={invites?.disabled}>
|
||||
<View
|
||||
style={[
|
||||
styles.iconContainer,
|
||||
invitesAvailable > 0 ? primaryBg : pal.btn,
|
||||
]}>
|
||||
<FontAwesomeIcon
|
||||
icon="ticket"
|
||||
style={
|
||||
(invitesAvailable > 0
|
||||
? primaryText
|
||||
: pal.text) as FontAwesomeIconStyle
|
||||
}
|
||||
/>
|
||||
</View>
|
||||
<Text type="lg" style={invitesAvailable > 0 ? pal.link : pal.text}>
|
||||
{invites?.disabled ? (
|
||||
<Trans>
|
||||
Your invite codes are hidden when logged in using an App
|
||||
Password
|
||||
</Trans>
|
||||
) : invitesAvailable === 1 ? (
|
||||
<Trans>{invitesAvailable} invite code available</Trans>
|
||||
) : (
|
||||
<Trans>{invitesAvailable} invite codes available</Trans>
|
||||
)}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity
|
||||
testID="inviteFriendBtn"
|
||||
style={[
|
||||
styles.linkCard,
|
||||
pal.view,
|
||||
isSwitchingAccounts && styles.dimmed,
|
||||
]}
|
||||
onPress={isSwitchingAccounts ? undefined : onPressInviteCodes}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={_(msg`Invite`)}
|
||||
accessibilityHint={_(msg`Opens invite code list`)}
|
||||
disabled={invites?.disabled}>
|
||||
<View
|
||||
style={[
|
||||
styles.iconContainer,
|
||||
invitesAvailable > 0 ? primaryBg : pal.btn,
|
||||
]}>
|
||||
<FontAwesomeIcon
|
||||
icon="ticket"
|
||||
style={
|
||||
(invitesAvailable > 0
|
||||
? primaryText
|
||||
: pal.text) as FontAwesomeIconStyle
|
||||
}
|
||||
/>
|
||||
</View>
|
||||
<Text
|
||||
type="lg"
|
||||
style={invitesAvailable > 0 ? pal.link : pal.text}>
|
||||
{invites?.disabled ? (
|
||||
<Trans>
|
||||
Your invite codes are hidden when logged in using an App
|
||||
Password
|
||||
</Trans>
|
||||
) : invitesAvailable === 1 ? (
|
||||
<Trans>{invitesAvailable} invite code available</Trans>
|
||||
) : (
|
||||
<Trans>{invitesAvailable} invite codes available</Trans>
|
||||
)}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
|
||||
<View style={styles.spacer20} />
|
||||
<View style={styles.spacer20} />
|
||||
</>
|
||||
)*/}
|
||||
|
||||
<Text type="xl-bold" style={[pal.text, styles.heading]}>
|
||||
<Trans>Accessibility</Trans>
|
||||
@@ -523,7 +529,7 @@ export function SettingsScreen({}: Props) {
|
||||
pal.view,
|
||||
isSwitchingAccounts && styles.dimmed,
|
||||
]}
|
||||
onPress={openHomeFeedPreferences}
|
||||
onPress={openFollowingFeedPreferences}
|
||||
accessibilityRole="button"
|
||||
accessibilityHint=""
|
||||
accessibilityLabel={_(msg`Opens the home feed preferences`)}>
|
||||
@@ -534,7 +540,7 @@ export function SettingsScreen({}: Props) {
|
||||
/>
|
||||
</View>
|
||||
<Text type="lg" style={pal.text}>
|
||||
<Trans>Home Feed Preferences</Trans>
|
||||
<Trans>Following Feed Preferences</Trans>
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity
|
||||
@@ -735,6 +741,29 @@ export function SettingsScreen({}: Props) {
|
||||
<Trans>Change Password</Trans>
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity
|
||||
testID="exportRepositoryBtn"
|
||||
style={[
|
||||
styles.linkCard,
|
||||
pal.view,
|
||||
isSwitchingAccounts && styles.dimmed,
|
||||
]}
|
||||
onPress={isSwitchingAccounts ? undefined : onPressExportRepository}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={_(msg`Export my data`)}
|
||||
accessibilityHint={_(
|
||||
msg`Download Bluesky account data (repository)`,
|
||||
)}>
|
||||
<View style={[styles.iconContainer, pal.btn]}>
|
||||
<FontAwesomeIcon
|
||||
icon="download"
|
||||
style={pal.text as FontAwesomeIconStyle}
|
||||
/>
|
||||
</View>
|
||||
<Text type="lg" style={pal.text} numberOfLines={1}>
|
||||
<Trans>Export My Data</Trans>
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity
|
||||
style={[pal.view, styles.linkCard]}
|
||||
onPress={onPressDeleteAccount}
|
||||
@@ -756,9 +785,6 @@ export function SettingsScreen({}: Props) {
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
<View style={styles.spacer20} />
|
||||
<Text type="xl-bold" style={[pal.text, styles.heading]}>
|
||||
<Trans>Developer Tools</Trans>
|
||||
</Text>
|
||||
<TouchableOpacity
|
||||
style={[pal.view, styles.linkCardNoIcon]}
|
||||
onPress={onPressSystemLog}
|
||||
@@ -769,14 +795,6 @@ export function SettingsScreen({}: Props) {
|
||||
<Trans>System log</Trans>
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
{__DEV__ ? (
|
||||
<ToggleButton
|
||||
type="default-light"
|
||||
label="Experiment: Use AppView Proxy"
|
||||
isSelected={debugHeaderEnabled}
|
||||
onPress={toggleDebugHeader}
|
||||
/>
|
||||
) : null}
|
||||
{__DEV__ ? (
|
||||
<>
|
||||
<TouchableOpacity
|
||||
@@ -7,14 +7,12 @@ import {H3, P} from '#/components/Typography'
|
||||
import * as Dialog from '#/components/Dialog'
|
||||
import * as Prompt from '#/components/Prompt'
|
||||
import {useDialogStateControlContext} from '#/state/dialogs'
|
||||
import {useOpenGlobalDialog} from '#/components/dialogs'
|
||||
import {ReportDialog} from '#/components/dialogs/ReportDialog'
|
||||
|
||||
export function Dialogs() {
|
||||
const control = Dialog.useDialogControl()
|
||||
const scrollable = Dialog.useDialogControl()
|
||||
const basic = Dialog.useDialogControl()
|
||||
const prompt = Prompt.usePromptControl()
|
||||
const {closeAllDialogs} = useDialogStateControlContext()
|
||||
const openDialog = useOpenGlobalDialog()
|
||||
|
||||
return (
|
||||
<View style={[a.gap_md]}>
|
||||
@@ -23,9 +21,9 @@ export function Dialogs() {
|
||||
color="secondary"
|
||||
size="small"
|
||||
onPress={() => {
|
||||
control.open()
|
||||
scrollable.open()
|
||||
prompt.open()
|
||||
openDialog(ReportDialog, {type: 'user', did: ''})
|
||||
basic.open()
|
||||
}}
|
||||
label="Open basic dialog">
|
||||
Open all dialogs
|
||||
@@ -36,7 +34,18 @@ export function Dialogs() {
|
||||
color="secondary"
|
||||
size="small"
|
||||
onPress={() => {
|
||||
control.open()
|
||||
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
|
||||
@@ -51,17 +60,6 @@ export function Dialogs() {
|
||||
Open prompt
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
variant="solid"
|
||||
color="primary"
|
||||
size="small"
|
||||
onPress={() => {
|
||||
openDialog(ReportDialog, {type: 'user', did: ''})
|
||||
}}
|
||||
label="Open prompt">
|
||||
Open report dialog
|
||||
</Button>
|
||||
|
||||
<Prompt.Outer control={prompt}>
|
||||
<Prompt.Title>This is a prompt</Prompt.Title>
|
||||
<Prompt.Description>
|
||||
@@ -74,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
|
||||
@@ -103,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>
|
||||
|
||||
@@ -6,6 +6,7 @@ import {H1} from '#/components/Typography'
|
||||
import {Globe_Stroke2_Corner0_Rounded as Globe} from '#/components/icons/Globe'
|
||||
import {ArrowTopRight_Stroke2_Corner0_Rounded as ArrowTopRight} from '#/components/icons/ArrowTopRight'
|
||||
import {CalendarDays_Stroke2_Corner0_Rounded as CalendarDays} from '#/components/icons/CalendarDays'
|
||||
import {Loader} from '#/components/Loader'
|
||||
|
||||
export function Icons() {
|
||||
const t = useTheme()
|
||||
@@ -36,6 +37,14 @@ export function Icons() {
|
||||
<CalendarDays size="lg" fill={t.atoms.text.color} />
|
||||
<CalendarDays size="xl" fill={t.atoms.text.color} />
|
||||
</View>
|
||||
|
||||
<View style={[a.flex_row, a.gap_xl]}>
|
||||
<Loader size="xs" fill={t.atoms.text.color} />
|
||||
<Loader size="sm" fill={t.atoms.text.color} />
|
||||
<Loader size="md" fill={t.atoms.text.color} />
|
||||
<Loader size="lg" fill={t.atoms.text.color} />
|
||||
<Loader size="xl" fill={t.atoms.text.color} />
|
||||
</View>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -19,9 +19,14 @@ export function Links() {
|
||||
style={[a.text_md]}>
|
||||
External
|
||||
</InlineLink>
|
||||
<InlineLink to="https://bsky.social" style={[a.text_md]}>
|
||||
<InlineLink to="https://bsky.social" style={[a.text_md, t.atoms.text]}>
|
||||
<H3>External with custom children</H3>
|
||||
</InlineLink>
|
||||
<InlineLink
|
||||
to="https://bsky.social"
|
||||
style={[a.text_md, t.atoms.text_contrast_low]}>
|
||||
External with custom children
|
||||
</InlineLink>
|
||||
<InlineLink
|
||||
to="https://bsky.social"
|
||||
warnOnMismatchingTextChild
|
||||
|
||||
@@ -2,99 +2,26 @@ import React from 'react'
|
||||
import {View} from 'react-native'
|
||||
|
||||
import * as tokens from '#/alf/tokens'
|
||||
import {atoms as a} from '#/alf'
|
||||
import {atoms as a, useTheme} from '#/alf'
|
||||
|
||||
export function Palette() {
|
||||
const t = useTheme()
|
||||
return (
|
||||
<View style={[a.gap_md]}>
|
||||
<View style={[a.flex_row, a.gap_md]}>
|
||||
<View
|
||||
style={[a.flex_1, {height: 60, backgroundColor: tokens.color.gray_0}]}
|
||||
/>
|
||||
<View
|
||||
style={[
|
||||
a.flex_1,
|
||||
{height: 60, backgroundColor: tokens.color.gray_25},
|
||||
]}
|
||||
/>
|
||||
<View
|
||||
style={[
|
||||
a.flex_1,
|
||||
{height: 60, backgroundColor: tokens.color.gray_50},
|
||||
]}
|
||||
/>
|
||||
<View
|
||||
style={[
|
||||
a.flex_1,
|
||||
{height: 60, backgroundColor: tokens.color.gray_100},
|
||||
]}
|
||||
/>
|
||||
<View
|
||||
style={[
|
||||
a.flex_1,
|
||||
{height: 60, backgroundColor: tokens.color.gray_200},
|
||||
]}
|
||||
/>
|
||||
<View
|
||||
style={[
|
||||
a.flex_1,
|
||||
{height: 60, backgroundColor: tokens.color.gray_300},
|
||||
]}
|
||||
/>
|
||||
<View
|
||||
style={[
|
||||
a.flex_1,
|
||||
{height: 60, backgroundColor: tokens.color.gray_400},
|
||||
]}
|
||||
/>
|
||||
<View
|
||||
style={[
|
||||
a.flex_1,
|
||||
{height: 60, backgroundColor: tokens.color.gray_500},
|
||||
]}
|
||||
/>
|
||||
<View
|
||||
style={[
|
||||
a.flex_1,
|
||||
{height: 60, backgroundColor: tokens.color.gray_600},
|
||||
]}
|
||||
/>
|
||||
<View
|
||||
style={[
|
||||
a.flex_1,
|
||||
{height: 60, backgroundColor: tokens.color.gray_700},
|
||||
]}
|
||||
/>
|
||||
<View
|
||||
style={[
|
||||
a.flex_1,
|
||||
{height: 60, backgroundColor: tokens.color.gray_800},
|
||||
]}
|
||||
/>
|
||||
<View
|
||||
style={[
|
||||
a.flex_1,
|
||||
{height: 60, backgroundColor: tokens.color.gray_900},
|
||||
]}
|
||||
/>
|
||||
<View
|
||||
style={[
|
||||
a.flex_1,
|
||||
{height: 60, backgroundColor: tokens.color.gray_950},
|
||||
]}
|
||||
/>
|
||||
<View
|
||||
style={[
|
||||
a.flex_1,
|
||||
{height: 60, backgroundColor: tokens.color.gray_975},
|
||||
]}
|
||||
/>
|
||||
<View
|
||||
style={[
|
||||
a.flex_1,
|
||||
{height: 60, backgroundColor: tokens.color.gray_1000},
|
||||
]}
|
||||
/>
|
||||
<View style={[a.flex_1, t.atoms.bg_contrast_25, {height: 60}]} />
|
||||
<View style={[a.flex_1, t.atoms.bg_contrast_50, {height: 60}]} />
|
||||
<View style={[a.flex_1, t.atoms.bg_contrast_100, {height: 60}]} />
|
||||
<View style={[a.flex_1, t.atoms.bg_contrast_200, {height: 60}]} />
|
||||
<View style={[a.flex_1, t.atoms.bg_contrast_300, {height: 60}]} />
|
||||
<View style={[a.flex_1, t.atoms.bg_contrast_400, {height: 60}]} />
|
||||
<View style={[a.flex_1, t.atoms.bg_contrast_500, {height: 60}]} />
|
||||
<View style={[a.flex_1, t.atoms.bg_contrast_600, {height: 60}]} />
|
||||
<View style={[a.flex_1, t.atoms.bg_contrast_700, {height: 60}]} />
|
||||
<View style={[a.flex_1, t.atoms.bg_contrast_800, {height: 60}]} />
|
||||
<View style={[a.flex_1, t.atoms.bg_contrast_900, {height: 60}]} />
|
||||
<View style={[a.flex_1, t.atoms.bg_contrast_950, {height: 60}]} />
|
||||
<View style={[a.flex_1, t.atoms.bg_contrast_975, {height: 60}]} />
|
||||
</View>
|
||||
|
||||
<View style={[a.flex_row, a.gap_md]}>
|
||||
|
||||
@@ -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]}
|
||||
|
||||
Reference in New Issue
Block a user