merge main
This commit is contained in:
+1
-1
@@ -5,7 +5,7 @@ import Animated, {
|
||||
useSharedValue,
|
||||
} from 'react-native-reanimated'
|
||||
|
||||
export function KeyboardPadding({maxHeight}: {maxHeight?: number}) {
|
||||
export function KeyboardControllerPadding({maxHeight}: {maxHeight?: number}) {
|
||||
const keyboardHeight = useSharedValue(0)
|
||||
|
||||
useKeyboardHandler(
|
||||
@@ -0,0 +1,7 @@
|
||||
export function KeyboardControllerPadding({
|
||||
maxHeight: _,
|
||||
}: {
|
||||
maxHeight?: number
|
||||
}) {
|
||||
return null
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
export function KeyboardPadding({maxHeight: _}: {maxHeight?: number}) {
|
||||
return null
|
||||
}
|
||||
@@ -0,0 +1,200 @@
|
||||
import React from 'react'
|
||||
import {View} from 'react-native'
|
||||
import {AppBskyActorDefs, moderateProfile, ModerationOpts} from '@atproto/api'
|
||||
import {msg, plural, Trans} from '@lingui/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
|
||||
import {makeProfileLink} from '#/lib/routes/links'
|
||||
import {sanitizeDisplayName} from 'lib/strings/display-names'
|
||||
import {UserAvatar} from '#/view/com/util/UserAvatar'
|
||||
import {atoms as a, useTheme} from '#/alf'
|
||||
import {Link} from '#/components/Link'
|
||||
import {Text} from '#/components/Typography'
|
||||
|
||||
const AVI_SIZE = 30
|
||||
const AVI_BORDER = 1
|
||||
|
||||
/**
|
||||
* Shared logic to determine if `KnownFollowers` should be shown.
|
||||
*
|
||||
* Checks the # of actual returned users instead of the `count` value, because
|
||||
* `count` includes blocked users and `followers` does not.
|
||||
*/
|
||||
export function shouldShowKnownFollowers(
|
||||
knownFollowers?: AppBskyActorDefs.KnownFollowers,
|
||||
) {
|
||||
return knownFollowers && knownFollowers.followers.length > 0
|
||||
}
|
||||
|
||||
export function KnownFollowers({
|
||||
profile,
|
||||
moderationOpts,
|
||||
}: {
|
||||
profile: AppBskyActorDefs.ProfileViewDetailed
|
||||
moderationOpts: ModerationOpts
|
||||
}) {
|
||||
const cache = React.useRef<Map<string, AppBskyActorDefs.KnownFollowers>>(
|
||||
new Map(),
|
||||
)
|
||||
|
||||
/*
|
||||
* Results for `knownFollowers` are not sorted consistently, so when
|
||||
* revalidating we can see a flash of this data updating. This cache prevents
|
||||
* this happening for screens that remain in memory. When pushing a new
|
||||
* screen, or once this one is popped, this cache is empty, so new data is
|
||||
* displayed.
|
||||
*/
|
||||
if (profile.viewer?.knownFollowers && !cache.current.has(profile.did)) {
|
||||
cache.current.set(profile.did, profile.viewer.knownFollowers)
|
||||
}
|
||||
|
||||
const cachedKnownFollowers = cache.current.get(profile.did)
|
||||
|
||||
if (cachedKnownFollowers && shouldShowKnownFollowers(cachedKnownFollowers)) {
|
||||
return (
|
||||
<KnownFollowersInner
|
||||
profile={profile}
|
||||
cachedKnownFollowers={cachedKnownFollowers}
|
||||
moderationOpts={moderationOpts}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
function KnownFollowersInner({
|
||||
profile,
|
||||
moderationOpts,
|
||||
cachedKnownFollowers,
|
||||
}: {
|
||||
profile: AppBskyActorDefs.ProfileViewDetailed
|
||||
moderationOpts: ModerationOpts
|
||||
cachedKnownFollowers: AppBskyActorDefs.KnownFollowers
|
||||
}) {
|
||||
const t = useTheme()
|
||||
const {_} = useLingui()
|
||||
|
||||
const textStyle = [
|
||||
a.flex_1,
|
||||
a.text_sm,
|
||||
a.leading_snug,
|
||||
t.atoms.text_contrast_medium,
|
||||
]
|
||||
|
||||
// list of users, minus blocks
|
||||
const returnedCount = cachedKnownFollowers.followers.length
|
||||
// db count, includes blocks
|
||||
const fullCount = cachedKnownFollowers.count
|
||||
// knownFollowers can return up to 5 users, but will exclude blocks
|
||||
// therefore, if we have less 5 users, use whichever count is lower
|
||||
const count =
|
||||
returnedCount < 5 ? Math.min(fullCount, returnedCount) : fullCount
|
||||
|
||||
const slice = cachedKnownFollowers.followers.slice(0, 3).map(f => {
|
||||
const moderation = moderateProfile(f, moderationOpts)
|
||||
return {
|
||||
profile: {
|
||||
...f,
|
||||
displayName: sanitizeDisplayName(
|
||||
f.displayName || f.handle,
|
||||
moderation.ui('displayName'),
|
||||
),
|
||||
},
|
||||
moderation,
|
||||
}
|
||||
})
|
||||
|
||||
return (
|
||||
<Link
|
||||
label={_(
|
||||
msg`Press to view followers of this account that you also follow`,
|
||||
)}
|
||||
to={makeProfileLink(profile, 'known-followers')}
|
||||
style={[
|
||||
a.flex_1,
|
||||
a.flex_row,
|
||||
a.gap_md,
|
||||
a.align_center,
|
||||
{marginLeft: -AVI_BORDER},
|
||||
]}>
|
||||
{({hovered, pressed}) => (
|
||||
<>
|
||||
<View
|
||||
style={[
|
||||
{
|
||||
height: AVI_SIZE,
|
||||
width: AVI_SIZE + (slice.length - 1) * a.gap_md.gap,
|
||||
},
|
||||
pressed && {
|
||||
opacity: 0.5,
|
||||
},
|
||||
]}>
|
||||
{slice.map(({profile: prof, moderation}, i) => (
|
||||
<View
|
||||
key={prof.did}
|
||||
style={[
|
||||
a.absolute,
|
||||
a.rounded_full,
|
||||
{
|
||||
borderWidth: AVI_BORDER,
|
||||
borderColor: t.atoms.bg.backgroundColor,
|
||||
width: AVI_SIZE + AVI_BORDER * 2,
|
||||
height: AVI_SIZE + AVI_BORDER * 2,
|
||||
left: i * a.gap_md.gap,
|
||||
zIndex: AVI_BORDER - i,
|
||||
},
|
||||
]}>
|
||||
<UserAvatar
|
||||
size={AVI_SIZE}
|
||||
avatar={prof.avatar}
|
||||
moderation={moderation.ui('avatar')}
|
||||
/>
|
||||
</View>
|
||||
))}
|
||||
</View>
|
||||
|
||||
<Text
|
||||
style={[
|
||||
textStyle,
|
||||
hovered && {
|
||||
textDecorationLine: 'underline',
|
||||
textDecorationColor: t.atoms.text_contrast_medium.color,
|
||||
},
|
||||
pressed && {
|
||||
opacity: 0.5,
|
||||
},
|
||||
]}
|
||||
numberOfLines={2}>
|
||||
<Trans>Followed by</Trans>{' '}
|
||||
{count > 2 ? (
|
||||
<>
|
||||
{slice.slice(0, 2).map(({profile: prof}, i) => (
|
||||
<Text key={prof.did} style={textStyle}>
|
||||
{prof.displayName}
|
||||
{i === 0 && ', '}
|
||||
</Text>
|
||||
))}
|
||||
{', '}
|
||||
{plural(count - 2, {
|
||||
one: 'and # other',
|
||||
other: 'and # others',
|
||||
})}
|
||||
</>
|
||||
) : count === 2 ? (
|
||||
slice.map(({profile: prof}, i) => (
|
||||
<Text key={prof.did} style={textStyle}>
|
||||
{prof.displayName} {i === 0 ? _(msg`and`) + ' ' : ''}
|
||||
</Text>
|
||||
))
|
||||
) : (
|
||||
<Text key={slice[0].profile.did} style={textStyle}>
|
||||
{slice[0].profile.displayName}
|
||||
</Text>
|
||||
)}
|
||||
</Text>
|
||||
</>
|
||||
)}
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
@@ -15,7 +15,6 @@ import * as Dialog from '#/components/Dialog'
|
||||
import * as Toggle from '#/components/forms/Toggle'
|
||||
import {Check_Stroke2_Corner0_Rounded as Check} from '#/components/icons/Check'
|
||||
import {ChevronLeft_Stroke2_Corner0_Rounded as ChevronLeft} from '#/components/icons/Chevron'
|
||||
import {KeyboardPadding} from '#/components/KeyboardPadding'
|
||||
import {Loader} from '#/components/Loader'
|
||||
import {Text} from '#/components/Typography'
|
||||
import {ReportDialogProps} from './types'
|
||||
@@ -222,7 +221,6 @@ export function SubmitView({
|
||||
{submitting && <ButtonIcon icon={Loader} />}
|
||||
</Button>
|
||||
</View>
|
||||
<KeyboardPadding />
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -28,7 +28,6 @@ import {Hashtag_Stroke2_Corner0_Rounded as Hashtag} from '#/components/icons/Has
|
||||
import {PageText_Stroke2_Corner0_Rounded as PageText} from '#/components/icons/PageText'
|
||||
import {PlusLarge_Stroke2_Corner0_Rounded as Plus} from '#/components/icons/Plus'
|
||||
import {TimesLarge_Stroke2_Corner0_Rounded as X} from '#/components/icons/Times'
|
||||
import {KeyboardPadding} from '#/components/KeyboardPadding'
|
||||
import {Loader} from '#/components/Loader'
|
||||
import * as Prompt from '#/components/Prompt'
|
||||
import {Text} from '#/components/Typography'
|
||||
@@ -257,7 +256,6 @@ function MutedWordsInner() {
|
||||
</View>
|
||||
|
||||
<Dialog.Close />
|
||||
<KeyboardPadding maxHeight={100} />
|
||||
</Dialog.ScrollableInner>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ import {CharProgress} from '#/view/com/composer/char-progress/CharProgress'
|
||||
import * as Toast from '#/view/com/util/Toast'
|
||||
import {atoms as a, useBreakpoints, useTheme} from '#/alf'
|
||||
import * as Dialog from '#/components/Dialog'
|
||||
import {KeyboardControllerPadding} from '#/components/KeyboardControllerPadding'
|
||||
import {Button, ButtonIcon, ButtonText} from '../Button'
|
||||
import {Divider} from '../Divider'
|
||||
import {ChevronLeft_Stroke2_Corner0_Rounded as Chevron} from '../icons/Chevron'
|
||||
@@ -47,6 +48,7 @@ let ReportDialog = ({
|
||||
<Dialog.ScrollableInner label={_(msg`Report this message`)}>
|
||||
<DialogInner params={params} />
|
||||
<Dialog.Close />
|
||||
<KeyboardControllerPadding />
|
||||
</Dialog.ScrollableInner>
|
||||
</Dialog.Outer>
|
||||
)
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
import {createSinglePathSVG} from './TEMPLATE'
|
||||
|
||||
/*
|
||||
* This icon is off-menu, not part of the icon set
|
||||
*/
|
||||
|
||||
export const ListPlus_Stroke2_Corner0_Rounded = createSinglePathSVG({
|
||||
path: 'M4 5a1 1 0 0 0 0 2h16a1 1 0 1 0 0-2H4Zm0 12a1 1 0 1 0 0 2h3a1 1 0 1 0 0-2H4Zm-1-5a1 1 0 0 1 1-1h5a1 1 0 1 1 0 2H4a1 1 0 0 1-1-1Zm14-3c.552 0 1 .41 1 .917V13.5h3.583c.507 0 .917.448.917 1s-.41 1-.917 1H18v3.583c0 .507-.448.917-1 .917s-1-.41-1-.917V15.5h-3.583c-.507 0-.917-.448-.917-1s.41-1 .917-1H16V9.917C16 9.41 16.448 9 17 9Z',
|
||||
})
|
||||
@@ -0,0 +1,5 @@
|
||||
import {createSinglePathSVG} from './TEMPLATE'
|
||||
|
||||
export const Menu_Stroke2_Corner0_Rounded = createSinglePathSVG({
|
||||
path: 'M2 6a1 1 0 0 1 1-1h18a1 1 0 1 1 0 2H3a1 1 0 0 1-1-1Zm0 6a1 1 0 0 1 1-1h18a1 1 0 1 1 0 2H3a1 1 0 0 1-1-1Zm0 6a1 1 0 0 1 1-1h18a1 1 0 1 1 0 2H3a1 1 0 0 1-1-1Z',
|
||||
})
|
||||
@@ -0,0 +1,5 @@
|
||||
import {createSinglePathSVG} from './TEMPLATE'
|
||||
|
||||
export const Pin_Stroke2_Corner0_Rounded = createSinglePathSVG({
|
||||
path: 'M6.5 3a1 1 0 0 1 1-1h9a1 1 0 0 1 1 1v3.997a6.25 6.25 0 0 0 1.83 4.42l.377.376A1 1 0 0 1 20 12.5V15a1 1 0 0 1-1 1h-6v5a1 1 0 1 1-2 0v-5H5a1 1 0 0 1-1-1v-2.5a1 1 0 0 1 .293-.707l.376-.377A6.25 6.25 0 0 0 6.5 6.996V3.001Zm2 1v2.997a8.25 8.25 0 0 1-2.416 5.834L6 12.914V14h12v-1.086l-.084-.083A8.25 8.25 0 0 1 15.5 6.997V4h-7Z',
|
||||
})
|
||||
@@ -0,0 +1,5 @@
|
||||
import {createSinglePathSVG} from './TEMPLATE'
|
||||
|
||||
export const Shapes_Stroke2_Corner0_Rounded = createSinglePathSVG({
|
||||
path: 'M7 3a1 1 0 0 1 1 1v2h2a1 1 0 1 1 0 2H8v2a1 1 0 1 1-2 0V8H4a1 1 0 0 1 0-2h2V4a1 1 0 0 1 1-1Zm6 4a4 4 0 1 1 8 0 4 4 0 0 1-8 0Zm4-2a2 2 0 1 0 0 4 2 2 0 0 0 0-4ZM3 14a1 1 0 0 1 1-1h6a1 1 0 0 1 1 1v6a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1v-6Zm2 1v4h4v-4H5Zm9.171-.829a1 1 0 0 1 1.415 0L17 15.585l1.414-1.414a1 1 0 1 1 1.414 1.414L18.414 17l1.414 1.414a1 1 0 0 1-1.414 1.414L17 18.414l-1.415 1.414a1 1 0 0 1-1.414-1.414l1.415-1.415-1.415-1.414a1 1 0 0 1 0-1.414Z',
|
||||
})
|
||||
@@ -0,0 +1,9 @@
|
||||
import {createSinglePathSVG} from './TEMPLATE'
|
||||
|
||||
export const Star_Stroke2_Corner0_Rounded = createSinglePathSVG({
|
||||
path: 'M12 1a1 1 0 0 1 .902.568l2.643 5.517 6.085.799a1 1 0 0 1 .557 1.718l-4.45 4.207 1.118 6.008a1 1 0 0 1-1.46 1.063L12 17.962 6.604 20.88a1 1 0 0 1-1.459-1.063l1.117-6.008-4.45-4.207a1 1 0 0 1 .558-1.718l6.085-.8 2.643-5.516A1 1 0 0 1 12 1Zm0 3.315-1.975 4.123a1 1 0 0 1-.772.56l-4.538.595 3.317 3.137a1 1 0 0 1 .296.91l-.834 4.485 4.03-2.179a1 1 0 0 1 .952 0l4.03 2.179-.834-4.485a1 1 0 0 1 .296-.91l3.317-3.137-4.538-.596a1 1 0 0 1-.772-.56L12 4.316Z',
|
||||
})
|
||||
|
||||
export const Star_Filled_Corner0_Rounded = createSinglePathSVG({
|
||||
path: 'M12.902 1.568a1 1 0 0 0-1.804 0L8.455 7.085l-6.085.799a1 1 0 0 0-.557 1.718l4.45 4.207-1.117 6.008a1 1 0 0 0 1.458 1.063L12 17.962l5.396 2.918a1 1 0 0 0 1.459-1.063l-1.117-6.008 4.45-4.207a1 1 0 0 0-.558-1.718l-6.085-.8-2.643-5.516Z',
|
||||
})
|
||||
@@ -14,7 +14,6 @@ import * as Toast from '#/view/com/util/Toast'
|
||||
import {atoms as a, useBreakpoints, useTheme} from '#/alf'
|
||||
import {Button, ButtonIcon, ButtonText} from '#/components/Button'
|
||||
import * as Dialog from '#/components/Dialog'
|
||||
import {KeyboardPadding} from '#/components/KeyboardPadding'
|
||||
import {InlineLinkText} from '#/components/Link'
|
||||
import {Text} from '#/components/Typography'
|
||||
import {Divider} from '../Divider'
|
||||
@@ -110,7 +109,6 @@ function LabelsOnMeDialogInner(props: LabelsOnMeDialogProps) {
|
||||
</>
|
||||
)}
|
||||
<Dialog.Close />
|
||||
<KeyboardPadding />
|
||||
</Dialog.ScrollableInner>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user