Added chronology events, removed eras
This commit is contained in:
		
							parent
							
								
									e91642616d
								
							
						
					
					
						commit
						4fd59464ba
					
				| @ -1,87 +0,0 @@ | ||||
| import { CollectionConfig } from "payload/types"; | ||||
| import { mustBeAdmin } from "../../accesses/collections/mustBeAdmin"; | ||||
| import { CollectionGroups, Collections } from "../../constants"; | ||||
| import { backPropagationField } from "../../fields/backPropagationField/backPropagationField"; | ||||
| import { rowField } from "../../fields/rowField/rowField"; | ||||
| import { slugField } from "../../fields/slugField/slugField"; | ||||
| import { translatedFields } from "../../fields/translatedFields/translatedFields"; | ||||
| import { buildCollectionConfig } from "../../utils/collectionConfig"; | ||||
| import { createEditor } from "../../utils/editor"; | ||||
| import { getAllEndpoint } from "./endpoints/getAllEndpoint"; | ||||
| import { importFromStrapi } from "./endpoints/importFromStrapi"; | ||||
| import { beforeValidateEndingGreaterThanStarting } from "./hooks/beforeValidateEndingGreaterThanStarting"; | ||||
| import { beforeValidateNoIntersection } from "./hooks/beforeValidateNoIntersection"; | ||||
| 
 | ||||
| const fields = { | ||||
|   slug: "slug", | ||||
|   startingYear: "startingYear", | ||||
|   endingYear: "endingYear", | ||||
|   translations: "translations", | ||||
|   translationsTitle: "title", | ||||
|   translationsDescription: "description", | ||||
|   events: "events", | ||||
| } as const satisfies Record<string, string>; | ||||
| 
 | ||||
| export const ChronologyEras: CollectionConfig = buildCollectionConfig({ | ||||
|   slug: Collections.ChronologyEras, | ||||
|   labels: { | ||||
|     singular: "Chronology Era", | ||||
|     plural: "Chronology Eras", | ||||
|   }, | ||||
|   defaultSort: fields.startingYear, | ||||
|   admin: { | ||||
|     group: CollectionGroups.Collections, | ||||
|     defaultColumns: [fields.slug, fields.startingYear, fields.endingYear, fields.translations], | ||||
|     useAsTitle: fields.slug, | ||||
|   }, | ||||
|   access: { | ||||
|     create: mustBeAdmin, | ||||
|     delete: mustBeAdmin, | ||||
|   }, | ||||
|   hooks: { | ||||
|     beforeValidate: [beforeValidateEndingGreaterThanStarting, beforeValidateNoIntersection], | ||||
|   }, | ||||
|   endpoints: [importFromStrapi, getAllEndpoint], | ||||
|   fields: [ | ||||
|     slugField({ name: fields.slug }), | ||||
|     rowField([ | ||||
|       { | ||||
|         name: fields.startingYear, | ||||
|         type: "number", | ||||
|         min: 0, | ||||
|         required: true, | ||||
|         admin: { description: "The year the era started (year included)" }, | ||||
|       }, | ||||
|       { | ||||
|         name: fields.endingYear, | ||||
|         type: "number", | ||||
|         min: 0, | ||||
|         required: true, | ||||
|         admin: { description: "The year the era ended (year included)" }, | ||||
|       }, | ||||
|     ]), | ||||
|     translatedFields({ | ||||
|       name: fields.translations, | ||||
|       admin: { useAsTitle: fields.translationsTitle }, | ||||
|       fields: [ | ||||
|         { name: fields.translationsTitle, type: "text", required: true }, | ||||
|         { | ||||
|           name: fields.translationsDescription, | ||||
|           type: "richText", | ||||
|           editor: createEditor({ inlines: true, lists: true, links: true }), | ||||
|         }, | ||||
|       ], | ||||
|     }), | ||||
|     backPropagationField({ | ||||
|       name: fields.events, | ||||
|       hasMany: true, | ||||
|       relationTo: Collections.ChronologyItems, | ||||
|       where: ({ startingYear, endingYear }) => ({ | ||||
|         and: [ | ||||
|           { "date.year": { greater_than_equal: startingYear } }, | ||||
|           { "date.year": { less_than_equal: endingYear } }, | ||||
|         ], | ||||
|       }), | ||||
|     }), | ||||
|   ], | ||||
| }); | ||||
| @ -1,99 +0,0 @@ | ||||
| import payload from "payload"; | ||||
| import { Collections } from "../../../constants"; | ||||
| import { EndpointEra } from "../../../sdk"; | ||||
| import { ChronologyEra, ChronologyItem } from "../../../types/collections"; | ||||
| import { CollectionEndpoint } from "../../../types/payload"; | ||||
| import { isDefined, isPayloadArrayType, isPayloadType } from "../../../utils/asserts"; | ||||
| import { handleRecorder } from "../../../utils/endpoints"; | ||||
| 
 | ||||
| export const getAllEndpoint: CollectionEndpoint = { | ||||
|   method: "get", | ||||
|   path: "/all", | ||||
|   handler: async (req, res) => { | ||||
|     if (!req.user) { | ||||
|       return res.status(403).send({ | ||||
|         errors: [ | ||||
|           { | ||||
|             message: "You are not allowed to perform this action.", | ||||
|           }, | ||||
|         ], | ||||
|       }); | ||||
|     } | ||||
| 
 | ||||
|     const eras: ChronologyEra[] = ( | ||||
|       await payload.find({ | ||||
|         collection: Collections.ChronologyEras, | ||||
|         pagination: false, | ||||
|       }) | ||||
|     ).docs; | ||||
| 
 | ||||
|     const result = eras.map<EndpointEra>( | ||||
|       ({ endingYear, startingYear, slug, translations, events: items }) => ({ | ||||
|         slug, | ||||
|         startingYear, | ||||
|         endingYear, | ||||
|         translations: | ||||
|           translations?.map(({ language, title, description }) => ({ | ||||
|             language: isPayloadType(language) ? language.id : language, | ||||
|             title, | ||||
|             ...(description ? { description } : {}), | ||||
|           })) ?? [], | ||||
|         items: | ||||
|           items | ||||
|             ?.filter(isPayloadType<ChronologyItem>) | ||||
|             .sort((a, b) => { | ||||
|               const aYear = a.date.year; | ||||
|               const bYear = b.date.year; | ||||
|               if (aYear !== bYear) return aYear - bYear; | ||||
|               const aMonth = a.date.month ?? 0; | ||||
|               const bMonth = b.date.month ?? 0; | ||||
|               if (aMonth !== bMonth) return aMonth - bMonth; | ||||
|               const aDay = a.date.day ?? 0; | ||||
|               const bDay = b.date.day ?? 0; | ||||
|               if (aDay !== bDay) return aDay - bDay; | ||||
|               return 0; | ||||
|             }) | ||||
|             .map(({ events, date: { year, day, month } }) => ({ | ||||
|               date: { | ||||
|                 year, | ||||
|                 ...(isDefined(day) ? { day } : {}), | ||||
|                 ...(isDefined(month) ? { month } : {}), | ||||
|               }, | ||||
|               events: events.map(({ translations }) => ({ | ||||
|                 translations: translations.map( | ||||
|                   ({ | ||||
|                     language, | ||||
|                     sourceLanguage, | ||||
|                     description, | ||||
|                     notes, | ||||
|                     proofreaders, | ||||
|                     transcribers, | ||||
|                     translators, | ||||
|                     title, | ||||
|                   }) => ({ | ||||
|                     language: isPayloadType(language) ? language.id : language, | ||||
|                     sourceLanguage: isPayloadType(sourceLanguage) | ||||
|                       ? sourceLanguage.id | ||||
|                       : sourceLanguage, | ||||
|                     ...(title ? { title } : {}), | ||||
|                     ...(description ? { description } : {}), | ||||
|                     ...(notes ? { notes } : {}), | ||||
|                     proofreaders: isPayloadArrayType(proofreaders) | ||||
|                       ? proofreaders.map(handleRecorder) | ||||
|                       : [], | ||||
|                     transcribers: isPayloadArrayType(transcribers) | ||||
|                       ? transcribers.map(handleRecorder) | ||||
|                       : [], | ||||
|                     translators: isPayloadArrayType(translators) | ||||
|                       ? translators.map(handleRecorder) | ||||
|                       : [], | ||||
|                   }) | ||||
|                 ), | ||||
|               })), | ||||
|             })) ?? [], | ||||
|       }) | ||||
|     ); | ||||
| 
 | ||||
|     res.status(200).json(result); | ||||
|   }, | ||||
| }; | ||||
| @ -1,38 +0,0 @@ | ||||
| import { Collections } from "../../../constants"; | ||||
| import { createStrapiImportEndpoint } from "../../../endpoints/createStrapiImportEndpoint"; | ||||
| import { StrapiLanguage } from "../../../types/strapi"; | ||||
| import { isDefined, isUndefined } from "../../../utils/asserts"; | ||||
| import { plainTextToLexical } from "../../../utils/string"; | ||||
| 
 | ||||
| type StrapiChronologyEra = { | ||||
|   slug: string; | ||||
|   starting_year: number; | ||||
|   ending_year: number; | ||||
|   title: { title: string; language: StrapiLanguage; description?: string }[]; | ||||
| }; | ||||
| 
 | ||||
| export const importFromStrapi = createStrapiImportEndpoint<StrapiChronologyEra>({ | ||||
|   strapi: { | ||||
|     collection: "chronology-eras", | ||||
|     params: { | ||||
|       populate: { title: { populate: "language" } }, | ||||
|     }, | ||||
|   }, | ||||
|   payload: { | ||||
|     collection: Collections.ChronologyEras, | ||||
|     convert: ({ slug, starting_year, ending_year, title: titles }) => ({ | ||||
|       slug, | ||||
|       startingYear: starting_year, | ||||
|       endingYear: ending_year, | ||||
|       translations: titles.map(({ language, title, description }) => { | ||||
|         if (isUndefined(language.data)) | ||||
|           throw new Error("Language is undefined for one of the translations"); | ||||
|         return { | ||||
|           language: language.data?.attributes.code, | ||||
|           title, | ||||
|           ...(isDefined(description) ? { description: plainTextToLexical(description) } : {}), | ||||
|         }; | ||||
|       }), | ||||
|     }), | ||||
|   }, | ||||
| }); | ||||
| @ -1,15 +0,0 @@ | ||||
| import { CollectionBeforeValidateHook } from "payload/types"; | ||||
| import { ChronologyEra } from "../../../types/collections"; | ||||
| import { isUndefined } from "../../../utils/asserts"; | ||||
| 
 | ||||
| export const beforeValidateEndingGreaterThanStarting: CollectionBeforeValidateHook< | ||||
|   ChronologyEra | ||||
| > = async ({ data }) => { | ||||
|   if (isUndefined(data)) throw new Error("The data is undefined"); | ||||
|   const { startingYear, endingYear } = data; | ||||
|   if (isUndefined(endingYear)) throw new Error("Ending year is undefined"); | ||||
|   if (isUndefined(startingYear)) throw new Error("Starting year is undefined"); | ||||
|   if (endingYear < startingYear) { | ||||
|     throw new Error("The ending year cannot be before the starting year."); | ||||
|   } | ||||
| }; | ||||
| @ -1,28 +0,0 @@ | ||||
| import payload from "payload"; | ||||
| import { CollectionBeforeValidateHook } from "payload/types"; | ||||
| import { Collections } from "../../../constants"; | ||||
| import { ChronologyEra } from "../../../types/collections"; | ||||
| import { hasIntersection, isUndefined } from "../../../utils/asserts"; | ||||
| 
 | ||||
| export const beforeValidateNoIntersection: CollectionBeforeValidateHook<ChronologyEra> = async ({ | ||||
|   data, | ||||
| }) => { | ||||
|   if (isUndefined(data)) throw new Error("The data is undefined"); | ||||
|   const { startingYear, endingYear } = data; | ||||
|   if (isUndefined(endingYear)) throw new Error("Ending year is undefined"); | ||||
|   if (isUndefined(startingYear)) throw new Error("Starting year is undefined"); | ||||
| 
 | ||||
|   const otherEras = await payload.find({ | ||||
|     collection: Collections.ChronologyEras, | ||||
|     limit: 100, | ||||
|   }); | ||||
| 
 | ||||
|   otherEras.docs.forEach((otherEra: ChronologyEra) => { | ||||
|     if (hasIntersection([startingYear, endingYear], [otherEra.startingYear, otherEra.endingYear])) { | ||||
|       throw new Error( | ||||
|         `This era (${startingYear} -> ${endingYear}) is intersecting with the era\ | ||||
|            "${otherEra.slug}" (${otherEra.startingYear} -> ${otherEra.endingYear})` | ||||
|       ); | ||||
|     } | ||||
|   }); | ||||
| }; | ||||
| @ -9,6 +9,11 @@ import { rowField } from "../../fields/rowField/rowField"; | ||||
| import { translatedFields } from "../../fields/translatedFields/translatedFields"; | ||||
| import { createEditor } from "../../utils/editor"; | ||||
| import { buildVersionedCollectionConfig } from "../../utils/versionedCollectionConfig"; | ||||
| import { collectibleBlock } from "./blocks/collectibleBlock"; | ||||
| import { pageBlock } from "./blocks/contentBlock"; | ||||
| import { urlBlock } from "./blocks/urlBlock"; | ||||
| import { getAllEndpoint } from "./endpoints/getAllEndpoint"; | ||||
| import { getByID } from "./endpoints/getByID"; | ||||
| import { importFromStrapi } from "./endpoints/importFromStrapi"; | ||||
| import { beforeValidatePopulateNameField } from "./hooks/beforeValidatePopulateNameField"; | ||||
| import { validateDate } from "./validations/validateDate"; | ||||
| @ -18,6 +23,7 @@ import { validateEventsTranslationsTitle } from "./validations/validateEventsTra | ||||
| const fields = { | ||||
|   name: "name", | ||||
|   events: "events", | ||||
|   eventsSources: "sources", | ||||
|   eventsTranslations: "translations", | ||||
|   eventsTranslationsTitle: "title", | ||||
|   eventsTranslationsDescription: "description", | ||||
| @ -26,15 +32,14 @@ const fields = { | ||||
|   dateYear: "year", | ||||
|   dateMonth: "month", | ||||
|   dateDay: "day", | ||||
|   era: "era", | ||||
|   status: "_status", | ||||
| } as const satisfies Record<string, string>; | ||||
| 
 | ||||
| export const ChronologyItems: CollectionConfig = buildVersionedCollectionConfig({ | ||||
|   slug: Collections.ChronologyItems, | ||||
| export const ChronologyEvents: CollectionConfig = buildVersionedCollectionConfig({ | ||||
|   slug: Collections.ChronologyEvents, | ||||
|   labels: { | ||||
|     singular: "Chronology Item", | ||||
|     plural: "Chronology Items", | ||||
|     singular: "Chronology Event", | ||||
|     plural: "Chronology Events", | ||||
|   }, | ||||
|   defaultSort: fields.name, | ||||
|   admin: { | ||||
| @ -45,7 +50,7 @@ export const ChronologyItems: CollectionConfig = buildVersionedCollectionConfig( | ||||
|       BeforeListTable: [ | ||||
|         () => | ||||
|           QuickFilters({ | ||||
|             slug: Collections.ChronologyItems, | ||||
|             slug: Collections.ChronologyEvents, | ||||
|             filterGroups: [ | ||||
|               languageBasedFilters("events.translations.language"), | ||||
|               publishStatusFilters, | ||||
| @ -54,7 +59,7 @@ export const ChronologyItems: CollectionConfig = buildVersionedCollectionConfig( | ||||
|       ], | ||||
|     }, | ||||
|   }, | ||||
|   endpoints: [importFromStrapi], | ||||
|   endpoints: [importFromStrapi, getAllEndpoint, getByID], | ||||
|   fields: [ | ||||
|     { | ||||
|       name: fields.name, | ||||
| @ -67,6 +72,11 @@ export const ChronologyItems: CollectionConfig = buildVersionedCollectionConfig( | ||||
|     { | ||||
|       type: "group", | ||||
|       name: fields.date, | ||||
|       admin: { | ||||
|         description: | ||||
|           "Make sure there isn't already an entry in the Chronology Events with the same date.\ | ||||
|       If you try to create another entry with the same date, it will refuse to publish.", | ||||
|       }, | ||||
|       validate: validateDate, | ||||
|       fields: [ | ||||
|         rowField([ | ||||
| @ -97,6 +107,12 @@ export const ChronologyItems: CollectionConfig = buildVersionedCollectionConfig( | ||||
|       required: true, | ||||
|       minRows: 1, | ||||
|       fields: [ | ||||
|         { | ||||
|           name: fields.eventsSources, | ||||
|           type: "blocks", | ||||
|           maxRows: 1, | ||||
|           blocks: [urlBlock, collectibleBlock, pageBlock], | ||||
|         }, | ||||
|         translatedFields({ | ||||
|           name: fields.eventsTranslations, | ||||
|           required: true, | ||||
							
								
								
									
										81
									
								
								src/collections/ChronologyEvents/blocks/collectibleBlock.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										81
									
								
								src/collections/ChronologyEvents/blocks/collectibleBlock.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,81 @@ | ||||
| import { Block } from "payload/types"; | ||||
| import { Collections } from "../../../constants"; | ||||
| import { translatedFields } from "../../../fields/translatedFields/translatedFields"; | ||||
| 
 | ||||
| export const collectibleBlock: Block = { | ||||
|   slug: "collectibleBlock", | ||||
|   interfaceName: "CollectibleBlock", | ||||
|   labels: { singular: "Collectible", plural: "Collectibles" }, | ||||
|   fields: [ | ||||
|     { | ||||
|       name: "collectible", | ||||
|       type: "relationship", | ||||
|       hasMany: false, | ||||
|       required: true, | ||||
|       relationTo: Collections.Collectibles, | ||||
|     }, | ||||
|     { | ||||
|       name: "range", | ||||
|       type: "blocks", | ||||
|       maxRows: 1, | ||||
|       admin: { className: "no-label" }, | ||||
|       blocks: [ | ||||
|         { | ||||
|           slug: "page", | ||||
|           labels: { singular: "Page", plural: "Pages" }, | ||||
|           fields: [ | ||||
|             { | ||||
|               name: "page", | ||||
|               type: "number", | ||||
|               required: true, | ||||
|               admin: { | ||||
|                 description: | ||||
|                   "Make sure the page range corresponds to the pages as written\ | ||||
|                       on the collectible. You can use negative page numbers if necessary.", | ||||
|               }, | ||||
|             }, | ||||
|           ], | ||||
|         }, | ||||
|         { | ||||
|           slug: "timestamp", | ||||
|           labels: { singular: "Timestamp", plural: "Timestamps" }, | ||||
|           fields: [ | ||||
|             { | ||||
|               name: "timestamp", | ||||
|               type: "text", | ||||
|               required: true, | ||||
|               defaultValue: "00:00:00", | ||||
|               validate: (value) => | ||||
|                 /\d{2}:\d{2}:\d{2}/g.test(value) | ||||
|                   ? true | ||||
|                   : "The format should be hh:mm:ss\ | ||||
|                       (e.g: 01:23:45 for 1 hour, 23 minutes, and 45 seconds)", | ||||
|               admin: { | ||||
|                 description: "hh:mm:ss (e.g: 01:23:45 for 1 hour, 23 minutes, and 45 seconds)", | ||||
|               }, | ||||
|             }, | ||||
|           ], | ||||
|         }, | ||||
|         { | ||||
|           slug: "other", | ||||
|           labels: { singular: "Other", plural: "Others" }, | ||||
|           fields: [ | ||||
|             translatedFields({ | ||||
|               admin: { className: "no-label" }, | ||||
|               name: "translations", | ||||
|               required: true, | ||||
|               minRows: 1, | ||||
|               fields: [ | ||||
|                 { | ||||
|                   name: "note", | ||||
|                   type: "textarea", | ||||
|                   required: true, | ||||
|                 }, | ||||
|               ], | ||||
|             }), | ||||
|           ], | ||||
|         }, | ||||
|       ], | ||||
|     }, | ||||
|   ], | ||||
| }; | ||||
							
								
								
									
										17
									
								
								src/collections/ChronologyEvents/blocks/contentBlock.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								src/collections/ChronologyEvents/blocks/contentBlock.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,17 @@ | ||||
| import { Block } from "payload/types"; | ||||
| import { Collections } from "../../../constants"; | ||||
| 
 | ||||
| export const pageBlock: Block = { | ||||
|   slug: "pageBlock", | ||||
|   interfaceName: "PageBlock", | ||||
|   labels: { singular: "Page", plural: "Pages" }, | ||||
|   fields: [ | ||||
|     { | ||||
|       name: "page", | ||||
|       type: "relationship", | ||||
|       hasMany: false, | ||||
|       required: true, | ||||
|       relationTo: Collections.Pages, | ||||
|     }, | ||||
|   ], | ||||
| }; | ||||
							
								
								
									
										8
									
								
								src/collections/ChronologyEvents/blocks/urlBlock.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								src/collections/ChronologyEvents/blocks/urlBlock.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,8 @@ | ||||
| import { Block } from "payload/types"; | ||||
| 
 | ||||
| export const urlBlock: Block = { | ||||
|   slug: "urlBlock", | ||||
|   interfaceName: "UrlBlock", | ||||
|   labels: { singular: "URL", plural: "URLs" }, | ||||
|   fields: [{ name: "url", type: "text", required: true }], | ||||
| }; | ||||
							
								
								
									
										150
									
								
								src/collections/ChronologyEvents/endpoints/getAllEndpoint.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										150
									
								
								src/collections/ChronologyEvents/endpoints/getAllEndpoint.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,150 @@ | ||||
| import payload from "payload"; | ||||
| import { Collections } from "../../../constants"; | ||||
| import { EndpointChronologyEvent, EndpointSource } from "../../../sdk"; | ||||
| import { ChronologyEvent, CollectibleBlock } from "../../../types/collections"; | ||||
| import { CollectionEndpoint } from "../../../types/payload"; | ||||
| import { isDefined, isNotEmpty, isPayloadArrayType, isPayloadType } from "../../../utils/asserts"; | ||||
| import { getDomainFromUrl, handleRecorder } from "../../../utils/endpoints"; | ||||
| import { convertCollectibleToPreview } from "../../Collectibles/endpoints/getBySlugEndpoint"; | ||||
| import { convertPageToPreview } from "../../Pages/endpoints/getBySlugEndpoint"; | ||||
| 
 | ||||
| export const getAllEndpoint: CollectionEndpoint = { | ||||
|   method: "get", | ||||
|   path: "/all", | ||||
|   handler: async (req, res) => { | ||||
|     if (!req.user) { | ||||
|       return res.status(403).send({ | ||||
|         errors: [ | ||||
|           { | ||||
|             message: "You are not allowed to perform this action.", | ||||
|           }, | ||||
|         ], | ||||
|       }); | ||||
|     } | ||||
| 
 | ||||
|     const result = ( | ||||
|       await payload.find({ | ||||
|         collection: Collections.ChronologyEvents, | ||||
|         pagination: false, | ||||
|         draft: false, | ||||
|         where: { | ||||
|           _status: { | ||||
|             equals: "published", | ||||
|           }, | ||||
|         }, | ||||
|       }) | ||||
|     ).docs; | ||||
| 
 | ||||
|     const events = result | ||||
|       .sort((a, b) => { | ||||
|         const aYear = a.date.year; | ||||
|         const bYear = b.date.year; | ||||
|         if (aYear !== bYear) return aYear - bYear; | ||||
|         const aMonth = a.date.month ?? 13; | ||||
|         const bMonth = b.date.month ?? 13; | ||||
|         if (aMonth !== bMonth) return aMonth - bMonth; | ||||
|         const aDay = a.date.day ?? 32; | ||||
|         const bDay = b.date.day ?? 32; | ||||
|         if (aDay !== bDay) return aDay - bDay; | ||||
|         return 0; | ||||
|       }) | ||||
|       .map<EndpointChronologyEvent>(eventToEndpointEvent); | ||||
| 
 | ||||
|     res.status(200).json(events); | ||||
|   }, | ||||
| }; | ||||
| 
 | ||||
| export const eventToEndpointEvent = ({ | ||||
|   date: { year, day, month }, | ||||
|   events, | ||||
|   id, | ||||
| }: ChronologyEvent): EndpointChronologyEvent => ({ | ||||
|   id, | ||||
|   date: { | ||||
|     year, | ||||
|     ...(isDefined(month) ? { month } : {}), | ||||
|     ...(isDefined(day) ? { day } : {}), | ||||
|   }, | ||||
|   events: events.map<EndpointChronologyEvent["events"][number]>(({ sources, translations }) => ({ | ||||
|     translations: translations.map( | ||||
|       ({ | ||||
|         language, | ||||
|         sourceLanguage, | ||||
|         description, | ||||
|         notes, | ||||
|         proofreaders, | ||||
|         title, | ||||
|         transcribers, | ||||
|         translators, | ||||
|       }) => ({ | ||||
|         language: isPayloadType(language) ? language.id : language, | ||||
|         sourceLanguage: isPayloadType(sourceLanguage) ? sourceLanguage.id : sourceLanguage, | ||||
|         ...(isNotEmpty(title) ? { title } : {}), | ||||
|         ...(isDefined(description) ? { description } : {}), | ||||
|         ...(isDefined(notes) ? { notes } : {}), | ||||
|         proofreaders: isPayloadArrayType(proofreaders) ? proofreaders.map(handleRecorder) : [], | ||||
|         transcribers: isPayloadArrayType(transcribers) ? transcribers.map(handleRecorder) : [], | ||||
|         translators: isPayloadArrayType(translators) ? translators.map(handleRecorder) : [], | ||||
|       }) | ||||
|     ), | ||||
|     sources: handleSources(sources), | ||||
|   })), | ||||
| }); | ||||
| 
 | ||||
| const handleSources = (sources: ChronologyEvent["events"][number]["sources"]): EndpointSource[] => { | ||||
|   return ( | ||||
|     sources?.flatMap<EndpointSource>((source) => { | ||||
|       switch (source.blockType) { | ||||
|         case "collectibleBlock": | ||||
|           const range = handleRange(source.range); | ||||
|           if (!isPayloadType(source.collectible)) return []; | ||||
|           return { | ||||
|             type: "collectible", | ||||
|             collectible: convertCollectibleToPreview(source.collectible), | ||||
|             ...(range ? { range } : {}), | ||||
|           }; | ||||
| 
 | ||||
|         case "pageBlock": | ||||
|           if (!isPayloadType(source.page)) return []; | ||||
|           return { | ||||
|             type: "page", | ||||
|             page: convertPageToPreview(source.page), | ||||
|           }; | ||||
| 
 | ||||
|         case "urlBlock": | ||||
|           return { | ||||
|             type: "url", | ||||
|             url: source.url, | ||||
|             label: getDomainFromUrl(source.url), | ||||
|           }; | ||||
|       } | ||||
|     }) ?? [] | ||||
|   ); | ||||
| }; | ||||
| 
 | ||||
| const handleRange = ( | ||||
|   rawRange: CollectibleBlock["range"] | ||||
| ): Extract<EndpointSource, { type: "collectible" }>["range"] => { | ||||
|   const range = rawRange?.[0]; | ||||
| 
 | ||||
|   switch (range?.blockType) { | ||||
|     case "page": | ||||
|       return { type: "page", page: range.page }; | ||||
| 
 | ||||
|     case "timestamp": | ||||
|       return { type: "timestamp", timestamp: range.timestamp }; | ||||
| 
 | ||||
|     case "other": | ||||
|       return { | ||||
|         type: "custom", | ||||
|         translations: range.translations.map(({ language, note }) => ({ | ||||
|           language: isPayloadType(language) ? language.id : language, | ||||
|           note, | ||||
|         })), | ||||
|       }; | ||||
| 
 | ||||
|     case undefined: | ||||
|     default: | ||||
|       return undefined; | ||||
|   } | ||||
| }; | ||||
							
								
								
									
										35
									
								
								src/collections/ChronologyEvents/endpoints/getByID.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								src/collections/ChronologyEvents/endpoints/getByID.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,35 @@ | ||||
| import payload from "payload"; | ||||
| import { Collections } from "../../../constants"; | ||||
| import { CollectionEndpoint } from "../../../types/payload"; | ||||
| import { eventToEndpointEvent } from "./getAllEndpoint"; | ||||
| 
 | ||||
| export const getByID: CollectionEndpoint = { | ||||
|   method: "get", | ||||
|   path: "/:id", | ||||
|   handler: async (req, res) => { | ||||
|     if (!req.user) { | ||||
|       return res.status(403).send({ | ||||
|         errors: [ | ||||
|           { | ||||
|             message: "You are not allowed to perform this action.", | ||||
|           }, | ||||
|         ], | ||||
|       }); | ||||
|     } | ||||
| 
 | ||||
|     if (!req.params.id) { | ||||
|       return res.status(400).send({ errors: [{ message: "Missing 'id' query params" }] }); | ||||
|     } | ||||
| 
 | ||||
|     try { | ||||
|       const result = await payload.findByID({ | ||||
|         collection: Collections.ChronologyEvents, | ||||
|         id: req.params.id, | ||||
|       }); | ||||
| 
 | ||||
|       return res.status(200).json(eventToEndpointEvent(result)); | ||||
|     } catch { | ||||
|       return res.sendStatus(404); | ||||
|     } | ||||
|   }, | ||||
| }; | ||||
| @ -1,7 +1,8 @@ | ||||
| import { Collections } from "../../../constants"; | ||||
| import { createStrapiImportEndpoint } from "../../../endpoints/createStrapiImportEndpoint"; | ||||
| import { StrapiLanguage } from "../../../types/strapi"; | ||||
| import { isUndefined } from "../../../utils/asserts"; | ||||
| import { isDefined, isUndefined } from "../../../utils/asserts"; | ||||
| import { plainTextToLexical } from "../../../utils/string"; | ||||
| 
 | ||||
| type StrapiChronologyItem = { | ||||
|   year: number; | ||||
| @ -25,20 +26,25 @@ export const importFromStrapi = createStrapiImportEndpoint<StrapiChronologyItem> | ||||
|     }, | ||||
|   }, | ||||
|   payload: { | ||||
|     collection: Collections.ChronologyItems, | ||||
|     collection: Collections.ChronologyEvents, | ||||
|     convert: ({ year, month, day, events }, user) => ({ | ||||
|       date: { year, month, day }, | ||||
|       events: events.map((event) => ({ | ||||
|         translations: event.translations.map(({ title, description, note, language }) => { | ||||
|           if (isUndefined(language.data)) | ||||
|             throw new Error("A language is required for a chronology item event translation"); | ||||
| 
 | ||||
|           const newLanguage = | ||||
|             language.data.attributes.code === "pt-br" ? "pt" : language.data.attributes.code; | ||||
|           const sourceLanguage = language.data.attributes.code === "ja" ? "ja" : "en"; | ||||
| 
 | ||||
|           return { | ||||
|             title, | ||||
|             description, | ||||
|             note, | ||||
|             language: language.data.attributes.code, | ||||
|             sourceLanguage: "en", | ||||
|             ...(language.data.attributes.code === "en" | ||||
|             ...(isDefined(title) ? { title } : {}), | ||||
|             ...(isDefined(note) ? { notes: plainTextToLexical(note) } : {}), | ||||
|             ...(isDefined(description) ? { description: plainTextToLexical(description) } : {}), | ||||
|             language: newLanguage, | ||||
|             sourceLanguage, | ||||
|             ...(newLanguage === sourceLanguage | ||||
|               ? { transcribers: [user.id] } | ||||
|               : { translators: [user.id] }), | ||||
|           }; | ||||
| @ -1,11 +1,11 @@ | ||||
| import { FieldHook } from "payload/dist/fields/config/types"; | ||||
| import { ChronologyItem } from "../../../types/collections"; | ||||
| import { ChronologyEvent } from "../../../types/collections"; | ||||
| import { isDefined, isUndefined } from "../../../utils/asserts"; | ||||
| 
 | ||||
| export const beforeValidatePopulateNameField: FieldHook< | ||||
|   ChronologyItem, | ||||
|   ChronologyItem["name"], | ||||
|   ChronologyItem | ||||
|   ChronologyEvent, | ||||
|   ChronologyEvent["name"], | ||||
|   ChronologyEvent | ||||
| > = ({ data }) => { | ||||
|   if (isUndefined(data) || isUndefined(data.date) || isUndefined(data.date.year)) | ||||
|     return "????-??-??"; | ||||
| @ -1,9 +1,9 @@ | ||||
| import { DateTime } from "luxon"; | ||||
| import { Validate } from "payload/types"; | ||||
| import { ChronologyItem } from "../../../types/collections"; | ||||
| import { ChronologyEvent } from "../../../types/collections"; | ||||
| import { isUndefined } from "../../../utils/asserts"; | ||||
| 
 | ||||
| export const validateDate: Validate<ChronologyItem["date"] | undefined> = (date) => { | ||||
| export const validateDate: Validate<ChronologyEvent["date"] | undefined> = (date) => { | ||||
|   if (isUndefined(date)) return "This field is required."; | ||||
|   const { year, month, day } = date; | ||||
|   if (isUndefined(day)) return true; | ||||
| @ -1,11 +1,11 @@ | ||||
| import { Validate } from "payload/types"; | ||||
| import { ChronologyItem } from "../../../types/collections"; | ||||
| import { ChronologyEvent } from "../../../types/collections"; | ||||
| import { isEmpty } from "../../../utils/asserts"; | ||||
| 
 | ||||
| export const validateEventsTranslationsDescription: Validate< | ||||
|   string | undefined, | ||||
|   ChronologyItem, | ||||
|   ChronologyItem["events"][number]["translations"][number], | ||||
|   ChronologyEvent, | ||||
|   ChronologyEvent["events"][number]["translations"][number], | ||||
|   unknown | ||||
| > = (_, { siblingData: { description, title } }) => { | ||||
|   if (!description && isEmpty(title)) { | ||||
| @ -1,11 +1,11 @@ | ||||
| import { Validate } from "payload/types"; | ||||
| import { ChronologyItem } from "../../../types/collections"; | ||||
| import { ChronologyEvent } from "../../../types/collections"; | ||||
| import { isEmpty } from "../../../utils/asserts"; | ||||
| 
 | ||||
| export const validateEventsTranslationsTitle: Validate< | ||||
|   string | undefined, | ||||
|   ChronologyItem, | ||||
|   ChronologyItem["events"][number]["translations"][number], | ||||
|   ChronologyEvent, | ||||
|   ChronologyEvent["events"][number]["translations"][number], | ||||
|   unknown | ||||
| > = (_, { siblingData: { description, title } }) => { | ||||
|   if (!description && isEmpty(title)) { | ||||
| @ -8,7 +8,7 @@ import { | ||||
|   isPayloadType, | ||||
|   isValidPayloadImage, | ||||
| } from "../../../utils/asserts"; | ||||
| import { convertTagsToGroups, handleParentPages } from "../../../utils/endpoints"; | ||||
| import { convertTagsToGroups, getDomainFromUrl, handleParentPages } from "../../../utils/endpoints"; | ||||
| import { convertPageToPreview } from "../../Pages/endpoints/getBySlugEndpoint"; | ||||
| 
 | ||||
| export const getBySlugEndpoint = createGetByEndpoint( | ||||
| @ -41,9 +41,9 @@ export const getBySlugEndpoint = createGetByEndpoint( | ||||
|       gallery: handleGallery(gallery), | ||||
|       scans: handleScans(collectible.scans), | ||||
|       nature: nature === "Physical" ? CollectibleNature.Physical : CollectibleNature.Digital, | ||||
|       parentPages: handleParentPages({collectibles: parentItems, folders}), | ||||
|       parentPages: handleParentPages({ collectibles: parentItems, folders }), | ||||
|       subitems: isPayloadArrayType(subitems) ? subitems.map(convertCollectibleToPreview) : [], | ||||
|       urls: urls?.map(({ url }) => ({ url, label: getLabelFromUrl(url) })) ?? [], | ||||
|       urls: urls?.map(({ url }) => ({ url, label: getDomainFromUrl(url) })) ?? [], | ||||
|       ...(weightEnabled && weight ? { weight: weight.amount } : {}), | ||||
|       ...handleSize(size, sizeEnabled), | ||||
|       ...handlePageInfo(pageInfo, pageInfoEnabled), | ||||
| @ -53,7 +53,7 @@ export const getBySlugEndpoint = createGetByEndpoint( | ||||
|   3 | ||||
| ); | ||||
| 
 | ||||
| export const handlePrice = ( | ||||
| const handlePrice = ( | ||||
|   price: Collectible["price"], | ||||
|   enabled: Collectible["priceEnabled"] | ||||
| ): { price: NonNullable<EndpointCollectible["price"]> } | {} => { | ||||
| @ -63,7 +63,7 @@ export const handlePrice = ( | ||||
|   }; | ||||
| }; | ||||
| 
 | ||||
| export const handleSize = ( | ||||
| const handleSize = ( | ||||
|   size: Collectible["size"], | ||||
|   enabled: Collectible["sizeEnabled"] | ||||
| ): { size: NonNullable<EndpointCollectible["size"]> } | {} => { | ||||
| @ -77,7 +77,7 @@ export const handleSize = ( | ||||
|   }; | ||||
| }; | ||||
| 
 | ||||
| export const handlePageInfo = ( | ||||
| const handlePageInfo = ( | ||||
|   pageInfo: Collectible["pageInfo"], | ||||
|   enabled: Collectible["pageInfoEnabled"] | ||||
| ): { pageInfo: NonNullable<EndpointCollectible["pageInfo"]> } | {} => { | ||||
| @ -91,7 +91,7 @@ export const handlePageInfo = ( | ||||
|   }; | ||||
| }; | ||||
| 
 | ||||
| export const handleGallery = (gallery: Collectible["gallery"]): EndpointCollectible["gallery"] => { | ||||
| const handleGallery = (gallery: Collectible["gallery"]): EndpointCollectible["gallery"] => { | ||||
|   const result: PayloadImage[] = []; | ||||
|   if (!gallery) return result; | ||||
| 
 | ||||
| @ -104,7 +104,7 @@ export const handleGallery = (gallery: Collectible["gallery"]): EndpointCollecti | ||||
|   return result.slice(0, 10); | ||||
| }; | ||||
| 
 | ||||
| export const handleScans = (scans: Collectible["scans"]): EndpointCollectible["scans"] => { | ||||
| const handleScans = (scans: Collectible["scans"]): EndpointCollectible["scans"] => { | ||||
|   const result: PayloadImage[] = []; | ||||
|   if (!scans) return result; | ||||
| 
 | ||||
| @ -133,9 +133,7 @@ export const handleScans = (scans: Collectible["scans"]): EndpointCollectible["s | ||||
|   return result.slice(0, 10); | ||||
| }; | ||||
| 
 | ||||
| export const handleContents = ( | ||||
|   contents: Collectible["contents"] | ||||
| ): EndpointCollectible["contents"] => { | ||||
| const handleContents = (contents: Collectible["contents"]): EndpointCollectible["contents"] => { | ||||
|   if (!contents) return []; | ||||
| 
 | ||||
|   return contents.flatMap(({ content, range: rangeArray }) => { | ||||
| @ -229,12 +227,3 @@ export const convertCollectibleToPreview = ({ | ||||
|       })) ?? [], | ||||
|   }; | ||||
| }; | ||||
| 
 | ||||
| const getLabelFromUrl = (url: string): string => { | ||||
|   const urlObject = new URL(url); | ||||
|   let domain = urlObject.hostname; | ||||
|   if (domain.startsWith("www.")) { | ||||
|     domain = domain.substring("www.".length); | ||||
|   } | ||||
|   return domain; | ||||
| }; | ||||
|  | ||||
| @ -7,7 +7,6 @@ import { translatedFields } from "../../fields/translatedFields/translatedFields | ||||
| import { buildCollectionConfig } from "../../utils/collectionConfig"; | ||||
| import { createEditor } from "../../utils/editor"; | ||||
| import { getBySlugEndpoint } from "./endpoints/getBySlugEndpoint"; | ||||
| import { getRootFoldersEndpoint } from "./endpoints/rootEndpoint"; | ||||
| 
 | ||||
| const fields = { | ||||
|   slug: "slug", | ||||
| @ -34,7 +33,7 @@ export const Folders = buildCollectionConfig({ | ||||
|       "Folders provide a way to structure our content. A folder can contain subfolders and/or files.", | ||||
|     preview: ({ slug }) => `https://v3.accords-library.com/en/folders/${slug}`, | ||||
|   }, | ||||
|   endpoints: [getRootFoldersEndpoint, getBySlugEndpoint], | ||||
|   endpoints: [getBySlugEndpoint], | ||||
|   fields: [ | ||||
|     rowField([ | ||||
|       slugField({ name: fields.slug }), | ||||
|  | ||||
| @ -1,52 +0,0 @@ | ||||
| import payload from "payload"; | ||||
| import { Collections } from "../../../constants"; | ||||
| import { Folder } from "../../../types/collections"; | ||||
| import { CollectionEndpoint } from "../../../types/payload"; | ||||
| import { isPayloadType } from "../../../utils/asserts"; | ||||
| import { convertFolderToPreview } from "./getBySlugEndpoint"; | ||||
| 
 | ||||
| export const getRootFoldersEndpoint: CollectionEndpoint = { | ||||
|   method: "get", | ||||
|   path: "/root", | ||||
|   handler: async (req, res) => { | ||||
|     if (!req.user) { | ||||
|       return res.status(403).send({ | ||||
|         errors: [ | ||||
|           { | ||||
|             message: "You are not allowed to perform this action.", | ||||
|           }, | ||||
|         ], | ||||
|       }); | ||||
|     } | ||||
| 
 | ||||
|     const homeFolder = ( | ||||
|       await payload.find({ | ||||
|         collection: Collections.Folders, | ||||
|         limit: 100, | ||||
|         where: { slug: { equals: "home" } }, | ||||
|       }) | ||||
|     ).docs[0]; | ||||
| 
 | ||||
|     if (!homeFolder) { | ||||
|       res.status(404); | ||||
|       return; | ||||
|     } | ||||
| 
 | ||||
|     const folders = homeFolder.sections?.[0]?.subfolders; | ||||
| 
 | ||||
|     if (!folders) { | ||||
|       res.status(404); | ||||
|       return; | ||||
|     } | ||||
| 
 | ||||
|     const result = folders.filter(isPayloadType).filter(isEmptyFolder).map(convertFolderToPreview); | ||||
| 
 | ||||
|     res.status(200).json(result); | ||||
|   }, | ||||
| }; | ||||
| 
 | ||||
| const isEmptyFolder = ({ sections, files }: Folder): boolean => { | ||||
|   if (sections && sections.length > 0) return true; | ||||
|   if (files && files.length > 0) return true; | ||||
|   return false; | ||||
| }; | ||||
| @ -3,8 +3,7 @@ import type { BreakBlock, Image, SectionBlock, TranscriptBlock } from "./types/c | ||||
| // END MOCKING SECTION
 | ||||
| 
 | ||||
| export enum Collections { | ||||
|   ChronologyEras = "chronology-eras", | ||||
|   ChronologyItems = "chronology-items", | ||||
|   ChronologyEvents = "chronology-events", | ||||
|   Currencies = "currencies", | ||||
|   Files = "files", | ||||
|   Languages = "languages", | ||||
|  | ||||
| @ -3,8 +3,7 @@ import { mongooseAdapter } from "@payloadcms/db-mongodb"; | ||||
| import path from "path"; | ||||
| import { buildConfig } from "payload/config"; | ||||
| import { BackgroundImages } from "./collections/BackgroundImages/BackgroundImages"; | ||||
| import { ChronologyEras } from "./collections/ChronologyEras/ChronologyEras"; | ||||
| import { ChronologyItems } from "./collections/ChronologyItems/ChronologyItems"; | ||||
| import { ChronologyEvents } from "./collections/ChronologyEvents/ChronologyEvents"; | ||||
| import { Collectibles } from "./collections/Collectibles/Collectibles"; | ||||
| import { Currencies } from "./collections/Currencies/Currencies"; | ||||
| import { Folders } from "./collections/Folders/Folders"; | ||||
| @ -44,16 +43,15 @@ export default buildConfig({ | ||||
|     Pages, | ||||
|     Collectibles, | ||||
|     Folders, | ||||
|     ChronologyItems, | ||||
|     ChronologyEras, | ||||
|     ChronologyEvents, | ||||
|     Notes, | ||||
|      | ||||
| 
 | ||||
|     Images, | ||||
|     BackgroundImages, | ||||
|     RecordersThumbnails, | ||||
|     Videos, | ||||
|     VideosChannels, | ||||
|      | ||||
| 
 | ||||
|     Tags, | ||||
|     TagsGroups, | ||||
|     Recorders, | ||||
|  | ||||
							
								
								
									
										40
									
								
								src/sdk.ts
									
									
									
									
									
								
							
							
						
						
									
										40
									
								
								src/sdk.ts
									
									
									
									
									
								
							| @ -320,6 +320,40 @@ export type TableOfContentEntry = { | ||||
|   children: TableOfContentEntry[]; | ||||
| }; | ||||
| 
 | ||||
| export type EndpointChronologyEvent = { | ||||
|   id: string; | ||||
|   date: { | ||||
|     year: number; | ||||
|     month?: number; | ||||
|     day?: number; | ||||
|   }; | ||||
|   events: { | ||||
|     sources: EndpointSource[]; | ||||
|     translations: { | ||||
|       language: string; | ||||
|       sourceLanguage: string; | ||||
|       title?: string; | ||||
|       description?: RichTextContent; | ||||
|       notes?: RichTextContent; | ||||
|       transcribers: EndpointRecorder[]; | ||||
|       translators: EndpointRecorder[]; | ||||
|       proofreaders: EndpointRecorder[]; | ||||
|     }[]; | ||||
|   }[]; | ||||
| }; | ||||
| 
 | ||||
| export type EndpointSource = | ||||
|   | { type: "url"; url: string; label: string } | ||||
|   | { | ||||
|       type: "collectible"; | ||||
|       collectible: EndpointCollectiblePreview; | ||||
|       range?: | ||||
|         | { type: "page"; page: number } | ||||
|         | { type: "timestamp"; timestamp: string } | ||||
|         | { type: "custom"; translations: { language: string; note: string }[] }; | ||||
|     } | ||||
|   | { type: "page"; page: EndpointPagePreview }; | ||||
| 
 | ||||
| export type PayloadImage = { | ||||
|   url: string; | ||||
|   width: number; | ||||
| @ -329,8 +363,6 @@ export type PayloadImage = { | ||||
| }; | ||||
| 
 | ||||
| export const payload = { | ||||
|   getEras: async (): Promise<EndpointEra[]> => | ||||
|     await (await request(payloadApiUrl(Collections.ChronologyEras, `all`))).json(), | ||||
|   getHomeFolders: async (): Promise<EndpointHomeFolder[]> => | ||||
|     await (await request(payloadApiUrl(Collections.HomeFolders, `all`, true))).json(), | ||||
|   getFolder: async (slug: string): Promise<EndpointFolder> => | ||||
| @ -347,4 +379,8 @@ export const payload = { | ||||
|     await (await request(payloadApiUrl(Collections.Pages, `slug/${slug}`))).json(), | ||||
|   getCollectible: async (slug: string): Promise<EndpointCollectible> => | ||||
|     await (await request(payloadApiUrl(Collections.Collectibles, `slug/${slug}`))).json(), | ||||
|   getChronologyEvents: async (): Promise<EndpointChronologyEvent[]> => | ||||
|     await (await request(payloadApiUrl(Collections.ChronologyEvents, `all`))).json(), | ||||
|   getChronologyEventByID: async (id: string): Promise<EndpointChronologyEvent> => | ||||
|     await (await request(payloadApiUrl(Collections.ChronologyEvents, id))).json(), | ||||
| }; | ||||
|  | ||||
| @ -77,10 +77,28 @@ html[data-theme="light"] { | ||||
| .blocks-field__block-pill-cueBlock + .section-title, | ||||
| .blocks-field__block-pill-pageRange + .section-title, | ||||
| .blocks-field__block-pill-timeRange + .section-title, | ||||
| .blocks-field__block-pill-urlBlock + .section-title, | ||||
| .blocks-field__block-pill-pageBlock + .section-title, | ||||
| .blocks-field__block-pill-collectibleBlock + .section-title, | ||||
| .blocks-field__block-pill-page + .section-title, | ||||
| .blocks-field__block-pill-timestamp + .section-title, | ||||
| .blocks-field__block-pill-other + .section-title, | ||||
| .blocks-field__block-pill-other + .section-title { | ||||
|   display: none; | ||||
| } | ||||
| 
 | ||||
| .collapsible__toggle-wrap:has( | ||||
|     .blocks-field__block-pill-collectibleBlock, | ||||
|     .blocks-field__block-pill-page, | ||||
|     .blocks-field__block-pill-timestamp, | ||||
|     .blocks-field__block-pill-other, | ||||
|     .blocks-field__block-pill-pageRange, | ||||
|     .blocks-field__block-pill-timeRange | ||||
|   ) | ||||
|   .blocks-field__block-number { | ||||
|   display: none; | ||||
| } | ||||
| 
 | ||||
| // Reduce margin on Lexical blocks with the classname "reduced-margins" | ||||
| 
 | ||||
| .rich-text-lexical.field-type.reduced-margins { | ||||
|  | ||||
| @ -100,3 +100,12 @@ export const handleRecorder = ({ | ||||
|   username: anonymize ? `Recorder#${id.substring(0, 5)}` : username, | ||||
|   ...(isValidPayloadImage(avatar) ? { avatar } : {}), | ||||
| }); | ||||
| 
 | ||||
| export const getDomainFromUrl = (url: string): string => { | ||||
|   const urlObject = new URL(url); | ||||
|   let domain = urlObject.hostname; | ||||
|   if (domain.startsWith("www.")) { | ||||
|     domain = domain.substring("www.".length); | ||||
|   } | ||||
|   return domain; | ||||
| }; | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 DrMint
						DrMint