Support soft-deleted statuses via tombstones
This commit is contained in:
parent
e9fee8aad3
commit
4a2b7faa59
8 changed files with 56 additions and 18 deletions
|
@ -21,6 +21,7 @@ import StatusMedia from './status-media';
|
|||
import StatusReplyMentions from './status-reply-mentions';
|
||||
import SensitiveContentOverlay from './statuses/sensitive-content-overlay';
|
||||
import StatusInfo from './statuses/status-info';
|
||||
import Tombstone from './tombstone';
|
||||
import { Card, Icon, Stack, Text } from './ui';
|
||||
|
||||
import type {
|
||||
|
@ -388,6 +389,17 @@ const Status: React.FC<IStatus> = (props) => {
|
|||
|
||||
const isUnderReview = actualStatus.visibility === 'self';
|
||||
const isSensitive = actualStatus.hidden;
|
||||
const isSoftDeleted = status.tombstone?.reason === 'deleted';
|
||||
|
||||
if (isSoftDeleted) {
|
||||
return (
|
||||
<Tombstone
|
||||
id={status.id}
|
||||
onMoveUp={(id) => onMoveUp ? onMoveUp(id) : null}
|
||||
onMoveDown={(id) => onMoveDown ? onMoveDown(id) : null}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<HotKeys handlers={handlers} data-testid='status'>
|
||||
|
|
|
@ -19,10 +19,17 @@ const Tombstone: React.FC<ITombstone> = ({ id, onMoveUp, onMoveDown }) => {
|
|||
|
||||
return (
|
||||
<HotKeys handlers={handlers}>
|
||||
<div className='focusable flex items-center justify-center border border-solid border-gray-200 bg-gray-100 p-9 dark:border-gray-800 dark:bg-gray-900 sm:rounded-xl' tabIndex={0}>
|
||||
<Text>
|
||||
<FormattedMessage id='statuses.tombstone' defaultMessage='One or more posts are unavailable.' />
|
||||
</Text>
|
||||
<div className='h-16'>
|
||||
<div
|
||||
className='focusable flex h-[42px] items-center justify-center rounded-lg border-2 border-gray-200 text-center'
|
||||
>
|
||||
<Text theme='muted'>
|
||||
<FormattedMessage
|
||||
id='statuses.tombstone'
|
||||
defaultMessage='One or more posts are unavailable.'
|
||||
/>
|
||||
</Text>
|
||||
</div>
|
||||
</div>
|
||||
</HotKeys>
|
||||
);
|
||||
|
|
|
@ -31,9 +31,8 @@ const ThreadStatus: React.FC<IThreadStatus> = (props): JSX.Element => {
|
|||
|
||||
return (
|
||||
<div
|
||||
className={clsx('thread__connector', {
|
||||
'thread__connector--top': isConnectedTop,
|
||||
'thread__connector--bottom': isConnectedBottom,
|
||||
className={clsx('absolute left-5 z-[1] hidden w-0.5 bg-gray-200 rtl:left-auto rtl:right-5 dark:bg-primary-800', {
|
||||
'!block top-[calc(12px+42px)] h-[calc(100%-42px-8px-1rem)]': isConnectedBottom,
|
||||
})}
|
||||
/>
|
||||
);
|
||||
|
|
|
@ -13,7 +13,7 @@ import {
|
|||
import { normalizeAttachment } from 'soapbox/normalizers/attachment';
|
||||
import { normalizeEmoji } from 'soapbox/normalizers/emoji';
|
||||
import { normalizeMention } from 'soapbox/normalizers/mention';
|
||||
import { cardSchema, pollSchema } from 'soapbox/schemas';
|
||||
import { cardSchema, pollSchema, tombstoneSchema } from 'soapbox/schemas';
|
||||
|
||||
import type { ReducerAccount } from 'soapbox/reducers/accounts';
|
||||
import type { Account, Attachment, Card, Emoji, Group, Mention, Poll, EmbeddedEntity } from 'soapbox/types/entities';
|
||||
|
@ -36,6 +36,10 @@ export const EventRecord = ImmutableRecord({
|
|||
links: ImmutableList<Attachment>(),
|
||||
});
|
||||
|
||||
interface Tombstone {
|
||||
reason: 'deleted'
|
||||
}
|
||||
|
||||
// https://docs.joinmastodon.org/entities/status/
|
||||
export const StatusRecord = ImmutableRecord({
|
||||
account: null as EmbeddedEntity<Account | ReducerAccount>,
|
||||
|
@ -72,6 +76,7 @@ export const StatusRecord = ImmutableRecord({
|
|||
sensitive: false,
|
||||
spoiler_text: '',
|
||||
tags: ImmutableList<ImmutableMap<string, any>>(),
|
||||
tombstone: null as Tombstone | null,
|
||||
uri: '',
|
||||
url: '',
|
||||
visibility: 'public' as StatusVisibility,
|
||||
|
@ -116,6 +121,15 @@ const normalizeStatusPoll = (status: ImmutableMap<string, any>) => {
|
|||
}
|
||||
};
|
||||
|
||||
const normalizeTombstone = (status: ImmutableMap<string, any>) => {
|
||||
try {
|
||||
const tombstone = tombstoneSchema.parse(status.get('tombstone').toJS());
|
||||
return status.set('tombstone', tombstone);
|
||||
} catch (_e) {
|
||||
return status.set('tombstone', null);
|
||||
}
|
||||
};
|
||||
|
||||
// Normalize card
|
||||
const normalizeStatusCard = (status: ImmutableMap<string, any>) => {
|
||||
try {
|
||||
|
@ -246,6 +260,7 @@ export const normalizeStatus = (status: Record<string, any>) => {
|
|||
fixContent(status);
|
||||
normalizeFilterResults(status);
|
||||
normalizeDislikes(status);
|
||||
normalizeTombstone(status);
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
|
|
@ -14,6 +14,7 @@ export { pollSchema, type Poll, type PollOption } from './poll';
|
|||
export { relationshipSchema, type Relationship } from './relationship';
|
||||
export { statusSchema, type Status } from './status';
|
||||
export { tagSchema, type Tag } from './tag';
|
||||
export { tombstoneSchema, type Tombstone } from './tombstone';
|
||||
|
||||
// Soapbox
|
||||
export { adSchema, type Ad } from './soapbox/ad';
|
|
@ -10,6 +10,10 @@ import { pollSchema } from './poll';
|
|||
import { tagSchema } from './tag';
|
||||
import { contentSchema, dateSchema, filteredArray } from './utils';
|
||||
|
||||
const tombstoneSchema = z.object({
|
||||
reason: z.enum(['deleted']),
|
||||
});
|
||||
|
||||
const baseStatusSchema = z.object({
|
||||
account: accountSchema,
|
||||
application: z.object({
|
||||
|
@ -46,6 +50,7 @@ const baseStatusSchema = z.object({
|
|||
sensitive: z.coerce.boolean(),
|
||||
spoiler_text: contentSchema,
|
||||
tags: filteredArray(tagSchema),
|
||||
tombstone: tombstoneSchema.nullable().optional(),
|
||||
uri: z.string().url().catch(''),
|
||||
url: z.string().url().catch(''),
|
||||
visibility: z.string().catch('public'),
|
||||
|
|
9
app/soapbox/schemas/tombstone.ts
Normal file
9
app/soapbox/schemas/tombstone.ts
Normal file
|
@ -0,0 +1,9 @@
|
|||
import { z } from 'zod';
|
||||
|
||||
const tombstoneSchema = z.object({
|
||||
reason: z.enum(['deleted']),
|
||||
});
|
||||
|
||||
type Tombstone = z.infer<typeof tombstoneSchema>;
|
||||
|
||||
export { tombstoneSchema, type Tombstone };
|
|
@ -12,14 +12,4 @@
|
|||
.status__content-wrapper {
|
||||
@apply pl-[calc(42px+12px)] rtl:pl-0 rtl:pr-[calc(42px+12px)];
|
||||
}
|
||||
|
||||
&__connector {
|
||||
@apply bg-gray-200 dark:bg-primary-800 absolute w-0.5 left-5 hidden z-[1] rtl:right-5 rtl:left-auto;
|
||||
|
||||
&--bottom {
|
||||
@apply block;
|
||||
height: calc(100% - 42px - 8px - 1rem);
|
||||
top: calc(12px + 42px);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue