diff --git a/frontend/src/locale/en/translation.json b/frontend/src/locale/en/translation.json
index e6f40ea..65be9ea 100644
--- a/frontend/src/locale/en/translation.json
+++ b/frontend/src/locale/en/translation.json
@@ -267,6 +267,7 @@
"header": "Recent events",
"warning": "This section only contains events that happened while {{APPNAME}} was open, so only use it for recent stuff",
"anonymous": "An anonymous viewer",
+ "marker": "Events from previous sessions",
"events": {
"follow": "{{name}} followed you",
"redemption": "{{name}} redeemed {{reward}}",
diff --git a/frontend/src/locale/it/translation.json b/frontend/src/locale/it/translation.json
index a95c08d..db38a4d 100644
--- a/frontend/src/locale/it/translation.json
+++ b/frontend/src/locale/it/translation.json
@@ -152,6 +152,7 @@
"anonymous": "Uno spettatore anonimo",
"header": "Eventi recenti",
"warning": "Questa sezione contiene solo gli eventi accaduti mentre {{APPNAME}} era aperto, quindi utilizzala solo per cose recenti",
+ "marker": "Eventi delle sessioni precedenti",
"events": {
"channel-updated": "Informazioni stream modificate",
"cheered": "{{name}} ti ha tifato con {{bits}} bit",
diff --git a/frontend/src/ui/components/LogViewer.tsx b/frontend/src/ui/components/LogViewer.tsx
index b49f595..0317810 100644
--- a/frontend/src/ui/components/LogViewer.tsx
+++ b/frontend/src/ui/components/LogViewer.tsx
@@ -1,8 +1,7 @@
import { ClipboardCopyIcon, Cross2Icon, SizeIcon } from '@radix-ui/react-icons';
import React, { useState } from 'react';
import { useTranslation } from 'react-i18next';
-import { useSelector } from 'react-redux';
-import { RootState } from 'src/store';
+import { useAppSelector } from 'src/store';
import * as DialogPrimitive from '@radix-ui/react-dialog';
import { delay } from '~/lib/time';
import { ProcessedLogEntry } from '~/store/logging/reducer';
@@ -294,12 +293,14 @@ export function LogItem({ data, expandDefault }: LogItemProps) {
const details = Object.entries(data.data).filter(([key]) => key.length > 1);
const [copied, setCopied] = useState(false);
const [showDetails, setShowDetails] = useState(expandDefault ?? false);
+
const copyToClipboard = async () => {
await navigator.clipboard.writeText(JSON.stringify(data.data));
setCopied(true);
await delay(2000);
setCopied(false);
};
+
return (
{formatTime(data.time)}
@@ -355,7 +356,7 @@ interface LogDialogProps {
}
function LogDialog({ initialFilter }: LogDialogProps) {
- const logEntries = useSelector((state: RootState) => state.logging.messages);
+ const logEntries = useAppSelector((state) => state.logging.messages);
const [filter, setFilter] = useState({
...emptyFilter,
...Object.fromEntries(initialFilter.map((f) => [f, true])),
@@ -440,7 +441,7 @@ function LogDialog({ initialFilter }: LogDialogProps) {
}
function LogViewer() {
- const logEntries = useSelector((state: RootState) => state.logging.messages);
+ const logEntries = useAppSelector((state) => state.logging.messages);
const [activeDialog, setActiveDialog] = useState(null);
const count = logEntries.reduce(
diff --git a/frontend/src/ui/pages/Dashboard.tsx b/frontend/src/ui/pages/Dashboard.tsx
index f04917f..cc92e70 100644
--- a/frontend/src/ui/pages/Dashboard.tsx
+++ b/frontend/src/ui/pages/Dashboard.tsx
@@ -16,7 +16,11 @@ import { modules } from '~/store/api/reducer';
import * as HoverCard from '@radix-ui/react-hover-card';
import { useEffect, useState } from 'react';
import { main } from '@wailsapp/go/models';
-import { GetProblems, GetTwitchAuthURL } from '@wailsapp/go/main/App';
+import {
+ GetLastLogs,
+ GetProblems,
+ GetTwitchAuthURL,
+} from '@wailsapp/go/main/App';
import { BrowserOpenURL } from '@wailsapp/runtime/runtime';
import {
PageContainer,
@@ -362,8 +366,64 @@ function TwitchEvent({ data }: { data: EventSubNotification }) {
);
}
-function TwitchEventLog({ events }: { events: EventSubNotification[] }) {
+const block = {
+ content: '""',
+ display: 'inline-block',
+ width: '1rem',
+ height: '0.1rem',
+ margin: '0 0.5rem',
+ backgroundColor: '$gray9',
+};
+
+const SessionMarker = styled('div', {
+ textTransform: 'uppercase',
+ fontWeight: '600',
+ fontSize: '0.75rem',
+ color: '$gray11',
+ margin: '0.25rem',
+ display: 'flex',
+ alignItems: 'center',
+ justifyContent: 'center',
+ '&::before': block,
+ '&::after': block,
+});
+
+interface TwitchEventLogProps {
+ events: EventSubNotification[];
+ dateMarker: number;
+}
+
+type EventItem =
+ | { type: 'marker' }
+ | { type: 'event'; event: EventSubNotification };
+
+function TwitchEventLog({ events, dateMarker }: TwitchEventLogProps) {
const { t } = useTranslation();
+
+ // Filter only supported events and order them by date
+ const orderedEvents: EventItem[] = events
+ .filter((ev) => supportedMessages.includes(ev.subscription.type))
+ .sort((a, b) =>
+ a.date && b.date
+ ? Date.parse(b.date) - Date.parse(a.date)
+ : Date.parse(b.subscription.created_at) -
+ Date.parse(a.subscription.created_at),
+ )
+ .map((ev) => ({ type: 'event', event: ev }));
+
+ // Add marker
+ if (dateMarker) {
+ const index = orderedEvents.findIndex((ev) => {
+ if (ev.type !== 'event') {
+ return false;
+ }
+ return Date.parse(ev.event.date) < dateMarker;
+ });
+ if (index >= 0) {
+ orderedEvents.splice(index, 0, { type: 'marker' });
+ }
+ }
+
return (
<>
@@ -383,17 +443,23 @@ function TwitchEventLog({ events }: { events: EventSubNotification[] }) {
- {events
- .filter((ev) => supportedMessages.includes(ev.subscription.type))
- .sort((a, b) =>
- a.date && b.date
- ? Date.parse(b.date) - Date.parse(a.date)
- : Date.parse(b.subscription.created_at) -
- Date.parse(a.subscription.created_at),
- )
- .map((ev) => (
-
- ))}
+ {orderedEvents.map((ev) => {
+ switch (ev.type) {
+ case 'marker':
+ return (
+
+ {t('pages.dashboard.twitch-events.marker')}
+
+ );
+ default:
+ return (
+
+ );
+ }
+ })}
>
@@ -439,6 +505,19 @@ function TwitchSection() {
const twitchInfo = useLiveKey('twitch/stream-info');
const kv = useAppSelector((state) => state.api.client);
const [twitchEvents, setTwitchEvents] = useState([]);
+ const [oldestLog, setOldestLog] = useState(Date.now());
+
+ useEffect(() => {
+ GetLastLogs().then((res) => {
+ // Get oldest log entry
+ const oldest = res.reduce((acc, log) => {
+ const parsedDate = Date.parse(log.time);
+ return parsedDate < acc ? parsedDate : acc;
+ }, Date.now());
+
+ setOldestLog(oldest);
+ });
+ }, []);
const keyfn = (ev: EventSubNotification) => JSON.stringify(ev);
@@ -493,7 +572,9 @@ function TwitchSection() {
) : (
{t('pages.dashboard.not-live')}
)}
- {twitchEvents ? : null}
+ {twitchEvents ? (
+
+ ) : null}
>
);
}