diff --git a/src/lib/components/RepoSummaryCard.svelte b/src/lib/components/RepoSummaryCard.svelte index fd26c7e..bff8799 100644 --- a/src/lib/components/RepoSummaryCard.svelte +++ b/src/lib/components/RepoSummaryCard.svelte @@ -2,7 +2,7 @@ import { summary_defaults } from './repo/type' import UserHeader from './users/UserHeader.svelte' - export let { name, description, identifier, maintainers, loading } = + export let { name, description, identifier, maintainers, naddr, loading } = summary_defaults let short_name: string $: { @@ -25,10 +25,7 @@
{:else} - {short_name} + {short_name} {#if short_descrption.length > 0}

{short_descrption} diff --git a/src/lib/components/ReposSummaryList.svelte b/src/lib/components/ReposSummaryList.svelte index 5f42db9..b5103ef 100644 --- a/src/lib/components/ReposSummaryList.svelte +++ b/src/lib/components/ReposSummaryList.svelte @@ -5,6 +5,27 @@ export let title: string = '' export let repos: RepoSummary[] = [] export let loading: boolean = false + export let group_by: 'name' | 'identifier' | undefined = undefined + + let grouped_repos: RepoSummary[][] = [] + let selected_group: string | undefined = undefined + $: { + grouped_repos = [] + repos.forEach((collection) => { + if (!group_by) { + grouped_repos.push([collection]) + return + } + const added_to_group = grouped_repos.some((group, i) => { + if (group.some((c) => c[group_by] === collection[group_by])) { + grouped_repos[i].push(collection) + return true + } + return false + }) + if (!added_to_group) grouped_repos.push([collection]) + }) + }

@@ -17,8 +38,46 @@

None

{:else}
- {#each repos as { name, description, identifier, maintainers }} - + {#each grouped_repos as group} + {#if group.length === 0} + + {:else if group.length === 1} + {#each group as { name, description, identifier, maintainers, naddr }} + + {/each} + {:else if group_by} +
+ +
{ + selected_group = group[0][group_by] + }} + > +
+
{group[0][group_by]}
+
{group.length} Items
+
+
+ {#each group as { name, description, identifier, maintainers, naddr }} +
+ +
+ {/each} +
+ {/if} {/each} {#if loading} @@ -30,3 +89,30 @@
{/if}
+{#if selected_group} + +{/if} diff --git a/src/lib/components/issues/type.ts b/src/lib/components/issues/type.ts index badad7e..6641c7c 100644 --- a/src/lib/components/issues/type.ts +++ b/src/lib/components/issues/type.ts @@ -7,7 +7,7 @@ export interface IssueSummary { type: 'issue' title: string descritpion: string - repo_identifier: string + repo_a: string id: string comments: number status: undefined | number @@ -21,7 +21,7 @@ export const summary_defaults: IssueSummary = { type: 'issue', title: '', descritpion: '', - repo_identifier: '', + repo_a: '', id: '', comments: 0, status: undefined, @@ -32,13 +32,13 @@ export const summary_defaults: IssueSummary = { } export interface IssueSummaries { - id: string | undefined + repo_a: string | undefined summaries: IssueSummary[] loading: boolean } export const summaries_defaults: IssueSummaries = { - id: '', + repo_a: '', summaries: [], loading: true, } diff --git a/src/lib/components/proposals/ProposalDetails.svelte b/src/lib/components/proposals/ProposalDetails.svelte index 6490100..00e74be 100644 --- a/src/lib/components/proposals/ProposalDetails.svelte +++ b/src/lib/components/proposals/ProposalDetails.svelte @@ -31,7 +31,6 @@ {/if} diff --git a/src/lib/components/proposals/ProposalHeader.svelte b/src/lib/components/proposals/ProposalHeader.svelte index eab08e7..0d3a13d 100644 --- a/src/lib/components/proposals/ProposalHeader.svelte +++ b/src/lib/components/proposals/ProposalHeader.svelte @@ -23,7 +23,6 @@ title, descritpion, id, - repo_identifier, comments, status, status_date, @@ -86,12 +85,7 @@ {#if !$logged_in_user} {:else} - {/if} + {/if}
opened {created_at_ago} diff --git a/src/lib/components/proposals/ProposalsListItem.svelte b/src/lib/components/proposals/ProposalsListItem.svelte index d8bc36c..eb20de3 100644 --- a/src/lib/components/proposals/ProposalsListItem.svelte +++ b/src/lib/components/proposals/ProposalsListItem.svelte @@ -14,6 +14,8 @@ proposal_status_open, } from '$lib/kinds' import { issue_icon_path } from '../issues/icons' + import { aToNaddr, extractAReference } from '../repo/utils' + import { nip19 } from 'nostr-tools' dayjs.extend(relativeTime) export let type: 'issue' | 'proposal' = 'proposal' @@ -22,7 +24,7 @@ title, descritpion, id, - repo_identifier, + repo_a, comments, status, status_date, @@ -39,6 +41,15 @@ else short_title = title created_at_ago = created_at ? dayjs(created_at * 1000).fromNow() : '' } + let repo_naddr = '' + let repo_identifier = '' + $: { + if (repo_a.length > 0) { + repo_naddr = aToNaddr(repo_a) || '' + let a_ref = extractAReference(repo_a) + repo_identifier = a_ref ? a_ref.identifier : '' + } + }
  • @@ -97,7 +108,7 @@ {/if} @@ -134,7 +145,7 @@
  • {#if show_repo && repo_identifier.length > 0}
  • - + {repo_identifier}
  • diff --git a/src/lib/components/proposals/StatusSelector.svelte b/src/lib/components/proposals/StatusSelector.svelte index 4d26219..db7976f 100644 --- a/src/lib/components/proposals/StatusSelector.svelte +++ b/src/lib/components/proposals/StatusSelector.svelte @@ -13,24 +13,18 @@ statusKindtoText, } from '$lib/kinds' import { getUserRelays, logged_in_user } from '$lib/stores/users' - import { - selected_repo_collection, - selected_repo_event, - } from '$lib/stores/repo' + import { selected_repo_event } from '$lib/stores/repo' import Status from '$lib/components/proposals/Status.svelte' export let status: number | undefined = undefined export let type: 'proposal' | 'issue' = 'proposal' - export let repo_identifier: string = '' export let proposal_or_issue_id: string = '' let loading = false let edit_mode = false $: { - edit_mode = - $logged_in_user !== undefined && - repo_identifier === $selected_repo_collection.identifier + edit_mode = $logged_in_user !== undefined } async function changeStatus(new_status_kind: number) { diff --git a/src/lib/components/proposals/type.ts b/src/lib/components/proposals/type.ts index c150cd8..713931a 100644 --- a/src/lib/components/proposals/type.ts +++ b/src/lib/components/proposals/type.ts @@ -7,7 +7,7 @@ export interface ProposalSummary { type: 'proposal' title: string descritpion: string - repo_identifier: string + repo_a: string id: string comments: number status: undefined | number @@ -21,7 +21,7 @@ export const summary_defaults: ProposalSummary = { type: 'proposal', title: '', descritpion: '', - repo_identifier: '', + repo_a: '', id: '', comments: 0, status: undefined, @@ -32,13 +32,13 @@ export const summary_defaults: ProposalSummary = { } export interface ProposalSummaries { - id: string | undefined + repo_a: string | undefined summaries: ProposalSummary[] loading: boolean } export const summaries_defaults: ProposalSummaries = { - id: '', + repo_a: '', summaries: [], loading: true, } diff --git a/src/lib/components/repo/RepoHeader.svelte b/src/lib/components/repo/RepoHeader.svelte index 4e905db..970164c 100644 --- a/src/lib/components/repo/RepoHeader.svelte +++ b/src/lib/components/repo/RepoHeader.svelte @@ -6,8 +6,10 @@ export let { event_id, identifier, + naddr, unique_commit, name, + author, description, clone, web, @@ -38,11 +40,11 @@
    {:else} {short_name} {/if} - + diff --git a/src/lib/components/repo/type.ts b/src/lib/components/repo/type.ts index d343c1d..6da5de3 100644 --- a/src/lib/components/repo/type.ts +++ b/src/lib/components/repo/type.ts @@ -7,6 +7,7 @@ import { export interface RepoEventBase { event_id: string naddr: string + author: string // pubkey identifier: string unique_commit: string | undefined name: string @@ -17,6 +18,8 @@ export interface RepoEventBase { maintainers: string | User[] relays: string[] referenced_by: string[] + // this is unreliable as relays dont return youngest first + most_recent_reference_timestamp: number created_at: number loading: boolean } @@ -31,6 +34,7 @@ export interface RepoEventWithMaintainersMetadata extends RepoEventBase { export const event_defaults: RepoEvent = { event_id: '', naddr: '', + author: '', identifier: '', unique_commit: '', name: '', @@ -41,22 +45,32 @@ export const event_defaults: RepoEvent = { maintainers: [], relays: [], referenced_by: [], + most_recent_reference_timestamp: 0, created_at: 0, loading: true, } -export interface RepoCollection { - selected_event_id: string - unique_commit: string - identifier: string +export interface RepoCollectionBase { + selected_a: string // :: + most_recent_index: number + maintainers: string | User[] events: RepoEvent[] loading: boolean } +export interface RepoCollection extends RepoCollectionBase { + maintainers: string[] +} + +export interface RepoCollectionWithMaintainersMetadata + extends RepoCollectionBase { + maintainers: UserObject[] +} + export const collection_defaults: RepoCollection = { - identifier: '', - unique_commit: '', - selected_event_id: '', + selected_a: '', + most_recent_index: -1, + maintainers: [], events: [], loading: true, } @@ -65,19 +79,39 @@ export interface RepoSummary { name: string description: string identifier: string + naddr: string unique_commit: string | undefined maintainers: User[] loading?: boolean created_at: number + most_recent_reference_timestamp: number } export const summary_defaults: RepoSummary = { name: '', identifier: '', + naddr: '', unique_commit: undefined, description: '', maintainers: [{ ...user_defaults }], loading: false, created_at: 0, + most_recent_reference_timestamp: 0, +} + +export interface SelectedPubkeyRepoCollections { + pubkey: string + collections: RepoCollection[] +} + +export interface RepoDIdentiferCollection { + d: string + events: RepoEvent[] + loading: boolean +} + +export interface RepoRecentCollection { + events: RepoEvent[] + loading: boolean } export type RepoPage = 'about' | 'issues' | 'proposals' diff --git a/src/lib/components/repo/utils.ts b/src/lib/components/repo/utils.ts index 839b6ab..de187a0 100644 --- a/src/lib/components/repo/utils.ts +++ b/src/lib/components/repo/utils.ts @@ -1,19 +1,12 @@ +import type { AddressPointer } from 'nostr-tools/lib/types/nip19' import type { RepoCollection, RepoEvent } from './type' +import { nip19 } from 'nostr-tools' +import { repo_kind } from '$lib/kinds' export const selectRepoFromCollection = ( collection: RepoCollection ): RepoEvent | undefined => { - if (collection.selected_event_id && collection.selected_event_id.length > 0) - return collection.events.find( - (e) => e.event_id === collection.selected_event_id - ) - - return [...collection.events].sort((a, b) => { - const a_ref = a.referenced_by ? a.referenced_by.length : 0 - const b_ref = b.referenced_by ? b.referenced_by.length : 0 - if (a_ref === b_ref) return b.created_at - a.created_at - return b_ref - a_ref - })[0] + return collection.events[collection.most_recent_index] } /** most servers will produce a CORS error so a proxy should be used */ @@ -70,3 +63,41 @@ const extractRepoAddress = (clone_string: string): string => { s = s.replace(':', '/') return s } + +const naddrToPointer = (s: string): AddressPointer | undefined => { + const decoded = nip19.decode(s) + if ( + typeof decoded.data === 'string' || + !Object.keys(decoded.data).includes('identifier') + ) + return undefined + return decoded.data as AddressPointer +} + +export const extractAReference = (a: string): AddressPointer | undefined => { + if (a.split(':').length !== 3) return undefined + const [k, pubkey, identifier] = a.split(':') + return { kind: Number(k), pubkey, identifier } +} + +export const naddrToRepoA = (s: string): string | undefined => { + const pointer = naddrToPointer(s) + if (pointer && pointer.kind === repo_kind) + return `${repo_kind}:${pointer.pubkey}:${pointer.identifier}` + return undefined +} + +export const aToNaddr = ( + a: string | AddressPointer +): `naddr1${string}` | undefined => { + const a_ref = typeof a === 'string' ? extractAReference(a) : a + if (!a_ref) return undefined + return nip19.naddrEncode(a_ref) +} + +export const neventOrNoteToHexId = (s: string): string | undefined => { + const decoded = nip19.decode(s) + if (decoded.type === 'note') return decoded.data + else if (decoded.type === 'nevent') return decoded.data.id + return undefined +} diff --git a/src/lib/stores/Issue.ts b/src/lib/stores/Issue.ts index d7267d6..d654801 100644 --- a/src/lib/stores/Issue.ts +++ b/src/lib/stores/Issue.ts @@ -8,7 +8,6 @@ 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 = writable({ @@ -16,7 +15,7 @@ export const selected_issue_full: Writable = writable({ }) // eslint-disable-next-line @typescript-eslint/no-unused-vars -let selected_issue_repo_id: string = '' +let selected_issue_repo_a: string = '' let selected_issue_id: string = '' export const selected_issue_replies: Writable = writable([]) @@ -29,7 +28,7 @@ let sub_replies: NDKSubscription const sub_replies_to_replies: NDKSubscription[] = [] -export const ensureIssueFull = (repo_identifier: string, issue_id: string) => { +export const ensureIssueFull = (repo_a: string, issue_id: string) => { if (selected_issue_id == issue_id) return if (issue_id == '') { selected_issue_full.set({ ...full_defaults }) @@ -41,7 +40,7 @@ export const ensureIssueFull = (repo_identifier: string, issue_id: string) => { if (sub_replies) sub_replies.stop() sub_replies_to_replies.forEach((sub) => sub.stop()) - selected_issue_repo_id = repo_identifier + selected_issue_repo_a = repo_a selected_issue_id = issue_id selected_issue_status_date = 0 selected_issue_replies.set([]) @@ -51,14 +50,14 @@ export const ensureIssueFull = (repo_identifier: string, issue_id: string) => { summary: { ...full_defaults.summary, id: issue_id, - repo_identifier: repo_identifier, + repo_a, loading: true, }, loading: true, }) new Promise(async (r) => { - const repo_collection = await awaitSelectedRepoCollection(repo_identifier) + const repo_collection = await awaitSelectedRepoCollection(repo_a) const repo = selectRepoFromCollection(repo_collection) const relays_to_use = repo && repo.relays.length > 3 @@ -79,10 +78,6 @@ export const ensureIssueFull = (repo_identifier: string, issue_id: string) => { 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, diff --git a/src/lib/stores/Issues.ts b/src/lib/stores/Issues.ts index 23b6023..0060028 100644 --- a/src/lib/stores/Issues.ts +++ b/src/lib/stores/Issues.ts @@ -24,27 +24,27 @@ import { } from '$lib/components/issues/type' export const issue_summaries: Writable = writable({ - id: '', + repo_a: '', summaries: [], loading: false, }) -let selected_repo_id: string | undefined = '' +let selected_repo_a: string | undefined = '' let sub: NDKSubscription -export const ensureIssueSummaries = async (repo_id: string | undefined) => { - if (selected_repo_id == repo_id) return +export const ensureIssueSummaries = async (repo_a: string | undefined) => { + if (selected_repo_a == repo_a) return issue_summaries.set({ - id: repo_id, + repo_a, summaries: [], - loading: repo_id !== '', + loading: repo_a !== '', }) if (sub) sub.stop() if (sub_statuses) sub_statuses.stop() - selected_repo_id = repo_id + selected_repo_a = repo_a setTimeout(() => { issue_summaries.update((summaries) => { @@ -61,8 +61,8 @@ export const ensureIssueSummaries = async (repo_id: string | undefined) => { limit: 100, } - if (repo_id) { - const repo_collection = await awaitSelectedRepoCollection(repo_id) + if (repo_a) { + const repo_collection = await awaitSelectedRepoCollection(repo_a) const repo = selectRepoFromCollection(repo_collection) if (!repo) { @@ -93,7 +93,7 @@ export const ensureIssueSummaries = async (repo_id: string | undefined) => { sub.on('event', (event: NDKEvent) => { try { if (event.kind == issue_kind) { - if (!extractRepoIdentiferFromIssueEvent(event) && !repo_id) { + if (!extractRepoIdentiferFromIssueEvent(event) && !repo_a) { // link to issue will not work as it requires an identifier return } @@ -105,8 +105,8 @@ export const ensureIssueSummaries = async (repo_id: string | undefined) => { { ...summary_defaults, id: event.id, - repo_identifier: - extractRepoIdentiferFromIssueEvent(event) || repo_id || '', + repo_a: + extractRepoIdentiferFromIssueEvent(event) || repo_a || '', title: extractIssueTitle(event.content), descritpion: extractIssueDescription(event.content), created_at: event.created_at, @@ -199,5 +199,5 @@ export const extractRepoIdentiferFromIssueEvent = ( if (!value) return undefined const split = value.split(':') if (split.length < 3) return undefined - return split[2] + return value } diff --git a/src/lib/stores/Proposal.ts b/src/lib/stores/Proposal.ts index f6de6bf..fb966ce 100644 --- a/src/lib/stores/Proposal.ts +++ b/src/lib/stores/Proposal.ts @@ -1,8 +1,6 @@ import { NDKRelaySet, type NDKEvent, NDKSubscription } from '@nostr-dev-kit/ndk' -import { writable, type Unsubscriber, type Writable } from 'svelte/store' +import { writable, 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 ProposalFull, full_defaults, @@ -10,7 +8,6 @@ import { import { proposal_status_kinds, proposal_status_open } from '$lib/kinds' import { awaitSelectedRepoCollection } from './repo' import { extractPatchMessage } from '$lib/components/events/content/utils' -import { goto } from '$app/navigation' import { selectRepoFromCollection } from '$lib/components/repo/utils' export const selected_proposal_full: Writable = writable({ @@ -18,9 +15,8 @@ export const selected_proposal_full: Writable = writable({ }) // eslint-disable-next-line @typescript-eslint/no-unused-vars -let selected_proposal_repo_id: string = '' +let selected_proposal_repo_a: string = '' let selected_proposal_id: string = '' -let proposal_summary_author_unsubsriber: Unsubscriber | undefined export const selected_proposal_replies: Writable = writable([]) @@ -32,10 +28,7 @@ let sub_replies: NDKSubscription const sub_replies_to_replies: NDKSubscription[] = [] -export const ensureProposalFull = ( - repo_identifier: string, - proposal_id: string -) => { +export const ensureProposalFull = (repo_a: string, proposal_id: string) => { if (selected_proposal_id == proposal_id) return if (proposal_id == '') { selected_proposal_full.set({ ...full_defaults }) @@ -47,7 +40,7 @@ export const ensureProposalFull = ( if (sub_replies) sub_replies.stop() sub_replies_to_replies.forEach((sub) => sub.stop()) - selected_proposal_repo_id = repo_identifier + selected_proposal_repo_a = repo_a selected_proposal_id = proposal_id selected_proposal_status_date = 0 selected_proposal_replies.set([]) @@ -57,16 +50,14 @@ export const ensureProposalFull = ( summary: { ...full_defaults.summary, id: proposal_id, - repo_identifier: repo_identifier, + repo_a, loading: true, }, loading: true, }) - if (proposal_summary_author_unsubsriber) proposal_summary_author_unsubsriber() - proposal_summary_author_unsubsriber = undefined new Promise(async (r) => { - const repo_collection = await awaitSelectedRepoCollection(repo_identifier) + const repo_collection = await awaitSelectedRepoCollection(repo_a) const repo = selectRepoFromCollection(repo_collection) const relays_to_use = repo && repo.relays.length > 3 @@ -87,12 +78,6 @@ export const ensureProposalFull = ( sub.on('event', (event: NDKEvent) => { try { if (event.id == proposal_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)}/proposal/${proposal_id}` - ) - } selected_proposal_full.update((full) => { return { ...full, @@ -108,29 +93,11 @@ export const ensureProposalFull = ( descritpion: event.tagValue('description') || '', created_at: event.created_at, comments: 0, - author: { - hexpubkey: event.pubkey, - loading: true, - npub: '', - }, + author: event.pubkey, loading: false, }, } }) - - proposal_summary_author_unsubsriber = ensureUser( - event.pubkey - ).subscribe((u: User) => { - selected_proposal_full.update((full) => { - return { - ...full, - summary: { - ...full.summary, - author: event.pubkey == u.hexpubkey ? u : full.summary.author, - }, - } - }) - }) } } catch {} }) diff --git a/src/lib/stores/Proposals.ts b/src/lib/stores/Proposals.ts index 86aade7..2aa6909 100644 --- a/src/lib/stores/Proposals.ts +++ b/src/lib/stores/Proposals.ts @@ -20,27 +20,27 @@ import { selectRepoFromCollection } from '$lib/components/repo/utils' import { returnRepoCollection } from './repos' export const proposal_summaries: Writable = writable({ - id: '', + repo_a: '', summaries: [], loading: false, }) -let selected_repo_id: string | undefined = '' +let selected_a: string | undefined = '' let sub: NDKSubscription -export const ensureProposalSummaries = async (repo_id: string | undefined) => { - if (selected_repo_id == repo_id) return +export const ensureProposalSummaries = async (repo_a: string | undefined) => { + if (selected_a == repo_a) return proposal_summaries.set({ - id: repo_id, + repo_a, summaries: [], - loading: repo_id !== '', + loading: repo_a !== '', }) if (sub) sub.stop() if (sub_statuses) sub_statuses.stop() - selected_repo_id = repo_id + selected_a = repo_a setTimeout(() => { proposal_summaries.update((summaries) => { @@ -57,8 +57,8 @@ export const ensureProposalSummaries = async (repo_id: string | undefined) => { limit: 100, } - if (repo_id) { - const repo_collection = await awaitSelectedRepoCollection(repo_id) + if (repo_a) { + const repo_collection = await awaitSelectedRepoCollection(repo_a) const repo = selectRepoFromCollection(repo_collection) if (!repo) { @@ -108,12 +108,10 @@ export const ensureProposalSummaries = async (repo_id: string | undefined) => { event.content.length > 0 && !event.tags.some((t) => t.length > 1 && t[1] === 'revision-root') ) { - if (!extractRepoIdentiferFromProposalEvent(event) && !repo_id) { + if (!extractRepoAFromProposalEvent(event) && !repo_a) { // link to proposal will not work as it requires an identifier return } - const repo_identifier = - extractRepoIdentiferFromProposalEvent(event) || repo_id || '' proposal_summaries.update((proposals) => { return { @@ -123,7 +121,7 @@ export const ensureProposalSummaries = async (repo_id: string | undefined) => { { ...summary_defaults, id: event.id, - repo_identifier, + repo_a: extractRepoAFromProposalEvent(event) || repo_a || '', title: ( event.tagValue('name') || event.tagValue('description') || @@ -141,9 +139,13 @@ export const ensureProposalSummaries = async (repo_id: string | undefined) => { }) // filter out non root proposals if repo event supports nip34+ features - if (!repo_id && repo_identifier.length > 0) { - const repo_collection = await returnRepoCollection(repo_identifier) - if (selected_repo_id === repo_id && repo_collection.unique_commit) { + if (repo_a && repo_a.length > 0) { + const repo_collection = await returnRepoCollection(repo_a) + if ( + selected_a === repo_a && + repo_collection.events[repo_collection.most_recent_index] + .unique_commit + ) { proposal_summaries.update((proposals) => { return { ...proposals, @@ -238,12 +240,12 @@ function getAndUpdateProposalStatus( }) } -export const extractRepoIdentiferFromProposalEvent = ( +export const extractRepoAFromProposalEvent = ( event: NDKEvent ): string | undefined => { const value = event.tagValue('a') if (!value) return undefined const split = value.split(':') if (split.length < 3) return undefined - return split[2] + return value } diff --git a/src/lib/stores/ReposIdentifier.ts b/src/lib/stores/ReposIdentifier.ts new file mode 100644 index 0000000..4cb101d --- /dev/null +++ b/src/lib/stores/ReposIdentifier.ts @@ -0,0 +1,56 @@ +import type { RepoDIdentiferCollection } from '$lib/components/repo/type' +import { writable, type Writable } from 'svelte/store' +import { ensureRepo, eventToRepoEvent } from './repos' +import { base_relays, ndk } from './ndk' +import { repo_kind } from '$lib/kinds' +import { NDKEvent, NDKRelaySet } from '@nostr-dev-kit/ndk' + +export const repos_identifer: { + [d: string]: Writable +} = {} + +export const ensureIdentifierRepoCollection = ( + identifier: string +): Writable => { + if (!Object.keys(repos_identifer).includes(identifier)) { + repos_identifer[identifier] = writable({ + d: '', + events: [], + loading: true, + }) + const sub = ndk.subscribe( + { kinds: [repo_kind], '#d': [identifier] }, + { closeOnEose: true }, + NDKRelaySet.fromRelayUrls(base_relays, ndk) + ) + sub.on('event', (event: NDKEvent) => { + const repo_event = eventToRepoEvent(event) + if (repo_event && repo_event.identifier === identifier) { + ensureRepo(event).subscribe((repo_event) => { + repos_identifer[identifier].update((collection) => { + let events = collection.events + let exists = false + events.map((e) => { + if (e.author === repo_event.author) { + exists = true + return repo_event + } else return e + }) + if (!exists) events = [...events, repo_event] + return { + ...collection, + events, + } + }) + }) + } + }) + sub.on('eose', () => { + repos_identifer[identifier].update((collection) => ({ + ...collection, + loading: false, + })) + }) + } + return repos_identifer[identifier] +} diff --git a/src/lib/stores/ReposPubkey.ts b/src/lib/stores/ReposPubkey.ts new file mode 100644 index 0000000..898940a --- /dev/null +++ b/src/lib/stores/ReposPubkey.ts @@ -0,0 +1,78 @@ +import type { SelectedPubkeyRepoCollections } from '$lib/components/repo/type' +import { get, writable, type Unsubscriber, type Writable } from 'svelte/store' +import { ensureRepoCollection, eventToRepoEvent } from './repos' +import { base_relays, ndk } from './ndk' +import { repo_kind } from '$lib/kinds' +import { NDKEvent, NDKRelaySet } from '@nostr-dev-kit/ndk' +import { extractAReference } from '$lib/components/repo/utils' + +export const selected_npub_repo_collections: Writable = + writable({ + pubkey: '', + collections: [], + }) + +const unsubscribers: Unsubscriber[] = [] + +export const ensureSelectedPubkeyRepoCollection = ( + pubkey: string +): Writable => { + const collections = get(selected_npub_repo_collections) + if (collections.pubkey === pubkey) return selected_npub_repo_collections + // TODO call unsubscribers + selected_npub_repo_collections.set({ + pubkey, + collections: [], + }) + + const sub = ndk.subscribe( + { kinds: [repo_kind], authors: [pubkey] }, + { closeOnEose: true }, + NDKRelaySet.fromRelayUrls(base_relays, ndk) + ) + const identifiers: string[] = [] + sub.on('event', (event: NDKEvent) => { + const repo_event = eventToRepoEvent(event) + if ( + repo_event && + repo_event.author === pubkey && + !identifiers.includes(repo_event.identifier) + ) + identifiers.push(repo_event.identifier) + }) + sub.on('eose', () => { + identifiers.forEach((identifier) => { + unsubscribers.push( + ensureRepoCollection(`${repo_kind}:${pubkey}:${identifier}`).subscribe( + (c) => { + if (!c.maintainers.includes(pubkey)) return + + selected_npub_repo_collections.update((selected_collections) => { + if (selected_collections.pubkey !== pubkey) + return { ...selected_collections } + let collection_in_selected_collections = false + const collections = selected_collections.collections.map( + (old_c) => { + const ref = extractAReference(old_c.selected_a) + if (ref && ref.identifier === identifier) { + collection_in_selected_collections = true + return { + ...c, + } + } + return { ...old_c } + } + ) + if (!collection_in_selected_collections) collections.push(c) + return { + ...selected_collections, + collections, + } + }) + } + ) + ) + }) + }) + return selected_npub_repo_collections +} diff --git a/src/lib/stores/ReposRecent.ts b/src/lib/stores/ReposRecent.ts new file mode 100644 index 0000000..b7ed981 --- /dev/null +++ b/src/lib/stores/ReposRecent.ts @@ -0,0 +1,55 @@ +import type { RepoRecentCollection } from '$lib/components/repo/type' +import { writable, type Writable } from 'svelte/store' +import { ensureRepo, eventToRepoEvent } from './repos' +import { base_relays, ndk } from './ndk' +import { repo_kind } from '$lib/kinds' +import { NDKEvent, NDKRelaySet } from '@nostr-dev-kit/ndk' + +export const recent_repos: Writable = writable({ + events: [], + loading: true, +}) + +let started = false + +export const ensureRecentRepos = (): Writable => { + if (started) return recent_repos + started = true + const sub = ndk.subscribe( + { kinds: [repo_kind] }, + { closeOnEose: true }, + NDKRelaySet.fromRelayUrls(base_relays, ndk) + ) + sub.on('event', (event: NDKEvent) => { + const repo_event = eventToRepoEvent(event) + if (repo_event) { + ensureRepo(event).subscribe((repo_event) => { + recent_repos.update((collection) => { + let events = collection.events + let exists = false + events.map((e) => { + if ( + e.author === repo_event.author && + e.identifier === repo_event.identifier + ) { + exists = true + return repo_event + } else return e + }) + if (!exists) events = [...events, repo_event] + return { + ...collection, + events, + } + }) + }) + } + }) + sub.on('eose', () => { + recent_repos.update((collection) => ({ + ...collection, + loading: false, + })) + }) + return recent_repos +} diff --git a/src/lib/stores/repo.ts b/src/lib/stores/repo.ts index 9ecb58b..254f637 100644 --- a/src/lib/stores/repo.ts +++ b/src/lib/stores/repo.ts @@ -30,52 +30,46 @@ selected_repo_collection.subscribe((collection) => { selected_repo_event.set({ ...selected_from_collection }) }) -let selected_repo_unique_commit_or_identifier: string = '' +let selected_repo_a: string = '' let selected_unsubscriber: Unsubscriber export const ensureSelectedRepoCollection = ( - unique_commit_or_identifier: string + a: string ): Writable => { - if ( - selected_repo_unique_commit_or_identifier !== unique_commit_or_identifier - ) { + if (selected_repo_a !== a) { let loading = true - selected_repo_unique_commit_or_identifier = unique_commit_or_identifier + selected_repo_a = a if (selected_unsubscriber) selected_unsubscriber() - selected_unsubscriber = ensureRepoCollection( - unique_commit_or_identifier - ).subscribe((repo_collection) => { - selected_repo_collection.set({ ...repo_collection }) - if (loading && !repo_collection.loading) { - loading = false - const repo_event = selectRepoFromCollection(repo_collection) - if (repo_event) - ensureRepoReadme(repo_event.clone, repo_collection.identifier) + selected_unsubscriber = ensureRepoCollection(a).subscribe( + (repo_collection) => { + selected_repo_collection.set({ ...repo_collection }) + if (loading && !repo_collection.loading) { + loading = false + const repo_event = selectRepoFromCollection(repo_collection) + if (repo_event) + ensureRepoReadme(repo_event.clone, repo_collection.selected_a) + } } - }) + ) } return selected_repo_collection } export const awaitSelectedRepoCollection = async ( - unique_commit_or_identifier: string + a: string ): Promise => { return new Promise((r) => { - const unsubscriber = ensureSelectedRepoCollection( - unique_commit_or_identifier - ).subscribe((repo_collection) => { - if ( - selected_repo_unique_commit_or_identifier == - unique_commit_or_identifier && - !repo_collection.loading - ) { - setTimeout(() => { - if (unsubscriber) unsubscriber() - }, 5) - r({ ...repo_collection }) + const unsubscriber = ensureSelectedRepoCollection(a).subscribe( + (repo_collection) => { + if (selected_repo_a === a && !repo_collection.loading) { + setTimeout(() => { + if (unsubscriber) unsubscriber() + }, 5) + r({ ...repo_collection }) + } } - }) + ) }) } @@ -83,19 +77,14 @@ export const selected_repo_readme: Writable = writable({ ...readme_defaults, }) -const ensureRepoReadme = async ( - clone: string[], - unique_commit_or_identifier: string -): Promise => { +const ensureRepoReadme = async (clone: string[], a: string): Promise => { selected_repo_readme.set({ ...readme_defaults }) /** update writable unless selected readme has changed */ const update = (md: string | undefined = undefined): void => { const latest_collection = get(selected_repo_collection) if ( - [latest_collection.identifier, latest_collection.unique_commit].includes( - unique_commit_or_identifier - ) + [latest_collection.selected_a, latest_collection.selected_a].includes(a) ) { selected_repo_readme.set({ md: md || '', diff --git a/src/lib/stores/repos.ts b/src/lib/stores/repos.ts index 27cf78f..9cc55bc 100644 --- a/src/lib/stores/repos.ts +++ b/src/lib/stores/repos.ts @@ -1,64 +1,54 @@ import { + event_defaults, collection_defaults, type RepoCollection, type RepoEvent, type RepoSummary, } from '$lib/components/repo/type' -import { NDKRelaySet, type NDKFilter, NDKEvent } from '@nostr-dev-kit/ndk' +import { NDKRelaySet, NDKEvent } from '@nostr-dev-kit/ndk' import { get, writable, type Writable } from 'svelte/store' import { base_relays, ndk } from './ndk' import { repo_kind } from '$lib/kinds' -import { selectRepoFromCollection } from '$lib/components/repo/utils' -import { selected_repo_collection } from './repo' +import { + extractAReference, + selectRepoFromCollection, +} from '$lib/components/repo/utils' +import { nip19 } from 'nostr-tools' export const repos: { - [unique_commit_or_identifier: string]: Writable + [a: string]: Writable } = {} -export const returnRepoCollection = async ( - unique_commit_or_identifier: string -): Promise => { - return new Promise((r) => { - const unsubscriber = ensureRepoCollection( - unique_commit_or_identifier - ).subscribe((c) => { - if (!c.loading) { - setTimeout(() => { - if (unsubscriber) unsubscriber() - }, 5) - r(c) - } - }) - }) -} +export const repo_collections: { + [a: string]: Writable +} = {} -export const ensureRepoCollection = ( - unique_commit_or_identifier: string -): Writable => { - if (!repos[unique_commit_or_identifier]) { - let base: RepoCollection = { - ...collection_defaults, +export const ensureRepo = (a: string | NDKEvent): Writable => { + if (typeof a !== 'string') { + const repo_event = eventToRepoEvent(a) + if (repo_event) { + const a = repoEventToARef(repo_event) + repos[a] = writable({ ...repo_event, loading: true }) + fetchReferencedBy(repo_event) + return repos[a] } - if (unique_commit_or_identifier.length === 40) { - base = { ...base, unique_commit: unique_commit_or_identifier } - } else { - base = { ...base, identifier: unique_commit_or_identifier } + return repos[''] + } + if (!repos[a]) { + const base: RepoEvent = { + ...event_defaults, } - repos[unique_commit_or_identifier] = writable(base) - const filter: NDKFilter = base.unique_commit - ? { - kinds: [repo_kind], - '#r': [base.unique_commit], - limit: 100, - } - : { - kinds: [repo_kind], - '#d': [base.identifier], - limit: 100, - } + repos[a] = writable(base) + + const a_ref = extractAReference(a) + + if (!a_ref) return repos[a] + + const { pubkey, identifier } = a_ref + const sub = ndk.subscribe( - filter, + { kinds: [repo_kind], '#d': [identifier], authors: [pubkey] }, { groupable: true, // default 100 @@ -69,36 +59,122 @@ export const ensureRepoCollection = ( ) sub.on('event', (event: NDKEvent) => { const repo_event = eventToRepoEvent(event) + if (repo_event) { - const collection_for_unique_commit = - unique_commit_or_identifier.length === 40 - // get repo events with same identifer but no unique_commit as - // the assumption is that they will be the same repo - if (collection_for_unique_commit) { - ensureRepoCollection(repo_event.identifier) - // we will process them just before we turn loading to true - } - repos[unique_commit_or_identifier].update((repo_collection) => { - return { - ...repo_collection, - unique_commit: - repo_collection.unique_commit.length > 0 - ? repo_collection.unique_commit - : repo_event.unique_commit || '', - events: [...repo_collection.events, repo_event as RepoEvent], - } - }) - fetchReferencedBy( - repo_event, - unique_commit_or_identifier, - collection_for_unique_commit + if ( + identifier === repo_event.identifier && + pubkey === repo_event.author ) + repos[a].update(() => { + return { + ...repo_event, + } + }) + fetchReferencedBy(repo_event) // TODO fetch stargazers } }) sub.on('eose', () => { // still awaiting reference_by at this point - repos[unique_commit_or_identifier].update((repo_collection) => { + repos[a].update((repo_event) => { + return { + ...repo_event, + loading: false, + } + }) + }) + } + setTimeout(() => { + repos[a].update((repo_event) => { + return { + ...repo_event, + loading: false, + } + }) + }, 5000) + return repos[a] +} + +export const returnRepo = async (a: string): Promise => { + return new Promise((r) => { + const unsubscriber = ensureRepo(a).subscribe((c) => { + if (!c.loading) { + setTimeout(() => { + if (unsubscriber) unsubscriber() + }, 5) + r(c) + } + }) + }) +} + +export const ensureRepoCollection = (a: string): Writable => { + if (!repo_collections[a]) { + const base: RepoCollection = { + ...collection_defaults, + selected_a: a, + } + + repo_collections[a] = writable(base) + + const a_ref = extractAReference(a) + + if (!a_ref) return repo_collections[a] + + const { pubkey, identifier } = a_ref + + returnRepo(a).then(async (repo_event) => { + if (get(repo_collections[a]).events.length > 0) return + repo_collections[a].update((collection) => { + return { + ...collection, + events: [repo_event], + maintainers: repo_event.maintainers, + most_recent_index: 0, + } + }) + + const new_maintainers: string[] = [] + + const addMaintainers = async (m: string) => { + const m_repo_event = await returnRepo(`${repo_kind}:${m}:${identifier}`) + repo_collections[a].update((collection) => { + m_repo_event.maintainers.forEach((m) => { + if ( + ![pubkey, ...collection.maintainers, ...new_maintainers].includes( + m + ) + ) + new_maintainers.push(m) + }) + const events = [...collection.events, m_repo_event] + const most_recent = events.sort( + (a, b) => b.created_at - a.created_at + )[0] + return { + ...collection, + events, + most_recent_index: events.findIndex( + (e) => e.author === most_recent.author + ), + maintainers: [...collection.maintainers, ...new_maintainers], + } + }) + } + + // add maintainer events + await Promise.all( + repo_event.maintainers + .filter((m) => m !== pubkey) + .map((m) => addMaintainers(m)) + ) + + // also add maintainers included in their maintainer events + while (new_maintainers.length > 0) { + await Promise.all(new_maintainers.map((m) => addMaintainers(m))) + } + + repo_collections[a].update((repo_collection) => { return { ...repo_collection, loading: false, @@ -107,22 +183,35 @@ export const ensureRepoCollection = ( }) } setTimeout(() => { - repos[unique_commit_or_identifier].update((collection) => { + repo_collections[a].update((repo_collection) => { return { - ...collection, - events: collection.events.map((e) => ({ ...e, loading: false })), + ...repo_collection, loading: false, } }) }, 5000) - return repos[unique_commit_or_identifier] + return repo_collections[a] +} + +export const returnRepoCollection = async ( + a: string +): Promise => { + return new Promise((r) => { + const unsubscriber = ensureRepoCollection(a).subscribe((c) => { + if (!c.loading) { + setTimeout(() => { + if (unsubscriber) unsubscriber() + }, 5) + r(c) + } + }) + }) } -const fetchReferencedBy = ( - repo_event: RepoEvent, - unique_commit_or_identifier: string, - collection_for_unique_commit: boolean -) => { +const repoEventToARef = (repo_event: RepoEvent): string => + `${repo_kind}:${repo_event.author}:${repo_event.identifier}` + +const fetchReferencedBy = (repo_event: RepoEvent) => { const relays_to_use = repo_event.relays.length < 3 ? repo_event.relays @@ -130,66 +219,39 @@ const fetchReferencedBy = ( const ref_sub = ndk.subscribe( { - '#a': [ - `${repo_kind}:${repo_event.maintainers[0]}:${repo_event.identifier}`, - ], + '#a': [repoEventToARef(repo_event)], limit: 10, }, { groupable: true, // default 100 groupableDelay: 200, - closeOnEose: !get(selected_repo_collection) - .events.map((e) => e.identifier) - .includes(repo_event.identifier), + closeOnEose: true, }, NDKRelaySet.fromRelayUrls(relays_to_use, ndk) ) ref_sub.on('event', (ref_event: NDKEvent) => { - repos[unique_commit_or_identifier].update((repo_collection) => { + repos[repoEventToARef(repo_event)].update((repo_event) => { return { - ...repo_collection, - events: [ - ...repo_collection.events.map((latest_ref_event) => { - if (latest_ref_event.event_id === repo_event.event_id) { - return { - ...latest_ref_event, - referenced_by: latest_ref_event.referenced_by - ? [...latest_ref_event.referenced_by, ref_event.id] - : [ref_event.id], - } - } - return latest_ref_event - }), - ], + ...repo_event, + referenced_by: repo_event.referenced_by.includes(ref_event.id) + ? [...repo_event.referenced_by] + : [...repo_event.referenced_by, ref_event.id], + most_recent_reference_timestamp: + ref_event.created_at && + repo_event.most_recent_reference_timestamp < ref_event.created_at + ? ref_event.created_at + : repo_event.most_recent_reference_timestamp, } }) }) - ref_sub.on('eose', () => { - repos[unique_commit_or_identifier].update((repo_collection) => { - const events = [ - ...repo_collection.events.map((latest_ref_event) => { - if (latest_ref_event.event_id === repo_event.event_id) { - return { - ...latest_ref_event, - // finished loading repo_event as we have all referenced_by events - loading: false, - } - } - return latest_ref_event - }), - ] - const still_loading_events_in_collection = events.some((e) => e.loading) - if (collection_for_unique_commit && !still_loading_events_in_collection) - addEventsWithMatchingIdentifiers(events) + ref_sub.on('eose', () => { + repos[repoEventToARef(repo_event)].update((repo_event) => { return { - ...repo_collection, - events, - loading: - still_loading_events_in_collection || - // for uninque_commit loading will complete after extra identifer events are added - collection_for_unique_commit, + ...repo_event, + // finished loading repo_event as we have all referenced_by events + loading: false, } }) }) @@ -202,7 +264,10 @@ export const eventToRepoEvent = (event: NDKEvent): RepoEvent | undefined => { event.getMatchingTags('maintainers').forEach((t: string[]) => { t.forEach((v, i) => { if (i > 0 && v !== maintainers[0]) { - maintainers.push(v) + try { + nip19.npubEncode(v) // will throw if invalid hex pubkey + maintainers.push(v) + } catch {} } }) }) @@ -233,6 +298,7 @@ export const eventToRepoEvent = (event: NDKEvent): RepoEvent | undefined => { return { event_id: event.id, naddr: event.encode(), + author: event.pubkey, identifier: event.replaceableDTag(), unique_commit: event.tagValue('r') || undefined, name: event.tagValue('name') || '', @@ -243,6 +309,7 @@ export const eventToRepoEvent = (event: NDKEvent): RepoEvent | undefined => { maintainers, relays, referenced_by: [], + most_recent_reference_timestamp: event.created_at || 0, created_at: event.created_at || 0, loading: true, // loading until references fetched } @@ -256,147 +323,29 @@ export const repoCollectionToSummary = ( return { name: selected.name, identifier: selected.identifier, + naddr: selected.naddr, unique_commit: selected.unique_commit, description: selected.description, maintainers: selected.maintainers, loading: collection.loading, created_at: selected.created_at, + most_recent_reference_timestamp: Math.max.apply( + 0, + collection.events.map((e) => e.most_recent_reference_timestamp) + ), } as RepoSummary } -/** to be called once all existing events have been found. this - * function is useful if we assume events with the same - * identifier reference the same repository */ -const addEventsWithMatchingIdentifiers = (exisiting_events: RepoEvent[]) => { - // add events with same identifier but no unique_commit - exisiting_events - // filter out duplicate identifiers - .filter( - (e, i) => - exisiting_events.findIndex((v) => v.identifier == e.identifier) === i - ) - // subscribe to each identifier - .forEach((repo_event) => { - ensureRepoCollection(repo_event.identifier).subscribe( - (identiifer_collection) => { - // if extra event(s) - if ( - identiifer_collection.events.some( - (identifier_repo) => - !exisiting_events.some( - (e) => e.event_id === identifier_repo.event_id - ) - ) - ) { - // add identifier events - repos[repo_event.unique_commit as string].update( - (repo_collection) => { - const events = [ - ...repo_collection.events, - ...identiifer_collection.events - .filter( - (identifier_repo) => - !repo_collection.events.some( - (e) => e.event_id === identifier_repo.event_id - ) - ) - .map((e) => ({ ...e })), - ] - return { - ...repo_collection, - events, - // if all RepoEvents are loaded, the collection is too - loading: events.some((e) => e.loading), - } - } - ) - } - } - ) - }) -} - -export const recent_repo_summaries: Writable = writable([]) - -export const recent_repo_summaries_loading = writable(false) - -let began_fetching_repo_events = false -export const ensureRecentReposEvents = () => { - if (began_fetching_repo_events) return - began_fetching_repo_events = true - recent_repo_summaries_loading.set(true) - const sub = ndk.subscribe( - { - kinds: [repo_kind], - limit: 100, - }, - { - closeOnEose: false, - } - ) - const events: RepoEvent[] = [] - sub.on('event', (event: NDKEvent) => { - const repo_event = eventToRepoEvent(event) - if (repo_event) events.push(repo_event) - }) - sub.on('eose', () => { - const unique_commits = [ - ...new Set(events.map((e) => e.unique_commit).filter((s) => !!s)), - ] as string[] - const identifers_not_linked_to_unique_commit = [ - ...new Set(events.map((e) => e.identifier)), - ].filter( - (identifier) => - !events.some((e) => e.identifier == identifier && e.unique_commit) - ) - unique_commits - .concat(identifers_not_linked_to_unique_commit) - .forEach((c) => { - ensureRepoCollection(c).subscribe((repo_collection) => { - const summary = repoCollectionToSummary(repo_collection) - if (!summary) return - recent_repo_summaries.update((repos) => { - if ( - summary.identifier === 'dotfiles' && - repos.some((repo) => repo.identifier === 'dotfiles') - ) - return [...repos] - // if duplicate - if ( - repos.some( - (repo) => - (repo.unique_commit && - repo.unique_commit === repo_collection.unique_commit) || - (!repo.unique_commit && - repo.identifier === repo_collection.identifier) - ) - ) { - return [ - // update summary - ...repos.map((repo) => { - if ( - summary && - ((repo.unique_commit && - repo.unique_commit === repo_collection.unique_commit) || - (!repo.unique_commit && - repo.identifier === repo_collection.identifier)) - ) - return summary - return { ...repo } - }), - ].sort((a, b) => b.created_at - a.created_at) - } - // if not duplicate - add summary - else if (summary) { - console.log( - `${summary.identifier} ${summary.unique_commit} col ${repo_collection.unique_commit}` - ) - return [...repos, summary] - } - return [...repos] - }) - }) - }) - recent_repo_summaries_loading.set(false) - }) +export const repoEventToSummary = (event: RepoEvent): RepoSummary => { + return { + name: event.name, + identifier: event.identifier, + naddr: event.naddr, + unique_commit: event.unique_commit, + description: event.description, + maintainers: event.maintainers, + loading: event.loading, + created_at: event.created_at, + most_recent_reference_timestamp: event.most_recent_reference_timestamp, + } as RepoSummary } diff --git a/src/lib/wrappers/ComposeIssue.svelte b/src/lib/wrappers/ComposeIssue.svelte index be40466..92b6f65 100644 --- a/src/lib/wrappers/ComposeIssue.svelte +++ b/src/lib/wrappers/ComposeIssue.svelte @@ -61,7 +61,7 @@ submitting = false submitted = true setTimeout(() => { - goto(`/repo/${repo_event.identifier}/issue/${event.id}`) + goto(`/r/${repo_event.identifier}/issues/${event.id}`) }, 2000) } catch {} } diff --git a/src/lib/wrappers/ComposeReply.svelte b/src/lib/wrappers/ComposeReply.svelte index c6ac56f..1e1752f 100644 --- a/src/lib/wrappers/ComposeReply.svelte +++ b/src/lib/wrappers/ComposeReply.svelte @@ -24,7 +24,7 @@ let submitted = false let edit_mode = false $: { - repo_identifier = $selected_repo_collection.identifier + repo_identifier = $selected_repo_event.identifier selected_issue_full selected_proposal_or_issue = type === 'proposal' ? $selected_proposal_full : $selected_issue_full @@ -66,10 +66,9 @@ if ($selected_repo_event.unique_commit) { new_event.tags.push(['r', $selected_repo_event.unique_commit]) } - new_event.tags.push([ - 'a', - `${repo_kind}:${$selected_repo_event.maintainers[0]}:${repo_identifier}`, - ]) + $selected_repo_collection.maintainers.forEach((m) => { + new_event.tags.push(['a', `${repo_kind}:${m}:${repo_identifier}`]) + }) let parent_event_user_relay = user_relays[event.pubkey] ? get(user_relays[event.pubkey]).ndk_relays?.writeRelayUrls[0] : undefined diff --git a/src/lib/wrappers/RepoDetails.svelte b/src/lib/wrappers/RepoDetails.svelte index e6ec30d..6bf329a 100644 --- a/src/lib/wrappers/RepoDetails.svelte +++ b/src/lib/wrappers/RepoDetails.svelte @@ -5,9 +5,9 @@ selected_repo_event, } from '$lib/stores/repo' - export let repo_id = '' + export let a = '' - ensureSelectedRepoCollection(repo_id) + ensureSelectedRepoCollection(a) diff --git a/src/lib/wrappers/RepoMenu.svelte b/src/lib/wrappers/RepoMenu.svelte index a5958be..e3b2c6e 100644 --- a/src/lib/wrappers/RepoMenu.svelte +++ b/src/lib/wrappers/RepoMenu.svelte @@ -5,17 +5,16 @@ import { proposal_status_open } from '$lib/kinds' import { issue_summaries } from '$lib/stores/Issues' import { proposal_summaries } from '$lib/stores/Proposals' - import { selected_repo_readme } from '$lib/stores/repo' + import { selected_repo_event, selected_repo_readme } from '$lib/stores/repo' export let selected_tab: RepoPage = 'about' - export let identifier = ''
    {#if !$selected_repo_readme.failed} @@ -23,7 +22,7 @@ {/if} @@ -44,7 +43,7 @@ {/if} diff --git a/src/lib/wrappers/RepoPageWrapper.svelte b/src/lib/wrappers/RepoPageWrapper.svelte index 6534a69..6d32ab4 100644 --- a/src/lib/wrappers/RepoPageWrapper.svelte +++ b/src/lib/wrappers/RepoPageWrapper.svelte @@ -10,32 +10,36 @@ import { ensureProposalSummaries } from '$lib/stores/Proposals' import { ensureIssueSummaries } from '$lib/stores/Issues' import type { RepoPage } from '$lib/components/repo/type' + import { naddrToRepoA } from '$lib/components/repo/utils' - export let identifier = '' + export let repo_naddr = '' export let selected_tab: RepoPage = 'about' export let with_side_bar = true export let show_details_on_mobile = false - ensureSelectedRepoCollection(identifier) - ensureProposalSummaries(identifier) - ensureIssueSummaries(identifier) + let invalid_naddr = false + let a = '' - let repo_error = false + $: { + const a_result = naddrToRepoA(repo_naddr) + if (a_result) { + a = a_result + invalid_naddr = false + ensureSelectedRepoCollection(a) + ensureProposalSummaries(a) + ensureIssueSummaries(a) + } else { + invalid_naddr = true + } + } let waited_5_secs = false setTimeout(() => { waited_5_secs = true }, 5000) - - $: { - repo_error = - !$selected_repo_collection.loading && - waited_5_secs && - $selected_repo_event.name.length === 0 - } -{#if repo_error} +{#if invalid_naddr || (waited_5_secs && $selected_repo_collection.loading && $selected_repo_event.name.length)} {:else} @@ -69,7 +77,7 @@

    Repository Details

    - +
    diff --git a/src/lib/wrappers/ReposRecent.svelte b/src/lib/wrappers/ReposRecent.svelte index 12b854e..16218d2 100644 --- a/src/lib/wrappers/ReposRecent.svelte +++ b/src/lib/wrappers/ReposRecent.svelte @@ -1,16 +1,17 @@ repoEventToSummary(c) || { ...summary_defaults } + )} + group_by="name" + loading={$recent_repos.loading} /> diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index d1bb389..67c46ca 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -79,7 +79,7 @@
    - to host the authoratitive code. eg. Gitea, Github, Gitlab, + to host the authoritative code. eg. Gitea, Github, Gitlab, BitBucket...
    diff --git a/src/routes/about/+page.svelte b/src/routes/about/+page.svelte index 20ff8db..3dccc8a 100644 --- a/src/routes/about/+page.svelte +++ b/src/routes/about/+page.svelte @@ -44,14 +44,20 @@

    please provide feedback

    - via an ngit issue, a - ngit issue, a + gitworkshop.dev issue or directly to DanConwayDev on nostr

    @@ -283,14 +289,20 @@

    please provide feedback

    - via an ngit issue, a - ngit issue, a + gitworkshop.dev issue or directly to DanConwayDev on nostr

    diff --git a/src/routes/e/[nostr_ref]/+page.svelte b/src/routes/e/[nostr_ref]/+page.svelte new file mode 100644 index 0000000..8ea6782 --- /dev/null +++ b/src/routes/e/[nostr_ref]/+page.svelte @@ -0,0 +1,122 @@ + + +{#if error && waited} + + + +{:else} + loading... +{/if} diff --git a/src/routes/e/[nostr_ref]/+page.ts b/src/routes/e/[nostr_ref]/+page.ts new file mode 100644 index 0000000..ed86aa7 --- /dev/null +++ b/src/routes/e/[nostr_ref]/+page.ts @@ -0,0 +1,5 @@ +export const load = ({ params }: { params: { nostr_ref: string } }) => { + return { + nostr_ref: params.nostr_ref, + } +} diff --git a/src/routes/ngit/+page.svelte b/src/routes/ngit/+page.svelte index fe4333a..e4267de 100644 --- a/src/routes/ngit/+page.svelte +++ b/src/routes/ngit/+page.svelte @@ -36,14 +36,20 @@

    please provide feedback

    - via an ngit issue, a - ngit issue, a + gitworkshop.dev issue or directly to DanConwayDev on nostr

    diff --git a/src/routes/p/[npub]/+page.svelte b/src/routes/p/[npub]/+page.svelte index bd01d14..976b90b 100644 --- a/src/routes/p/[npub]/+page.svelte +++ b/src/routes/p/[npub]/+page.svelte @@ -2,12 +2,13 @@ import { nip19 } from 'nostr-tools' import Container from '$lib/components/Container.svelte' import ReposSummaryList from '$lib/components/ReposSummaryList.svelte' - import { - ensureRecentReposEvents, - recent_repo_summaries, - recent_repo_summaries_loading, - } from '$lib/stores/repos' import UserHeader from '$lib/components/users/UserHeader.svelte' + import { + ensureSelectedPubkeyRepoCollection, + selected_npub_repo_collections, + } from '$lib/stores/ReposPubkey' + import { repoCollectionToSummary } from '$lib/stores/repos' + import { summary_defaults } from '$lib/components/repo/type' export let data: { npub: string } @@ -19,11 +20,11 @@ if (decoded.type === 'npub') pubkey = decoded.data else if (decoded.type === 'nprofile') pubkey = decoded.data.pubkey else error = true + if (pubkey) ensureSelectedPubkeyRepoCollection(pubkey) } catch { error = true } } - ensureRecentReposEvents() {#if error} @@ -56,10 +57,10 @@
    pubkey && summary.maintainers.includes(pubkey) + repos={$selected_npub_repo_collections.collections.map( + (c) => repoCollectionToSummary(c) || { ...summary_defaults } )} - loading={$recent_repo_summaries_loading} + loading={false} />
    diff --git a/src/routes/repo/[repo_id]/+page.svelte b/src/routes/r/[repo_naddr]/+page.svelte similarity index 86% rename from src/routes/repo/[repo_id]/+page.svelte rename to src/routes/r/[repo_naddr]/+page.svelte index cea0044..a812731 100644 --- a/src/routes/repo/[repo_id]/+page.svelte +++ b/src/routes/r/[repo_naddr]/+page.svelte @@ -4,16 +4,16 @@ import RepoPageWrapper from '$lib/wrappers/RepoPageWrapper.svelte' import { goto } from '$app/navigation' - export let data: { repo_id: string } - let identifier = data.repo_id + export let data: { repo_naddr: string } + let repo_naddr = data.repo_naddr $: { if ($selected_repo_readme.failed === true) - goto(`/repo/${identifier}/proposals`) + goto(`/r/${repo_naddr}/proposals`) } - +

    README.md

    diff --git a/src/routes/r/[repo_naddr]/+page.ts b/src/routes/r/[repo_naddr]/+page.ts new file mode 100644 index 0000000..f4c1c44 --- /dev/null +++ b/src/routes/r/[repo_naddr]/+page.ts @@ -0,0 +1,7 @@ +export const load = ({ params }: { params: { repo_naddr: string } }) => { + return { + repo_naddr: params.repo_naddr, + } +} + +export const ssr = false diff --git a/src/routes/repo/[repo_id]/issues/+page.svelte b/src/routes/r/[repo_naddr]/issues/+page.svelte similarity index 93% rename from src/routes/repo/[repo_id]/issues/+page.svelte rename to src/routes/r/[repo_naddr]/issues/+page.svelte index 7338eb4..6f0ef54 100644 --- a/src/routes/repo/[repo_id]/issues/+page.svelte +++ b/src/routes/r/[repo_naddr]/issues/+page.svelte @@ -10,14 +10,14 @@ import { issue_summaries } from '$lib/stores/Issues' import RepoPageWrapper from '$lib/wrappers/RepoPageWrapper.svelte' - export let data: { repo_id: string } - let identifier = data.repo_id + export let data: { repo_naddr: string } + let repo_naddr = data.repo_naddr let status: number = proposal_status_open let filtered: IssueSummary[] = [] $: filtered = $issue_summaries.summaries.filter((s) => s.status === status) - +
    @@ -67,7 +67,7 @@
    create issue diff --git a/src/routes/r/[repo_naddr]/issues/+page.ts b/src/routes/r/[repo_naddr]/issues/+page.ts new file mode 100644 index 0000000..0f52eb4 --- /dev/null +++ b/src/routes/r/[repo_naddr]/issues/+page.ts @@ -0,0 +1,5 @@ +export const load = ({ params }: { params: { repo_naddr: string } }) => { + return { + repo_naddr: params.repo_naddr, + } +} diff --git a/src/routes/repo/[repo_id]/issue/[issue_id]/+page.svelte b/src/routes/r/[repo_naddr]/issues/[issue_nip19]/+page.svelte similarity index 64% rename from src/routes/repo/[repo_id]/issue/[issue_id]/+page.svelte rename to src/routes/r/[repo_naddr]/issues/[issue_nip19]/+page.svelte index f215a5c..a39313f 100644 --- a/src/routes/repo/[repo_id]/issue/[issue_id]/+page.svelte +++ b/src/routes/r/[repo_naddr]/issues/[issue_nip19]/+page.svelte @@ -1,9 +1,4 @@ - - {#if issue_error} + + {#if invalid_issue_ref || (waited_5_secs && issue_error)} {:else} diff --git a/src/routes/r/[repo_naddr]/issues/[issue_nip19]/+page.ts b/src/routes/r/[repo_naddr]/issues/[issue_nip19]/+page.ts new file mode 100644 index 0000000..7cee0d5 --- /dev/null +++ b/src/routes/r/[repo_naddr]/issues/[issue_nip19]/+page.ts @@ -0,0 +1,10 @@ +export const load = ({ + params, +}: { + params: { issue_nip19: string; repo_naddr: string } +}) => { + return { + repo_naddr: decodeURIComponent(params.repo_naddr), + issue_nip19: params.issue_nip19, + } +} diff --git a/src/routes/repo/[repo_id]/issues/new/+page.svelte b/src/routes/r/[repo_naddr]/issues/new/+page.svelte similarity index 65% rename from src/routes/repo/[repo_id]/issues/new/+page.svelte rename to src/routes/r/[repo_naddr]/issues/new/+page.svelte index e791699..d3d2d77 100644 --- a/src/routes/repo/[repo_id]/issues/new/+page.svelte +++ b/src/routes/r/[repo_naddr]/issues/new/+page.svelte @@ -8,27 +8,31 @@ import RepoHeader from '$lib/components/repo/RepoHeader.svelte' import Container from '$lib/components/Container.svelte' import ComposeIssue from '$lib/wrappers/ComposeIssue.svelte' + import { naddrToRepoA } from '$lib/components/repo/utils' - export let data: { repo_id: string } - let identifier = data.repo_id + export let data: { repo_naddr: string } + let repo_naddr = data.repo_naddr + let invalid_naddr = false + let a = '' - ensureSelectedRepoCollection(identifier) - let repo_error = false + $: { + const a_result = naddrToRepoA(repo_naddr) + if (a_result) { + a = a_result + invalid_naddr = false + ensureSelectedRepoCollection(a) + } else { + invalid_naddr = true + } + } let waited_5_secs = false setTimeout(() => { waited_5_secs = true }, 5000) - - $: { - repo_error = - !$selected_repo_collection.loading && - waited_5_secs && - $selected_repo_event.name.length === 0 - } -{#if repo_error} +{#if invalid_naddr || (waited_5_secs && $selected_repo_collection.loading && $selected_repo_event.name.length)} {:else} @@ -56,7 +64,7 @@
    diff --git a/src/routes/r/[repo_naddr]/issues/new/+page.ts b/src/routes/r/[repo_naddr]/issues/new/+page.ts new file mode 100644 index 0000000..0f52eb4 --- /dev/null +++ b/src/routes/r/[repo_naddr]/issues/new/+page.ts @@ -0,0 +1,5 @@ +export const load = ({ params }: { params: { repo_naddr: string } }) => { + return { + repo_naddr: params.repo_naddr, + } +} diff --git a/src/routes/repo/[repo_id]/proposals/+page.svelte b/src/routes/r/[repo_naddr]/proposals/+page.svelte similarity index 96% rename from src/routes/repo/[repo_id]/proposals/+page.svelte rename to src/routes/r/[repo_naddr]/proposals/+page.svelte index 59e46c1..ab299f8 100644 --- a/src/routes/repo/[repo_id]/proposals/+page.svelte +++ b/src/routes/r/[repo_naddr]/proposals/+page.svelte @@ -11,14 +11,15 @@ import { proposal_summaries } from '$lib/stores/Proposals' import RepoPageWrapper from '$lib/wrappers/RepoPageWrapper.svelte' - export let data: { repo_id: string } - let identifier = data.repo_id + export let data: { repo_naddr: string } + let repo_naddr = data.repo_naddr + let status: number = proposal_status_open let filtered: ProposalSummary[] = [] $: filtered = $proposal_summaries.summaries.filter((s) => s.status === status) - +
    diff --git a/src/routes/r/[repo_naddr]/proposals/+page.ts b/src/routes/r/[repo_naddr]/proposals/+page.ts new file mode 100644 index 0000000..0f52eb4 --- /dev/null +++ b/src/routes/r/[repo_naddr]/proposals/+page.ts @@ -0,0 +1,5 @@ +export const load = ({ params }: { params: { repo_naddr: string } }) => { + return { + repo_naddr: params.repo_naddr, + } +} diff --git a/src/routes/r/[repo_naddr]/proposals/[proposal_nip19]/+page.svelte b/src/routes/r/[repo_naddr]/proposals/[proposal_nip19]/+page.svelte new file mode 100644 index 0000000..61de9bc --- /dev/null +++ b/src/routes/r/[repo_naddr]/proposals/[proposal_nip19]/+page.svelte @@ -0,0 +1,107 @@ + + + + {#if invalid_proposal_ref || (waited_5_secs && proposal_error)} + + + + {:else} + + +
    +
    +
    + {#if $selected_proposal_full.proposal_event} + + {/if} +
    +
    + +
    +
    + {/if} +
    diff --git a/src/routes/r/[repo_naddr]/proposals/[proposal_nip19]/+page.ts b/src/routes/r/[repo_naddr]/proposals/[proposal_nip19]/+page.ts new file mode 100644 index 0000000..b90979b --- /dev/null +++ b/src/routes/r/[repo_naddr]/proposals/[proposal_nip19]/+page.ts @@ -0,0 +1,10 @@ +export const load = ({ + params, +}: { + params: { proposal_nip19: string; repo_naddr: string } +}) => { + return { + repo_naddr: decodeURIComponent(params.repo_naddr), + proposal_nip19: params.proposal_nip19, + } +} diff --git a/src/routes/repo/[repo_id]/+page.ts b/src/routes/repo/[repo_id]/+page.ts deleted file mode 100644 index ecedb66..0000000 --- a/src/routes/repo/[repo_id]/+page.ts +++ /dev/null @@ -1,7 +0,0 @@ -export const load = ({ params }: { params: { repo_id: string } }) => { - return { - repo_id: decodeURIComponent(params.repo_id), - } -} - -export const ssr = false diff --git a/src/routes/repo/[repo_id]/issue/[issue_id]/+page.ts b/src/routes/repo/[repo_id]/issue/[issue_id]/+page.ts deleted file mode 100644 index ce6cc54..0000000 --- a/src/routes/repo/[repo_id]/issue/[issue_id]/+page.ts +++ /dev/null @@ -1,10 +0,0 @@ -export const load = ({ - params, -}: { - params: { issue_id: string; repo_id: string } -}) => { - return { - repo_id: decodeURIComponent(params.repo_id), - issue_id: params.issue_id, - } -} diff --git a/src/routes/repo/[repo_id]/issues/+page.ts b/src/routes/repo/[repo_id]/issues/+page.ts deleted file mode 100644 index c70bf13..0000000 --- a/src/routes/repo/[repo_id]/issues/+page.ts +++ /dev/null @@ -1,5 +0,0 @@ -export const load = ({ params }: { params: { repo_id: string } }) => { - return { - repo_id: decodeURIComponent(params.repo_id), - } -} diff --git a/src/routes/repo/[repo_id]/issues/new/+page.ts b/src/routes/repo/[repo_id]/issues/new/+page.ts deleted file mode 100644 index c70bf13..0000000 --- a/src/routes/repo/[repo_id]/issues/new/+page.ts +++ /dev/null @@ -1,5 +0,0 @@ -export const load = ({ params }: { params: { repo_id: string } }) => { - return { - repo_id: decodeURIComponent(params.repo_id), - } -} diff --git a/src/routes/repo/[repo_id]/proposal/[proposal_id]/+page.svelte b/src/routes/repo/[repo_id]/proposal/[proposal_id]/+page.svelte deleted file mode 100644 index 563413c..0000000 --- a/src/routes/repo/[repo_id]/proposal/[proposal_id]/+page.svelte +++ /dev/null @@ -1,122 +0,0 @@ - - - - {#if proposal_error} - - - - {:else} - - -
    -
    -
    - {#if $selected_proposal_full.proposal_event} - - {/if} -
    -
    - -
    -
    - {/if} -
    diff --git a/src/routes/repo/[repo_id]/proposal/[proposal_id]/+page.ts b/src/routes/repo/[repo_id]/proposal/[proposal_id]/+page.ts deleted file mode 100644 index e9c877d..0000000 --- a/src/routes/repo/[repo_id]/proposal/[proposal_id]/+page.ts +++ /dev/null @@ -1,10 +0,0 @@ -export const load = ({ - params, -}: { - params: { proposal_id: string; repo_id: string } -}) => { - return { - repo_id: decodeURIComponent(params.repo_id), - proposal_id: params.proposal_id, - } -} diff --git a/src/routes/repo/[repo_id]/proposals/+page.ts b/src/routes/repo/[repo_id]/proposals/+page.ts deleted file mode 100644 index c70bf13..0000000 --- a/src/routes/repo/[repo_id]/proposals/+page.ts +++ /dev/null @@ -1,5 +0,0 @@ -export const load = ({ params }: { params: { repo_id: string } }) => { - return { - repo_id: decodeURIComponent(params.repo_id), - } -} diff --git a/src/routes/repo/[repo_identifier]/+page.svelte b/src/routes/repo/[repo_identifier]/+page.svelte new file mode 100644 index 0000000..295d66d --- /dev/null +++ b/src/routes/repo/[repo_identifier]/+page.svelte @@ -0,0 +1,20 @@ + + + +
    + +
    +
    diff --git a/src/routes/repo/[repo_identifier]/+page.ts b/src/routes/repo/[repo_identifier]/+page.ts new file mode 100644 index 0000000..0c18d20 --- /dev/null +++ b/src/routes/repo/[repo_identifier]/+page.ts @@ -0,0 +1,7 @@ +export const load = ({ params }: { params: { repo_identifier: string } }) => { + return { + repo_identifier: params.repo_identifier, + } +} + +export const ssr = false diff --git a/src/routes/repo/[repo_identifier]/issue/[event_id]/+page.ts b/src/routes/repo/[repo_identifier]/issue/[event_id]/+page.ts new file mode 100644 index 0000000..677bea0 --- /dev/null +++ b/src/routes/repo/[repo_identifier]/issue/[event_id]/+page.ts @@ -0,0 +1,5 @@ +import { redirect } from '@sveltejs/kit' + +export const load = ({ params }: { params: { event_id: string } }) => { + throw redirect(301, `/e/${params.event_id}`) +} diff --git a/src/routes/repo/[repo_identifier]/issues/+page.ts b/src/routes/repo/[repo_identifier]/issues/+page.ts new file mode 100644 index 0000000..4d5aebc --- /dev/null +++ b/src/routes/repo/[repo_identifier]/issues/+page.ts @@ -0,0 +1,5 @@ +import { redirect } from '@sveltejs/kit' + +export const load = ({ params }: { params: { repo_identifier: string } }) => { + throw redirect(301, `/repo/${params.repo_identifier}`) +} diff --git a/src/routes/repo/[repo_identifier]/proposal/[event_id]/+page.ts b/src/routes/repo/[repo_identifier]/proposal/[event_id]/+page.ts new file mode 100644 index 0000000..677bea0 --- /dev/null +++ b/src/routes/repo/[repo_identifier]/proposal/[event_id]/+page.ts @@ -0,0 +1,5 @@ +import { redirect } from '@sveltejs/kit' + +export const load = ({ params }: { params: { event_id: string } }) => { + throw redirect(301, `/e/${params.event_id}`) +} diff --git a/src/routes/repo/[repo_identifier]/proposals/+page.ts b/src/routes/repo/[repo_identifier]/proposals/+page.ts new file mode 100644 index 0000000..4d5aebc --- /dev/null +++ b/src/routes/repo/[repo_identifier]/proposals/+page.ts @@ -0,0 +1,5 @@ +import { redirect } from '@sveltejs/kit' + +export const load = ({ params }: { params: { repo_identifier: string } }) => { + throw redirect(301, `/repo/${params.repo_identifier}`) +}