Skip to content

Commit b1d4e87

Browse files
authored
Allow event processing cursor to be set in the UI (#55)
1 parent 6a0f60e commit b1d4e87

13 files changed

+746
-344
lines changed

websqrl/components/BlueskyEventStory.tsx

+54-29
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,15 @@ import type { Result } from "../src/types";
44
import { StoryResult } from "./StoryResult";
55

66
export const BLUESKY_FETCH_FEATURES = [
7-
"AuthorUsername",
8-
"AuthorProfileImageUrl",
7+
"Version",
8+
"EventName",
9+
"RecordType",
10+
"AuthorId",
911
"TweetText",
1012
"TweetId",
1113
"TweetDate",
14+
"Subject",
15+
"SubjectCid",
1216
] as const;
1317
export type BlueskyFetchFeature = typeof BLUESKY_FETCH_FEATURES[number];
1418

@@ -19,41 +23,62 @@ export const BlueskyEventStory: React.FC<BlueskyEventStoryProps> = ({
1923
result,
2024
logs,
2125
}) => {
22-
const userUrl = `https://twitter.com/${features.AuthorUsername}`;
23-
const tweetUrl = `https://twitter.com/${features.AuthorUsername}/status/${features.TweetId}`;
26+
const userUrl = `https://bsky.app/profile/${features.AuthorId}`;
27+
28+
if (features.EventName === "create-post") {
29+
return (
30+
<Col gap={10}>
31+
<Row gap={6}>
32+
<a target="_blank" href={userUrl}>
33+
Author: {features.AuthorId}
34+
</a>
35+
<Inline color={styleConstants.secondary}>
36+
({features.TweetDate})
37+
</Inline>
38+
</Row>
39+
40+
{features.TweetText && (
41+
<Block
42+
component="blockquote"
43+
borderLeft="2px solid"
44+
padding="4px 10px"
45+
borderColor={styleConstants.secondary}
46+
>
47+
{features.TweetText}
48+
</Block>
49+
)}
50+
51+
<StoryResult result={result} logs={logs}></StoryResult>
52+
</Col>
53+
);
54+
}
2455

2556
return (
2657
<Col gap={10}>
58+
<p>Event type: {features.EventName}</p>
2759
<Row gap={6}>
28-
<Block
29-
backgroundImage={`url("${features.AuthorProfileImageUrl}")`}
30-
backgroundPosition="center center"
31-
backgroundSize="contain"
32-
height={40}
33-
width={40}
34-
borderRadius={4}
35-
/>
36-
3760
<a target="_blank" href={userUrl}>
38-
@{features.AuthorUsername}
61+
Author: {features.AuthorId}
3962
</a>
40-
<Inline color={styleConstants.secondary}>
41-
(
42-
<a href={tweetUrl} target="_blank">
43-
{features.TweetDate}
44-
</a>
45-
)
46-
</Inline>
63+
<Inline color={styleConstants.secondary}>({features.TweetDate})</Inline>
4764
</Row>
4865

49-
<Block
50-
component="blockquote"
51-
borderLeft="2px solid"
52-
padding="4px 10px"
53-
borderColor={styleConstants.secondary}
54-
>
55-
{features.TweetText}
56-
</Block>
66+
{features.TweetText && (
67+
<Block
68+
component="blockquote"
69+
borderLeft="2px solid"
70+
padding="4px 10px"
71+
borderColor={styleConstants.secondary}
72+
>
73+
{features.TweetText}
74+
</Block>
75+
)}
76+
77+
{features.SubjectCid ? (
78+
<p>Subject CID: {features.SubjectCid}</p>
79+
) : features.Subject ? (
80+
<p>Subject: {features.Subject}</p>
81+
) : null}
5782

5883
<StoryResult result={result} logs={logs}></StoryResult>
5984
</Col>

websqrl/components/Playhead.tsx

+104
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
import { Block } from "jsxstyle";
2+
import { useEffect, useRef, useState } from "react";
3+
import { getFileNameForDate } from "../src/getFileNameForDate";
4+
import { StatusResponse } from "../src/types";
5+
6+
export type PlayheadSpeed = "realtime" | number;
7+
8+
interface PlayheadProps {
9+
speed: PlayheadSpeed;
10+
status: Omit<StatusResponse, "type"> | undefined;
11+
setSpeed: (speed: PlayheadSpeed) => void;
12+
setTimestamp: (timestamp: number) => void;
13+
}
14+
15+
const tenMinutePx = 200;
16+
const oneMinuteMs = 1000 * 60;
17+
const tenMinutesMs = 10 * oneMinuteMs;
18+
// give event processing a minute to upload latest data to S3
19+
const windowBuffer = 11 * oneMinuteMs;
20+
21+
export const Playhead: React.FC<PlayheadProps> = ({ setTimestamp, status }) => {
22+
const [playheadWidth, setPlayheadWidth] = useState<number>(0);
23+
const playheadContainerElementRef = useRef<HTMLDivElement>(null);
24+
const [now, setNow] = useState(new Date().valueOf());
25+
26+
useEffect(() => {
27+
const nowTimer = setInterval(() => {
28+
setNow(new Date().valueOf());
29+
}, 5000);
30+
31+
return () => clearInterval(nowTimer);
32+
});
33+
34+
useEffect(() => {
35+
const ref = playheadContainerElementRef.current;
36+
if (!ref) return;
37+
const resizeObserver = new ResizeObserver((things) => {
38+
for (const thing of things) {
39+
if (thing.target !== ref) continue;
40+
setPlayheadWidth(thing.contentRect.width);
41+
}
42+
});
43+
44+
resizeObserver.observe(ref);
45+
return () => {
46+
resizeObserver.unobserve(ref);
47+
resizeObserver.disconnect();
48+
};
49+
}, [playheadContainerElementRef.current]);
50+
51+
const totalElements = Math.max(playheadWidth / tenMinutePx);
52+
const playheadElements = Array.from({ length: totalElements }).map(
53+
(_, index) => {
54+
const windowDate = new Date(now - tenMinutesMs * index - windowBuffer);
55+
windowDate.setMinutes(Math.floor(windowDate.getMinutes() / 10) * 10);
56+
windowDate.setSeconds(0);
57+
windowDate.setMilliseconds(0);
58+
const key = getFileNameForDate(windowDate);
59+
60+
const isCurrentItem = status?.timestamp === key;
61+
62+
const progressElement = !isCurrentItem ? null : (
63+
<Block>
64+
{(status.currentIndex + 1).toLocaleString()} of{" "}
65+
{status.total.toLocaleString()} events
66+
</Block>
67+
);
68+
69+
return (
70+
<Block
71+
component="button"
72+
key={key}
73+
position="absolute"
74+
appearance="none"
75+
WebkitAppearance="none"
76+
border="none"
77+
backgroundColor="none"
78+
hoverBackgroundColor="none"
79+
width={tenMinutePx}
80+
borderBottom={status?.timestamp === key && "5px solid #888"}
81+
top={0}
82+
bottom={0}
83+
right={tenMinutePx * index}
84+
cursor="pointer"
85+
onClick={() => setTimestamp(windowDate.valueOf())}
86+
>
87+
{windowDate.toLocaleTimeString()}
88+
{progressElement}
89+
</Block>
90+
);
91+
}
92+
);
93+
94+
return (
95+
<Block
96+
height={50}
97+
flex="0 0 auto"
98+
position="relative"
99+
props={{ ref: playheadContainerElementRef }}
100+
>
101+
{playheadElements}
102+
</Block>
103+
);
104+
};

websqrl/components/StoryResult.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ export const StoryResult: React.FC<{
2424

2525
return (
2626
<>
27-
{result.shown && (
27+
{result.shown && result.shownCause && (
2828
<Block color={styleConstants.secondary}>
2929
<Block>Rules fired</Block>
3030
<Block component="ul" listStyle="inside">

0 commit comments

Comments
 (0)