diff --git a/apps/frontend/src/components/new-launch/manage.modal.tsx b/apps/frontend/src/components/new-launch/manage.modal.tsx index 168ff4038..72aa0db10 100644 --- a/apps/frontend/src/components/new-launch/manage.modal.tsx +++ b/apps/frontend/src/components/new-launch/manage.modal.tsx @@ -1,6 +1,6 @@ 'use client'; -import React, { FC, useCallback, useRef, useState } from 'react'; +import React, { FC, useCallback, useEffect, useRef, useState } from 'react'; import { AddEditModalProps } from '@gitroom/frontend/components/new-launch/add.edit.modal'; import clsx from 'clsx'; import { useT } from '@gitroom/react/translation/get.transation.service.client'; @@ -27,6 +27,8 @@ import { SelectCustomer } from '@gitroom/frontend/components/launches/select.cus import { CopilotPopup } from '@copilotkit/react-ui'; import { DummyCodeComponent } from '@gitroom/frontend/components/new-launch/dummy.code.component'; import { stripHtmlValidation } from '@gitroom/helpers/utils/strip.html.validation'; +import { Checkbox } from '@gitroom/react/form/checkbox'; +import dayjs from 'dayjs'; function countCharacters(text: string, type: string): number { if (type !== 'x') { @@ -41,6 +43,7 @@ export const ManageModal: FC = (props) => { const ref = useRef(null); const existingData = useExistingData(); const [loading, setLoading] = useState(false); + const [randomizeMinute, setRandomizeMinute] = useState(true); const toaster = useToaster(); const modal = useModals(); @@ -132,6 +135,26 @@ export const ManageModal: FC = (props) => { [integrations] ); + const didRandomizeInitially = useRef(false); + useEffect(() => { + if (dummy) return; + if (!randomizeMinute) return; + if (existingData.integration) return; + if (!date) return; + if (didRandomizeInitially.current) return; + if (date.minute() !== 0) return; + + const base = date.clone().second(0).millisecond(0); + let minute = Math.floor(Math.random() * 60); + const now = dayjs(); + if (base.isSame(now, 'hour')) { + const minNowPlus1 = now.minute() + 1; + if (minute < minNowPlus1) minute = Math.min(minNowPlus1, 59); + } + setDate(base.minute(minute)); + didRandomizeInitially.current = true; + }, [randomizeMinute, date, dummy, existingData, setDate]); + const schedule = useCallback( (type: 'draft' | 'now' | 'schedule') => async () => { setLoading(true); @@ -233,6 +256,7 @@ export const ManageModal: FC = (props) => { ...(repeater ? { inter: repeater } : {}), tags, shortLink, + randomizeMinute, date: date.utc().format('YYYY-MM-DDTHH:mm:ss'), posts: checkAllValid.map((post: any) => ({ integration: { @@ -332,6 +356,33 @@ export const ManageModal: FC = (props) => { )} + {!dummy && !existingData.integration && ( +
+ { + const next = e?.target ? !!e.target.value : !!e; + setRandomizeMinute(next); + const base = date.clone().second(0).millisecond(0); + if (next) { + const currentMinute = base.minute(); + let minute = Math.floor(Math.random() * 60); + let tries = 0; + while (minute === currentMinute && tries < 5) { + minute = Math.floor(Math.random() * 60); + tries++; + } + setDate(base.minute(minute)); + } else { + setDate(base.minute(0)); + } + }} + /> +
{t('randomize_minute', 'Randomize minute')}
+
+ )} diff --git a/libraries/nestjs-libraries/src/database/prisma/posts/posts.service.ts b/libraries/nestjs-libraries/src/database/prisma/posts/posts.service.ts index fea332712..9722c54ca 100644 --- a/libraries/nestjs-libraries/src/database/prisma/posts/posts.service.ts +++ b/libraries/nestjs-libraries/src/database/prisma/posts/posts.service.ts @@ -1006,4 +1006,4 @@ export class PostsService { message ); } -} +} \ No newline at end of file diff --git a/libraries/nestjs-libraries/src/dtos/posts/create.post.dto.ts b/libraries/nestjs-libraries/src/dtos/posts/create.post.dto.ts index dd9a8471b..f95d4f9e0 100644 --- a/libraries/nestjs-libraries/src/dtos/posts/create.post.dto.ts +++ b/libraries/nestjs-libraries/src/dtos/posts/create.post.dto.ts @@ -83,6 +83,10 @@ export class CreatePostDto { @IsNumber() inter?: number; + @IsOptional() + @IsBoolean() + randomizeMinute?: boolean; + @IsDefined() @IsDateString() date: string;