mirror of
https://github.com/mastodon/mastodon.git
synced 2026-09-11 15:46:06 -05:00
Attachment stories (#39661)
This commit is contained in:
@@ -0,0 +1,190 @@
|
||||
import { List, Map } from 'immutable';
|
||||
|
||||
import type { Meta, StoryObj } from '@storybook/react-vite';
|
||||
|
||||
import {
|
||||
accountFactoryImmutable,
|
||||
cardFactoryAPI,
|
||||
collectionFactoryAPI,
|
||||
statusFactoryAPI,
|
||||
statusFactoryImmutable,
|
||||
statusQuotedFactoryAPI,
|
||||
} from '@/testing/factories';
|
||||
|
||||
import { StatusAttachments } from './attachments';
|
||||
import type { AttachmentArgs } from './testing';
|
||||
import { attachmentArgTypes, attachmentFactory } from './testing';
|
||||
|
||||
interface StatusAttachmentsStoryProps extends AttachmentArgs {
|
||||
isFiltered: boolean;
|
||||
isSensitive: boolean;
|
||||
isPictureInPicture: boolean;
|
||||
isQuote: boolean;
|
||||
}
|
||||
|
||||
const meta = {
|
||||
title: 'Components/Status/StatusAttachments',
|
||||
render() {
|
||||
return (
|
||||
<div style={{ width: 'min(600px, 80vw)' }}>
|
||||
<StatusAttachments statusId='1' contextType='home' />
|
||||
</div>
|
||||
);
|
||||
},
|
||||
args: {
|
||||
attachment1: 'image',
|
||||
attachment2: undefined,
|
||||
attachment3: undefined,
|
||||
isFiltered: false,
|
||||
isSensitive: false,
|
||||
isPictureInPicture: false,
|
||||
isQuote: false,
|
||||
},
|
||||
argTypes: {
|
||||
...attachmentArgTypes,
|
||||
isFiltered: {
|
||||
control: 'boolean',
|
||||
},
|
||||
isSensitive: {
|
||||
control: 'boolean',
|
||||
},
|
||||
isPictureInPicture: {
|
||||
control: 'boolean',
|
||||
},
|
||||
isQuote: {
|
||||
control: 'boolean',
|
||||
},
|
||||
},
|
||||
parameters: {
|
||||
state: {
|
||||
filters: {
|
||||
'1': Map({
|
||||
id: '1',
|
||||
title: 'Test',
|
||||
context: List(['home']),
|
||||
expires_at: null,
|
||||
filter_action: 'blur',
|
||||
}),
|
||||
},
|
||||
accounts: {
|
||||
'1': accountFactoryImmutable(),
|
||||
},
|
||||
server: {
|
||||
translationLanguages: {
|
||||
item: {
|
||||
xx: ['en', 'de', 'fr'],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
stateFn(args: StatusAttachmentsStoryProps) {
|
||||
const status = statusFactoryAPI();
|
||||
|
||||
if (args.isFiltered) {
|
||||
status.filtered = [
|
||||
{
|
||||
filter: {
|
||||
id: '1',
|
||||
title: 'Test',
|
||||
context: 'home',
|
||||
expires_at: '',
|
||||
filter_action: 'warn',
|
||||
},
|
||||
keyword_matches: [],
|
||||
status_matches: [],
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
if (args.isSensitive) {
|
||||
status.sensitive = true;
|
||||
}
|
||||
|
||||
if (args.isQuote) {
|
||||
status.quote = {
|
||||
state: 'accepted',
|
||||
quoted_status: statusQuotedFactoryAPI({ id: '2' }),
|
||||
};
|
||||
}
|
||||
|
||||
if (args.attachment1 === 'card') {
|
||||
status.card = cardFactoryAPI({
|
||||
image:
|
||||
'https://images.pexels.com/photos/16859306/pexels-photo-16859306.jpeg',
|
||||
});
|
||||
} else if (args.attachment1 === 'collection') {
|
||||
status.tagged_collections = [collectionFactoryAPI()];
|
||||
} else if (args.attachment1) {
|
||||
status.media_attachments.push(
|
||||
...attachmentFactory(
|
||||
args.attachment1,
|
||||
args.attachment2,
|
||||
args.attachment3,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
statuses: {
|
||||
'1': statusFactoryImmutable(status),
|
||||
},
|
||||
picture_in_picture: args.isPictureInPicture
|
||||
? {
|
||||
statusId: '1',
|
||||
accountId: '1',
|
||||
type: args.attachment1 === 'audio' ? 'audio' : 'video',
|
||||
}
|
||||
: undefined,
|
||||
};
|
||||
},
|
||||
},
|
||||
} as Meta<StatusAttachmentsStoryProps>;
|
||||
|
||||
export default meta;
|
||||
|
||||
type Story = StoryObj<typeof meta>;
|
||||
|
||||
export const Image: Story = {};
|
||||
|
||||
export const Video: Story = {
|
||||
args: {
|
||||
attachment1: 'video',
|
||||
},
|
||||
};
|
||||
|
||||
export const Audio: Story = {
|
||||
args: {
|
||||
attachment1: 'audio',
|
||||
},
|
||||
};
|
||||
|
||||
export const Card: Story = {
|
||||
args: {
|
||||
attachment1: 'card',
|
||||
},
|
||||
};
|
||||
|
||||
export const Collection: Story = {
|
||||
args: {
|
||||
attachment1: 'collection',
|
||||
},
|
||||
};
|
||||
|
||||
export const Sensitive: Story = {
|
||||
args: {
|
||||
isSensitive: true,
|
||||
},
|
||||
};
|
||||
|
||||
export const Filtered: Story = {
|
||||
args: {
|
||||
isFiltered: true,
|
||||
},
|
||||
};
|
||||
|
||||
export const PictureInPicture: Story = {
|
||||
args: {
|
||||
attachment1: 'video',
|
||||
isPictureInPicture: true,
|
||||
},
|
||||
};
|
||||
@@ -120,13 +120,12 @@ const MediaAttachments: React.FC<{
|
||||
const description =
|
||||
attachment.translation?.description ?? attachment.description;
|
||||
|
||||
const immutableAttachments = useAppSelector(
|
||||
(state) =>
|
||||
state.statuses.getIn(
|
||||
statusId,
|
||||
'media_attachments',
|
||||
) as Immutable.List<MediaAttachment>,
|
||||
);
|
||||
const immutableAttachments = useAppSelector((state) => {
|
||||
return state.statuses.getIn([
|
||||
statusId,
|
||||
'media_attachments',
|
||||
]) as Immutable.List<MediaAttachment>;
|
||||
});
|
||||
const mediaFilters = useAppSelector((state) =>
|
||||
selectMediaMatchFilters(state, { statusId, contextType }),
|
||||
);
|
||||
|
||||
@@ -6,16 +6,16 @@ import { Map as ImmutableMap } from 'immutable';
|
||||
import type { Meta, StoryObj } from '@storybook/react-vite';
|
||||
import { fn } from 'storybook/test';
|
||||
|
||||
import type { ApiMediaAttachmentJSON } from '@/mastodon/api_types/media_attachments';
|
||||
import type { StatusVisibility } from '@/mastodon/api_types/statuses';
|
||||
import {
|
||||
accountFactoryImmutable,
|
||||
mediaAttachmentFactoryAPI,
|
||||
pollFactoryImmutable,
|
||||
statusFactoryAPI,
|
||||
statusFactoryImmutable,
|
||||
} from '@/testing/factories';
|
||||
|
||||
import type { AttachmentArgs } from './testing';
|
||||
import { attachmentArgTypes, attachmentFactory } from './testing';
|
||||
import { TypedStatus } from './types';
|
||||
|
||||
type ContextTypes =
|
||||
@@ -29,16 +29,7 @@ type ContextTypes =
|
||||
| 'search'
|
||||
| 'thread';
|
||||
|
||||
type AttachmentTypes =
|
||||
| 'image-1'
|
||||
| 'image-2'
|
||||
| 'image-3'
|
||||
| 'video'
|
||||
| 'audio'
|
||||
| 'gifv'
|
||||
| 'unknown';
|
||||
|
||||
interface StatusStoryProps {
|
||||
interface StatusStoryProps extends AttachmentArgs {
|
||||
// Contents
|
||||
text: string;
|
||||
visibility: StatusVisibility;
|
||||
@@ -46,7 +37,6 @@ interface StatusStoryProps {
|
||||
isReply?: boolean;
|
||||
isPoll?: boolean;
|
||||
isQuote?: boolean;
|
||||
attachments?: AttachmentTypes;
|
||||
contentWarning?: string;
|
||||
|
||||
// Interactions
|
||||
@@ -84,7 +74,9 @@ const StatusStoryComponent: FC<StatusStoryProps> = (props) => {
|
||||
isReply,
|
||||
isPoll,
|
||||
isQuote,
|
||||
attachments,
|
||||
attachment1,
|
||||
attachment2,
|
||||
attachment3,
|
||||
contentWarning,
|
||||
|
||||
hasFavourited,
|
||||
@@ -108,107 +100,17 @@ const StatusStoryComponent: FC<StatusStoryProps> = (props) => {
|
||||
const { account, status } = useMemo(() => {
|
||||
const account = accountFactoryImmutable();
|
||||
|
||||
const media_attachments: ApiMediaAttachmentJSON[] = [];
|
||||
switch (attachments) {
|
||||
// Use fall through add attachments depending on count.
|
||||
case 'image-3':
|
||||
media_attachments.push(
|
||||
mediaAttachmentFactoryAPI({
|
||||
id: '2',
|
||||
url: 'https://cataas.com/cat/EbVq9zMc4Xxv7s73',
|
||||
meta: {
|
||||
original: {
|
||||
width: 960,
|
||||
height: 1280,
|
||||
size: '960x1280',
|
||||
aspect: 0.75,
|
||||
},
|
||||
},
|
||||
}),
|
||||
);
|
||||
// eslint-disable-next-line no-fallthrough
|
||||
case 'image-2':
|
||||
media_attachments.push(
|
||||
mediaAttachmentFactoryAPI({
|
||||
id: '3',
|
||||
url: 'https://cataas.com/cat/YFaQ4xWYoWURSz37',
|
||||
meta: {
|
||||
original: {
|
||||
width: 964,
|
||||
height: 1280,
|
||||
size: '964x1280',
|
||||
aspect: 0.753125,
|
||||
},
|
||||
},
|
||||
}),
|
||||
);
|
||||
// eslint-disable-next-line no-fallthrough
|
||||
case 'image-1':
|
||||
media_attachments.push(
|
||||
mediaAttachmentFactoryAPI({
|
||||
id: '4',
|
||||
url: 'https://cataas.com/cat/bYBTjiFUqjUPIBUD',
|
||||
meta: {
|
||||
original: {
|
||||
width: 1280,
|
||||
height: 964,
|
||||
size: '1280x964',
|
||||
aspect: 1.32780083,
|
||||
},
|
||||
},
|
||||
}),
|
||||
);
|
||||
break;
|
||||
case 'video':
|
||||
media_attachments.push(
|
||||
mediaAttachmentFactoryAPI({
|
||||
type: 'video',
|
||||
url: 'https://www.pexels.com/download/video/11760787/',
|
||||
meta: {
|
||||
original: {
|
||||
width: 2160,
|
||||
height: 4096,
|
||||
},
|
||||
},
|
||||
}),
|
||||
);
|
||||
break;
|
||||
case 'audio':
|
||||
media_attachments.push(
|
||||
mediaAttachmentFactoryAPI({
|
||||
type: 'audio',
|
||||
url: 'https://upload.wikimedia.org/wikipedia/commons/4/40/Elephant_voice_-_trumpeting.ogg',
|
||||
}),
|
||||
);
|
||||
break;
|
||||
case 'gifv':
|
||||
media_attachments.push(
|
||||
mediaAttachmentFactoryAPI({
|
||||
type: 'gifv',
|
||||
url: 'https://www.pexels.com/download/video/11760787/',
|
||||
meta: {
|
||||
original: {
|
||||
width: 2160,
|
||||
height: 4096,
|
||||
},
|
||||
},
|
||||
}),
|
||||
);
|
||||
break;
|
||||
case 'unknown':
|
||||
media_attachments.push(
|
||||
mediaAttachmentFactoryAPI({ type: attachments }),
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
return {
|
||||
account,
|
||||
status: statusFactoryImmutable({
|
||||
text,
|
||||
spoiler_text: contentWarning,
|
||||
visibility,
|
||||
media_attachments,
|
||||
media_attachments: attachmentFactory(
|
||||
attachment1,
|
||||
attachment2,
|
||||
attachment3,
|
||||
),
|
||||
reblogged: hasReblogged,
|
||||
favourited: hasFavourited,
|
||||
bookmarked: hasBookmarked,
|
||||
@@ -247,10 +149,12 @@ const StatusStoryComponent: FC<StatusStoryProps> = (props) => {
|
||||
}),
|
||||
};
|
||||
}, [
|
||||
attachments,
|
||||
text,
|
||||
contentWarning,
|
||||
visibility,
|
||||
attachment1,
|
||||
attachment2,
|
||||
attachment3,
|
||||
hasReblogged,
|
||||
hasFavourited,
|
||||
hasBookmarked,
|
||||
@@ -367,27 +271,17 @@ const meta = {
|
||||
isPoll: categoryContents,
|
||||
isQuote: categoryContents,
|
||||
text: categoryContents,
|
||||
attachments: {
|
||||
attachment1: {
|
||||
...categoryContents,
|
||||
control: 'select',
|
||||
options: [
|
||||
'One image',
|
||||
'Two images',
|
||||
'Three images',
|
||||
'Video',
|
||||
'Audio',
|
||||
'GIF',
|
||||
'Other',
|
||||
],
|
||||
mapping: {
|
||||
'One image': 'image-1',
|
||||
'Two images': 'image-2',
|
||||
'Three images': 'image-3',
|
||||
Video: 'video',
|
||||
Audio: 'audio',
|
||||
GIF: 'gifv',
|
||||
Other: 'unknown',
|
||||
} satisfies Record<string, AttachmentTypes>,
|
||||
...attachmentArgTypes.attachment1,
|
||||
},
|
||||
attachment2: {
|
||||
...categoryContents,
|
||||
...attachmentArgTypes.attachment2,
|
||||
},
|
||||
attachment3: {
|
||||
...categoryContents,
|
||||
...attachmentArgTypes.attachment3,
|
||||
},
|
||||
contentWarning: categoryContents,
|
||||
|
||||
@@ -445,7 +339,9 @@ const meta = {
|
||||
isPoll: false,
|
||||
isQuote: false,
|
||||
contentWarning: '',
|
||||
attachments: undefined,
|
||||
attachment1: undefined,
|
||||
attachment2: undefined,
|
||||
attachment3: undefined,
|
||||
|
||||
hasFavourited: false,
|
||||
hasReblogged: false,
|
||||
@@ -518,19 +414,21 @@ export const LongText: Story = {
|
||||
|
||||
export const Images: Story = {
|
||||
args: {
|
||||
attachments: 'image-3',
|
||||
attachment1: 'image',
|
||||
attachment2: 'image',
|
||||
attachment3: 'image',
|
||||
},
|
||||
};
|
||||
|
||||
export const Video: Story = {
|
||||
args: {
|
||||
attachments: 'video',
|
||||
attachment1: 'video',
|
||||
},
|
||||
};
|
||||
|
||||
export const Audio: Story = {
|
||||
args: {
|
||||
attachments: 'audio',
|
||||
attachment1: 'audio',
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
163
app/javascript/mastodon/components/status/testing.ts
Normal file
163
app/javascript/mastodon/components/status/testing.ts
Normal file
@@ -0,0 +1,163 @@
|
||||
import type { ArgTypes } from '@storybook/react-vite';
|
||||
|
||||
import type { MediaAttachmentType } from '@/mastodon/api_types/media_attachments';
|
||||
import { mediaAttachmentFactoryAPI } from '@/testing/factories';
|
||||
|
||||
export type MainAttachmentType = MediaAttachmentType | 'collection' | 'card';
|
||||
export type ExtraAttachmentType = Exclude<
|
||||
MediaAttachmentType,
|
||||
'video' | 'audio'
|
||||
>;
|
||||
|
||||
export interface AttachmentArgs {
|
||||
attachment1?: MainAttachmentType;
|
||||
attachment2?: ExtraAttachmentType;
|
||||
attachment3?: ExtraAttachmentType;
|
||||
}
|
||||
|
||||
export const attachmentArgTypes = {
|
||||
attachment1: {
|
||||
control: 'select',
|
||||
options: [
|
||||
'image',
|
||||
'gifv',
|
||||
'video',
|
||||
'unknown',
|
||||
'audio',
|
||||
'collection',
|
||||
'card',
|
||||
] satisfies MainAttachmentType[],
|
||||
},
|
||||
attachment2: {
|
||||
control: 'select',
|
||||
options: ['image', 'gifv', 'unknown'] satisfies ExtraAttachmentType[],
|
||||
},
|
||||
attachment3: {
|
||||
control: 'select',
|
||||
options: ['image', 'gifv', 'unknown'] satisfies ExtraAttachmentType[],
|
||||
},
|
||||
} as const satisfies ArgTypes<AttachmentArgs>;
|
||||
|
||||
export function attachmentFactory(
|
||||
attachment1?: MainAttachmentType,
|
||||
attachment2?: ExtraAttachmentType,
|
||||
attachment3?: ExtraAttachmentType,
|
||||
) {
|
||||
if (!attachment1 || attachment1 === 'card' || attachment1 === 'collection') {
|
||||
return [];
|
||||
}
|
||||
|
||||
const attachments = [mainMediaAttachmentFactory(attachment1)];
|
||||
if (attachment2) {
|
||||
attachments.push(extraAttachmentFactory(attachment2, '2'));
|
||||
}
|
||||
if (attachment3) {
|
||||
attachments.push(extraAttachmentFactory(attachment3, '3'));
|
||||
}
|
||||
return attachments;
|
||||
}
|
||||
|
||||
export function mainMediaAttachmentFactory(type: MediaAttachmentType) {
|
||||
if (type === 'video') {
|
||||
return mediaAttachmentFactoryAPI({
|
||||
type: 'video',
|
||||
url: 'https://www.pexels.com/download/video/11760787/',
|
||||
preview_url:
|
||||
'https://images.pexels.com/photos/16859306/pexels-photo-16859306.jpeg',
|
||||
meta: {
|
||||
original: {
|
||||
width: 2160,
|
||||
height: 4096,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (type === 'audio') {
|
||||
return mediaAttachmentFactoryAPI({
|
||||
type: 'audio',
|
||||
url: 'https://upload.wikimedia.org/wikipedia/commons/4/40/Elephant_voice_-_trumpeting.ogg',
|
||||
preview_url:
|
||||
'https://images.pexels.com/photos/16859306/pexels-photo-16859306.jpeg',
|
||||
});
|
||||
}
|
||||
|
||||
return extraAttachmentFactory(type, '1');
|
||||
}
|
||||
|
||||
type MediaData = Record<
|
||||
'1' | '2' | '3',
|
||||
{ url: string; width: number; height: number }
|
||||
>;
|
||||
|
||||
const imageIdToData = {
|
||||
'1': {
|
||||
url: 'https://cataas.com/cat/bYBTjiFUqjUPIBUD',
|
||||
width: 1280,
|
||||
height: 964,
|
||||
},
|
||||
'2': {
|
||||
url: 'https://cataas.com/cat/YFaQ4xWYoWURSz37',
|
||||
width: 964,
|
||||
height: 1280,
|
||||
},
|
||||
'3': {
|
||||
url: 'https://cataas.com/cat/EbVq9zMc4Xxv7s73',
|
||||
width: 960,
|
||||
height: 1280,
|
||||
},
|
||||
} satisfies MediaData;
|
||||
const gifIdToData = {
|
||||
'1': {
|
||||
url: 'https://www.pexels.com/download/video/11760787/',
|
||||
width: 2160,
|
||||
height: 4096,
|
||||
},
|
||||
'2': {
|
||||
url: 'https://www.pexels.com/download/video/19787248/',
|
||||
width: 3840,
|
||||
height: 2160,
|
||||
},
|
||||
'3': {
|
||||
url: 'https://www.pexels.com/download/video/37411294/',
|
||||
width: 3840,
|
||||
height: 2160,
|
||||
},
|
||||
} satisfies MediaData;
|
||||
|
||||
export function extraAttachmentFactory(
|
||||
type: ExtraAttachmentType,
|
||||
id: '1' | '2' | '3',
|
||||
) {
|
||||
const metaData = type === 'image' ? imageIdToData[id] : gifIdToData[id];
|
||||
switch (type) {
|
||||
case 'image':
|
||||
return mediaAttachmentFactoryAPI({
|
||||
id,
|
||||
type: 'image',
|
||||
url: metaData.url,
|
||||
meta: {
|
||||
original: {
|
||||
width: metaData.width,
|
||||
height: metaData.height,
|
||||
size: `${metaData.width}x${metaData.height}`,
|
||||
aspect: metaData.width / metaData.height,
|
||||
},
|
||||
},
|
||||
});
|
||||
case 'gifv':
|
||||
return mediaAttachmentFactoryAPI({
|
||||
id,
|
||||
type: 'gifv',
|
||||
url: metaData.url,
|
||||
meta: {
|
||||
original: {
|
||||
width: metaData.width,
|
||||
height: metaData.height,
|
||||
},
|
||||
},
|
||||
});
|
||||
case 'unknown':
|
||||
return mediaAttachmentFactoryAPI({ id, type: 'unknown' });
|
||||
}
|
||||
}
|
||||
@@ -30,7 +30,7 @@ export const selectAccountStatus = createAppSelector(
|
||||
[
|
||||
selectPlainStatus,
|
||||
(state, statusId: string) => {
|
||||
const accountId = state.statuses.getIn(statusId, 'account');
|
||||
const accountId = state.statuses.getIn([statusId, 'account']);
|
||||
if (typeof accountId !== 'string') {
|
||||
return null;
|
||||
}
|
||||
@@ -52,7 +52,7 @@ export const selectExpandedStatus = createAppSelector(
|
||||
[
|
||||
selectAccountStatus,
|
||||
(state, statusId: string) => {
|
||||
const reblogId = state.statuses.getIn(statusId, 'reblog');
|
||||
const reblogId = state.statuses.getIn([statusId, 'reblog']);
|
||||
if (typeof reblogId !== 'string') {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ import { fromJS } from 'immutable';
|
||||
import type { PartialDeep } from 'type-fest';
|
||||
|
||||
import { normalizeStatus } from '@/mastodon/actions/importer/statuses';
|
||||
import type { ApiCollectionJSON } from '@/mastodon/api_types/collections';
|
||||
import type {
|
||||
ApiAudioAttachmentJSON,
|
||||
ApiGifvAttachmentJSON,
|
||||
@@ -14,7 +15,10 @@ import type {
|
||||
import type { ApiPollJSON } from '@/mastodon/api_types/polls';
|
||||
import type { ApiQuotedStatusJSON } from '@/mastodon/api_types/quotes';
|
||||
import type { ApiRelationshipJSON } from '@/mastodon/api_types/relationships';
|
||||
import type { ApiStatusJSON } from '@/mastodon/api_types/statuses';
|
||||
import type {
|
||||
ApiPreviewCardJSON,
|
||||
ApiStatusJSON,
|
||||
} from '@/mastodon/api_types/statuses';
|
||||
import type {
|
||||
CustomEmojiData,
|
||||
UnicodeEmojiData,
|
||||
@@ -173,7 +177,7 @@ const baseAttachment = {
|
||||
id: '1',
|
||||
url: 'https://example.com/image/1',
|
||||
preview_url: 'https://example.com/image/1/preview',
|
||||
blurhash: '',
|
||||
blurhash: 'LGF5]+Yk^6#M@-5c,1J5@[or[Q6.',
|
||||
} as const;
|
||||
const imageMeta = {
|
||||
width: 100,
|
||||
@@ -321,6 +325,51 @@ export const pollFactoryImmutable = (
|
||||
})) ?? [],
|
||||
});
|
||||
|
||||
export const cardFactoryAPI: FactoryFunction<ApiPreviewCardJSON> = (
|
||||
options = {},
|
||||
) => ({
|
||||
url: 'https://example.com',
|
||||
title: 'Example Card',
|
||||
description: 'This is an example card',
|
||||
language: 'en',
|
||||
type: 'link',
|
||||
author_name: 'A. Person',
|
||||
author_url: 'https://example.com/person',
|
||||
provider_name: 'example',
|
||||
provider_url: 'https://example.com/provider',
|
||||
html: '<p>Here is an example card</p>',
|
||||
width: 100,
|
||||
height: 100,
|
||||
image: null,
|
||||
embed_url: 'https://example.com/embed',
|
||||
image_description: '',
|
||||
blurhash: '',
|
||||
published_at: null,
|
||||
authors: [],
|
||||
...options,
|
||||
});
|
||||
|
||||
export const collectionFactoryAPI: FactoryFunction<ApiCollectionJSON> = (
|
||||
options = {},
|
||||
) => ({
|
||||
id: '1',
|
||||
account_id: '1',
|
||||
name: 'Test Collection',
|
||||
uri: options.url ?? 'https://example.com/collection',
|
||||
url: options.uri ?? 'https://example.com/collection',
|
||||
local: true,
|
||||
item_count: options.items?.length ?? 0,
|
||||
description: 'This is a test collection.',
|
||||
tag: null,
|
||||
language: 'en',
|
||||
sensitive: false,
|
||||
discoverable: true,
|
||||
created_at: '2023-01-01',
|
||||
updated_at: '2023-01-01',
|
||||
items: [],
|
||||
...options,
|
||||
});
|
||||
|
||||
export const relationshipsFactoryAPI: FactoryFunction<ApiRelationshipJSON> = ({
|
||||
id,
|
||||
...data
|
||||
|
||||
Reference in New Issue
Block a user