[Chat] Handle moderation lock override in group chats (#10835)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
+1
-1
@@ -93,7 +93,7 @@
|
||||
"prettier": "prettier --check ."
|
||||
},
|
||||
"dependencies": {
|
||||
"@atproto/api": "0.20.11",
|
||||
"@atproto/api": "0.20.12",
|
||||
"@atproto/syntax": "0.6.1",
|
||||
"@bitdrift/react-native": "^0.6.8",
|
||||
"@braintree/sanitize-url": "^6.0.2",
|
||||
|
||||
Generated
+5
-5
@@ -242,8 +242,8 @@ importers:
|
||||
.:
|
||||
dependencies:
|
||||
'@atproto/api':
|
||||
specifier: 0.20.11
|
||||
version: 0.20.11
|
||||
specifier: 0.20.12
|
||||
version: 0.20.12
|
||||
'@atproto/syntax':
|
||||
specifier: 0.6.1
|
||||
version: 0.6.1
|
||||
@@ -877,8 +877,8 @@ packages:
|
||||
graphql:
|
||||
optional: true
|
||||
|
||||
'@atproto/api@0.20.11':
|
||||
resolution: {integrity: sha512-1NoVJpBDAdotxo1iMZdMd75JstpdKWgBYOnxfVD4m+52bRjgU4cFg3EOGNUognZntFgL/bIHyEgyN7SJWVf6Ig==}
|
||||
'@atproto/api@0.20.12':
|
||||
resolution: {integrity: sha512-pNCrl/BSmkjlrVu0W5A9zkOVRIWdWAdajuHX/EyhDZFxu8HExsiQi6j46H5kv85GrNRXS/QtULE+ocSuMtEJfw==}
|
||||
engines: {node: '>=22'}
|
||||
|
||||
'@atproto/common-web@0.5.0':
|
||||
@@ -9493,7 +9493,7 @@ snapshots:
|
||||
|
||||
'@0no-co/graphql.web@1.2.0': {}
|
||||
|
||||
'@atproto/api@0.20.11':
|
||||
'@atproto/api@0.20.12':
|
||||
dependencies:
|
||||
'@atproto/common-web': 0.5.0
|
||||
'@atproto/lexicon': 0.7.1
|
||||
|
||||
@@ -3,6 +3,7 @@ import {Pressable, View} from 'react-native'
|
||||
import {
|
||||
ChatBskyActorDefs,
|
||||
ChatBskyConvoDefs,
|
||||
ChatBskyConvoUnlockConvo,
|
||||
type ModerationOpts,
|
||||
} from '@atproto/api'
|
||||
import {Trans, useLingui} from '@lingui/react/macro'
|
||||
@@ -417,6 +418,12 @@ function SettingsHeader({
|
||||
if (lock) {
|
||||
logger.error('Failed to lock group chat', {message: e})
|
||||
Toast.show(l`Failed to lock group chat`, {type: 'error'})
|
||||
} else if (
|
||||
e instanceof ChatBskyConvoUnlockConvo.ConvoLockedByModerationError
|
||||
) {
|
||||
Toast.show(l`This chat is locked by a moderation action`, {
|
||||
type: 'error',
|
||||
})
|
||||
} else {
|
||||
logger.error('Failed to unlock group chat', {message: e})
|
||||
Toast.show(l`Failed to unlock group chat`, {type: 'error'})
|
||||
@@ -447,7 +454,11 @@ function SettingsHeader({
|
||||
|
||||
const createdAt = new Date(convo.details.createdAt)
|
||||
|
||||
const canLockGroupChat = isOwner && lockStatus !== 'locked-permanently'
|
||||
// A lock forced by a moderation action cannot be undone by the owner.
|
||||
const canLockGroupChat =
|
||||
isOwner &&
|
||||
lockStatus !== 'locked-permanently' &&
|
||||
!convo.details.lockStatusModerationOverride
|
||||
|
||||
const groupNameComponent = (
|
||||
<Text
|
||||
|
||||
@@ -31,6 +31,7 @@ export function ChatFooter({
|
||||
style={[
|
||||
a.flex_row,
|
||||
a.align_center,
|
||||
a.flex_1,
|
||||
{
|
||||
minHeight: 32,
|
||||
},
|
||||
@@ -40,7 +41,7 @@ export function ChatFooter({
|
||||
fill={t.atoms.text_contrast_medium.color}
|
||||
style={[a.mr_sm]}
|
||||
/>
|
||||
<View>
|
||||
<View style={[a.flex_1]}>
|
||||
<Text
|
||||
numberOfLines={1}
|
||||
style={[
|
||||
@@ -52,7 +53,7 @@ export function ChatFooter({
|
||||
</Text>
|
||||
{subheading ? (
|
||||
<Text
|
||||
numberOfLines={1}
|
||||
numberOfLines={2}
|
||||
style={[
|
||||
a.text_xs,
|
||||
a.leading_snug,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import {Pressable} from 'react-native'
|
||||
import {ChatBskyConvoUnlockConvo} from '@atproto/api'
|
||||
import {Trans, useLingui} from '@lingui/react/macro'
|
||||
import {StackActions, useNavigation} from '@react-navigation/native'
|
||||
|
||||
@@ -32,12 +33,20 @@ export function ChatLocked({
|
||||
|
||||
const primaryMember = convo?.primaryMember
|
||||
const isOwner = !!primaryMember && primaryMember.did === currentAccount?.did
|
||||
// The lock is forced by a moderation action, so the owner cannot undo it.
|
||||
const isModerationLock = convo.details.lockStatusModerationOverride
|
||||
|
||||
const {mutate: lockConvo} = useLockConvo(convo.view.id, {
|
||||
onSuccess: () => {
|
||||
Toast.show(l({message: 'Group chat unlocked', context: 'toast'}))
|
||||
},
|
||||
onError: e => {
|
||||
if (e instanceof ChatBskyConvoUnlockConvo.ConvoLockedByModerationError) {
|
||||
Toast.show(l`This chat is locked by a moderation action`, {
|
||||
type: 'error',
|
||||
})
|
||||
return
|
||||
}
|
||||
logger.error('Failed to unlock group chat', {message: e})
|
||||
Toast.show(l`Failed to unlock group chat`, {type: 'error'})
|
||||
},
|
||||
@@ -59,10 +68,14 @@ export function ChatLocked({
|
||||
return (
|
||||
<ChatFooter
|
||||
heading={l`This chat is locked`}
|
||||
subheading={l`No one can send messages`}
|
||||
subheading={
|
||||
isModerationLock
|
||||
? l`This group is locked by a moderation action on the owner`
|
||||
: l`No one can send messages`
|
||||
}
|
||||
icon={LockIcon}>
|
||||
{isOwner ? (
|
||||
<>
|
||||
isModerationLock ? null : (
|
||||
<Pressable
|
||||
accessibilityRole="button"
|
||||
hitSlop={HITSLOP_10}
|
||||
@@ -79,7 +92,7 @@ export function ChatLocked({
|
||||
<Trans>Unlock chat</Trans>
|
||||
</Text>
|
||||
</Pressable>
|
||||
</>
|
||||
)
|
||||
) : (
|
||||
<>
|
||||
<Pressable
|
||||
|
||||
@@ -1087,7 +1087,10 @@ export class Convo {
|
||||
this.commit()
|
||||
}
|
||||
|
||||
updateLockStatus(lockStatus: ChatBskyConvoDefs.ConvoLockStatus) {
|
||||
updateLockStatus(
|
||||
lockStatus: ChatBskyConvoDefs.ConvoLockStatus,
|
||||
lockStatusModerationOverride: boolean,
|
||||
) {
|
||||
if (this.convo?.kind !== 'group') {
|
||||
throw new Error('updateLockStatus can only be called on group convo')
|
||||
}
|
||||
@@ -1096,6 +1099,7 @@ export class Convo {
|
||||
kind: {
|
||||
...this.convo.details,
|
||||
lockStatus,
|
||||
lockStatusModerationOverride,
|
||||
},
|
||||
})
|
||||
|
||||
|
||||
@@ -151,8 +151,15 @@ export function ConvoProvider({
|
||||
if (data.kind.joinLink !== convo.convo.details.joinLink) {
|
||||
convo.updateJoinLink(data.kind.joinLink)
|
||||
}
|
||||
if (data.kind.lockStatus !== convo.convo.details.lockStatus) {
|
||||
convo.updateLockStatus(data.kind.lockStatus)
|
||||
if (
|
||||
data.kind.lockStatus !== convo.convo.details.lockStatus ||
|
||||
data.kind.lockStatusModerationOverride !==
|
||||
convo.convo.details.lockStatusModerationOverride
|
||||
) {
|
||||
convo.updateLockStatus(
|
||||
data.kind.lockStatus,
|
||||
data.kind.lockStatusModerationOverride,
|
||||
)
|
||||
}
|
||||
}
|
||||
if (
|
||||
|
||||
@@ -532,12 +532,22 @@ export function ListConvosProviderInner({
|
||||
}
|
||||
return {...convo, rev: log.rev}
|
||||
})
|
||||
// The log event doesn't say whether the lock is forced by a
|
||||
// moderation override, so refetch to pick up the flag.
|
||||
void queryClient.invalidateQueries({
|
||||
queryKey: CONVO_KEY(log.convoId),
|
||||
})
|
||||
} else if (ChatBskyConvoDefs.isLogUnlockConvo(log)) {
|
||||
mutateConvoView(log.convoId, convo => {
|
||||
if (ChatBskyConvoDefs.isGroupConvo(convo.kind)) {
|
||||
return {
|
||||
...convo,
|
||||
kind: {...convo.kind, lockStatus: 'unlocked'},
|
||||
kind: {
|
||||
...convo.kind,
|
||||
lockStatus: 'unlocked',
|
||||
// An unlocked convo cannot be moderation-locked.
|
||||
lockStatusModerationOverride: false,
|
||||
},
|
||||
rev: log.rev,
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user