Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
aa16139
chore: migrate client-side code from moment to date-fns
ggazzo Apr 7, 2026
b5be9ee
fix: update test expectations for date-fns format differences
ggazzo Apr 7, 2026
617a337
fix: revert unrelated UserInfo.tsx changes from rebase
ggazzo Apr 7, 2026
473846e
fix: update UserInfo snapshot for date-fns ordinal format
ggazzo Apr 7, 2026
9421ef9
fix: restore 'moment' in BIP-39 wordlist and handle fractional timezo…
ggazzo Apr 7, 2026
cb2bbc5
fix: address code review findings (P2)
ggazzo Apr 7, 2026
3ac9e71
fix: prettier formatting in ActiveUsersSection
ggazzo Apr 7, 2026
d117091
fix: prettier formatting in ActiveUsersSection
ggazzo Apr 7, 2026
443e7c0
fix: revert cron to ~1.8.2 to avoid breaking server-side business hours
ggazzo Apr 7, 2026
73abbc0
fix: restore cron type declaration for sendAt
ggazzo Apr 7, 2026
c9cc50f
fix: revert RetentionPolicyWarning test for cron@1.8.x behavior
ggazzo Apr 8, 2026
9cb67c4
refactor: remove moment-timezone from @rocket.chat/tools
ggazzo Apr 8, 2026
d83fea1
fix: add new to Intl.DateTimeFormat call to satisfy new-cap lint rule
ggazzo Apr 8, 2026
ab53666
Merge branch 'develop' into chore/remove-moment-client
ggazzo Apr 9, 2026
1f18f19
Merge branch 'develop' into chore/remove-moment-client
ggazzo Apr 9, 2026
3f86b25
Apply suggestion from @ggazzo
ggazzo Apr 9, 2026
4149a8d
Update apps/meteor/client/views/admin/engagementDashboard/users/Conte…
ggazzo Apr 9, 2026
4df4b0f
fix: use real IANA timezones instead of Etc/GMT and map moment Y token
ggazzo Apr 9, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ exports[`renders Default without crashing 1`] = `
>
<div>
<p>
a minute June 1, 2024 at 12:30 AM
1 minute June 1, 2024 at 12:30 AM
</p>
</div>
</div>
Expand Down Expand Up @@ -63,7 +63,7 @@ exports[`renders InvalidSettings without crashing 1`] = `
>
<div>
<p>
a minute June 12, 2024 at 12:00 AM
1 minute June 12, 2024 at 12:00 AM
</p>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ exports[`renders WithDateObject without crashing 1`] = `
aria-hidden="false"
class="rcx-box rcx-box--full rcx-icon--name-mail rcx-icon rcx-css-1p3fsdk"
role="status"
title="Invited January 1, 2025"
title="Invited January 1st, 2025"
>
</i>
Expand All @@ -22,7 +22,7 @@ exports[`renders WithISOStringDate without crashing 1`] = `
aria-hidden="false"
class="rcx-box rcx-box--full rcx-icon--name-mail rcx-icon rcx-css-1p3fsdk"
role="status"
title="Invited January 1, 2025"
title="Invited January 1st, 2025"
>
</i>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -541,7 +541,7 @@ exports[`renders InvitedUser without crashing 1`] = `
<div
class="rcx-box rcx-box--full rcx-css-6hwe8l rcx-css-faoni4"
>
January 1, 2025
January 1st, 2025
</div>
</div>
</div>
Expand Down
88 changes: 74 additions & 14 deletions apps/meteor/client/components/dashboards/periods.ts
Original file line number Diff line number Diff line change
@@ -1,32 +1,92 @@
import type { TranslationKey } from '@rocket.chat/ui-contexts';
import type { DurationInputArg1, DurationInputArg2 } from 'moment';
import moment from 'moment';
import { startOfDay, startOfWeek, startOfMonth, startOfYear, endOfDay, subDays, subMonths } from 'date-fns';

const label = (translationKey: TranslationKey): readonly [translationKey: TranslationKey] => [translationKey];

function startOfDayUTC(d: Date): Date {
return new Date(Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate(), 0, 0, 0, 0));
}

function endOfDayUTC(d: Date): Date {
return new Date(Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate(), 23, 59, 59, 999));
}

function startOfWeekUTC(d: Date): Date {
const day = d.getUTCDay(); // 0 = Sunday
const sunday = new Date(d);
sunday.setUTCDate(d.getUTCDate() - day);
return startOfDayUTC(sunday);
}

function startOfMonthUTC(d: Date): Date {
return new Date(Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), 1, 0, 0, 0, 0));
}

function startOfYearUTC(d: Date): Date {
return new Date(Date.UTC(d.getUTCFullYear(), 0, 1, 0, 0, 0, 0));
}

type StartOf = 'day' | 'year' | 'month' | 'week';
type Subtract = { amount: number; unit: 'days' | 'months' };

export const getClosedPeriod =
({
startOf,
subtract,
startOf: startOfKey,
subtract: subtractOpt,
}: {
startOf: 'day' | 'year' | 'month' | 'week';
subtract?: { amount: DurationInputArg1; unit: DurationInputArg2 };
startOf: StartOf;
subtract?: Subtract;
}): ((utc: boolean) => {
start: Date;
end: Date;
}) =>
(utc): { start: Date; end: Date } => {
const start = utc ? moment().utc() : moment();
const end = utc ? moment().utc() : moment();
(utc: boolean): { start: Date; end: Date } => {
const now = new Date();

let start: Date;
if (subtractOpt) {
const { amount, unit } = subtractOpt;
start = unit === 'days' ? subDays(now, amount) : subMonths(now, amount);
} else {
start = new Date(now.getTime());
}

if (utc) {
switch (startOfKey) {
case 'day':
start = startOfDayUTC(start);
break;
case 'week':
start = startOfWeekUTC(start);
break;
case 'month':
start = startOfMonthUTC(start);
break;
case 'year':
start = startOfYearUTC(start);
break;
}
return { start, end: endOfDayUTC(now) };
}

if (subtract) {
const { amount, unit } = subtract;
start.subtract(amount, unit);
switch (startOfKey) {
case 'day':
start = startOfDay(start);
break;
case 'week':
start = startOfWeek(start);
break;
case 'month':
start = startOfMonth(start);
break;
case 'year':
start = startOfYear(start);
break;
}

return {
start: start.startOf(startOf).toDate(),
end: end.endOf('day').toDate(),
start,
end: endOfDay(now),
};
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ describe('Thread Metrics', () => {
expect(navigateSpy).toHaveBeenCalledWith('thread', 'rid', 'thread', 'mid');

const threadCount = screen.getByTitle('Last_message__date__');
expect(threadCount).toHaveTextContent('5 replies July 1, 2024');
expect(threadCount).toHaveTextContent('5 replies July 1st, 2024');
});

it('should render small not followed with 3 participants and unread', async () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { isRoomFederated } from '@rocket.chat/core-typings';
import type { IRoom, IMessage, ISubscription } from '@rocket.chat/core-typings';
import { usePermission, useSetting, useUser } from '@rocket.chat/ui-contexts';
import moment from 'moment';
import { differenceInMinutes } from 'date-fns';

import type { MessageActionConfig } from '../../../../app/ui-utils/client/lib/MessageAction';
import { useChat } from '../../../views/room/contexts/ChatContext';
Expand Down Expand Up @@ -32,8 +32,7 @@ export const useEditMessageAction = (
}

if (!canBypassBlockTimeLimit && blockEditInMinutes) {
const msgTs = message.ts ? moment(message.ts) : undefined;
const currentTsDiff = msgTs ? moment().diff(msgTs, 'minutes') : undefined;
const currentTsDiff = message.ts ? differenceInMinutes(new Date(), message.ts) : undefined;
return typeof currentTsDiff === 'number' && currentTsDiff < blockEditInMinutes;
}

Expand Down
2 changes: 1 addition & 1 deletion apps/meteor/client/definitions/cron.d.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
declare module 'cron' {
export declare function sendAt(precision: string): Moment;
export declare function sendAt(precision: string): { valueOf(): number };
}
5 changes: 3 additions & 2 deletions apps/meteor/client/hooks/useFormatDate.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { useSetting } from '@rocket.chat/ui-contexts';
import moment from 'moment';
import { useCallback } from 'react';

import { formatDate } from '../lib/utils/dateFormat';

export const useFormatDate = () => {
const format = useSetting('Message_DateFormat');
return useCallback((time: string | Date | number) => moment(time).format(String(format)), [format]);
return useCallback((time: string | Date | number) => formatDate(time, String(format)), [format]);
};
12 changes: 6 additions & 6 deletions apps/meteor/client/hooks/useFormatDateAndTime.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { useUserPreference, useSetting } from '@rocket.chat/ui-contexts';
import type { MomentInput } from 'moment';
import moment from 'moment';
import { useCallback } from 'react';

import { formatDate } from '../lib/utils/dateFormat';

type UseFormatDateAndTimeParams = {
withSeconds?: boolean;
};
Expand All @@ -12,15 +12,15 @@ export const useFormatDateAndTime = ({ withSeconds }: UseFormatDateAndTimeParams
const format = useSetting('Message_TimeAndDateFormat', 'LLL');

return useCallback(
(time: MomentInput) => {
(time: string | Date | number = new Date()) => {
switch (clockMode) {
case 1:
return moment(time).format(withSeconds ? 'MMMM D, Y h:mm:ss A' : 'MMMM D, Y h:mm A');
return formatDate(time, withSeconds ? 'MMMM D, YYYY h:mm:ss A' : 'MMMM D, YYYY h:mm A');
case 2:
return moment(time).format(withSeconds ? 'MMMM D, Y H:mm:ss' : 'MMMM D, Y H:mm');
return formatDate(time, withSeconds ? 'MMMM D, YYYY H:mm:ss' : 'MMMM D, YYYY H:mm');

default:
return moment(time).format(withSeconds ? 'L LTS' : format);
return formatDate(time, withSeconds ? 'L LTS' : format);
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}
},
[clockMode, format, withSeconds],
Expand Down
10 changes: 5 additions & 5 deletions apps/meteor/client/hooks/useFormatTime.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { useUserPreference, useSetting } from '@rocket.chat/ui-contexts';
import type { MomentInput } from 'moment';
import moment from 'moment';
import { useCallback } from 'react';

import { formatDate } from '../lib/utils/dateFormat';

const dayFormat = ['h:mm A', 'H:mm'] as const;

export const useFormatTime = () => {
Expand All @@ -11,14 +11,14 @@ export const useFormatTime = () => {
const sameDay = clockMode !== undefined ? dayFormat[clockMode - 1] : format;

return useCallback(
(time: MomentInput) => {
(time: string | Date | number) => {
switch (clockMode) {
case 1:
case 2:
return moment(time).format(sameDay);
return formatDate(time, sameDay);

default:
return moment(time).format(format);
return formatDate(time, format);
}
},
[clockMode, format, sameDay],
Expand Down
13 changes: 2 additions & 11 deletions apps/meteor/client/hooks/useFormattedRelativeTime.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,5 @@
import moment from 'moment';
import { useMemo } from 'react';

export const useFormattedRelativeTime = (timeMs: number): string =>
useMemo(() => {
moment.relativeTimeThreshold('s', 60);
moment.relativeTimeThreshold('ss', 0);
moment.relativeTimeThreshold('m', 60);
moment.relativeTimeThreshold('h', 24);
moment.relativeTimeThreshold('d', 31);
moment.relativeTimeThreshold('M', 12);
import { formatDurationMs } from '../lib/utils/dateFormat';

return moment.duration(timeMs).humanize();
}, [timeMs]);
export const useFormattedRelativeTime = (timeMs: number): string => useMemo(() => formatDurationMs(timeMs), [timeMs]);
Comment thread
dougfabris marked this conversation as resolved.
Comment thread
dougfabris marked this conversation as resolved.
20 changes: 10 additions & 10 deletions apps/meteor/client/hooks/usePruneWarningMessage.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,9 @@ describe('usePruneWarningMessage hook', () => {
TTLChannels: 60000,
}),
});
expect(result.current).toEqual('a minute June 1, 2024 at 12:30 AM');
expect(result.current).toEqual('1 minute June 1, 2024 at 12:30 AM');
await jest.advanceTimersByTimeAsync(31 * 60 * 1000);
await waitFor(() => expect(result.current).toEqual('a minute June 1, 2024 at 1:00 AM'));
await waitFor(() => expect(result.current).toEqual('1 minute June 1, 2024 at 1:00 AM'));
});

it('Should return the default warning with precision set to every_hour', () => {
Expand All @@ -52,7 +52,7 @@ describe('usePruneWarningMessage hook', () => {
precision: '1',
}),
});
expect(result.current).toEqual('a minute June 1, 2024 at 1:00 AM');
expect(result.current).toEqual('1 minute June 1, 2024 at 1:00 AM');
});

it('Should return the default warning with precision set to every_six_hours', () => {
Expand All @@ -64,7 +64,7 @@ describe('usePruneWarningMessage hook', () => {
precision: '2',
}),
});
expect(result.current).toEqual('a minute June 1, 2024 at 6:00 AM');
expect(result.current).toEqual('1 minute June 1, 2024 at 6:00 AM');
});

it('Should return the default warning with precision set to every_day', () => {
Expand All @@ -76,7 +76,7 @@ describe('usePruneWarningMessage hook', () => {
precision: '3',
}),
});
expect(result.current).toEqual('a minute June 2, 2024 at 12:00 AM');
expect(result.current).toEqual('1 minute June 2, 2024 at 12:00 AM');
});

it('Should return the default warning with advanced precision', () => {
Expand All @@ -89,7 +89,7 @@ describe('usePruneWarningMessage hook', () => {
advancedPrecisionCron: '0 0 1 */1 *',
}),
});
expect(result.current).toEqual('a minute July 1, 2024 at 12:00 AM');
expect(result.current).toEqual('1 minute July 1, 2024 at 12:00 AM');
});
});

Expand All @@ -102,7 +102,7 @@ describe('usePruneWarningMessage hook', () => {
TTLChannels: 60000,
}),
});
expect(result.current).toEqual('a minute June 1, 2024 at 12:30 AM');
expect(result.current).toEqual('1 minute June 1, 2024 at 12:30 AM');
});

it('Should return the unpinned messages warning', () => {
Expand All @@ -114,7 +114,7 @@ describe('usePruneWarningMessage hook', () => {
doNotPrunePinned: true,
}),
});
expect(result.current).toEqual('Unpinned a minute June 1, 2024 at 12:30 AM');
expect(result.current).toEqual('Unpinned 1 minute June 1, 2024 at 12:30 AM');
});

it('Should return the files only warning', () => {
Expand All @@ -127,7 +127,7 @@ describe('usePruneWarningMessage hook', () => {
filesOnly: true,
}),
});
expect(result.current).toEqual('FilesOnly a minute June 1, 2024 at 12:30 AM');
expect(result.current).toEqual('FilesOnly 1 minute June 1, 2024 at 12:30 AM');
});

it('Should return the unpinned files only warning', () => {
Expand All @@ -141,7 +141,7 @@ describe('usePruneWarningMessage hook', () => {
doNotPrunePinned: true,
}),
});
expect(result.current).toEqual('UnpinnedFilesOnly a minute June 1, 2024 at 12:30 AM');
expect(result.current).toEqual('UnpinnedFilesOnly 1 minute June 1, 2024 at 12:30 AM');
});
});

Expand Down
Loading
Loading