Skip to content

Commit

Permalink
feat: long press mod log item (#1869)
Browse files Browse the repository at this point in the history
driveby: fixed negative mod log durations
  • Loading branch information
aeharding authored Feb 27, 2025
1 parent 4a0d455 commit 2ed94f4
Show file tree
Hide file tree
Showing 8 changed files with 102 additions and 26 deletions.
10 changes: 5 additions & 5 deletions src/features/labels/Ago.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { subDays, subMinutes, subMonths, subSeconds, subYears } from "date-fns";
import { describe, expect, it } from "vitest";

import { formatRelative } from "./Ago";
import { formatRelativeToNow } from "./Ago";

describe("formatRelative Function", () => {
describe("formatRelativeToNow Function", () => {
const currentTime = {
date: new Date(),
expected: {
Expand Down Expand Up @@ -82,17 +82,17 @@ describe("formatRelative Function", () => {

testCases.forEach(({ name, date, expected }) => {
it(`should format ${name} correctly in ultrashort format`, () => {
const result = formatRelative(date, "ultrashort");
const result = formatRelativeToNow(date, "ultrashort");
expect(result).toBe(expected.ultrashort);
});

it(`should format ${name} correctly in short format`, () => {
const result = formatRelative(date, "short");
const result = formatRelativeToNow(date, "short");
expect(result).toBe(expected.short);
});

it(`should format ${name} correctly in verbose format`, () => {
const result = formatRelative(date, "verbose");
const result = formatRelativeToNow(date, "verbose");
expect(result).toBe(expected.verbose);
});
});
Expand Down
39 changes: 31 additions & 8 deletions src/features/labels/Ago.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,48 @@ import { round } from "es-toolkit";
import { formatDistanceShort } from "./formatDistanceShort";

interface AgoProps {
/** @default now */
to?: string;
date: string;

as?: "ultrashort" | "short" | "verbose";
shorthand?: boolean;

className?: string;
}

export default function Ago({ date, className, as = "ultrashort" }: AgoProps) {
return (
<span className={className}>{formatRelative(new Date(date), as)}</span>
);
export default function Ago({
date: dateStr,
className,
as = "ultrashort",
to: toStr,
}: AgoProps) {
const to = toStr ? new Date(toStr) : new Date();
const date = new Date(dateStr);

const duration = intervalToDuration({ start: date, end: to });

return <span className={className}>{formatRelative(duration, as)}</span>;
}

export function formatRelative(
export function formatRelativeToNow(
date: Date,
...args: Parameters<typeof formatRelative> extends [unknown, ...infer Rest]
? Rest
: never
) {
const duration = intervalToDuration({
start: date,
end: new Date(),
});

return formatRelative(duration, ...args);
}

function formatRelative(
duration: Duration,
as: AgoProps["as"] = "ultrashort",
): string {
const now = new Date();
const duration = intervalToDuration({ start: date, end: now });

let distance: string;

switch (as) {
Expand Down
8 changes: 4 additions & 4 deletions src/features/labels/Edited.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { MouseEvent } from "react";

import Stat from "#/features/post/detail/Stat";

import { formatRelative } from "./Ago";
import { formatRelativeToNow } from "./Ago";

import styles from "./Edited.module.css";

Expand All @@ -24,8 +24,8 @@ export default function Edited({ item, showDate, className }: EditedProps) {
if (!edited) return;
if (!showDate) return;

const createdLabel = formatRelative(new Date(item.counts.published));
const editedLabel = formatRelative(new Date(edited));
const createdLabel = formatRelativeToNow(new Date(item.counts.published));
const editedLabel = formatRelativeToNow(new Date(edited));

if (createdLabel === editedLabel) return;

Expand All @@ -42,7 +42,7 @@ export default function Edited({ item, showDate, className }: EditedProps) {
const date = new Date(edited);

present({
header: `Edited ${formatRelative(date)} Ago`,
header: `Edited ${formatRelativeToNow(date)} Ago`,
message: `Last edited on ${date.toDateString()} at ${date.toLocaleTimeString(undefined, { hour: "2-digit", minute: "2-digit" })}`,
buttons: ["OK"],
});
Expand Down
2 changes: 1 addition & 1 deletion src/features/moderation/logs/ModRolePersonLink.module.css
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
.icon {
font-size: 1.2em;
vertical-align: middle;
transform: translateY(-1.5px);
transform: translateY(-1px);
}
4 changes: 4 additions & 0 deletions src/features/moderation/logs/ModlogItem.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -68,3 +68,7 @@
color: var(--ion-color-medium2);
}
}

.agoIcon {
font-size: 1.2em;
}
32 changes: 29 additions & 3 deletions src/features/moderation/logs/ModlogItem.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,23 @@
import { IonIcon, IonItem } from "@ionic/react";
import { timerOutline } from "ionicons/icons";
import { Person } from "lemmy-js-client";
import { useCallback } from "react";
import { useRef } from "react";
import { useLongPress } from "use-long-press";

import Ago from "#/features/labels/Ago";
import { cx } from "#/helpers/css";
import { isTouchDevice } from "#/helpers/device";
import { stopIonicTapClick } from "#/helpers/ionic";
import { filterEvents } from "#/helpers/longPress";
import { useBuildGeneralBrowseLink } from "#/helpers/routes";

import { ModeratorRole } from "../useCanModerate";
import useIsAdmin from "../useIsAdmin";
import { ModlogItemType } from "./helpers";
import ModlogItemMoreActions from "./ModlogItemMoreActions";
import ModlogItemMoreActions, {
ModlogItemMoreActionsHandle,
} from "./ModlogItemMoreActions";
import ModRolePersonLink from "./ModRolePersonLink";
import addCommunity from "./types/addCommunity";
import addInstance from "./types/addInstance";
Expand Down Expand Up @@ -116,6 +123,19 @@ export function ModlogItem({ item }: ModLogItemProps) {
return role_ ?? "mod";
})();

const ellipsisHandleRef = useRef<ModlogItemMoreActionsHandle>(null);

const onCommentLongPress = useCallback(() => {
ellipsisHandleRef.current?.present();
stopIonicTapClick();
}, []);

const bind = useLongPress(onCommentLongPress, {
threshold: 800,
cancelOnMovement: 15,
filterEvents,
});

return (
<IonItem
mode="ios" // Use iOS style activatable tap highlight
Expand All @@ -126,6 +146,7 @@ export function ModlogItem({ item }: ModLogItemProps) {
href={undefined}
routerLink={link ? buildGeneralBrowseLink(link) : undefined}
detail={false}
{...bind()}
>
<div className={styles.container}>
<div className={styles.startContent}>
Expand All @@ -135,7 +156,11 @@ export function ModlogItem({ item }: ModLogItemProps) {
<div className={styles.header}>
<div>{title}</div>
<aside>
<ModlogItemMoreActions item={item} role={role} />
<ModlogItemMoreActions
item={item}
role={role}
ref={ellipsisHandleRef}
/>
<Ago date={when} />
</aside>
</div>
Expand All @@ -145,7 +170,8 @@ export function ModlogItem({ item }: ModLogItemProps) {
{by && <ModRolePersonLink role={role} person={by} />}
{expires && (
<aside>
<IonIcon icon={timerOutline} /> <Ago date={expires} />
<IonIcon icon={timerOutline} className={styles.agoIcon} />{" "}
<Ago date={when} to={expires} />
</aside>
)}
</div>
Expand Down
29 changes: 26 additions & 3 deletions src/features/moderation/logs/ModlogItemMoreActions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
peopleOutline,
personOutline,
} from "ionicons/icons";
import { useCallback, useImperativeHandle } from "react";

import { getHandle } from "#/helpers/lemmy";
import useAppNavigation from "#/helpers/useAppNavigation";
Expand All @@ -17,11 +18,17 @@ import styles from "./ModlogItemMoreActions.module.css";
interface ModlogItemMoreActions {
item: ModlogItemType;
role: ModeratorRole;
ref: React.RefObject<ModlogItemMoreActionsHandle>;
}

export interface ModlogItemMoreActionsHandle {
present: () => void;
}

export default function ModlogItemMoreActions({
item,
role,
ref,
}: ModlogItemMoreActions) {
const { navigateToCommunity, navigateToUser } = useAppNavigation();
const [presentActionSheet] = useIonActionSheet();
Expand All @@ -39,7 +46,7 @@ export default function ModlogItemMoreActions({
if ("admin" in item) return item.admin;
})();

function presentMoreActions() {
const present = useCallback(() => {
presentActionSheet({
cssClass: "left-align-buttons",
buttons: compact([
Expand Down Expand Up @@ -77,7 +84,23 @@ export default function ModlogItemMoreActions({
},
]),
});
}
}, [
community,
moderator,
navigateToCommunity,
navigateToUser,
person,
presentActionSheet,
role,
]);

useImperativeHandle(
ref,
() => ({
present,
}),
[present],
);

return (
<button className={styles.button}>
Expand All @@ -86,7 +109,7 @@ export default function ModlogItemMoreActions({
icon={ellipsisHorizontal}
onClick={(e) => {
e.stopPropagation();
presentMoreActions();
present();
}}
/>
</button>
Expand Down
4 changes: 2 additions & 2 deletions src/features/user/Scores.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useIonAlert } from "@ionic/react";
import { PersonAggregates } from "lemmy-js-client";

import Ago, { formatRelative } from "#/features/labels/Ago";
import Ago, { formatRelativeToNow } from "#/features/labels/Ago";
import { formatNumber } from "#/helpers/number";

import styles from "./Scores.module.css";
Expand Down Expand Up @@ -66,7 +66,7 @@ export default function Scores({ aggregates, accountCreated }: ScoreProps) {
className={styles.score}
onClick={() => {
present({
header: `Account is ${formatRelative(creationDate, "verbose")} old`,
header: `Account is ${formatRelativeToNow(creationDate, "verbose")} old`,
message: `Created on ${creationDate.toDateString()} at ${creationDate.toLocaleTimeString(undefined, { hour: "2-digit", minute: "2-digit" })}`,
buttons: [{ text: "OK" }],
});
Expand Down

0 comments on commit 2ed94f4

Please sign in to comment.