Enable access for group clip clops with no messages (#10276)
This commit is contained in:
@@ -13,7 +13,11 @@ import {makeProfileLink} from '#/lib/routes/links'
|
|||||||
import {type NavigationProp} from '#/lib/routes/types'
|
import {type NavigationProp} from '#/lib/routes/types'
|
||||||
import {logger} from '#/logger'
|
import {logger} from '#/logger'
|
||||||
import {type Shadow} from '#/state/cache/profile-shadow'
|
import {type Shadow} from '#/state/cache/profile-shadow'
|
||||||
import {isConvoActive, useConvo} from '#/state/messages/convo'
|
import {
|
||||||
|
type ActiveConvoStates,
|
||||||
|
isConvoActive,
|
||||||
|
useConvo,
|
||||||
|
} from '#/state/messages/convo'
|
||||||
import {type ConvoItem} from '#/state/messages/convo/types'
|
import {type ConvoItem} from '#/state/messages/convo/types'
|
||||||
import {useSession} from '#/state/session'
|
import {useSession} from '#/state/session'
|
||||||
import {PreviewableUserAvatar} from '#/view/com/util/UserAvatar'
|
import {PreviewableUserAvatar} from '#/view/com/util/UserAvatar'
|
||||||
@@ -36,10 +40,13 @@ export function MessagesListHeader({
|
|||||||
moderation,
|
moderation,
|
||||||
}: {
|
}: {
|
||||||
profile?: Shadow<AppBskyActorDefs.ProfileViewDetailed>
|
profile?: Shadow<AppBskyActorDefs.ProfileViewDetailed>
|
||||||
moderation?: ModerationDecision
|
moderation?: ModerationDecision | null
|
||||||
}) {
|
}) {
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
|
|
||||||
|
const convoState = useConvo()
|
||||||
|
const isGroupChat = convoState?.isGroup?.()
|
||||||
|
|
||||||
const blockInfo = useMemo(() => {
|
const blockInfo = useMemo(() => {
|
||||||
if (!moderation) return
|
if (!moderation) return
|
||||||
const modui = moderation.ui('profileView')
|
const modui = moderation.ui('profileView')
|
||||||
@@ -58,12 +65,21 @@ export function MessagesListHeader({
|
|||||||
<View style={[{minHeight: PFP_SIZE}, a.justify_center]}>
|
<View style={[{minHeight: PFP_SIZE}, a.justify_center]}>
|
||||||
<Layout.Header.BackButton />
|
<Layout.Header.BackButton />
|
||||||
</View>
|
</View>
|
||||||
{profile && moderation && blockInfo ? (
|
{isConvoActive(convoState) ? (
|
||||||
<HeaderReady
|
moderation && blockInfo && profile && !isGroupChat ? (
|
||||||
profile={profile}
|
<ProfileHeaderReady
|
||||||
moderation={moderation}
|
convoState={convoState}
|
||||||
blockInfo={blockInfo}
|
profile={profile}
|
||||||
/>
|
moderation={moderation}
|
||||||
|
blockInfo={blockInfo}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<GroupHeaderReady
|
||||||
|
convoState={convoState}
|
||||||
|
profile={profile}
|
||||||
|
moderation={moderation}
|
||||||
|
/>
|
||||||
|
)
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<View style={[a.flex_row, a.align_center, a.gap_md, a.flex_1]}>
|
<View style={[a.flex_row, a.align_center, a.gap_md, a.flex_1]}>
|
||||||
@@ -94,11 +110,13 @@ export function MessagesListHeader({
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function HeaderReady({
|
function ProfileHeaderReady({
|
||||||
|
convoState,
|
||||||
profile,
|
profile,
|
||||||
moderation,
|
moderation,
|
||||||
blockInfo,
|
blockInfo,
|
||||||
}: {
|
}: {
|
||||||
|
convoState: ActiveConvoStates
|
||||||
profile: Shadow<AppBskyActorDefs.ProfileViewDetailed>
|
profile: Shadow<AppBskyActorDefs.ProfileViewDetailed>
|
||||||
moderation: ModerationDecision
|
moderation: ModerationDecision
|
||||||
blockInfo: {
|
blockInfo: {
|
||||||
@@ -107,21 +125,12 @@ function HeaderReady({
|
|||||||
}
|
}
|
||||||
}) {
|
}) {
|
||||||
const {t: l} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const t = useTheme()
|
|
||||||
const convoState = useConvo()
|
|
||||||
const {currentAccount} = useSession()
|
const {currentAccount} = useSession()
|
||||||
|
|
||||||
const navigation = useNavigation<NavigationProp>()
|
|
||||||
|
|
||||||
const groupInfo = convoState.getGroupInfo?.()
|
|
||||||
const isGroupChat = groupInfo != null
|
|
||||||
|
|
||||||
const isDeletedAccount = profile?.handle === 'missing.invalid'
|
const isDeletedAccount = profile?.handle === 'missing.invalid'
|
||||||
const displayName = isGroupChat
|
const displayName = isDeletedAccount
|
||||||
? (groupInfo.name ?? l`${profile.handle}'s group chat`)
|
? l`Deleted Account`
|
||||||
: isDeletedAccount
|
: createSanitizedDisplayName(profile, true, moderation.ui('displayName'))
|
||||||
? l`Deleted Account`
|
|
||||||
: createSanitizedDisplayName(profile, true, moderation.ui('displayName'))
|
|
||||||
|
|
||||||
const latestMessageFromOther = convoState.items.findLast(
|
const latestMessageFromOther = convoState.items.findLast(
|
||||||
(item: ConvoItem) =>
|
(item: ConvoItem) =>
|
||||||
@@ -134,6 +143,63 @@ function HeaderReady({
|
|||||||
? latestMessageFromOther.message
|
? latestMessageFromOther.message
|
||||||
: undefined
|
: undefined
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Wrapper
|
||||||
|
heading={
|
||||||
|
<Link
|
||||||
|
label={l`View ${displayName}’s profile`}
|
||||||
|
style={[a.flex_row, a.gap_md, a.flex_1, a.pr_md]}
|
||||||
|
to={makeProfileLink(profile)}>
|
||||||
|
<PreviewableUserAvatar
|
||||||
|
size={PFP_SIZE}
|
||||||
|
profile={profile}
|
||||||
|
moderation={moderation.ui('avatar')}
|
||||||
|
disableHoverCard={moderation.blocked}
|
||||||
|
/>
|
||||||
|
<ProfileBadges profile={profile} size="md" style={[a.pl_xs]} />
|
||||||
|
</Link>
|
||||||
|
}
|
||||||
|
muted={convoState.convo?.muted}
|
||||||
|
settings={
|
||||||
|
isConvoActive(convoState) ? (
|
||||||
|
<ConvoMenu
|
||||||
|
convo={convoState.convo}
|
||||||
|
profile={profile}
|
||||||
|
currentScreen="conversation"
|
||||||
|
blockInfo={blockInfo}
|
||||||
|
latestReportableMessage={latestReportableMessage}
|
||||||
|
/>
|
||||||
|
) : null
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function GroupHeaderReady({
|
||||||
|
convoState,
|
||||||
|
profile,
|
||||||
|
moderation,
|
||||||
|
}: {
|
||||||
|
convoState: ActiveConvoStates
|
||||||
|
profile?: Shadow<AppBskyActorDefs.ProfileViewDetailed>
|
||||||
|
moderation?: ModerationDecision | null
|
||||||
|
}) {
|
||||||
|
const {t: l} = useLingui()
|
||||||
|
|
||||||
|
const navigation = useNavigation<NavigationProp>()
|
||||||
|
|
||||||
|
const groupInfo = convoState.getGroupInfo?.()
|
||||||
|
|
||||||
|
const isDeletedAccount = profile?.handle === 'missing.invalid'
|
||||||
|
const displayName = isDeletedAccount
|
||||||
|
? l`Deleted Account`
|
||||||
|
: profile
|
||||||
|
? createSanitizedDisplayName(profile, true, moderation?.ui('displayName'))
|
||||||
|
: undefined
|
||||||
|
const groupName =
|
||||||
|
groupInfo?.name ??
|
||||||
|
(displayName ? l`${displayName}’s group chat` : l`Group chat`)
|
||||||
|
|
||||||
const handleNavigateToSettings = () => {
|
const handleNavigateToSettings = () => {
|
||||||
const convoId = convoState.convo?.id
|
const convoId = convoState.convo?.id
|
||||||
if (convoId) {
|
if (convoId) {
|
||||||
@@ -145,84 +211,67 @@ function HeaderReady({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Wrapper
|
||||||
|
heading={
|
||||||
|
<>
|
||||||
|
<AvatarBubbles size="small" profiles={convoState.recipients ?? []} />
|
||||||
|
<Text style={[a.text_md, a.font_semi_bold]} numberOfLines={1}>
|
||||||
|
{groupName}
|
||||||
|
</Text>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
muted={convoState.convo?.muted}
|
||||||
|
settings={
|
||||||
|
isConvoActive(convoState) ? (
|
||||||
|
<Button
|
||||||
|
label={l`Open group chat settings`}
|
||||||
|
size="small"
|
||||||
|
color="secondary"
|
||||||
|
shape="round"
|
||||||
|
variant="ghost"
|
||||||
|
style={[a.bg_transparent]}
|
||||||
|
onPress={handleNavigateToSettings}>
|
||||||
|
<ButtonIcon icon={DotsHorizontalIcon} size="md" />
|
||||||
|
</Button>
|
||||||
|
) : null
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function Wrapper({
|
||||||
|
heading,
|
||||||
|
muted,
|
||||||
|
settings,
|
||||||
|
}: {
|
||||||
|
heading: React.ReactNode
|
||||||
|
muted: boolean
|
||||||
|
settings: React.ReactNode
|
||||||
|
}) {
|
||||||
return (
|
return (
|
||||||
<View style={[a.flex_1]}>
|
<View style={[a.flex_1]}>
|
||||||
<View style={[a.w_full, a.flex_row, a.align_center, a.justify_between]}>
|
<View style={[a.w_full, a.flex_row, a.align_center, a.justify_between]}>
|
||||||
{isGroupChat ? (
|
<View style={[a.flex_row, a.align_center, a.gap_md, a.flex_1, a.pr_md]}>
|
||||||
<View
|
{heading}
|
||||||
style={[a.flex_row, a.align_center, a.gap_md, a.flex_1, a.pr_md]}>
|
<MuteStatus muted={muted} />
|
||||||
<AvatarBubbles
|
</View>
|
||||||
size="small"
|
|
||||||
profiles={convoState.recipients ?? []}
|
|
||||||
/>
|
|
||||||
<Text style={[a.text_md, a.font_semi_bold]} numberOfLines={1}>
|
|
||||||
{displayName}
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
) : (
|
|
||||||
<Link
|
|
||||||
label={l`View ${displayName}'s profile`}
|
|
||||||
style={[a.flex_row, a.gap_md, a.flex_1, a.pr_md]}
|
|
||||||
to={makeProfileLink(profile)}>
|
|
||||||
<PreviewableUserAvatar
|
|
||||||
size={PFP_SIZE}
|
|
||||||
profile={profile}
|
|
||||||
moderation={moderation.ui('avatar')}
|
|
||||||
disableHoverCard={moderation.blocked}
|
|
||||||
/>
|
|
||||||
<View style={[a.flex_1]}>
|
|
||||||
<View style={[a.flex_row, a.align_center]}>
|
|
||||||
<Text
|
|
||||||
emoji
|
|
||||||
style={[a.text_md, a.font_semi_bold, a.self_start]}
|
|
||||||
numberOfLines={1}>
|
|
||||||
{displayName}
|
|
||||||
</Text>
|
|
||||||
<ProfileBadges profile={profile} size="md" style={[a.pl_xs]} />
|
|
||||||
{convoState.convo?.muted && (
|
|
||||||
<>
|
|
||||||
<Text style={[a.text_md, t.atoms.text_contrast_medium]}>
|
|
||||||
{' '}
|
|
||||||
·{' '}
|
|
||||||
</Text>
|
|
||||||
<BellOffIcon
|
|
||||||
size="sm"
|
|
||||||
style={t.atoms.text_contrast_medium}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
</Link>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<View style={[{minHeight: PFP_SIZE}, a.justify_center]}>
|
<View style={[{minHeight: PFP_SIZE}, a.justify_center]}>
|
||||||
<Layout.Header.Slot>
|
<Layout.Header.Slot>{settings}</Layout.Header.Slot>
|
||||||
{isConvoActive(convoState) ? (
|
|
||||||
isGroupChat ? (
|
|
||||||
<Button
|
|
||||||
label={l`Open group chat settings`}
|
|
||||||
size="small"
|
|
||||||
color="secondary"
|
|
||||||
shape="round"
|
|
||||||
variant="ghost"
|
|
||||||
style={[a.bg_transparent]}
|
|
||||||
onPress={handleNavigateToSettings}>
|
|
||||||
<ButtonIcon icon={DotsHorizontalIcon} size="md" />
|
|
||||||
</Button>
|
|
||||||
) : (
|
|
||||||
<ConvoMenu
|
|
||||||
convo={convoState.convo}
|
|
||||||
profile={profile}
|
|
||||||
currentScreen="conversation"
|
|
||||||
blockInfo={blockInfo}
|
|
||||||
latestReportableMessage={latestReportableMessage}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
) : null}
|
|
||||||
</Layout.Header.Slot>
|
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function MuteStatus({muted}: {muted: boolean}) {
|
||||||
|
const t = useTheme()
|
||||||
|
|
||||||
|
return muted ? (
|
||||||
|
<>
|
||||||
|
<Text style={[a.text_md, t.atoms.text_contrast_medium]}> · </Text>
|
||||||
|
<BellOffIcon size="sm" style={t.atoms.text_contrast_medium} />
|
||||||
|
</>
|
||||||
|
) : undefined
|
||||||
|
}
|
||||||
|
|||||||
@@ -173,16 +173,12 @@ function Inner() {
|
|||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
<View style={[a.flex_1]}>
|
<View style={[a.flex_1]}>
|
||||||
{moderation && recipient ? (
|
<InnerReady
|
||||||
<InnerReady
|
moderation={moderation}
|
||||||
moderation={moderation}
|
recipient={recipient}
|
||||||
recipient={recipient}
|
hasScrolled={hasScrolled}
|
||||||
hasScrolled={hasScrolled}
|
setHasScrolled={setHasScrolled}
|
||||||
setHasScrolled={setHasScrolled}
|
/>
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<View style={[a.align_center, a.gap_sm, a.flex_1]} />
|
|
||||||
)}
|
|
||||||
{!readyToShow && (
|
{!readyToShow && (
|
||||||
<View
|
<View
|
||||||
style={[
|
style={[
|
||||||
@@ -210,8 +206,8 @@ function InnerReady({
|
|||||||
hasScrolled,
|
hasScrolled,
|
||||||
setHasScrolled,
|
setHasScrolled,
|
||||||
}: {
|
}: {
|
||||||
moderation: ModerationDecision
|
moderation: ModerationDecision | null
|
||||||
recipient: Shadow<AppBskyActorDefs.ProfileViewDetailed>
|
recipient: Shadow<AppBskyActorDefs.ProfileViewDetailed> | undefined
|
||||||
hasScrolled: boolean
|
hasScrolled: boolean
|
||||||
setHasScrolled: React.Dispatch<React.SetStateAction<boolean>>
|
setHasScrolled: React.Dispatch<React.SetStateAction<boolean>>
|
||||||
}) {
|
}) {
|
||||||
@@ -289,12 +285,14 @@ function InnerReady({
|
|||||||
hasAcceptOverride={!!params.accept}
|
hasAcceptOverride={!!params.accept}
|
||||||
transparentHeaderHeight={IS_LIQUID_GLASS ? headerHeight : 0}
|
transparentHeaderHeight={IS_LIQUID_GLASS ? headerHeight : 0}
|
||||||
footer={
|
footer={
|
||||||
<MessagesListBlockedFooter
|
moderation && recipient ? (
|
||||||
recipient={recipient}
|
<MessagesListBlockedFooter
|
||||||
convoId={convoState.convo.id}
|
recipient={recipient}
|
||||||
hasMessages={convoState.items.length > 0}
|
convoId={convoState.convo.id}
|
||||||
moderation={moderation}
|
hasMessages={convoState.items.length > 0}
|
||||||
/>
|
moderation={moderation}
|
||||||
|
/>
|
||||||
|
) : null
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|||||||
Reference in New Issue
Block a user