Skip to content

Commit 2813ed0

Browse files
committed
feat: protected reposting post when clicking update
1 parent defd9e0 commit 2813ed0

File tree

6 files changed

+177
-38
lines changed

6 files changed

+177
-38
lines changed

apps/backend/src/api/routes/posts.controller.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -171,9 +171,10 @@ export class PostsController {
171171
changeDate(
172172
@GetOrgFromRequest() org: Organization,
173173
@Param('id') id: string,
174-
@Body('date') date: string
174+
@Body('date') date: string,
175+
@Body('action') action: 'schedule' | 'update' = 'schedule'
175176
) {
176-
return this._postsService.changeDate(org.id, id, date);
177+
return this._postsService.changeDate(org.id, id, date, action);
177178
}
178179

179180
@Post('/separate-posts')

apps/frontend/src/components/launches/calendar.tsx

Lines changed: 66 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ import { deleteDialog } from '@gitroom/react/helpers/delete.dialog';
5454
import { useVariables } from '@gitroom/react/helpers/variable.context';
5555
import { stripHtmlValidation } from '@gitroom/helpers/utils/strip.html.validation';
5656
import { newDayjs } from '@gitroom/frontend/components/layout/set.timezone';
57+
import { Button } from '@gitroom/react/form/button';
5758

5859
// Extend dayjs with necessary plugins
5960
extend(isSameOrAfter);
@@ -418,17 +419,80 @@ export const CalendarColumn: FC<{
418419
accept: 'post',
419420
drop: async (item: any) => {
420421
if (isBeforeNow) return;
422+
423+
// Find the post to check its state
424+
const post = posts.find((p) => p.id === item.id);
425+
let action: 'schedule' | 'update' = 'schedule';
426+
427+
// Check if post is already published or queued in the past
428+
if (
429+
post &&
430+
(post.state === 'PUBLISHED' ||
431+
(post.state === 'QUEUE' && dayjs().isAfter(dayjs.utc(post.publishDate))))
432+
) {
433+
const whatToDo = await new Promise<'schedule' | 'update' | 'cancel'>(
434+
(resolve) => {
435+
modal.openModal({
436+
title: t('what_do_you_want_to_do', 'What do you want to do?'),
437+
children: (
438+
<div className="flex flex-col">
439+
<div className="text-[20px] mb-[20px]">
440+
{t(
441+
'post_already_published_drag',
442+
'This post was already published, what do you want to do?'
443+
)}
444+
</div>
445+
<div className="flex w-full gap-[10px]">
446+
<div className="flex-1 flex">
447+
<Button
448+
type="button"
449+
className="flex-1"
450+
onClick={() => {
451+
modal.closeAll();
452+
resolve('update');
453+
}}
454+
>
455+
{t('just_update_post_details', 'Just update the post details')}
456+
</Button>
457+
</div>
458+
<div className="flex-1 flex">
459+
<Button
460+
type="button"
461+
className="flex-1"
462+
onClick={() => {
463+
modal.closeAll();
464+
resolve('schedule');
465+
}}
466+
>
467+
{t('reschedule_post', 'Reschedule the post')}
468+
</Button>
469+
</div>
470+
</div>
471+
</div>
472+
),
473+
onClose: () => resolve('cancel'),
474+
});
475+
}
476+
);
477+
478+
if (whatToDo === 'cancel') {
479+
return;
480+
}
481+
action = whatToDo;
482+
}
483+
421484
if (!item.interval) {
422485
changeDate(item.id, getDate);
423486
}
424487
const { status } = await fetch(`/posts/${item.id}/date`, {
425488
method: 'PUT',
426489
body: JSON.stringify({
427490
date: getDate.utc().format('YYYY-MM-DDTHH:mm:ss'),
491+
action,
428492
}),
429493
});
430494
if (status !== 500) {
431-
if (item.interval) {
495+
if (item.interval || action === 'schedule') {
432496
reloadCalendarView();
433497
return;
434498
}
@@ -438,7 +502,7 @@ export const CalendarColumn: FC<{
438502
collect: (monitor) => ({
439503
canDrop: isBeforeNow ? false : !!monitor.canDrop() && !!monitor.isOver(),
440504
}),
441-
}));
505+
}), [posts]);
442506

443507
const editPost = useCallback(
444508
(

apps/frontend/src/components/new-launch/manage.modal.tsx

Lines changed: 54 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ import {
4242
} from '@gitroom/frontend/components/ui/icons';
4343
import { useHasScroll } from '@gitroom/frontend/components/ui/is.scroll.hook';
4444
import { useShortlinkPreference } from '@gitroom/frontend/components/settings/shortlink-preference.component';
45+
import dayjs from 'dayjs';
46+
import { Button } from '@gitroom/react/form/button';
4547

4648
function countCharacters(text: string, type: string): number {
4749
if (type !== 'x') {
@@ -108,14 +110,9 @@ export const ManageModal: FC<AddEditModalProps> = (props) => {
108110
return (
109111
<div className="flex items-center gap-[10px]">
110112
<div className="relative">
111-
<SettingsIcon
112-
size={15}
113-
className="text-white"
114-
/>
115-
</div>
116-
<div>
117-
Settings
113+
<SettingsIcon size={15} className="text-white" />
118114
</div>
115+
<div>Settings</div>
119116
</div>
120117
);
121118
}
@@ -202,7 +199,51 @@ export const ManageModal: FC<AddEditModalProps> = (props) => {
202199
}, [existingData, mutate, modal]);
203200

204201
const schedule = useCallback(
205-
(type: 'draft' | 'now' | 'schedule') => async () => {
202+
(type: 'draft' | 'now' | 'schedule' | 'update') => async () => {
203+
if (
204+
(type === 'now' || type === 'schedule') &&
205+
(existingData?.posts?.[0]?.state === 'PUBLISHED' ||
206+
(existingData?.posts?.[0]?.state === 'QUEUE' &&
207+
dayjs().isAfter(date.utc())))
208+
) {
209+
const whatToDo = await new Promise((resolve) => {
210+
modal.openModal({
211+
title: 'What do you want to do?',
212+
children: (
213+
<div className="flex flex-col">
214+
<div className="text-[20px] mb-[20px]">
215+
This post was already published, what do you want to do?
216+
</div>
217+
<div className="flex w-full gap-[10px]">
218+
<div className="flex-1 flex">
219+
<Button
220+
type="button"
221+
className="flex-1"
222+
onClick={() => resolve('update')}
223+
>
224+
Just update the post details
225+
</Button>
226+
</div>
227+
<div className="flex-1 flex">
228+
<Button
229+
type="button"
230+
className="flex-1"
231+
onClick={() => resolve('republish')}
232+
>
233+
Republish the post
234+
</Button>
235+
</div>
236+
</div>
237+
</div>
238+
),
239+
});
240+
});
241+
242+
if (whatToDo === 'update') {
243+
type = 'update';
244+
}
245+
}
246+
206247
setLoading(true);
207248
const checkAllValid = await ref.current.checkAllValid();
208249
if (type !== 'draft') {
@@ -476,10 +517,11 @@ export const ManageModal: FC<AddEditModalProps> = (props) => {
476517
'text-[14px] text-textColor font-[500] relative'
477518
)}
478519
>
479-
<div
480-
className="absolute left-0 top-0 w-full h-full flex flex-col overflow-x-hidden overflow-y-auto scrollbar scrollbar-thumb-newBgColorInner scrollbar-track-newColColor"
481-
>
482-
<div id="social-settings" className="flex flex-col gap-[20px] bg-newBgColor" />
520+
<div className="absolute left-0 top-0 w-full h-full flex flex-col overflow-x-hidden overflow-y-auto scrollbar scrollbar-thumb-newBgColorInner scrollbar-track-newColColor">
521+
<div
522+
id="social-settings"
523+
className="flex flex-col gap-[20px] bg-newBgColor"
524+
/>
483525
</div>
484526
</div>
485527
<style>

libraries/nestjs-libraries/src/database/prisma/posts/posts.repository.ts

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -330,15 +330,29 @@ export class PostsRepository {
330330
return update;
331331
}
332332

333-
async changeDate(orgId: string, id: string, date: string, isDraft: boolean) {
333+
async changeDate(
334+
orgId: string,
335+
id: string,
336+
date: string,
337+
isDraft: boolean,
338+
action: 'schedule' | 'update' = 'schedule'
339+
) {
334340
return this._post.model.post.update({
335341
where: {
336342
organizationId: orgId,
337343
id,
338344
},
339345
data: {
340-
state: isDraft ? 'DRAFT' : 'QUEUE',
341346
publishDate: dayjs(date).toDate(),
347+
// schedule: set state to QUEUE (or DRAFT if it was a draft)
348+
// update: don't change the state
349+
...(action === 'schedule'
350+
? {
351+
state: isDraft ? 'DRAFT' : 'QUEUE',
352+
releaseId: null,
353+
releaseURL: null,
354+
}
355+
: {}),
342356
},
343357
});
344358
}
@@ -366,7 +380,7 @@ export class PostsRepository {
366380
}
367381

368382
async createOrUpdatePost(
369-
state: 'draft' | 'schedule' | 'now',
383+
state: 'draft' | 'schedule' | 'now' | 'update',
370384
orgId: string,
371385
date: string,
372386
body: PostBody,
@@ -405,7 +419,12 @@ export class PostsRepository {
405419
group: uuid,
406420
intervalInDays: inter ? +inter : null,
407421
approvedSubmitForOrder: APPROVED_SUBMIT_FOR_ORDER.NO,
408-
state: state === 'draft' ? ('DRAFT' as const) : ('QUEUE' as const),
422+
...(state === 'update'
423+
? {}
424+
: {
425+
state:
426+
state === 'draft' ? ('DRAFT' as const) : ('QUEUE' as const),
427+
}),
409428
image: JSON.stringify(value.image),
410429
settings: JSON.stringify(body.settings),
411430
organization: {

libraries/nestjs-libraries/src/database/prisma/posts/posts.service.ts

Lines changed: 29 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -627,12 +627,14 @@ export class PostsService {
627627
return [] as any[];
628628
}
629629

630-
this.startWorkflow(
631-
post.settings.__type.split('-')[0].toLowerCase(),
632-
posts[0].id,
633-
orgId,
634-
posts[0].state
635-
).catch((err) => {});
630+
if (body.type !== 'update') {
631+
this.startWorkflow(
632+
post.settings.__type.split('-')[0].toLowerCase(),
633+
posts[0].id,
634+
orgId,
635+
posts[0].state
636+
).catch((err) => {});
637+
}
636638

637639
Sentry.metrics.count('post_created', 1);
638640
postList.push({
@@ -652,23 +654,34 @@ export class PostsService {
652654
return this._postRepository.changeState(id, state, err, body);
653655
}
654656

655-
async changeDate(orgId: string, id: string, date: string) {
657+
async changeDate(
658+
orgId: string,
659+
id: string,
660+
date: string,
661+
action: 'schedule' | 'update' = 'schedule'
662+
) {
656663
const getPostById = await this._postRepository.getPostById(id, orgId);
664+
665+
// schedule: Set status to QUEUE and change date (reschedule the post)
666+
// update: Just change the date without changing the status
657667
const newDate = await this._postRepository.changeDate(
658668
orgId,
659669
id,
660670
date,
661-
getPostById.state === 'DRAFT'
671+
getPostById.state === 'DRAFT',
672+
action
662673
);
663674

664-
try {
665-
await this.startWorkflow(
666-
getPostById.integration.providerIdentifier.split('-')[0].toLowerCase(),
667-
getPostById.id,
668-
orgId,
669-
getPostById.state
670-
);
671-
} catch (err) {}
675+
if (action === 'schedule') {
676+
try {
677+
await this.startWorkflow(
678+
getPostById.integration.providerIdentifier.split('-')[0].toLowerCase(),
679+
getPostById.id,
680+
orgId,
681+
getPostById.state === 'DRAFT' ? 'DRAFT' : 'QUEUE'
682+
);
683+
} catch (err) {}
684+
}
672685

673686
return newDate;
674687
}

libraries/nestjs-libraries/src/dtos/posts/create.post.dto.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,8 +88,8 @@ class Tags {
8888

8989
export class CreatePostDto {
9090
@IsDefined()
91-
@IsIn(['draft', 'schedule', 'now'])
92-
type: 'draft' | 'schedule' | 'now';
91+
@IsIn(['draft', 'schedule', 'now', 'update'])
92+
type: 'draft' | 'schedule' | 'now' | 'update';
9393

9494
@IsOptional()
9595
@IsString()

0 commit comments

Comments
 (0)