Replaced most of the usages of moment with date-fns

This commit is contained in:
Alejandro Celaya
2021-06-24 20:13:06 +02:00
parent ee65c0c050
commit 4be1a295d8
21 changed files with 124 additions and 119 deletions

View File

@@ -1,7 +1,7 @@
import { faTags as tagsIcon } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { isEmpty, pipe } from 'ramda';
import moment from 'moment';
import { parseISO } from 'date-fns';
import SearchField from '../utils/SearchField';
import Tag from '../tags/helpers/Tag';
import { DateRangeSelector } from '../utils/dates/DateRangeSelector';
@@ -16,7 +16,7 @@ interface SearchBarProps {
shortUrlsListParams: ShortUrlsListParams;
}
const dateOrNull = (date?: string) => date ? moment(date) : null;
const dateOrNull = (date?: string) => date ? parseISO(date) : null;
const SearchBar = (colorGenerator: ColorGenerator) => ({ listShortUrls, shortUrlsListParams }: SearchBarProps) => {
const selectedTags = shortUrlsListParams.tags ?? [];

View File

@@ -2,8 +2,8 @@ import { FC, useEffect, useState } from 'react';
import { InputType } from 'reactstrap/lib/Input';
import { Button, FormGroup, Input, Row } from 'reactstrap';
import { isEmpty, pipe, replace, trim } from 'ramda';
import m from 'moment';
import classNames from 'classnames';
import { parseISO } from 'date-fns';
import DateInput, { DateInputProps } from '../utils/DateInput';
import {
supportsCrawlableVisits,
@@ -38,6 +38,7 @@ export interface ShortUrlFormProps {
}
const normalizeTag = pipe(trim, replace(/ /g, '-'));
const toDate = (date?: string | Date): Date | undefined => typeof date === 'string' ? parseISO(date) : date;
export const ShortUrlForm = (
TagsSelector: FC<TagsSelectorProps>,
@@ -74,7 +75,7 @@ export const ShortUrlForm = (
const renderDateInput = (id: DateFields, placeholder: string, props: Partial<DateInputProps> = {}) => (
<div className="form-group">
<DateInput
selected={shortUrlData[id] ? m(shortUrlData[id]) : null}
selected={shortUrlData[id] ? toDate(shortUrlData[id] as string | Date) : null}
placeholderText={placeholder}
isClearable
onChange={(date) => setShortUrlData({ ...shortUrlData, [id]: date })}
@@ -163,8 +164,8 @@ export const ShortUrlForm = (
<div className={limitAccessCardClasses}>
<SimpleCard title="Limit access to the short URL">
{renderOptionalInput('maxVisits', 'Maximum number of visits allowed', 'number', { min: 1 })}
{renderDateInput('validSince', 'Enabled since...', { maxDate: shortUrlData.validUntil ? m(shortUrlData.validUntil) : undefined })}
{renderDateInput('validUntil', 'Enabled until...', { minDate: shortUrlData.validSince ? m(shortUrlData.validSince) : undefined })}
{renderDateInput('validSince', 'Enabled since...', { maxDate: shortUrlData.validUntil ? toDate(shortUrlData.validUntil) : undefined })}
{renderDateInput('validUntil', 'Enabled until...', { minDate: shortUrlData.validSince ? toDate(shortUrlData.validSince) : undefined })}
</SimpleCard>
</div>
</Row>

View File

@@ -1,12 +1,11 @@
import * as m from 'moment';
import { Nullable, OptionalString } from '../../utils/utils';
export interface EditShortUrlData {
longUrl?: string;
tags?: string[];
title?: string;
validSince?: m.Moment | string | null;
validUntil?: m.Moment | string | null;
validSince?: Date | string | null;
validUntil?: Date | string | null;
maxVisits?: number | null;
validateUrl?: boolean;
crawlable?: boolean;

View File

@@ -1,6 +1,5 @@
import { isEmpty } from 'ramda';
import { FC, useEffect, useRef } from 'react';
import Moment from 'react-moment';
import { isEmpty } from 'ramda';
import { ExternalLink } from 'react-external-link';
import ColorGenerator from '../../utils/services/ColorGenerator';
import { StateFlagTimeout } from '../../utils/helpers/hooks';
@@ -8,6 +7,7 @@ import Tag from '../../tags/helpers/Tag';
import { SelectedServer } from '../../servers/data';
import { CopyToClipboardIcon } from '../../utils/CopyToClipboardIcon';
import { ShortUrl } from '../data';
import { Time } from '../../utils/Time';
import ShortUrlVisitsCount from './ShortUrlVisitsCount';
import { ShortUrlsRowMenuProps } from './ShortUrlsRowMenu';
import './ShortUrlsRow.scss';
@@ -53,7 +53,7 @@ const ShortUrlsRow = (
return (
<tr className="short-urls-row">
<td className="indivisible short-urls-row__cell" data-th="Created at: ">
<Moment format="YYYY-MM-DD HH:mm">{shortUrl.dateCreated}</Moment>
<Time date={shortUrl.dateCreated} />
</td>
<td className="short-urls-row__cell" data-th="Short URL: ">
<span className="indivisible short-urls-row__cell--relative">

View File

@@ -1,33 +1,12 @@
import { useRef } from 'react';
import { isNil, dissoc } from 'ramda';
import { isNil } from 'ramda';
import DatePicker, { ReactDatePickerProps } from 'react-datepicker';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faCalendarAlt as calendarIcon } from '@fortawesome/free-regular-svg-icons';
import classNames from 'classnames';
import moment from 'moment';
import './DateInput.scss';
interface DatePropsInterface {
endDate?: moment.Moment | null;
maxDate?: moment.Moment | null;
minDate?: moment.Moment | null;
selected?: moment.Moment | null;
startDate?: moment.Moment | null;
onChange?: (date: moment.Moment | null) => void;
}
export type DateInputProps = DatePropsInterface & Omit<ReactDatePickerProps, keyof DatePropsInterface>;
const transformProps = (props: DateInputProps): ReactDatePickerProps => ({
// @ts-expect-error The DatePicker type definition is wrong. It has a ref prop
...dissoc('ref', props),
endDate: props.endDate?.toDate(),
maxDate: props.maxDate?.toDate(),
minDate: props.minDate?.toDate(),
selected: props.selected?.toDate(),
startDate: props.startDate?.toDate(),
onChange: (date: Date | null) => props.onChange?.(date && moment(date)),
});
export type DateInputProps = ReactDatePickerProps;
const DateInput = (props: DateInputProps) => {
const { className, isClearable, selected } = props;
@@ -37,7 +16,7 @@ const DateInput = (props: DateInputProps) => {
return (
<div className="date-input-container">
<DatePicker
{...transformProps(props)}
{...props}
dateFormat="yyyy-MM-dd"
className={classNames('date-input-container__input form-control', className)}
// @ts-expect-error The DatePicker type definition is wrong. It has a ref prop

18
src/utils/Time.tsx Normal file
View File

@@ -0,0 +1,18 @@
import { parseISO, format as formatDate, getUnixTime, formatDistance } from 'date-fns';
import { isDateObject } from './helpers/date';
interface DateProps {
date: Date | string;
format?: string;
relative?: boolean;
}
export const Time = ({ date, format = 'yyyy-MM-dd HH:mm', relative = false }: DateProps) => {
const dateObject = isDateObject(date) ? date : parseISO(date);
return (
<time dateTime={`${getUnixTime(dateObject)}000`}>
{relative ? `${formatDistance(new Date(), dateObject)} ago` : formatDate(dateObject, format)}
</time>
);
};

View File

@@ -1,10 +1,9 @@
import moment from 'moment';
import DateInput from '../DateInput';
import { DateRange } from './types';
interface DateRangeRowProps extends DateRange {
onStartDateChange: (date: moment.Moment | null) => void;
onEndDateChange: (date: moment.Moment | null) => void;
onStartDateChange: (date: Date | null) => void;
onEndDateChange: (date: Date | null) => void;
disabled?: boolean;
}

View File

@@ -1,10 +1,10 @@
import moment from 'moment';
import { subDays, startOfDay, endOfDay } from 'date-fns';
import { filter, isEmpty } from 'ramda';
import { formatInternational } from '../../helpers/date';
export interface DateRange {
startDate?: moment.Moment | null;
endDate?: moment.Moment | null;
startDate?: Date | null;
endDate?: Date | null;
}
export type DateInterval = 'today' | 'yesterday' | 'last7Days' | 'last30Days' | 'last90Days' | 'last180days' | 'last365Days';
@@ -54,6 +54,8 @@ export const rangeOrIntervalToString = (range?: DateRange | DateInterval): strin
return INTERVAL_TO_STRING_MAP[range];
};
const startOfDaysAgo = (daysAgo: number) => startOfDay(subDays(new Date(), daysAgo));
export const intervalToDateRange = (dateInterval?: DateInterval): DateRange => {
if (!dateInterval) {
return {};
@@ -61,21 +63,19 @@ export const intervalToDateRange = (dateInterval?: DateInterval): DateRange => {
switch (dateInterval) {
case 'today':
return { startDate: moment().startOf('day'), endDate: moment() };
return { startDate: startOfDay(new Date()), endDate: new Date() };
case 'yesterday':
const yesterday = moment().subtract(1, 'day'); // eslint-disable-line no-case-declarations
return { startDate: yesterday.startOf('day'), endDate: yesterday.endOf('day') };
return { startDate: startOfDaysAgo(1), endDate: endOfDay(subDays(new Date(), 1)) };
case 'last7Days':
return { startDate: moment().subtract(7, 'days').startOf('day'), endDate: moment() };
return { startDate: startOfDaysAgo(7), endDate: new Date() };
case 'last30Days':
return { startDate: moment().subtract(30, 'days').startOf('day'), endDate: moment() };
return { startDate: startOfDaysAgo(30), endDate: new Date() };
case 'last90Days':
return { startDate: moment().subtract(90, 'days').startOf('day'), endDate: moment() };
return { startDate: startOfDaysAgo(90), endDate: new Date() };
case 'last180days':
return { startDate: moment().subtract(180, 'days').startOf('day'), endDate: moment() };
return { startDate: startOfDaysAgo(180), endDate: new Date() };
case 'last365Days':
return { startDate: moment().subtract(365, 'days').startOf('day'), endDate: moment() };
return { startDate: startOfDaysAgo(365), endDate: new Date() };
}
return {};

View File

@@ -1,15 +1,20 @@
import * as moment from 'moment';
import { format, formatISO } from 'date-fns';
import { OptionalString } from '../utils';
type MomentOrString = moment.Moment | string;
type NullableDate = MomentOrString | null;
type DateOrString = Date | string;
type NullableDate = DateOrString | null;
const isMomentObject = (date: MomentOrString): date is moment.Moment => typeof (date as moment.Moment).format === 'function';
export const isDateObject = (date: DateOrString): date is Date => typeof date !== 'string';
const formatDateFromFormat = (date?: NullableDate, format?: string): OptionalString =>
!date || !isMomentObject(date) ? date : date.format(format);
const formatDateFromFormat = (date?: NullableDate, theFormat?: string): OptionalString => {
if (!date || !isDateObject(date)) {
return date;
}
export const formatDate = (format = 'YYYY-MM-DD') => (date?: NullableDate) => formatDateFromFormat(date, format);
return theFormat ? format(date, theFormat) : formatISO(date);
};
export const formatDate = (format = 'yyyy-MM-dd') => (date?: NullableDate) => formatDateFromFormat(date, format);
export const formatIsoDate = (date?: NullableDate) => formatDateFromFormat(date, undefined);

View File

@@ -1,7 +1,7 @@
import { UncontrolledTooltip } from 'reactstrap';
import Moment from 'react-moment';
import { ExternalLink } from 'react-external-link';
import { ShortUrlDetail } from '../short-urls/reducers/shortUrlDetail';
import { Time } from '../utils/Time';
import { ShortUrlVisits } from './reducers/shortUrlVisits';
import VisitsHeader from './VisitsHeader';
import './ShortUrlVisitsHeader.scss';
@@ -22,18 +22,14 @@ const ShortUrlVisitsHeader = ({ shortUrlDetail, shortUrlVisits, goBack }: ShortU
const renderDate = () => !shortUrl ? <small>Loading...</small> : (
<span>
<b id="created" className="short-url-visits-header__created-at">
<Moment fromNow>{shortUrl.dateCreated}</Moment>
<Time date={shortUrl.dateCreated} relative />
</b>
<UncontrolledTooltip placement="bottom" target="created">
<Moment format="YYYY-MM-DD HH:mm">{shortUrl.dateCreated}</Moment>
<Time date={shortUrl.dateCreated} />
</UncontrolledTooltip>
</span>
);
const visitsStatsTitle = (
<>
Visits for <ExternalLink href={shortLink} />
</>
);
const visitsStatsTitle = <>Visits for <ExternalLink href={shortLink} /></>;
return (
<VisitsHeader title={visitsStatsTitle} goBack={goBack} visits={visits} shortUrl={shortUrl}>

View File

@@ -1,5 +1,4 @@
import { useEffect, useMemo, useState, useRef } from 'react';
import Moment from 'react-moment';
import classNames from 'classnames';
import { min, splitEvery } from 'ramda';
import {
@@ -16,6 +15,7 @@ import { determineOrderDir, OrderDir } from '../utils/utils';
import { prettify } from '../utils/helpers/numbers';
import { supportsBotVisits } from '../utils/helpers/features';
import { SelectedServer } from '../servers/data';
import { Time } from '../utils/Time';
import { NormalizedOrphanVisit, NormalizedVisit } from './types';
import './VisitsTable.scss';
@@ -194,9 +194,7 @@ const VisitsTable = ({
)}
</td>
)}
<td>
<Moment format="YYYY-MM-DD HH:mm">{visit.date}</Moment>
</td>
<td><Time date={visit.date} /></td>
<td>{visit.country}</td>
<td>{visit.city}</td>
<td>{visit.browser}</td>