Browse Source

feat: create issue page with status and replies

intergrating where possible with the existing proposal components
master
DanConwayDev 2 years ago
parent
commit
e56d28de98
No known key found for this signature in database
GPG Key ID: 68E15486D73F75E1
  1. 3
      src/lib/components/events/EventWrapper.svelte
  2. 17
      src/lib/components/events/content/utils.ts
  3. 10
      src/lib/components/proposals/ProposalDetails.svelte
  4. 3
      src/lib/components/proposals/ProposalHeader.svelte
  5. 29
      src/lib/components/proposals/Status.svelte
  6. 20
      src/lib/components/proposals/StatusSelector.svelte
  7. 8
      src/lib/kinds.ts
  8. 200
      src/lib/stores/Issue.ts
  9. 14
      src/lib/stores/Issues.ts
  10. 13
      src/lib/stores/Proposal.ts
  11. 2
      src/lib/wrappers/ComposeIssue.svelte
  12. 15
      src/lib/wrappers/ComposeReply.svelte
  13. 2
      src/lib/wrappers/EventCard.svelte
  14. 5
      src/lib/wrappers/Thread.svelte
  15. 94
      src/routes/repo/[repo_id]/issue/[issue_id]/+page.svelte
  16. 10
      src/routes/repo/[repo_id]/issue/[issue_id]/+page.ts
  17. 4
      src/routes/repo/[repo_id]/proposal/[proposal_id]/+page.svelte

3
src/lib/components/events/EventWrapper.svelte

@ -7,6 +7,7 @@
import { logged_in_user } from '$lib/stores/users' import { logged_in_user } from '$lib/stores/users'
import type { NDKEvent } from '@nostr-dev-kit/ndk' import type { NDKEvent } from '@nostr-dev-kit/ndk'
export let type: 'proposal' | 'issue' = 'proposal'
export let author: User = { ...user_defaults } export let author: User = { ...user_defaults }
export let created_at: number | undefined export let created_at: number | undefined
export let event_id = '' export let event_id = ''
@ -96,7 +97,7 @@
> >
</div> </div>
<div class=""> <div class="">
<ComposeReply reply_to_event_id={event_id} /> <ComposeReply {type} reply_to_event_id={event_id} />
</div> </div>
</div> </div>
{/if} {/if}

17
src/lib/components/events/content/utils.ts

@ -104,3 +104,20 @@ export const extractPatchMessage = (s: string): string | undefined => {
return undefined return undefined
} }
} }
/** this doesn't work for all patch formats and options */
export const extractPatchTitle = (s: string): string | undefined => {
const msg = extractPatchMessage(s)
if (!msg) return undefined
return s.split('\n')[0]
}
export const extractIssueTitle = (s: string): string => {
return s.split('\n')[0] || ''
}
export const extractIssueDescription = (s: string): string => {
const split = s.split('\n')
if (split.length === 0) return ''
return s.substring(split[0].length) || ''
}

10
src/lib/components/proposals/ProposalDetails.svelte

@ -1,9 +1,12 @@
<script lang="ts"> <script lang="ts">
import { full_defaults } from './type' import { full_defaults, summary_defaults, type ProposalSummary } from './type'
import UserHeader from '../users/UserHeader.svelte' import UserHeader from '../users/UserHeader.svelte'
import StatusSelector from './StatusSelector.svelte' import StatusSelector from './StatusSelector.svelte'
import type { IssueSummary } from '../issues/type'
export let { summary, labels, loading } = { ...full_defaults } export let type: 'proposal' | 'issue' = 'proposal'
export let summary: ProposalSummary | IssueSummary = { ...summary_defaults }
export let { labels, loading } = { ...full_defaults }
</script> </script>
<div class="max-w-md"> <div class="max-w-md">
@ -26,9 +29,10 @@
{:else} {:else}
<h4>Status</h4> <h4>Status</h4>
<StatusSelector <StatusSelector
{type}
status={summary.status} status={summary.status}
repo_identifier={summary.repo_identifier} repo_identifier={summary.repo_identifier}
proposal_id={summary.id} proposal_or_issue_id={summary.id}
/> />
{/if} {/if}
</div> </div>

3
src/lib/components/proposals/ProposalHeader.svelte

@ -10,6 +10,7 @@
import Status from './Status.svelte' import Status from './Status.svelte'
dayjs.extend(relativeTime) dayjs.extend(relativeTime)
export let type: 'proposal' | 'issue' = 'proposal'
export let { export let {
title, title,
descritpion, descritpion,
@ -59,7 +60,7 @@
</div> </div>
<div class="pt-1"> <div class="pt-1">
<div class="mr-3 inline align-middle"> <div class="mr-3 inline align-middle">
<Status {status} /> <Status {type} {status} />
</div> </div>
<div class="mr-3 inline align-middle"> <div class="mr-3 inline align-middle">
opened {created_at_ago} opened {created_at_ago}

29
src/lib/components/proposals/Status.svelte

@ -5,9 +5,11 @@
proposal_status_draft, proposal_status_draft,
proposal_status_open, proposal_status_open,
} from '$lib/kinds' } from '$lib/kinds'
import { issue_icon_path } from '../issues/icons'
import { proposal_icon_path } from './icons' import { proposal_icon_path } from './icons'
export let status: number | undefined = undefined export let status: number | undefined = undefined
export let type: 'proposal' | 'issue' = 'proposal'
export let edit_mode = false export let edit_mode = false
</script> </script>
@ -32,7 +34,14 @@
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 18 18" viewBox="0 0 18 18"
class="h-5 w-5 flex-none fill-success-content pt-1" class="h-5 w-5 flex-none fill-success-content pt-1"
><path d={proposal_icon_path.open_patch} /> >
{#if type === 'proposal'}
<path d={proposal_icon_path.open_patch} />
{:else if type === 'issue'}
{#each issue_icon_path.open as p}
<path d={p} />
{/each}
{/if}
</svg> </svg>
Open Open
{:else if status === proposal_status_applied} {:else if status === proposal_status_applied}
@ -40,16 +49,30 @@
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16" viewBox="0 0 16 16"
class="h-5 w-5 flex-none fill-primary-content pt-1" class="h-5 w-5 flex-none fill-primary-content pt-1"
><path d={proposal_icon_path.applied} /></svg
> >
{#if type === 'proposal'}
<path d={proposal_icon_path.applied} />
{:else if type === 'issue'}
{#each issue_icon_path.resolved as p}
<path d={p} />
{/each}
{/if}
</svg>
Applied Applied
{:else if status === proposal_status_closed} {:else if status === proposal_status_closed}
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16" viewBox="0 0 16 16"
class="h-5 w-5 flex-none fill-neutral-content pt-1" class="h-5 w-5 flex-none fill-neutral-content pt-1"
><path d={proposal_icon_path.close} /></svg
> >
{#if type === 'proposal'}
<path d={proposal_icon_path.close} />
{:else if type === 'issue'}
{#each issue_icon_path.closed as p}
<path d={p} />
{/each}
{/if}
</svg>
Closed Closed
{:else if status === proposal_status_draft} {:else if status === proposal_status_draft}
<svg <svg

20
src/lib/components/proposals/StatusSelector.svelte

@ -20,8 +20,9 @@
import Status from '$lib/components/proposals/Status.svelte' import Status from '$lib/components/proposals/Status.svelte'
export let status: number | undefined = undefined export let status: number | undefined = undefined
export let type: 'proposal' | 'issue' = 'proposal'
export let repo_identifier: string = '' export let repo_identifier: string = ''
export let proposal_id: string = '' export let proposal_or_issue_id: string = ''
let loading = false let loading = false
@ -37,7 +38,7 @@
let event = new NDKEvent(ndk) let event = new NDKEvent(ndk)
event.kind = new_status_kind event.kind = new_status_kind
// tag proposal event // tag proposal event
event.tags.push(['e', proposal_id, 'root']) event.tags.push(['e', proposal_or_issue_id, 'root'])
// tag proposal revision event // tag proposal revision event
$selected_proposal_replies $selected_proposal_replies
.filter((reply) => .filter((reply) =>
@ -70,7 +71,8 @@
try { try {
let _ = await event.publish(NDKRelaySet.fromRelayUrls(relays, ndk)) let _ = await event.publish(NDKRelaySet.fromRelayUrls(relays, ndk))
selected_proposal_full.update((proposal_full) => { selected_proposal_full.update((proposal_full) => {
if (proposal_full.summary.id !== proposal_id) return proposal_full if (proposal_full.summary.id !== proposal_or_issue_id)
return proposal_full
return { return {
...proposal_full, ...proposal_full,
summary: { summary: {
@ -89,20 +91,20 @@
<Status /> <Status />
{:else} {:else}
<div class="dropdown"> <div class="dropdown">
<Status {edit_mode} {status} /> <Status {type} {edit_mode} {status} />
{#if edit_mode} {#if edit_mode}
<ul <ul
tabIndex={0} tabIndex={0}
class="menu dropdown-content z-[1] ml-0 w-52 rounded-box bg-base-300 p-2 shadow" class="menu dropdown-content z-[1] ml-0 w-52 rounded-box bg-base-300 p-2 shadow"
> >
{#if status !== proposal_status_draft} {#if status !== proposal_status_draft && type !== 'issue'}
<li class="pl-0"> <li class="pl-0">
<button <button
on:click={() => { on:click={() => {
changeStatus(proposal_status_draft) changeStatus(proposal_status_draft)
}} }}
class="btn btn-neutral btn-sm mx-2 align-middle" class="btn btn-neutral btn-sm mx-2 align-middle"
>{statusKindtoText(proposal_status_draft)}</button >{statusKindtoText(proposal_status_draft, type)}</button
> >
</li> </li>
{/if} {/if}
@ -113,7 +115,7 @@
changeStatus(proposal_status_open) changeStatus(proposal_status_open)
}} }}
class="btn btn-success btn-sm mx-2 align-middle" class="btn btn-success btn-sm mx-2 align-middle"
>{statusKindtoText(proposal_status_open)}</button >{statusKindtoText(proposal_status_open, type)}</button
> >
</li> </li>
{/if} {/if}
@ -124,7 +126,7 @@
changeStatus(proposal_status_applied) changeStatus(proposal_status_applied)
}} }}
class="btn btn-primary btn-sm mx-2 align-middle" class="btn btn-primary btn-sm mx-2 align-middle"
>{statusKindtoText(proposal_status_applied)}</button >{statusKindtoText(proposal_status_applied, type)}</button
> >
</li> </li>
{/if} {/if}
@ -135,7 +137,7 @@
changeStatus(proposal_status_closed) changeStatus(proposal_status_closed)
}} }}
class="btn btn-neutral btn-sm mx-2 align-middle" class="btn btn-neutral btn-sm mx-2 align-middle"
>{statusKindtoText(proposal_status_closed)}</button >{statusKindtoText(proposal_status_closed, type)}</button
> >
</li> </li>
{/if} {/if}

8
src/lib/kinds.ts

@ -11,9 +11,13 @@ export const proposal_status_kinds: number[] = [
proposal_status_draft, proposal_status_draft,
] ]
export function statusKindtoText(kind: number): string { export function statusKindtoText(
kind: number,
type: 'proposal' | 'issue'
): string {
if (kind === proposal_status_open) return 'Open' if (kind === proposal_status_open) return 'Open'
if (kind === proposal_status_applied) return 'Applied' if (type === 'proposal' && kind === proposal_status_applied) return 'Applied'
if (type === 'issue' && kind === proposal_status_applied) return 'Resolved'
if (kind === proposal_status_closed) return 'Closed' if (kind === proposal_status_closed) return 'Closed'
return 'Draft' return 'Draft'
} }

200
src/lib/stores/Issue.ts

@ -0,0 +1,200 @@
import { NDKRelaySet, type NDKEvent, NDKSubscription } from '@nostr-dev-kit/ndk'
import { writable, type Unsubscriber, type Writable } from 'svelte/store'
import { base_relays, ndk } from './ndk'
import type { User } from '$lib/components/users/type'
import { ensureUser } from './users'
import { type IssueFull, full_defaults } from '$lib/components/issues/type'
import { proposal_status_kinds, proposal_status_open } from '$lib/kinds'
import { awaitSelectedRepoCollection } from './repo'
import {
extractIssueDescription,
extractIssueTitle,
} from '$lib/components/events/content/utils'
import { goto } from '$app/navigation'
import { selectRepoFromCollection } from '$lib/components/repo/utils'
export const selected_issue_full: Writable<IssueFull> = writable({
...full_defaults,
})
// eslint-disable-next-line @typescript-eslint/no-unused-vars
let selected_issue_repo_id: string = ''
let selected_issue_id: string = ''
let issue_summary_author_unsubsriber: Unsubscriber | undefined
export const selected_issue_replies: Writable<NDKEvent[]> = writable([])
let selected_issue_status_date = 0
let sub: NDKSubscription
let sub_replies: NDKSubscription
export const ensureIssueFull = (repo_identifier: string, issue_id: string) => {
if (selected_issue_id == issue_id) return
if (issue_id == '') {
selected_issue_full.set({ ...full_defaults })
selected_issue_replies.set([])
return
}
if (sub) sub.stop()
if (sub_replies) sub_replies.stop()
selected_issue_repo_id = repo_identifier
selected_issue_id = issue_id
selected_issue_status_date = 0
selected_issue_replies.set([])
selected_issue_full.set({
...full_defaults,
summary: {
...full_defaults.summary,
id: issue_id,
repo_identifier: repo_identifier,
loading: true,
},
loading: true,
})
if (issue_summary_author_unsubsriber) issue_summary_author_unsubsriber()
issue_summary_author_unsubsriber = undefined
new Promise(async (r) => {
const repo_collection = await awaitSelectedRepoCollection(repo_identifier)
const repo = selectRepoFromCollection(repo_collection)
const relays_to_use =
repo && repo.relays.length > 3
? repo.relays
: [...base_relays].concat(repo ? repo.relays : [])
sub = ndk.subscribe(
{
ids: [issue_id],
limit: 50,
},
{
closeOnEose: true,
},
NDKRelaySet.fromRelayUrls(relays_to_use, ndk)
)
sub.on('event', (event: NDKEvent) => {
try {
if (event.id == issue_id) {
const event_repo_id = event.tagValue('a')?.split(':')[2]
if (event_repo_id && event_repo_id !== repo_identifier) {
goto(`/repo/${encodeURIComponent(event_repo_id)}/issue/${issue_id}`)
}
selected_issue_full.update((full) => {
return {
...full,
issue_event: event,
summary: {
...full.summary,
title: extractIssueTitle(event.content),
descritpion: extractIssueDescription(event.content),
created_at: event.created_at,
comments: 0,
author: {
hexpubkey: event.pubkey,
loading: true,
npub: '',
},
loading: false,
},
}
})
issue_summary_author_unsubsriber = ensureUser(event.pubkey).subscribe(
(u: User) => {
selected_issue_full.update((full) => {
return {
...full,
summary: {
...full.summary,
author:
event.pubkey == u.hexpubkey ? u : full.summary.author,
},
}
})
}
)
}
} catch {}
})
sub.on('eose', () => {
selected_issue_full.update((full) => {
const updated = {
...full,
summary: {
...full.summary,
loading: false,
},
}
if (full.loading === false) {
r({ ...updated })
}
return updated
})
})
sub_replies = ndk.subscribe(
{
'#e': [issue_id],
},
{
closeOnEose: false,
},
NDKRelaySet.fromRelayUrls(relays_to_use, ndk)
)
const process_replies = (event: NDKEvent) => {
if (
event.kind &&
proposal_status_kinds.includes(event.kind) &&
event.created_at &&
selected_issue_status_date < event.created_at
) {
selected_issue_status_date = event.created_at
selected_issue_full.update((full) => {
return {
...full,
summary: {
...full.summary,
status: event.kind,
// this wont be 0 as we are ensuring it is not undefined above
status_date: event.created_at || 0,
},
}
})
}
selected_issue_replies.update((replies) => {
return [...replies, event].sort(
(a, b) => (a.created_at || 0) - (b.created_at || 0)
)
})
}
sub_replies.on('event', (event: NDKEvent) => {
process_replies(event)
})
sub_replies.on('eose', () => {
selected_issue_full.update((full) => {
const updated = {
...full,
summary: {
...full.summary,
status: full.summary.status || proposal_status_open,
},
loading: false,
}
if (full.summary.loading === false) {
r({ ...updated })
}
return updated
})
})
})
}

14
src/lib/stores/Issues.ts

@ -15,7 +15,10 @@ import {
proposal_status_open, proposal_status_open,
repo_kind, repo_kind,
} from '$lib/kinds' } from '$lib/kinds'
import { extractPatchMessage } from '$lib/components/events/content/utils' import {
extractIssueDescription,
extractIssueTitle,
} from '$lib/components/events/content/utils'
import { selectRepoFromCollection } from '$lib/components/repo/utils' import { selectRepoFromCollection } from '$lib/components/repo/utils'
import { import {
summary_defaults, summary_defaults,
@ -112,13 +115,8 @@ export const ensureIssueSummaries = async (repo_id: string | undefined) => {
id: event.id, id: event.id,
repo_identifier: repo_identifier:
extractRepoIdentiferFromIssueEvent(event) || repo_id || '', extractRepoIdentiferFromIssueEvent(event) || repo_id || '',
title: ( title: extractIssueTitle(event.content),
event.tagValue('name') || descritpion: extractIssueDescription(event.content),
event.tagValue('description') ||
extractPatchMessage(event.content) ||
''
).split('\n')[0],
descritpion: event.tagValue('description') || '',
created_at: event.created_at, created_at: event.created_at,
comments: 0, comments: 0,
author: { author: {

13
src/lib/stores/Proposal.ts

@ -30,7 +30,10 @@ let sub: NDKSubscription
let sub_replies: NDKSubscription let sub_replies: NDKSubscription
export const ensureProposalFull = (repo_id: string, proposal_id: string) => { export const ensureProposalFull = (
repo_identifier: string,
proposal_id: string
) => {
if (selected_proposal_id == proposal_id) return if (selected_proposal_id == proposal_id) return
if (proposal_id == '') { if (proposal_id == '') {
selected_proposal_full.set({ ...full_defaults }) selected_proposal_full.set({ ...full_defaults })
@ -41,7 +44,7 @@ export const ensureProposalFull = (repo_id: string, proposal_id: string) => {
if (sub) sub.stop() if (sub) sub.stop()
if (sub_replies) sub_replies.stop() if (sub_replies) sub_replies.stop()
selected_proposal_repo_id = repo_id selected_proposal_repo_id = repo_identifier
selected_proposal_id = proposal_id selected_proposal_id = proposal_id
selected_proposal_status_date = 0 selected_proposal_status_date = 0
selected_proposal_replies.set([]) selected_proposal_replies.set([])
@ -51,7 +54,7 @@ export const ensureProposalFull = (repo_id: string, proposal_id: string) => {
summary: { summary: {
...full_defaults.summary, ...full_defaults.summary,
id: proposal_id, id: proposal_id,
repo_identifier: repo_id, repo_identifier: repo_identifier,
loading: true, loading: true,
}, },
loading: true, loading: true,
@ -60,7 +63,7 @@ export const ensureProposalFull = (repo_id: string, proposal_id: string) => {
proposal_summary_author_unsubsriber = undefined proposal_summary_author_unsubsriber = undefined
new Promise(async (r) => { new Promise(async (r) => {
const repo_collection = await awaitSelectedRepoCollection(repo_id) const repo_collection = await awaitSelectedRepoCollection(repo_identifier)
const repo = selectRepoFromCollection(repo_collection) const repo = selectRepoFromCollection(repo_collection)
const relays_to_use = const relays_to_use =
repo && repo.relays.length > 3 repo && repo.relays.length > 3
@ -82,7 +85,7 @@ export const ensureProposalFull = (repo_id: string, proposal_id: string) => {
try { try {
if (event.id == proposal_id) { if (event.id == proposal_id) {
const event_repo_id = event.tagValue('a')?.split(':')[2] const event_repo_id = event.tagValue('a')?.split(':')[2]
if (event_repo_id && event_repo_id !== repo_id) { if (event_repo_id && event_repo_id !== repo_identifier) {
goto( goto(
`/repo/${encodeURIComponent(event_repo_id)}/proposal/${proposal_id}` `/repo/${encodeURIComponent(event_repo_id)}/proposal/${proposal_id}`
) )

2
src/lib/wrappers/ComposeIssue.svelte

@ -60,7 +60,7 @@
submitting = false submitting = false
submitted = true submitted = true
setTimeout(() => { setTimeout(() => {
goto(`/repo/${repo_event.identifier}/issues/${event.id}`) goto(`/repo/${repo_event.identifier}/issue/${event.id}`)
}, 2000) }, 2000)
} catch {} } catch {}
} }

15
src/lib/wrappers/ComposeReply.svelte

@ -9,27 +9,34 @@
} from '$lib/stores/repo' } from '$lib/stores/repo'
import Compose from '$lib/components/events/Compose.svelte' import Compose from '$lib/components/events/Compose.svelte'
import { selected_proposal_full } from '$lib/stores/Proposal' import { selected_proposal_full } from '$lib/stores/Proposal'
import { selected_issue_full } from '$lib/stores/Issue'
export let type: 'proposal' | 'issue' = 'proposal'
export let reply_to_event_id = '' export let reply_to_event_id = ''
let repo_identifier: string let repo_identifier: string
let proposal_id: string let proposal_or_issue_id: string
let submitting = false let submitting = false
let submitted = false let submitted = false
let edit_mode = false let edit_mode = false
$: { $: {
repo_identifier = $selected_repo_collection.identifier repo_identifier = $selected_repo_collection.identifier
proposal_id = $selected_proposal_full.summary.id selected_issue_full
proposal_or_issue_id = (
type === 'proposal' ? $selected_proposal_full : $selected_issue_full
).summary.id
edit_mode = edit_mode =
repo_identifier.length > 0 && proposal_id.length > 0 && !submitted repo_identifier.length > 0 &&
proposal_or_issue_id.length > 0 &&
!submitted
} }
async function sendReply(content: string) { async function sendReply(content: string) {
if (!$logged_in_user) return if (!$logged_in_user) return
let event = new NDKEvent(ndk) let event = new NDKEvent(ndk)
event.kind = reply_kind event.kind = reply_kind
event.tags.push(['e', proposal_id, 'root']) event.tags.push(['e', proposal_or_issue_id, 'root'])
if (reply_to_event_id.length > 0) { if (reply_to_event_id.length > 0) {
event.tags.push(['e', reply_to_event_id, 'reply']) event.tags.push(['e', reply_to_event_id, 'reply'])
} }

2
src/lib/wrappers/EventCard.svelte

@ -11,6 +11,7 @@
import { writable, type Unsubscriber } from 'svelte/store' import { writable, type Unsubscriber } from 'svelte/store'
export let event: NDKEvent export let event: NDKEvent
export let type: 'proposal' | 'issue' = 'proposal'
let author = writable({ ...user_defaults }) let author = writable({ ...user_defaults })
let author_unsubsriber: Unsubscriber let author_unsubsriber: Unsubscriber
@ -26,6 +27,7 @@
</script> </script>
<EventWrapper <EventWrapper
{type}
author={$author} author={$author}
created_at={event.created_at} created_at={event.created_at}
event_id={event.id} event_id={event.id}

5
src/lib/wrappers/Thread.svelte

@ -6,6 +6,7 @@
import { writable } from 'svelte/store' import { writable } from 'svelte/store'
export let event: NDKEvent export let event: NDKEvent
export let type: 'proposal' | 'issue' = 'proposal'
export let replies: NDKEvent[] | undefined = undefined export let replies: NDKEvent[] | undefined = undefined
@ -19,10 +20,10 @@
} }
</script> </script>
<EventCard {event} /> <EventCard {type} {event} />
<ThreadWrapper> <ThreadWrapper>
{#each $replies_store as event} {#each $replies_store as event}
<EventCard {event} /> <EventCard {type} {event} />
{/each} {/each}
</ThreadWrapper> </ThreadWrapper>

94
src/routes/repo/[repo_id]/issue/[issue_id]/+page.svelte

@ -0,0 +1,94 @@
<script lang="ts">
import {
ensureSelectedRepoCollection,
selected_repo_collection,
selected_repo_event,
} from '$lib/stores/repo'
import {
ensureIssueFull,
selected_issue_full,
selected_issue_replies,
} from '$lib/stores/Issue'
import RepoHeader from '$lib/components/repo/RepoHeader.svelte'
import Thread from '$lib/wrappers/Thread.svelte'
import Container from '$lib/components/Container.svelte'
import ParsedContent from '$lib/components/events/content/ParsedContent.svelte'
import ComposeReply from '$lib/wrappers/ComposeReply.svelte'
import { patch_kind } from '$lib/kinds'
import Patch from '$lib/components/events/content/Patch.svelte'
import ProposalHeader from '$lib/components/proposals/ProposalHeader.svelte'
import ProposalDetails from '$lib/components/proposals/ProposalDetails.svelte'
export let data: {
repo_id: string
issue_id: string
}
let repo_id = data.repo_id
let issue_id = data.issue_id
ensureSelectedRepoCollection(repo_id)
ensureIssueFull(repo_id, issue_id)
let repo_error = false
let issue_error = false
$: {
repo_error =
!$selected_repo_collection.loading &&
$selected_repo_event.name.length === 0
issue_error =
!$selected_issue_full.summary.loading &&
$selected_issue_full.summary.created_at === 0
}
</script>
{#if !repo_error}
<RepoHeader {...$selected_repo_event} />
{/if}
{#if issue_error}
<Container>
<div role="alert" class="alert alert-error m-auto mt-6 w-full max-w-xs">
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-6 w-6 shrink-0 stroke-current"
fill="none"
viewBox="0 0 24 24"
><path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z"
/></svg
>
<span>Error! cannot find Issue {repo_error ? 'or repo ' : ''}event</span>
</div>
</Container>
{:else}
<ProposalHeader {...$selected_issue_full.summary} />
<Container>
<div class="mx-auto max-w-6xl md:flex">
<div class="md:mr-2 md:w-2/3">
<div class="max-w-4xl">
<div class="my-3">
<ParsedContent content={$selected_issue_full.summary.descritpion} />
</div>
{#each $selected_issue_replies as event}
<Thread type="issue" {event} replies={[]} />
{/each}
<div class="my-3">
<ComposeReply type="issue" />
</div>
</div>
</div>
<div class="prose ml-2 hidden w-1/3 md:flex">
<ProposalDetails
type="issue"
summary={$selected_issue_full.summary}
labels={$selected_issue_full.labels}
loading={$selected_issue_full.loading}
/>
</div>
</div>
</Container>
{/if}

10
src/routes/repo/[repo_id]/issue/[issue_id]/+page.ts

@ -0,0 +1,10 @@
export const load = ({
params,
}: {
params: { issue_id: string; repo_id: string }
}) => {
return {
repo_id: decodeURIComponent(params.repo_id),
issue_id: params.issue_id,
}
}

4
src/routes/repo/[repo_id]/proposal/[proposal_id]/+page.svelte

@ -110,10 +110,10 @@
</div> </div>
</div> </div>
{#each $selected_proposal_replies as event} {#each $selected_proposal_replies as event}
<Thread {event} replies={[]} /> <Thread type="proposal" {event} replies={[]} />
{/each} {/each}
<div class="my-3"> <div class="my-3">
<ComposeReply /> <ComposeReply type="proposal" />
</div> </div>
</div> </div>
</div> </div>

Loading…
Cancel
Save