Normalize Relationship with zod
This commit is contained in:
parent
489145ffb8
commit
0016aeacec
11 changed files with 48 additions and 212 deletions
|
@ -1,10 +1,11 @@
|
|||
import { Map as ImmutableMap } from 'immutable';
|
||||
|
||||
import { __stub } from 'soapbox/api';
|
||||
import { buildRelationship } from 'soapbox/jest/factory';
|
||||
import { mockStore, rootState } from 'soapbox/jest/test-helpers';
|
||||
import { ReducerRecord, EditRecord } from 'soapbox/reducers/account-notes';
|
||||
|
||||
import { normalizeAccount, normalizeRelationship } from '../../normalizers';
|
||||
import { normalizeAccount } from '../../normalizers';
|
||||
import { changeAccountNoteComment, initAccountNoteModal, submitAccountNote } from '../account-notes';
|
||||
|
||||
import type { Account } from 'soapbox/types/entities';
|
||||
|
@ -66,7 +67,7 @@ describe('initAccountNoteModal()', () => {
|
|||
|
||||
beforeEach(() => {
|
||||
const state = rootState
|
||||
.set('relationships', ImmutableMap({ '1': normalizeRelationship({ note: 'hello' }) }));
|
||||
.set('relationships', ImmutableMap({ '1': buildRelationship({ note: 'hello' }) }));
|
||||
store = mockStore(state);
|
||||
});
|
||||
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
import { Map as ImmutableMap } from 'immutable';
|
||||
|
||||
import { __stub } from 'soapbox/api';
|
||||
import { buildRelationship } from 'soapbox/jest/factory';
|
||||
import { mockStore, rootState } from 'soapbox/jest/test-helpers';
|
||||
import { ListRecord, ReducerRecord } from 'soapbox/reducers/user-lists';
|
||||
|
||||
import { normalizeAccount, normalizeInstance, normalizeRelationship } from '../../normalizers';
|
||||
import { normalizeAccount, normalizeInstance } from '../../normalizers';
|
||||
import {
|
||||
authorizeFollowRequest,
|
||||
blockAccount,
|
||||
|
@ -1340,7 +1341,7 @@ describe('fetchRelationships()', () => {
|
|||
describe('without newAccountIds', () => {
|
||||
beforeEach(() => {
|
||||
const state = rootState
|
||||
.set('relationships', ImmutableMap({ [id]: normalizeRelationship({}) }))
|
||||
.set('relationships', ImmutableMap({ [id]: buildRelationship() }))
|
||||
.set('me', '123');
|
||||
store = mockStore(state);
|
||||
});
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
// import { Map as ImmutableMap } from 'immutable';
|
||||
import React from 'react';
|
||||
|
||||
import { render, screen } from '../../../../jest/test-helpers';
|
||||
import { normalizeAccount, normalizeRelationship } from '../../../../normalizers';
|
||||
import { buildRelationship } from 'soapbox/jest/factory';
|
||||
import { render, screen } from 'soapbox/jest/test-helpers';
|
||||
import { normalizeAccount } from 'soapbox/normalizers';
|
||||
|
||||
import SubscribeButton from '../subscription-button';
|
||||
|
||||
import type { ReducerAccount } from 'soapbox/reducers/accounts';
|
||||
|
@ -19,162 +20,10 @@ describe('<SubscribeButton />', () => {
|
|||
|
||||
describe('with "accountNotifies" disabled', () => {
|
||||
it('renders nothing', () => {
|
||||
const account = normalizeAccount({ ...justin, relationship: normalizeRelationship({ following: true }) }) as ReducerAccount;
|
||||
const account = normalizeAccount({ ...justin, relationship: buildRelationship({ following: true }) }) as ReducerAccount;
|
||||
|
||||
render(<SubscribeButton account={account} />, undefined, store);
|
||||
expect(screen.queryAllByTestId('icon-button')).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
|
||||
// describe('with "accountNotifies" enabled', () => {
|
||||
// beforeEach(() => {
|
||||
// store = {
|
||||
// ...store,
|
||||
// instance: normalizeInstance({
|
||||
// version: '3.4.1 (compatible; TruthSocial 1.0.0)',
|
||||
// software: 'TRUTHSOCIAL',
|
||||
// pleroma: ImmutableMap({}),
|
||||
// }),
|
||||
// };
|
||||
// });
|
||||
|
||||
// describe('when the relationship is requested', () => {
|
||||
// beforeEach(() => {
|
||||
// account = normalizeAccount({ ...account, relationship: normalizeRelationship({ requested: true }) });
|
||||
|
||||
// store = {
|
||||
// ...store,
|
||||
// accounts: ImmutableMap({
|
||||
// '1': account,
|
||||
// }),
|
||||
// };
|
||||
// });
|
||||
|
||||
// it('renders the button', () => {
|
||||
// render(<SubscribeButton account={account} />, null, store);
|
||||
// expect(screen.getByTestId('icon-button')).toBeInTheDocument();
|
||||
// });
|
||||
|
||||
// describe('when the user "isSubscribed"', () => {
|
||||
// beforeEach(() => {
|
||||
// account = normalizeAccount({
|
||||
// ...account,
|
||||
// relationship: normalizeRelationship({ requested: true, notifying: true }),
|
||||
// });
|
||||
|
||||
// store = {
|
||||
// ...store,
|
||||
// accounts: ImmutableMap({
|
||||
// '1': account,
|
||||
// }),
|
||||
// };
|
||||
// });
|
||||
|
||||
// it('renders the unsubscribe button', () => {
|
||||
// render(<SubscribeButton account={account} />, null, store);
|
||||
// expect(screen.getByTestId('icon-button').title).toEqual(`Unsubscribe to notifications from @${account.acct}`);
|
||||
// });
|
||||
// });
|
||||
|
||||
// describe('when the user is not "isSubscribed"', () => {
|
||||
// beforeEach(() => {
|
||||
// account = normalizeAccount({
|
||||
// ...account,
|
||||
// relationship: normalizeRelationship({ requested: true, notifying: false }),
|
||||
// });
|
||||
|
||||
// store = {
|
||||
// ...store,
|
||||
// accounts: ImmutableMap({
|
||||
// '1': account,
|
||||
// }),
|
||||
// };
|
||||
// });
|
||||
|
||||
// it('renders the unsubscribe button', () => {
|
||||
// render(<SubscribeButton account={account} />, null, store);
|
||||
// expect(screen.getByTestId('icon-button').title).toEqual(`Subscribe to notifications from @${account.acct}`);
|
||||
// });
|
||||
// });
|
||||
// });
|
||||
|
||||
// describe('when the user is not following the account', () => {
|
||||
// beforeEach(() => {
|
||||
// account = normalizeAccount({ ...account, relationship: normalizeRelationship({ following: false }) });
|
||||
|
||||
// store = {
|
||||
// ...store,
|
||||
// accounts: ImmutableMap({
|
||||
// '1': account,
|
||||
// }),
|
||||
// };
|
||||
// });
|
||||
|
||||
// it('renders nothing', () => {
|
||||
// render(<SubscribeButton account={account} />, null, store);
|
||||
// expect(screen.queryAllByTestId('icon-button')).toHaveLength(0);
|
||||
// });
|
||||
// });
|
||||
|
||||
// describe('when the user is following the account', () => {
|
||||
// beforeEach(() => {
|
||||
// account = normalizeAccount({ ...account, relationship: normalizeRelationship({ following: true }) });
|
||||
|
||||
// store = {
|
||||
// ...store,
|
||||
// accounts: ImmutableMap({
|
||||
// '1': account,
|
||||
// }),
|
||||
// };
|
||||
// });
|
||||
|
||||
// it('renders the button', () => {
|
||||
// render(<SubscribeButton account={account} />, null, store);
|
||||
// expect(screen.getByTestId('icon-button')).toBeInTheDocument();
|
||||
// });
|
||||
|
||||
// describe('when the user "isSubscribed"', () => {
|
||||
// beforeEach(() => {
|
||||
// account = normalizeAccount({
|
||||
// ...account,
|
||||
// relationship: normalizeRelationship({ requested: true, notifying: true }),
|
||||
// });
|
||||
|
||||
// store = {
|
||||
// ...store,
|
||||
// accounts: ImmutableMap({
|
||||
// '1': account,
|
||||
// }),
|
||||
// };
|
||||
// });
|
||||
|
||||
// it('renders the unsubscribe button', () => {
|
||||
// render(<SubscribeButton account={account} />, null, store);
|
||||
// expect(screen.getByTestId('icon-button').title).toEqual(`Unsubscribe to notifications from @${account.acct}`);
|
||||
// });
|
||||
// });
|
||||
|
||||
// describe('when the user is not "isSubscribed"', () => {
|
||||
// beforeEach(() => {
|
||||
// account = normalizeAccount({
|
||||
// ...account,
|
||||
// relationship: normalizeRelationship({ requested: true, notifying: false }),
|
||||
// });
|
||||
|
||||
// store = {
|
||||
// ...store,
|
||||
// accounts: ImmutableMap({
|
||||
// '1': account,
|
||||
// }),
|
||||
// };
|
||||
// });
|
||||
|
||||
// it('renders the unsubscribe button', () => {
|
||||
// render(<SubscribeButton account={account} />, null, store);
|
||||
// expect(screen.getByTestId('icon-button').title).toEqual(`Subscribe to notifications from @${account.acct}`);
|
||||
// });
|
||||
// });
|
||||
// });
|
||||
// });
|
||||
|
||||
});
|
||||
|
|
|
@ -6,11 +6,13 @@ import {
|
|||
groupSchema,
|
||||
groupRelationshipSchema,
|
||||
groupTagSchema,
|
||||
relationshipSchema,
|
||||
type Ad,
|
||||
type Card,
|
||||
type Group,
|
||||
type GroupRelationship,
|
||||
type GroupTag,
|
||||
type Relationship,
|
||||
} from 'soapbox/schemas';
|
||||
|
||||
// TODO: there's probably a better way to create these factory functions.
|
||||
|
@ -46,4 +48,17 @@ function buildAd(props: Partial<Ad> = {}): Ad {
|
|||
}, props));
|
||||
}
|
||||
|
||||
export { buildCard, buildGroup, buildGroupRelationship, buildGroupTag, buildAd };
|
||||
function buildRelationship(props: Partial<Relationship> = {}): Relationship {
|
||||
return relationshipSchema.parse(Object.assign({
|
||||
id: uuidv4(),
|
||||
}, props));
|
||||
}
|
||||
|
||||
export {
|
||||
buildCard,
|
||||
buildGroup,
|
||||
buildGroupRelationship,
|
||||
buildGroupTag,
|
||||
buildAd,
|
||||
buildRelationship,
|
||||
};
|
|
@ -20,7 +20,6 @@ export { LocationRecord, normalizeLocation } from './location';
|
|||
export { MentionRecord, normalizeMention } from './mention';
|
||||
export { NotificationRecord, normalizeNotification } from './notification';
|
||||
export { PollRecord, PollOptionRecord, normalizePoll } from './poll';
|
||||
export { RelationshipRecord, normalizeRelationship } from './relationship';
|
||||
export { StatusRecord, normalizeStatus } from './status';
|
||||
export { StatusEditRecord, normalizeStatusEdit } from './status-edit';
|
||||
export { TagRecord, normalizeTag } from './tag';
|
||||
|
|
|
@ -1,35 +0,0 @@
|
|||
/**
|
||||
* Relationship normalizer:
|
||||
* Converts API relationships into our internal format.
|
||||
* @see {@link https://docs.joinmastodon.org/entities/relationship/}
|
||||
*/
|
||||
import {
|
||||
Map as ImmutableMap,
|
||||
Record as ImmutableRecord,
|
||||
fromJS,
|
||||
} from 'immutable';
|
||||
|
||||
// https://docs.joinmastodon.org/entities/relationship/
|
||||
// https://api.pleroma.social/#operation/AccountController.relationships
|
||||
export const RelationshipRecord = ImmutableRecord({
|
||||
blocked_by: false,
|
||||
blocking: false,
|
||||
domain_blocking: false,
|
||||
endorsed: false,
|
||||
followed_by: false,
|
||||
following: false,
|
||||
id: '',
|
||||
muting: false,
|
||||
muting_notifications: false,
|
||||
note: '',
|
||||
notifying: false,
|
||||
requested: false,
|
||||
showing_reblogs: false,
|
||||
subscribing: false,
|
||||
});
|
||||
|
||||
export const normalizeRelationship = (relationship: Record<string, any>) => {
|
||||
return RelationshipRecord(
|
||||
ImmutableMap(fromJS(relationship)),
|
||||
);
|
||||
};
|
|
@ -3,8 +3,9 @@ import sumBy from 'lodash/sumBy';
|
|||
import { useEffect } from 'react';
|
||||
|
||||
import { __stub } from 'soapbox/api';
|
||||
import { buildRelationship } from 'soapbox/jest/factory';
|
||||
import { createTestStore, mockStore, queryClient, renderHook, rootState, waitFor } from 'soapbox/jest/test-helpers';
|
||||
import { normalizeChatMessage, normalizeRelationship } from 'soapbox/normalizers';
|
||||
import { normalizeChatMessage } from 'soapbox/normalizers';
|
||||
import { normalizeEmojiReaction } from 'soapbox/normalizers/emoji-reaction';
|
||||
import { Store } from 'soapbox/store';
|
||||
import { ChatMessage } from 'soapbox/types/entities';
|
||||
|
@ -120,7 +121,7 @@ describe('useChatMessages', () => {
|
|||
const state = rootState
|
||||
.set(
|
||||
'relationships',
|
||||
ImmutableMap({ '1': normalizeRelationship({ blocked_by: true }) }),
|
||||
ImmutableMap({ '1': buildRelationship({ blocked_by: true }) }),
|
||||
);
|
||||
store = mockStore(state);
|
||||
});
|
||||
|
@ -239,7 +240,7 @@ describe('useChat()', () => {
|
|||
mock.onGet(`/api/v1/pleroma/chats/${chat.id}`).reply(200, chat);
|
||||
mock
|
||||
.onGet(`/api/v1/accounts/relationships?id[]=${chat.account.id}`)
|
||||
.reply(200, [normalizeRelationship({ id: relationshipId, blocked_by: true })]);
|
||||
.reply(200, [buildRelationship({ id: relationshipId, blocked_by: true })]);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import { useEffect } from 'react';
|
||||
|
||||
import { __stub } from 'soapbox/api';
|
||||
import { buildRelationship } from 'soapbox/jest/factory';
|
||||
import { createTestStore, queryClient, renderHook, rootState, waitFor } from 'soapbox/jest/test-helpers';
|
||||
import { normalizeRelationship } from 'soapbox/normalizers';
|
||||
import { Store } from 'soapbox/store';
|
||||
|
||||
import { useFetchRelationships } from '../relationships';
|
||||
|
@ -25,7 +25,7 @@ describe('useFetchRelationships()', () => {
|
|||
__stub((mock) => {
|
||||
mock
|
||||
.onGet(`/api/v1/accounts/relationships?id[]=${id}`)
|
||||
.reply(200, [normalizeRelationship({ id, blocked_by: true })]);
|
||||
.reply(200, [buildRelationship({ id, blocked_by: true })]);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -55,7 +55,7 @@ describe('useFetchRelationships()', () => {
|
|||
__stub((mock) => {
|
||||
mock
|
||||
.onGet(`/api/v1/accounts/relationships?id[]=${ids[0]}&id[]=${ids[1]}`)
|
||||
.reply(200, ids.map((id) => normalizeRelationship({ id, blocked_by: true })));
|
||||
.reply(200, ids.map((id) => buildRelationship({ id, blocked_by: true })));
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ import { List as ImmutableList, Map as ImmutableMap } from 'immutable';
|
|||
import get from 'lodash/get';
|
||||
|
||||
import { STREAMING_FOLLOW_RELATIONSHIPS_UPDATE } from 'soapbox/actions/streaming';
|
||||
import { normalizeRelationship } from 'soapbox/normalizers/relationship';
|
||||
import { type Relationship, relationshipSchema } from 'soapbox/schemas';
|
||||
|
||||
import { ACCOUNT_NOTE_SUBMIT_SUCCESS } from '../actions/account-notes';
|
||||
import {
|
||||
|
@ -35,13 +35,16 @@ import {
|
|||
import type { AnyAction } from 'redux';
|
||||
import type { APIEntity } from 'soapbox/types/entities';
|
||||
|
||||
type Relationship = ReturnType<typeof normalizeRelationship>;
|
||||
type State = ImmutableMap<string, Relationship>;
|
||||
type APIEntities = Array<APIEntity>;
|
||||
|
||||
const normalizeRelationships = (state: State, relationships: APIEntities) => {
|
||||
relationships.forEach(relationship => {
|
||||
state = state.set(relationship.id, normalizeRelationship(relationship));
|
||||
try {
|
||||
state = state.set(relationship.id, relationshipSchema.parse(relationship));
|
||||
} catch (_e) {
|
||||
// do nothing
|
||||
}
|
||||
});
|
||||
|
||||
return state;
|
||||
|
@ -84,8 +87,12 @@ const followStateToRelationship = (followState: string) => {
|
|||
};
|
||||
|
||||
const updateFollowRelationship = (state: State, id: string, followState: string) => {
|
||||
const map = followStateToRelationship(followState);
|
||||
return state.update(id, normalizeRelationship({}), relationship => relationship.merge(map));
|
||||
const relationship = state.get(id) || relationshipSchema.parse({ id });
|
||||
|
||||
return state.set(id, {
|
||||
...relationship,
|
||||
...followStateToRelationship(followState),
|
||||
});
|
||||
};
|
||||
|
||||
export default function relationships(state: State = ImmutableMap<string, Relationship>(), action: AnyAction) {
|
||||
|
|
|
@ -19,4 +19,4 @@ const relationshipSchema = z.object({
|
|||
|
||||
type Relationship = z.infer<typeof relationshipSchema>;
|
||||
|
||||
export { relationshipSchema, Relationship };
|
||||
export { relationshipSchema, type Relationship };
|
|
@ -21,7 +21,6 @@ import {
|
|||
NotificationRecord,
|
||||
PollRecord,
|
||||
PollOptionRecord,
|
||||
RelationshipRecord,
|
||||
StatusEditRecord,
|
||||
StatusRecord,
|
||||
TagRecord,
|
||||
|
@ -52,7 +51,6 @@ type Mention = ReturnType<typeof MentionRecord>;
|
|||
type Notification = ReturnType<typeof NotificationRecord>;
|
||||
type Poll = ReturnType<typeof PollRecord>;
|
||||
type PollOption = ReturnType<typeof PollOptionRecord>;
|
||||
type Relationship = ReturnType<typeof RelationshipRecord>;
|
||||
type StatusEdit = ReturnType<typeof StatusEditRecord>;
|
||||
type Tag = ReturnType<typeof TagRecord>;
|
||||
|
||||
|
@ -96,7 +94,6 @@ export {
|
|||
Notification,
|
||||
Poll,
|
||||
PollOption,
|
||||
Relationship,
|
||||
Status,
|
||||
StatusEdit,
|
||||
Tag,
|
||||
|
@ -111,4 +108,5 @@ export type {
|
|||
Group,
|
||||
GroupMember,
|
||||
GroupRelationship,
|
||||
Relationship,
|
||||
} from 'soapbox/schemas';
|
Loading…
Reference in a new issue