pl-fe: wip valibot migration
Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
This commit is contained in:
@ -1,10 +1,10 @@
|
||||
import * as v from 'valibot';
|
||||
|
||||
const historySchema = v.object({
|
||||
const historySchema = v.array(v.object({
|
||||
day: v.pipe(v.unknown(), v.transform(Number)),
|
||||
accounts: v.pipe(v.unknown(), v.transform(Number)),
|
||||
uses: v.pipe(v.unknown(), v.transform(Number)),
|
||||
});
|
||||
}));
|
||||
|
||||
/** @see {@link https://docs.joinmastodon.org/entities/tag} */
|
||||
const tagSchema = v.object({
|
||||
|
||||
@ -8,6 +8,7 @@
|
||||
*/
|
||||
import { credentialAccountSchema, PlApiClient, type CreateAccountParams, type Token } from 'pl-api';
|
||||
import { defineMessages } from 'react-intl';
|
||||
import * as v from 'valibot';
|
||||
|
||||
import { createAccount } from 'pl-fe/actions/accounts';
|
||||
import { createApp } from 'pl-fe/actions/apps';
|
||||
@ -157,7 +158,7 @@ const verifyCredentials = (token: string, accountUrl?: string) =>
|
||||
if (error?.response?.status === 403 && error?.response?.json?.id) {
|
||||
// The user is waitlisted
|
||||
const account = error.response.json;
|
||||
const parsedAccount = credentialAccountSchema.parse(error.response.json);
|
||||
const parsedAccount = v.parse(credentialAccountSchema, error.response.json);
|
||||
dispatch(importFetchedAccount(parsedAccount));
|
||||
dispatch({ type: VERIFY_CREDENTIALS_SUCCESS, token, account: parsedAccount });
|
||||
if (account.id === getState().me) dispatch(fetchMeSuccess(parsedAccount));
|
||||
|
||||
@ -7,6 +7,7 @@
|
||||
*/
|
||||
|
||||
import { instanceSchema, PlApiClient, type Instance } from 'pl-api';
|
||||
import * as v from 'valibot';
|
||||
|
||||
import { createApp } from 'pl-fe/actions/apps';
|
||||
import { authLoggedIn, verifyCredentials, switchAccount } from 'pl-fe/actions/auth';
|
||||
@ -24,7 +25,7 @@ const fetchExternalInstance = (baseURL: string) =>
|
||||
if (error.response?.status === 401) {
|
||||
// Authenticated fetch is enabled.
|
||||
// Continue with a limited featureset.
|
||||
return instanceSchema.parse({});
|
||||
return v.parse(instanceSchema, {});
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
|
||||
@ -5,6 +5,7 @@ import {
|
||||
type AdminCreateAnnouncementParams,
|
||||
type AdminUpdateAnnouncementParams,
|
||||
} from 'pl-api';
|
||||
import * as v from 'valibot';
|
||||
|
||||
import { useClient } from 'pl-fe/hooks';
|
||||
import { normalizeAnnouncement, AdminAnnouncement } from 'pl-fe/normalizers';
|
||||
@ -36,7 +37,7 @@ const useAnnouncements = () => {
|
||||
retry: false,
|
||||
onSuccess: (data) =>
|
||||
queryClient.setQueryData(['admin', 'announcements'], (prevResult: ReadonlyArray<AdminAnnouncement>) =>
|
||||
[...prevResult, adminAnnouncementSchema.parse(data)],
|
||||
[...prevResult, v.parse(adminAnnouncementSchema, data)],
|
||||
),
|
||||
onSettled: () => userAnnouncements.refetch(),
|
||||
});
|
||||
@ -50,7 +51,7 @@ const useAnnouncements = () => {
|
||||
retry: false,
|
||||
onSuccess: (data) =>
|
||||
queryClient.setQueryData(['admin', 'announcements'], (prevResult: ReadonlyArray<AdminAnnouncement>) =>
|
||||
prevResult.map((announcement) => announcement.id === data.id ? adminAnnouncementSchema.parse(data) : announcement),
|
||||
prevResult.map((announcement) => announcement.id === data.id ? v.parse(adminAnnouncementSchema, data) : announcement),
|
||||
),
|
||||
onSettled: () => userAnnouncements.refetch(),
|
||||
});
|
||||
|
||||
@ -1,11 +1,12 @@
|
||||
import { useMutation, useQuery } from '@tanstack/react-query';
|
||||
import { announcementReactionSchema, type AnnouncementReaction } from 'pl-api';
|
||||
import * as v from 'valibot';
|
||||
|
||||
import { useClient } from 'pl-fe/hooks';
|
||||
import { type Announcement, normalizeAnnouncement } from 'pl-fe/normalizers';
|
||||
import { queryClient } from 'pl-fe/queries/client';
|
||||
|
||||
const updateReaction = (reaction: AnnouncementReaction, count: number, me?: boolean, overwrite?: boolean) => announcementReactionSchema.parse({
|
||||
const updateReaction = (reaction: AnnouncementReaction, count: number, me?: boolean, overwrite?: boolean) => v.parse(announcementReactionSchema, {
|
||||
...reaction,
|
||||
me: typeof me === 'boolean' ? me : reaction.me,
|
||||
count: overwrite ? count : (reaction.count + count),
|
||||
@ -18,7 +19,7 @@ const updateReactions = (reactions: AnnouncementReaction[], name: string, count:
|
||||
reactions = reactions.map(reaction => reaction.name === name ? updateReaction(reaction, count, me, overwrite) : reaction);
|
||||
}
|
||||
|
||||
return [...reactions, updateReaction(announcementReactionSchema.parse({ name }), count, me, overwrite)];
|
||||
return [...reactions, updateReaction(v.parse(announcementReactionSchema, { name }), count, me, overwrite)];
|
||||
};
|
||||
|
||||
const useAnnouncements = () => {
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { instanceSchema } from 'pl-api';
|
||||
import * as v from 'valibot';
|
||||
|
||||
import { __stub } from 'pl-fe/api';
|
||||
import { buildGroup } from 'pl-fe/jest/factory';
|
||||
@ -8,7 +9,7 @@ import { useGroups } from './useGroups';
|
||||
|
||||
const group = buildGroup({ id: '1', display_name: 'soapbox' });
|
||||
const store = {
|
||||
instance: instanceSchema.parse({
|
||||
instance: v.parse(instanceSchema, {
|
||||
version: '3.4.1 (compatible; TruthSocial 1.0.0+unreleased)',
|
||||
}),
|
||||
};
|
||||
|
||||
@ -1,10 +1,11 @@
|
||||
import { useMutation, useQuery } from '@tanstack/react-query';
|
||||
import { type InteractionPolicies, interactionPoliciesSchema } from 'pl-api';
|
||||
import * as v from 'valibot';
|
||||
|
||||
import { useClient, useFeatures, useLoggedIn } from 'pl-fe/hooks';
|
||||
import { queryClient } from 'pl-fe/queries/client';
|
||||
|
||||
const emptySchema = interactionPoliciesSchema.parse({});
|
||||
const emptySchema = v.parse(interactionPoliciesSchema, {});
|
||||
|
||||
const useInteractionPolicies = () => {
|
||||
const client = useClient();
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import clsx from 'clsx';
|
||||
import { type MediaAttachment, type PreviewCard as CardEntity, mediaAttachmentSchema } from 'pl-api';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import * as v from 'valibot';
|
||||
|
||||
import Blurhash from 'pl-fe/components/blurhash';
|
||||
import { HStack, Stack, Text, Icon } from 'pl-fe/components/ui';
|
||||
@ -43,7 +44,7 @@ const PreviewCard: React.FC<IPreviewCard> = ({
|
||||
const trimmedDescription = trim(card.description, maxDescription);
|
||||
|
||||
const handlePhotoClick = () => {
|
||||
const attachment = mediaAttachmentSchema.parse({
|
||||
const attachment = v.parse(mediaAttachmentSchema, {
|
||||
id: '',
|
||||
type: 'image',
|
||||
url: card.embed_url,
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import * as v from 'valibot';
|
||||
import { z } from 'zod';
|
||||
|
||||
import { useAppDispatch } from 'pl-fe/hooks/useAppDispatch';
|
||||
|
||||
@ -3,6 +3,7 @@ import { GOTOSOCIAL, MASTODON, mediaAttachmentSchema } from 'pl-api';
|
||||
import React from 'react';
|
||||
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import * as v from 'valibot';
|
||||
|
||||
import { biteAccount, blockAccount, pinAccount, removeFromFollowers, unblockAccount, unmuteAccount, unpinAccount } from 'pl-fe/actions/accounts';
|
||||
import { mentionCompose, directCompose } from 'pl-fe/actions/compose';
|
||||
@ -240,7 +241,7 @@ const Header: React.FC<IHeader> = ({ account }) => {
|
||||
};
|
||||
|
||||
const onAvatarClick = () => {
|
||||
const avatar = mediaAttachmentSchema.parse({
|
||||
const avatar = v.parse(mediaAttachmentSchema, {
|
||||
id: '',
|
||||
type: 'image',
|
||||
url: account.avatar,
|
||||
@ -256,7 +257,7 @@ const Header: React.FC<IHeader> = ({ account }) => {
|
||||
};
|
||||
|
||||
const onHeaderClick = () => {
|
||||
const header = mediaAttachmentSchema.parse({
|
||||
const header = v.parse(mediaAttachmentSchema, {
|
||||
type: 'image',
|
||||
url: account.header,
|
||||
});
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { instanceSchema } from 'pl-api';
|
||||
import React from 'react';
|
||||
import * as v from 'valibot';
|
||||
|
||||
import { fireEvent, render, screen } from 'pl-fe/jest/test-helpers';
|
||||
|
||||
@ -9,7 +10,7 @@ describe('<LoginForm />', () => {
|
||||
it('renders for Pleroma', () => {
|
||||
const mockFn = vi.fn();
|
||||
const store = {
|
||||
instance: instanceSchema.parse({
|
||||
instance: v.parse(instanceSchema, {
|
||||
version: '2.7.2 (compatible; Pleroma 2.3.0)',
|
||||
}),
|
||||
};
|
||||
@ -22,7 +23,7 @@ describe('<LoginForm />', () => {
|
||||
it('renders for Mastodon', () => {
|
||||
const mockFn = vi.fn();
|
||||
const store = {
|
||||
instance: instanceSchema.parse({
|
||||
instance: v.parse(instanceSchema, {
|
||||
version: '3.0.0',
|
||||
}),
|
||||
};
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { instanceSchema } from 'pl-api';
|
||||
import React from 'react';
|
||||
import * as v from 'valibot';
|
||||
|
||||
import { render, screen } from 'pl-fe/jest/test-helpers';
|
||||
|
||||
@ -8,7 +9,7 @@ import LoginPage from './login-page';
|
||||
describe('<LoginPage />', () => {
|
||||
it('renders correctly on load', () => {
|
||||
const store = {
|
||||
instance: instanceSchema.parse({
|
||||
instance: v.parse(instanceSchema, {
|
||||
version: '2.7.2 (compatible; Pleroma 2.3.0)',
|
||||
}),
|
||||
};
|
||||
|
||||
@ -26,6 +26,7 @@ import { mediaAttachmentSchema } from 'pl-api';
|
||||
import * as React from 'react';
|
||||
import { Suspense, useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { FormattedMessage, defineMessages, useIntl } from 'react-intl';
|
||||
import * as v from 'valibot';
|
||||
|
||||
import { HStack, Icon, IconButton } from 'pl-fe/components/ui';
|
||||
import { useSettings } from 'pl-fe/hooks';
|
||||
@ -122,7 +123,7 @@ const ImageComponent = ({
|
||||
);
|
||||
|
||||
const previewImage = () => {
|
||||
const image = mediaAttachmentSchema.parse({
|
||||
const image = v.parse(mediaAttachmentSchema, {
|
||||
id: '',
|
||||
type: 'image',
|
||||
url: src,
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { statusSchema } from 'pl-api';
|
||||
import * as v from 'valibot';
|
||||
|
||||
import { Entities } from 'pl-fe/entity-store/entities';
|
||||
import { normalizeStatus } from 'pl-fe/normalizers';
|
||||
@ -22,7 +23,7 @@ const buildStatus = (state: RootState, draftStatus: DraftStatus) => {
|
||||
const me = state.me as string;
|
||||
const account = state.entities[Entities.ACCOUNTS]?.store[me];
|
||||
|
||||
const status = statusSchema.parse({
|
||||
const status = v.parse(statusSchema, {
|
||||
id: 'draft',
|
||||
account,
|
||||
content: draftStatus.text.replace(new RegExp('\n', 'g'), '<br>'), /* eslint-disable-line no-control-regex */
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { mediaAttachmentSchema } from 'pl-api';
|
||||
import React, { useState } from 'react';
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
import * as v from 'valibot';
|
||||
|
||||
import GroupAvatar from 'pl-fe/components/groups/group-avatar';
|
||||
import { ParsedContent } from 'pl-fe/components/parsed-content';
|
||||
@ -52,7 +53,7 @@ const GroupHeader: React.FC<IGroupHeader> = ({ group }) => {
|
||||
}
|
||||
|
||||
const onAvatarClick = () => {
|
||||
const avatar = mediaAttachmentSchema.parse({
|
||||
const avatar = v.parse(mediaAttachmentSchema, {
|
||||
id: '',
|
||||
type: 'image',
|
||||
url: group.avatar,
|
||||
@ -68,7 +69,7 @@ const GroupHeader: React.FC<IGroupHeader> = ({ group }) => {
|
||||
};
|
||||
|
||||
const onHeaderClick = () => {
|
||||
const header = mediaAttachmentSchema.parse({
|
||||
const header = v.parse(mediaAttachmentSchema, {
|
||||
id: '',
|
||||
type: 'image',
|
||||
url: group.header,
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { statusSchema, type ScheduledStatus } from 'pl-api';
|
||||
import * as v from 'valibot';
|
||||
|
||||
import { Entities } from 'pl-fe/entity-store/entities';
|
||||
import { normalizeStatus } from 'pl-fe/normalizers/status';
|
||||
@ -9,7 +10,7 @@ const buildStatus = (state: RootState, scheduledStatus: ScheduledStatus) => {
|
||||
const me = state.me as string;
|
||||
const account = state.entities[Entities.ACCOUNTS]?.store[me];
|
||||
|
||||
const status = statusSchema.parse({
|
||||
const status = v.parse(statusSchema, {
|
||||
account,
|
||||
content: scheduledStatus.params.text?.replace(new RegExp('\n', 'g'), '<br>'), /* eslint-disable-line no-control-regex */
|
||||
created_at: scheduledStatus.params.scheduled_at,
|
||||
|
||||
@ -29,7 +29,6 @@ const UploadButton: React.FC<IUploadButton> = ({ disabled, onSelectFile }) => {
|
||||
fileElement.current?.click();
|
||||
};
|
||||
|
||||
|
||||
return (
|
||||
<HStack className='size-full cursor-pointer text-primary-500 dark:text-accent-blue' space={3} alignItems='center' justifyContent='center' element='label'>
|
||||
<Icon
|
||||
|
||||
@ -157,7 +157,6 @@ const GlobalHotkeys: React.FC<IGlobalHotkeys> = ({ children, node }) => {
|
||||
return handlers;
|
||||
}, [account?.id]);
|
||||
|
||||
|
||||
return (
|
||||
<HotKeys keyMap={keyMap} handlers={handlers} ref={setHotkeysRef} attach={window} focused>
|
||||
{children}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { Map as ImmutableMap, List as ImmutableList } from 'immutable';
|
||||
import { statusSchema } from 'pl-api';
|
||||
import * as v from 'valibot';
|
||||
|
||||
import { normalizeStatus } from 'pl-fe/normalizers/status';
|
||||
import { makeGetAccount } from 'pl-fe/selectors';
|
||||
@ -46,7 +47,7 @@ const buildStatus = (state: RootState, pendingStatus: PendingStatus, idempotency
|
||||
visibility: pendingStatus.visibility,
|
||||
};
|
||||
|
||||
return normalizeStatus(statusSchema.parse(status));
|
||||
return normalizeStatus(v.parse(statusSchema, status));
|
||||
};
|
||||
|
||||
export { buildStatus };
|
||||
|
||||
@ -17,6 +17,7 @@ import {
|
||||
type Relationship,
|
||||
type Status,
|
||||
} from 'pl-api';
|
||||
import * as v from 'valibot';
|
||||
|
||||
import type { PartialDeep } from 'type-fest';
|
||||
|
||||
@ -24,18 +25,18 @@ import type { PartialDeep } from 'type-fest';
|
||||
// This looks promising but didn't work on my first attempt: https://github.com/anatine/zod-plugins/tree/main/packages/zod-mock
|
||||
|
||||
const buildAccount = (props: PartialDeep<Account> = {}): Account =>
|
||||
accountSchema.parse(Object.assign({
|
||||
v.parse(accountSchema, Object.assign({
|
||||
id: crypto.randomUUID(),
|
||||
url: `https://soapbox.test/users/${crypto.randomUUID()}`,
|
||||
}, props));
|
||||
|
||||
const buildCard = (props: PartialDeep<PreviewCard> = {}): PreviewCard =>
|
||||
previewCardSchema.parse(Object.assign({
|
||||
v.parse(previewCardSchema, Object.assign({
|
||||
url: 'https://soapbox.test',
|
||||
}, props));
|
||||
|
||||
const buildGroup = (props: PartialDeep<Group> = {}): Group =>
|
||||
groupSchema.parse(Object.assign({
|
||||
v.parse(groupSchema, Object.assign({
|
||||
id: crypto.randomUUID(),
|
||||
owner: {
|
||||
id: crypto.randomUUID(),
|
||||
@ -43,28 +44,28 @@ const buildGroup = (props: PartialDeep<Group> = {}): Group =>
|
||||
}, props));
|
||||
|
||||
const buildGroupRelationship = (props: PartialDeep<GroupRelationship> = {}): GroupRelationship =>
|
||||
groupRelationshipSchema.parse(Object.assign({
|
||||
v.parse(groupRelationshipSchema, Object.assign({
|
||||
id: crypto.randomUUID(),
|
||||
}, props));
|
||||
|
||||
const buildGroupMember = (
|
||||
props: PartialDeep<GroupMember> = {},
|
||||
accountProps: PartialDeep<Account> = {},
|
||||
): GroupMember => groupMemberSchema.parse(Object.assign({
|
||||
): GroupMember => v.parse(groupMemberSchema, Object.assign({
|
||||
id: crypto.randomUUID(),
|
||||
account: buildAccount(accountProps),
|
||||
role: GroupRoles.USER,
|
||||
}, props));
|
||||
|
||||
const buildInstance = (props: PartialDeep<Instance> = {}) => instanceSchema.parse(props);
|
||||
const buildInstance = (props: PartialDeep<Instance> = {}) => v.parse(instanceSchema, props);
|
||||
|
||||
const buildRelationship = (props: PartialDeep<Relationship> = {}): Relationship =>
|
||||
relationshipSchema.parse(Object.assign({
|
||||
v.parse(relationshipSchema, Object.assign({
|
||||
id: crypto.randomUUID(),
|
||||
}, props));
|
||||
|
||||
const buildStatus = (props: PartialDeep<Status> = {}) =>
|
||||
statusSchema.parse(Object.assign({
|
||||
v.parse(statusSchema, Object.assign({
|
||||
id: crypto.randomUUID(),
|
||||
account: buildAccount(),
|
||||
}, props));
|
||||
|
||||
@ -1,14 +1,15 @@
|
||||
import { instanceSchema } from 'pl-api';
|
||||
import * as v from 'valibot';
|
||||
|
||||
import alexJson from 'pl-fe/__fixtures__/pleroma-account.json';
|
||||
|
||||
import { buildAccount } from './factory';
|
||||
|
||||
/** Store with registrations open. */
|
||||
const storeOpen = { instance: instanceSchema.parse({ registrations: true }) };
|
||||
const storeOpen = { instance: v.parse(instanceSchema, { registrations: true }) };
|
||||
|
||||
/** Store with registrations closed. */
|
||||
const storeClosed = { instance: instanceSchema.parse({ registrations: false }) };
|
||||
const storeClosed = { instance: v.parse(instanceSchema, { registrations: false }) };
|
||||
|
||||
/** Store with a logged-in user. */
|
||||
const storeLoggedIn = {
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
import escapeTextContentForBrowser from 'escape-html';
|
||||
import DOMPurify from 'isomorphic-dompurify';
|
||||
|
||||
|
||||
import emojify from 'pl-fe/features/emoji';
|
||||
import { makeEmojiMap } from 'pl-fe/utils/normalizers';
|
||||
|
||||
|
||||
@ -6,6 +6,7 @@
|
||||
import escapeTextContentForBrowser from 'escape-html';
|
||||
import DOMPurify from 'isomorphic-dompurify';
|
||||
import { type Account as BaseAccount, type Status as BaseStatus, type CustomEmoji, type MediaAttachment, mentionSchema, type Translation } from 'pl-api';
|
||||
import * as v from 'valibot';
|
||||
|
||||
import emojify from 'pl-fe/features/emoji';
|
||||
import { unescapeHTML } from 'pl-fe/utils/html';
|
||||
@ -111,7 +112,7 @@ const normalizeStatus = (status: BaseStatus & {
|
||||
const hasSelfMention = status.mentions.some(mention => status.account.id === mention.id);
|
||||
|
||||
if (isSelfReply && !hasSelfMention) {
|
||||
const selfMention = mentionSchema.parse(status.account);
|
||||
const selfMention = v.parse(mentionSchema, status.account);
|
||||
mentions = [selfMention, ...mentions];
|
||||
}
|
||||
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { InfiniteData, keepPreviousData, useInfiniteQuery, useMutation, useQuery } from '@tanstack/react-query';
|
||||
import sumBy from 'lodash/sumBy';
|
||||
import { type Chat, type ChatMessage as BaseChatMessage, type PaginatedResponse, chatMessageSchema, type Relationship } from 'pl-api';
|
||||
import * as v from 'valibot';
|
||||
|
||||
import { importFetchedAccount, importFetchedAccounts } from 'pl-fe/actions/importer';
|
||||
import { ChatWidgetScreens, useChatContext } from 'pl-fe/contexts/chat-context';
|
||||
@ -171,7 +172,7 @@ const useChatActions = (chatId: string) => {
|
||||
...page,
|
||||
items: [
|
||||
normalizeChatMessage({
|
||||
...chatMessageSchema.parse({
|
||||
...v.parse(chatMessageSchema, {
|
||||
chat_id: variables.chatId,
|
||||
content: variables.content,
|
||||
id: pendingId,
|
||||
|
||||
@ -4,11 +4,11 @@
|
||||
*/
|
||||
|
||||
import { produce } from 'immer';
|
||||
import { Account, accountSchema } from 'pl-api';
|
||||
|
||||
import { VERIFY_CREDENTIALS_SUCCESS, AUTH_ACCOUNT_REMEMBER_SUCCESS } from 'pl-fe/actions/auth';
|
||||
import { ME_FETCH_SUCCESS, ME_PATCH_SUCCESS } from 'pl-fe/actions/me';
|
||||
|
||||
import type { Account, CredentialAccount } from 'pl-api';
|
||||
import type { AnyAction } from 'redux';
|
||||
|
||||
interface AccountMeta {
|
||||
@ -18,16 +18,8 @@ interface AccountMeta {
|
||||
|
||||
type State = Record<string, AccountMeta | undefined>;
|
||||
|
||||
const importAccount = (state: State, data: unknown): State => {
|
||||
const result = accountSchema.safeParse(data);
|
||||
|
||||
if (!result.success) {
|
||||
return state;
|
||||
}
|
||||
|
||||
const account = result.data;
|
||||
|
||||
return produce(state, draft => {
|
||||
const importAccount = (state: State, account: CredentialAccount): State =>
|
||||
produce(state, draft => {
|
||||
const existing = draft[account.id];
|
||||
|
||||
draft[account.id] = {
|
||||
@ -35,7 +27,6 @@ const importAccount = (state: State, data: unknown): State => {
|
||||
source: account.__meta.source ?? existing?.source,
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
const accounts_meta = (state: Readonly<State> = {}, action: AnyAction): State => {
|
||||
switch (action.type) {
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { List as ImmutableList, Map as ImmutableMap, Record as ImmutableRecord, fromJS } from 'immutable';
|
||||
import trim from 'lodash/trim';
|
||||
import { applicationSchema, PlApiClient, tokenSchema, type Application, type CredentialAccount, type Token } from 'pl-api';
|
||||
import * as v from 'valibot';
|
||||
|
||||
import { MASTODON_PRELOAD_IMPORT } from 'pl-fe/actions/preload';
|
||||
import * as BuildConfig from 'pl-fe/build-config';
|
||||
@ -60,8 +61,8 @@ const getLocalState = () => {
|
||||
if (!state) return undefined;
|
||||
|
||||
return ReducerRecord({
|
||||
app: state.app && applicationSchema.parse(state.app),
|
||||
tokens: ImmutableMap(Object.entries(state.tokens).map(([key, value]) => [key, tokenSchema.parse(value)])),
|
||||
app: state.app && v.parse(applicationSchema, state.app),
|
||||
tokens: ImmutableMap(Object.entries(state.tokens).map(([key, value]) => [key, v.parse(tokenSchema, value)])),
|
||||
users: ImmutableMap(Object.entries(state.users).map(([key, value]) => [key, AuthUserRecord(value as any)])),
|
||||
me: state.me,
|
||||
client: new PlApiClient(parseBaseURL(state.me) || backendUrl, state.users[state.me]?.access_token),
|
||||
@ -237,7 +238,7 @@ const importMastodonPreload = (state: State, data: ImmutableMap<string, any>) =>
|
||||
const accessToken = data.getIn(['meta', 'access_token']) as string;
|
||||
|
||||
if (validId(accessToken) && validId(accountId) && isURL(accountUrl)) {
|
||||
state.setIn(['tokens', accessToken], tokenSchema.parse({
|
||||
state.setIn(['tokens', accessToken], v.parse(tokenSchema, {
|
||||
access_token: accessToken,
|
||||
account: accountId,
|
||||
me: accountUrl,
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { produce } from 'immer';
|
||||
import { Map as ImmutableMap, List as ImmutableList, fromJS } from 'immutable';
|
||||
import { type Instance, instanceSchema } from 'pl-api';
|
||||
import * as v from 'valibot';
|
||||
|
||||
import { ADMIN_CONFIG_UPDATE_REQUEST, ADMIN_CONFIG_UPDATE_SUCCESS } from 'pl-fe/actions/admin';
|
||||
import { INSTANCE_FETCH_FAIL, INSTANCE_FETCH_SUCCESS, InstanceAction } from 'pl-fe/actions/instance';
|
||||
@ -10,11 +11,11 @@ import ConfigDB from 'pl-fe/utils/config-db';
|
||||
|
||||
import type { AnyAction } from 'redux';
|
||||
|
||||
const initialState: Instance = instanceSchema.parse({});
|
||||
const initialState: Instance = v.parse(instanceSchema, {});
|
||||
|
||||
const preloadImport = (state: Instance, action: Record<string, any>, path: string) => {
|
||||
const instance = action.data[path];
|
||||
return instance ? instanceSchema.parse(instance) : state;
|
||||
return instance ? v.parse(instanceSchema, instance) : state;
|
||||
};
|
||||
|
||||
const getConfigValue = (instanceConfig: ImmutableMap<string, any>, key: string) => {
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { List as ImmutableList, fromJS } from 'immutable';
|
||||
import { emojiReactionSchema } from 'pl-api';
|
||||
import * as v from 'valibot';
|
||||
|
||||
import {
|
||||
simulateEmojiReact,
|
||||
@ -11,7 +12,7 @@ describe('simulateEmojiReact', () => {
|
||||
const emojiReacts = ImmutableList([
|
||||
{ 'count': 2, 'me': false, 'name': '👍', 'url': undefined },
|
||||
{ 'count': 2, 'me': false, 'name': '❤', 'url': undefined },
|
||||
].map((react) => emojiReactionSchema.parse(react)));
|
||||
].map((react) => v.parse(emojiReactionSchema, react)));
|
||||
expect(simulateEmojiReact(emojiReacts, '❤')).toEqual(fromJS([
|
||||
{ 'count': 2, 'me': false, 'name': '👍', 'url': undefined },
|
||||
{ 'count': 3, 'me': true, 'name': '❤', 'url': undefined },
|
||||
@ -22,7 +23,7 @@ describe('simulateEmojiReact', () => {
|
||||
const emojiReacts = ImmutableList([
|
||||
{ 'count': 2, 'me': false, 'name': '👍', 'url': undefined },
|
||||
{ 'count': 2, 'me': false, 'name': '❤', 'url': undefined },
|
||||
].map((react) => emojiReactionSchema.parse(react)));
|
||||
].map((react) => v.parse(emojiReactionSchema, react)));
|
||||
expect(simulateEmojiReact(emojiReacts, '😯')).toEqual(fromJS([
|
||||
{ 'count': 2, 'me': false, 'name': '👍', 'url': undefined },
|
||||
{ 'count': 2, 'me': false, 'name': '❤', 'url': undefined },
|
||||
@ -34,7 +35,7 @@ describe('simulateEmojiReact', () => {
|
||||
const emojiReacts = ImmutableList([
|
||||
{ 'count': 2, 'me': false, 'name': '👍', 'url': undefined },
|
||||
{ 'count': 2, 'me': false, 'name': '❤', 'url': undefined },
|
||||
].map((react) => emojiReactionSchema.parse(react)));
|
||||
].map((react) => v.parse(emojiReactionSchema, react)));
|
||||
expect(simulateEmojiReact(emojiReacts, 'soapbox', 'https://gleasonator.com/emoji/Gleasonator/soapbox.png')).toEqual(fromJS([
|
||||
{ 'count': 2, 'me': false, 'name': '👍', 'url': undefined },
|
||||
{ 'count': 2, 'me': false, 'name': '❤', 'url': undefined },
|
||||
@ -48,7 +49,7 @@ describe('simulateUnEmojiReact', () => {
|
||||
const emojiReacts = ImmutableList([
|
||||
{ 'count': 2, 'me': false, 'name': '👍' },
|
||||
{ 'count': 3, 'me': true, 'name': '❤' },
|
||||
].map((react) => emojiReactionSchema.parse(react)));
|
||||
].map((react) => v.parse(emojiReactionSchema, react)));
|
||||
expect(simulateUnEmojiReact(emojiReacts, '❤')).toEqual(fromJS([
|
||||
{ 'count': 2, 'me': false, 'name': '👍' },
|
||||
{ 'count': 2, 'me': false, 'name': '❤' },
|
||||
@ -60,7 +61,7 @@ describe('simulateUnEmojiReact', () => {
|
||||
{ 'count': 2, 'me': false, 'name': '👍' },
|
||||
{ 'count': 2, 'me': false, 'name': '❤' },
|
||||
{ 'count': 1, 'me': true, 'name': '😯' },
|
||||
].map((react) => emojiReactionSchema.parse(react)));
|
||||
].map((react) => v.parse(emojiReactionSchema, react)));
|
||||
expect(simulateUnEmojiReact(emojiReacts, '😯')).toEqual(fromJS([
|
||||
{ 'count': 2, 'me': false, 'name': '👍' },
|
||||
{ 'count': 2, 'me': false, 'name': '❤' },
|
||||
@ -72,7 +73,7 @@ describe('simulateUnEmojiReact', () => {
|
||||
{ 'count': 2, 'me': false, 'name': '👍' },
|
||||
{ 'count': 2, 'me': false, 'name': '❤' },
|
||||
{ 'count': 1, 'me': true, 'name': 'soapbox', 'url': 'https://gleasonator.com/emoji/Gleasonator/soapbox.png' },
|
||||
].map((react) => emojiReactionSchema.parse(react)));
|
||||
].map((react) => v.parse(emojiReactionSchema, react)));
|
||||
expect(simulateUnEmojiReact(emojiReacts, 'soapbox')).toEqual(fromJS([
|
||||
{ 'count': 2, 'me': false, 'name': '👍' },
|
||||
{ 'count': 2, 'me': false, 'name': '❤' },
|
||||
|
||||
@ -1,18 +1,19 @@
|
||||
import { emojiReactionSchema, type EmojiReaction } from 'pl-api';
|
||||
import * as v from 'valibot';
|
||||
|
||||
const simulateEmojiReact = (emojiReacts: Array<EmojiReaction>, emoji: string, url?: string) => {
|
||||
const idx = emojiReacts.findIndex(e => e.name === emoji);
|
||||
const emojiReact = emojiReacts[idx];
|
||||
|
||||
if (idx > -1 && emojiReact) {
|
||||
return emojiReacts.map((reaction, id) => id === idx ? emojiReactionSchema.parse({
|
||||
return emojiReacts.map((reaction, id) => id === idx ? v.parse(emojiReactionSchema, {
|
||||
...emojiReact,
|
||||
count: (emojiReact.count || 0) + 1,
|
||||
me: true,
|
||||
url,
|
||||
}) : reaction);
|
||||
} else {
|
||||
return [...emojiReacts, emojiReactionSchema.parse({
|
||||
return [...emojiReacts, v.parse(emojiReactionSchema, {
|
||||
count: 1,
|
||||
me: true,
|
||||
name: emoji,
|
||||
@ -30,7 +31,7 @@ const simulateUnEmojiReact = (emojiReacts: Array<EmojiReaction>, emoji: string)
|
||||
if (emojiReact) {
|
||||
const newCount = (emojiReact.count || 1) - 1;
|
||||
if (newCount < 1) return emojiReacts.filter((_, id) => id !== idx);
|
||||
return emojiReacts.map((reaction, id) => id === idx ? emojiReactionSchema.parse({
|
||||
return emojiReacts.map((reaction, id) => id === idx ? v.parse(emojiReactionSchema, {
|
||||
...emojiReact,
|
||||
count: (emojiReact.count || 1) - 1,
|
||||
me: false,
|
||||
|
||||
Reference in New Issue
Block a user