mirror of https://git.sr.ht/~ashkeel/strimertul
feat: add marker in recent events to show which events are new
This commit is contained in:
parent
5bdc05ced8
commit
a1fab34a70
|
@ -267,6 +267,7 @@
|
||||||
"header": "Recent events",
|
"header": "Recent events",
|
||||||
"warning": "This section only contains events that happened while {{APPNAME}} was open, so only use it for recent stuff",
|
"warning": "This section only contains events that happened while {{APPNAME}} was open, so only use it for recent stuff",
|
||||||
"anonymous": "An anonymous viewer",
|
"anonymous": "An anonymous viewer",
|
||||||
|
"marker": "Events from previous sessions",
|
||||||
"events": {
|
"events": {
|
||||||
"follow": "<n>{{name}}</n> followed you",
|
"follow": "<n>{{name}}</n> followed you",
|
||||||
"redemption": "<n>{{name}}</n> redeemed <r>{{reward}}</r>",
|
"redemption": "<n>{{name}}</n> redeemed <r>{{reward}}</r>",
|
||||||
|
|
|
@ -152,6 +152,7 @@
|
||||||
"anonymous": "Uno spettatore anonimo",
|
"anonymous": "Uno spettatore anonimo",
|
||||||
"header": "Eventi recenti",
|
"header": "Eventi recenti",
|
||||||
"warning": "Questa sezione contiene solo gli eventi accaduti mentre {{APPNAME}} era aperto, quindi utilizzala solo per cose 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": {
|
"events": {
|
||||||
"channel-updated": "Informazioni stream modificate",
|
"channel-updated": "Informazioni stream modificate",
|
||||||
"cheered": "<n>{{name}}</n> ti ha tifato con <b>{{bits}} bit</b>",
|
"cheered": "<n>{{name}}</n> ti ha tifato con <b>{{bits}} bit</b>",
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
import { ClipboardCopyIcon, Cross2Icon, SizeIcon } from '@radix-ui/react-icons';
|
import { ClipboardCopyIcon, Cross2Icon, SizeIcon } from '@radix-ui/react-icons';
|
||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useSelector } from 'react-redux';
|
import { useAppSelector } from 'src/store';
|
||||||
import { RootState } from 'src/store';
|
|
||||||
import * as DialogPrimitive from '@radix-ui/react-dialog';
|
import * as DialogPrimitive from '@radix-ui/react-dialog';
|
||||||
import { delay } from '~/lib/time';
|
import { delay } from '~/lib/time';
|
||||||
import { ProcessedLogEntry } from '~/store/logging/reducer';
|
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 details = Object.entries(data.data).filter(([key]) => key.length > 1);
|
||||||
const [copied, setCopied] = useState(false);
|
const [copied, setCopied] = useState(false);
|
||||||
const [showDetails, setShowDetails] = useState(expandDefault ?? false);
|
const [showDetails, setShowDetails] = useState(expandDefault ?? false);
|
||||||
|
|
||||||
const copyToClipboard = async () => {
|
const copyToClipboard = async () => {
|
||||||
await navigator.clipboard.writeText(JSON.stringify(data.data));
|
await navigator.clipboard.writeText(JSON.stringify(data.data));
|
||||||
setCopied(true);
|
setCopied(true);
|
||||||
await delay(2000);
|
await delay(2000);
|
||||||
setCopied(false);
|
setCopied(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<LogEntryContainer level={levelStyle}>
|
<LogEntryContainer level={levelStyle}>
|
||||||
<LogTime level={levelStyle}>{formatTime(data.time)}</LogTime>
|
<LogTime level={levelStyle}>{formatTime(data.time)}</LogTime>
|
||||||
|
@ -355,7 +356,7 @@ interface LogDialogProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
function LogDialog({ initialFilter }: LogDialogProps) {
|
function LogDialog({ initialFilter }: LogDialogProps) {
|
||||||
const logEntries = useSelector((state: RootState) => state.logging.messages);
|
const logEntries = useAppSelector((state) => state.logging.messages);
|
||||||
const [filter, setFilter] = useState({
|
const [filter, setFilter] = useState({
|
||||||
...emptyFilter,
|
...emptyFilter,
|
||||||
...Object.fromEntries(initialFilter.map((f) => [f, true])),
|
...Object.fromEntries(initialFilter.map((f) => [f, true])),
|
||||||
|
@ -440,7 +441,7 @@ function LogDialog({ initialFilter }: LogDialogProps) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function LogViewer() {
|
function LogViewer() {
|
||||||
const logEntries = useSelector((state: RootState) => state.logging.messages);
|
const logEntries = useAppSelector((state) => state.logging.messages);
|
||||||
const [activeDialog, setActiveDialog] = useState<LogLevel>(null);
|
const [activeDialog, setActiveDialog] = useState<LogLevel>(null);
|
||||||
|
|
||||||
const count = logEntries.reduce(
|
const count = logEntries.reduce(
|
||||||
|
|
|
@ -16,7 +16,11 @@ import { modules } from '~/store/api/reducer';
|
||||||
import * as HoverCard from '@radix-ui/react-hover-card';
|
import * as HoverCard from '@radix-ui/react-hover-card';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { main } from '@wailsapp/go/models';
|
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 { BrowserOpenURL } from '@wailsapp/runtime/runtime';
|
||||||
import {
|
import {
|
||||||
PageContainer,
|
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();
|
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 (
|
return (
|
||||||
<>
|
<>
|
||||||
<HoverCard.Root>
|
<HoverCard.Root>
|
||||||
|
@ -383,17 +443,23 @@ function TwitchEventLog({ events }: { events: EventSubNotification[] }) {
|
||||||
</HoverCard.Root>
|
</HoverCard.Root>
|
||||||
<Scrollbar vertical={true} viewport={{ maxHeight: '250px' }}>
|
<Scrollbar vertical={true} viewport={{ maxHeight: '250px' }}>
|
||||||
<EventListContainer>
|
<EventListContainer>
|
||||||
{events
|
{orderedEvents.map((ev) => {
|
||||||
.filter((ev) => supportedMessages.includes(ev.subscription.type))
|
switch (ev.type) {
|
||||||
.sort((a, b) =>
|
case 'marker':
|
||||||
a.date && b.date
|
return (
|
||||||
? Date.parse(b.date) - Date.parse(a.date)
|
<SessionMarker key="marker">
|
||||||
: Date.parse(b.subscription.created_at) -
|
{t('pages.dashboard.twitch-events.marker')}
|
||||||
Date.parse(a.subscription.created_at),
|
</SessionMarker>
|
||||||
)
|
);
|
||||||
.map((ev) => (
|
default:
|
||||||
<TwitchEvent key={eventSubKeyFunction(ev)} data={ev} />
|
return (
|
||||||
))}
|
<TwitchEvent
|
||||||
|
key={eventSubKeyFunction(ev.event)}
|
||||||
|
data={ev.event}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
})}
|
||||||
</EventListContainer>
|
</EventListContainer>
|
||||||
</Scrollbar>
|
</Scrollbar>
|
||||||
</>
|
</>
|
||||||
|
@ -439,6 +505,19 @@ function TwitchSection() {
|
||||||
const twitchInfo = useLiveKey<StreamInfo[]>('twitch/stream-info');
|
const twitchInfo = useLiveKey<StreamInfo[]>('twitch/stream-info');
|
||||||
const kv = useAppSelector((state) => state.api.client);
|
const kv = useAppSelector((state) => state.api.client);
|
||||||
const [twitchEvents, setTwitchEvents] = useState<EventSubNotification[]>([]);
|
const [twitchEvents, setTwitchEvents] = useState<EventSubNotification[]>([]);
|
||||||
|
const [oldestLog, setOldestLog] = useState(Date.now());
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
GetLastLogs().then((res) => {
|
||||||
|
// Get oldest log entry
|
||||||
|
const oldest = res.reduce<number>((acc, log) => {
|
||||||
|
const parsedDate = Date.parse(log.time);
|
||||||
|
return parsedDate < acc ? parsedDate : acc;
|
||||||
|
}, Date.now());
|
||||||
|
|
||||||
|
setOldestLog(oldest);
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
const keyfn = (ev: EventSubNotification) => JSON.stringify(ev);
|
const keyfn = (ev: EventSubNotification) => JSON.stringify(ev);
|
||||||
|
|
||||||
|
@ -493,7 +572,9 @@ function TwitchSection() {
|
||||||
) : (
|
) : (
|
||||||
<TextBlock>{t('pages.dashboard.not-live')}</TextBlock>
|
<TextBlock>{t('pages.dashboard.not-live')}</TextBlock>
|
||||||
)}
|
)}
|
||||||
{twitchEvents ? <TwitchEventLog events={twitchEvents} /> : null}
|
{twitchEvents ? (
|
||||||
|
<TwitchEventLog events={twitchEvents} dateMarker={oldestLog} />
|
||||||
|
) : null}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue