Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 | 608x 61x 61x 61x 103x 103x 103x 103x 103x 103x 61x 122x 61x 122x 61x 61x 61x 58x 100x 61x 39x 61x 58x 126x 103x 1x 100x 1x 27x | import { addHours, isBefore } from "date-fns";
import { ReactElement, useCallback, useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import { sortBy } from "remeda";
import styled from "styled-components";
import { RaisedCard } from "client/components/RaisedCard";
import { useAppDispatch, useAppSelector } from "client/utils/hooks";
import { useTimeFormatters } from "client/utils/useTimeFormatters";
import { useRealTimeNow } from "client/utils/useTimeNow";
import { EventLogEventMessage } from "client/views/event-log/EventLogEventMessage";
import { submitUpdateEventLogIsSeen } from "client/views/login/loginThunks";
export const EventLog = (): ReactElement => {
const { t } = useTranslation();
const { formatRelativeTime, getWeekdayAndTime } = useTimeFormatters();
// Defined here so it closes over the formatters bound to the active language
const getTime = (createdAt: string, timeNow: Date): string => {
const created = new Date(createdAt);
const relativeTimePeriod = addHours(created, 4);
const useRelativeTime = isBefore(timeNow, relativeTimePeriod);
Eif (useRelativeTime) {
// The clock only advances once a minute, so an item that arrived since
// the last tick is stamped after it. Reading such an item as "in a few
// seconds" would be nonsense, so measure those from their own timestamp.
const reference = isBefore(timeNow, created) ? created : timeNow;
return formatRelativeTime(reference, created);
}
return getWeekdayAndTime(createdAt);
};
const dispatch = useAppDispatch();
const eventLogItems = useAppSelector((state) => state.login.eventLogItems);
const programItems = useAppSelector(
(state) => state.allProgramItems.programItems,
);
// Real time rather than the mocked one: these timestamps are written by the
// server, which the time mock doesn't move
const timeNow = useRealTimeNow();
// Snapshot taken once on mount: the effect below marks the items seen, which
// updates the store, and the list would otherwise drop its unseen
// highlighting while the user is still looking at it
const [localEventLogItems, setLocalEventLogItems] = useState(eventLogItems);
const setEventsSeen = useCallback((): void => {
for (const { eventLogItemId, isSeen } of eventLogItems) {
if (isSeen) {
continue;
}
// eslint-disable-next-line @typescript-eslint/no-floating-promises
dispatch(
submitUpdateEventLogIsSeen({
eventLogItemId,
isSeen: true,
}),
);
}
}, [dispatch, eventLogItems]);
useEffect(() => {
setEventsSeen();
}, [setEventsSeen]);
return (
<div>
<Title>{t("eventLog.title")}</Title>
{localEventLogItems.length === 0 && (
<RaisedCard>{t("eventLog.noNotifications")}</RaisedCard>
)}
{sortBy(localEventLogItems, [(item) => item.createdAt, "desc"]).map(
(eventLogItem) => {
return (
<RaisedCard
isHighlighted={!eventLogItem.isSeen}
key={eventLogItem.eventLogItemId}
data-testid="event-log-item"
>
<EventLogEventMessage
eventLogItem={eventLogItem}
programItems={programItems}
showDetails={true}
/>
<MessageCreatedAt>
<span>{getTime(eventLogItem.createdAt, timeNow)}</span>
</MessageCreatedAt>
</RaisedCard>
);
},
)}
</div>
);
};
const MessageCreatedAt = styled.div`
display: flex;
justify-content: right;
margin: 8px 4px -4px 0;
color: ${(props) => props.theme.textSecondary};
`;
const Title = styled.h1`
font-size: ${(props) => props.theme.fontSizeMainHeader};
`;
|