Migrate pl-fe schemas to Valibot
Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
This commit is contained in:
parent
95f38374c1
commit
7abeee3d1c
5 changed files with 92 additions and 74 deletions
|
@ -1,85 +1,89 @@
|
|||
import { z } from 'zod';
|
||||
import * as v from 'valibot';
|
||||
|
||||
import { locales } from 'pl-fe/messages';
|
||||
|
||||
import { coerceObject } from '../utils';
|
||||
|
||||
const skinToneSchema = z.union([
|
||||
z.literal(1), z.literal(2), z.literal(3), z.literal(4), z.literal(5), z.literal(6),
|
||||
]);
|
||||
const skinToneSchema = v.picklist([1, 2, 3, 4, 5, 6]);
|
||||
|
||||
const settingsSchema = z.object({
|
||||
onboarded: z.boolean().catch(false),
|
||||
skinTone: skinToneSchema.catch(1),
|
||||
reduceMotion: z.boolean().catch(false),
|
||||
underlineLinks: z.boolean().catch(false),
|
||||
autoPlayGif: z.boolean().catch(true),
|
||||
displayMedia: z.enum(['default', 'hide_all', 'show_all']).catch('default'),
|
||||
displaySpoilers: z.boolean().catch(false),
|
||||
unfollowModal: z.boolean().catch(true),
|
||||
boostModal: z.boolean().catch(false),
|
||||
deleteModal: z.boolean().catch(true),
|
||||
missingDescriptionModal: z.boolean().catch(true),
|
||||
defaultPrivacy: z.enum(['public', 'unlisted', 'private', 'direct']).catch('public'),
|
||||
defaultContentType: z.enum(['text/plain', 'text/markdown']).catch('text/plain'),
|
||||
themeMode: z.enum(['system', 'light', 'dark', 'black']).catch('system'),
|
||||
locale: z.string().catch(navigator.language).pipe(z.enum(locales)).catch('en'),
|
||||
showExplanationBox: z.boolean().catch(true),
|
||||
explanationBox: z.boolean().catch(true),
|
||||
autoloadTimelines: z.boolean().catch(true),
|
||||
autoloadMore: z.boolean().catch(true),
|
||||
preserveSpoilers: z.boolean().catch(false),
|
||||
autoTranslate: z.boolean().catch(false),
|
||||
knownLanguages: z.array(z.string()).catch([]),
|
||||
const settingsSchema = v.object({
|
||||
onboarded: v.fallback(v.boolean(), false),
|
||||
skinTone: v.fallback(skinToneSchema, 1),
|
||||
reduceMotion: v.fallback(v.boolean(), false),
|
||||
underlineLinks: v.fallback(v.boolean(), false),
|
||||
autoPlayGif: v.fallback(v.boolean(), true),
|
||||
displayMedia: v.fallback(v.picklist(['default', 'hide_all', 'show_all']), 'default'),
|
||||
displaySpoilers: v.fallback(v.boolean(), false),
|
||||
unfollowModal: v.fallback(v.boolean(), true),
|
||||
boostModal: v.fallback(v.boolean(), false),
|
||||
deleteModal: v.fallback(v.boolean(), true),
|
||||
missingDescriptionModal: v.fallback(v.boolean(), true),
|
||||
defaultPrivacy: v.fallback(v.picklist(['public', 'unlisted', 'private', 'direct']), 'public'),
|
||||
defaultContentType: v.fallback(v.picklist(['text/plain', 'text/markdown']), 'text/plain'),
|
||||
themeMode: v.fallback(v.picklist(['system', 'light', 'dark', 'black']), 'system'),
|
||||
locale: v.fallback(
|
||||
v.pipe(
|
||||
v.fallback(v.string(), navigator.language),
|
||||
v.picklist(locales),
|
||||
),
|
||||
'en',
|
||||
),
|
||||
showExplanationBox: v.fallback(v.boolean(), true),
|
||||
explanationBox: v.fallback(v.boolean(), true),
|
||||
autoloadTimelines: v.fallback(v.boolean(), true),
|
||||
autoloadMore: v.fallback(v.boolean(), true),
|
||||
preserveSpoilers: v.fallback(v.boolean(), false),
|
||||
autoTranslate: v.fallback(v.boolean(), false),
|
||||
knownLanguages: v.fallback(v.array(v.string()), []),
|
||||
|
||||
systemFont: z.boolean().catch(false),
|
||||
demetricator: z.boolean().catch(false),
|
||||
systemFont: v.fallback(v.boolean(), false),
|
||||
demetricator: v.fallback(v.boolean(), false),
|
||||
|
||||
isDeveloper: z.boolean().catch(false),
|
||||
isDeveloper: v.fallback(v.boolean(), false),
|
||||
|
||||
chats: coerceObject({
|
||||
mainWindow: z.enum(['minimized', 'open']).catch('minimized'),
|
||||
sound: z.boolean().catch(true),
|
||||
mainWindow: v.fallback(v.picklist(['minimized', 'open']), 'minimized'),
|
||||
sound: v.fallback(v.boolean(), true),
|
||||
}),
|
||||
|
||||
timelines: z.record(coerceObject({
|
||||
timelines: v.fallback(v.record(v.string(), coerceObject({
|
||||
shows: coerceObject({
|
||||
reblog: z.boolean().catch(true),
|
||||
reply: z.boolean().catch(true),
|
||||
direct: z.boolean().catch(false),
|
||||
reblog: v.fallback(v.boolean(), true),
|
||||
reply: v.fallback(v.boolean(), true),
|
||||
direct: v.fallback(v.boolean(), false),
|
||||
}),
|
||||
other: coerceObject({
|
||||
onlyMedia: z.boolean().catch(false),
|
||||
onlyMedia: v.fallback(v.boolean(), false),
|
||||
}),
|
||||
})).catch({}),
|
||||
})), {}),
|
||||
|
||||
account_timeline: coerceObject({
|
||||
shows: coerceObject({
|
||||
pinned: z.boolean().catch(true),
|
||||
pinned: v.fallback(v.boolean(), true),
|
||||
}),
|
||||
}),
|
||||
|
||||
remote_timeline: coerceObject({
|
||||
pinnedHosts: z.string().array().catch([]),
|
||||
pinnedHosts: v.fallback(v.array(v.string()), []),
|
||||
}),
|
||||
|
||||
notifications: coerceObject({
|
||||
quickFilter: coerceObject({
|
||||
active: z.string().catch('all'),
|
||||
advanced: z.boolean().catch(false),
|
||||
show: z.boolean().catch(true),
|
||||
active: v.fallback(v.string(), 'all'),
|
||||
advanced: v.fallback(v.boolean(), false),
|
||||
show: v.fallback(v.boolean(), true),
|
||||
}),
|
||||
sounds: z.record(z.boolean()).catch({}),
|
||||
sounds: v.fallback(v.record(v.string(), v.boolean()), {}),
|
||||
}),
|
||||
|
||||
frequentlyUsedEmojis: z.record(z.number()).catch({}),
|
||||
frequentlyUsedLanguages: z.record(z.number()).catch({}),
|
||||
frequentlyUsedEmojis: v.fallback(v.record(v.string(), v.number()), {}),
|
||||
frequentlyUsedLanguages: v.fallback(v.record(v.string(), v.number()), {}),
|
||||
|
||||
saved: z.boolean().catch(true),
|
||||
saved: v.fallback(v.boolean(), true),
|
||||
|
||||
demo: z.boolean().catch(false),
|
||||
demo: v.fallback(v.boolean(), false),
|
||||
});
|
||||
|
||||
type Settings = z.infer<typeof settingsSchema>;
|
||||
type Settings = v.InferOutput<typeof settingsSchema>;
|
||||
|
||||
export { settingsSchema, type Settings };
|
||||
|
|
|
@ -1,20 +1,26 @@
|
|||
import { z } from 'zod';
|
||||
import * as v from 'valibot';
|
||||
|
||||
import { coerceObject } from './utils';
|
||||
|
||||
const mrfSimpleSchema = coerceObject({
|
||||
accept: z.string().array().catch([]),
|
||||
avatar_removal: z.string().array().catch([]),
|
||||
banner_removal: z.string().array().catch([]),
|
||||
federated_timeline_removal: z.string().array().catch([]),
|
||||
followers_only: z.string().array().catch([]),
|
||||
media_nsfw: z.string().array().catch([]),
|
||||
media_removal: z.string().array().catch([]),
|
||||
reject: z.string().array().catch([]),
|
||||
reject_deletes: z.string().array().catch([]),
|
||||
report_removal: z.string().array().catch([]),
|
||||
});
|
||||
const mrfSimpleSchema = coerceObject(v.entriesFromList(
|
||||
[
|
||||
'accept',
|
||||
'avatar_removal',
|
||||
'banner_removal',
|
||||
'federated_timeline_removal',
|
||||
'followers_only',
|
||||
'media_nsfw',
|
||||
'media_removal',
|
||||
'reject',
|
||||
'reject_deletes',
|
||||
'report_removal',
|
||||
],
|
||||
v.fallback(v.array(v.string()), []),
|
||||
));
|
||||
|
||||
type MRFSimple = z.infer<typeof mrfSimpleSchema>;
|
||||
(window as any).v = v;
|
||||
(window as any).mrfSimpleSchema = mrfSimpleSchema;
|
||||
|
||||
type MRFSimple = v.InferOutput<typeof mrfSimpleSchema>;
|
||||
|
||||
export { mrfSimpleSchema, type MRFSimple };
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import * as v from 'valibot';
|
||||
import z from 'zod';
|
||||
|
||||
import type { CustomEmoji } from 'pl-api';
|
||||
|
@ -18,8 +19,13 @@ const makeCustomEmojiMap = (customEmojis: CustomEmoji[]) =>
|
|||
result[`:${emoji.shortcode}:`] = emoji;
|
||||
return result;
|
||||
}, {});
|
||||
|
||||
/** zod schema to force the value into an object, if it isn't already. */
|
||||
const coerceObject = <T extends z.ZodRawShape>(shape: T) =>
|
||||
z.object({}).passthrough().catch({}).pipe(z.object(shape));
|
||||
const coerceObject = <T extends v.ObjectEntries>(shape: T) =>
|
||||
v.pipe(
|
||||
v.any(),
|
||||
v.transform((input) => typeof input === 'object' ? input : {}),
|
||||
v.object(shape),
|
||||
);
|
||||
|
||||
export { filteredArray, makeCustomEmojiMap, coerceObject };
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { produce } from 'immer';
|
||||
import * as v from 'valibot';
|
||||
import { create } from 'zustand';
|
||||
|
||||
import { settingsSchema, type Settings } from 'pl-fe/schemas/pl-fe/settings';
|
||||
|
@ -6,7 +7,7 @@ import { settingsSchema, type Settings } from 'pl-fe/schemas/pl-fe/settings';
|
|||
import type { Emoji } from 'pl-fe/features/emoji';
|
||||
import type { APIEntity } from 'pl-fe/types/entities';
|
||||
|
||||
const settingsSchemaPartial = settingsSchema.partial();
|
||||
const settingsSchemaPartial = v.partial(settingsSchema);
|
||||
|
||||
type State = {
|
||||
defaultSettings: Settings;
|
||||
|
@ -35,22 +36,22 @@ const changeSetting = (object: APIEntity, path: string[], value: any) => {
|
|||
const mergeSettings = (state: State) => state.settings = { ...state.defaultSettings, ...state.userSettings };
|
||||
|
||||
const useSettingsStore = create<State>((set) => ({
|
||||
defaultSettings: settingsSchema.parse({}),
|
||||
defaultSettings: v.parse(settingsSchema, {}),
|
||||
userSettings: {},
|
||||
|
||||
settings: settingsSchema.parse({}),
|
||||
settings: v.parse(settingsSchema, {}),
|
||||
|
||||
loadDefaultSettings: (settings: APIEntity) => set(produce((state: State) => {
|
||||
if (typeof settings !== 'object') return;
|
||||
|
||||
state.defaultSettings = settingsSchema.parse(settings);
|
||||
state.defaultSettings = v.parse(settingsSchema, settings);
|
||||
mergeSettings(state);
|
||||
})),
|
||||
|
||||
loadUserSettings: (settings?: APIEntity) => set(produce((state: State) => {
|
||||
if (typeof settings !== 'object') return;
|
||||
|
||||
state.userSettings = settingsSchemaPartial.parse(settings);
|
||||
state.userSettings = v.parse(settingsSchemaPartial, settings);
|
||||
mergeSettings(state);
|
||||
})),
|
||||
|
||||
|
|
|
@ -4,8 +4,9 @@ import {
|
|||
Set as ImmutableSet,
|
||||
} from 'immutable';
|
||||
import trimStart from 'lodash/trimStart';
|
||||
import * as v from 'valibot';
|
||||
|
||||
import { type MRFSimple, mrfSimpleSchema } from 'pl-fe/schemas/pleroma';
|
||||
import { mrfSimpleSchema } from 'pl-fe/schemas/pleroma';
|
||||
|
||||
type Config = ImmutableMap<string, any>;
|
||||
type Policy = Record<string, any>;
|
||||
|
@ -18,7 +19,7 @@ const find = (
|
|||
config.isSuperset(ImmutableMap({ group, key })),
|
||||
);
|
||||
|
||||
const toSimplePolicy = (configs: ImmutableList<Config>): MRFSimple => {
|
||||
const toSimplePolicy = (configs: ImmutableList<Config>) => {
|
||||
const config = find(configs, ':pleroma', ':mrf_simple');
|
||||
|
||||
const reducer = (acc: ImmutableMap<string, any>, curr: ImmutableMap<string, any>) => {
|
||||
|
@ -30,9 +31,9 @@ const toSimplePolicy = (configs: ImmutableList<Config>): MRFSimple => {
|
|||
if (config?.get) {
|
||||
const value = config.get('value', ImmutableList());
|
||||
const result = value.reduce(reducer, ImmutableMap());
|
||||
return mrfSimpleSchema.parse(result.toJS());
|
||||
return v.parse(mrfSimpleSchema, result.toJS());
|
||||
} else {
|
||||
return mrfSimpleSchema.parse({});
|
||||
return v.parse(mrfSimpleSchema, {});
|
||||
}
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in a new issue