Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/fresh-dots-dance.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"sideshow": patch
---

Show a pulsing sidecar dot next to the version pill when a post revision was updated recently.
29 changes: 29 additions & 0 deletions viewer/src/Card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ export function frameForSource(source: unknown): { id: string; iframe: HTMLIFram
// one line, max generous enough for a long diff/markdown without runaway growth.
const MIN_FRAME_H = 24;
const MAX_FRAME_H = 4000;
const RECENT_UPDATE_MS = 2 * 60 * 1000;
export function applyFrameHeight(iframe: HTMLIFrameElement, reportedHeight: unknown): void {
iframe.style.height = Math.min(Math.max(Number(reportedHeight), MIN_FRAME_H), MAX_FRAME_H) + "px";
}
Expand Down Expand Up @@ -185,6 +186,7 @@ export function Card(props: { post: Post; standalone?: boolean }) {
const [annotating, setAnnotating] = createSignal(false);
const [anchorDraft, setAnchorDraft] = createSignal<CommentAnchor | null>(null);
const [fullscreenSurface, setFullscreenSurface] = createSignal<FullscreenSurface | null>(null);
const [recentUpdateNow, setRecentUpdateNow] = createSignal(Date.now());
let stopPoll: (() => void) | undefined;

const surfaceTitle = (surfaceIndex: number) =>
Expand All @@ -200,6 +202,15 @@ export function Card(props: { post: Post; standalone?: boolean }) {
const anchoredComments = (surfaceIndex: number) =>
comments().filter((c) => c.postId === props.post.id && c.anchor?.surfaceIndex === surfaceIndex);

const isRecentRevision = () => {
const updatedAt = new Date(props.post.updatedAt).getTime();
return (
props.post.version > 1 &&
Number.isFinite(updatedAt) &&
recentUpdateNow() - updatedAt < RECENT_UPDATE_MS
);
};

const closeFullscreen = () => {
const opener = fullscreenOpener;
fullscreenOpener = undefined;
Expand Down Expand Up @@ -241,6 +252,20 @@ export function Card(props: { post: Post; standalone?: boolean }) {
createEffect(scrollIfTarget);
onCleanup(() => stopPoll?.());

createEffect(() => {
const version = props.post.version;
const updatedAt = new Date(props.post.updatedAt).getTime();
if (version <= 1 || !Number.isFinite(updatedAt)) return;

const now = Date.now();
setRecentUpdateNow(now);
const remaining = RECENT_UPDATE_MS - (now - updatedAt);
if (remaining <= 0) return;

const timer = setTimeout(() => setRecentUpdateNow(Date.now()), remaining + 50);
onCleanup(() => clearTimeout(timer));
});

createEffect(() => {
if (!fullscreenSurface()) return;
queueMicrotask(() => fullscreenCloseButton?.focus());
Expand Down Expand Up @@ -350,6 +375,10 @@ export function Card(props: { post: Post; standalone?: boolean }) {
</select>
)}
</Show>
<Show when={isRecentRevision()}>
<span class="update-dot" title="updated just now" aria-hidden="true"></span>
<span class="update-status">updated recently</span>
</Show>
</span>
<span class="sp"></span>
<span class="card-meta">{relTime(props.post.updatedAt)}</span>
Expand Down
39 changes: 39 additions & 0 deletions viewer/src/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -514,6 +514,12 @@ main {
font-weight: 500;
font-size: 14px;
}
.card-head .vslot {
display: inline-flex;
align-items: center;
gap: 5px;
white-space: nowrap;
}
.vbadge {
font-size: 11px;
color: var(--muted);
Expand All @@ -529,6 +535,36 @@ select.vbadge {
cursor: pointer;
appearance: none;
}
.card-head .update-dot {
width: 7px;
height: 7px;
border-radius: 50%;
background: var(--accent);
box-shadow: 0 0 0 3px var(--accent-bg);
animation: update-dot-pulse 1.6s ease-out infinite;
}
.card-head .update-status {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
@keyframes update-dot-pulse {
0%,
100% {
opacity: 1;
transform: scale(1);
}
60% {
opacity: 0.48;
transform: scale(0.82);
}
}
.card-meta {
font-size: 12px;
color: var(--faint);
Expand Down Expand Up @@ -1860,6 +1896,9 @@ iframe {
color: var(--accent);
}
@media (prefers-reduced-motion: reduce) {
.card-head .update-dot {
animation: none;
}
.topbar .menu,
aside,
#scrim,
Expand Down
Loading