Remove normalizeAd
This commit is contained in:
parent
81de7c268e
commit
54d8d12054
|
@ -74,7 +74,7 @@ const importFetchedGroup = (group: APIEntity) =>
|
|||
importFetchedGroups([group]);
|
||||
|
||||
const importFetchedGroups = (groups: APIEntity[]) => {
|
||||
const entities = filteredArray(groupSchema).catch([]).parse(groups);
|
||||
const entities = filteredArray(groupSchema).parse(groups);
|
||||
return importGroups(entities);
|
||||
};
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ import React from 'react';
|
|||
|
||||
import { buildGroup, buildGroupRelationship } from 'soapbox/jest/factory';
|
||||
import { render, screen } from 'soapbox/jest/test-helpers';
|
||||
import { GroupRoles } from 'soapbox/schemas/group-member';
|
||||
import { Group } from 'soapbox/types/entities';
|
||||
|
||||
import GroupActionButton from '../group-action-button';
|
||||
|
@ -45,7 +46,7 @@ describe('<GroupActionButton />', () => {
|
|||
beforeEach(() => {
|
||||
group = buildGroup({
|
||||
relationship: buildGroupRelationship({
|
||||
member: null,
|
||||
member: false,
|
||||
}),
|
||||
});
|
||||
});
|
||||
|
@ -98,7 +99,7 @@ describe('<GroupActionButton />', () => {
|
|||
relationship: buildGroupRelationship({
|
||||
requested: false,
|
||||
member: true,
|
||||
role: 'owner',
|
||||
role: GroupRoles.OWNER,
|
||||
}),
|
||||
});
|
||||
});
|
||||
|
@ -116,7 +117,7 @@ describe('<GroupActionButton />', () => {
|
|||
relationship: buildGroupRelationship({
|
||||
requested: false,
|
||||
member: true,
|
||||
role: 'user',
|
||||
role: GroupRoles.USER,
|
||||
}),
|
||||
});
|
||||
});
|
||||
|
|
|
@ -17,7 +17,7 @@ describe('<GroupOptionsButton />', () => {
|
|||
requested: false,
|
||||
member: true,
|
||||
blocked_by: true,
|
||||
role: 'user',
|
||||
role: GroupRoles.USER,
|
||||
}),
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,9 +1,13 @@
|
|||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
import {
|
||||
adSchema,
|
||||
cardSchema,
|
||||
groupSchema,
|
||||
groupRelationshipSchema,
|
||||
groupTagSchema,
|
||||
type Ad,
|
||||
type Card,
|
||||
type Group,
|
||||
type GroupRelationship,
|
||||
type GroupTag,
|
||||
|
@ -12,22 +16,34 @@ import {
|
|||
// TODO: there's probably a better way to create these factory functions.
|
||||
// This looks promising but didn't work on my first attempt: https://github.com/anatine/zod-plugins/tree/main/packages/zod-mock
|
||||
|
||||
function buildGroup(props: Record<string, any> = {}): Group {
|
||||
function buildCard(props: Partial<Card> = {}): Card {
|
||||
return cardSchema.parse(Object.assign({
|
||||
url: 'https://soapbox.test',
|
||||
}, props));
|
||||
}
|
||||
|
||||
function buildGroup(props: Partial<Group> = {}): Group {
|
||||
return groupSchema.parse(Object.assign({
|
||||
id: uuidv4(),
|
||||
}, props));
|
||||
}
|
||||
|
||||
function buildGroupRelationship(props: Record<string, any> = {}): GroupRelationship {
|
||||
function buildGroupRelationship(props: Partial<GroupRelationship> = {}): GroupRelationship {
|
||||
return groupRelationshipSchema.parse(Object.assign({
|
||||
id: uuidv4(),
|
||||
}, props));
|
||||
}
|
||||
|
||||
function buildGroupTag(props: Record<string, any> = {}): GroupTag {
|
||||
function buildGroupTag(props: Partial<GroupTag> = {}): GroupTag {
|
||||
return groupTagSchema.parse(Object.assign({
|
||||
id: uuidv4(),
|
||||
}, props));
|
||||
}
|
||||
|
||||
export { buildGroup, buildGroupRelationship, buildGroupTag };
|
||||
function buildAd(props: Partial<Ad> = {}): Ad {
|
||||
return adSchema.parse(Object.assign({
|
||||
card: buildCard(),
|
||||
}, props));
|
||||
}
|
||||
|
||||
export { buildCard, buildGroup, buildGroupRelationship, buildGroupTag, buildAd };
|
|
@ -26,5 +26,4 @@ export { StatusRecord, normalizeStatus } from './status';
|
|||
export { StatusEditRecord, normalizeStatusEdit } from './status-edit';
|
||||
export { TagRecord, normalizeTag } from './tag';
|
||||
|
||||
export { AdRecord, normalizeAd } from './soapbox/ad';
|
||||
export { SoapboxConfigRecord, normalizeSoapboxConfig } from './soapbox/soapbox-config';
|
||||
|
|
|
@ -1,28 +0,0 @@
|
|||
import {
|
||||
Map as ImmutableMap,
|
||||
Record as ImmutableRecord,
|
||||
fromJS,
|
||||
} from 'immutable';
|
||||
|
||||
import { normalizeCard } from '../card';
|
||||
|
||||
import type { Ad } from 'soapbox/features/ads/providers';
|
||||
|
||||
export const AdRecord = ImmutableRecord<Ad>({
|
||||
card: normalizeCard({}),
|
||||
impression: undefined as string | undefined,
|
||||
expires_at: undefined as string | undefined,
|
||||
reason: undefined as string | undefined,
|
||||
});
|
||||
|
||||
/** Normalizes an ad from Soapbox Config. */
|
||||
export const normalizeAd = (ad: Record<string, any>) => {
|
||||
const map = ImmutableMap<string, any>(fromJS(ad));
|
||||
const card = normalizeCard(map.get('card').toJS());
|
||||
const expiresAt = map.get('expires_at') || map.get('expires');
|
||||
|
||||
return AdRecord(map.merge({
|
||||
card,
|
||||
expires_at: expiresAt,
|
||||
}));
|
||||
};
|
|
@ -6,12 +6,12 @@ import {
|
|||
} from 'immutable';
|
||||
import trimStart from 'lodash/trimStart';
|
||||
|
||||
import { adSchema } from 'soapbox/schemas';
|
||||
import { filteredArray } from 'soapbox/schemas/utils';
|
||||
import { normalizeUsername } from 'soapbox/utils/input';
|
||||
import { toTailwind } from 'soapbox/utils/tailwind';
|
||||
import { generateAccent } from 'soapbox/utils/theme';
|
||||
|
||||
import { normalizeAd } from './ad';
|
||||
|
||||
import type {
|
||||
Ad,
|
||||
PromoPanelItem,
|
||||
|
@ -125,8 +125,12 @@ export const SoapboxConfigRecord = ImmutableRecord({
|
|||
type SoapboxConfigMap = ImmutableMap<string, any>;
|
||||
|
||||
const normalizeAds = (soapboxConfig: SoapboxConfigMap): SoapboxConfigMap => {
|
||||
const ads = ImmutableList<Record<string, any>>(soapboxConfig.get('ads'));
|
||||
return soapboxConfig.set('ads', ads.map(normalizeAd));
|
||||
if (soapboxConfig.has('ads')) {
|
||||
const ads = filteredArray(adSchema).parse(soapboxConfig.get('ads').toJS());
|
||||
return soapboxConfig.set('ads', ads);
|
||||
} else {
|
||||
return soapboxConfig;
|
||||
}
|
||||
};
|
||||
|
||||
const normalizeCryptoAddress = (address: unknown): CryptoAddress => {
|
||||
|
|
|
@ -2,7 +2,8 @@ import { useQuery } from '@tanstack/react-query';
|
|||
|
||||
import { Ad, getProvider } from 'soapbox/features/ads/providers';
|
||||
import { useAppDispatch } from 'soapbox/hooks';
|
||||
import { normalizeAd } from 'soapbox/normalizers';
|
||||
import { adSchema } from 'soapbox/schemas';
|
||||
import { filteredArray } from 'soapbox/schemas/utils';
|
||||
import { isExpired } from 'soapbox/utils/ads';
|
||||
|
||||
const AdKeys = {
|
||||
|
@ -28,7 +29,9 @@ function useAds() {
|
|||
});
|
||||
|
||||
// Filter out expired ads.
|
||||
const data = result.data?.map(normalizeAd).filter(ad => !isExpired(ad));
|
||||
const data = filteredArray(adSchema)
|
||||
.parse(result.data)
|
||||
.filter(ad => !isExpired(ad));
|
||||
|
||||
return {
|
||||
...result,
|
||||
|
|
|
@ -22,7 +22,7 @@ const accountSchema = z.object({
|
|||
created_at: z.string().datetime().catch(new Date().toUTCString()),
|
||||
discoverable: z.boolean().catch(false),
|
||||
display_name: z.string().catch(''),
|
||||
emojis: filteredArray(customEmojiSchema).catch([]),
|
||||
emojis: filteredArray(customEmojiSchema),
|
||||
favicon: z.string().catch(''),
|
||||
fields: z.any(), // TODO
|
||||
followers_count: z.number().catch(0),
|
||||
|
|
|
@ -19,7 +19,7 @@ const groupSchema = z.object({
|
|||
deleted_at: z.string().datetime().or(z.null()).catch(null),
|
||||
display_name: z.string().catch(''),
|
||||
domain: z.string().catch(''),
|
||||
emojis: filteredArray(customEmojiSchema).catch([]),
|
||||
emojis: filteredArray(customEmojiSchema),
|
||||
group_visibility: z.string().catch(''), // TruthSocial
|
||||
header: z.string().catch(headerMissing),
|
||||
header_static: z.string().catch(''),
|
||||
|
|
|
@ -1,22 +1,11 @@
|
|||
/**
|
||||
* Schemas
|
||||
*/
|
||||
export { accountSchema } from './account';
|
||||
export { customEmojiSchema } from './custom-emoji';
|
||||
export { groupSchema } from './group';
|
||||
export { groupMemberSchema } from './group-member';
|
||||
export { groupRelationshipSchema } from './group-relationship';
|
||||
export { groupTagSchema } from './group-tag';
|
||||
export { relationshipSchema } from './relationship';
|
||||
export { accountSchema, type Account } from './account';
|
||||
export { cardSchema, type Card } from './card';
|
||||
export { customEmojiSchema, type CustomEmoji } from './custom-emoji';
|
||||
export { groupSchema, type Group } from './group';
|
||||
export { groupMemberSchema, type GroupMember } from './group-member';
|
||||
export { groupRelationshipSchema, type GroupRelationship } from './group-relationship';
|
||||
export { groupTagSchema, type GroupTag } from './group-tag';
|
||||
export { relationshipSchema, type Relationship } from './relationship';
|
||||
|
||||
/**
|
||||
* Entity Types
|
||||
*/
|
||||
export type { Account } from './account';
|
||||
export type { Card } from './card';
|
||||
export type { CustomEmoji } from './custom-emoji';
|
||||
export type { Group } from './group';
|
||||
export type { GroupMember } from './group-member';
|
||||
export type { GroupRelationship } from './group-relationship';
|
||||
export type { GroupTag } from './group-tag';
|
||||
export type { Relationship } from './relationship';
|
||||
// Soapbox
|
||||
export { adSchema, type Ad } from './soapbox/ad';
|
|
@ -0,0 +1,14 @@
|
|||
import { z } from 'zod';
|
||||
|
||||
import { cardSchema } from '../card';
|
||||
|
||||
const adSchema = z.object({
|
||||
card: cardSchema,
|
||||
impression: z.string().optional().catch(undefined),
|
||||
expires_at: z.string().optional().catch(undefined),
|
||||
reason: z.string().optional().catch(undefined),
|
||||
});
|
||||
|
||||
type Ad = z.infer<typeof adSchema>;
|
||||
|
||||
export { adSchema, type Ad };
|
|
@ -4,7 +4,7 @@ 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()
|
||||
return z.any().array().catch([])
|
||||
.transform((arr) => (
|
||||
arr.map((item) => {
|
||||
const parsed = schema.safeParse(item);
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import { AdRecord } from 'soapbox/normalizers/soapbox/ad';
|
||||
import {
|
||||
PromoPanelItemRecord,
|
||||
FooterItemRecord,
|
||||
|
@ -8,7 +7,6 @@ import {
|
|||
|
||||
type Me = string | null | false | undefined;
|
||||
|
||||
type Ad = ReturnType<typeof AdRecord>;
|
||||
type PromoPanelItem = ReturnType<typeof PromoPanelItemRecord>;
|
||||
type FooterItem = ReturnType<typeof FooterItemRecord>;
|
||||
type CryptoAddress = ReturnType<typeof CryptoAddressRecord>;
|
||||
|
@ -16,9 +14,12 @@ type SoapboxConfig = ReturnType<typeof SoapboxConfigRecord>;
|
|||
|
||||
export {
|
||||
Me,
|
||||
Ad,
|
||||
PromoPanelItem,
|
||||
FooterItem,
|
||||
CryptoAddress,
|
||||
SoapboxConfig,
|
||||
};
|
||||
|
||||
export type {
|
||||
Ad,
|
||||
} from 'soapbox/schemas';
|
|
@ -1,4 +1,4 @@
|
|||
import { normalizeAd } from 'soapbox/normalizers';
|
||||
import { buildAd } from 'soapbox/jest/factory';
|
||||
|
||||
import { isExpired } from '../ads';
|
||||
|
||||
|
@ -14,10 +14,10 @@ test('isExpired()', () => {
|
|||
const epoch = now.getTime();
|
||||
|
||||
// Sanity tests.
|
||||
expect(isExpired(normalizeAd({ expires_at: iso }))).toBe(true);
|
||||
expect(isExpired(normalizeAd({ expires_at: new Date(epoch + 999999).toISOString() }))).toBe(false);
|
||||
expect(isExpired(buildAd({ expires_at: iso }))).toBe(true);
|
||||
expect(isExpired(buildAd({ expires_at: new Date(epoch + 999999).toISOString() }))).toBe(false);
|
||||
|
||||
// Testing the 5-minute mark.
|
||||
expect(isExpired(normalizeAd({ expires_at: new Date(epoch + threeMins).toISOString() }), fiveMins)).toBe(true);
|
||||
expect(isExpired(normalizeAd({ expires_at: new Date(epoch + fiveMins + 1000).toISOString() }), fiveMins)).toBe(false);
|
||||
expect(isExpired(buildAd({ expires_at: new Date(epoch + threeMins).toISOString() }), fiveMins)).toBe(true);
|
||||
expect(isExpired(buildAd({ expires_at: new Date(epoch + fiveMins + 1000).toISOString() }), fiveMins)).toBe(false);
|
||||
});
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import type { Ad } from 'soapbox/types/soapbox';
|
||||
import type { Ad } from 'soapbox/schemas';
|
||||
|
||||
/** Time (ms) window to not display an ad if it's about to expire. */
|
||||
const AD_EXPIRY_THRESHOLD = 5 * 60 * 1000;
|
||||
|
|
Loading…
Reference in New Issue