statuses: enable posting with media attachments
This commit is contained in:
parent
dcc9d05ee5
commit
b5a84627c8
|
@ -1,4 +1,5 @@
|
||||||
import { type AppController } from '@/app.ts';
|
import { type AppController } from '@/app.ts';
|
||||||
|
import { Conf } from '@/config.ts';
|
||||||
import { type Event, ISO6391, z } from '@/deps.ts';
|
import { type Event, ISO6391, z } from '@/deps.ts';
|
||||||
import { getAncestors, getDescendants, getEvent } from '@/queries.ts';
|
import { getAncestors, getDescendants, getEvent } from '@/queries.ts';
|
||||||
import { toStatus } from '@/transformers/nostr-to-mastoapi.ts';
|
import { toStatus } from '@/transformers/nostr-to-mastoapi.ts';
|
||||||
|
@ -40,45 +41,46 @@ const createStatusController: AppController = async (c) => {
|
||||||
const body = await parseBody(c.req.raw);
|
const body = await parseBody(c.req.raw);
|
||||||
const result = createStatusSchema.safeParse(body);
|
const result = createStatusSchema.safeParse(body);
|
||||||
|
|
||||||
if (result.success) {
|
if (!result.success) {
|
||||||
const { data } = result;
|
|
||||||
|
|
||||||
if (data.visibility !== 'public') {
|
|
||||||
return c.json({ error: 'Only posting publicly is supported.' }, 422);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data.poll) {
|
|
||||||
return c.json({ error: 'Polls are not yet supported.' }, 422);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data.media_ids?.length) {
|
|
||||||
return c.json({ error: 'Media uploads are not yet supported.' }, 422);
|
|
||||||
}
|
|
||||||
|
|
||||||
const tags: string[][] = [];
|
|
||||||
|
|
||||||
if (data.in_reply_to_id) {
|
|
||||||
tags.push(['e', data.in_reply_to_id, 'reply']);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data.sensitive && data.spoiler_text) {
|
|
||||||
tags.push(['content-warning', data.spoiler_text]);
|
|
||||||
} else if (data.sensitive) {
|
|
||||||
tags.push(['content-warning']);
|
|
||||||
} else if (data.spoiler_text) {
|
|
||||||
tags.push(['subject', data.spoiler_text]);
|
|
||||||
}
|
|
||||||
|
|
||||||
const event = await createEvent({
|
|
||||||
kind: 1,
|
|
||||||
content: data.status ?? '',
|
|
||||||
tags,
|
|
||||||
}, c);
|
|
||||||
|
|
||||||
return c.json(await toStatus(event, c.get('pubkey')));
|
|
||||||
} else {
|
|
||||||
return c.json({ error: 'Bad request', schema: result.error }, 400);
|
return c.json({ error: 'Bad request', schema: result.error }, 400);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const { data } = result;
|
||||||
|
|
||||||
|
if (data.visibility !== 'public') {
|
||||||
|
return c.json({ error: 'Only posting publicly is supported.' }, 422);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.poll) {
|
||||||
|
return c.json({ error: 'Polls are not yet supported.' }, 422);
|
||||||
|
}
|
||||||
|
|
||||||
|
const tags: string[][] = [];
|
||||||
|
|
||||||
|
if (data.in_reply_to_id) {
|
||||||
|
tags.push(['e', data.in_reply_to_id, 'reply']);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.sensitive && data.spoiler_text) {
|
||||||
|
tags.push(['content-warning', data.spoiler_text]);
|
||||||
|
} else if (data.sensitive) {
|
||||||
|
tags.push(['content-warning']);
|
||||||
|
} else if (data.spoiler_text) {
|
||||||
|
tags.push(['subject', data.spoiler_text]);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const cid of data.media_ids ?? []) {
|
||||||
|
const url = new URL(`/ipfs/${cid}`, Conf.mediaDomain).toString();
|
||||||
|
tags.push(['media', url]);
|
||||||
|
}
|
||||||
|
|
||||||
|
const event = await createEvent({
|
||||||
|
kind: 1,
|
||||||
|
content: data.status ?? '',
|
||||||
|
tags,
|
||||||
|
}, c);
|
||||||
|
|
||||||
|
return c.json(await toStatus(event, c.get('pubkey')));
|
||||||
};
|
};
|
||||||
|
|
||||||
const contextController: AppController = async (c) => {
|
const contextController: AppController = async (c) => {
|
||||||
|
|
|
@ -54,7 +54,7 @@ function parseNoteContent(content: string): ParsedNoteContent {
|
||||||
|
|
||||||
interface MediaLink {
|
interface MediaLink {
|
||||||
url: string;
|
url: string;
|
||||||
mimeType: string;
|
mimeType?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getMediaLinks(links: Link[]): MediaLink[] {
|
function getMediaLinks(links: Link[]): MediaLink[] {
|
||||||
|
|
|
@ -118,7 +118,6 @@ async function toStatus(event: Event<1>, viewerPubkey?: string) {
|
||||||
];
|
];
|
||||||
|
|
||||||
const { html, links, firstUrl } = parseNoteContent(event.content);
|
const { html, links, firstUrl } = parseNoteContent(event.content);
|
||||||
const mediaLinks = getMediaLinks(links);
|
|
||||||
|
|
||||||
const [mentions, card, repliesCount, reblogsCount, favouritesCount, [repostEvent], [reactionEvent]] = await Promise
|
const [mentions, card, repliesCount, reblogsCount, favouritesCount, [repostEvent], [reactionEvent]] = await Promise
|
||||||
.all([
|
.all([
|
||||||
|
@ -140,6 +139,12 @@ async function toStatus(event: Event<1>, viewerPubkey?: string) {
|
||||||
const cw = event.tags.find(isCWTag);
|
const cw = event.tags.find(isCWTag);
|
||||||
const subject = event.tags.find((tag) => tag[0] === 'subject');
|
const subject = event.tags.find((tag) => tag[0] === 'subject');
|
||||||
|
|
||||||
|
const mediaLinks = getMediaLinks(links);
|
||||||
|
|
||||||
|
const media = event.tags
|
||||||
|
.filter((tag) => tag[0] === 'media')
|
||||||
|
.map((tag) => ({ url: tag[1], mimeType: tag[2] || undefined }));
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: event.id,
|
id: event.id,
|
||||||
account,
|
account,
|
||||||
|
@ -161,7 +166,7 @@ async function toStatus(event: Event<1>, viewerPubkey?: string) {
|
||||||
bookmarked: false,
|
bookmarked: false,
|
||||||
reblog: null,
|
reblog: null,
|
||||||
application: null,
|
application: null,
|
||||||
media_attachments: mediaLinks.map(renderAttachment),
|
media_attachments: mediaLinks.concat(media).map(renderAttachment),
|
||||||
mentions,
|
mentions,
|
||||||
tags: [],
|
tags: [],
|
||||||
emojis: toEmojis(event),
|
emojis: toEmojis(event),
|
||||||
|
@ -187,7 +192,7 @@ function buildInlineRecipients(mentions: Mention[]): string {
|
||||||
|
|
||||||
const attachmentTypeSchema = z.enum(['image', 'video', 'gifv', 'audio', 'unknown']).catch('unknown');
|
const attachmentTypeSchema = z.enum(['image', 'video', 'gifv', 'audio', 'unknown']).catch('unknown');
|
||||||
|
|
||||||
function renderAttachment({ url, mimeType }: MediaLink) {
|
function renderAttachment({ url, mimeType = '' }: MediaLink) {
|
||||||
const [baseType, _subType] = mimeType.split('/');
|
const [baseType, _subType] = mimeType.split('/');
|
||||||
const type = attachmentTypeSchema.parse(baseType);
|
const type = attachmentTypeSchema.parse(baseType);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue