From 6337a181d9a3233cff5454215d2c71065644bcaa Mon Sep 17 00:00:00 2001 From: Ben Furber Date: Thu, 9 Jan 2025 11:11:07 +0000 Subject: [PATCH] chore: rename all library types --- .../createModerationEmails.ts | 5 +- .../createSubmissionEmails.ts | 7 +- .../src/emailNotifications/templateHelpers.ts | 27 ++++--- .../src/emulator/seed/content-generate.ts | 9 +-- packages/cypress/src/fixtures/howto.ts | 4 +- .../cypress/src/support/CustomAssertations.ts | 12 +-- packages/cypress/src/support/commands.ts | 4 +- shared/models/howto.ts | 78 ------------------- shared/models/index.ts | 2 +- shared/models/library.ts | 78 +++++++++++++++++++ .../Howto/Content/Common/Howto.form.test.tsx | 4 +- src/pages/Howto/Content/Common/Howto.form.tsx | 6 +- .../Howto/Content/Common/HowtoFieldStep.tsx | 4 +- .../Howto/Content/CreateHowto/CreateHowto.tsx | 4 +- .../Howto/Content/CreateHowto/Template.tsx | 6 +- .../Howto/Content/EditHowto/EditHowto.tsx | 6 +- .../Howto/Content/EditHowto/Template.tsx | 6 +- src/pages/Howto/Content/Howto/Howto.test.tsx | 4 +- src/pages/Howto/Content/Howto/Howto.tsx | 13 +--- .../HowtoDescription/HowtoDescription.tsx | 4 +- .../Howto/HowtoDownloads/HowtoDownloads.tsx | 4 +- src/pages/Howto/Content/Howto/Step/Step.tsx | 4 +- .../Howto/Content/HowtoList/HowToCard.tsx | 4 +- .../Howto/Content/HowtoList/HowtoList.tsx | 6 +- src/pages/Howto/howto.service.ts | 8 +- src/pages/User/types.ts | 4 +- src/pages/common/Breadcrumbs/Breadcrumbs.tsx | 8 +- src/routes/_.library.$slug._index.tsx | 6 +- src/routes/_.library.$slug.edit.tsx | 4 +- src/services/userService.server.ts | 4 +- src/stores/Howto/howto.store.test.ts | 4 +- src/stores/Howto/howto.store.tsx | 51 ++++++------ src/test/factories/Howto.ts | 19 ++--- 33 files changed, 197 insertions(+), 212 deletions(-) delete mode 100644 shared/models/howto.ts create mode 100644 shared/models/library.ts diff --git a/functions/src/emailNotifications/createModerationEmails.ts b/functions/src/emailNotifications/createModerationEmails.ts index da9956a609..8a58943141 100644 --- a/functions/src/emailNotifications/createModerationEmails.ts +++ b/functions/src/emailNotifications/createModerationEmails.ts @@ -10,8 +10,7 @@ import { getUserAndEmail } from './utils' import type { QueryDocumentSnapshot } from 'firebase-admin/firestore' import type { Change } from 'firebase-functions/v1' -import type { IModerable } from 'oa-shared' -import type { IHowtoDB } from 'oa-shared/models/howto' +import type { ILibrary, IModerable } from 'oa-shared' import type { IMapPin } from 'oa-shared/models/maps' export async function handleModerationUpdate( @@ -27,7 +26,7 @@ export async function handleModerationUpdate( } } -export async function createHowtoModerationEmail(howto: IHowtoDB) { +export async function createHowtoModerationEmail(howto: ILibrary.DB) { const { toUser, toUserEmail } = await getUserAndEmail(howto._createdBy) if (howto.moderation === IModerationStatus.ACCEPTED) { diff --git a/functions/src/emailNotifications/createSubmissionEmails.ts b/functions/src/emailNotifications/createSubmissionEmails.ts index 4bd0924f06..bd01a04ab3 100644 --- a/functions/src/emailNotifications/createSubmissionEmails.ts +++ b/functions/src/emailNotifications/createSubmissionEmails.ts @@ -11,9 +11,8 @@ import { getSenderMessageEmail, } from './templateHelpers' import { getUserAndEmail, isValidMessageRequest } from './utils' -import { IMessageDB } from 'oa-shared/models/messages' -import { IHowtoDB } from 'oa-shared/models/howto' -import { IMapPin } from 'oa-shared/models/maps' + +import type { ILibrary, IMapPin, IMessageDB } from 'oa-shared' export async function createMessageEmails(message: IMessageDB) { const isValid = await isValidMessageRequest(message) @@ -38,7 +37,7 @@ export async function createMessageEmails(message: IMessageDB) { } } -export async function createHowtoSubmissionEmail(howto: IHowtoDB) { +export async function createHowtoSubmissionEmail(howto: ILibrary.DB) { const { toUser, toUserEmail } = await getUserAndEmail(howto._createdBy) if (howto.moderation === IModerationStatus.AWAITING_MODERATION) { diff --git a/functions/src/emailNotifications/templateHelpers.ts b/functions/src/emailNotifications/templateHelpers.ts index bd3e7437c1..452829c538 100644 --- a/functions/src/emailNotifications/templateHelpers.ts +++ b/functions/src/emailNotifications/templateHelpers.ts @@ -1,17 +1,20 @@ +import { getEmailHtml } from './templates/index' import { NOTIFICATION_LIST_IMAGE } from './constants' import { + getNotificationListItem, getProjectImageSrc, - SITE_URL, getProjectName, - getNotificationListItem, getProjectSignoff, + SITE_URL, } from './utils' -import { getEmailHtml } from './templates/index' -import { IUserDB } from 'oa-shared/models/user' -import { INotification } from 'oa-shared/models/notifications' -import { IHowtoDB } from 'oa-shared/models/howto' -import { IMapPin } from 'oa-shared/models/maps' -import { IMessageDB } from 'oa-shared/models/messages' + +import type { + ILibrary, + IMapPin, + IMessageDB, + INotification, + IUserDB, +} from 'oa-shared' export interface Email { html: string @@ -137,7 +140,7 @@ export const getNotificationEmail = ( export const HOW_TO_APPROVAL_SUBJECT = 'Your project has been approved!' export const getHowToApprovalEmail = ( user: IUserDB, - howto: IHowtoDB, + howto: ILibrary.DB, ): Email => { return { html: getEmailHtml('how-to-approval', { @@ -186,7 +189,7 @@ export const getSenderMessageEmail = ({ export const HOW_TO_SUBMISSION_SUBJECT = 'Your project has been submitted' export const getHowToSubmissionEmail = ( user: IUserDB, - howto: IHowtoDB, + howto: ILibrary.DB, ): Email => { return { html: getEmailHtml('how-to-submission', { @@ -237,7 +240,7 @@ export const getUserVerifiedBadgeAddedEmail = (user: IUserDB): Email => ({ export const HOW_TO_REJECTED_SUBJECT = 'Your project has been rejected' export const getHowToRejectedEmail = ( user: IUserDB, - howto: IHowtoDB, + howto: ILibrary.DB, ): Email => ({ subject: HOW_TO_REJECTED_SUBJECT, html: getEmailHtml('how-to-rejected', { @@ -260,7 +263,7 @@ export const HOW_TO_NEEDS_IMPROVEMENTS_SUBJECT = 'Your project needs improvements' export const getHowToNeedsImprovementsEmail = ( user: IUserDB, - howto: IHowtoDB, + howto: ILibrary.DB, ): Email => ({ subject: HOW_TO_NEEDS_IMPROVEMENTS_SUBJECT, html: getEmailHtml('how-to-needs-improvements', { diff --git a/functions/src/emulator/seed/content-generate.ts b/functions/src/emulator/seed/content-generate.ts index fcc4b157fb..53034e91aa 100644 --- a/functions/src/emulator/seed/content-generate.ts +++ b/functions/src/emulator/seed/content-generate.ts @@ -1,11 +1,10 @@ +import { DifficultyLevel, IModerationStatus } from 'oa-shared' import { MOCK_AUTH_USERS } from 'oa-shared/mocks/auth' import { setDoc, updateDoc } from '../../Firebase/firestoreDB' +import type { ILibrary, IUserDB } from 'oa-shared' import type { IMockAuthUser } from 'oa-shared/mocks/auth' -import { DifficultyLevel, IHowtoDB } from 'oa-shared/models/howto' -import { IModerationStatus } from 'oa-shared' -import { IUserDB } from 'oa-shared/models/user' /** * Populate additional mock howtos alongside production data for ease of testing @@ -22,11 +21,11 @@ export async function seedContentGenerate() { export function getMockHowto( uid: string, - moderation: IHowtoDB['moderation'] = IModerationStatus.ACCEPTED, + moderation: ILibrary.DB['moderation'] = IModerationStatus.ACCEPTED, ) { const _id = `00_${uid}_howto` const loginInfo = `username : ${uid}@example.com\npassword : ${uid}` - const howto: IHowtoDB = { + const howto: ILibrary.DB = { _id, _created: new Date().toISOString(), _modified: new Date().toISOString(), diff --git a/packages/cypress/src/fixtures/howto.ts b/packages/cypress/src/fixtures/howto.ts index 482eb086dc..0cc2ec26de 100644 --- a/packages/cypress/src/fixtures/howto.ts +++ b/packages/cypress/src/fixtures/howto.ts @@ -3,12 +3,12 @@ import { DifficultyLevel, IModerationStatus } from 'oa-shared' import { generateAlphaNumeric } from '../utils/TestUtils' -import type { IHowtoDB } from 'oa-shared' +import type { ILibrary } from 'oa-shared' const _id = generateAlphaNumeric(20) const _created = dateformat(Date.now(), 'yyyy-mm-dd') -export const howto: IHowtoDB = { +export const howto: ILibrary.DB = { _id, _deleted: false, _createdBy: 'howto_super_user', diff --git a/packages/cypress/src/support/CustomAssertations.ts b/packages/cypress/src/support/CustomAssertations.ts index c6f004905a..915c8e9bf8 100644 --- a/packages/cypress/src/support/CustomAssertations.ts +++ b/packages/cypress/src/support/CustomAssertations.ts @@ -1,12 +1,6 @@ import chaiSubset from 'chai-subset' -import type { - IHowto, - IHowtoStep, - IResearchDB, - IUserDB, - ProfileTypeName, -} from 'oa-shared' +import type { ILibrary, IResearchDB, IUserDB, ProfileTypeName } from 'oa-shared' declare global { namespace Chai { @@ -37,7 +31,7 @@ chai.use((chaiObj) => { const eqHowto = (chaiObj) => { function compare(this: any, expected: any) { - const subject: IHowto = this._obj + const subject: ILibrary.Item = this._obj const { _createdBy, _deleted, @@ -90,7 +84,7 @@ const eqHowto = (chaiObj) => { } const eqHowtoStep = (chaiObj) => { function compare(this: any, expected: any, index: number) { - const subject: IHowtoStep = this._obj + const subject: ILibrary.Step = this._obj const { _animationKey, text, title } = expected expect(subject, `Step ${index} with info`).to.containSubset({ _animationKey, diff --git a/packages/cypress/src/support/commands.ts b/packages/cypress/src/support/commands.ts index b488584d03..535d1c0db2 100644 --- a/packages/cypress/src/support/commands.ts +++ b/packages/cypress/src/support/commands.ts @@ -5,7 +5,7 @@ import { deleteDB } from 'idb' import { Auth, TestDB } from './db/firebase' -import type { IHowtoDB, IQuestionDB, IResearchDB } from 'oa-shared' +import type { ILibrary, IQuestionDB, IResearchDB } from 'oa-shared' import type { IUserSignUpDetails } from '../utils/TestUtils' import type { firebase } from './db/firebase' @@ -33,7 +33,7 @@ declare global { opStr: any, value: string, ): Chainable - addHowto(howto: IHowtoDB, user: IUserSignUpDetails): Chainable + addHowto(howto: ILibrary.DB, user: IUserSignUpDetails): Chainable addQuestion( question: IQuestionDB, user: IUserSignUpDetails, diff --git a/shared/models/howto.ts b/shared/models/howto.ts deleted file mode 100644 index 058a69a2d9..0000000000 --- a/shared/models/howto.ts +++ /dev/null @@ -1,78 +0,0 @@ -import type { ICategory } from './categories' -import type { IConvertedFileMeta } from './common' -import type { DBDoc } from './db' -import type { IModerable } from './moderation' -import type { IUploadedFileMeta } from './storage' -import type { ISelectedTags } from './tags' -import type { UserMention } from './user' -import type { ISharedFeatures } from './voteUseful' - -export enum DifficultyLevel { - EASY = 'Easy', - MEDIUM = 'Medium', - HARD = 'Hard', - VERY_HARD = 'Very Hard', -} - -// By default all project form input fields come as strings -// The IHowto interface can imposes the correct formats on fields -// Additionally convert from local filemeta to uploaded filemeta -export interface IHowto extends IHowtoFormInput { - _createdBy: string - _deleted: boolean - cover_image?: IUploadedFileMeta - cover_image_alt?: string - fileLink?: string - total_downloads?: number - latestCommentDate?: string | undefined - mentions: UserMention[] - previousSlugs: string[] - totalComments: number - totalUsefulVotes?: number - keywords?: string[] -} - -/** - * Howtos retrieved from the database also include metadata such as _id, _created and _modified - */ -export type IHowtoDB = IHowto & DBDoc - -export interface IHowtoStep extends IHowToStepFormInput { - // *** NOTE - adding an '_animationKey' field to track when specific array element removed for - images?: Array - videoUrl?: string - title?: string - text?: string - _animationKey?: string -} - -export interface IHowToStepFormInput { - images?: Array - title?: string - text?: string - videoUrl?: string - _animationKey?: string -} - -export interface IHowtoFormInput extends IModerable, ISharedFeatures { - slug: string - title: string - allowDraftSave?: boolean - category?: ICategory - // NOTE cover image input starts as convertedFileMeta but is transformed on upload - cover_image?: IUploadedFileMeta | IConvertedFileMeta - cover_image_alt?: string - // Added to be able to recover on edit by admin - creatorCountry?: string - totalComments?: number - latestCommentDate?: string - description?: string - difficulty_level?: DifficultyLevel - files?: Array - fileLink?: string - mentions?: UserMention[] - steps: IHowToStepFormInput[] - // note, tags will remain optional as if populated {} will be stripped by db (firestore) - tags?: ISelectedTags - time?: string -} diff --git a/shared/models/index.ts b/shared/models/index.ts index d9036a8728..6eeec9f2f0 100644 --- a/shared/models/index.ts +++ b/shared/models/index.ts @@ -1,6 +1,6 @@ export * from './common' export * from './db' -export * from './howto' +export * from './library' export * from './maps' export * from './notifications' export * from './research' diff --git a/shared/models/library.ts b/shared/models/library.ts new file mode 100644 index 0000000000..bb34cd5965 --- /dev/null +++ b/shared/models/library.ts @@ -0,0 +1,78 @@ +import type { ICategory } from './categories' +import type { IConvertedFileMeta } from './common' +import type { DBDoc } from './db' +import type { IModerable } from './moderation' +import type { IUploadedFileMeta } from './storage' +import type { ISelectedTags } from './tags' +import type { UserMention } from './user' +import type { ISharedFeatures } from './voteUseful' + +export enum DifficultyLevel { + EASY = 'Easy', + MEDIUM = 'Medium', + HARD = 'Hard', + VERY_HARD = 'Very Hard', +} + +export declare namespace ILibrary { + type DB = ILibrary.Item & DBDoc + + interface FormInput extends IModerable, ISharedFeatures { + slug: string + title: string + allowDraftSave?: boolean + category?: ICategory + + // NOTE cover image input starts as convertedFileMeta but is transformed on upload + cover_image?: IUploadedFileMeta | IConvertedFileMeta + cover_image_alt?: string + + // Added to be able to recover on edit by admin + creatorCountry?: string + totalComments?: number + latestCommentDate?: string + description?: string + difficulty_level?: DifficultyLevel + files?: Array + fileLink?: string + mentions?: UserMention[] + steps: StepInput[] + + // note, tags will remain optional as if populated {} will be stripped by db (firestore) + tags?: ISelectedTags + time?: string + } + + interface Item extends FormInput { + _createdBy: string + _deleted: boolean + cover_image?: IUploadedFileMeta + cover_image_alt?: string + fileLink?: string + total_downloads?: number + latestCommentDate?: string | undefined + mentions: UserMention[] + previousSlugs: string[] + totalComments: number + totalUsefulVotes?: number + keywords?: string[] + } + + interface StepInput { + images?: Array + title?: string + text?: string + videoUrl?: string + _animationKey?: string + } + + interface Step extends StepInput { + images?: Array + videoUrl?: string + title?: string + text?: string + + // *** NOTE - adding an '_animationKey' field to track when specific array element removed for + _animationKey?: string + } +} diff --git a/src/pages/Howto/Content/Common/Howto.form.test.tsx b/src/pages/Howto/Content/Common/Howto.form.test.tsx index ef9b9599a0..4b8263735e 100644 --- a/src/pages/Howto/Content/Common/Howto.form.test.tsx +++ b/src/pages/Howto/Content/Common/Howto.form.test.tsx @@ -11,7 +11,7 @@ import { describe, expect, it, vi } from 'vitest' import { HowtoForm } from './Howto.form' -import type { IHowtoDB } from 'oa-shared' +import type { ILibrary } from 'oa-shared' import type { ParentType } from './Howto.form' const Theme = testingThemeStyles @@ -161,7 +161,7 @@ describe('Howto form', () => { }) }) -const Wrapper = (formValues: IHowtoDB, parentType: ParentType, navProps) => { +const Wrapper = (formValues: ILibrary.DB, parentType: ParentType, navProps) => { const ReactStub = createRemixStub( [ { diff --git a/src/pages/Howto/Content/Common/Howto.form.tsx b/src/pages/Howto/Content/Common/Howto.form.tsx index d70507ff3a..324e30263c 100644 --- a/src/pages/Howto/Content/Common/Howto.form.tsx +++ b/src/pages/Howto/Content/Common/Howto.form.tsx @@ -35,7 +35,7 @@ import { HowtoPostingGuidelines } from './HowtoPostingGuidelines' import { HowToSubmitStatus } from './SubmitStatus' import type { FormApi } from 'final-form' -import type { IHowtoFormInput } from 'oa-shared' +import type { ILibrary } from 'oa-shared' export type ParentType = 'create' | 'edit' @@ -77,7 +77,7 @@ export const HowtoForm = observer((props: IProps) => { const formId = 'howtoForm' const headingText = parentType === 'create' ? create : edit - const checkFilesValid = (formValues: IHowtoFormInput) => { + const checkFilesValid = (formValues: ILibrary.FormInput) => { if ( formValues.fileLink && formValues.files && @@ -90,7 +90,7 @@ export const HowtoForm = observer((props: IProps) => { return true } } - const onSubmit = async (formValues: IHowtoFormInput, form: FormApi) => { + const onSubmit = async (formValues: ILibrary.FormInput, form: FormApi) => { if (!checkFilesValid(formValues)) { return } diff --git a/src/pages/Howto/Content/Common/HowtoFieldStep.tsx b/src/pages/Howto/Content/Common/HowtoFieldStep.tsx index 53957d8790..21cbde93ca 100644 --- a/src/pages/Howto/Content/Common/HowtoFieldStep.tsx +++ b/src/pages/Howto/Content/Common/HowtoFieldStep.tsx @@ -21,7 +21,7 @@ import { } from '../../constants' import { buttons, errors, steps } from '../../labels' -import type { IHowtoStep, IUploadedFileMeta } from 'oa-shared' +import type { ILibrary, IUploadedFileMeta } from 'oa-shared' const ImageInputFieldWrapper = styled.div` width: 150px; @@ -30,7 +30,7 @@ const ImageInputFieldWrapper = styled.div` ` interface IProps { - step: any | IHowtoStep + step: any | ILibrary.Step index: number images: IUploadedFileMeta[] onDelete: (index: number) => void diff --git a/src/pages/Howto/Content/CreateHowto/CreateHowto.tsx b/src/pages/Howto/Content/CreateHowto/CreateHowto.tsx index f946836de4..f24619b252 100644 --- a/src/pages/Howto/Content/CreateHowto/CreateHowto.tsx +++ b/src/pages/Howto/Content/CreateHowto/CreateHowto.tsx @@ -4,10 +4,10 @@ import { HowtoForm } from 'src/pages/Howto/Content/Common/Howto.form' import TEMPLATE from './Template' -import type { IHowtoFormInput } from 'oa-shared' +import type { ILibrary } from 'oa-shared' const CreateHowto = observer(() => { - const formValues = { ...TEMPLATE.INITIAL_VALUES } as IHowtoFormInput + const formValues = { ...TEMPLATE.INITIAL_VALUES } as ILibrary.FormInput return }) diff --git a/src/pages/Howto/Content/CreateHowto/Template.tsx b/src/pages/Howto/Content/CreateHowto/Template.tsx index 99c0b0ab1a..4b572a91cb 100644 --- a/src/pages/Howto/Content/CreateHowto/Template.tsx +++ b/src/pages/Howto/Content/CreateHowto/Template.tsx @@ -1,9 +1,9 @@ import { DifficultyLevel } from 'oa-shared' -import type { IHowtoFormInput } from 'oa-shared' +import type { ILibrary } from 'oa-shared' // initialise fields which contain nested objects (and steps to have 3 placeholders) -const INITIAL_VALUES: Partial = { +const INITIAL_VALUES: Partial = { steps: [ { title: '', @@ -28,7 +28,7 @@ const INITIAL_VALUES: Partial = { files: [], } -const TESTING_VALUES: Partial = { +const TESTING_VALUES: Partial = { title: `Test-${new Date().toString()}`, description: 'example description', time: '1-2 weeks', diff --git a/src/pages/Howto/Content/EditHowto/EditHowto.tsx b/src/pages/Howto/Content/EditHowto/EditHowto.tsx index 459668b6c1..27caaef8de 100644 --- a/src/pages/Howto/Content/EditHowto/EditHowto.tsx +++ b/src/pages/Howto/Content/EditHowto/EditHowto.tsx @@ -6,15 +6,15 @@ import { Text } from 'theme-ui' import { HowtoForm } from '../Common/Howto.form' -import type { IHowtoDB, IUser } from 'oa-shared' +import type { ILibrary, IUser } from 'oa-shared' interface IState { - formValues: IHowtoDB + formValues: ILibrary.DB loggedInUser?: IUser | undefined } type EditHowtoProps = { - howto: IHowtoDB + howto: ILibrary.DB } const EditHowto = ({ howto }: EditHowtoProps) => { diff --git a/src/pages/Howto/Content/EditHowto/Template.tsx b/src/pages/Howto/Content/EditHowto/Template.tsx index 5ba1d19339..7a9d319b32 100644 --- a/src/pages/Howto/Content/EditHowto/Template.tsx +++ b/src/pages/Howto/Content/EditHowto/Template.tsx @@ -1,9 +1,9 @@ import { DifficultyLevel } from 'oa-shared' -import type { IHowtoFormInput } from 'oa-shared' +import type { ILibrary } from 'oa-shared' // initialise fields which contain nested objects (and steps to have 3 placeholders) -const INITIAL_VALUES: Partial = { +const INITIAL_VALUES: Partial = { steps: [ { title: 'EDITABLE', @@ -28,7 +28,7 @@ const INITIAL_VALUES: Partial = { files: [], } -const TESTING_VALUES: Partial = { +const TESTING_VALUES: Partial = { title: `Test-${new Date().toString()}`, description: 'example description', time: '1-2 weeks', diff --git a/src/pages/Howto/Content/Howto/Howto.test.tsx b/src/pages/Howto/Content/Howto/Howto.test.tsx index 8c6fc8863a..1b3fdd4108 100644 --- a/src/pages/Howto/Content/Howto/Howto.test.tsx +++ b/src/pages/Howto/Content/Howto/Howto.test.tsx @@ -14,7 +14,7 @@ import { describe, expect, it, vi } from 'vitest' import { Howto } from './Howto' -import type { IHowtoDB } from 'oa-shared' +import type { ILibrary } from 'oa-shared' import type { HowtoStore } from 'src/stores/Howto/howto.store' const Theme = preciousPlasticTheme.styles @@ -48,7 +48,7 @@ vi.mock('src/common/hooks/useCommonStores', () => ({ const factory = ( howtoStore?: Partial, - overrideHowto?: IHowtoDB, + overrideHowto?: ILibrary.DB, ) => { const ReactStub = createRemixStub([ { diff --git a/src/pages/Howto/Content/Howto/Howto.tsx b/src/pages/Howto/Content/Howto/Howto.tsx index 7ffc681473..d6f3e16753 100644 --- a/src/pages/Howto/Content/Howto/Howto.tsx +++ b/src/pages/Howto/Content/Howto/Howto.tsx @@ -18,15 +18,10 @@ import HowtoDescription from './HowtoDescription/HowtoDescription' import { HowtoDiscussion } from './HowToDiscussion/HowToDiscussion' import Step from './Step/Step' -import type { - IHowtoDB, - IHowtoStep, - IHowToStepFormInput, - IUser, -} from 'oa-shared' +import type { ILibrary, IUser } from 'oa-shared' type HowtoParams = { - howto: IHowtoDB + howto: ILibrary.DB } export const Howto = observer(({ howto }: HowtoParams) => { @@ -87,8 +82,8 @@ export const Howto = observer(({ howto }: HowtoParams) => { } /> - {howto.steps.map((step: IHowToStepFormInput, index: number) => ( - + {howto.steps.map((step: ILibrary.StepInput, index: number) => ( + ))} }> diff --git a/src/pages/Howto/Content/Howto/HowtoDescription/HowtoDescription.tsx b/src/pages/Howto/Content/Howto/HowtoDescription/HowtoDescription.tsx index 42e709d4e7..e096e8718e 100644 --- a/src/pages/Howto/Content/Howto/HowtoDescription/HowtoDescription.tsx +++ b/src/pages/Howto/Content/Howto/HowtoDescription/HowtoDescription.tsx @@ -30,12 +30,12 @@ import { Alert, Box, Card, Divider, Flex, Heading, Image, Text } from 'theme-ui' import { ContentAuthorTimestamp } from '../../../../common/ContentAuthorTimestamp/ContentAuthorTimestamp' import { HowtoDownloads } from '../HowtoDownloads/HowtoDownloads' -import type { IHowtoDB, ITag, IUser } from 'oa-shared' +import type { ILibrary, ITag, IUser } from 'oa-shared' const DELETION_LABEL = 'Project marked for deletion' interface IProps { - howto: IHowtoDB & { tagList?: ITag[] } + howto: ILibrary.DB & { tagList?: ITag[] } loggedInUser: IUser | undefined commentsCount: number votedUsefulCount?: number diff --git a/src/pages/Howto/Content/Howto/HowtoDownloads/HowtoDownloads.tsx b/src/pages/Howto/Content/Howto/HowtoDownloads/HowtoDownloads.tsx index 0342d7a6f5..a06d440fd4 100644 --- a/src/pages/Howto/Content/Howto/HowtoDownloads/HowtoDownloads.tsx +++ b/src/pages/Howto/Content/Howto/HowtoDownloads/HowtoDownloads.tsx @@ -10,10 +10,10 @@ import { updateHowtoDownloadCooldown, } from './downloadCooldown' -import type { IHowtoDB, IUser } from 'oa-shared' +import type { ILibrary, IUser } from 'oa-shared' interface IProps { - howto: IHowtoDB + howto: ILibrary.DB loggedInUser: IUser | undefined } diff --git a/src/pages/Howto/Content/Howto/Step/Step.tsx b/src/pages/Howto/Content/Howto/Step/Step.tsx index 2a07fc7ac1..8f36ac3010 100644 --- a/src/pages/Howto/Content/Howto/Step/Step.tsx +++ b/src/pages/Howto/Content/Howto/Step/Step.tsx @@ -6,10 +6,10 @@ import { formatImagesForGallery } from 'src/utils/formatImageListForGallery' import { capitalizeFirstLetter } from 'src/utils/helpers' import { Box, Card, Flex, Heading, Text } from 'theme-ui' -import type { IHowtoStep } from 'oa-shared' +import type { ILibrary } from 'oa-shared' interface IProps { - step: IHowtoStep + step: ILibrary.Step stepindex: number } diff --git a/src/pages/Howto/Content/HowtoList/HowToCard.tsx b/src/pages/Howto/Content/HowtoList/HowToCard.tsx index c559f3bd37..37e4b0f83c 100644 --- a/src/pages/Howto/Content/HowtoList/HowToCard.tsx +++ b/src/pages/Howto/Content/HowtoList/HowToCard.tsx @@ -11,10 +11,10 @@ import { cdnImageUrl } from 'src/utils/cdnImageUrl' import { capitalizeFirstLetter } from 'src/utils/helpers' import { Box, Card, Flex, Heading, Image } from 'theme-ui' -import type { IHowto } from 'oa-shared' +import type { ILibrary } from 'oa-shared' interface IProps { - howto: IHowto + howto: ILibrary.Item } export const HowToCard = ({ howto }: IProps) => { diff --git a/src/pages/Howto/Content/HowtoList/HowtoList.tsx b/src/pages/Howto/Content/HowtoList/HowtoList.tsx index 441b2bf389..052158723d 100644 --- a/src/pages/Howto/Content/HowtoList/HowtoList.tsx +++ b/src/pages/Howto/Content/HowtoList/HowtoList.tsx @@ -14,7 +14,7 @@ import HowToCard from './HowToCard' import { HowtoHeader } from './HowtoListHeader' import type { DocumentData, QueryDocumentSnapshot } from 'firebase/firestore' -import type { IHowto } from 'oa-shared' +import type { ILibrary } from 'oa-shared' import type { HowtoSortOption } from './HowtoSortOptions' export const HowtoList = observer(() => { @@ -22,13 +22,13 @@ export const HowtoList = observer(() => { const { userStore } = useCommonStores().stores const [isFetching, setIsFetching] = useState(true) - const [howtos, setHowtos] = useState([]) + const [howtos, setHowtos] = useState([]) const [total, setTotal] = useState(0) const [lastVisible, setLastVisible] = useState< QueryDocumentSnapshot | undefined >(undefined) const { draftCount, isFetchingDrafts, drafts, showDrafts, handleShowDrafts } = - useDrafts({ + useDrafts({ getDraftCount: howtoService.getDraftCount, getDrafts: howtoService.getDrafts, }) diff --git a/src/pages/Howto/howto.service.ts b/src/pages/Howto/howto.service.ts index cbe943c4fb..904e0a3c71 100644 --- a/src/pages/Howto/howto.service.ts +++ b/src/pages/Howto/howto.service.ts @@ -23,7 +23,7 @@ import type { QueryFilterConstraint, QueryNonFilterConstraint, } from 'firebase/firestore' -import type { ICategory, IHowto, IHowtoDB, IUserDB } from 'oa-shared' +import type { ICategory, ILibrary, IUserDB } from 'oa-shared' import type { HowtoSortOption } from './Content/HowtoList/HowtoSortOptions' export enum HowtosSearchParams { @@ -55,7 +55,7 @@ const search = async ( : undefined const items = documentSnapshots.docs - ? documentSnapshots.docs.map((x) => x.data() as IHowto) + ? documentSnapshots.docs.map((x) => x.data() as ILibrary.Item) : [] const total = (await getCountFromServer(countQuery)).data().count @@ -166,7 +166,7 @@ const getDrafts = async (userId: string) => { const { itemsQuery } = createDraftQuery(userId) const docs = await getDocs(itemsQuery) - return docs.docs ? docs.docs.map((x) => x.data() as IHowto) : [] + return docs.docs ? docs.docs.map((x) => x.data() as ILibrary.Item) : [] } const getSort = (sort: HowtoSortOption) => { @@ -207,7 +207,7 @@ const getBySlug = async (slug: string) => { return null } - const howto = snapshot.docs[0].data() as IHowtoDB + const howto = snapshot.docs[0].data() as ILibrary.DB if (!howto) { return null diff --git a/src/pages/User/types.ts b/src/pages/User/types.ts index 874aac8cb9..7bbefe1ce8 100644 --- a/src/pages/User/types.ts +++ b/src/pages/User/types.ts @@ -1,6 +1,6 @@ -import type { IHowtoDB, IResearchDB } from 'oa-shared' +import type { ILibrary, IResearchDB } from 'oa-shared' export interface UserCreatedDocs { - howtos: IHowtoDB[] + howtos: ILibrary.DB[] research: IResearchDB[] } diff --git a/src/pages/common/Breadcrumbs/Breadcrumbs.tsx b/src/pages/common/Breadcrumbs/Breadcrumbs.tsx index 4eec4ba177..c3d271503e 100644 --- a/src/pages/common/Breadcrumbs/Breadcrumbs.tsx +++ b/src/pages/common/Breadcrumbs/Breadcrumbs.tsx @@ -1,17 +1,17 @@ import { Breadcrumbs as BreadcrumbsComponent } from 'oa-components' -import type { IHowto, IQuestion, IResearch } from 'oa-shared' +import type { ILibrary, IQuestion, IResearch } from 'oa-shared' type Step = { text: string; link?: string } interface BreadcrumbsProps { steps?: Step[] - content?: IResearch.ItemDB | IQuestion.Item | IHowto + content?: IResearch.ItemDB | IQuestion.Item | ILibrary.Item variant?: 'research' | 'question' | 'howto' } const generateSteps = ( - content: IResearch.ItemDB | IQuestion.Item | IHowto | undefined, + content: IResearch.ItemDB | IQuestion.Item | ILibrary.Item | undefined, variant: 'research' | 'question' | 'howto' | undefined, ) => { const steps: Step[] = [] @@ -40,7 +40,7 @@ const generateSteps = ( steps.push({ text: item.title }) } else if (variant == 'howto') { - const item = content as IHowto + const item = content as ILibrary.Item steps.push({ text: 'Library', link: '/library' }) if (item.category) { diff --git a/src/routes/_.library.$slug._index.tsx b/src/routes/_.library.$slug._index.tsx index 6329b690fa..ecfb771864 100644 --- a/src/routes/_.library.$slug._index.tsx +++ b/src/routes/_.library.$slug._index.tsx @@ -7,7 +7,7 @@ import { pageViewService } from 'src/services/pageViewService.server' import { generateTags, mergeMeta } from 'src/utils/seo.utils' import type { LoaderFunctionArgs } from '@remix-run/node' -import type { IHowtoDB } from 'oa-shared' +import type { ILibrary } from 'oa-shared' export async function loader({ params }: LoaderFunctionArgs) { const howto = await howtoService.getBySlug(params.slug as string) @@ -27,7 +27,7 @@ export function HydrateFallback() { } export const meta = mergeMeta(({ data }) => { - const howto = data?.howto as IHowtoDB + const howto = data?.howto as ILibrary.DB if (!howto) { return [] @@ -40,7 +40,7 @@ export const meta = mergeMeta(({ data }) => { export default function Index() { const data = useLoaderData() - const howto = data.howto as IHowtoDB // there is some inference issue, shouldn't need 'as' + const howto = data.howto as ILibrary.DB // there is some inference issue, shouldn't need 'as' if (!howto) { return diff --git a/src/routes/_.library.$slug.edit.tsx b/src/routes/_.library.$slug.edit.tsx index ff2a0818f9..8607f55be6 100644 --- a/src/routes/_.library.$slug.edit.tsx +++ b/src/routes/_.library.$slug.edit.tsx @@ -5,7 +5,7 @@ import { AuthRoute } from 'src/pages/common/AuthRoute' import EditHowto from 'src/pages/Howto/Content/EditHowto/EditHowto' import { howtoService } from 'src/pages/Howto/howto.service' -import type { IHowtoDB } from 'oa-shared' +import type { ILibrary } from 'oa-shared' import type { LoaderFunctionArgs } from 'react-router' export async function loader({ params }: LoaderFunctionArgs) { @@ -16,7 +16,7 @@ export async function loader({ params }: LoaderFunctionArgs) { export default function Index() { const data = useLoaderData() - const howto = data.howto as IHowtoDB // there is some inference issue, shouldn't need 'as' + const howto = data.howto as ILibrary.DB return ( diff --git a/src/services/userService.server.ts b/src/services/userService.server.ts index 7416456611..820459819c 100644 --- a/src/services/userService.server.ts +++ b/src/services/userService.server.ts @@ -2,7 +2,7 @@ import { collection, getDocs, query, where } from 'firebase/firestore' import { DB_ENDPOINTS, IModerationStatus } from 'oa-shared' import { firestore } from 'src/utils/firebase' -import type { IHowtoDB, IResearchDB, IUserDB } from 'oa-shared' +import type { ILibrary, IResearchDB, IUserDB } from 'oa-shared' import type { UserCreatedDocs } from 'src/pages/User/types' const getById = async (id: string): Promise => { @@ -71,7 +71,7 @@ const getHowtosByAuthor = async (userId: string) => { where('_createdBy', '==', userId), ), ) - ).docs.map((doc) => doc.data() as IHowtoDB) + ).docs.map((doc) => doc.data() as ILibrary.DB) } export const userService = { diff --git a/src/stores/Howto/howto.store.test.ts b/src/stores/Howto/howto.store.test.ts index b0a5c48648..d75a9b4516 100644 --- a/src/stores/Howto/howto.store.test.ts +++ b/src/stores/Howto/howto.store.test.ts @@ -10,7 +10,7 @@ import { FactoryUser } from 'src/test/factories/User' import { HowtoStore } from './howto.store' -import type { IHowtoDB, IUser } from 'oa-shared' +import type { ILibrary, IUser } from 'oa-shared' import type { IRootStore } from '../RootStore' const mockGetDoc = vi.fn() @@ -24,7 +24,7 @@ vi.mock('firebase/firestore', () => ({ })) const factory = async ( - howTos: IHowtoDB[] = [FactoryHowto({})], + howTos: ILibrary.DB[] = [FactoryHowto({})], userOverloads?: Partial, ) => { const store = new HowtoStore({} as IRootStore) diff --git a/src/stores/Howto/howto.store.tsx b/src/stores/Howto/howto.store.tsx index 3a4cb5d00c..a436699c4e 100644 --- a/src/stores/Howto/howto.store.tsx +++ b/src/stores/Howto/howto.store.tsx @@ -9,10 +9,7 @@ import { toggleDocUsefulByUser } from '../common/toggleDocUsefulByUser' import type { IConvertedFileMeta, - IHowto, - IHowtoDB, - IHowtoFormInput, - IHowToStepFormInput, + ILibrary, IUploadedFileMeta, IUser, UserMention, @@ -23,7 +20,7 @@ const COLLECTION_NAME = 'howtos' export class HowtoStore extends ModuleStore { // we have two property relating to docs that can be observed - public uploadStatus: IHowToUploadStatus = getInitialUploadStatus() + public uploadStatus: ILibraryItemUploadStatus = getInitialUploadStatus() constructor(rootStore: IRootStore) { // call constructor on common ModuleStore (with db endpoint), which automatically fetches all docs at @@ -39,14 +36,14 @@ export class HowtoStore extends ModuleStore { } public async toggleUsefulByUser( - howto: IHowtoDB, + howto: ILibrary.DB, userName: string, ): Promise { const updatedItem = (await toggleDocUsefulByUser( COLLECTION_NAME, howto._id, userName, - )) as IHowtoDB + )) as ILibrary.DB runInAction(() => { if ((updatedItem.votedUsefulBy || []).includes(userName)) { @@ -60,7 +57,7 @@ export class HowtoStore extends ModuleStore { }) } - public updateUploadStatus(update: keyof IHowToUploadStatus) { + public updateUploadStatus(update: keyof ILibraryItemUploadStatus) { this.uploadStatus[update] = true } @@ -69,12 +66,14 @@ export class HowtoStore extends ModuleStore { } public async incrementDownloadCount(howToID: string) { - const dbRef = this.db.collection(COLLECTION_NAME).doc(howToID) + const dbRef = this.db + .collection(COLLECTION_NAME) + .doc(howToID) const howToData = await toJS(dbRef.get('server')) const totalDownloads = howToData?.total_downloads || 0 if (howToData) { - const updatedHowto: IHowto = { + const updatedHowto: ILibrary.Item = { ...howToData, total_downloads: totalDownloads! + 1, } @@ -105,10 +104,12 @@ export class HowtoStore extends ModuleStore { } private async updateHowtoItem( - howToItem: IHowto, + howToItem: ILibrary.Item, setLastEditTimestamp = false, ) { - const dbRef = this.db.collection(COLLECTION_NAME).doc(howToItem._id) + const dbRef = this.db + .collection(COLLECTION_NAME) + .doc(howToItem._id) const { text: description, users } = await this.addUserReference( howToItem.description || '', @@ -181,7 +182,7 @@ export class HowtoStore extends ModuleStore { return } - const dbRef = this.db.collection(COLLECTION_NAME).doc(id) + const dbRef = this.db.collection(COLLECTION_NAME).doc(id) const howToData = await dbRef.get('server') if (id && howToData) { @@ -198,14 +199,14 @@ export class HowtoStore extends ModuleStore { // upload a new or update an existing project public async uploadHowTo( - values: IHowtoFormInput | IHowtoDB, - ): Promise { + values: ILibrary.FormInput | ILibrary.DB, + ): Promise { logger.debug('uploading project', { values }) this.updateUploadStatus('Start') // create a reference either to the existing document (if editing) or a new document if creating const dbRef = this.db - .collection(COLLECTION_NAME) - .doc((values as IHowtoDB)._id) + .collection(COLLECTION_NAME) + .doc((values as ILibrary.DB)._id) const id = dbRef.id const existingDoc = await dbRef.get('server') @@ -213,7 +214,7 @@ export class HowtoStore extends ModuleStore { const user = this.activeUser as IUser - let howto: IHowtoDB | null = null + let howto: ILibrary.DB | null = null try { // upload any pending images, avoid trying to re-upload images previously saved // if cover already uploaded stored as object not array @@ -246,7 +247,7 @@ export class HowtoStore extends ModuleStore { const creatorCountry = this.getCreatorCountry(user, values) const fileLink = values.fileLink ?? '' const totalComments = values.totalComments ? values.totalComments : 0 - const mentions = (values as IHowtoDB)?.mentions ?? [] + const mentions = (values as ILibrary.DB)?.mentions ?? [] const slug = await this.setSlug(values) const previousSlugs = this.setPreviousSlugs(values, slug) const total_downloads = values['total_downloads'] ?? 0 @@ -255,7 +256,7 @@ export class HowtoStore extends ModuleStore { const keywords = getKeywords(values.title + ' ' + values.description) keywords.push(_createdBy) - const howToData: IHowto = { + const howToData: ILibrary.Item = { ...(existingDoc || {}), _id, _createdBy, @@ -297,7 +298,7 @@ export class HowtoStore extends ModuleStore { return howto } - private async findMentionsInSteps(steps: IHowToStepFormInput[]) { + private async findMentionsInSteps(steps: ILibrary.StepInput[]) { const stepMentions: UserMention[] = [] for (const step of steps) { @@ -320,7 +321,7 @@ export class HowtoStore extends ModuleStore { } } - private getCreatorCountry(user: IUser, values: IHowtoFormInput) { + private getCreatorCountry(user: IUser, values: ILibrary.FormInput) { const { creatorCountry, _createdBy } = values const userCountry = getUserCountry(user) @@ -344,7 +345,7 @@ export class HowtoStore extends ModuleStore { } } - private async uploadStepImages(steps: IHowToStepFormInput[], id: string) { + private async uploadStepImages(steps: ILibrary.StepInput[], id: string) { for (const step of steps) { // determine any new images to upload const stepImages = (step.images as IConvertedFileMeta[]).filter( @@ -362,7 +363,7 @@ export class HowtoStore extends ModuleStore { } } -interface IHowToUploadStatus { +interface ILibraryItemUploadStatus { Start: boolean Cover: boolean Files: boolean @@ -371,7 +372,7 @@ interface IHowToUploadStatus { Complete: boolean } -const getInitialUploadStatus = (): IHowToUploadStatus => ({ +const getInitialUploadStatus = (): ILibraryItemUploadStatus => ({ Start: false, Cover: false, 'Step Images': false, diff --git a/src/test/factories/Howto.ts b/src/test/factories/Howto.ts index 05f51b9016..6abf661fab 100644 --- a/src/test/factories/Howto.ts +++ b/src/test/factories/Howto.ts @@ -1,14 +1,9 @@ import { faker } from '@faker-js/faker' -import { - DifficultyLevel, - type IHowtoDB, - type IHowtoStep, - IModerationStatus, -} from 'oa-shared' +import { DifficultyLevel, type ILibrary, IModerationStatus } from 'oa-shared' export const FactoryHowto = ( - howtoOverloads: Partial = {}, -): IHowtoDB => ({ + howtoOverloads: Partial = {}, +): ILibrary.DB => ({ files: [], fileLink: '', difficulty_level: faker.helpers.arrayElement([ @@ -61,8 +56,8 @@ export const FactoryHowto = ( }) export const FactoryHowtoStep = ( - howtoStepOverloads: Partial = {}, -): IHowtoStep => ({ + howtoStepOverloads: Partial = {}, +): ILibrary.Step => ({ images: [ { downloadUrl: faker.internet.url(), @@ -80,8 +75,8 @@ export const FactoryHowtoStep = ( }) export const FactoryHowtoDraft = ( - howtoOverloads: Partial = {}, -): IHowtoDB => ({ + howtoOverloads: Partial = {}, +): ILibrary.DB => ({ _id: faker.string.uuid(), _contentModifiedTimestamp: faker.date.past().toString(), _created: faker.date.past().toString(),