Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[🐴] Blocks/mutes and revalidation #4020

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 61 additions & 9 deletions src/components/dms/ConvoMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ import {
} from '#/state/queries/messages/conversation'
import {useLeaveConvo} from '#/state/queries/messages/leave-conversation'
import {useMuteConvo} from '#/state/queries/messages/mute-conversation'
import {
useAwaitedProfileBlockMutation,
useAwaitedProfileUnblockMutation,
} from '#/state/queries/profile'
import * as Toast from '#/view/com/util/Toast'
import {atoms as a, useTheme} from '#/alf'
import {ArrowBoxLeft_Stroke2_Corner0_Rounded as ArrowBoxLeft} from '#/components/icons/ArrowBoxLeft'
Expand All @@ -34,10 +38,11 @@ let ConvoMenu = ({
showMarkAsRead,
hideTrigger,
triggerOpacity,
onUpdateConvo,
}: {
convo: ChatBskyConvoDefs.ConvoView
profile: AppBskyActorDefs.ProfileViewBasic
onUpdateConvo?: (convo: ChatBskyConvoDefs.ConvoView) => void
onUpdateConvo?: () => void
control?: Menu.MenuControlProps
currentScreen: 'list' | 'conversation'
showMarkAsRead?: boolean
Expand All @@ -49,6 +54,7 @@ let ConvoMenu = ({
const t = useTheme()
const leaveConvoControl = Prompt.usePromptControl()
const reportControl = Prompt.usePromptControl()
const blockedByListControl = Prompt.usePromptControl()
const {mutate: markAsRead} = useMarkAsReadMutation()

const {data: convo} = useConvoQuery(initialConvo)
Expand All @@ -64,12 +70,49 @@ let ConvoMenu = ({
} else {
Toast.show(_(msg`Chat unmuted`))
}
onUpdateConvo?.()
},
onError: () => {
Toast.show(_(msg`Could not mute chat`))
},
})

const {mutateAsync: blockProfile, isPending: isBlockPending} =
useAwaitedProfileBlockMutation()
const {mutateAsync: unblockProfile, isPending: isUnblockPending} =
useAwaitedProfileUnblockMutation()
const isBlockMutationPending = isBlockPending || isUnblockPending
const isBlocking = Boolean(profile.viewer?.blocking)
const isBlockedByList = Boolean(profile.viewer?.blockingByList)

const toggleBlock = useCallback(async () => {
if (isBlockMutationPending) return

if (isBlockedByList) {
blockedByListControl.open()
return
}

if (profile.viewer?.blocking) {
await unblockProfile({
subject: profile.did,
blockUri: profile.viewer?.blocking,
})
} else {
await blockProfile({subject: profile.did})
}

onUpdateConvo?.()
}, [
profile,
isBlockMutationPending,
blockProfile,
unblockProfile,
onUpdateConvo,
isBlockedByList,
blockedByListControl,
])

const {mutate: leaveConvo} = useLeaveConvo(convo?.id, {
onSuccess: () => {
if (currentScreen === 'conversation') {
Expand Down Expand Up @@ -146,18 +189,17 @@ let ConvoMenu = ({
</Menu.Item>
</Menu.Group>
<Menu.Divider />
{/* TODO(samuel): implement this */}
<Menu.Group>
<Menu.Item
label={_(msg`Block account`)}
onPress={() => {}}
disabled>
disabled={isBlockMutationPending}
label={
isBlocking ? _(msg`Unblock account`) : _(msg`Block account`)
}
onPress={toggleBlock}>
<Menu.ItemText>
<Trans>Block account</Trans>
{isBlocking ? _(msg`Unblock account`) : _(msg`Block account`)}
</Menu.ItemText>
<Menu.ItemIcon
icon={profile.viewer?.blocking ? PersonCheck : PersonX}
/>
<Menu.ItemIcon icon={isBlocking ? PersonX : PersonCheck} />
</Menu.Item>
<Menu.Item
label={_(msg`Report conversation`)}
Expand Down Expand Up @@ -202,6 +244,16 @@ let ConvoMenu = ({
confirmButtonCta={_(msg`I understand`)}
onConfirm={noop}
/>

<Prompt.Basic
control={blockedByListControl}
title={_(msg`Blocked by list`)}
description={_(
msg`This user is blocked by one of your moderation lists. You can unblock them from the moderation list settings.`,
)}
confirmButtonCta={_(msg`I understand`)}
onConfirm={noop}
/>
</>
)
}
Expand Down
1 change: 1 addition & 0 deletions src/screens/Messages/Conversation/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,7 @@ let Header = ({
convo={convoState.convo}
profile={profile}
currentScreen="conversation"
onUpdateConvo={convoState.revalidateConvo}
/>
) : (
<View style={{width: 30}} />
Expand Down
8 changes: 8 additions & 0 deletions src/screens/Messages/List/ChatListItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ import {ChatBskyConvoDefs} from '@atproto/api'
import {msg} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {useNavigation} from '@react-navigation/native'
import {useQueryClient} from '@tanstack/react-query'

import {NavigationProp} from '#/lib/routes/types'
import {isNative} from '#/platform/detection'
import {RQKEY as ListConvosQueryKey} from '#/state/queries/messages/list-converations'
import {useSession} from '#/state/session'
import {TimeElapsed} from '#/view/com/util/TimeElapsed'
import {UserAvatar} from '#/view/com/util/UserAvatar'
Expand Down Expand Up @@ -36,6 +38,7 @@ export function ChatListItem({
const displayName = isDeletedAccount
? 'Deleted Account'
: otherUser?.displayName || otherUser?.handle
const queryClient = useQueryClient()

let lastMessage = _(msg`No messages yet`)
let lastMessageSentAt: string | null = null
Expand Down Expand Up @@ -73,6 +76,10 @@ export function ChatListItem({
})
}, [convo.id, navigation])

const onUpdateConvo = React.useCallback(() => {
queryClient.invalidateQueries({queryKey: ListConvosQueryKey})
}, [queryClient])

if (!otherUser) {
return null
}
Expand Down Expand Up @@ -204,6 +211,7 @@ export function ChatListItem({
triggerOpacity={
!gtMobile || showActions || menuControl.isOpen ? 1 : 0
}
onUpdateConvo={onUpdateConvo}
/>
</View>
</View>
Expand Down
48 changes: 34 additions & 14 deletions src/state/messages/convo/agent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ export class Convo {
sender: AppBskyActorDefs.ProfileViewBasic | undefined
recipients: AppBskyActorDefs.ProfileViewBasic[] | undefined = undefined
snapshot: ConvoState | undefined
blocks: AppBskyActorDefs.ProfileViewBasic[] = []

constructor(params: ConvoParams) {
this.id = nanoid(3)
Expand All @@ -97,6 +98,7 @@ export class Convo {
this.sendMessage = this.sendMessage.bind(this)
this.deleteMessage = this.deleteMessage.bind(this)
this.fetchMessageHistory = this.fetchMessageHistory.bind(this)
this.revalidateConvo = this.revalidateConvo.bind(this)
this.ingestFirehose = this.ingestFirehose.bind(this)
this.onFirehoseConnect = this.onFirehoseConnect.bind(this)
this.onFirehoseError = this.onFirehoseError.bind(this)
Expand Down Expand Up @@ -144,10 +146,12 @@ export class Convo {
error: undefined,
sender: undefined,
recipients: undefined,
blocks: undefined,
isFetchingHistory: this.isFetchingHistory,
deleteMessage: undefined,
sendMessage: undefined,
fetchMessageHistory: undefined,
revalidateConvo: undefined,
}
}
case ConvoStatus.Suspended:
Expand All @@ -160,10 +164,12 @@ export class Convo {
error: undefined,
sender: this.sender!,
recipients: this.recipients!,
blocks: this.blocks,
isFetchingHistory: this.isFetchingHistory,
deleteMessage: this.deleteMessage,
sendMessage: this.sendMessage,
fetchMessageHistory: this.fetchMessageHistory,
revalidateConvo: this.revalidateConvo,
}
}
case ConvoStatus.Error: {
Expand All @@ -174,10 +180,12 @@ export class Convo {
error: this.error,
sender: undefined,
recipients: undefined,
blocks: undefined,
isFetchingHistory: false,
deleteMessage: undefined,
sendMessage: undefined,
fetchMessageHistory: undefined,
revalidateConvo: undefined,
}
}
default: {
Expand All @@ -188,10 +196,12 @@ export class Convo {
error: undefined,
sender: undefined,
recipients: undefined,
blocks: undefined,
isFetchingHistory: false,
deleteMessage: undefined,
sendMessage: undefined,
fetchMessageHistory: undefined,
revalidateConvo: undefined,
}
}
}
Expand Down Expand Up @@ -361,6 +371,7 @@ export class Convo {
this.convo = undefined
this.sender = undefined
this.recipients = undefined
this.blocks = []
this.snapshot = undefined

this.status = ConvoStatus.Uninitialized
Expand All @@ -380,11 +391,12 @@ export class Convo {

private async setup() {
try {
const {convo, sender, recipients} = await this.fetchConvo()
const {convo, sender, recipients, blocks} = await this.fetchConvo()

this.convo = convo
this.sender = sender
this.recipients = recipients
this.blocks = blocks

/*
* Some validation prior to `Ready` status
Expand Down Expand Up @@ -452,6 +464,7 @@ export class Convo {
convo: ChatBskyConvoDefs.ConvoView
sender: AppBskyActorDefs.ProfileViewBasic | undefined
recipients: AppBskyActorDefs.ProfileViewBasic[]
blocks: AppBskyActorDefs.ProfileViewBasic[]
}>
| undefined
async fetchConvo() {
Expand All @@ -461,6 +474,7 @@ export class Convo {
convo: ChatBskyConvoDefs.ConvoView
sender: AppBskyActorDefs.ProfileViewBasic | undefined
recipients: AppBskyActorDefs.ProfileViewBasic[]
blocks: AppBskyActorDefs.ProfileViewBasic[]
}>(async (resolve, reject) => {
try {
const response = await networkRetry(2, () => {
Expand All @@ -478,6 +492,14 @@ export class Convo {
convo,
sender: convo.members.find(m => m.did === this.senderUserDid),
recipients: convo.members.filter(m => m.did !== this.senderUserDid),
blocks:
convo.members.filter(m =>
Boolean(
m.viewer?.blocking ||
m.viewer?.blockingByList ||
m.viewer?.blockedBy,
),
) || [],
})
} catch (e) {
reject(e)
Expand All @@ -489,29 +511,27 @@ export class Convo {
return this.pendingFetchConvo
}

async refreshConvo() {
private async refreshConvo({commitResult = false} = {}) {
try {
const {convo, sender, recipients} = await this.fetchConvo()
const {convo, sender, recipients, blocks} = await this.fetchConvo()
// throw new Error('UNCOMMENT TO TEST REFRESH FAILURE')
this.convo = convo || this.convo
this.sender = sender || this.sender
this.recipients = recipients || this.recipients
this.blocks = blocks || this.blocks

if (commitResult) {
this.commit()
}
} catch (e: any) {
logger.error(e, {context: `Convo: failed to refresh convo`})

this.footerItems.set(ConvoItemError.Network, {
type: 'error-recoverable',
key: ConvoItemError.Network,
code: ConvoItemError.Network,
retry: () => {
this.footerItems.delete(ConvoItemError.Network)
this.resume()
},
})
this.commit()
}
}

async revalidateConvo() {
this.refreshConvo({commitResult: true})
}

async fetchMessageHistory() {
logger.debug('Convo: fetch message history', {}, logger.DebugContext.convo)

Expand Down
Loading
Loading