Misc fixes from qa pass (#5650)

This commit is contained in:
Tom Moor
2023-08-04 23:40:36 -04:00
committed by GitHub
parent 80acc16791
commit 042ea7b61f
11 changed files with 96 additions and 54 deletions

View File

@@ -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()
),
});

View File

@@ -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 [

View File

@@ -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,
});

View File

@@ -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

View File

@@ -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}

View File

@@ -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>

View File

@@ -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 />,
},
];
}

View File

@@ -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(

View File

@@ -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,
}),
})}
</>
)}

View File

@@ -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?",

View File

@@ -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.
*