-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
318 additions
and
13 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,187 @@ | ||
<script lang="ts"> | ||
import type { Action } from "@bindings/cob/patch/Action"; | ||
import type { Operation } from "@bindings/cob/Operation"; | ||
import type { Author } from "@bindings/cob/Author"; | ||
type FlatAction = Action & { | ||
id: string; | ||
author: Author; | ||
timestamp: number; | ||
}; | ||
type FlatActionWithPrevious = Action & { | ||
previous?: Action; | ||
}; | ||
type StateTracker = Record<Action["type"], Action>; | ||
import Label from "@app/components/Label.svelte"; | ||
import PatchStateBadge from "./PatchStateBadge.svelte"; | ||
import NodeId from "./NodeId.svelte"; | ||
import { authorForNodeId } from "@app/lib/utils"; | ||
import Id from "./Id.svelte"; | ||
import Icon from "./Icon.svelte"; | ||
interface Props { | ||
activity: Operation<Action>[]; | ||
} | ||
/* eslint-disable prefer-const */ | ||
let { activity }: Props = $props(); | ||
/* eslint-enable prefer-const */ | ||
console.log({ activity }); | ||
let timeline = $derived(enrichActivity(flattenActivity(activity))); | ||
function flattenActivity(activity: Operation<Action>[]): FlatAction[] { | ||
return activity.flatMap(operation => | ||
operation.actions.map(action => ({ | ||
...action, | ||
id: operation.id, | ||
author: operation.author, | ||
timestamp: operation.timestamp, | ||
})), | ||
); | ||
} | ||
function enrichActivity( | ||
flatActivity: FlatAction[], | ||
): FlatActionWithPrevious[] { | ||
let result: FlatActionWithPrevious[] = []; | ||
let timelineStateTracker: StateTracker = {} as StateTracker; | ||
flatActivity.forEach(entry => { | ||
if (timelineStateTracker[entry.type]) { | ||
result.push({ ...entry, previous: timelineStateTracker[entry.type] }); | ||
} else { | ||
result.push(entry); | ||
} | ||
timelineStateTracker[entry.type] = entry; | ||
}); | ||
return result; | ||
} | ||
function itemDiff(previousState: string[], newState: string[]) { | ||
const removed = previousState.filter(x => !newState.includes(x)); | ||
const added = newState.filter(x => !previousState.includes(x)); | ||
return { removed, added }; | ||
} | ||
</script> | ||
|
||
<style> | ||
.timeline { | ||
display: flex; | ||
gap: 0.75rem; | ||
flex-direction: column; | ||
} | ||
.timeline-item { | ||
display: flex; | ||
align-items: center; | ||
gap: 0.5rem; | ||
} | ||
</style> | ||
|
||
<div class="timeline txt-small"> | ||
{#each timeline as op} | ||
{#if op.type === "revision"} | ||
<div class="timeline-item"> | ||
<Icon name="revision" /> | ||
<NodeId {...authorForNodeId(op.author)} /> | ||
<div>created a new revision <Id id={op.id} variant="oid" /></div> | ||
</div> | ||
{:else if op.type === "lifecycle"} | ||
<div class="timeline-item"> | ||
<Icon name="arrow-right-hollow" /> | ||
<NodeId {...authorForNodeId(op.author)} /> | ||
{#if op.previous} | ||
changed state from | ||
<PatchStateBadge state={op.previous.state} /> | ||
-> | ||
<PatchStateBadge state={op.state} /> | ||
{:else} | ||
changed state from | ||
<PatchStateBadge state={{ status: "open" }} /> | ||
-> | ||
<PatchStateBadge state={op.state} /> | ||
{/if} | ||
</div> | ||
{:else if op.type === "label"} | ||
{@const changed = itemDiff(op.previous?.labels ?? [], op.labels)} | ||
{#if changed.added.length || changed.removed.length} | ||
<div class="timeline-item"> | ||
<NodeId {...authorForNodeId(op.author)} /> | ||
{#if changed.added.length} | ||
{#each changed.added as label} | ||
<Label {label} /> | ||
{/each} | ||
{/if} | ||
{#if changed.removed.length} | ||
removed labels | ||
{#each changed.removed as label} | ||
<Label {label} /> | ||
{/each} | ||
{/if} | ||
</div> | ||
{/if} | ||
{:else if op.type === "assign"} | ||
{@const changed = itemDiff(op.previous?.assignees ?? [], op.assignees)} | ||
{#if changed.added.length || changed.removed.length} | ||
<div class="timeline-item"> | ||
<NodeId {...authorForNodeId(op.author)} /> | ||
{#if changed.added.length} | ||
assigned | ||
{#each changed.added as assignee} | ||
{assignee} | ||
{/each} | ||
{/if} | ||
{#if changed.removed.length} | ||
unassigned | ||
{#each changed.removed as assignee} | ||
{assignee} | ||
{/each} | ||
{/if} | ||
</div> | ||
{/if} | ||
{:else if op.type === "merge"} | ||
<div class="timeline-item"> | ||
<div style:color="var(--color-fill-primary)"> | ||
<Icon name="merge" /> | ||
</div> | ||
<NodeId {...authorForNodeId(op.author)} /> | ||
<div> | ||
merged this patch at revision <Id id={op.revision} variant="oid" /> | ||
</div> | ||
</div> | ||
{:else if op.type === "edit"} | ||
{#if op.previous} | ||
<div class="timeline-item"> | ||
<Icon name="pen" /> | ||
<NodeId {...authorForNodeId(op.author)} /> | ||
changed title | ||
<s> | ||
{op.previous.title} | ||
</s> | ||
-> {op.title} | ||
</div> | ||
{/if} | ||
{:else if op.type === "review"} | ||
<div class="timeline-item"> | ||
{#if op.verdict === "accept"} | ||
<div style:color="var(--color-foreground-success)"> | ||
<Icon name="comment-checkmark" /> | ||
</div> | ||
<NodeId {...authorForNodeId(op.author)} /> | ||
reviewed and accepted revision <Id id={op.revision} variant="oid" /> | ||
{:else} | ||
<div style:color="var(--color-foreground-red)"> | ||
<Icon name="comment-cross" /> | ||
</div> | ||
<NodeId {...authorForNodeId(op.author)} /> | ||
reviewed and rejected revision <Id id={op.revision} variant="oid" /> | ||
{/if} | ||
</div> | ||
{/if} | ||
{/each} | ||
</div> |
Oops, something went wrong.