+
+
{#if name == identifier}
{#if loading}
@@ -66,6 +156,32 @@
{identifier}
{/if}
{/if}
+
+ {#if ref}
+
+ + xmlns="http://www.w3.org/2000/svg "
+ viewBox="0 0 16 16"
+ class={`w-4 h-4 mr-2 ${isStarred ? "fill-yellow-500" : "fill-gray-400"}`}
+ >
+ {#if isStarred}
+ {#each star_icon_path.filled as p}
+
+ {/each}
+ {:else}
+ {#each star_icon_path.outline as p}
+
+ {/each}
+ {/if}
+
+ {#if isStarred}
+ Unstar
+ {:else}
+ Star
+ {/if}
+
+ {/if}
+
{#if loading}
diff --git a/src/lib/components/stars/icons.ts b/src/lib/components/stars/icons.ts
new file mode 100644
index 0000000..73c465a
--- /dev/null
+++ b/src/lib/components/stars/icons.ts
@@ -0,0 +1,11 @@
+// icon are MIT licenced
+export const star_icon_path = {
+ // https://icon-sets.iconify.design/gravity-ui/star/
+ outline: [
+ "m9.194 5l.351.873l.94.064l3.197.217l-2.46 2.055l-.722.603l.23.914l.782 3.108l-2.714-1.704L8 10.629l-.798.5l-2.714 1.705l.782-3.108l.23-.914l-.723-.603l-2.46-2.055l3.198-.217l.94-.064l.35-.874L8 2.025zm-7.723-.292l3.943-.268L6.886.773C7.29-.231 8.71-.231 9.114.773l1.472 3.667l3.943.268c1.08.073 1.518 1.424.688 2.118L12.185 9.36l.964 3.832c.264 1.05-.886 1.884-1.802 1.31L8 12.4l-3.347 2.101c-.916.575-2.066-.26-1.802-1.309l.964-3.832L.783 6.826c-.83-.694-.391-2.045.688-2.118",
+ ],
+ // https://icon-sets.iconify.design/gravity-ui/star-fill/
+ filled: [
+ "M6.886.773C7.29-.231 8.71-.231 9.114.773l1.472 3.667l3.943.268c1.08.073 1.518 1.424.688 2.118L12.185 9.36l.964 3.832c.264 1.05-.886 1.884-1.802 1.31L8 12.4l-3.347 2.101c-.916.575-2.066-.26-1.802-1.309l.964-3.832L.783 6.826c-.83-.694-.391-2.045.688-2.118l3.943-.268z",
+ ],
+}
diff --git a/src/lib/components/stars/type.ts b/src/lib/components/stars/type.ts
new file mode 100644
index 0000000..f076086
--- /dev/null
+++ b/src/lib/components/stars/type.ts
@@ -0,0 +1,13 @@
+import type { NDKEvent } from '@nostr-dev-kit/ndk'
+
+export interface Stargazers {
+ id: string | undefined
+ events: NDKEvent[]
+ loading: boolean;
+}
+
+export const stars_defaults: Stargazers = {
+ id: '',
+ events: [],
+ loading: true,
+}
diff --git a/src/lib/kinds.ts b/src/lib/kinds.ts
index fd65cf2..06cd21b 100644
--- a/src/lib/kinds.ts
+++ b/src/lib/kinds.ts
@@ -27,3 +27,5 @@ export const repo_kind: number = 30617
export const patch_kind: number = 1617
export const issue_kind: number = 1621
+
+export const bookmarks_kind: number = 10617
diff --git a/src/lib/promise.ts b/src/lib/promise.ts
new file mode 100644
index 0000000..1577059
--- /dev/null
+++ b/src/lib/promise.ts
@@ -0,0 +1,3 @@
+export function timeout(ms: number) {
+ return new Promise((resolve) => setTimeout(resolve, ms))
+}
diff --git a/src/lib/stores/Issues.ts b/src/lib/stores/Issues.ts
index 23b6023..ba229fb 100644
--- a/src/lib/stores/Issues.ts
+++ b/src/lib/stores/Issues.ts
@@ -34,7 +34,7 @@ let selected_repo_id: string | undefined = ''
let sub: NDKSubscription
export const ensureIssueSummaries = async (repo_id: string | undefined) => {
- if (selected_repo_id == repo_id) return
+ if (selected_repo_id === repo_id) return
issue_summaries.set({
id: repo_id,
summaries: [],
diff --git a/src/lib/stores/Proposal.ts b/src/lib/stores/Proposal.ts
index c92d5f7..7aad603 100644
--- a/src/lib/stores/Proposal.ts
+++ b/src/lib/stores/Proposal.ts
@@ -109,7 +109,7 @@ export const ensureProposalFull = (
created_at: event.created_at,
comments: 0,
author: {
- hexpubkey: event.pubkey,
+ pubkey: event.pubkey,
loading: true,
npub: '',
},
diff --git a/src/lib/stores/Stargazers.ts b/src/lib/stores/Stargazers.ts
new file mode 100644
index 0000000..fbf5a11
--- /dev/null
+++ b/src/lib/stores/Stargazers.ts
@@ -0,0 +1,74 @@
+import {
+ NDKRelaySet,
+ type NDKEvent,
+ NDKSubscription,
+ type NDKFilter,
+} from '@nostr-dev-kit/ndk'
+import { writable, type Writable } from 'svelte/store'
+import { awaitSelectedRepoCollection } from './repo'
+import { selectRepoFromCollection } from '$lib/components/repo/utils'
+import { base_relays, ndk } from './ndk'
+import { repo_kind, bookmarks_kind } from '$lib/kinds'
+import { stars_defaults, type Stargazers } from '$lib/components/stars/type'
+
+export const stargazers: Writable = writable(stars_defaults)
+
+let selected_repo_id: string | undefined = ''
+
+let sub: NDKSubscription
+
+export async function fetchStargazers(repo_id: string | undefined) {
+ if (selected_repo_id === repo_id) return
+ selected_repo_id = repo_id;
+ stargazers.set({
+ id: repo_id,
+ events: [],
+ loading: true,
+ })
+ if (sub) sub.stop()
+ if (repo_id) {
+ const repo_collection = await awaitSelectedRepoCollection(repo_id)
+ const repo = selectRepoFromCollection(repo_collection)
+ if (!repo) {
+ // TODO: display error info bar
+ return
+ }
+ const relays_to_use =
+ repo.relays.length > 3
+ ? repo.relays
+ : [...base_relays].concat(repo.relays)
+
+ // todo: relays usually return max 500 results, if a repo is very popular, we may need to paginate
+ const filter = {
+ kinds: [bookmarks_kind],
+ '#a': repo.maintainers.map((m) => `${repo_kind}:${m}:${repo.identifier}`),
+ }
+
+ sub = ndk.subscribe(
+ filter,
+ {
+ closeOnEose: false,
+ },
+ NDKRelaySet.fromRelayUrls(relays_to_use, ndk)
+ )
+
+ sub.on('event', (event: NDKEvent) => {
+ stargazers.update((stars) => {
+ return {
+ ...stars,
+ events: stars.events.concat([event]),
+ loading: false,
+ }
+ })
+ })
+
+ sub.on('eose', () => {
+ stargazers.update((stars) => {
+ return {
+ ...stars,
+ loading: false,
+ }
+ })
+ })
+ }
+}
diff --git a/src/lib/wrappers/RepoMenu.svelte b/src/lib/wrappers/RepoMenu.svelte
index a5958be..df9e477 100644
--- a/src/lib/wrappers/RepoMenu.svelte
+++ b/src/lib/wrappers/RepoMenu.svelte
@@ -1,17 +1,23 @@
import { issue_icon_path } from '$lib/components/issues/icons'
+ import { star_icon_path } from '$lib/components/stars/icons'
import { proposal_icon_path } from '$lib/components/proposals/icons'
import type { RepoPage } from '$lib/components/repo/type'
import { proposal_status_open } from '$lib/kinds'
import { issue_summaries } from '$lib/stores/Issues'
+ import { stargazers } from '$lib/stores/Stargazers'
+ import { logged_in_user } from '$lib/stores/users'
import { proposal_summaries } from '$lib/stores/Proposals'
import { selected_repo_readme } from '$lib/stores/repo'
export let selected_tab: RepoPage = 'about'
export let identifier = ''
+
+ let isStarred = false
+ $: isStarred = $stargazers.events.some((e) => e.pubkey === $logged_in_user?.hexpubkey)
-
+
diff --git a/src/lib/wrappers/RepoPageWrapper.svelte b/src/lib/wrappers/RepoPageWrapper.svelte
index 76107b8..d9618cd 100644
--- a/src/lib/wrappers/RepoPageWrapper.svelte
+++ b/src/lib/wrappers/RepoPageWrapper.svelte
@@ -9,6 +9,7 @@
import Container from '$lib/components/Container.svelte'
import { ensureProposalSummaries } from '$lib/stores/Proposals'
import { ensureIssueSummaries } from '$lib/stores/Issues'
+ import { fetchStargazers } from '$lib/stores/Stargazers'
import type { RepoPage } from '$lib/components/repo/type'
export let identifier = ''
@@ -18,6 +19,7 @@
ensureSelectedRepoCollection(identifier)
ensureProposalSummaries(identifier)
ensureIssueSummaries(identifier)
+ fetchStargazers(identifier)
let repo_error = false
diff --git a/src/routes/repo/[repo_id]/stargazers/+page.svelte b/src/routes/repo/[repo_id]/stargazers/+page.svelte
new file mode 100644
index 0000000..e77188d
--- /dev/null
+++ b/src/routes/repo/[repo_id]/stargazers/+page.svelte
@@ -0,0 +1,34 @@
+
+ import UserHeader from '$lib/components/users/UserHeader.svelte'
+ import type { IssueSummary } from '$lib/components/issues/type'
+ import {
+ proposal_status_applied,
+ proposal_status_closed,
+ proposal_status_open,
+ statusKindtoText,
+ } from '$lib/kinds'
+ import { stargazers } from '$lib/stores/Stargazers'
+ import RepoPageWrapper from '$lib/wrappers/RepoPageWrapper.svelte'
+
+ export let data: { repo_id: string }
+ let identifier = data.repo_id
+ let status: number = proposal_status_open
+
+
+
+ {#if !$stargazers.loading }
+
+ {#if $stargazers.events.length === 0}
+
+ there aren't any stargazers yet
+
+ {:else}
+
+ {#each $stargazers.events as event}
+
+ {/each}
+
+ {/if}
+
+ {/if}
+
diff --git a/src/routes/repo/[repo_id]/stargazers/+page.ts b/src/routes/repo/[repo_id]/stargazers/+page.ts
new file mode 100644
index 0000000..c70bf13
--- /dev/null
+++ b/src/routes/repo/[repo_id]/stargazers/+page.ts
@@ -0,0 +1,5 @@
+export const load = ({ params }: { params: { repo_id: string } }) => {
+ return {
+ repo_id: decodeURIComponent(params.repo_id),
+ }
+}
--
libgit2 1.7.2