diff --git a/src/components/Home/releaseNotesModal.tsx b/src/components/Home/releaseNotesModal.tsx index e46be103f00791fa7fbc9cdcec30e3b71849e711..dfd8fe16895ada6bf46b88fe4bacb773158e3efb 100644 --- a/src/components/Home/releaseNotesModal.tsx +++ b/src/components/Home/releaseNotesModal.tsx @@ -5,6 +5,7 @@ import './releaseNotesModal.scss' import Dialog from '@material-ui/core/Dialog' import { AppStore } from 'store' import { useSelector } from 'react-redux' +import { decoreText } from 'utils/decoreText' interface ReleaseNotesModalProps { open: boolean @@ -52,7 +53,7 @@ const ReleaseNotesModal: React.FC<ReleaseNotesModalProps> = ({ {note.title} </div> <div className="release-note-description text-16-normal"> - {note.description} + {decoreText(note.description)} </div> </div> ))} diff --git a/src/components/Splash/SplashRoot.tsx b/src/components/Splash/SplashRoot.tsx index 3db7ec06dd8dc8ef7969edfef773979b200ad6e6..b15bc34157d07f919829e5f6c4529df0fc5cee88 100644 --- a/src/components/Splash/SplashRoot.tsx +++ b/src/components/Splash/SplashRoot.tsx @@ -116,8 +116,6 @@ const SplashRoot = ({ fadeTimer = 1000, children }: SplashRootProps) => { const migrationsResult: ReleaseNotes = await ms.runMigrations( migrations ) - // Init index - await initializationService.initIndex() // Init last release notes when they exist dispatch( @@ -300,7 +298,7 @@ const SplashRoot = ({ fadeTimer = 1000, children }: SplashRootProps) => { splashStart: true, })) } - } catch (err) { + } catch (err: any) { if (err.message === 'Failed to fetch' && !initStepErrors) { setinitStepErrors(InitStepsErrors.UNKNOWN_ERROR) } diff --git a/src/components/TotalConsumption/TotalConsumption.tsx b/src/components/TotalConsumption/TotalConsumption.tsx index 53fcde843a814f94bb656306306725b15bc0ec68..5f9855ca023ae44e37f1197e4ce746eaacbbb1c0 100644 --- a/src/components/TotalConsumption/TotalConsumption.tsx +++ b/src/components/TotalConsumption/TotalConsumption.tsx @@ -30,10 +30,13 @@ const TotalConsumption: React.FC<TotalConsumptionProps> = ({ useEffect(() => { const calculateTotalValue = async () => { const consumptionService = new ConsumptionService(client) - const activateHalfHourLoad = await consumptionService.checkDoctypeEntries( - FluidType.ELECTRICITY, - TimeStep.HALF_AN_HOUR - ) + const activateHalfHourLoad = + fluidType === FluidType.ELECTRICITY + ? await consumptionService.checkDoctypeEntries( + FluidType.ELECTRICITY, + TimeStep.HALF_AN_HOUR + ) + : false const converterService = new ConverterService() let total = 0 diff --git a/src/migrations/migration.data.ts b/src/migrations/migration.data.ts index d8661b43cca9db00c3a3dea764e2a94525571f41..39a18c6d6fb75441c1114346c73f3ed22126fd55 100644 --- a/src/migrations/migration.data.ts +++ b/src/migrations/migration.data.ts @@ -249,7 +249,7 @@ export const migrations: Migration[] = [ releaseNotes: null, docTypes: PROFILETYPE_DOCTYPE, run: async (_client: Client, docs: any[]): Promise<any[]> => { - function checkDate(d1, d2) { + function checkDate(d1: string, d2: string) { const dtd1: DateTime = DateTime.fromISO(d1) const dtd2: DateTime = DateTime.fromISO(d2) return dtd1.year === dtd2.year && dtd1.month === dtd2.month @@ -531,4 +531,18 @@ export const migrations: Migration[] = [ }) }, }, + { + baseSchemaVersion: 20, + targetSchemaVersion: 21, + appVersion: '1.11.0', + description: 'Inform user of the new SGE konnector', + releaseNotes: { + title: 'Vos connecteurs evoluent !', + description: + "Pour continuer à accéder à vos données, merci de vous reconnecter via ce nouveau parcours. Aucune donnée ne sera perdue, et vos données seront à nouveau mises à jour quotidiennement. <p>Pourquoi ce changement ?</p> Pour faciliter l'accès aux données de consommation au plus grand nombre. Plus besoin de se créer un compte Enedis, l'accès aux données en est facilité. N'hésitez pas à en parler autour de vous ! :)", + }, + docTypes: '', + run: async (): Promise<any> => {}, + isEmpty: true, + }, ] diff --git a/src/migrations/migration.service.spec.ts b/src/migrations/migration.service.spec.ts index 88d32046efdcbec6962f860d9230b96e655094f5..850480717dff11793f01c8873425e2a9f419f400 100644 --- a/src/migrations/migration.service.spec.ts +++ b/src/migrations/migration.service.spec.ts @@ -8,12 +8,28 @@ import { MIGRATION_RESULT_COMPLETE, MIGRATION_RESULT_FAILED, } from './migration.data' +import { Client, QueryResult } from 'cozy-client' +import { Notes, ReleaseNotes } from 'models/releaseNotes.model' +import { Schema } from 'models/schema.models' const migrateSpy = jest.spyOn(Migrate, 'migrate') describe('Migration service', () => { - const ms = new MigrationService(mockClient) - - it('should run migrations', () => { + const ms = new MigrationService(mockClient, jest.fn()) + const releaseNotes: Notes = { + title: '', + description: '', + } + beforeEach(() => { + migrateSpy.mockClear() + }) + it('should run migrations', async () => { + const schema: Schema = { _id: '1', version: 0 } + const mockQueryResult: QueryResult<Schema[]> = { + data: [schema], + bookmark: '', + next: false, + skip: 0, + } const migrations: Migration[] = [ { baseSchemaVersion: 0, @@ -21,7 +37,8 @@ describe('Migration service', () => { appVersion: '1.2.4', description: 'Removing mailToken from profil', docTypes: PROFILE_DOCTYPE, - run: (docs: any[]): Profile[] => { + releaseNotes: releaseNotes, + run: async (client: Client, docs: any[]): Promise<Profile[]> => { return docs.map(doc => { if (doc.mailToken) { delete doc.mailToken @@ -31,11 +48,19 @@ describe('Migration service', () => { }, }, ] - ms.runMigrations(migrations) + mockClient.query.mockResolvedValue(mockQueryResult) + await ms.runMigrations(migrations) expect(migrateSpy).toBeCalledTimes(1) }) - it('should run migrations with one fail', () => { + it('should run migrations with one fail', async () => { + const schema: Schema = { _id: '1', version: 0 } + const mockQueryResult: QueryResult<Schema[]> = { + data: [schema], + bookmark: '', + next: false, + skip: 0, + } const migrations: Migration[] = [ { baseSchemaVersion: 0, @@ -43,7 +68,8 @@ describe('Migration service', () => { appVersion: '1.2.4', description: 'Removing mailToken from profil', docTypes: PROFILE_DOCTYPE, - run: (): Profile[] => { + releaseNotes: releaseNotes, + run: async (client: Client, docs: any[]): Promise<Profile[]> => { return [] }, }, @@ -53,12 +79,20 @@ describe('Migration service', () => { errors: [], } const migrateSpyOneFail = migrateSpy.mockResolvedValueOnce(result) - ms.runMigrations(migrations) + mockClient.query.mockResolvedValue(mockQueryResult) + + await ms.runMigrations(migrations) expect(migrateSpyOneFail).toBeCalledTimes(2) }) - it('should not run migrations with two fail', () => { - migrateSpy.mockClear() + it('should not run migrations with two fail', async () => { + const schema: Schema = { _id: '1', version: 0 } + const mockQueryResult: QueryResult<Schema[]> = { + data: [schema], + bookmark: '', + next: false, + skip: 0, + } const migrations: Migration[] = [ { baseSchemaVersion: 0, @@ -66,7 +100,8 @@ describe('Migration service', () => { appVersion: '1.2.4', description: 'Removing mailToken from profil', docTypes: PROFILE_DOCTYPE, - run: (): Profile[] => { + releaseNotes: releaseNotes, + run: async (client: Client, docs: any[]): Promise<Profile[]> => { return [] }, }, @@ -75,17 +110,28 @@ describe('Migration service', () => { type: MIGRATION_RESULT_FAILED, errors: [], } + mockClient.query.mockResolvedValue(mockQueryResult) const migrateSpyTwoFailsKo = jest .spyOn(Migrate, 'migrate') .mockResolvedValueOnce(result) .mockResolvedValueOnce(result) - ms.runMigrations(migrations) - expect(migrateSpyTwoFailsKo).toBeCalledTimes(1) - expect(migrateSpy).toBeCalledTimes(1) + try { + await ms.runMigrations(migrations) + expect(migrateSpyTwoFailsKo).toBeCalledTimes(1) + expect(migrateSpy).toBeCalledTimes(1) + } catch (error) { + expect(error).toEqual(new Error()) + } }) - it('should run migrations with two fail', () => { - migrateSpy.mockClear() + it('should skip migrations if schema number is up to date', async () => { + const schema: Schema = { _id: '1', version: 1 } + const mockQueryResult: QueryResult<Schema[]> = { + data: [schema], + bookmark: '', + next: false, + skip: 0, + } const migrations: Migration[] = [ { baseSchemaVersion: 0, @@ -93,25 +139,53 @@ describe('Migration service', () => { appVersion: '1.2.4', description: 'Removing mailToken from profil', docTypes: PROFILE_DOCTYPE, - run: (): Profile[] => { + releaseNotes: releaseNotes, + run: async (client: Client, docs: any[]): Promise<Profile[]> => { return [] }, }, ] - const result: MigrationResult = { - type: MIGRATION_RESULT_FAILED, - errors: [], - } - const resultOk: MigrationResult = { - type: MIGRATION_RESULT_COMPLETE, - errors: [], + mockClient.query.mockResolvedValue(mockQueryResult) + + await ms.runMigrations(migrations) + expect(migrateSpy).toBeCalledTimes(0) + }) + it('should run 2 migrations properly from a fresh instance and dont show releasenotes', async () => { + const schema: Schema = { _id: '1', version: 0 } + const mockQueryResult: QueryResult<Schema[]> = { + data: [schema], + bookmark: '', + next: false, + skip: 0, } - const migrateSpyTwoFails = jest - .spyOn(Migrate, 'migrate') - .mockResolvedValueOnce(result) - .mockResolvedValueOnce(resultOk) - ms.runMigrations(migrations) - expect(migrateSpyTwoFails).toBeCalledTimes(1) - expect(migrateSpy).toBeCalledTimes(1) + const migrations: Migration[] = [ + { + baseSchemaVersion: 0, + targetSchemaVersion: 1, + appVersion: '1.2.4', + description: 'Removing mailToken from profil', + docTypes: PROFILE_DOCTYPE, + releaseNotes: releaseNotes, + run: async (client: Client, docs: any[]): Promise<Profile[]> => { + return [] + }, + }, + { + baseSchemaVersion: 1, + targetSchemaVersion: 2, + appVersion: '1.2.4', + description: 'Removing mailToken from profil', + docTypes: PROFILE_DOCTYPE, + releaseNotes: releaseNotes, + run: async (client: Client, docs: any[]): Promise<Profile[]> => { + return [] + }, + }, + ] + mockClient.query.mockResolvedValue(mockQueryResult) + + const res = await ms.runMigrations(migrations) + expect(migrateSpy).toBeCalledTimes(2) + expect(res.show).toBeFalsy() }) }) diff --git a/src/migrations/migration.service.ts b/src/migrations/migration.service.ts index 5d65dddbc2f7796e067b492a96957be7bfb34cde..0ede26effbcc62ae082c685bf34e01da1b581ef5 100644 --- a/src/migrations/migration.service.ts +++ b/src/migrations/migration.service.ts @@ -1,4 +1,4 @@ -import { Client } from 'cozy-client' +import { Client, QueryDefinition, QueryResult, Q } from 'cozy-client' import { Migration, MigrationResult } from './migration.type' import { migrationLog, migrate } from './migration' import { @@ -8,6 +8,8 @@ import { import log from 'utils/logger' import { ReleaseNotes } from 'models/releaseNotes.model' import { InitStepsErrors } from 'models/initialisationSteps.model' +import { Schema } from 'models/schema.models' +import { SCHEMAS_DOCTYPE } from 'doctypes/com-grandlyon-ecolyo-schemas' export class MigrationService { private readonly _client: Client @@ -23,8 +25,18 @@ export class MigrationService { this._client = _client this._setinitStepError = _setinitStepError } + /** + * Return schema version + * @param _client cozyClient + * @returns Promise<number> Version number of schema + */ + public async currentSchemaVersion(_client: Client): Promise<number> { + const query: QueryDefinition = Q(SCHEMAS_DOCTYPE) + const data: QueryResult<Schema[]> = await _client.query(query.limitBy(1)) + return data?.data[0]?.version || 0 + } - async runMigrations(migrations: Migration[]): Promise<ReleaseNotes> { + public async runMigrations(migrations: Migration[]): Promise<ReleaseNotes> { log.info('[Migration] Running migrations...') let releaseStatus = false const releaseNotes: ReleaseNotes = { @@ -36,37 +48,52 @@ export class MigrationService { }, ], } - for (const migration of migrations) { - // First attempt - const migrationResult: MigrationResult = await migrate( - migration, - this._client - ) - log.info(migrationLog(migration, migrationResult)) + const currentVersion = await this.currentSchemaVersion(this._client) + const targetVersion = migrations[migrations.length - 1].targetSchemaVersion + + // Prevent Migration service to run every migration if not needed + if (currentVersion != targetVersion) { + const startMigrationIndex = + migrations.length - (targetVersion - currentVersion) + const migrationsToRun = migrations.splice(startMigrationIndex) + + for (const migration of migrationsToRun) { + // First attempt + const migrationResult: MigrationResult = await migrate( + migration, + this._client + ) + log.info(migrationLog(migration, migrationResult)) - if (migrationResult.type === MIGRATION_RESULT_FAILED) { - // Retry in case of failure - const result = await migrate(migration, this._client) - if (result.type === MIGRATION_RESULT_FAILED) { - // Error in case of second failure - this._setinitStepError(InitStepsErrors.MIGRATION_ERROR) - log.error(migrationLog(migration, result)) - throw new Error() - } else { - log.info(migrationLog(migration, result)) + if (migrationResult.type === MIGRATION_RESULT_FAILED) { + // Retry in case of failure + const result = await migrate(migration, this._client) + if (result.type === MIGRATION_RESULT_FAILED) { + // Error in case of second failure + this._setinitStepError(InitStepsErrors.MIGRATION_ERROR) + log.error(migrationLog(migration, result)) + throw new Error() + } else { + log.info(migrationLog(migration, result)) + } } - } - if ( - migration.releaseNotes !== null && - migrationResult.type === MIGRATION_RESULT_COMPLETE - ) { - releaseNotes.notes.push(migration.releaseNotes) - releaseStatus = true + if ( + migration.releaseNotes !== null && + migrationResult.type === MIGRATION_RESULT_COMPLETE + ) { + releaseNotes.notes.push(migration.releaseNotes) + releaseStatus = true + } } + releaseNotes.show = releaseStatus + // In case of first instance, don't show release notes + if (startMigrationIndex === 0) releaseNotes.show = false + log.info('[Migration] Done') + return releaseNotes + } else { + log.info('[Migration] Skipped Migration Process, already up-to-date') + return releaseNotes } - releaseNotes.show = releaseStatus - log.info('[Migration] Done') - return releaseNotes } } diff --git a/src/migrations/migration.ts b/src/migrations/migration.ts index bf0ee97ec3cb664a4276d2e5e388729bf49bda97..402db829053910fbda726350de22454ebe3a090d 100644 --- a/src/migrations/migration.ts +++ b/src/migrations/migration.ts @@ -130,6 +130,13 @@ export async function migrate( migration: Migration, _client: Client ): Promise<MigrationResult> { + if (migration.isEmpty) { + updateSchemaVersion(_client, migration.targetSchemaVersion) + return { + errors: [], + type: 'MigrationComplete', + } + } if (!(await schemaExist(_client))) { await initSchemaDoctype(_client) } diff --git a/src/migrations/migration.type.ts b/src/migrations/migration.type.ts index c5d87f581a0c47d93f38cf89f0b9a94fe45b9b79..b65e8c2d6209762333643a0b58ea401f4e8c5c7e 100644 --- a/src/migrations/migration.type.ts +++ b/src/migrations/migration.type.ts @@ -30,6 +30,7 @@ export type Migration = { isCreate?: boolean isDeprecated?: boolean queryOptions?: MigrationQueryOptions + isEmpty?: boolean appVersion: string run: (_client: Client, docs: any[]) => Promise<any[]> } diff --git a/src/models/initialisationSteps.model.ts b/src/models/initialisationSteps.model.ts index e139357efe2ce57f7e159f5c1abc6c87c5e8f61e..f8584e812b1262725d10bf1b4041633a660403fa 100644 --- a/src/models/initialisationSteps.model.ts +++ b/src/models/initialisationSteps.model.ts @@ -15,7 +15,6 @@ export enum InitStepsErrors { ECOGESTURE_ERROR = 'ecogesture_error', CHALLENGES_ERROR = 'challenges_error', ANALYSIS_ERROR = 'analysis_error', - INDEX_ERROR = 'index_error', PRICES_ERROR = 'prices_error', CONSOS_ERROR = 'consos_error', PARTNERS_ERROR = 'partners_error', diff --git a/src/services/initialization.service.spec.ts b/src/services/initialization.service.spec.ts index e782eedb8fc165277f36494c357ee67a8b2e6e61..ba4cc353721c95b0bd7e5c48075a534591007763 100644 --- a/src/services/initialization.service.spec.ts +++ b/src/services/initialization.service.spec.ts @@ -175,39 +175,6 @@ describe('Initialization service', () => { mockClient.create.mockClear() }) - describe('initIndex method', () => { - beforeEach(() => { - mockCreateIndexKonnector.mockClear() - mockCreateIndexAccount.mockClear() - }) - it('should return true when all indexes created', async () => { - const mockQueryResult: QueryResult<boolean> = { - data: true, - bookmark: '', - next: false, - skip: 0, - } - mockClient.query.mockResolvedValueOnce(mockQueryResult) - mockCreateIndexKonnector.mockResolvedValueOnce(mockQueryResult) - mockCreateIndexAccount.mockResolvedValueOnce(mockQueryResult) - await expect(initializationService.initIndex()).resolves.toBe(true) - }) - it('should throw error when an index is not created', async () => { - const mockQueryResult: QueryResult<boolean> = { - data: true, - bookmark: '', - next: false, - skip: 0, - } - mockClient.query.mockResolvedValueOnce(mockQueryResult) - mockCreateIndexKonnector.mockRejectedValueOnce(new Error()) - mockCreateIndexAccount.mockResolvedValueOnce(mockQueryResult) - await expect(initializationService.initIndex()).rejects.toThrow( - new Error() - ) - }) - }) - describe('initProfile method', () => { beforeEach(() => { mockGetProfile.mockClear() diff --git a/src/services/initialization.service.ts b/src/services/initialization.service.ts index 77553f5021d696eebf4cde7855088794ea63ca98..e199dff5074c37e92938b06e70795305fd452932 100644 --- a/src/services/initialization.service.ts +++ b/src/services/initialization.service.ts @@ -9,22 +9,11 @@ import { CHALLENGE_DOCTYPE, DUEL_DOCTYPE, ECOGESTURE_DOCTYPE, - EGL_DAY_DOCTYPE, - EGL_MONTH_DOCTYPE, - EGL_YEAR_DOCTYPE, - ENEDIS_DAY_DOCTYPE, - ENEDIS_MINUTE_DOCTYPE, - ENEDIS_MONTH_DOCTYPE, - ENEDIS_YEAR_DOCTYPE, EXPLORATION_DOCTYPE, - GRDF_DAY_DOCTYPE, - GRDF_MONTH_DOCTYPE, - GRDF_YEAR_DOCTYPE, PROFILE_DOCTYPE, QUIZ_DOCTYPE, } from 'doctypes' import { FluidType } from 'enum/fluid.enum' -import { TimeStep } from 'enum/timeStep.enum' import { DateTime } from 'luxon' import { Dataload, @@ -38,13 +27,11 @@ import { import { InitSteps, InitStepsErrors } from 'models/initialisationSteps.model' import { ProfileEcogesture } from 'models/profileEcogesture.model' import React from 'react' -import AccountService from 'services/account.service' import ChallengeService from 'services/challenge.service' import DuelService from 'services/duel.service' import EcogestureService from 'services/ecogesture.service' import ExplorationService from 'services/exploration.service' import FluidService from 'services/fluid.service' -import KonnectorService from 'services/konnector.service' import KonnectorStatusService from 'services/konnectorStatus.service' import ProfileService from 'services/profile.service' import QuizService from 'services/quiz.service' @@ -76,85 +63,6 @@ export default class InitializationService { this._setinitStepError = _setinitStepError } - /* - * Call a query with where clause to create the index if not exist - */ - private async createIndex( - doctype: string, - timestep: TimeStep - ): Promise<object> { - const getMongoSelector = () => { - switch (timestep) { - case TimeStep.YEAR: - return { - year: { - $lte: 9999, - }, - } - case TimeStep.MONTH: - return { - year: { - $lte: 9999, - }, - month: { - $lte: 12, - }, - } - case TimeStep.DAY: - case TimeStep.HALF_AN_HOUR: - return { - year: { - $lte: 9999, - }, - month: { - $lte: 12, - }, - day: { - $lte: 31, - }, - } - default: - return {} - } - } - const query: QueryDefinition = Q(doctype) - .where(getMongoSelector()) - .limitBy(1) - return await this._client.query(query) - } - - /* - * create index for each Doctype - * sucess return: true - * failure throw error - */ - public async initIndex(): Promise<boolean> { - try { - const accountService = new AccountService(this._client) - const konnectorService = new KonnectorService(this._client) - await Promise.all([ - this.createIndex(EGL_YEAR_DOCTYPE, TimeStep.YEAR), - this.createIndex(EGL_MONTH_DOCTYPE, TimeStep.MONTH), - this.createIndex(EGL_DAY_DOCTYPE, TimeStep.DAY), - this.createIndex(ENEDIS_YEAR_DOCTYPE, TimeStep.YEAR), - this.createIndex(ENEDIS_MONTH_DOCTYPE, TimeStep.MONTH), - this.createIndex(ENEDIS_DAY_DOCTYPE, TimeStep.DAY), - this.createIndex(ENEDIS_MINUTE_DOCTYPE, TimeStep.HALF_AN_HOUR), - this.createIndex(GRDF_YEAR_DOCTYPE, TimeStep.YEAR), - this.createIndex(GRDF_MONTH_DOCTYPE, TimeStep.MONTH), - this.createIndex(GRDF_DAY_DOCTYPE, TimeStep.DAY), - konnectorService.createIndexKonnector(), - accountService.createIndexAccount(), - ]) - log.info('[Initialization] Indexes created') - return true - } catch (error) { - this._setinitStepError(InitStepsErrors.INDEX_ERROR) - log.error('Initialization error - initIndex: ', error) - throw error - } - } - /* * Check if profil exist * If not, the profil is created diff --git a/src/services/queryRunner.service.ts b/src/services/queryRunner.service.ts index 4df247207173205cb1fabd42bc7af6dfbdab6b0e..e27c295fb66b9e3ef487eafe3908327c295c8178 100644 --- a/src/services/queryRunner.service.ts +++ b/src/services/queryRunner.service.ts @@ -1,4 +1,3 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ import { Client, QueryDefinition, Q } from 'cozy-client' import { DateTime, Interval } from 'luxon' import { @@ -45,7 +44,6 @@ export default class QueryRunner { limit: number ) { const doctype = this.getRelevantDoctype(fluidType, timeStep) - return Q(doctype) .where(this.getPredicate(timePeriod, timeStep)) .limitBy(limit) @@ -237,7 +235,6 @@ export default class QueryRunner { private getRelevantDoctype(fluidType: FluidType, timeStep: TimeStep) { let doctype = '' - switch (fluidType) { case FluidType.ELECTRICITY: { @@ -357,7 +354,7 @@ export default class QueryRunner { if (timeStep === TimeStep.HALF_AN_HOUR) { const lastDayOfPreviousMonth = { startDate: maxTimePeriod.startDate.plus({ day: -1 }), - endDate: maxTimePeriod.startDate.plus({ day: -1 }).endOf('days'), + endDate: maxTimePeriod.startDate.plus({ day: -1 }).endOf('day'), } const lastDayOfPreviousMonthQuery: QueryDefinition = this.buildMaxQuery( timeStep, diff --git a/src/utils/decoreText.tsx b/src/utils/decoreText.tsx index 595da24170a76bf2f801bd2061cc094eb0f7755b..ebf8d62132f65a943e503a3f2538259c12219fa2 100644 --- a/src/utils/decoreText.tsx +++ b/src/utils/decoreText.tsx @@ -18,6 +18,16 @@ export const decoreText = (line: string, action?: () => void) => { {line.substring(indexEnd + 4, line.length)} </> ) + } else if (line.includes('<p>')) { + const indexStart = line.indexOf('<p>') + const indexEnd = line.indexOf('</p>') + return ( + <> + {line.substring(0, indexStart)} + <p>{line.substring(indexStart + 3, indexEnd)}</p> + {line.substring(indexEnd + 4, line.length)} + </> + ) } else if (line.includes('<span>')) { const indexStart = line.indexOf('<span>') const indexEnd = line.indexOf('</span>')