Misc fixes from qa pass (#5650)
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
import { formatDistanceToNow } from "date-fns";
|
||||
import { sortBy } from "lodash";
|
||||
import { observer } from "mobx-react";
|
||||
import * as React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { dateToRelative } from "@shared/utils/date";
|
||||
import Document from "~/models/Document";
|
||||
import User from "~/models/User";
|
||||
import Avatar from "~/components/Avatar";
|
||||
@@ -53,7 +53,7 @@ function DocumentViews({ document, isOpen }: Props) {
|
||||
? t("Currently editing")
|
||||
: t("Currently viewing")
|
||||
: t("Viewed {{ timeAgo }} ago", {
|
||||
timeAgo: formatDistanceToNow(
|
||||
timeAgo: dateToRelative(
|
||||
view ? Date.parse(view.lastViewedAt) : new Date()
|
||||
),
|
||||
});
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { formatDistanceToNow } from "date-fns";
|
||||
import { deburr, difference, sortBy } from "lodash";
|
||||
import { observer } from "mobx-react";
|
||||
import { DOMParser as ProsemirrorDOMParser } from "prosemirror-model";
|
||||
@@ -10,6 +9,7 @@ import { Optional } from "utility-types";
|
||||
import insertFiles from "@shared/editor/commands/insertFiles";
|
||||
import { AttachmentPreset } from "@shared/types";
|
||||
import { Heading } from "@shared/utils/ProsemirrorHelper";
|
||||
import { dateLocale, dateToRelative } from "@shared/utils/date";
|
||||
import { getDataTransferFiles } from "@shared/utils/files";
|
||||
import parseDocumentSlug from "@shared/utils/parseDocumentSlug";
|
||||
import { isInternalUrl } from "@shared/utils/urls";
|
||||
@@ -23,6 +23,7 @@ import useDictionary from "~/hooks/useDictionary";
|
||||
import useEmbeds from "~/hooks/useEmbeds";
|
||||
import useStores from "~/hooks/useStores";
|
||||
import useToasts from "~/hooks/useToasts";
|
||||
import useUserLocale from "~/hooks/useUserLocale";
|
||||
import { NotFoundError } from "~/utils/errors";
|
||||
import { uploadFile } from "~/utils/files";
|
||||
import { isModKey } from "~/utils/keyboard";
|
||||
@@ -60,6 +61,8 @@ function Editor(props: Props, ref: React.RefObject<SharedEditor> | null) {
|
||||
onCreateCommentMark,
|
||||
onDeleteCommentMark,
|
||||
} = props;
|
||||
const userLocale = useUserLocale();
|
||||
const locale = dateLocale(userLocale);
|
||||
const { auth, comments, documents } = useStores();
|
||||
const { showToast } = useToasts();
|
||||
const dictionary = useDictionary();
|
||||
@@ -92,8 +95,10 @@ function Editor(props: Props, ref: React.RefObject<SharedEditor> | null) {
|
||||
|
||||
try {
|
||||
const document = await documents.fetch(slug);
|
||||
const time = formatDistanceToNow(Date.parse(document.updatedAt), {
|
||||
const time = dateToRelative(Date.parse(document.updatedAt), {
|
||||
addSuffix: true,
|
||||
shorten: true,
|
||||
locale,
|
||||
});
|
||||
|
||||
return [
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { format as formatDate, formatDistanceToNow } from "date-fns";
|
||||
import { format as formatDate } from "date-fns";
|
||||
import * as React from "react";
|
||||
import { dateLocale, locales } from "@shared/utils/date";
|
||||
import { dateLocale, dateToRelative, locales } from "@shared/utils/date";
|
||||
import Tooltip from "~/components/Tooltip";
|
||||
import useUserLocale from "~/hooks/useUserLocale";
|
||||
|
||||
@@ -60,26 +60,21 @@ const LocaleTime: React.FC<Props> = ({
|
||||
};
|
||||
}, []);
|
||||
|
||||
const date = new Date(Date.parse(dateTime));
|
||||
const locale = dateLocale(userLocale);
|
||||
let relativeContent = formatDistanceToNow(Date.parse(dateTime), {
|
||||
const relativeContent = dateToRelative(date, {
|
||||
addSuffix,
|
||||
locale,
|
||||
shorten,
|
||||
});
|
||||
|
||||
if (shorten) {
|
||||
relativeContent = relativeContent
|
||||
.replace("about", "")
|
||||
.replace("less than a minute ago", "just now")
|
||||
.replace("minute", "min");
|
||||
}
|
||||
|
||||
const tooltipContent = formatDate(Date.parse(dateTime), formatLocaleLong, {
|
||||
const tooltipContent = formatDate(date, formatLocaleLong, {
|
||||
locale,
|
||||
});
|
||||
const content =
|
||||
relative !== false
|
||||
? relativeContent
|
||||
: formatDate(Date.parse(dateTime), formatLocale, {
|
||||
: formatDate(date, formatLocale, {
|
||||
locale,
|
||||
});
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { formatDistanceToNow } from "date-fns";
|
||||
import * as React from "react";
|
||||
import { dateToRelative } from "@shared/utils/date";
|
||||
import lazyWithRetry from "~/utils/lazyWithRetry";
|
||||
|
||||
const LocaleTime = lazyWithRetry(() => import("~/components/LocaleTime"));
|
||||
@@ -9,17 +9,11 @@ type Props = React.ComponentProps<typeof LocaleTime> & {
|
||||
};
|
||||
|
||||
function Time({ onClick, ...props }: Props) {
|
||||
let content = formatDistanceToNow(Date.parse(props.dateTime), {
|
||||
const content = dateToRelative(Date.parse(props.dateTime), {
|
||||
addSuffix: props.addSuffix,
|
||||
shorten: props.shorten,
|
||||
});
|
||||
|
||||
if (props.shorten) {
|
||||
content = content
|
||||
.replace("about", "")
|
||||
.replace("less than a minute ago", "just now")
|
||||
.replace("minute", "min");
|
||||
}
|
||||
|
||||
return (
|
||||
<span onClick={onClick}>
|
||||
<React.Suspense
|
||||
|
||||
@@ -2,8 +2,8 @@ import {
|
||||
CaretDownIcon,
|
||||
CaretUpIcon,
|
||||
CaseSensitiveIcon,
|
||||
MoreIcon,
|
||||
RegexIcon,
|
||||
ReplaceIcon,
|
||||
} from "outline-icons";
|
||||
import * as React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
@@ -35,6 +35,7 @@ export default function FindAndReplace({ readOnly }: Props) {
|
||||
);
|
||||
const selectionRef = React.useRef<string | undefined>();
|
||||
const inputRef = React.useRef<HTMLInputElement>(null);
|
||||
const inputReplaceRef = React.useRef<HTMLInputElement>(null);
|
||||
const { t } = useTranslation();
|
||||
const theme = useTheme();
|
||||
const [showReplace, setShowReplace] = React.useState(false);
|
||||
@@ -102,10 +103,10 @@ export default function FindAndReplace({ readOnly }: Props) {
|
||||
);
|
||||
|
||||
// Callbacks
|
||||
const handleMore = React.useCallback(
|
||||
() => setShowReplace((state) => !state),
|
||||
[]
|
||||
);
|
||||
const handleMore = React.useCallback(() => {
|
||||
setShowReplace((state) => !state);
|
||||
setTimeout(() => inputReplaceRef.current?.focus(), 100);
|
||||
}, []);
|
||||
|
||||
const handleCaseSensitive = React.useCallback(() => {
|
||||
setCaseSensitive((state) => {
|
||||
@@ -306,12 +307,12 @@ export default function FindAndReplace({ readOnly }: Props) {
|
||||
{navigation}
|
||||
{!readOnly && (
|
||||
<Tooltip
|
||||
tooltip={t("More options")}
|
||||
tooltip={t("Replace options")}
|
||||
delay={500}
|
||||
placement="bottom"
|
||||
>
|
||||
<ButtonLarge onClick={handleMore}>
|
||||
<MoreIcon color={theme.textSecondary} />
|
||||
<ReplaceIcon color={theme.textSecondary} />
|
||||
</ButtonLarge>
|
||||
</Tooltip>
|
||||
)}
|
||||
@@ -322,6 +323,7 @@ export default function FindAndReplace({ readOnly }: Props) {
|
||||
<StyledInput
|
||||
maxLength={255}
|
||||
value={replaceTerm}
|
||||
ref={inputReplaceRef}
|
||||
placeholder={t("Replacement")}
|
||||
onKeyDown={handleReplaceKeyDown}
|
||||
onRequestSubmit={handleReplaceAll}
|
||||
|
||||
@@ -105,7 +105,10 @@ function ToolbarMenu(props: Props) {
|
||||
{item.children ? (
|
||||
<ToolbarDropdown item={item} />
|
||||
) : (
|
||||
<ToolbarButton onClick={handleClick(item)} active={isActive}>
|
||||
<ToolbarButton
|
||||
onClick={handleClick(item)}
|
||||
active={isActive && !item.label}
|
||||
>
|
||||
{item.label && <Label>{item.label}</Label>}
|
||||
{item.icon}
|
||||
</ToolbarButton>
|
||||
|
||||
@@ -12,13 +12,6 @@ export default function dividerMenuItems(
|
||||
const { schema } = state;
|
||||
|
||||
return [
|
||||
{
|
||||
name: "hr",
|
||||
tooltip: dictionary.pageBreak,
|
||||
attrs: { markup: "***" },
|
||||
active: isNodeActive(schema.nodes.hr, { markup: "***" }),
|
||||
icon: <PageBreakIcon />,
|
||||
},
|
||||
{
|
||||
name: "hr",
|
||||
tooltip: dictionary.hr,
|
||||
@@ -26,5 +19,12 @@ export default function dividerMenuItems(
|
||||
active: isNodeActive(schema.nodes.hr, { markup: "---" }),
|
||||
icon: <HorizontalRuleIcon />,
|
||||
},
|
||||
{
|
||||
name: "hr",
|
||||
tooltip: dictionary.pageBreak,
|
||||
attrs: { markup: "***" },
|
||||
active: isNodeActive(schema.nodes.hr, { markup: "***" }),
|
||||
icon: <PageBreakIcon />,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { differenceInMilliseconds, formatDistanceToNow } from "date-fns";
|
||||
import { differenceInMilliseconds } from "date-fns";
|
||||
import { toJS } from "mobx";
|
||||
import { observer } from "mobx-react";
|
||||
import { darken } from "polished";
|
||||
@@ -7,6 +7,7 @@ import { useTranslation } from "react-i18next";
|
||||
import styled, { css } from "styled-components";
|
||||
import breakpoint from "styled-components-breakpoint";
|
||||
import { s } from "@shared/styles";
|
||||
import { dateToRelative } from "@shared/utils/date";
|
||||
import { Minute } from "@shared/utils/time";
|
||||
import Comment from "~/models/Comment";
|
||||
import Avatar from "~/components/Avatar";
|
||||
@@ -37,9 +38,9 @@ function useShowTime(
|
||||
}
|
||||
|
||||
const previousTimeStamp = previousCreatedAt
|
||||
? formatDistanceToNow(Date.parse(previousCreatedAt))
|
||||
? dateToRelative(Date.parse(previousCreatedAt))
|
||||
: undefined;
|
||||
const currentTimeStamp = formatDistanceToNow(Date.parse(createdAt));
|
||||
const currentTimeStamp = dateToRelative(Date.parse(createdAt));
|
||||
|
||||
const msSincePreviousComment = previousCreatedAt
|
||||
? differenceInMilliseconds(
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { formatDistanceToNow } from "date-fns";
|
||||
import invariant from "invariant";
|
||||
import { debounce, isEmpty } from "lodash";
|
||||
import { observer } from "mobx-react";
|
||||
@@ -8,7 +7,7 @@ import { useTranslation, Trans } from "react-i18next";
|
||||
import { Link } from "react-router-dom";
|
||||
import styled from "styled-components";
|
||||
import { s } from "@shared/styles";
|
||||
import { dateLocale } from "@shared/utils/date";
|
||||
import { dateLocale, dateToRelative } from "@shared/utils/date";
|
||||
import { SHARE_URL_SLUG_REGEX } from "@shared/utils/urlHelpers";
|
||||
import Document from "~/models/Document";
|
||||
import Share from "~/models/Share";
|
||||
@@ -193,13 +192,10 @@ function SharePopover({
|
||||
<>
|
||||
.{" "}
|
||||
{t("The shared link was last accessed {{ timeAgo }}.", {
|
||||
timeAgo: formatDistanceToNow(
|
||||
Date.parse(share?.lastAccessedAt),
|
||||
{
|
||||
addSuffix: true,
|
||||
locale,
|
||||
}
|
||||
),
|
||||
timeAgo: dateToRelative(Date.parse(share?.lastAccessedAt), {
|
||||
addSuffix: true,
|
||||
locale,
|
||||
}),
|
||||
})}
|
||||
</>
|
||||
)}
|
||||
|
||||
@@ -253,7 +253,7 @@
|
||||
"Find": "Find",
|
||||
"Match case": "Match case",
|
||||
"Enable regex": "Enable regex",
|
||||
"More options": "More options",
|
||||
"Replace options": "Replace options",
|
||||
"Replacement": "Replacement",
|
||||
"Replace": "Replace",
|
||||
"Replace all": "Replace all",
|
||||
@@ -552,6 +552,7 @@
|
||||
"All users see the same publicly shared view": "All users see the same publicly shared view",
|
||||
"Custom link": "Custom link",
|
||||
"The document will be accessible at <2>{{url}}</2>": "The document will be accessible at <2>{{url}}</2>",
|
||||
"More options": "More options",
|
||||
"Close": "Close",
|
||||
"{{ teamName }} is using {{ appName }} to share documents, please login to continue.": "{{ teamName }} is using {{ appName }} to share documents, please login to continue.",
|
||||
"Are you sure you want to delete the <em>{{ documentTitle }}</em> template?": "Are you sure you want to delete the <em>{{ documentTitle }}</em> template?",
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
/* eslint-disable import/no-duplicates */
|
||||
import { subDays, subMonths, subWeeks, subYears } from "date-fns";
|
||||
import {
|
||||
addSeconds,
|
||||
formatDistanceToNow,
|
||||
subDays,
|
||||
subMonths,
|
||||
subWeeks,
|
||||
subYears,
|
||||
} from "date-fns";
|
||||
import {
|
||||
cs,
|
||||
de,
|
||||
@@ -40,6 +47,44 @@ export function subtractDate(date: Date, period: DateFilter) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a humanized relative time string for the given date.
|
||||
*
|
||||
* @param date The date to convert
|
||||
* @param options The options to pass to date-fns
|
||||
* @returns The relative time string
|
||||
*/
|
||||
export function dateToRelative(
|
||||
date: Date | number,
|
||||
options?: {
|
||||
includeSeconds?: boolean;
|
||||
addSuffix?: boolean;
|
||||
locale?: Locale | undefined;
|
||||
shorten?: boolean;
|
||||
}
|
||||
) {
|
||||
const now = new Date();
|
||||
const parsedDateTime = new Date(date);
|
||||
|
||||
// Protect against "in less than a minute" when users computer clock is off.
|
||||
const normalizedDateTime =
|
||||
parsedDateTime > now && parsedDateTime < addSeconds(now, 60)
|
||||
? now
|
||||
: parsedDateTime;
|
||||
|
||||
const output = formatDistanceToNow(normalizedDateTime, options);
|
||||
|
||||
// Some tweaks to make english language shorter.
|
||||
if (options?.shorten) {
|
||||
return output
|
||||
.replace("about", "")
|
||||
.replace("less than a minute ago", "just now")
|
||||
.replace("minute", "min");
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a locale string from Unicode CLDR format to BCP47 format.
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user