Groups zod See merge request soapbox-pub/soapbox!2338environments/review-develop-3zknud/deployments/2819
commit
a40222c2de
@ -0,0 +1,17 @@
|
||||
import z from 'zod';
|
||||
|
||||
/**
|
||||
* Represents a custom emoji.
|
||||
* https://docs.joinmastodon.org/entities/CustomEmoji/
|
||||
*/
|
||||
const customEmojiSchema = z.object({
|
||||
category: z.string().catch(''),
|
||||
shortcode: z.string(),
|
||||
static_url: z.string().catch(''),
|
||||
url: z.string(),
|
||||
visible_in_picker: z.boolean().catch(true),
|
||||
});
|
||||
|
||||
type CustomEmoji = z.infer<typeof customEmojiSchema>;
|
||||
|
||||
export { customEmojiSchema, CustomEmoji };
|
@ -0,0 +1,12 @@
|
||||
import z from 'zod';
|
||||
|
||||
const groupRelationshipSchema = z.object({
|
||||
id: z.string(),
|
||||
member: z.boolean().catch(false),
|
||||
requested: z.boolean().catch(false),
|
||||
role: z.string().nullish().catch(null),
|
||||
});
|
||||
|
||||
type GroupRelationship = z.infer<typeof groupRelationshipSchema>;
|
||||
|
||||
export { groupRelationshipSchema, GroupRelationship };
|
@ -0,0 +1,49 @@
|
||||
import escapeTextContentForBrowser from 'escape-html';
|
||||
import z from 'zod';
|
||||
|
||||
import emojify from 'soapbox/features/emoji';
|
||||
import { unescapeHTML } from 'soapbox/utils/html';
|
||||
|
||||
import { customEmojiSchema } from './custom-emoji';
|
||||
import { groupRelationshipSchema } from './group-relationship';
|
||||
import { filteredArray, makeCustomEmojiMap } from './utils';
|
||||
|
||||
const avatarMissing = require('assets/images/avatar-missing.png');
|
||||
const headerMissing = require('assets/images/header-missing.png');
|
||||
|
||||
const groupSchema = z.object({
|
||||
avatar: z.string().catch(avatarMissing),
|
||||
avatar_static: z.string().catch(''),
|
||||
created_at: z.string().datetime().catch(new Date().toUTCString()),
|
||||
display_name: z.string().catch(''),
|
||||
domain: z.string().catch(''),
|
||||
emojis: filteredArray(customEmojiSchema).catch([]),
|
||||
group_visibility: z.string().catch(''), // TruthSocial
|
||||
header: z.string().catch(headerMissing),
|
||||
header_static: z.string().catch(''),
|
||||
id: z.string().catch(''),
|
||||
locked: z.boolean().catch(false),
|
||||
membership_required: z.boolean().catch(false),
|
||||
members_count: z.number().catch(0),
|
||||
note: z.string().transform(note => note === '<p></p>' ? '' : note).catch(''),
|
||||
relationship: groupRelationshipSchema.nullable().catch(null), // Dummy field to be overwritten later
|
||||
statuses_visibility: z.string().catch('public'),
|
||||
uri: z.string().catch(''),
|
||||
url: z.string().catch(''),
|
||||
}).transform(group => {
|
||||
group.avatar_static = group.avatar_static || group.avatar;
|
||||
group.header_static = group.header_static || group.header;
|
||||
group.locked = group.locked || group.group_visibility === 'members_only'; // TruthSocial
|
||||
|
||||
const customEmojiMap = makeCustomEmojiMap(group.emojis);
|
||||
return {
|
||||
...group,
|
||||
display_name_html: emojify(escapeTextContentForBrowser(group.display_name), customEmojiMap),
|
||||
note_emojified: emojify(group.note, customEmojiMap),
|
||||
note_plain: unescapeHTML(group.note),
|
||||
};
|
||||
});
|
||||
|
||||
type Group = z.infer<typeof groupSchema>;
|
||||
|
||||
export { groupSchema, Group };
|
@ -0,0 +1,3 @@
|
||||
export { customEmojiSchema, CustomEmoji } from './custom-emoji';
|
||||
export { groupSchema, Group } from './group';
|
||||
export { groupRelationshipSchema, GroupRelationship } from './group-relationship';
|
@ -0,0 +1,21 @@
|
||||
import z from 'zod';
|
||||
|
||||
import type { CustomEmoji } from './custom-emoji';
|
||||
|
||||
/** Validates individual items in an array, dropping any that aren't valid. */
|
||||
function filteredArray<T extends z.ZodTypeAny>(schema: T) {
|
||||
return z.any().array().transform((arr) => (
|
||||
arr.map((item) => schema.safeParse(item).success ? item as z.infer<T> : undefined)
|
||||
.filter((item): item is z.infer<T> => Boolean(item))
|
||||
));
|
||||
}
|
||||
|
||||
/** Map a list of CustomEmoji to their shortcodes. */
|
||||
function makeCustomEmojiMap(customEmojis: CustomEmoji[]) {
|
||||
return customEmojis.reduce<Record<string, CustomEmoji>>((result, emoji) => {
|
||||
result[`:${emoji.shortcode}:`] = emoji;
|
||||
return result;
|
||||
}, {});
|
||||
}
|
||||
|
||||
export { filteredArray, makeCustomEmojiMap };
|
Loading…
Reference in new issue