Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f17418c131 | |||
| 84213eabcb | |||
| a8ac790fb3 | |||
| b77eec8079 | |||
| 6f7ee7efab | |||
| f9ade07f81 | |||
| db86b6048e | |||
| 28c764e6ca | |||
| 72ecc4355c | |||
| 06fc413eab | |||
| 5335c8c2d4 | |||
| 5551549e13 |
@@ -156,6 +156,7 @@
|
||||
"lodash.isequal": "^4.5.0",
|
||||
"lodash.shuffle": "^4.2.0",
|
||||
"lodash.throttle": "^4.1.1",
|
||||
"mix-css-color": "^0.2.0",
|
||||
"multiformats": "^13.1.0",
|
||||
"nanoid": "^5.0.5",
|
||||
"normalize-url": "^8.0.0",
|
||||
@@ -200,6 +201,7 @@
|
||||
"tippy.js": "^6.3.7",
|
||||
"tlds": "^1.234.0",
|
||||
"tldts": "^6.1.46",
|
||||
"wcag-contrast": "^3.0.0",
|
||||
"zeego": "^1.6.2",
|
||||
"zod": "^3.20.2"
|
||||
},
|
||||
@@ -227,6 +229,7 @@
|
||||
"@types/react-dom": "^18.2.18",
|
||||
"@types/react-responsive": "^8.0.5",
|
||||
"@types/react-test-renderer": "^17.0.1",
|
||||
"@types/wcag-contrast": "^3.0.3",
|
||||
"@typescript-eslint/eslint-plugin": "^7.18.0",
|
||||
"@typescript-eslint/parser": "^7.18.0",
|
||||
"babel-jest": "^29.7.0",
|
||||
|
||||
+1
-1
@@ -122,7 +122,7 @@ function InnerApp() {
|
||||
}, [_])
|
||||
|
||||
return (
|
||||
<Alf theme={theme}>
|
||||
<Alf themeName={theme}>
|
||||
<ThemeProvider theme={theme}>
|
||||
<Splash isReady={isReady && hasCheckedReferrer}>
|
||||
<RootSiblingParent>
|
||||
|
||||
+1
-1
@@ -101,7 +101,7 @@ function InnerApp() {
|
||||
if (!isReady || !hasCheckedReferrer) return null
|
||||
|
||||
return (
|
||||
<Alf theme={theme}>
|
||||
<Alf themeName={theme}>
|
||||
<ThemeProvider theme={theme}>
|
||||
<RootSiblingParent>
|
||||
<VideoVolumeProvider>
|
||||
|
||||
+12
-3
@@ -63,8 +63,12 @@ export const Context = React.createContext<Alf>({
|
||||
|
||||
export function ThemeProvider({
|
||||
children,
|
||||
theme: themeName,
|
||||
}: React.PropsWithChildren<{theme: ThemeName}>) {
|
||||
themeName,
|
||||
theme,
|
||||
}: React.PropsWithChildren<{
|
||||
themeName: ThemeName
|
||||
theme?: ReturnType<typeof createThemes>
|
||||
}>) {
|
||||
const [fontScale, setFontScale] = React.useState<Alf['fonts']['scale']>(() =>
|
||||
getFontScale(),
|
||||
)
|
||||
@@ -74,6 +78,7 @@ export function ThemeProvider({
|
||||
const setFontScaleAndPersist = React.useCallback<
|
||||
Alf['fonts']['setFontScale']
|
||||
>(
|
||||
// eslint-disable-next-line @typescript-eslint/no-shadow
|
||||
fontScale => {
|
||||
setFontScale(fontScale)
|
||||
persistFontScale(fontScale)
|
||||
@@ -87,6 +92,7 @@ export function ThemeProvider({
|
||||
const setFontFamilyAndPersist = React.useCallback<
|
||||
Alf['fonts']['setFontFamily']
|
||||
>(
|
||||
// eslint-disable-next-line @typescript-eslint/no-shadow
|
||||
fontFamily => {
|
||||
setFontFamily(fontFamily)
|
||||
persistFontFamily(fontFamily)
|
||||
@@ -94,6 +100,9 @@ export function ThemeProvider({
|
||||
[setFontFamily],
|
||||
)
|
||||
const themes = React.useMemo(() => {
|
||||
if (theme) {
|
||||
return theme
|
||||
}
|
||||
return createThemes({
|
||||
hues: {
|
||||
primary: BLUE_HUE,
|
||||
@@ -101,7 +110,7 @@ export function ThemeProvider({
|
||||
positive: GREEN_HUE,
|
||||
},
|
||||
})
|
||||
}, [])
|
||||
}, [theme])
|
||||
|
||||
const value = React.useMemo<Alf>(
|
||||
() => ({
|
||||
|
||||
+14
-14
@@ -8,13 +8,14 @@ import {useNavigation} from '@react-navigation/native'
|
||||
import {NavigationProp} from '#/lib/routes/types'
|
||||
import {toShortUrl} from '#/lib/strings/url-helpers'
|
||||
import {isNative} from '#/platform/detection'
|
||||
import {atoms as a, flatten, native, TextStyleProp, useTheme, web} from '#/alf'
|
||||
import {atoms as a, flatten, native, TextStyleProp, web} from '#/alf'
|
||||
import {isOnlyEmoji} from '#/alf/typography'
|
||||
import {useInteractionState} from '#/components/hooks/useInteractionState'
|
||||
import {InlineLinkText, LinkProps} from '#/components/Link'
|
||||
import {ProfileHoverCard} from '#/components/ProfileHoverCard'
|
||||
import {TagMenu, useTagMenuControl} from '#/components/TagMenu'
|
||||
import {Text, TextProps} from '#/components/Typography'
|
||||
import {useDynamicColor} from './hooks/useDynamicColor'
|
||||
|
||||
const WORD_WRAP = {wordWrap: 1}
|
||||
|
||||
@@ -29,6 +30,7 @@ export type RichTextProps = TextStyleProp &
|
||||
onLinkPress?: LinkProps['onPress']
|
||||
interactiveStyle?: TextStyle
|
||||
emojiMultiplier?: number
|
||||
dynamicColor?: boolean
|
||||
}
|
||||
|
||||
export function RichText({
|
||||
@@ -43,6 +45,7 @@ export function RichText({
|
||||
onLinkPress,
|
||||
interactiveStyle,
|
||||
emojiMultiplier = 1.85,
|
||||
dynamicColor = false,
|
||||
}: RichTextProps) {
|
||||
const richText = React.useMemo(
|
||||
() =>
|
||||
@@ -50,10 +53,13 @@ export function RichText({
|
||||
[value],
|
||||
)
|
||||
|
||||
const interactiveColor = useDynamicColor({enabled: dynamicColor})
|
||||
|
||||
const flattenedStyle = flatten(style)
|
||||
const plainStyles = [a.leading_snug, flattenedStyle]
|
||||
const interactiveStyles = [
|
||||
a.leading_snug,
|
||||
interactiveColor,
|
||||
flatten(interactiveStyle),
|
||||
flattenedStyle,
|
||||
]
|
||||
@@ -182,7 +188,6 @@ function RichTextTag({
|
||||
selectable?: boolean
|
||||
authorHandle?: string
|
||||
} & TextStyleProp) {
|
||||
const t = useTheme()
|
||||
const {_} = useLingui()
|
||||
const control = useTagMenuControl()
|
||||
const {
|
||||
@@ -224,22 +229,17 @@ function RichTextTag({
|
||||
{...web({
|
||||
onMouseEnter: onHoverIn,
|
||||
onMouseLeave: onHoverOut,
|
||||
onFocus: onFocus,
|
||||
onBlur: onBlur,
|
||||
})}
|
||||
// @ts-ignore
|
||||
onFocus={onFocus}
|
||||
onBlur={onBlur}
|
||||
style={[
|
||||
web({
|
||||
cursor: 'pointer',
|
||||
}),
|
||||
{color: t.palette.primary_500},
|
||||
(hovered || focused) && {
|
||||
...web({
|
||||
web({cursor: 'pointer'}),
|
||||
web(
|
||||
(hovered || focused) && {
|
||||
outline: 0,
|
||||
textDecorationLine: 'underline',
|
||||
textDecorationColor: t.palette.primary_500,
|
||||
}),
|
||||
},
|
||||
},
|
||||
),
|
||||
style,
|
||||
]}>
|
||||
{text}
|
||||
|
||||
@@ -76,7 +76,7 @@ export function MessageProfileButton({
|
||||
accessibilityRole="button"
|
||||
testID="dmBtn"
|
||||
size="small"
|
||||
color="secondary"
|
||||
color="secondary_inverted"
|
||||
variant="solid"
|
||||
shape="round"
|
||||
label={_(msg`Message ${profile.handle}`)}
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
import {useMemo} from 'react'
|
||||
import {StyleProp, TextStyle} from 'react-native'
|
||||
import mixColors from 'mix-css-color'
|
||||
import {rgb as compareContrast} from 'wcag-contrast'
|
||||
|
||||
import {useTheme} from '#/alf'
|
||||
|
||||
export function useDynamicColor({enabled} = {enabled: false}) {
|
||||
const t = useTheme()
|
||||
|
||||
const color = enabled
|
||||
? getDynamicColor(t.name !== 'light', t.palette.primary_500)
|
||||
: t.palette.primary_500
|
||||
|
||||
return useMemo(
|
||||
() =>
|
||||
({
|
||||
color,
|
||||
textDecorationColor: color,
|
||||
} satisfies StyleProp<TextStyle>),
|
||||
[color],
|
||||
)
|
||||
}
|
||||
|
||||
// the goal is to return a blue color that should be an acceptable constrast with
|
||||
// the background color. the default blue is rgb(16, 131, 254)
|
||||
// we want to color mix it with the background color
|
||||
// and flip to whiteish if it's going to be unacceptable low contrast
|
||||
const BASE_COLOR_LIGHT = 'rgb(16, 131, 254)'
|
||||
const BASE_COLOR_DARK = 'rgb(32, 139, 254)'
|
||||
function getDynamicColor(dark: boolean, themeColor: string) {
|
||||
const baseColor = dark ? BASE_COLOR_DARK : BASE_COLOR_LIGHT
|
||||
const blendedColor = mixColors(baseColor, themeColor, 75)
|
||||
debugColorContrast(
|
||||
blendedColor,
|
||||
// for debug, we simulate the linear gradient on the profile
|
||||
mixColors(themeColor, '#ffffff', 30),
|
||||
)
|
||||
let [h, s, l] = blendedColor.hsla
|
||||
return `hsl(${h}, ${s}%, ${l * 1.1}%)`
|
||||
}
|
||||
|
||||
function debugColorContrast(
|
||||
foreground: ReturnType<typeof mixColors>,
|
||||
background: ReturnType<typeof mixColors>,
|
||||
) {
|
||||
const score = getContrastScore(foreground, background)
|
||||
console.log(
|
||||
`%c ${score > 4.5 ? 'PASS' : 'FAIL'}: ${Math.trunc(score * 10) / 10} `,
|
||||
`color: ${foreground.hex}; background: ${background.hex}`,
|
||||
)
|
||||
}
|
||||
|
||||
function getContrastScore(
|
||||
foreground: ReturnType<typeof mixColors>,
|
||||
background: ReturnType<typeof mixColors>,
|
||||
) {
|
||||
const [r1, g1, b1] = foreground.rgba
|
||||
const [r2, g2, b2] = background.rgba
|
||||
return compareContrast([r1, g1, b1], [r2, g2, b2])
|
||||
}
|
||||
@@ -49,6 +49,7 @@ interface Props {
|
||||
moderationOpts: ModerationOpts
|
||||
hideBackButton?: boolean
|
||||
isPlaceholderProfile?: boolean
|
||||
backgroundColor: string
|
||||
}
|
||||
|
||||
let ProfileHeaderLabeler = ({
|
||||
@@ -58,6 +59,7 @@ let ProfileHeaderLabeler = ({
|
||||
moderationOpts,
|
||||
hideBackButton = false,
|
||||
isPlaceholderProfile,
|
||||
backgroundColor,
|
||||
}: Props): React.ReactNode => {
|
||||
const profile: Shadow<AppBskyActorDefs.ProfileViewDetailed> =
|
||||
useProfileShadow(profileUnshadowed)
|
||||
@@ -168,7 +170,8 @@ let ProfileHeaderLabeler = ({
|
||||
profile={profile}
|
||||
moderation={moderation}
|
||||
hideBackButton={hideBackButton}
|
||||
isPlaceholderProfile={isPlaceholderProfile}>
|
||||
isPlaceholderProfile={isPlaceholderProfile}
|
||||
backgroundColor={backgroundColor}>
|
||||
<View
|
||||
style={[a.px_lg, a.pt_md, a.pb_sm]}
|
||||
pointerEvents={isIOS ? 'auto' : 'box-none'}>
|
||||
@@ -261,6 +264,7 @@ let ProfileHeaderLabeler = ({
|
||||
value={descriptionRT}
|
||||
enableTags
|
||||
authorHandle={profile.handle}
|
||||
dynamicColor
|
||||
/>
|
||||
</View>
|
||||
) : undefined}
|
||||
|
||||
@@ -46,6 +46,7 @@ interface Props {
|
||||
moderationOpts: ModerationOpts
|
||||
hideBackButton?: boolean
|
||||
isPlaceholderProfile?: boolean
|
||||
backgroundColor: string
|
||||
}
|
||||
|
||||
let ProfileHeaderStandard = ({
|
||||
@@ -54,6 +55,7 @@ let ProfileHeaderStandard = ({
|
||||
moderationOpts,
|
||||
hideBackButton = false,
|
||||
isPlaceholderProfile,
|
||||
backgroundColor,
|
||||
}: Props): React.ReactNode => {
|
||||
const profile: Shadow<AppBskyActorDefs.ProfileViewDetailed> =
|
||||
useProfileShadow(profileUnshadowed)
|
||||
@@ -153,7 +155,8 @@ let ProfileHeaderStandard = ({
|
||||
profile={profile}
|
||||
moderation={moderation}
|
||||
hideBackButton={hideBackButton}
|
||||
isPlaceholderProfile={isPlaceholderProfile}>
|
||||
isPlaceholderProfile={isPlaceholderProfile}
|
||||
backgroundColor={backgroundColor}>
|
||||
<View
|
||||
style={[a.px_lg, a.pt_md, a.pb_sm, a.overflow_hidden]}
|
||||
pointerEvents={isIOS ? 'auto' : 'box-none'}>
|
||||
@@ -173,11 +176,10 @@ let ProfileHeaderStandard = ({
|
||||
<Button
|
||||
testID="profileHeaderEditProfileButton"
|
||||
size="small"
|
||||
color="secondary"
|
||||
color="secondary_inverted"
|
||||
variant="solid"
|
||||
onPress={onPressEditProfile}
|
||||
label={_(msg`Edit profile`)}
|
||||
style={[a.rounded_full]}>
|
||||
label={_(msg`Edit profile`)}>
|
||||
<ButtonText>
|
||||
<Trans>Edit Profile</Trans>
|
||||
</ButtonText>
|
||||
@@ -210,7 +212,9 @@ let ProfileHeaderStandard = ({
|
||||
<Button
|
||||
testID={profile.viewer?.following ? 'unfollowBtn' : 'followBtn'}
|
||||
size="small"
|
||||
color={profile.viewer?.following ? 'secondary' : 'primary'}
|
||||
color={
|
||||
profile.viewer?.following ? 'secondary_inverted' : 'primary'
|
||||
}
|
||||
variant="solid"
|
||||
label={
|
||||
profile.viewer?.following
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import React, {memo} from 'react'
|
||||
import {StyleSheet, TouchableWithoutFeedback, View} from 'react-native'
|
||||
import {MeasuredDimensions, runOnJS, runOnUI} from 'react-native-reanimated'
|
||||
import {LinearGradient} from 'expo-linear-gradient'
|
||||
import {AppBskyActorDefs, ModerationDecision} from '@atproto/api'
|
||||
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
||||
import {msg} from '@lingui/macro'
|
||||
@@ -29,6 +30,7 @@ interface Props {
|
||||
moderation: ModerationDecision
|
||||
hideBackButton?: boolean
|
||||
isPlaceholderProfile?: boolean
|
||||
backgroundColor: string
|
||||
}
|
||||
|
||||
let ProfileHeaderShell = ({
|
||||
@@ -37,6 +39,7 @@ let ProfileHeaderShell = ({
|
||||
moderation,
|
||||
hideBackButton = false,
|
||||
isPlaceholderProfile,
|
||||
backgroundColor,
|
||||
}: React.PropsWithChildren<Props>): React.ReactNode => {
|
||||
const t = useTheme()
|
||||
const {currentAccount} = useSession()
|
||||
@@ -96,7 +99,18 @@ let ProfileHeaderShell = ({
|
||||
)
|
||||
|
||||
return (
|
||||
<View style={t.atoms.bg} pointerEvents={isIOS ? 'auto' : 'box-none'}>
|
||||
<View pointerEvents={isIOS ? 'auto' : 'box-none'}>
|
||||
<LinearGradient
|
||||
key={t.name}
|
||||
colors={[t.palette.primary_25, t.palette.primary_25, backgroundColor]}
|
||||
style={{
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
}}
|
||||
/>
|
||||
<View
|
||||
pointerEvents={isIOS ? 'auto' : 'box-none'}
|
||||
style={[a.relative, {height: 150}]}>
|
||||
@@ -162,7 +176,7 @@ let ProfileHeaderShell = ({
|
||||
<View
|
||||
style={[
|
||||
t.atoms.bg,
|
||||
{borderColor: t.atoms.bg.backgroundColor},
|
||||
{borderColor: t.palette.primary_25},
|
||||
styles.avi,
|
||||
profile.associated?.labeler && styles.aviLabeler,
|
||||
]}>
|
||||
|
||||
@@ -43,6 +43,7 @@ interface Props {
|
||||
moderationOpts: ModerationOpts
|
||||
hideBackButton?: boolean
|
||||
isPlaceholderProfile?: boolean
|
||||
backgroundColor: string
|
||||
}
|
||||
|
||||
let ProfileHeader = (props: Props): React.ReactNode => {
|
||||
|
||||
@@ -179,7 +179,7 @@ let ProfileMenu = ({
|
||||
label={_(msg`More options`)}
|
||||
hitSlop={HITSLOP_20}
|
||||
variant="solid"
|
||||
color="secondary"
|
||||
color="secondary_inverted"
|
||||
size="small"
|
||||
shape="round">
|
||||
<ButtonIcon icon={Ellipsis} size="sm" />
|
||||
|
||||
@@ -917,6 +917,7 @@ function MockAccountScreen({
|
||||
profile={profile}
|
||||
moderationOpts={moderationOpts}
|
||||
descriptionRT={new RichText({text: profile.description as string})}
|
||||
backgroundColor={t.atoms.bg.backgroundColor}
|
||||
/>
|
||||
</ScreenHider>
|
||||
</View>
|
||||
|
||||
+161
-139
@@ -44,7 +44,8 @@ import {CenteredView} from '#/view/com/util/Views'
|
||||
import {ProfileHeader, ProfileHeaderLoading} from '#/screens/Profile/Header'
|
||||
import {ProfileFeedSection} from '#/screens/Profile/Sections/Feed'
|
||||
import {ProfileLabelsSection} from '#/screens/Profile/Sections/Labels'
|
||||
import {web} from '#/alf'
|
||||
import {ThemeProvider as Alf, useTheme, web} from '#/alf'
|
||||
import {createThemes} from '#/alf/themes'
|
||||
import * as Layout from '#/components/Layout'
|
||||
import {ScreenHider} from '#/components/moderation/ScreenHider'
|
||||
import {ProfileStarterPacks} from '#/components/StarterPack/ProfileStarterPacks'
|
||||
@@ -341,6 +342,24 @@ function ProfileScreenLoaded({
|
||||
// rendering
|
||||
// =
|
||||
|
||||
const [hue, setHue] = React.useState(0)
|
||||
React.useEffect(() => {
|
||||
const id = setInterval(() => {
|
||||
setHue(Math.random() * 360)
|
||||
}, 5000)
|
||||
return () => clearInterval(id)
|
||||
}, [])
|
||||
|
||||
const t = useTheme()
|
||||
|
||||
const theme = createThemes({
|
||||
hues: {
|
||||
primary: hue,
|
||||
negative: 0,
|
||||
positive: 0,
|
||||
},
|
||||
})
|
||||
|
||||
const renderHeader = () => {
|
||||
return (
|
||||
<ExpoScrollForwarderView scrollViewTag={scrollViewTag}>
|
||||
@@ -351,6 +370,7 @@ function ProfileScreenLoaded({
|
||||
moderationOpts={moderationOpts}
|
||||
hideBackButton={hideBackButton}
|
||||
isPlaceholderProfile={showPlaceholder}
|
||||
backgroundColor={t.atoms.bg.backgroundColor}
|
||||
/>
|
||||
</ExpoScrollForwarderView>
|
||||
)
|
||||
@@ -362,141 +382,143 @@ function ProfileScreenLoaded({
|
||||
style={styles.container}
|
||||
screenDescription={_(msg`profile`)}
|
||||
modui={moderation.ui('profileView')}>
|
||||
<PagerWithHeader
|
||||
testID="profilePager"
|
||||
isHeaderReady={!showPlaceholder}
|
||||
items={sectionTitles}
|
||||
onPageSelected={onPageSelected}
|
||||
onCurrentPageSelected={onCurrentPageSelected}
|
||||
renderHeader={renderHeader}
|
||||
allowHeaderOverScroll>
|
||||
{showFiltersTab
|
||||
? ({headerHeight, isFocused, scrollElRef}) => (
|
||||
<ProfileLabelsSection
|
||||
ref={labelsSectionRef}
|
||||
labelerInfo={labelerInfo}
|
||||
labelerError={labelerError}
|
||||
isLabelerLoading={isLabelerLoading}
|
||||
moderationOpts={moderationOpts}
|
||||
scrollElRef={scrollElRef as ListRef}
|
||||
headerHeight={headerHeight}
|
||||
isFocused={isFocused}
|
||||
setScrollViewTag={setScrollViewTag}
|
||||
/>
|
||||
)
|
||||
: null}
|
||||
{showListsTab && !!profile.associated?.labeler
|
||||
? ({headerHeight, isFocused, scrollElRef}) => (
|
||||
<ProfileLists
|
||||
ref={listsSectionRef}
|
||||
did={profile.did}
|
||||
scrollElRef={scrollElRef as ListRef}
|
||||
headerOffset={headerHeight}
|
||||
enabled={isFocused}
|
||||
setScrollViewTag={setScrollViewTag}
|
||||
/>
|
||||
)
|
||||
: null}
|
||||
{showPostsTab
|
||||
? ({headerHeight, isFocused, scrollElRef}) => (
|
||||
<ProfileFeedSection
|
||||
ref={postsSectionRef}
|
||||
feed={`author|${profile.did}|posts_and_author_threads`}
|
||||
headerHeight={headerHeight}
|
||||
isFocused={isFocused}
|
||||
scrollElRef={scrollElRef as ListRef}
|
||||
ignoreFilterFor={profile.did}
|
||||
setScrollViewTag={setScrollViewTag}
|
||||
/>
|
||||
)
|
||||
: null}
|
||||
{showRepliesTab
|
||||
? ({headerHeight, isFocused, scrollElRef}) => (
|
||||
<ProfileFeedSection
|
||||
ref={repliesSectionRef}
|
||||
feed={`author|${profile.did}|posts_with_replies`}
|
||||
headerHeight={headerHeight}
|
||||
isFocused={isFocused}
|
||||
scrollElRef={scrollElRef as ListRef}
|
||||
ignoreFilterFor={profile.did}
|
||||
setScrollViewTag={setScrollViewTag}
|
||||
/>
|
||||
)
|
||||
: null}
|
||||
{showMediaTab
|
||||
? ({headerHeight, isFocused, scrollElRef}) => (
|
||||
<ProfileFeedSection
|
||||
ref={mediaSectionRef}
|
||||
feed={`author|${profile.did}|posts_with_media`}
|
||||
headerHeight={headerHeight}
|
||||
isFocused={isFocused}
|
||||
scrollElRef={scrollElRef as ListRef}
|
||||
ignoreFilterFor={profile.did}
|
||||
setScrollViewTag={setScrollViewTag}
|
||||
/>
|
||||
)
|
||||
: null}
|
||||
{showLikesTab
|
||||
? ({headerHeight, isFocused, scrollElRef}) => (
|
||||
<ProfileFeedSection
|
||||
ref={likesSectionRef}
|
||||
feed={`likes|${profile.did}`}
|
||||
headerHeight={headerHeight}
|
||||
isFocused={isFocused}
|
||||
scrollElRef={scrollElRef as ListRef}
|
||||
ignoreFilterFor={profile.did}
|
||||
setScrollViewTag={setScrollViewTag}
|
||||
/>
|
||||
)
|
||||
: null}
|
||||
{showFeedsTab
|
||||
? ({headerHeight, isFocused, scrollElRef}) => (
|
||||
<ProfileFeedgens
|
||||
ref={feedsSectionRef}
|
||||
did={profile.did}
|
||||
scrollElRef={scrollElRef as ListRef}
|
||||
headerOffset={headerHeight}
|
||||
enabled={isFocused}
|
||||
setScrollViewTag={setScrollViewTag}
|
||||
/>
|
||||
)
|
||||
: null}
|
||||
{showStarterPacksTab
|
||||
? ({headerHeight, isFocused, scrollElRef}) => (
|
||||
<ProfileStarterPacks
|
||||
ref={starterPacksSectionRef}
|
||||
isMe={isMe}
|
||||
starterPacksQuery={starterPacksQuery}
|
||||
scrollElRef={scrollElRef as ListRef}
|
||||
headerOffset={headerHeight}
|
||||
enabled={isFocused}
|
||||
setScrollViewTag={setScrollViewTag}
|
||||
/>
|
||||
)
|
||||
: null}
|
||||
{showListsTab && !profile.associated?.labeler
|
||||
? ({headerHeight, isFocused, scrollElRef}) => (
|
||||
<ProfileLists
|
||||
ref={listsSectionRef}
|
||||
did={profile.did}
|
||||
scrollElRef={scrollElRef as ListRef}
|
||||
headerOffset={headerHeight}
|
||||
enabled={isFocused}
|
||||
setScrollViewTag={setScrollViewTag}
|
||||
/>
|
||||
)
|
||||
: null}
|
||||
</PagerWithHeader>
|
||||
{hasSession && (
|
||||
<FAB
|
||||
testID="composeFAB"
|
||||
onPress={onPressCompose}
|
||||
icon={<ComposeIcon2 strokeWidth={1.5} size={29} style={s.white} />}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={_(msg`New post`)}
|
||||
accessibilityHint=""
|
||||
/>
|
||||
)}
|
||||
<Alf themeName={t.name} theme={theme}>
|
||||
<PagerWithHeader
|
||||
testID="profilePager"
|
||||
isHeaderReady={!showPlaceholder}
|
||||
items={sectionTitles}
|
||||
onPageSelected={onPageSelected}
|
||||
onCurrentPageSelected={onCurrentPageSelected}
|
||||
renderHeader={renderHeader}
|
||||
allowHeaderOverScroll>
|
||||
{showFiltersTab
|
||||
? ({headerHeight, isFocused, scrollElRef}) => (
|
||||
<ProfileLabelsSection
|
||||
ref={labelsSectionRef}
|
||||
labelerInfo={labelerInfo}
|
||||
labelerError={labelerError}
|
||||
isLabelerLoading={isLabelerLoading}
|
||||
moderationOpts={moderationOpts}
|
||||
scrollElRef={scrollElRef as ListRef}
|
||||
headerHeight={headerHeight}
|
||||
isFocused={isFocused}
|
||||
setScrollViewTag={setScrollViewTag}
|
||||
/>
|
||||
)
|
||||
: null}
|
||||
{showListsTab && !!profile.associated?.labeler
|
||||
? ({headerHeight, isFocused, scrollElRef}) => (
|
||||
<ProfileLists
|
||||
ref={listsSectionRef}
|
||||
did={profile.did}
|
||||
scrollElRef={scrollElRef as ListRef}
|
||||
headerOffset={headerHeight}
|
||||
enabled={isFocused}
|
||||
setScrollViewTag={setScrollViewTag}
|
||||
/>
|
||||
)
|
||||
: null}
|
||||
{showPostsTab
|
||||
? ({headerHeight, isFocused, scrollElRef}) => (
|
||||
<ProfileFeedSection
|
||||
ref={postsSectionRef}
|
||||
feed={`author|${profile.did}|posts_and_author_threads`}
|
||||
headerHeight={headerHeight}
|
||||
isFocused={isFocused}
|
||||
scrollElRef={scrollElRef as ListRef}
|
||||
ignoreFilterFor={profile.did}
|
||||
setScrollViewTag={setScrollViewTag}
|
||||
/>
|
||||
)
|
||||
: null}
|
||||
{showRepliesTab
|
||||
? ({headerHeight, isFocused, scrollElRef}) => (
|
||||
<ProfileFeedSection
|
||||
ref={repliesSectionRef}
|
||||
feed={`author|${profile.did}|posts_with_replies`}
|
||||
headerHeight={headerHeight}
|
||||
isFocused={isFocused}
|
||||
scrollElRef={scrollElRef as ListRef}
|
||||
ignoreFilterFor={profile.did}
|
||||
setScrollViewTag={setScrollViewTag}
|
||||
/>
|
||||
)
|
||||
: null}
|
||||
{showMediaTab
|
||||
? ({headerHeight, isFocused, scrollElRef}) => (
|
||||
<ProfileFeedSection
|
||||
ref={mediaSectionRef}
|
||||
feed={`author|${profile.did}|posts_with_media`}
|
||||
headerHeight={headerHeight}
|
||||
isFocused={isFocused}
|
||||
scrollElRef={scrollElRef as ListRef}
|
||||
ignoreFilterFor={profile.did}
|
||||
setScrollViewTag={setScrollViewTag}
|
||||
/>
|
||||
)
|
||||
: null}
|
||||
{showLikesTab
|
||||
? ({headerHeight, isFocused, scrollElRef}) => (
|
||||
<ProfileFeedSection
|
||||
ref={likesSectionRef}
|
||||
feed={`likes|${profile.did}`}
|
||||
headerHeight={headerHeight}
|
||||
isFocused={isFocused}
|
||||
scrollElRef={scrollElRef as ListRef}
|
||||
ignoreFilterFor={profile.did}
|
||||
setScrollViewTag={setScrollViewTag}
|
||||
/>
|
||||
)
|
||||
: null}
|
||||
{showFeedsTab
|
||||
? ({headerHeight, isFocused, scrollElRef}) => (
|
||||
<ProfileFeedgens
|
||||
ref={feedsSectionRef}
|
||||
did={profile.did}
|
||||
scrollElRef={scrollElRef as ListRef}
|
||||
headerOffset={headerHeight}
|
||||
enabled={isFocused}
|
||||
setScrollViewTag={setScrollViewTag}
|
||||
/>
|
||||
)
|
||||
: null}
|
||||
{showStarterPacksTab
|
||||
? ({headerHeight, isFocused, scrollElRef}) => (
|
||||
<ProfileStarterPacks
|
||||
ref={starterPacksSectionRef}
|
||||
isMe={isMe}
|
||||
starterPacksQuery={starterPacksQuery}
|
||||
scrollElRef={scrollElRef as ListRef}
|
||||
headerOffset={headerHeight}
|
||||
enabled={isFocused}
|
||||
setScrollViewTag={setScrollViewTag}
|
||||
/>
|
||||
)
|
||||
: null}
|
||||
{showListsTab && !profile.associated?.labeler
|
||||
? ({headerHeight, isFocused, scrollElRef}) => (
|
||||
<ProfileLists
|
||||
ref={listsSectionRef}
|
||||
did={profile.did}
|
||||
scrollElRef={scrollElRef as ListRef}
|
||||
headerOffset={headerHeight}
|
||||
enabled={isFocused}
|
||||
setScrollViewTag={setScrollViewTag}
|
||||
/>
|
||||
)
|
||||
: null}
|
||||
</PagerWithHeader>
|
||||
{hasSession && (
|
||||
<FAB
|
||||
testID="composeFAB"
|
||||
onPress={onPressCompose}
|
||||
icon={<ComposeIcon2 strokeWidth={1.5} size={29} style={s.white} />}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={_(msg`New post`)}
|
||||
accessibilityHint=""
|
||||
/>
|
||||
)}
|
||||
</Alf>
|
||||
</ScreenHider>
|
||||
)
|
||||
}
|
||||
@@ -516,10 +538,10 @@ function useRichText(text: string): [RichTextAPI, boolean] {
|
||||
let ignore = false
|
||||
async function resolveRTFacets() {
|
||||
// new each time
|
||||
const resolvedRT = new RichTextAPI({text})
|
||||
await resolvedRT.detectFacets(agent)
|
||||
const newRT = new RichTextAPI({text})
|
||||
await newRT.detectFacets(agent)
|
||||
if (!ignore) {
|
||||
setResolvedRT(resolvedRT)
|
||||
setResolvedRT(newRT)
|
||||
}
|
||||
}
|
||||
resolveRTFacets()
|
||||
|
||||
@@ -104,13 +104,13 @@ function StorybookInner() {
|
||||
|
||||
<Settings />
|
||||
|
||||
<ThemeProvider theme="light">
|
||||
<ThemeProvider themeName="light">
|
||||
<Theming />
|
||||
</ThemeProvider>
|
||||
<ThemeProvider theme="dim">
|
||||
<ThemeProvider themeName="dim">
|
||||
<Theming />
|
||||
</ThemeProvider>
|
||||
<ThemeProvider theme="dark">
|
||||
<ThemeProvider themeName="dark">
|
||||
<Theming />
|
||||
</ThemeProvider>
|
||||
|
||||
|
||||
@@ -7204,6 +7204,11 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/use-sync-external-store/-/use-sync-external-store-0.0.6.tgz#60be8d21baab8c305132eb9cb912ed497852aadc"
|
||||
integrity sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==
|
||||
|
||||
"@types/wcag-contrast@^3.0.3":
|
||||
version "3.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@types/wcag-contrast/-/wcag-contrast-3.0.3.tgz#0b0339ab210b859eeee555b3fef58d293216905c"
|
||||
integrity sha512-oprevfwJSLfpQK4KaWsRKJuNoebV76+xhmbXiWJGy+FkS34LpCgCMNIwRXWTb8xmmSxUE2ycFOYE7uyRVRm3LA==
|
||||
|
||||
"@types/ws@^8.5.5":
|
||||
version "8.5.5"
|
||||
resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.5.tgz#af587964aa06682702ee6dcbc7be41a80e4b28eb"
|
||||
@@ -8871,7 +8876,7 @@ color-name@1.1.3:
|
||||
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25"
|
||||
integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==
|
||||
|
||||
color-name@^1.0.0, color-name@~1.1.4:
|
||||
color-name@^1.0.0, color-name@^1.1.4, color-name@~1.1.4:
|
||||
version "1.1.4"
|
||||
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
|
||||
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
|
||||
@@ -10314,6 +10319,11 @@ eslint@^8.19.0:
|
||||
strip-ansi "^6.0.1"
|
||||
text-table "^0.2.0"
|
||||
|
||||
esm@^3.0.84:
|
||||
version "3.2.25"
|
||||
resolved "https://registry.yarnpkg.com/esm/-/esm-3.2.25.tgz#342c18c29d56157688ba5ce31f8431fbb795cc10"
|
||||
integrity sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==
|
||||
|
||||
espree@^9.6.0, espree@^9.6.1:
|
||||
version "9.6.1"
|
||||
resolved "https://registry.yarnpkg.com/espree/-/espree-9.6.1.tgz#a2a17b8e434690a5432f2f8018ce71d331a48c6f"
|
||||
@@ -11707,6 +11717,11 @@ hermes-profile-transformer@^0.0.6:
|
||||
dependencies:
|
||||
source-map "^0.7.3"
|
||||
|
||||
hex-rgb@^4.1.0:
|
||||
version "4.3.0"
|
||||
resolved "https://registry.yarnpkg.com/hex-rgb/-/hex-rgb-4.3.0.tgz#af5e974e83bb2fefe44d55182b004ec818c07776"
|
||||
integrity sha512-Ox1pJVrDCyGHMG9CFg1tmrRUMRPRsAWYc/PinY0XzJU4K7y7vjNoLKIQ7BR5UJMCxNN8EM1MNDmHWA/B3aZUuw==
|
||||
|
||||
history@^5.3.0:
|
||||
version "5.3.0"
|
||||
resolved "https://registry.yarnpkg.com/history/-/history-5.3.0.tgz#1548abaa245ba47992f063a0783db91ef201c73b"
|
||||
@@ -14296,6 +14311,14 @@ minizlib@^2.1.1:
|
||||
minipass "^3.0.0"
|
||||
yallist "^4.0.0"
|
||||
|
||||
mix-css-color@^0.2.0:
|
||||
version "0.2.0"
|
||||
resolved "https://registry.yarnpkg.com/mix-css-color/-/mix-css-color-0.2.0.tgz#413a2346effcb36a815b1d09bcbac12bcf3aac63"
|
||||
integrity sha512-mZugANySFPE21tjELbQddhC6HAZNzqp7gDxmW8fJFURSWtJ0nuXU26dyrb/1AR6ZYxdEAtW2bbWT9QnRtI6Jzg==
|
||||
dependencies:
|
||||
parse-css-color "^0.1.2"
|
||||
pure-color "^1.3.0"
|
||||
|
||||
mkdirp-classic@^0.5.2, mkdirp-classic@^0.5.3:
|
||||
version "0.5.3"
|
||||
resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113"
|
||||
@@ -14931,6 +14954,14 @@ parent-module@^1.0.0:
|
||||
dependencies:
|
||||
callsites "^3.0.0"
|
||||
|
||||
parse-css-color@^0.1.2:
|
||||
version "0.1.2"
|
||||
resolved "https://registry.yarnpkg.com/parse-css-color/-/parse-css-color-0.1.2.tgz#9d178f2d2b5eb7245c0deb22ea35046b79ff3f83"
|
||||
integrity sha512-z7v/tf0edGsnlm9VONQtH+u/YVrdUqZXrSBzqM13scef8Abl2VyZfYsZaJoyb/AyY4SIxtoJChSQ4MURHfY3Sg==
|
||||
dependencies:
|
||||
color-name "^1.1.4"
|
||||
hex-rgb "^4.1.0"
|
||||
|
||||
parse-json@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0"
|
||||
@@ -15934,6 +15965,11 @@ punycode@^2.1.0, punycode@^2.1.1:
|
||||
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.0.tgz#f67fa67c94da8f4d0cfff981aee4118064199b8f"
|
||||
integrity sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==
|
||||
|
||||
pure-color@^1.3.0:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/pure-color/-/pure-color-1.3.0.tgz#1fe064fb0ac851f0de61320a8bf796836422f33e"
|
||||
integrity sha512-QFADYnsVoBMw1srW7OVKEYjG+MbIa49s54w1MA1EDY6r2r/sTcKKYqRX1f4GYvnXP7eN/Pe9HFcX+hwzmrXRHA==
|
||||
|
||||
pure-rand@^6.0.0:
|
||||
version "6.0.2"
|
||||
resolved "https://registry.yarnpkg.com/pure-rand/-/pure-rand-6.0.2.tgz#a9c2ddcae9b68d736a8163036f088a2781c8b306"
|
||||
@@ -16655,6 +16691,13 @@ relateurl@^0.2.7:
|
||||
resolved "https://registry.yarnpkg.com/relateurl/-/relateurl-0.2.7.tgz#54dbf377e51440aca90a4cd274600d3ff2d888a9"
|
||||
integrity sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==
|
||||
|
||||
relative-luminance@^2.0.0:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/relative-luminance/-/relative-luminance-2.0.1.tgz#2babddf3a5a59673227d6f02e0f68e13989e3d13"
|
||||
integrity sha512-wFuITNthJilFPwkK7gNJcULxXBcfFZvZORsvdvxeOdO44wCeZnuQkf3nFFzOR/dpJNxYsdRZJLsepWbyKhnMww==
|
||||
dependencies:
|
||||
esm "^3.0.84"
|
||||
|
||||
remove-trailing-slash@^0.1.0:
|
||||
version "0.1.1"
|
||||
resolved "https://registry.yarnpkg.com/remove-trailing-slash/-/remove-trailing-slash-0.1.1.tgz#be2285a59f39c74d1bce4f825950061915e3780d"
|
||||
@@ -18763,6 +18806,13 @@ wbuf@^1.1.0, wbuf@^1.7.3:
|
||||
dependencies:
|
||||
minimalistic-assert "^1.0.0"
|
||||
|
||||
wcag-contrast@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/wcag-contrast/-/wcag-contrast-3.0.0.tgz#51c2df43f15162993006454b3362844205889efb"
|
||||
integrity sha512-RWbpg/S7FOXDCwqC2oFhN/vh8dHzj0OS6dpyOSDHyQFSmqmR+lAUStV/ziTT1GzDqL9wol+nZQB4vCi5yEak+w==
|
||||
dependencies:
|
||||
relative-luminance "^2.0.0"
|
||||
|
||||
wcwidth@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8"
|
||||
|
||||
Reference in New Issue
Block a user