Skip to content

Commit

Permalink
feat(patch-detail): [wip] add revision section with selector and meta…
Browse files Browse the repository at this point in the history
…data

- adds section headings
- polishes typography and whitespace
- not all revision metadata are shown (e.g. reviews, etc)
- patch description and new Activity section use mock data

Signed-off-by: Konstantinos Maninakis <maninak@protonmail.com>
  • Loading branch information
maninak committed Jan 22, 2024
1 parent 8a05c85 commit 3df152a
Show file tree
Hide file tree
Showing 9 changed files with 265 additions and 19 deletions.
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@
- can be opened via a new button "View Patch Details" on each item in the list of Patches
- panel's title shows the patch description in full if it's short, otherwise truncated to the nearest full word fitting the limit
- the following Patch info are shown in the new view
- status
- status (e.g. open, merged, archived, ...)
- the status badge's background color is a dynamic color mix of the patch status color and the dynamic editor-foreground inherited from vscode's current theme so as to ensure text contrast reaching at least [WCAAG AA](https://developer.mozilla.org/en-US/docs/Web/Accessibility/Understanding_WCAG/Perceivable/Color_contrast) level of accessibility at all times while also retaining a relative consistency of the colors across all our UIs
- major events like "created", "last updated", "merged" and related info with logic crafting optimal copy for each case (see similar tooltip improvements below)
- checked-out indicator, if the Git branch associated with this Radicle Patch is currently checked out
- id (with on-hover button to copy Patch identifier to clipboard)
- revision authors
- labels
Expand All @@ -24,6 +25,7 @@
- a "Check Out Default" button that checks out the Git branch marked as default for the Radicle project
- shown only if the Patch is checked out
- Patch check-out status remains in sync across all views and the actual underlying Git state as the latter changes
- TODO: revision description is hidden under an expandable-on-click control (to avoid showing the same content twice) if the selected revision is the first revision
- **commands**: add new command to check out the current Radicle project's default Git branch
- **patch-list:** show button to "Check Out Default Git Branch" for the currently checked-out Patch on the list
- **patch-list:** auto-retry fetching list of Patches from httpd (with geometric backoff) if an error occured
Expand Down
2 changes: 1 addition & 1 deletion src/types/httpd.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ export interface Comment {
body: string
edits: Edit[]
embeds: Embed[]
reactions: [string, string][]
reactions: [string, string][] // TODO: maninak verify updated types
timestamp: number
replyTo: string | null
}
Expand Down
2 changes: 2 additions & 0 deletions src/utils/patch.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import type { Patch, Revision } from '../types'

// TODO: maninak change logic (and rename here and everywhere) to not get the latest revision but the most important one which is `patch.status === merged ? mergedRevision(s) ? latestRevision`

export function getFirstAndLatestRevisions(patch: Patch): {
firstRevision: Revision
latestRevision: Revision
Expand Down
14 changes: 12 additions & 2 deletions src/webviews/src/assets/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,22 @@
@tailwind components;
@tailwind utilities;

:is(code, pre, span, b)[title]:hover {
:where(code, pre, span, b)[title]:hover {
@apply underline decoration-dotted cursor-default;
text-underline-position: under;
}

:is(code, pre) {
:where(code, pre) {
@apply m-0;
display: initial;
}

:where(details) {
& summary {
@apply w-max cursor-pointer select-none;
}
}

:where(.parsed-md) :last-child {
@apply mb-0;
}
241 changes: 232 additions & 9 deletions src/webviews/src/components/PatchDetail.vue
Original file line number Diff line number Diff line change
@@ -1,20 +1,42 @@
<script setup lang="ts">
import { provideVSCodeDesignSystem, vsCodeButton } from '@vscode/webview-ui-toolkit'
import {
provideVSCodeDesignSystem,
vsCodeButton,
vsCodeDropdown,
vsCodeOption
} from '@vscode/webview-ui-toolkit'
import { ref, computed } from 'vue'
import { storeToRefs } from 'pinia'
import { getIdentityAliasOrId, shortenHash } from 'extensionUtils/string'
import { getFormattedDate } from 'extensionUtils/time'
import { notifyExtension } from 'extensionUtils/webview-messaging'
import { usePatchDetailStore } from '@/stores/patchDetailStore'
import PatchStatusBadge from './PatchStatusBadge.vue'
import PatchMajorEvents from './PatchMajorEvents.vue'
import PatchMetadata from './PatchMetadata.vue'
import { toRaw } from 'vue'
import type { Revision } from '../../../types'
provideVSCodeDesignSystem().register(vsCodeButton())
provideVSCodeDesignSystem().register(vsCodeButton(), vsCodeDropdown(), vsCodeOption())
const { patch, firstRevision } = storeToRefs(usePatchDetailStore())
const { patch, firstRevision, latestRevision } = storeToRefs(usePatchDetailStore())
const selectedRevisionOption = ref(shortenHash(latestRevision.value.id))
// TODO: maninak if patch is merged pre-select revision that got merged
const selectedRevision = computed(
() =>
patch.value.revisions.find((revision) =>
revision.id.includes(selectedRevisionOption.value)
) as Revision
)
const shouldHideRevisionDescription = computed(
() =>
selectedRevision.value.description && selectedRevision.value.id === firstRevision.value.id
)
function refetchPatchData() {
notifyExtension({ command: 'refreshPatchData', payload: { patchId: patch.value.id } })
// TODO: maninak implement and consider using `<progress-ring>` while loading
}
function checkOutPatchBranch() {
Expand All @@ -24,10 +46,21 @@ function checkOutPatchBranch() {
function checkOutDefaultBranch() {
notifyExtension({ command: 'checkOutDefaultBranch', payload: undefined })
}
const mockRevisionDescription = `
This is a patch description parsed from Markdown:
<ul>
<li> a list item</li>
<li> another <b>bold</b> item</li>
<li> another <em>italic</em> item</li>
<li> an item with <code>preformatted</code> text</li>
</ul>
`
</script>

<template>
<article class="flex flex-col gap-8">
<article class="flex flex-col gap-12">
<!-- TODO: maninak make h2s (and maybe also header?) sticky -->
<header class="pt-4 flex justify-between">
<div class="flex gap-4 items-center">
<PatchStatusBadge class="text-sm" />
Expand Down Expand Up @@ -66,10 +99,200 @@ function checkOutDefaultBranch() {
>
</aside>
</header>
<PatchMetadata />
<main>
<h1 class="mt-0 mb-5 text-3xl">{{ patch.title }}</h1>
<pre>{{ firstRevision.description }}</pre>
<main class="flex flex-col gap-12">
<section id="patch">
<h2 class="text-lg mt-0 mb-3"># Patch</h2>
<PatchMetadata />
<h1 class="parsed-md my-4 text-3xl font-mono">{{ patch.title }}</h1>
<!-- TODO: maninak roll back to <pre>{{ firstRevision.description }}</pre> -->
<div class="parsed-md font-mono" v-html="mockRevisionDescription"></div>
</section>

<section id="revision">
<h2
class="flex flex-row items-center w-max gap-[0.5em] text-lg mt-0 mb-3"
title="Select a patch revision"
>
<label for="revision-selector" class="cursor-pointer"># Revision</label>
<vscode-dropdown id="revision-selector" v-model="selectedRevisionOption">
<!-- TODO: maninak maybe show inline if the revision is first, latest, merged, approved/rejected? -->
<vscode-option
v-for="revision in [...patch.revisions].reverse()"
:key="revision.id"
class="font-mono"
>{{ shortenHash(revision.id) }}</vscode-option
>
</vscode-dropdown>
</h2>
<div class="*:min-h-[1.5em]">
<div class="flex flex-row items-center w-max gap-[0.5em] group">
Id:
<pre :title="selectedRevision.id">{{ shortenHash(selectedRevision.id) }}</pre>
<vscode-button
class="invisible group-hover:visible"
appearance="icon"
title="Copy Revision Identifier to Clipboard"
@click="
notifyExtension({
command: 'copyToClipboardAndNotify',
payload: { textToCopy: selectedRevision.id }
})
"
>
<span class="codicon codicon-copy"></span>
</vscode-button>
</div>
<div class="flex flex-row items-center w-max gap-[0.5em]">
Author:
<pre :title="selectedRevision.author.id" class="flex gap-[0.5em] w-max">{{
getIdentityAliasOrId(selectedRevision.author)
}}</pre>
</div>
<div class="flex flex-row items-center w-max gap-[0.5em]">
Date:
<pre>{{ getFormattedDate(selectedRevision.timestamp) }}</pre>
</div>
<div class="flex flex-row items-center w-max gap-[0.5em]">
Latest commit:
<pre :title="selectedRevision.oid">{{ shortenHash(selectedRevision.oid) }}</pre>
</div>
<div class="flex flex-row items-center w-max gap-[0.5em]">
Based on commit:
<pre :title="selectedRevision.base">{{ shortenHash(selectedRevision.base) }}</pre>
</div>
</div>
<div class="mt-4">
<details v-if="shouldHideRevisionDescription">
<summary style="color: var(--vscode-foreground)" title="Click to expand/collapse"
>Revision description</summary
>
<div class="parsed-md font-mono mt-[0.25em]">{{
selectedRevision.description
}}</div>
</details>
<div class="parsed-md font-mono" v-else>{{ selectedRevision.description }}</div>
</div>
</section>

<section id="activity">
<h2 class="text-lg mt-0 mb-3"># Activity</h2>
<ul class="timeline -ml-[20px] flex flex-col gap-4">
<li class="-ml-4 flex gap-3 list-none items-center">
<span
title="Radicle patch revision"
class="p-[2px] rounded-full no-underline codicon codicon-pulse"
style="
background-color: color-mix(
in srgb-linear,
var(--vscode-editor-background),
var(--vscode-editor-foreground) 5%
);
"
></span>
<span>
Patch created with revision <pre>cdba53e</pre> by
<pre>danielkalman 21 hours ago</pre>
</span>
</li>
<li class="-ml-4 flex gap-3 list-none items-center">
<span
title="Radicle patch revision"
class="p-[2px] rounded-full no-underline codicon codicon-pulse"
style="
background-color: color-mix(
in srgb-linear,
var(--vscode-editor-background),
var(--vscode-editor-foreground) 5%
);
"
></span>
<span>
Patch updated with revision <pre>829bee2</pre> by
<pre>danielkalman 10 hours ago</pre>
</span>
</li>
<li class="-ml-4 flex gap-3 list-none">
<span
title="Git commit"
class="px-[2px] no-underline before:bg-vscode-editor-background codicon codicon-git-commit"
></span>
<div class="flex items-center">
<pre>Add embeds to patch descriptions</pre>
<vscode-button
class="ml-1"
appearance="icon"
title="Show/hide additional commit info"
>
<span class="codicon codicon-ellipsis"></span>
</vscode-button>
</div>
<pre class="ml-4" tile="87afc6287afc6287afc62">87afc62</pre>
&ndash;
<pre title="me@dnlklmn.dev">dnlklmn</pre>
<pre title=""></pre> <pre>28 hours ago</pre>
</li>
<li class="-ml-4 flex gap-3 list-none">
<span
title="Git commit"
class="px-[2px] no-underline before:bg-vscode-editor-background codicon codicon-git-commit"
></span>
<div class="flex items-center">
<pre>Add <code>--no-announce</code> to patch comment creation for stdout</pre>
<vscode-button
class="ml-1"
appearance="icon"
title="Show/hide additional commit info"
>
<span class="codicon codicon-ellipsis"></span>
</vscode-button>
</div>
<pre class="ml-4" title="cc08490cc08490cc08490">cc08490</pre>
&ndash;
<pre title="me@sebastinez.dev">Sebastian Martinez</pre>
<pre title=""></pre> <pre>11 hours ago</pre>
</li>
<li class="-ml-4 flex gap-3 items-center list-none">
<span
title="Git commit"
class="px-[2px] no-underline before:bg-vscode-editor-background codicon codicon-git-commit"
></span>
<div class="flex items-center">
<pre>Add embeds also to revisions</pre>
<vscode-button
class="ml-1"
appearance="icon"
title="Show/hide additional commit info"
>
<span class="codicon codicon-ellipsis"></span>
</vscode-button>
</div>
<pre class="ml-4" title="aed8538aed8538aed8538">aed8538</pre>
&ndash;
<pre title="me@sebastinez.dev">Sebastian Martinez</pre>
<pre>10 hours ago</pre>
</li>
</ul>
</section>
</main>
</article>
</template>

<style scoped>
.timeline {
position: relative;
&:before {
content: '';
position: absolute;
z-index: -1;
left: 33.4px;
height: 100%;
width: 1.2px;
background-color: color-mix(
in srgb-linear,
var(--vscode-editor-background),
var(--vscode-editor-foreground) 5%
);
}
}
</style>
14 changes: 10 additions & 4 deletions src/webviews/src/components/PatchMetadata.vue
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,15 @@ const { patch, authors } = storeToRefs(usePatchDetailStore())
</script>

<template>
<aside class="*:h-[1.5em]">
<aside class="*:min-h-[1.5em]">
<pre
v-if="patch.isCheckedOut"
title="The Git branch associated with this Radicle patch is currently checked out"
>
✓ Checked out</pre
>
<div class="flex items-center w-max group">
Patch Id: &nbsp;
Id: &nbsp;
<pre :title="patch.id">{{ shortenHash(patch.id) }}</pre>
<vscode-button
class="ml-1 invisible group-hover:visible"
Expand All @@ -31,15 +37,15 @@ const { patch, authors } = storeToRefs(usePatchDetailStore())
<span class="codicon codicon-copy"></span>
</vscode-button>
</div>
<div class="flex items-center w-max group">
<div class="flex items-center w-max">
Authors: &nbsp;
<span class="flex gap-[0.5em]">
<pre v-for="author in authors" :key="author.id" :title="author.id">{{
getIdentityAliasOrId(author)
}}</pre>
</span>
</div>
<div v-if="patch.labels.length" class="flex items-center w-max group">
<div v-if="patch.labels.length" class="flex items-center w-max">
Labels: &nbsp;
<span class="flex gap-[0.5em]"
><code v-for="label in patch.labels" :key="label">{{ label }}</code></span
Expand Down
2 changes: 1 addition & 1 deletion src/webviews/src/components/PatchStatusBadge.vue
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,6 @@ const status = computed(() => patch.value.state.status)
:style="`background: color-mix(in srgb-linear, var(--vscode-patch-${status}), var(--vscode-editor-foreground) 5%);`"
>
<PatchStatusIcon :status="status" />
<span class="capitalize">{{ status }}</span>
<span class="-mb-[0.125em] capitalize font-mono">{{ status }}</span>
</span>
</template>
1 change: 1 addition & 0 deletions src/webviews/src/stores/patchDetailStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export const usePatchDetailStore = defineStore('patch-detail', () => {
)

watchEffect(() => {
// TODO: maninak save and restore scroll position
vscode.setState(state.value)
})

Expand Down
4 changes: 3 additions & 1 deletion src/webviews/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ import vue from '@vitejs/plugin-vue'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
vue(),
vue({
template: {compilerOptions: { isCustomElement: (tag) => tag.includes("vscode-") }},
}),
],
resolve: {
alias: {
Expand Down

0 comments on commit 3df152a

Please sign in to comment.