65 changed files with 2634 additions and 2385 deletions
@ -0,0 +1,44 @@
@@ -0,0 +1,44 @@
|
||||
// eslint-plugin-svelte |
||||
// https://sveltejs.github.io/eslint-plugin-svelte/user-guide/ |
||||
{ |
||||
"root": true, |
||||
"extends": [ |
||||
// add more generic rule sets here, such as: |
||||
// 'eslint:recommended', |
||||
"plugin:@typescript-eslint/recommended", |
||||
"prettier", |
||||
"plugin:svelte/recommended" |
||||
], |
||||
"parser": "@typescript-eslint/parser", |
||||
"parserOptions": { |
||||
// ... |
||||
"project": "./tsconfig.json", |
||||
"extraFileExtensions": [".svelte"] // This is a required setting in `@typescript-eslint/parser` v4.24.0. |
||||
}, |
||||
"plugins": ["@typescript-eslint", "prettier"], |
||||
"rules": { |
||||
"no-console": 1, |
||||
"prettier/prettier": 2, |
||||
"comma-dangle": [2, "always-multiline"], |
||||
"no-alert": "off", |
||||
"no-unused-vars": "off", |
||||
"@typescript-eslint/no-unused-vars": [ |
||||
"error", |
||||
{ |
||||
"argsIgnorePattern": "^_", |
||||
"varsIgnorePattern": "^_" |
||||
} |
||||
] |
||||
// 'svelte/rule-name': 'error' |
||||
}, |
||||
"overrides": [ |
||||
{ |
||||
"files": ["*.svelte"], |
||||
"parser": "svelte-eslint-parser", |
||||
// Parse the `<script>` in `.svelte` as TypeScript by adding the following configuration. |
||||
"parserOptions": { |
||||
"parser": "@typescript-eslint/parser" |
||||
} |
||||
} |
||||
] |
||||
} |
||||
@ -0,0 +1,16 @@
@@ -0,0 +1,16 @@
|
||||
{ |
||||
"trailingComma": "es5", |
||||
"tabWidth": 2, |
||||
"semi": false, |
||||
"singleQuote": true, |
||||
"printWidth": 80, |
||||
"plugins": ["prettier-plugin-svelte", "prettier-plugin-tailwindcss"], |
||||
"overrides": [ |
||||
{ |
||||
"files": "*.eslintrc", |
||||
"options": { |
||||
"trailingComma": "none" |
||||
} |
||||
} |
||||
] |
||||
} |
||||
@ -1,6 +1,10 @@
@@ -1,6 +1,10 @@
|
||||
{ |
||||
"diffEditor.ignoreTrimWhitespace": false, |
||||
"editor.formatOnPaste": true, |
||||
"editor.formatOnSave": true, |
||||
"svelte.enable-ts-plugin": true |
||||
} |
||||
"eslint.validate": ["javascript", "javascriptreact", "svelte"], |
||||
"editor.defaultFormatter": "esbenp.prettier-vscode", |
||||
"diffEditor.ignoreTrimWhitespace": false, |
||||
"editor.formatOnPaste": true, |
||||
"editor.formatOnSave": true, |
||||
"files.associations": { |
||||
"*.css": "tailwindcss" |
||||
} |
||||
} |
||||
|
||||
@ -1,7 +0,0 @@
@@ -1,7 +0,0 @@
|
||||
maintainers: |
||||
- npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr |
||||
relays: |
||||
- wss://relay.snort.social/ |
||||
- wss://relayable.org/ |
||||
- wss://relay.damus.io/ |
||||
- wss://nos.lol/ |
||||
@ -1,13 +1,13 @@
@@ -1,13 +1,13 @@
|
||||
<script lang="ts"> |
||||
export let no_wrap: boolean = false; |
||||
export let no_wrap: boolean = false |
||||
</script> |
||||
|
||||
<div class="mx-auto lg:container"> |
||||
{#if no_wrap} |
||||
<slot /> |
||||
{:else} |
||||
<div class="px-3"> |
||||
<slot /> |
||||
</div> |
||||
{/if} |
||||
{#if no_wrap} |
||||
<slot /> |
||||
{:else} |
||||
<div class="px-3"> |
||||
<slot /> |
||||
</div> |
||||
{/if} |
||||
</div> |
||||
|
||||
@ -1,38 +1,38 @@
@@ -1,38 +1,38 @@
|
||||
<script lang="ts" context="module"> |
||||
export interface Args { |
||||
name: string; |
||||
description: string; |
||||
repo_id: string; |
||||
loading?: boolean; |
||||
created_at: number; |
||||
name: string |
||||
description: string |
||||
repo_id: string |
||||
loading?: boolean |
||||
created_at: number |
||||
} |
||||
export const defaults: Args = { |
||||
name: "", |
||||
repo_id: "", |
||||
description: "", |
||||
name: '', |
||||
repo_id: '', |
||||
description: '', |
||||
loading: false, |
||||
created_at: 0, |
||||
}; |
||||
} |
||||
</script> |
||||
|
||||
<script lang="ts"> |
||||
export let { name, description, repo_id: repo_id, loading } = defaults; |
||||
let short_name: string; |
||||
export let { name, description, repo_id: repo_id, loading } = defaults |
||||
let short_name: string |
||||
$: { |
||||
if (name.length > 45) short_name = name.slice(0, 45) + "..."; |
||||
else if (name.length == 0) short_name = "Untitled"; |
||||
else short_name = name; |
||||
if (name.length > 45) short_name = name.slice(0, 45) + '...' |
||||
else if (name.length == 0) short_name = 'Untitled' |
||||
else short_name = name |
||||
} |
||||
$: short_descrption = |
||||
description.length > 50 ? description.slice(0, 45) + "..." : description; |
||||
description.length > 50 ? description.slice(0, 45) + '...' : description |
||||
</script> |
||||
|
||||
<div class="p-4 bg-base-200 my-2 rounded-lg"> |
||||
<div class="my-2 rounded-lg bg-base-200 p-4"> |
||||
{#if loading} |
||||
<div class="h-5 mb-2 skeleton w-40"></div> |
||||
<div class="h-4 skeleton w-100"></div> |
||||
<div class="skeleton mb-2 h-5 w-40"></div> |
||||
<div class="w-100 skeleton h-4"></div> |
||||
{:else} |
||||
<a class="link-primary break-words" href="/repo/{repo_id}">{short_name}</a> |
||||
<p class="text-sm text-muted break-words">{short_descrption}</p> |
||||
<p class="text-muted break-words text-sm">{short_descrption}</p> |
||||
{/if} |
||||
</div> |
||||
|
||||
@ -1,56 +1,56 @@
@@ -1,56 +1,56 @@
|
||||
<script lang="ts" context="module"> |
||||
import type { Meta } from "@storybook/svelte"; |
||||
import ReposSummaryList from "./ReposSummaryList.svelte"; |
||||
import { Story, Template } from "@storybook/addon-svelte-csf"; |
||||
import { RepoSummaryCardArgsVectors as vectors } from "./repo/vectors"; |
||||
import type { Meta } from '@storybook/svelte' |
||||
import ReposSummaryList from './ReposSummaryList.svelte' |
||||
import { Story, Template } from '@storybook/addon-svelte-csf' |
||||
import { RepoSummaryCardArgsVectors as vectors } from './repo/vectors' |
||||
|
||||
export const meta: Meta<ReposSummaryList> = { |
||||
title: "Repo/Summary/List", |
||||
component: ReposSummaryList, |
||||
tags: ["autodocs"], |
||||
}; |
||||
export const meta: Meta<ReposSummaryList> = { |
||||
title: 'Repo/Summary/List', |
||||
component: ReposSummaryList, |
||||
tags: ['autodocs'], |
||||
} |
||||
</script> |
||||
|
||||
<Template let:args> |
||||
<ReposSummaryList {...args} /> |
||||
<ReposSummaryList {...args} /> |
||||
</Template> |
||||
|
||||
<Story |
||||
name="Default" |
||||
args={{ |
||||
title: "Featured Repositories", |
||||
repos: [vectors.Short, vectors.Long, vectors.LongNoSpaces], |
||||
}} |
||||
name="Default" |
||||
args={{ |
||||
title: 'Featured Repositories', |
||||
repos: [vectors.Short, vectors.Long, vectors.LongNoSpaces], |
||||
}} |
||||
/> |
||||
|
||||
<Story |
||||
name="No Title" |
||||
args={{ |
||||
repos: [vectors.Short, vectors.Long], |
||||
}} |
||||
name="No Title" |
||||
args={{ |
||||
repos: [vectors.Short, vectors.Long], |
||||
}} |
||||
/> |
||||
<Story |
||||
name="Empty" |
||||
args={{ |
||||
title: "Latest", |
||||
repos: [], |
||||
}} |
||||
name="Empty" |
||||
args={{ |
||||
title: 'Latest', |
||||
repos: [], |
||||
}} |
||||
/> |
||||
|
||||
<Story |
||||
name="Loading" |
||||
args={{ |
||||
title: "Latest", |
||||
repos: [], |
||||
loading: true, |
||||
}} |
||||
name="Loading" |
||||
args={{ |
||||
title: 'Latest', |
||||
repos: [], |
||||
loading: true, |
||||
}} |
||||
/> |
||||
|
||||
<Story |
||||
name="Partially Loaded" |
||||
args={{ |
||||
title: "Latest", |
||||
repos: [vectors.Short, vectors.Long], |
||||
loading: true, |
||||
}} |
||||
name="Partially Loaded" |
||||
args={{ |
||||
title: 'Latest', |
||||
repos: [vectors.Short, vectors.Long], |
||||
loading: true, |
||||
}} |
||||
/> |
||||
|
||||
@ -1,32 +1,31 @@
@@ -1,32 +1,31 @@
|
||||
<script lang="ts"> |
||||
import ParsedContent from "./content/ParsedContent.svelte"; |
||||
export let sendReply: (content: string) => void = (content: string) => {}; |
||||
export let submitting = false; |
||||
let submit = () => { |
||||
sendReply(content); |
||||
}; |
||||
let content = ""; |
||||
export let sendReply: (content: string) => void = () => {} |
||||
export let submitting = false |
||||
let submit = () => { |
||||
sendReply(content) |
||||
} |
||||
let content = '' |
||||
</script> |
||||
|
||||
<div class=""> |
||||
<textarea |
||||
disabled={submitting} |
||||
bind:value={content} |
||||
class="textarea textarea-primary w-full" |
||||
placeholder="reply..." |
||||
></textarea> |
||||
<div class="flex"> |
||||
<div class="flex-auto"></div> |
||||
<button |
||||
on:click={submit} |
||||
disabled={submitting} |
||||
class="btn btn-primary align-bottom mt-2 btn-sm align-right" |
||||
> |
||||
{#if submitting} |
||||
Sending |
||||
{:else} |
||||
Send |
||||
{/if} |
||||
</button> |
||||
</div> |
||||
<textarea |
||||
disabled={submitting} |
||||
bind:value={content} |
||||
class="textarea textarea-primary w-full" |
||||
placeholder="reply..." |
||||
></textarea> |
||||
<div class="flex"> |
||||
<div class="flex-auto"></div> |
||||
<button |
||||
on:click={submit} |
||||
disabled={submitting} |
||||
class="align-right btn btn-primary btn-sm mt-2 align-bottom" |
||||
> |
||||
{#if submitting} |
||||
Sending |
||||
{:else} |
||||
Send |
||||
{/if} |
||||
</button> |
||||
</div> |
||||
</div> |
||||
|
||||
@ -1,68 +1,67 @@
@@ -1,68 +1,67 @@
|
||||
<script lang="ts"> |
||||
import dayjs from "dayjs"; |
||||
import UserHeader from "../users/UserHeader.svelte"; |
||||
import type { User } from "../users/type"; |
||||
import { defaults as user_defaults } from "../users/type"; |
||||
import Compose from "$lib/wrappers/Compose.svelte"; |
||||
import { logged_in_user } from "$lib/stores/users"; |
||||
import dayjs from 'dayjs' |
||||
import UserHeader from '../users/UserHeader.svelte' |
||||
import type { User } from '../users/type' |
||||
import { defaults as user_defaults } from '../users/type' |
||||
import Compose from '$lib/wrappers/Compose.svelte' |
||||
import { logged_in_user } from '$lib/stores/users' |
||||
|
||||
export let author: User = { ...user_defaults }; |
||||
export let created_at: number | undefined; |
||||
export let event_id = ""; |
||||
export let logged_in = $logged_in_user; |
||||
let show_compose = false; |
||||
export let author: User = { ...user_defaults } |
||||
export let created_at: number | undefined |
||||
export let event_id = '' |
||||
export let logged_in = $logged_in_user |
||||
let show_compose = false |
||||
|
||||
let created_at_ago = ""; |
||||
$: created_at_ago = created_at ? dayjs(created_at * 1000).fromNow() : ""; |
||||
let created_at_ago = '' |
||||
$: created_at_ago = created_at ? dayjs(created_at * 1000).fromNow() : '' |
||||
</script> |
||||
|
||||
<div class="pl-3 p-3 border-b border-base-300"> |
||||
<div class="flex"> |
||||
<div class="flex-auto"> |
||||
<UserHeader user={author} /> |
||||
</div> |
||||
{#if !show_compose} |
||||
<div class="mt-1 mb-1 aling-middle"> |
||||
<span class="text-xs mb-1">{created_at_ago}</span> |
||||
{#if logged_in} |
||||
<button |
||||
on:click={() => { |
||||
show_compose = true; |
||||
}} |
||||
class="btn btn-xs" |
||||
><svg |
||||
xmlns="http://www.w3.org/2000/svg" |
||||
width="16" |
||||
height="16" |
||||
viewBox="0 0 16 16" |
||||
><path |
||||
fill="currentColor" |
||||
d="M6.78 1.97a.75.75 0 0 1 0 1.06L3.81 6h6.44A4.75 4.75 0 0 1 15 10.75v2.5a.75.75 0 0 1-1.5 0v-2.5a3.25 3.25 0 0 0-3.25-3.25H3.81l2.97 2.97a.749.749 0 0 1-.326 1.275a.749.749 0 0 1-.734-.215L1.47 7.28a.75.75 0 0 1 0-1.06l4.25-4.25a.75.75 0 0 1 1.06 0" |
||||
/></svg |
||||
></button |
||||
> |
||||
{/if} |
||||
</div> |
||||
{/if} |
||||
<div class="border-b border-base-300 p-3 pl-3"> |
||||
<div class="flex"> |
||||
<div class="flex-auto"> |
||||
<UserHeader user={author} /> |
||||
</div> |
||||
<div class="ml-11"> |
||||
<slot /> |
||||
{#if show_compose} |
||||
<div class=""> |
||||
<div class="flex"> |
||||
<div class="flex-auto"></div> |
||||
<button |
||||
on:click={() => { |
||||
show_compose = false; |
||||
}} |
||||
class="btn btn-sm btn-circle btn-ghost right-2 top-2" |
||||
>✕</button |
||||
> |
||||
</div> |
||||
<div class=""> |
||||
<Compose reply_to_event_id={event_id} /> |
||||
</div> |
||||
</div> |
||||
{#if !show_compose} |
||||
<div class="aling-middle mb-1 mt-1"> |
||||
<span class="mb-1 text-xs">{created_at_ago}</span> |
||||
{#if logged_in} |
||||
<button |
||||
on:click={() => { |
||||
show_compose = true |
||||
}} |
||||
class="btn btn-xs" |
||||
><svg |
||||
xmlns="http://www.w3.org/2000/svg" |
||||
width="16" |
||||
height="16" |
||||
viewBox="0 0 16 16" |
||||
><path |
||||
fill="currentColor" |
||||
d="M6.78 1.97a.75.75 0 0 1 0 1.06L3.81 6h6.44A4.75 4.75 0 0 1 15 10.75v2.5a.75.75 0 0 1-1.5 0v-2.5a3.25 3.25 0 0 0-3.25-3.25H3.81l2.97 2.97a.749.749 0 0 1-.326 1.275a.749.749 0 0 1-.734-.215L1.47 7.28a.75.75 0 0 1 0-1.06l4.25-4.25a.75.75 0 0 1 1.06 0" |
||||
/></svg |
||||
></button |
||||
> |
||||
{/if} |
||||
</div> |
||||
</div> |
||||
{/if} |
||||
</div> |
||||
<div class="ml-11"> |
||||
<slot /> |
||||
{#if show_compose} |
||||
<div class=""> |
||||
<div class="flex"> |
||||
<div class="flex-auto"></div> |
||||
<button |
||||
on:click={() => { |
||||
show_compose = false |
||||
}} |
||||
class="btn btn-circle btn-ghost btn-sm right-2 top-2">✕</button |
||||
> |
||||
</div> |
||||
<div class=""> |
||||
<Compose reply_to_event_id={event_id} /> |
||||
</div> |
||||
</div> |
||||
{/if} |
||||
</div> |
||||
</div> |
||||
|
||||
@ -1,3 +1,3 @@
@@ -1,3 +1,3 @@
|
||||
<div class="border-l border-blue-500 pl-1"> |
||||
<slot /> |
||||
<slot /> |
||||
</div> |
||||
|
||||
@ -1,23 +1,23 @@
@@ -1,23 +1,23 @@
|
||||
<script lang="ts"> |
||||
import type { NDKTag } from "@nostr-dev-kit/ndk"; |
||||
import Status from "$lib/components/prs/Status.svelte"; |
||||
import type { NDKTag } from '@nostr-dev-kit/ndk' |
||||
import Status from '$lib/components/prs/Status.svelte' |
||||
|
||||
export let tags: NDKTag[] = []; |
||||
export let tags: NDKTag[] = [] |
||||
|
||||
function extractTagContent(name: string): string | undefined { |
||||
let tag = tags.find((tag) => tag[0] === name); |
||||
return tag ? tag[1] : undefined; |
||||
} |
||||
let status: string | undefined; |
||||
$: { |
||||
status = extractTagContent("l"); |
||||
} |
||||
function extractTagContent(name: string): string | undefined { |
||||
let tag = tags.find((tag) => tag[0] === name) |
||||
return tag ? tag[1] : undefined |
||||
} |
||||
let status: string | undefined |
||||
$: { |
||||
status = extractTagContent('l') |
||||
} |
||||
</script> |
||||
|
||||
<div class=""> |
||||
{#if status} |
||||
set status to <Status {status} /> |
||||
{:else} |
||||
set status incorrectly |
||||
{/if} |
||||
{#if status} |
||||
set status to <Status {status} /> |
||||
{:else} |
||||
set status incorrectly |
||||
{/if} |
||||
</div> |
||||
|
||||
@ -1,59 +1,54 @@
@@ -1,59 +1,54 @@
|
||||
<script lang="ts"> |
||||
import type { NDKTag } from "@nostr-dev-kit/ndk"; |
||||
import parseDiff from "parse-diff"; |
||||
import ParsedContent from "./ParsedContent.svelte"; |
||||
import type { NDKTag } from '@nostr-dev-kit/ndk' |
||||
import parseDiff from 'parse-diff' |
||||
import ParsedContent from './ParsedContent.svelte' |
||||
|
||||
export let content: string = ""; |
||||
export let tags: NDKTag[] = []; |
||||
export let lite: boolean = true; |
||||
export let content: string = '' |
||||
export let tags: NDKTag[] = [] |
||||
|
||||
let commit_id = extractTagContent("commit") || "[unknown commit_id]"; |
||||
let commit_message = extractTagContent("description") || "[untitled]"; |
||||
let commit_id = extractTagContent('commit') || '[unknown commit_id]' |
||||
let commit_message = extractTagContent('description') || '[untitled]' |
||||
|
||||
let files = parseDiff(content); |
||||
function extractTagContent(name: string): string | undefined { |
||||
let tag = tags.find((tag) => tag[0] === name); |
||||
return tag ? tag[1] : undefined; |
||||
} |
||||
let files = parseDiff(content) |
||||
function extractTagContent(name: string): string | undefined { |
||||
let tag = tags.find((tag) => tag[0] === name) |
||||
return tag ? tag[1] : undefined |
||||
} |
||||
</script> |
||||
|
||||
<div class=""> |
||||
<div class="bg-base-300 rounded-t p-1 flex"> |
||||
<article class="ml-2 prose font-mono flex-grow"> |
||||
<ParsedContent content={commit_message} /> |
||||
</article> |
||||
<div class="text-xs text-neutral p-1 flex-none align-middle"> |
||||
commit |
||||
</div> |
||||
</div> |
||||
<div class="flex rounded-t bg-base-300 p-1"> |
||||
<article class="prose ml-2 flex-grow font-mono"> |
||||
<ParsedContent content={commit_message} /> |
||||
</article> |
||||
<div class="flex-none p-1 align-middle text-xs text-neutral">commit</div> |
||||
</div> |
||||
|
||||
<div class="bg-base-200 p-1 rounded-b"> |
||||
<table class="table table-xs table-zebra"> |
||||
<tr> |
||||
<td class="text-xs">Changes: </td> |
||||
<td class="text-right"> |
||||
<span class="text-xs font-mono" |
||||
>{commit_id.substring(0, 8)}</span |
||||
> |
||||
</td> |
||||
</tr> |
||||
{#each files as file} |
||||
<tr> |
||||
<td> |
||||
<span |
||||
class:text-success={file.new} |
||||
class:text-error={file.deleted} |
||||
class="text-success" |
||||
> |
||||
{file.to || file.from} |
||||
</span> |
||||
</td> |
||||
<td class="text-right"> |
||||
<span class="text-success">+{file.additions}</span> |
||||
<span class="text-error">- {file.deletions}</span> |
||||
</td> |
||||
</tr> |
||||
{/each} |
||||
</table> |
||||
</div> |
||||
<div class="rounded-b bg-base-200 p-1"> |
||||
<table class="table table-zebra table-xs"> |
||||
<tr> |
||||
<td class="text-xs">Changes: </td> |
||||
<td class="text-right"> |
||||
<span class="font-mono text-xs">{commit_id.substring(0, 8)}</span> |
||||
</td> |
||||
</tr> |
||||
{#each files as file} |
||||
<tr> |
||||
<td> |
||||
<span |
||||
class:text-success={file.new} |
||||
class:text-error={file.deleted} |
||||
class="text-success" |
||||
> |
||||
{file.to || file.from} |
||||
</span> |
||||
</td> |
||||
<td class="text-right"> |
||||
<span class="text-success">+{file.additions}</span> |
||||
<span class="text-error">- {file.deletions}</span> |
||||
</td> |
||||
</tr> |
||||
{/each} |
||||
</table> |
||||
</div> |
||||
</div> |
||||
|
||||
@ -1,28 +1,28 @@
@@ -1,28 +1,28 @@
|
||||
<script lang="ts"> |
||||
import type { NDKTag } from "@nostr-dev-kit/ndk"; |
||||
import { |
||||
isParsedNewLine, |
||||
isParsedText, |
||||
parseContent, |
||||
type ParsedPart, |
||||
} from "./utils"; |
||||
export let content: string = ""; |
||||
export let tags: NDKTag[] = []; |
||||
import type { NDKTag } from '@nostr-dev-kit/ndk' |
||||
import { |
||||
isParsedNewLine, |
||||
isParsedText, |
||||
parseContent, |
||||
type ParsedPart, |
||||
} from './utils' |
||||
export let content: string = '' |
||||
export let tags: NDKTag[] = [] |
||||
|
||||
let fullContent: ParsedPart[] = []; |
||||
let fullContent: ParsedPart[] = [] |
||||
|
||||
$: fullContent = parseContent({ content, tags }); |
||||
$: fullContent = parseContent({ content, tags }) |
||||
</script> |
||||
|
||||
<div> |
||||
{#each fullContent as part, i} |
||||
{#if isParsedNewLine(part)} |
||||
{#if part.value.length > 1} |
||||
<br /> |
||||
{/if} |
||||
<br /> |
||||
{:else if isParsedText(part)} |
||||
{part.value} |
||||
{/if} |
||||
{/each} |
||||
{#each fullContent as part} |
||||
{#if isParsedNewLine(part)} |
||||
{#if part.value.length > 1} |
||||
<br /> |
||||
{/if} |
||||
<br /> |
||||
{:else if isParsedText(part)} |
||||
{part.value} |
||||
{/if} |
||||
{/each} |
||||
</div> |
||||
|
||||
@ -1,96 +1,95 @@
@@ -1,96 +1,95 @@
|
||||
import type { NDKTag } from "@nostr-dev-kit/ndk"; |
||||
import { nip19 } from "nostr-tools"; |
||||
import { identity, last, pluck } from "ramda"; |
||||
|
||||
export const TOPIC = "topic"; |
||||
export const LINK = "link"; |
||||
export const LINKCOLLECTION = "link[]"; |
||||
export const HTML = "html"; |
||||
export const INVOICE = "invoice"; |
||||
export const NOSTR_NOTE = "nostr:note"; |
||||
export const NOSTR_NEVENT = "nostr:nevent"; |
||||
export const NOSTR_NPUB = "nostr:npub"; |
||||
export const NOSTR_NPROFILE = "nostr:nprofile"; |
||||
export const NOSTR_NADDR = "nostr:naddr"; |
||||
import type { NDKTag } from '@nostr-dev-kit/ndk' |
||||
import { last } from 'ramda' |
||||
|
||||
export const TOPIC = 'topic' |
||||
export const LINK = 'link' |
||||
export const LINKCOLLECTION = 'link[]' |
||||
export const HTML = 'html' |
||||
export const INVOICE = 'invoice' |
||||
export const NOSTR_NOTE = 'nostr:note' |
||||
export const NOSTR_NEVENT = 'nostr:nevent' |
||||
export const NOSTR_NPUB = 'nostr:npub' |
||||
export const NOSTR_NPROFILE = 'nostr:nprofile' |
||||
export const NOSTR_NADDR = 'nostr:naddr' |
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const first = (list: any) => (list ? list[0] : undefined); |
||||
const first = (list: any) => (list ? list[0] : undefined) |
||||
|
||||
export const fromNostrURI = (s: string) => s.replace(/^[\w+]+:\/?\/?/, ""); |
||||
export const fromNostrURI = (s: string) => s.replace(/^[\w+]+:\/?\/?/, '') |
||||
|
||||
export const urlIsMedia = (url: string) => |
||||
!url.match(/\.(apk|docx|xlsx|csv|dmg)/) && last(url.split("://"))?.includes("/"); |
||||
!url.match(/\.(apk|docx|xlsx|csv|dmg)/) && |
||||
last(url.split('://'))?.includes('/') |
||||
|
||||
export type ContentArgs = { |
||||
content: string; |
||||
tags?: Array<NDKTag>; |
||||
}; |
||||
content: string |
||||
tags?: Array<NDKTag> |
||||
} |
||||
|
||||
export type ParsedPart = ParsedNewLine | ParsedText; |
||||
export type ParsedPart = ParsedNewLine | ParsedText |
||||
|
||||
export const NEWLINE = "newline"; |
||||
export const NEWLINE = 'newline' |
||||
|
||||
export type ParsedNewLine = { |
||||
type: "newline", |
||||
value: string, |
||||
}; |
||||
type: 'newline' |
||||
value: string |
||||
} |
||||
|
||||
export const isParsedNewLine = (part: ParsedPart): part is ParsedNewLine => { |
||||
return part.type == "newline" |
||||
}; |
||||
return part.type == 'newline' |
||||
} |
||||
|
||||
export const TEXT = "text"; |
||||
export const TEXT = 'text' |
||||
|
||||
export type ParsedText = { |
||||
type: "text", |
||||
value: string, |
||||
}; |
||||
type: 'text' |
||||
value: string |
||||
} |
||||
|
||||
export const isParsedText = (part: ParsedPart): part is ParsedText => { |
||||
return part.type == "text" |
||||
}; |
||||
|
||||
export const parseContent = ({ content, tags = [] }: ContentArgs): ParsedPart[] => { |
||||
const result: ParsedPart[] = []; |
||||
let text = content.trim(); |
||||
let buffer = ""; |
||||
|
||||
const parseNewline = () => { |
||||
const newline = first(text.match(/^\n+/)); |
||||
|
||||
if (newline) { |
||||
return [NEWLINE, newline, newline]; |
||||
} |
||||
}; |
||||
|
||||
while (text) { |
||||
// The order that this runs matters
|
||||
const part = |
||||
parseNewline(); |
||||
|
||||
if (part) { |
||||
if (buffer) { |
||||
result.push({ type: "text", value: buffer }); |
||||
buffer = ""; |
||||
} |
||||
|
||||
const [type, raw, value] = part; |
||||
|
||||
result.push({ type, value }); |
||||
text = text.slice(raw.length); |
||||
} else { |
||||
// Instead of going character by character and re-running all the above regular expressions
|
||||
// a million times, try to match the next word and add it to the buffer
|
||||
const match = first(text.match(/^[\w\d]+ ?/i)) || text[0]; |
||||
|
||||
buffer += match; |
||||
text = text.slice(match.length); |
||||
} |
||||
return part.type == 'text' |
||||
} |
||||
|
||||
export const parseContent = ({ content }: ContentArgs): ParsedPart[] => { |
||||
const result: ParsedPart[] = [] |
||||
let text = content.trim() |
||||
let buffer = '' |
||||
|
||||
const parseNewline = () => { |
||||
const newline = first(text.match(/^\n+/)) |
||||
|
||||
if (newline) { |
||||
return [NEWLINE, newline, newline] |
||||
} |
||||
} |
||||
|
||||
while (text) { |
||||
// The order that this runs matters
|
||||
const part = parseNewline() |
||||
|
||||
if (buffer) { |
||||
result.push({ type: TEXT, value: buffer }); |
||||
if (part) { |
||||
if (buffer) { |
||||
result.push({ type: 'text', value: buffer }) |
||||
buffer = '' |
||||
} |
||||
|
||||
const [type, raw, value] = part |
||||
|
||||
result.push({ type, value }) |
||||
text = text.slice(raw.length) |
||||
} else { |
||||
// Instead of going character by character and re-running all the above regular expressions
|
||||
// a million times, try to match the next word and add it to the buffer
|
||||
const match = first(text.match(/^[\w\d]+ ?/i)) || text[0] |
||||
|
||||
buffer += match |
||||
text = text.slice(match.length) |
||||
} |
||||
} |
||||
|
||||
if (buffer) { |
||||
result.push({ type: TEXT, value: buffer }) |
||||
} |
||||
|
||||
return result; |
||||
}; |
||||
return result |
||||
} |
||||
|
||||
@ -1,12 +1,6 @@
@@ -1,12 +1,6 @@
|
||||
import { defaults as user_defaults } from "../users/type"; |
||||
import type { User } from "../users/type"; |
||||
import type { User } from '../users/type' |
||||
|
||||
export interface Event { |
||||
author: User; |
||||
content: any; |
||||
author: User |
||||
content: unknown |
||||
} |
||||
|
||||
let defaults: Event = { |
||||
author: { ...user_defaults }, |
||||
content: [], |
||||
} |
||||
@ -1,47 +1,47 @@
@@ -1,47 +1,47 @@
|
||||
<script lang="ts"> |
||||
import { full_defaults } from "./type"; |
||||
import UserHeader from "../users/UserHeader.svelte"; |
||||
import StatusSelector from "./StatusSelector.svelte"; |
||||
import { full_defaults } from './type' |
||||
import UserHeader from '../users/UserHeader.svelte' |
||||
import StatusSelector from './StatusSelector.svelte' |
||||
|
||||
export let { summary, labels, loading } = { ...full_defaults }; |
||||
export let { summary, labels, loading } = { ...full_defaults } |
||||
</script> |
||||
|
||||
<div class="max-w-md"> |
||||
<div> |
||||
{#if loading} |
||||
<div class="h-5 my-3 skeleton w-20"></div> |
||||
<div class="badge skeleton my-2 w-60 block"></div> |
||||
<div class="badge skeleton my-2 w-40 block"></div> |
||||
{:else} |
||||
<h4>Author</h4> |
||||
<UserHeader user={summary.author} /> |
||||
{/if} |
||||
</div> |
||||
<div> |
||||
{#if loading} |
||||
<div class="skeleton my-3 h-5 w-20"></div> |
||||
<div class="badge skeleton my-2 block w-60"></div> |
||||
<div class="badge skeleton my-2 block w-40"></div> |
||||
{:else} |
||||
<h4>Author</h4> |
||||
<UserHeader user={summary.author} /> |
||||
{/if} |
||||
</div> |
||||
|
||||
<div> |
||||
{#if loading} |
||||
<div class="h-5 my-3 skeleton w-20"></div> |
||||
<div class="badge skeleton my-2 w-60 block"></div> |
||||
<div class="badge skeleton my-2 w-40 block"></div> |
||||
{:else} |
||||
<h4>Status</h4> |
||||
<StatusSelector |
||||
status={summary.status} |
||||
repo_id={summary.repo_id} |
||||
pr_id={summary.id} |
||||
/> |
||||
{/if} |
||||
</div> |
||||
<div> |
||||
{#if loading} |
||||
<div class="skeleton my-3 h-5 w-20"></div> |
||||
<div class="badge skeleton my-2 block w-60"></div> |
||||
<div class="badge skeleton my-2 block w-40"></div> |
||||
{:else} |
||||
<h4>Status</h4> |
||||
<StatusSelector |
||||
status={summary.status} |
||||
repo_id={summary.repo_id} |
||||
pr_id={summary.id} |
||||
/> |
||||
{/if} |
||||
</div> |
||||
|
||||
<div> |
||||
{#if loading} |
||||
<div class="badge skeleton w-20"></div> |
||||
<div class="badge skeleton w-20"></div> |
||||
{:else} |
||||
<h4>Labels</h4> |
||||
{#each labels as label} |
||||
<div class="badge badge-secondary mr-2">{label}</div> |
||||
{/each} |
||||
{/if} |
||||
</div> |
||||
<div> |
||||
{#if loading} |
||||
<div class="badge skeleton w-20"></div> |
||||
<div class="badge skeleton w-20"></div> |
||||
{:else} |
||||
<h4>Labels</h4> |
||||
{#each labels as label} |
||||
<div class="badge badge-secondary mr-2">{label}</div> |
||||
{/each} |
||||
{/if} |
||||
</div> |
||||
</div> |
||||
|
||||
@ -1,56 +1,56 @@
@@ -1,56 +1,56 @@
|
||||
<script lang="ts" context="module"> |
||||
import type { Meta } from "@storybook/svelte"; |
||||
import PRsList from "./PRsList.svelte"; |
||||
import { Story, Template } from "@storybook/addon-svelte-csf"; |
||||
import { PRsListItemArgsVectors as vectors } from "./vectors"; |
||||
import type { Meta } from '@storybook/svelte' |
||||
import PRsList from './PRsList.svelte' |
||||
import { Story, Template } from '@storybook/addon-svelte-csf' |
||||
import { PRsListItemArgsVectors as vectors } from './vectors' |
||||
|
||||
export const meta: Meta<PRsList> = { |
||||
title: "PRs/List/List", |
||||
component: PRsList, |
||||
tags: ["autodocs"], |
||||
}; |
||||
export const meta: Meta<PRsList> = { |
||||
title: 'PRs/List/List', |
||||
component: PRsList, |
||||
tags: ['autodocs'], |
||||
} |
||||
</script> |
||||
|
||||
<Template let:args> |
||||
<PRsList {...args} /> |
||||
<PRsList {...args} /> |
||||
</Template> |
||||
|
||||
<Story |
||||
name="Default" |
||||
args={{ |
||||
title: "Open PRs", |
||||
prs: [vectors.Short, vectors.Long, vectors.LongNoSpaces], |
||||
}} |
||||
name="Default" |
||||
args={{ |
||||
title: 'Open PRs', |
||||
prs: [vectors.Short, vectors.Long, vectors.LongNoSpaces], |
||||
}} |
||||
/> |
||||
|
||||
<Story |
||||
name="No Title" |
||||
args={{ |
||||
prs: [vectors.Short, vectors.Long], |
||||
}} |
||||
name="No Title" |
||||
args={{ |
||||
prs: [vectors.Short, vectors.Long], |
||||
}} |
||||
/> |
||||
<Story |
||||
name="Empty" |
||||
args={{ |
||||
title: "Open PRs", |
||||
prs: [], |
||||
}} |
||||
name="Empty" |
||||
args={{ |
||||
title: 'Open PRs', |
||||
prs: [], |
||||
}} |
||||
/> |
||||
|
||||
<Story |
||||
name="Loading" |
||||
args={{ |
||||
title: "Open PRs", |
||||
prs: [], |
||||
loading: true, |
||||
}} |
||||
name="Loading" |
||||
args={{ |
||||
title: 'Open PRs', |
||||
prs: [], |
||||
loading: true, |
||||
}} |
||||
/> |
||||
|
||||
<Story |
||||
name="Partially Loaded" |
||||
args={{ |
||||
title: "Open PRs", |
||||
prs: [vectors.Short, vectors.Long], |
||||
loading: true, |
||||
}} |
||||
name="Partially Loaded" |
||||
args={{ |
||||
title: 'Open PRs', |
||||
prs: [vectors.Short, vectors.Long], |
||||
loading: true, |
||||
}} |
||||
/> |
||||
|
||||
@ -1,34 +1,31 @@
@@ -1,34 +1,31 @@
|
||||
<script lang="ts"> |
||||
import { fade } from "svelte/transition"; |
||||
import { onMount } from "svelte"; |
||||
import PRsListItem from '$lib/components/prs/PRsListItem.svelte' |
||||
import type { PRSummary } from './type' |
||||
|
||||
import PRsListItem from "$lib/components/prs/PRsListItem.svelte"; |
||||
import type { PRSummary } from "./type"; |
||||
|
||||
export let title: string = ""; |
||||
export let prs: PRSummary[] = []; |
||||
export let loading: boolean = false; |
||||
export let title: string = '' |
||||
export let prs: PRSummary[] = [] |
||||
export let loading: boolean = false |
||||
</script> |
||||
|
||||
<div class=""> |
||||
{#if title.length > 0} |
||||
<div class="prose"> |
||||
<h4>{title}</h4> |
||||
</div> |
||||
{/if} |
||||
{#if prs.length == 0 && !loading} |
||||
<p class="prose">None</p> |
||||
{#if title.length > 0} |
||||
<div class="prose"> |
||||
<h4>{title}</h4> |
||||
</div> |
||||
{/if} |
||||
{#if prs.length == 0 && !loading} |
||||
<p class="prose">None</p> |
||||
{/if} |
||||
<ul class=" divide-y divide-neutral-600"> |
||||
{#each prs as pr} |
||||
<PRsListItem {...pr} /> |
||||
{/each} |
||||
{#if loading} |
||||
<PRsListItem loading={true} /> |
||||
{#if prs.length == 0} |
||||
<PRsListItem loading={true} /> |
||||
<PRsListItem loading={true} /> |
||||
{/if} |
||||
{/if} |
||||
<ul class=" divide-y divide-neutral-600"> |
||||
{#each prs as pr} |
||||
<PRsListItem {...pr} /> |
||||
{/each} |
||||
{#if loading} |
||||
<PRsListItem loading={true} /> |
||||
{#if prs.length == 0} |
||||
<PRsListItem loading={true} /> |
||||
<PRsListItem loading={true} /> |
||||
{/if} |
||||
{/if} |
||||
</ul> |
||||
</ul> |
||||
</div> |
||||
|
||||
@ -1,27 +1,27 @@
@@ -1,27 +1,27 @@
|
||||
<script lang="ts" context="module"> |
||||
import type { Meta } from "@storybook/svelte"; |
||||
import Status from "./Status.svelte"; |
||||
import { Story, Template } from "@storybook/addon-svelte-csf"; |
||||
|
||||
export const meta: Meta<Status> = { |
||||
title: "PRs/Status", |
||||
component: Status, |
||||
tags: ["autodocs"], |
||||
}; |
||||
import type { Meta } from '@storybook/svelte' |
||||
import Status from './Status.svelte' |
||||
import { Story, Template } from '@storybook/addon-svelte-csf' |
||||
|
||||
export const meta: Meta<Status> = { |
||||
title: 'PRs/Status', |
||||
component: Status, |
||||
tags: ['autodocs'], |
||||
} |
||||
</script> |
||||
|
||||
<Template let:args> |
||||
<Status {...args} /> |
||||
<Status {...args} /> |
||||
</Template> |
||||
|
||||
<Story name="Open" args={{ status: "Open"}} /> |
||||
<Story name="Open" args={{ status: 'Open' }} /> |
||||
|
||||
<Story name="Closed" args={{ status: "Closed"}} /> |
||||
<Story name="Closed" args={{ status: 'Closed' }} /> |
||||
|
||||
<Story name="Draft" args={{ status: "Draft"}} /> |
||||
<Story name="Draft" args={{ status: 'Draft' }} /> |
||||
|
||||
<Story name="Merged" args={{ status: "Merged"}} /> |
||||
<Story name="Merged" args={{ status: 'Merged' }} /> |
||||
|
||||
<Story name="Open Edit Mode" args={{ edit_mode: true, status: "Open"}} /> |
||||
<Story name="Open Edit Mode" args={{ edit_mode: true, status: 'Open' }} /> |
||||
|
||||
<Story name="Loading" args={{ status: undefined}} /> |
||||
<Story name="Loading" args={{ status: undefined }} /> |
||||
|
||||
@ -1,71 +1,71 @@
@@ -1,71 +1,71 @@
|
||||
<script lang="ts"> |
||||
import { pr_icon_path } from "./icons"; |
||||
import { pr_icon_path } from './icons' |
||||
|
||||
export let status: string | undefined = undefined; |
||||
export let edit_mode = false; |
||||
export let status: string | undefined = undefined |
||||
export let edit_mode = false |
||||
</script> |
||||
|
||||
{#if !status} |
||||
<div class="skeleton w-24 h-8 rounded-md inline-block align-middle"></div> |
||||
<div class="skeleton inline-block h-8 w-24 rounded-md align-middle"></div> |
||||
{:else} |
||||
<div |
||||
tabIndex={0} |
||||
role="button" |
||||
class:btn-success={status && status === "Open"} |
||||
class:btn-primary={status && status === "Merged"} |
||||
class:btn-neutral={!status || status === "Draft" || status === "Closed"} |
||||
class:cursor-default={!edit_mode} |
||||
class="btn btn-success btn-sm align-middle" |
||||
> |
||||
{#if status === "Open"} |
||||
<!-- http://icon-sets.iconify.design/octicon/git-pull-request-16/ --> |
||||
<svg |
||||
xmlns="http://www.w3.org/2000/svg" |
||||
viewBox="0 0 18 18" |
||||
class="h-5 w-5 pt-1 flex-none fill-success-content" |
||||
><path d={pr_icon_path.open} /> |
||||
</svg> |
||||
Open |
||||
{:else if status === "Merged"} |
||||
<!-- https://icon-sets.iconify.design/octicon/git-merge-16/ --> |
||||
<svg |
||||
xmlns="http://www.w3.org/2000/svg" |
||||
viewBox="0 0 16 16" |
||||
class="h-5 w-5 pt-1 flex-none fill-primary-content" |
||||
><path d={pr_icon_path.merge} /></svg |
||||
> |
||||
Merged |
||||
{:else if status === "Closed"} |
||||
<!-- https://icon-sets.iconify.design/octicon/git-pull-request-closed-16/ --> |
||||
<svg |
||||
xmlns="http://www.w3.org/2000/svg" |
||||
viewBox="0 0 16 16" |
||||
class="h-5 w-5 pt-1 flex-none fill-neutral-content" |
||||
><path d={pr_icon_path.close} /></svg |
||||
> |
||||
Closed |
||||
{:else if status === "Draft"} |
||||
<!-- https://icon-sets.iconify.design/octicon/git-pull-request-draft-16// --> |
||||
<svg |
||||
xmlns="http://www.w3.org/2000/svg" |
||||
viewBox="0 0 16 16" |
||||
class="h-5 w-5 pt-1 flex-none fill-neutral-content" |
||||
><path d={pr_icon_path.draft} /></svg |
||||
> |
||||
Draft |
||||
{:else} |
||||
{status} |
||||
{/if} |
||||
{#if edit_mode} |
||||
<svg |
||||
xmlns="http://www.w3.org/2000/svg" |
||||
viewBox="0 0 24 24" |
||||
class="h-5 w-5 flex-none fill-success-content" |
||||
><path |
||||
fill="currentColor" |
||||
d="M11.646 15.146L5.854 9.354a.5.5 0 0 1 .353-.854h11.586a.5.5 0 0 1 .353.854l-5.793 5.792a.5.5 0 0 1-.707 0" |
||||
/></svg |
||||
> |
||||
{/if} |
||||
</div> |
||||
<div |
||||
tabIndex={0} |
||||
role="button" |
||||
class:btn-success={status && status === 'Open'} |
||||
class:btn-primary={status && status === 'Merged'} |
||||
class:btn-neutral={!status || status === 'Draft' || status === 'Closed'} |
||||
class:cursor-default={!edit_mode} |
||||
class="btn btn-success btn-sm align-middle" |
||||
> |
||||
{#if status === 'Open'} |
||||
<!-- http://icon-sets.iconify.design/octicon/git-pull-request-16/ --> |
||||
<svg |
||||
xmlns="http://www.w3.org/2000/svg" |
||||
viewBox="0 0 18 18" |
||||
class="h-5 w-5 flex-none fill-success-content pt-1" |
||||
><path d={pr_icon_path.open} /> |
||||
</svg> |
||||
Open |
||||
{:else if status === 'Merged'} |
||||
<!-- https://icon-sets.iconify.design/octicon/git-merge-16/ --> |
||||
<svg |
||||
xmlns="http://www.w3.org/2000/svg" |
||||
viewBox="0 0 16 16" |
||||
class="h-5 w-5 flex-none fill-primary-content pt-1" |
||||
><path d={pr_icon_path.merge} /></svg |
||||
> |
||||
Merged |
||||
{:else if status === 'Closed'} |
||||
<!-- https://icon-sets.iconify.design/octicon/git-pull-request-closed-16/ --> |
||||
<svg |
||||
xmlns="http://www.w3.org/2000/svg" |
||||
viewBox="0 0 16 16" |
||||
class="h-5 w-5 flex-none fill-neutral-content pt-1" |
||||
><path d={pr_icon_path.close} /></svg |
||||
> |
||||
Closed |
||||
{:else if status === 'Draft'} |
||||
<!-- https://icon-sets.iconify.design/octicon/git-pull-request-draft-16// --> |
||||
<svg |
||||
xmlns="http://www.w3.org/2000/svg" |
||||
viewBox="0 0 16 16" |
||||
class="h-5 w-5 flex-none fill-neutral-content pt-1" |
||||
><path d={pr_icon_path.draft} /></svg |
||||
> |
||||
Draft |
||||
{:else} |
||||
{status} |
||||
{/if} |
||||
{#if edit_mode} |
||||
<svg |
||||
xmlns="http://www.w3.org/2000/svg" |
||||
viewBox="0 0 24 24" |
||||
class="h-5 w-5 flex-none fill-success-content" |
||||
><path |
||||
fill="currentColor" |
||||
d="M11.646 15.146L5.854 9.354a.5.5 0 0 1 .353-.854h11.586a.5.5 0 0 1 .353.854l-5.793 5.792a.5.5 0 0 1-.707 0" |
||||
/></svg |
||||
> |
||||
{/if} |
||||
</div> |
||||
{/if} |
||||
|
||||
@ -1,126 +1,120 @@
@@ -1,126 +1,120 @@
|
||||
<script lang="ts"> |
||||
import { ndk } from "$lib/stores/ndk"; |
||||
import { NDKEvent, NDKRelaySet, type NDKTag } from "@nostr-dev-kit/ndk"; |
||||
import type { PRStatus } from "./type"; |
||||
import { selected_pr_full, selected_pr_replies } from "$lib/stores/PR"; |
||||
import { pr_status_kind } from "$lib/kinds"; |
||||
import { getUserRelays, logged_in_user } from "$lib/stores/users"; |
||||
import { selected_repo } from "$lib/stores/repo"; |
||||
import Status from "$lib/components/prs/Status.svelte"; |
||||
import { ndk } from '$lib/stores/ndk' |
||||
import { NDKEvent, NDKRelaySet } from '@nostr-dev-kit/ndk' |
||||
import type { PRStatus } from './type' |
||||
import { selected_pr_full } from '$lib/stores/PR' |
||||
import { pr_status_kind } from '$lib/kinds' |
||||
import { getUserRelays, logged_in_user } from '$lib/stores/users' |
||||
import { selected_repo } from '$lib/stores/repo' |
||||
import Status from '$lib/components/prs/Status.svelte' |
||||
|
||||
export let status: PRStatus | undefined = undefined; |
||||
export let repo_id: string = ""; |
||||
export let pr_id: string = ""; |
||||
export let status: PRStatus | undefined = undefined |
||||
export let repo_id: string = '' |
||||
export let pr_id: string = '' |
||||
|
||||
let loading = false; |
||||
let loading = false |
||||
|
||||
let edit_mode = false; |
||||
$: { |
||||
edit_mode = |
||||
$logged_in_user !== undefined && repo_id === $selected_repo.repo_id; |
||||
} |
||||
let edit_mode = false |
||||
$: { |
||||
edit_mode = |
||||
$logged_in_user !== undefined && repo_id === $selected_repo.repo_id |
||||
} |
||||
|
||||
async function changeStatus(new_status: PRStatus) { |
||||
if (!$logged_in_user) return; |
||||
let event = new NDKEvent(ndk); |
||||
event.kind = pr_status_kind; |
||||
event.tags.push(["l", new_status]); |
||||
event.tags.push(["e", pr_id, "root"]); |
||||
event.tags.push(["r", `r-${repo_id}`]); |
||||
loading = true; |
||||
let relays = [...$selected_repo.relays]; |
||||
try { |
||||
event.sign(); |
||||
} catch { |
||||
alert("failed to sign event"); |
||||
} |
||||
try { |
||||
let user_relays = await getUserRelays($logged_in_user.hexpubkey); |
||||
relays = [ |
||||
...relays, |
||||
...(user_relays.ndk_relays |
||||
? user_relays.ndk_relays.writeRelayUrls |
||||
: []), |
||||
// TODO: pr event pubkey relays |
||||
]; |
||||
} catch { |
||||
alert("failed to get user relays"); |
||||
} |
||||
try { |
||||
let res = await event.publish( |
||||
NDKRelaySet.fromRelayUrls(relays, ndk), |
||||
); |
||||
selected_pr_full.update((pr_full) => { |
||||
if (pr_full.summary.id !== pr_id) return pr_full; |
||||
return { |
||||
...pr_full, |
||||
summary: { |
||||
...pr_full.summary, |
||||
status: new_status, |
||||
status_date: event.created_at || 0, |
||||
}, |
||||
}; |
||||
}); |
||||
loading = false; |
||||
} catch {} |
||||
async function changeStatus(new_status: PRStatus) { |
||||
if (!$logged_in_user) return |
||||
let event = new NDKEvent(ndk) |
||||
event.kind = pr_status_kind |
||||
event.tags.push(['l', new_status]) |
||||
event.tags.push(['e', pr_id, 'root']) |
||||
event.tags.push(['r', `r-${repo_id}`]) |
||||
loading = true |
||||
let relays = [...$selected_repo.relays] |
||||
try { |
||||
event.sign() |
||||
} catch { |
||||
alert('failed to sign event') |
||||
} |
||||
try { |
||||
let user_relays = await getUserRelays($logged_in_user.hexpubkey) |
||||
relays = [ |
||||
...relays, |
||||
...(user_relays.ndk_relays |
||||
? user_relays.ndk_relays.writeRelayUrls |
||||
: []), |
||||
// TODO: pr event pubkey relays |
||||
] |
||||
} catch { |
||||
alert('failed to get user relays') |
||||
} |
||||
try { |
||||
let _ = await event.publish(NDKRelaySet.fromRelayUrls(relays, ndk)) |
||||
selected_pr_full.update((pr_full) => { |
||||
if (pr_full.summary.id !== pr_id) return pr_full |
||||
return { |
||||
...pr_full, |
||||
summary: { |
||||
...pr_full.summary, |
||||
status: new_status, |
||||
status_date: event.created_at || 0, |
||||
}, |
||||
} |
||||
}) |
||||
loading = false |
||||
} catch {} |
||||
} |
||||
</script> |
||||
|
||||
{#if loading || !status} |
||||
<Status /> |
||||
<Status /> |
||||
{:else} |
||||
<div class="dropdown"> |
||||
<Status {edit_mode} {status} /> |
||||
{#if edit_mode} |
||||
<ul |
||||
tabIndex={0} |
||||
class="dropdown-content z-[1] menu p-2 ml-0 shadow bg-base-300 rounded-box w-52" |
||||
<div class="dropdown"> |
||||
<Status {edit_mode} {status} /> |
||||
{#if edit_mode} |
||||
<ul |
||||
tabIndex={0} |
||||
class="menu dropdown-content z-[1] ml-0 w-52 rounded-box bg-base-300 p-2 shadow" |
||||
> |
||||
{#if status !== 'Draft'} |
||||
<li class="pl-0"> |
||||
<button |
||||
on:click={() => { |
||||
changeStatus('Draft') |
||||
}} |
||||
class="btn btn-neutral btn-sm mx-2 align-middle">Draft</button |
||||
> |
||||
</li> |
||||
{/if} |
||||
{#if status !== 'Open'} |
||||
<li class="pl-0"> |
||||
<button |
||||
on:click={() => { |
||||
changeStatus('Open') |
||||
}} |
||||
class="btn btn-success btn-sm mx-2 align-middle">Open</button |
||||
> |
||||
</li> |
||||
{/if} |
||||
{#if status !== 'Merged'} |
||||
<li class="pl-0"> |
||||
<button |
||||
on:click={() => { |
||||
changeStatus('Merged') |
||||
}} |
||||
class="btn btn-primary btn-sm mx-2 align-middle">Merged</button |
||||
> |
||||
</li> |
||||
{/if} |
||||
{#if status !== 'Closed'} |
||||
<li class="pl-0"> |
||||
<button |
||||
on:click={() => { |
||||
changeStatus('Closed') |
||||
}} |
||||
class="btn btn-neutral btn-sm mx-2 align-middle">Closed</button |
||||
> |
||||
{#if status !== "Draft"} |
||||
<li class="pl-0"> |
||||
<button |
||||
on:click={() => { |
||||
changeStatus("Draft"); |
||||
}} |
||||
class="btn btn-neutral btn-sm mx-2 align-middle" |
||||
>Draft</button |
||||
> |
||||
</li> |
||||
{/if} |
||||
{#if status !== "Open"} |
||||
<li class="pl-0"> |
||||
<button |
||||
on:click={() => { |
||||
changeStatus("Open"); |
||||
}} |
||||
class="btn btn-success btn-sm mx-2 align-middle" |
||||
>Open</button |
||||
> |
||||
</li> |
||||
{/if} |
||||
{#if status !== "Merged"} |
||||
<li class="pl-0"> |
||||
<button |
||||
on:click={() => { |
||||
changeStatus("Merged"); |
||||
}} |
||||
class="btn btn-primary btn-sm mx-2 align-middle" |
||||
>Merged</button |
||||
> |
||||
</li> |
||||
{/if} |
||||
{#if status !== "Closed"} |
||||
<li class="pl-0"> |
||||
<button |
||||
on:click={() => { |
||||
changeStatus("Closed"); |
||||
}} |
||||
class="btn btn-neutral btn-sm mx-2 align-middle" |
||||
>Closed</button |
||||
> |
||||
</li> |
||||
{/if} |
||||
</ul> |
||||
</li> |
||||
{/if} |
||||
</div> |
||||
</ul> |
||||
{/if} |
||||
</div> |
||||
{/if} |
||||
|
||||
@ -1,6 +1,9 @@
@@ -1,6 +1,9 @@
|
||||
export let pr_icon_path = { |
||||
open: "M1.5 3.25a2.25 2.25 0 1 1 3 2.122v5.256a2.251 2.251 0 1 1-1.5 0V5.372A2.25 2.25 0 0 1 1.5 3.25m5.677-.177L9.573.677A.25.25 0 0 1 10 .854V2.5h1A2.5 2.5 0 0 1 13.5 5v5.628a2.251 2.251 0 1 1-1.5 0V5a1 1 0 0 0-1-1h-1v1.646a.25.25 0 0 1-.427.177L7.177 3.427a.25.25 0 0 1 0-.354M3.75 2.5a.75.75 0 1 0 0 1.5a.75.75 0 0 0 0-1.5m0 9.5a.75.75 0 1 0 0 1.5a.75.75 0 0 0 0-1.5m8.25.75a.75.75 0 1 0 1.5 0a.75.75 0 0 0-1.5 0", |
||||
close: "M3.25 1A2.25 2.25 0 0 1 4 5.372v5.256a2.251 2.251 0 1 1-1.5 0V5.372A2.251 2.251 0 0 1 3.25 1m9.5 5.5a.75.75 0 0 1 .75.75v3.378a2.251 2.251 0 1 1-1.5 0V7.25a.75.75 0 0 1 .75-.75m-2.03-5.273a.75.75 0 0 1 1.06 0l.97.97l.97-.97a.748.748 0 0 1 1.265.332a.75.75 0 0 1-.205.729l-.97.97l.97.97a.751.751 0 0 1-.018 1.042a.751.751 0 0 1-1.042.018l-.97-.97l-.97.97a.749.749 0 0 1-1.275-.326a.749.749 0 0 1 .215-.734l.97-.97l-.97-.97a.75.75 0 0 1 0-1.06ZM2.5 3.25a.75.75 0 1 0 1.5 0a.75.75 0 0 0-1.5 0M3.25 12a.75.75 0 1 0 0 1.5a.75.75 0 0 0 0-1.5m9.5 0a.75.75 0 1 0 0 1.5a.75.75 0 0 0 0-1.5", |
||||
draft: "M3.25 1A2.25 2.25 0 0 1 4 5.372v5.256a2.251 2.251 0 1 1-1.5 0V5.372A2.251 2.251 0 0 1 3.25 1m9.5 14a2.25 2.25 0 1 1 0-4.5a2.25 2.25 0 0 1 0 4.5M2.5 3.25a.75.75 0 1 0 1.5 0a.75.75 0 0 0-1.5 0M3.25 12a.75.75 0 1 0 0 1.5a.75.75 0 0 0 0-1.5m9.5 0a.75.75 0 1 0 0 1.5a.75.75 0 0 0 0-1.5M14 7.5a1.25 1.25 0 1 1-2.5 0a1.25 1.25 0 0 1 2.5 0m0-4.25a1.25 1.25 0 1 1-2.5 0a1.25 1.25 0 0 1 2.5 0", |
||||
merge: "M5.45 5.154A4.25 4.25 0 0 0 9.25 7.5h1.378a2.251 2.251 0 1 1 0 1.5H9.25A5.734 5.734 0 0 1 5 7.123v3.505a2.25 2.25 0 1 1-1.5 0V5.372a2.25 2.25 0 1 1 1.95-.218M4.25 13.5a.75.75 0 1 0 0-1.5a.75.75 0 0 0 0 1.5m8.5-4.5a.75.75 0 1 0 0-1.5a.75.75 0 0 0 0 1.5M5 3.25a.75.75 0 1 0 0 .005z", |
||||
}; |
||||
export const pr_icon_path = { |
||||
open: 'M1.5 3.25a2.25 2.25 0 1 1 3 2.122v5.256a2.251 2.251 0 1 1-1.5 0V5.372A2.25 2.25 0 0 1 1.5 3.25m5.677-.177L9.573.677A.25.25 0 0 1 10 .854V2.5h1A2.5 2.5 0 0 1 13.5 5v5.628a2.251 2.251 0 1 1-1.5 0V5a1 1 0 0 0-1-1h-1v1.646a.25.25 0 0 1-.427.177L7.177 3.427a.25.25 0 0 1 0-.354M3.75 2.5a.75.75 0 1 0 0 1.5a.75.75 0 0 0 0-1.5m0 9.5a.75.75 0 1 0 0 1.5a.75.75 0 0 0 0-1.5m8.25.75a.75.75 0 1 0 1.5 0a.75.75 0 0 0-1.5 0', |
||||
close: |
||||
'M3.25 1A2.25 2.25 0 0 1 4 5.372v5.256a2.251 2.251 0 1 1-1.5 0V5.372A2.251 2.251 0 0 1 3.25 1m9.5 5.5a.75.75 0 0 1 .75.75v3.378a2.251 2.251 0 1 1-1.5 0V7.25a.75.75 0 0 1 .75-.75m-2.03-5.273a.75.75 0 0 1 1.06 0l.97.97l.97-.97a.748.748 0 0 1 1.265.332a.75.75 0 0 1-.205.729l-.97.97l.97.97a.751.751 0 0 1-.018 1.042a.751.751 0 0 1-1.042.018l-.97-.97l-.97.97a.749.749 0 0 1-1.275-.326a.749.749 0 0 1 .215-.734l.97-.97l-.97-.97a.75.75 0 0 1 0-1.06ZM2.5 3.25a.75.75 0 1 0 1.5 0a.75.75 0 0 0-1.5 0M3.25 12a.75.75 0 1 0 0 1.5a.75.75 0 0 0 0-1.5m9.5 0a.75.75 0 1 0 0 1.5a.75.75 0 0 0 0-1.5', |
||||
draft: |
||||
'M3.25 1A2.25 2.25 0 0 1 4 5.372v5.256a2.251 2.251 0 1 1-1.5 0V5.372A2.251 2.251 0 0 1 3.25 1m9.5 14a2.25 2.25 0 1 1 0-4.5a2.25 2.25 0 0 1 0 4.5M2.5 3.25a.75.75 0 1 0 1.5 0a.75.75 0 0 0-1.5 0M3.25 12a.75.75 0 1 0 0 1.5a.75.75 0 0 0 0-1.5m9.5 0a.75.75 0 1 0 0 1.5a.75.75 0 0 0 0-1.5M14 7.5a1.25 1.25 0 1 1-2.5 0a1.25 1.25 0 0 1 2.5 0m0-4.25a1.25 1.25 0 1 1-2.5 0a1.25 1.25 0 0 1 2.5 0', |
||||
merge: |
||||
'M5.45 5.154A4.25 4.25 0 0 0 9.25 7.5h1.378a2.251 2.251 0 1 1 0 1.5H9.25A5.734 5.734 0 0 1 5 7.123v3.505a2.25 2.25 0 1 1-1.5 0V5.372a2.25 2.25 0 1 1 1.95-.218M4.25 13.5a.75.75 0 1 0 0-1.5a.75.75 0 0 0 0 1.5m8.5-4.5a.75.75 0 1 0 0-1.5a.75.75 0 0 0 0 1.5M5 3.25a.75.75 0 1 0 0 .005z', |
||||
} |
||||
|
||||
@ -1,70 +1,71 @@
@@ -1,70 +1,71 @@
|
||||
import type { User } from "../users/type"; |
||||
import { defaults as user_defaults } from "../users/type"; |
||||
import type { Event } from "../events/type"; |
||||
import type { NDKEvent } from "@nostr-dev-kit/ndk"; |
||||
import type { User } from '../users/type' |
||||
import { defaults as user_defaults } from '../users/type' |
||||
import type { Event } from '../events/type' |
||||
import type { NDKEvent } from '@nostr-dev-kit/ndk' |
||||
|
||||
export interface PRSummary { |
||||
title: string; |
||||
descritpion: string; |
||||
repo_id: string; |
||||
id: string; |
||||
comments: number; |
||||
status: undefined | PRStatus, |
||||
status_date: number, |
||||
author: User; |
||||
created_at: number | undefined; |
||||
loading: boolean; |
||||
title: string |
||||
descritpion: string |
||||
repo_id: string |
||||
id: string |
||||
comments: number |
||||
status: undefined | PRStatus |
||||
status_date: number |
||||
author: User |
||||
created_at: number | undefined |
||||
loading: boolean |
||||
} |
||||
|
||||
export const summary_defaults: PRSummary = { |
||||
title: "", |
||||
descritpion: "", |
||||
repo_id: "", |
||||
id: "", |
||||
comments: 0, |
||||
status: undefined, |
||||
status_date: 0, |
||||
author: { ...user_defaults }, |
||||
created_at: 0, |
||||
loading: true, |
||||
}; |
||||
title: '', |
||||
descritpion: '', |
||||
repo_id: '', |
||||
id: '', |
||||
comments: 0, |
||||
status: undefined, |
||||
status_date: 0, |
||||
author: { ...user_defaults }, |
||||
created_at: 0, |
||||
loading: true, |
||||
} |
||||
|
||||
export interface PRSummaries { |
||||
id: string; |
||||
summaries: PRSummary[]; |
||||
loading: boolean; |
||||
id: string |
||||
summaries: PRSummary[] |
||||
loading: boolean |
||||
} |
||||
|
||||
export const summaries_defaults: PRSummaries = { |
||||
id: "", |
||||
summaries: [], |
||||
loading: true, |
||||
}; |
||||
id: '', |
||||
summaries: [], |
||||
loading: true, |
||||
} |
||||
|
||||
export type PRStatus = "Draft" | "Open" | "Merged" | "Closed"; |
||||
export type PRStatus = 'Draft' | 'Open' | 'Merged' | 'Closed' |
||||
|
||||
export function isPRStatus(potential_status: string | undefined): potential_status is PRStatus { |
||||
return !!potential_status |
||||
&& ( |
||||
potential_status == "Draft" |
||||
|| potential_status == "Open" |
||||
|| potential_status == "Merged" |
||||
|| potential_status == "Closed" |
||||
) |
||||
export function isPRStatus( |
||||
potential_status: string | undefined |
||||
): potential_status is PRStatus { |
||||
return ( |
||||
!!potential_status && |
||||
(potential_status == 'Draft' || |
||||
potential_status == 'Open' || |
||||
potential_status == 'Merged' || |
||||
potential_status == 'Closed') |
||||
) |
||||
} |
||||
export interface PRFull { |
||||
summary: PRSummary; |
||||
pr_event: NDKEvent | undefined; |
||||
labels: string[]; |
||||
events: Event[]; |
||||
loading: boolean; |
||||
summary: PRSummary |
||||
pr_event: NDKEvent | undefined |
||||
labels: string[] |
||||
events: Event[] |
||||
loading: boolean |
||||
} |
||||
|
||||
export const full_defaults: PRFull = { |
||||
summary: { ...summary_defaults }, |
||||
pr_event: undefined, |
||||
labels: [], |
||||
events: [], |
||||
loading: true, |
||||
}; |
||||
|
||||
summary: { ...summary_defaults }, |
||||
pr_event: undefined, |
||||
labels: [], |
||||
events: [], |
||||
loading: true, |
||||
} |
||||
|
||||
@ -1,59 +1,61 @@
@@ -1,59 +1,61 @@
|
||||
import dayjs from "dayjs"; |
||||
import relativeTime from "dayjs/plugin/relativeTime"; |
||||
import type { PRSummary } from "./type"; |
||||
import { UserVectors } from "../users/vectors"; |
||||
import dayjs from 'dayjs' |
||||
import relativeTime from 'dayjs/plugin/relativeTime' |
||||
import type { PRSummary } from './type' |
||||
import { UserVectors } from '../users/vectors' |
||||
|
||||
dayjs.extend(relativeTime); |
||||
dayjs.extend(relativeTime) |
||||
|
||||
let Short = { |
||||
title: "short title", |
||||
const Short = { |
||||
title: 'short title', |
||||
author: { ...UserVectors.default }, |
||||
created_at: dayjs().subtract(7, 'days').unix(), |
||||
comments: 2, |
||||
status: 'Open', |
||||
loading: false, |
||||
} as PRSummary |
||||
|
||||
export const PRsListItemArgsVectors = { |
||||
Short, |
||||
Long: { |
||||
title: |
||||
'rather long title that goes on and on and on and on and on and on and on and on and on and on and on and on and on and on and on', |
||||
author: { ...UserVectors.default }, |
||||
created_at: dayjs().subtract(7, 'days').unix(), |
||||
comments: 2, |
||||
status: "Open", |
||||
created_at: dayjs().subtract(1, 'minute').unix(), |
||||
comments: 0, |
||||
status: 'Open', |
||||
loading: false, |
||||
} as PRSummary; |
||||
|
||||
export let PRsListItemArgsVectors = { |
||||
Short, |
||||
Long: { |
||||
title: "rather long title that goes on and on and on and on and on and on and on and on and on and on and on and on and on and on and on", |
||||
author: { ...UserVectors.default }, |
||||
created_at: dayjs().subtract(1, 'minute').unix(), |
||||
comments: 0, |
||||
status: "Open", |
||||
loading: false, |
||||
} as PRSummary, |
||||
LongNoSpaces: { |
||||
title: "LongNameLongNameLongNameLongNameLongNameLongNameLongNameLongNameLongNameLongNameLongNameLongNameLongNameLongName", |
||||
author: { ...UserVectors.default }, |
||||
created_at: dayjs().subtract(3, 'month').subtract(3, 'days').unix(), |
||||
comments: 1, |
||||
status: "Open", |
||||
loading: false, |
||||
} as PRSummary, |
||||
AuthorLoading: { |
||||
title: "short title", |
||||
author: { ...UserVectors.loading }, |
||||
created_at: dayjs().subtract(3, 'month').subtract(3, 'days').unix(), |
||||
comments: 1, |
||||
status: "Open", |
||||
loading: false, |
||||
} as PRSummary, |
||||
StatusLoading: { |
||||
...Short, |
||||
status: undefined, |
||||
} as PRSummary, |
||||
StatusDraft: { |
||||
...Short, |
||||
status: "Draft", |
||||
} as PRSummary, |
||||
StatusClosed: { |
||||
...Short, |
||||
status: "Closed", |
||||
} as PRSummary, |
||||
StatusMerged: { |
||||
...Short, |
||||
status: "Merged", |
||||
} as PRSummary, |
||||
}; |
||||
} as PRSummary, |
||||
LongNoSpaces: { |
||||
title: |
||||
'LongNameLongNameLongNameLongNameLongNameLongNameLongNameLongNameLongNameLongNameLongNameLongNameLongNameLongName', |
||||
author: { ...UserVectors.default }, |
||||
created_at: dayjs().subtract(3, 'month').subtract(3, 'days').unix(), |
||||
comments: 1, |
||||
status: 'Open', |
||||
loading: false, |
||||
} as PRSummary, |
||||
AuthorLoading: { |
||||
title: 'short title', |
||||
author: { ...UserVectors.loading }, |
||||
created_at: dayjs().subtract(3, 'month').subtract(3, 'days').unix(), |
||||
comments: 1, |
||||
status: 'Open', |
||||
loading: false, |
||||
} as PRSummary, |
||||
StatusLoading: { |
||||
...Short, |
||||
status: undefined, |
||||
} as PRSummary, |
||||
StatusDraft: { |
||||
...Short, |
||||
status: 'Draft', |
||||
} as PRSummary, |
||||
StatusClosed: { |
||||
...Short, |
||||
status: 'Closed', |
||||
} as PRSummary, |
||||
StatusMerged: { |
||||
...Short, |
||||
status: 'Merged', |
||||
} as PRSummary, |
||||
} |
||||
|
||||
@ -1,98 +1,85 @@
@@ -1,98 +1,85 @@
|
||||
<script lang="ts" context="module"> |
||||
import type { NDKUserProfile } from "@nostr-dev-kit/ndk"; |
||||
</script> |
||||
|
||||
<script lang="ts"> |
||||
import type { User } from "$lib/components/users/type"; |
||||
import UserHeader from "$lib/components/users/UserHeader.svelte"; |
||||
import { defaults } from "./type"; |
||||
import UserHeader from '$lib/components/users/UserHeader.svelte' |
||||
import { defaults } from './type' |
||||
|
||||
export let { |
||||
repo_id, |
||||
name, |
||||
description, |
||||
git_server, |
||||
tags, |
||||
maintainers, |
||||
relays, |
||||
loading, |
||||
} = defaults; |
||||
let short_name: string; |
||||
$: { |
||||
if (name.length > 45) short_name = name.slice(0, 45) + "..."; |
||||
else if (name.length == 0) short_name = "Untitled"; |
||||
else short_name = name; |
||||
} |
||||
$: short_descrption = |
||||
description.length > 500 |
||||
? description.slice(0, 450) + "..." |
||||
: description; |
||||
export let { |
||||
repo_id, |
||||
name, |
||||
description, |
||||
git_server, |
||||
tags, |
||||
maintainers, |
||||
relays, |
||||
loading, |
||||
} = defaults |
||||
$: short_descrption = |
||||
description.length > 500 ? description.slice(0, 450) + '...' : description |
||||
</script> |
||||
|
||||
<div class="max-w-md prose w-full"> |
||||
<div class="prose w-full max-w-md"> |
||||
{#if loading} |
||||
<div class="skeleton my-3 h-5 w-20"></div> |
||||
<div class="skeleton my-2 h-4"></div> |
||||
<div class="skeleton my-2 mb-3 h-4 w-2/3"></div> |
||||
{:else if description.length == 0} |
||||
<div /> |
||||
{:else} |
||||
<h4>description</h4> |
||||
<p class="my-2 break-words text-sm">{short_descrption}</p> |
||||
{/if} |
||||
<div> |
||||
{#if loading} |
||||
<div class="badge skeleton w-20"></div> |
||||
<div class="badge skeleton w-20"></div> |
||||
{:else} |
||||
{#each tags as tag} |
||||
<div class="badge badge-secondary mr-2">{tag}</div> |
||||
{/each} |
||||
{/if} |
||||
</div> |
||||
<div> |
||||
{#if loading} |
||||
<div class="skeleton my-3 h-5 w-20"></div> |
||||
<div class="badge skeleton my-2 block w-60"></div> |
||||
{:else if git_server.length == 0} |
||||
<div /> |
||||
{:else} |
||||
<h4>git server</h4> |
||||
<a |
||||
href={git_server} |
||||
target="_blank" |
||||
class="link link-primary my-2 break-words" |
||||
> |
||||
{git_server} |
||||
</a> |
||||
{/if} |
||||
</div> |
||||
<div> |
||||
{#if loading} |
||||
<div class="skeleton my-3 h-5 w-20"></div> |
||||
<div class="badge skeleton my-2 block w-60"></div> |
||||
<div class="badge skeleton my-2 block w-40"></div> |
||||
{:else if maintainers.length == 0} |
||||
<div /> |
||||
{:else} |
||||
<h4>maintainers</h4> |
||||
{#each maintainers as maintainer} |
||||
<UserHeader user={maintainer} /> |
||||
{/each} |
||||
{/if} |
||||
</div> |
||||
<div> |
||||
{#if loading} |
||||
<div class="h-5 my-3 skeleton w-20"></div> |
||||
<div class="h-4 my-2 skeleton"></div> |
||||
<div class="h-4 my-2 mb-3 w-2/3 skeleton"></div> |
||||
{:else if description.length == 0} |
||||
<div /> |
||||
<div class="skeleton my-3 h-5 w-20"></div> |
||||
<div class="badge skeleton my-2 block w-60"></div> |
||||
<div class="badge skeleton my-2 block w-40"></div> |
||||
{:else if relays.length == 0} |
||||
<div /> |
||||
{:else} |
||||
<h4>description</h4> |
||||
<p class="text-sm my-2 break-words">{short_descrption}</p> |
||||
<h4>relays</h4> |
||||
{#each relays as relay} |
||||
<div class="badge badge-secondary my-2 block">{relay}</div> |
||||
{/each} |
||||
{/if} |
||||
<div> |
||||
{#if loading} |
||||
<div class="badge skeleton w-20"></div> |
||||
<div class="badge skeleton w-20"></div> |
||||
{:else} |
||||
{#each tags as tag} |
||||
<div class="badge badge-secondary mr-2">{tag}</div> |
||||
{/each} |
||||
{/if} |
||||
</div> |
||||
<div> |
||||
{#if loading} |
||||
<div class="h-5 my-3 skeleton w-20"></div> |
||||
<div class="badge skeleton my-2 w-60 block"></div> |
||||
{:else if git_server.length == 0} |
||||
<div /> |
||||
{:else} |
||||
<h4>git server</h4> |
||||
<a |
||||
href={git_server} |
||||
target="_blank" |
||||
class="link link-primary break-words my-2" |
||||
> |
||||
{git_server} |
||||
</a> |
||||
{/if} |
||||
</div> |
||||
<div> |
||||
{#if loading} |
||||
<div class="h-5 my-3 skeleton w-20"></div> |
||||
<div class="badge skeleton my-2 w-60 block"></div> |
||||
<div class="badge skeleton my-2 w-40 block"></div> |
||||
{:else if maintainers.length == 0} |
||||
<div /> |
||||
{:else} |
||||
<h4>maintainers</h4> |
||||
{#each maintainers as maintainer} |
||||
<UserHeader user={maintainer} /> |
||||
{/each} |
||||
{/if} |
||||
</div> |
||||
<div> |
||||
{#if loading} |
||||
<div class="h-5 my-3 skeleton w-20"></div> |
||||
<div class="badge skeleton my-2 w-60 block"></div> |
||||
<div class="badge skeleton my-2 w-40 block"></div> |
||||
{:else if relays.length == 0} |
||||
<div /> |
||||
{:else} |
||||
<h4>relays</h4> |
||||
{#each relays as relay} |
||||
<div class="badge badge-secondary block my-2">{relay}</div> |
||||
{/each} |
||||
{/if} |
||||
</div> |
||||
</div> |
||||
</div> |
||||
|
||||
@ -1,43 +1,37 @@
@@ -1,43 +1,37 @@
|
||||
<script lang="ts" context="module"> |
||||
import type { NDKUserProfile } from "@nostr-dev-kit/ndk"; |
||||
</script> |
||||
|
||||
<script lang="ts"> |
||||
import type { User } from "$lib/components/users/type"; |
||||
import UserHeader from "$lib/components/users/UserHeader.svelte"; |
||||
import Container from "../Container.svelte"; |
||||
import { defaults } from "./type"; |
||||
import Container from '../Container.svelte' |
||||
import { defaults } from './type' |
||||
|
||||
export let { |
||||
repo_id, |
||||
name, |
||||
description, |
||||
git_server, |
||||
tags, |
||||
maintainers, |
||||
relays, |
||||
loading, |
||||
} = defaults; |
||||
let short_name: string; |
||||
$: { |
||||
if (name.length > 45) short_name = name.slice(0, 45) + "..."; |
||||
else if (name.length == 0) short_name = "Untitled"; |
||||
else short_name = name; |
||||
} |
||||
export let { |
||||
repo_id, |
||||
name, |
||||
description, |
||||
git_server, |
||||
tags, |
||||
maintainers, |
||||
relays, |
||||
loading, |
||||
} = defaults |
||||
let short_name: string |
||||
$: { |
||||
if (name.length > 45) short_name = name.slice(0, 45) + '...' |
||||
else if (name.length == 0) short_name = 'Untitled' |
||||
else short_name = name |
||||
} |
||||
</script> |
||||
|
||||
<div class="bg-base-300 border-b border-accent-content"> |
||||
<Container no_wrap={true}> |
||||
{#if loading} |
||||
<div class="p-3"> |
||||
<div class="h-6 skeleton w-28 bg-base-200"></div> |
||||
</div> |
||||
{:else} |
||||
<a |
||||
href={`/repo/${repo_id}`} |
||||
class="btn btn-ghost text-sm break-words strong mt-0 mb-0 px-3" |
||||
>{short_name}</a |
||||
> |
||||
{/if} |
||||
</Container> |
||||
<div class="border-b border-accent-content bg-base-300"> |
||||
<Container no_wrap={true}> |
||||
{#if loading} |
||||
<div class="p-3"> |
||||
<div class="skeleton h-6 w-28 bg-base-200"></div> |
||||
</div> |
||||
{:else} |
||||
<a |
||||
href={`/repo/${repo_id}`} |
||||
class="strong btn btn-ghost mb-0 mt-0 break-words px-3 text-sm" |
||||
>{short_name}</a |
||||
> |
||||
{/if} |
||||
</Container> |
||||
</div> |
||||
|
||||
@ -1,22 +1,22 @@
@@ -1,22 +1,22 @@
|
||||
import type { User } from "../users/type"; |
||||
import type { User } from '../users/type' |
||||
|
||||
export interface Repo { |
||||
repo_id: string; |
||||
name: string; |
||||
description: string; |
||||
git_server: string; |
||||
tags: string[]; |
||||
maintainers: User[]; |
||||
relays: string[]; |
||||
loading: boolean; |
||||
repo_id: string |
||||
name: string |
||||
description: string |
||||
git_server: string |
||||
tags: string[] |
||||
maintainers: User[] |
||||
relays: string[] |
||||
loading: boolean |
||||
} |
||||
export const defaults: Repo = { |
||||
repo_id: "", |
||||
name: "", |
||||
description: "", |
||||
git_server: "", |
||||
tags: [], |
||||
maintainers: [], |
||||
relays: [], |
||||
loading: true, |
||||
}; |
||||
repo_id: '', |
||||
name: '', |
||||
description: '', |
||||
git_server: '', |
||||
tags: [], |
||||
maintainers: [], |
||||
relays: [], |
||||
loading: true, |
||||
} |
||||
|
||||
@ -1,92 +1,89 @@
@@ -1,92 +1,89 @@
|
||||
import type { Args as SummaryCardArgs } from "../RepoSummaryCard.svelte"; |
||||
import type { NDKUserProfile } from "@nostr-dev-kit/ndk"; |
||||
import type { User } from "../users/type"; |
||||
import { UserVectors, withName } from "../users/vectors"; |
||||
import type { Repo } from "./type"; |
||||
import type { Args as SummaryCardArgs } from '../RepoSummaryCard.svelte' |
||||
import { UserVectors, withName } from '../users/vectors' |
||||
import type { Repo } from './type' |
||||
|
||||
export let RepoSummaryCardArgsVectors = { |
||||
Short: { |
||||
name: "Short Name", |
||||
description: "short description", |
||||
} as SummaryCardArgs, |
||||
Long: { |
||||
name: "Long Name that goes on and on and on and on and on and on and on and on and on", |
||||
description: |
||||
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis quis nisl eget turpis congue molestie. Nulla vitae purus nec augue accumsan facilisis sed sed ligula. Vestibulum sed risus lacinia risus lacinia molestie. Ut lorem quam, consequat eget tempus in, rhoncus vel nunc. Duis efficitur a leo vel sodales. Nam id fermentum lacus. Etiam nec placerat velit. Praesent ac consectetur est. Aenean iaculis commodo enim.", |
||||
} as SummaryCardArgs, |
||||
LongNoSpaces: { |
||||
name: "LongNameLongNameLongNameLongNameLongNameLongNameLongNameLongName", |
||||
description: |
||||
"LoremipsumLoremipsumLoremipsumLoremipsumLoremipsumLoremipsumLoremipsumLoremipsumLoremipsumLoremipsumLoremipsumLoremipsumLoremipsumLoremipsumLoremipsumLoremipsumLoremipsumLoremipsumLoremipsumLoremipsumLoremipsumLoremipsumLoremipsumLoremipsumLoremipsum>", |
||||
} as SummaryCardArgs, |
||||
}; |
||||
let base: Repo = { |
||||
repo_id: "9ee507fc4357d7ee16a5d8901bedcd103f23c17d", |
||||
name: "Short Name", |
||||
description: "short description", |
||||
git_server: "github.com/example/example", |
||||
tags: ["svelte", "nostr", "code-collaboration", "git"], |
||||
relays: [ |
||||
"relay.damus.io", |
||||
"relay.snort.social", |
||||
"relayable.org", |
||||
export const RepoSummaryCardArgsVectors = { |
||||
Short: { |
||||
name: 'Short Name', |
||||
description: 'short description', |
||||
} as SummaryCardArgs, |
||||
Long: { |
||||
name: 'Long Name that goes on and on and on and on and on and on and on and on and on', |
||||
description: |
||||
'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis quis nisl eget turpis congue molestie. Nulla vitae purus nec augue accumsan facilisis sed sed ligula. Vestibulum sed risus lacinia risus lacinia molestie. Ut lorem quam, consequat eget tempus in, rhoncus vel nunc. Duis efficitur a leo vel sodales. Nam id fermentum lacus. Etiam nec placerat velit. Praesent ac consectetur est. Aenean iaculis commodo enim.', |
||||
} as SummaryCardArgs, |
||||
LongNoSpaces: { |
||||
name: 'LongNameLongNameLongNameLongNameLongNameLongNameLongNameLongName', |
||||
description: |
||||
'LoremipsumLoremipsumLoremipsumLoremipsumLoremipsumLoremipsumLoremipsumLoremipsumLoremipsumLoremipsumLoremipsumLoremipsumLoremipsumLoremipsumLoremipsumLoremipsumLoremipsumLoremipsumLoremipsumLoremipsumLoremipsumLoremipsumLoremipsumLoremipsumLoremipsum>', |
||||
} as SummaryCardArgs, |
||||
} |
||||
const base: Repo = { |
||||
repo_id: '9ee507fc4357d7ee16a5d8901bedcd103f23c17d', |
||||
name: 'Short Name', |
||||
description: 'short description', |
||||
git_server: 'github.com/example/example', |
||||
tags: ['svelte', 'nostr', 'code-collaboration', 'git'], |
||||
relays: ['relay.damus.io', 'relay.snort.social', 'relayable.org'], |
||||
maintainers: [ |
||||
withName(UserVectors.default, 'carole'), |
||||
withName(UserVectors.default, 'bob'), |
||||
withName(UserVectors.default, 'steve'), |
||||
], |
||||
loading: false, |
||||
} |
||||
|
||||
export const RepoDetailsArgsVectors = { |
||||
Short: { ...base } as Repo, |
||||
Long: { |
||||
...base, |
||||
name: 'Long Name that goes on and on and on and on and on and on and on and on and on', |
||||
description: |
||||
'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis quis nisl eget turpis congue molestie. Nulla vitae purus nec augue accumsan facilisis sed sed ligula. Vestibulum sed risus lacinia risus lacinia molestie. Ut lorem quam, consequat eget tempus in, rhoncus vel nunc. Duis efficitur a leo vel sodales. Nam id fermentum lacus. Etiam nec placerat velit. Praesent ac consectetur est. Aenean iaculis commodo enim.\n Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis quis nisl eget turpis congue molestie.', |
||||
} as Repo, |
||||
LongNoSpaces: { |
||||
...base, |
||||
name: 'LongNameLongNameLongNameLongNameLongNameLongNameLongNameLongName', |
||||
description: |
||||
'LoremipsumLoremipsumLoremipsumLoremipsumLoremipsumLoremipsumLoremipsumLoremipsumLoremipsumLoremipsumLoremipsumLoremipsumLoremipsumLoremipsumLoremipsumLoremipsumLoremipsumLoremipsumLoremipsumLoremipsumLoremipsumLoremipsumLoremipsumLoremipsumLoremipsum', |
||||
} as Repo, |
||||
NoNameOrDescription: { ...base, name: '', description: '' } as Repo, |
||||
NoDescription: { ...base, description: '' } as Repo, |
||||
NoTags: { ...base, tags: [] } as Repo, |
||||
NoGitServer: { ...base, git_server: '' } as Repo, |
||||
MaintainersOneProfileNotLoaded: { |
||||
...base, |
||||
maintainers: [ |
||||
{ ...base.maintainers[0] }, |
||||
{ ...UserVectors.loading }, |
||||
{ ...base.maintainers[2] }, |
||||
], |
||||
} as Repo, |
||||
MaintainersOneProfileDisplayNameWithoutName: { |
||||
...base, |
||||
maintainers: [ |
||||
withName(UserVectors.default, "carole"), |
||||
withName(UserVectors.default, "bob"), |
||||
withName(UserVectors.default, "steve"), |
||||
{ ...base.maintainers[0] }, |
||||
{ ...UserVectors.display_name_only }, |
||||
{ ...base.maintainers[2] }, |
||||
], |
||||
loading: false, |
||||
}; |
||||
|
||||
export let RepoDetailsArgsVectors = { |
||||
Short: { ...base, } as Repo, |
||||
Long: { |
||||
...base, |
||||
name: "Long Name that goes on and on and on and on and on and on and on and on and on", |
||||
description: |
||||
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis quis nisl eget turpis congue molestie. Nulla vitae purus nec augue accumsan facilisis sed sed ligula. Vestibulum sed risus lacinia risus lacinia molestie. Ut lorem quam, consequat eget tempus in, rhoncus vel nunc. Duis efficitur a leo vel sodales. Nam id fermentum lacus. Etiam nec placerat velit. Praesent ac consectetur est. Aenean iaculis commodo enim.\n Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis quis nisl eget turpis congue molestie.", |
||||
} as Repo, |
||||
LongNoSpaces: { |
||||
...base, |
||||
name: "LongNameLongNameLongNameLongNameLongNameLongNameLongNameLongName", |
||||
description: |
||||
"LoremipsumLoremipsumLoremipsumLoremipsumLoremipsumLoremipsumLoremipsumLoremipsumLoremipsumLoremipsumLoremipsumLoremipsumLoremipsumLoremipsumLoremipsumLoremipsumLoremipsumLoremipsumLoremipsumLoremipsumLoremipsumLoremipsumLoremipsumLoremipsumLoremipsum", |
||||
} as Repo, |
||||
NoNameOrDescription: { ...base, name: "", description: "" } as Repo, |
||||
NoDescription: { ...base, description: "" } as Repo, |
||||
NoTags: { ...base, tags: [] } as Repo, |
||||
NoGitServer: { ...base, git_server: "" } as Repo, |
||||
MaintainersOneProfileNotLoaded: { |
||||
...base, maintainers: [ |
||||
{ ...base.maintainers[0] }, |
||||
{ ...UserVectors.loading }, |
||||
{ ...base.maintainers[2] }, |
||||
] |
||||
} as Repo, |
||||
MaintainersOneProfileDisplayNameWithoutName: { |
||||
...base, maintainers: [ |
||||
{ ...base.maintainers[0] }, |
||||
{ ...UserVectors.display_name_only }, |
||||
{ ...base.maintainers[2] }, |
||||
] |
||||
} as Repo, |
||||
MaintainersOneProfileNameAndDisplayNamePresent: { |
||||
...base, maintainers: [ |
||||
{ ...base.maintainers[0] }, |
||||
{ ...UserVectors.display_name_and_name }, |
||||
{ ...base.maintainers[2] }, |
||||
] |
||||
} as Repo, |
||||
MaintainersOneProfileNoNameOrDisplayNameBeingPresent: { |
||||
...base, maintainers: [ |
||||
{ ...base.maintainers[0] }, |
||||
{ ...UserVectors.no_profile }, |
||||
{ ...base.maintainers[2] }, |
||||
|
||||
] |
||||
} as Repo, |
||||
NoMaintainers: { ...base, maintainers: [] } as Repo, |
||||
NoRelays: { ...base, relays: [] } as Repo, |
||||
NoMaintainersOrRelays: { ...base, maintainers: [], relays: [] } as Repo, |
||||
}; |
||||
} as Repo, |
||||
MaintainersOneProfileNameAndDisplayNamePresent: { |
||||
...base, |
||||
maintainers: [ |
||||
{ ...base.maintainers[0] }, |
||||
{ ...UserVectors.display_name_and_name }, |
||||
{ ...base.maintainers[2] }, |
||||
], |
||||
} as Repo, |
||||
MaintainersOneProfileNoNameOrDisplayNameBeingPresent: { |
||||
...base, |
||||
maintainers: [ |
||||
{ ...base.maintainers[0] }, |
||||
{ ...UserVectors.no_profile }, |
||||
{ ...base.maintainers[2] }, |
||||
], |
||||
} as Repo, |
||||
NoMaintainers: { ...base, maintainers: [] } as Repo, |
||||
NoRelays: { ...base, relays: [] } as Repo, |
||||
NoMaintainersOrRelays: { ...base, maintainers: [], relays: [] } as Repo, |
||||
} |
||||
|
||||
@ -1,32 +1,33 @@
@@ -1,32 +1,33 @@
|
||||
<script lang="ts"> |
||||
import { getName, type User } from "./type"; |
||||
import { getName, type User } from './type' |
||||
|
||||
export let user: User = { |
||||
hexpubkey: "", |
||||
npub: "", |
||||
loading: true, |
||||
}; |
||||
$: ({ profile, loading } = user); |
||||
$: display_name = getName(user); |
||||
export let user: User = { |
||||
hexpubkey: '', |
||||
npub: '', |
||||
loading: true, |
||||
} |
||||
|
||||
$: ({ profile, loading } = user) |
||||
$: display_name = getName(user) |
||||
</script> |
||||
|
||||
<div class="flex my-2"> |
||||
<div class="avatar flex-none"> |
||||
<div |
||||
class="w-8 h-8 rounded" |
||||
class:skeleton={!profile && loading} |
||||
class:bg-neutral={!loading && (!profile || !profile.image)} |
||||
> |
||||
{#if !!profile?.image} |
||||
<img class="my-0" src={profile?.image} alt={display_name} /> |
||||
{/if} |
||||
</div> |
||||
</div> |
||||
<div class="flex-auto pl-3 m-auto"> |
||||
{#if loading} |
||||
<div class="w-24 h-4 skeleton"></div> |
||||
{:else} |
||||
{display_name} |
||||
{/if} |
||||
<div class="my-2 flex"> |
||||
<div class="avatar flex-none"> |
||||
<div |
||||
class="h-8 w-8 rounded" |
||||
class:skeleton={!profile && loading} |
||||
class:bg-neutral={!loading && (!profile || !profile.image)} |
||||
> |
||||
{#if profile && profile?.image} |
||||
<img class="my-0" src={profile?.image} alt={display_name} /> |
||||
{/if} |
||||
</div> |
||||
</div> |
||||
<div class="m-auto flex-auto pl-3"> |
||||
{#if loading} |
||||
<div class="skeleton h-4 w-24"></div> |
||||
{:else} |
||||
{display_name} |
||||
{/if} |
||||
</div> |
||||
</div> |
||||
|
||||
@ -1,29 +1,28 @@
@@ -1,29 +1,28 @@
|
||||
import type { NDKUserProfile } from "@nostr-dev-kit/ndk"; |
||||
import type { NDKUserProfile } from '@nostr-dev-kit/ndk' |
||||
|
||||
export interface User { |
||||
loading: boolean; |
||||
hexpubkey: string; |
||||
npub: string; |
||||
profile?: NDKUserProfile; |
||||
loading: boolean |
||||
hexpubkey: string |
||||
npub: string |
||||
profile?: NDKUserProfile |
||||
} |
||||
|
||||
export let defaults: User = { |
||||
loading: true, |
||||
hexpubkey: "", |
||||
npub: "", |
||||
export const defaults: User = { |
||||
loading: true, |
||||
hexpubkey: '', |
||||
npub: '', |
||||
} |
||||
|
||||
export function getName(user: User, fallback_to_pubkey: boolean = false): string { |
||||
return user.profile ? ( |
||||
user.profile.name |
||||
? user.profile.name |
||||
: user.profile.displayName |
||||
? user.profile.displayName |
||||
: truncateNpub(user.npub) |
||||
) |
||||
: truncateNpub(user.npub); |
||||
export function getName(user: User): string { |
||||
return user.profile |
||||
? user.profile.name |
||||
? user.profile.name |
||||
: user.profile.displayName |
||||
? user.profile.displayName |
||||
: truncateNpub(user.npub) |
||||
: truncateNpub(user.npub) |
||||
} |
||||
|
||||
function truncateNpub(npub: string): string { |
||||
return `${npub.substring(0, 9)}...`; |
||||
} |
||||
return `${npub.substring(0, 9)}...` |
||||
} |
||||
|
||||
@ -1,30 +1,35 @@
@@ -1,30 +1,35 @@
|
||||
import type { User } from "./type"; |
||||
import type { User } from './type' |
||||
|
||||
// nsec1rg53qfv09az39dlw6j64ange3cx8sh5p8np29qcxtythplvplktsv93tnr
|
||||
let base: User = { |
||||
hexpubkey: |
||||
"3eb45c6f15752d796fa5465d0530a5a5feb79fb6f08c0a4176be9d73cc28c40d", |
||||
npub: "npub18669cmc4w5khjma9gews2v995hlt08ak7zxq5stkh6wh8npgcsxslt2xjn", |
||||
loading: false, |
||||
}; |
||||
const base: User = { |
||||
hexpubkey: '3eb45c6f15752d796fa5465d0530a5a5feb79fb6f08c0a4176be9d73cc28c40d', |
||||
npub: 'npub18669cmc4w5khjma9gews2v995hlt08ak7zxq5stkh6wh8npgcsxslt2xjn', |
||||
loading: false, |
||||
} |
||||
|
||||
let image = "../test-profile-image.jpg"; |
||||
const image = '../test-profile-image.jpg' |
||||
|
||||
export let UserVectors = { |
||||
loading: { ...base, loading: true } as User, |
||||
default: { ...base, profile: { name: "DanConwayDev", image } } as User, |
||||
display_name_only: { ...base, profile: { displayName: "DanConwayDev", image } } as User, |
||||
display_name_and_name: { ...base, profile: { name: "Dan", displayName: "DanConwayDev", image } } as User, |
||||
no_image: { ...base, profile: { name: "DanConwayDev" } } as User, |
||||
no_profile: { ...base } as User, |
||||
}; |
||||
export const UserVectors = { |
||||
loading: { ...base, loading: true } as User, |
||||
default: { ...base, profile: { name: 'DanConwayDev', image } } as User, |
||||
display_name_only: { |
||||
...base, |
||||
profile: { displayName: 'DanConwayDev', image }, |
||||
} as User, |
||||
display_name_and_name: { |
||||
...base, |
||||
profile: { name: 'Dan', displayName: 'DanConwayDev', image }, |
||||
} as User, |
||||
no_image: { ...base, profile: { name: 'DanConwayDev' } } as User, |
||||
no_profile: { ...base } as User, |
||||
} |
||||
|
||||
export function withName(base: User, name: string): User { |
||||
return { |
||||
...base, |
||||
profile: { |
||||
...base.profile, |
||||
name, |
||||
} |
||||
} as User |
||||
return { |
||||
...base, |
||||
profile: { |
||||
...base.profile, |
||||
name, |
||||
}, |
||||
} as User |
||||
} |
||||
|
||||
@ -1,9 +1,9 @@
@@ -1,9 +1,9 @@
|
||||
export let reply_kind = 1; |
||||
export const reply_kind: number = 1 |
||||
|
||||
export let pr_status_kind = 19851985; |
||||
export const pr_status_kind: number = 19851985 |
||||
|
||||
export let repo_kind = 30317; |
||||
export const repo_kind: number = 30317 |
||||
|
||||
export let pr_kind = 318; |
||||
export const pr_kind: number = 318 |
||||
|
||||
export let patch_kind = 317; |
||||
export const patch_kind: number = 317 |
||||
|
||||
@ -1,184 +1,193 @@
@@ -1,184 +1,193 @@
|
||||
import { NDKRelaySet, type NDKEvent, NDKSubscription } from "@nostr-dev-kit/ndk"; |
||||
import { writable, type Unsubscriber, type Writable } from "svelte/store" |
||||
import { ndk } from "./ndk"; |
||||
import type { User } from "$lib/components/users/type"; |
||||
import { ensureUser } from "./users"; |
||||
import { type PRFull, full_defaults, isPRStatus, type PRStatus } from "$lib/components/prs/type"; |
||||
import { pr_kind, pr_status_kind } from "$lib/kinds"; |
||||
import { ensureSelectedRepo } from "./repo"; |
||||
|
||||
export let selected_pr_full: Writable<PRFull> = writable({ ...full_defaults }); |
||||
|
||||
let selected_pr_repo_id: string = ""; |
||||
let selected_pr_id: string = ""; |
||||
let pr_summary_author_unsubsriber: Unsubscriber | undefined; |
||||
|
||||
export let selected_pr_replies: Writable<NDKEvent[]> = writable([]); |
||||
|
||||
let selected_pr_status_date = 0; |
||||
|
||||
let sub: NDKSubscription; |
||||
|
||||
let sub_replies: NDKSubscription; |
||||
|
||||
export let ensurePRFull = (repo_id: string, pr_id: string) => { |
||||
if (selected_pr_id == pr_id) return; |
||||
if (pr_id == "") { |
||||
selected_pr_full.set({ ...full_defaults }); |
||||
selected_pr_replies.set([]); |
||||
return; |
||||
} |
||||
|
||||
if (sub) sub.stop(); |
||||
if (sub_replies) sub_replies.stop(); |
||||
|
||||
selected_pr_repo_id = repo_id; |
||||
selected_pr_id = pr_id; |
||||
selected_pr_status_date = 0; |
||||
selected_pr_replies.set([]); |
||||
|
||||
selected_pr_full.set({ |
||||
...full_defaults, |
||||
summary: { |
||||
...full_defaults.summary, |
||||
id: pr_id, |
||||
repo_id: repo_id, |
||||
loading: true, |
||||
}, |
||||
loading: true, |
||||
}); |
||||
if (pr_summary_author_unsubsriber) pr_summary_author_unsubsriber(); |
||||
pr_summary_author_unsubsriber = undefined; |
||||
|
||||
new Promise(async (r) => { |
||||
let repo = await ensureSelectedRepo(repo_id); |
||||
|
||||
sub = ndk.subscribe( |
||||
{ |
||||
ids: [pr_id], |
||||
kinds: [pr_kind], |
||||
'#r': [`r-${repo_id}`], |
||||
limit: 50, |
||||
}, |
||||
{ |
||||
closeOnEose: false, |
||||
}, |
||||
NDKRelaySet.fromRelayUrls(repo.relays, ndk), |
||||
); |
||||
|
||||
sub.on("event", (event: NDKEvent) => { |
||||
try { |
||||
if (event.kind == pr_kind |
||||
&& event.getMatchingTags("r").find(t => t[1] === `r-${repo_id}`) |
||||
&& event.id == pr_id |
||||
) { |
||||
selected_pr_full.update(full => { |
||||
return { |
||||
...full, |
||||
pr_event: event, |
||||
summary: { |
||||
...full.summary, |
||||
title: event.tagValue("name") || "", |
||||
descritpion: event.tagValue("description") || "", |
||||
created_at: event.created_at, |
||||
comments: 0, |
||||
author: { |
||||
hexpubkey: event.pubkey, |
||||
loading: true, |
||||
npub: "", |
||||
}, |
||||
loading: false, |
||||
} |
||||
}; |
||||
}); |
||||
|
||||
pr_summary_author_unsubsriber = ensureUser(event.pubkey).subscribe((u: User) => { |
||||
selected_pr_full.update(full => { |
||||
return { |
||||
...full, |
||||
summary: { |
||||
...full.summary, |
||||
author: event.pubkey == u.hexpubkey ? u : full.summary.author, |
||||
} |
||||
}; |
||||
}); |
||||
}); |
||||
} |
||||
} catch { } |
||||
}); |
||||
|
||||
sub.on("eose", () => { |
||||
selected_pr_full.update(full => { |
||||
let updated = { |
||||
...full, |
||||
summary: { |
||||
...full.summary, |
||||
loading: false, |
||||
}, |
||||
}; |
||||
if (full.loading === false) { |
||||
r({ ...updated }); |
||||
} |
||||
return updated; |
||||
}); |
||||
}); |
||||
|
||||
sub_replies = ndk.subscribe( |
||||
{ |
||||
"#e": [pr_id], |
||||
}, |
||||
{ |
||||
closeOnEose: false |
||||
}, |
||||
NDKRelaySet.fromRelayUrls(repo.relays, ndk), |
||||
); |
||||
|
||||
sub_replies.on("event", (event: NDKEvent) => { |
||||
if (event.kind == pr_status_kind |
||||
&& event.created_at && selected_pr_status_date < event.created_at |
||||
&& event.getMatchingTags("l").length === 1 |
||||
&& event.getMatchingTags("l")[0].length > 1 |
||||
) { |
||||
let potential_status = event.getMatchingTags("l")[0][1]; |
||||
|
||||
if (isPRStatus(potential_status)) { |
||||
selected_pr_status_date = event.created_at; |
||||
selected_pr_full.update(full => { |
||||
return { |
||||
...full, |
||||
summary: { |
||||
...full.summary, |
||||
status: potential_status as PRStatus, |
||||
// this wont be 0 as we are ensuring it is not undefined above
|
||||
status_date: event.created_at || 0, |
||||
}, |
||||
}; |
||||
}); |
||||
} |
||||
import { NDKRelaySet, type NDKEvent, NDKSubscription } from '@nostr-dev-kit/ndk' |
||||
import { writable, type Unsubscriber, type Writable } from 'svelte/store' |
||||
import { ndk } from './ndk' |
||||
import type { User } from '$lib/components/users/type' |
||||
import { ensureUser } from './users' |
||||
import { |
||||
type PRFull, |
||||
full_defaults, |
||||
isPRStatus, |
||||
type PRStatus, |
||||
} from '$lib/components/prs/type' |
||||
import { pr_kind, pr_status_kind } from '$lib/kinds' |
||||
import { ensureSelectedRepo } from './repo' |
||||
|
||||
export const selected_pr_full: Writable<PRFull> = writable({ ...full_defaults }) |
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
let selected_pr_repo_id: string = '' |
||||
let selected_pr_id: string = '' |
||||
let pr_summary_author_unsubsriber: Unsubscriber | undefined |
||||
|
||||
export const selected_pr_replies: Writable<NDKEvent[]> = writable([]) |
||||
|
||||
let selected_pr_status_date = 0 |
||||
|
||||
let sub: NDKSubscription |
||||
|
||||
let sub_replies: NDKSubscription |
||||
|
||||
export const ensurePRFull = (repo_id: string, pr_id: string) => { |
||||
if (selected_pr_id == pr_id) return |
||||
if (pr_id == '') { |
||||
selected_pr_full.set({ ...full_defaults }) |
||||
selected_pr_replies.set([]) |
||||
return |
||||
} |
||||
|
||||
if (sub) sub.stop() |
||||
if (sub_replies) sub_replies.stop() |
||||
|
||||
selected_pr_repo_id = repo_id |
||||
selected_pr_id = pr_id |
||||
selected_pr_status_date = 0 |
||||
selected_pr_replies.set([]) |
||||
|
||||
selected_pr_full.set({ |
||||
...full_defaults, |
||||
summary: { |
||||
...full_defaults.summary, |
||||
id: pr_id, |
||||
repo_id: repo_id, |
||||
loading: true, |
||||
}, |
||||
loading: true, |
||||
}) |
||||
if (pr_summary_author_unsubsriber) pr_summary_author_unsubsriber() |
||||
pr_summary_author_unsubsriber = undefined |
||||
|
||||
new Promise(async (r) => { |
||||
const repo = await ensureSelectedRepo(repo_id) |
||||
|
||||
sub = ndk.subscribe( |
||||
{ |
||||
ids: [pr_id], |
||||
kinds: [pr_kind], |
||||
'#r': [`r-${repo_id}`], |
||||
limit: 50, |
||||
}, |
||||
{ |
||||
closeOnEose: false, |
||||
}, |
||||
NDKRelaySet.fromRelayUrls(repo.relays, ndk) |
||||
) |
||||
|
||||
sub.on('event', (event: NDKEvent) => { |
||||
try { |
||||
if ( |
||||
event.kind == pr_kind && |
||||
event.getMatchingTags('r').find((t) => t[1] === `r-${repo_id}`) && |
||||
event.id == pr_id |
||||
) { |
||||
selected_pr_full.update((full) => { |
||||
return { |
||||
...full, |
||||
pr_event: event, |
||||
summary: { |
||||
...full.summary, |
||||
title: event.tagValue('name') || '', |
||||
descritpion: event.tagValue('description') || '', |
||||
created_at: event.created_at, |
||||
comments: 0, |
||||
author: { |
||||
hexpubkey: event.pubkey, |
||||
loading: true, |
||||
npub: '', |
||||
}, |
||||
loading: false, |
||||
}, |
||||
} |
||||
selected_pr_replies.update(replies => { |
||||
return [ |
||||
...replies, |
||||
event, |
||||
]; |
||||
}); |
||||
}); |
||||
|
||||
sub_replies.on("eose", () => { |
||||
selected_pr_full.update(full => { |
||||
let updated = { |
||||
...full, |
||||
summary: { |
||||
...full.summary, |
||||
status: full.summary.status || "Open", |
||||
}, |
||||
loading: false, |
||||
}; |
||||
if (full.summary.loading === false) { |
||||
r({ ...updated }); |
||||
}) |
||||
|
||||
pr_summary_author_unsubsriber = ensureUser(event.pubkey).subscribe( |
||||
(u: User) => { |
||||
selected_pr_full.update((full) => { |
||||
return { |
||||
...full, |
||||
summary: { |
||||
...full.summary, |
||||
author: |
||||
event.pubkey == u.hexpubkey ? u : full.summary.author, |
||||
}, |
||||
} |
||||
return updated; |
||||
}); |
||||
}); |
||||
}); |
||||
}) |
||||
} |
||||
) |
||||
} |
||||
} catch {} |
||||
}) |
||||
|
||||
sub.on('eose', () => { |
||||
selected_pr_full.update((full) => { |
||||
const updated = { |
||||
...full, |
||||
summary: { |
||||
...full.summary, |
||||
loading: false, |
||||
}, |
||||
} |
||||
if (full.loading === false) { |
||||
r({ ...updated }) |
||||
} |
||||
return updated |
||||
}) |
||||
}) |
||||
|
||||
sub_replies = ndk.subscribe( |
||||
{ |
||||
'#e': [pr_id], |
||||
}, |
||||
{ |
||||
closeOnEose: false, |
||||
}, |
||||
NDKRelaySet.fromRelayUrls(repo.relays, ndk) |
||||
) |
||||
|
||||
sub_replies.on('event', (event: NDKEvent) => { |
||||
if ( |
||||
event.kind == pr_status_kind && |
||||
event.created_at && |
||||
selected_pr_status_date < event.created_at && |
||||
event.getMatchingTags('l').length === 1 && |
||||
event.getMatchingTags('l')[0].length > 1 |
||||
) { |
||||
const potential_status = event.getMatchingTags('l')[0][1] |
||||
|
||||
if (isPRStatus(potential_status)) { |
||||
selected_pr_status_date = event.created_at |
||||
selected_pr_full.update((full) => { |
||||
return { |
||||
...full, |
||||
summary: { |
||||
...full.summary, |
||||
status: potential_status as PRStatus, |
||||
// this wont be 0 as we are ensuring it is not undefined above
|
||||
status_date: event.created_at || 0, |
||||
}, |
||||
} |
||||
}) |
||||
} |
||||
} |
||||
selected_pr_replies.update((replies) => { |
||||
return [...replies, event] |
||||
}) |
||||
}) |
||||
|
||||
sub_replies.on('eose', () => { |
||||
selected_pr_full.update((full) => { |
||||
const updated = { |
||||
...full, |
||||
summary: { |
||||
...full.summary, |
||||
status: full.summary.status || 'Open', |
||||
}, |
||||
loading: false, |
||||
} |
||||
if (full.summary.loading === false) { |
||||
r({ ...updated }) |
||||
} |
||||
return updated |
||||
}) |
||||
}) |
||||
}) |
||||
} |
||||
|
||||
@ -1,173 +1,174 @@
@@ -1,173 +1,174 @@
|
||||
import { NDKRelaySet, type NDKEvent, NDKSubscription } from "@nostr-dev-kit/ndk"; |
||||
import { writable, type Unsubscriber, type Writable } from "svelte/store" |
||||
import { ndk } from "./ndk"; |
||||
import { isPRStatus, summary_defaults } from "$lib/components/prs/type"; |
||||
import type { User } from "$lib/components/users/type"; |
||||
import { ensureUser } from "./users"; |
||||
import type { PRStatus, PRSummaries } from "$lib/components/prs/type"; |
||||
import { ensureSelectedRepo } from "./repo"; |
||||
import { pr_status_kind } from "$lib/kinds"; |
||||
import type { Repo } from "$lib/components/repo/type"; |
||||
|
||||
export let pr_summaries: Writable<PRSummaries> = writable({ |
||||
id: "", |
||||
import { NDKRelaySet, type NDKEvent, NDKSubscription } from '@nostr-dev-kit/ndk' |
||||
import { writable, type Unsubscriber, type Writable } from 'svelte/store' |
||||
import { ndk } from './ndk' |
||||
import { isPRStatus, summary_defaults } from '$lib/components/prs/type' |
||||
import type { User } from '$lib/components/users/type' |
||||
import { ensureUser } from './users' |
||||
import type { PRStatus, PRSummaries } from '$lib/components/prs/type' |
||||
import { ensureSelectedRepo } from './repo' |
||||
import { pr_status_kind } from '$lib/kinds' |
||||
import type { Repo } from '$lib/components/repo/type' |
||||
|
||||
export const pr_summaries: Writable<PRSummaries> = writable({ |
||||
id: '', |
||||
summaries: [], |
||||
loading: false, |
||||
}) |
||||
|
||||
const pr_kind: number = 318 |
||||
|
||||
let selected_repo_id: string = '' |
||||
|
||||
let authors_unsubscribers: Unsubscriber[] = [] |
||||
|
||||
let sub: NDKSubscription |
||||
|
||||
export const ensurePRSummaries = async (repo_id: string) => { |
||||
if (selected_repo_id == repo_id) return |
||||
pr_summaries.set({ |
||||
id: repo_id, |
||||
summaries: [], |
||||
loading: false, |
||||
}); |
||||
|
||||
let pr_kind: number = 318; |
||||
|
||||
let selected_repo_id: string = ""; |
||||
|
||||
let authors_unsubscribers: Unsubscriber[] = []; |
||||
|
||||
let sub: NDKSubscription; |
||||
|
||||
export let ensurePRSummaries = async (repo_id: string) => { |
||||
if (selected_repo_id == repo_id) return; |
||||
pr_summaries.set({ |
||||
id: repo_id, |
||||
summaries: [], |
||||
loading: repo_id !== "", |
||||
}); |
||||
|
||||
if (sub) sub.stop(); |
||||
if (sub_statuses) sub_statuses.stop(); |
||||
authors_unsubscribers.forEach(u => u()); |
||||
authors_unsubscribers = []; |
||||
|
||||
selected_repo_id = repo_id; |
||||
|
||||
let repo = await ensureSelectedRepo(repo_id); |
||||
|
||||
sub = ndk.subscribe( |
||||
{ |
||||
kinds: [pr_kind], |
||||
'#r': [`r-${repo_id}`], |
||||
limit: 50, |
||||
}, |
||||
{ |
||||
closeOnEose: false, |
||||
}, |
||||
NDKRelaySet.fromRelayUrls(repo.relays, ndk), |
||||
); |
||||
|
||||
sub.on("event", (event: NDKEvent) => { |
||||
try { |
||||
if (event.kind == pr_kind |
||||
&& event.getMatchingTags("r").find(t => t[1] === `r-${repo_id}`) |
||||
) { |
||||
pr_summaries.update(prs => { |
||||
return { |
||||
...prs, |
||||
summaries: [ |
||||
...prs.summaries, |
||||
{ |
||||
...summary_defaults, |
||||
id: event.id, |
||||
repo_id: repo_id, |
||||
title: event.tagValue("name") || "", |
||||
descritpion: event.tagValue("description") || "", |
||||
created_at: event.created_at, |
||||
comments: 0, |
||||
author: { |
||||
hexpubkey: event.pubkey, |
||||
loading: true, |
||||
npub: "", |
||||
}, |
||||
loading: false, |
||||
} |
||||
], |
||||
} |
||||
}); |
||||
|
||||
authors_unsubscribers.push( |
||||
ensureUser(event.pubkey).subscribe((u: User) => { |
||||
pr_summaries.update(prs => { |
||||
return { |
||||
...prs, |
||||
summaries: prs.summaries.map(o => ({ |
||||
...o, |
||||
author: u, |
||||
})), |
||||
} |
||||
}); |
||||
}) |
||||
); |
||||
} |
||||
} catch { } |
||||
}); |
||||
sub.on("eose", () => { |
||||
pr_summaries.update(prs => { |
||||
getAndUpdatePRStatus(prs, repo); |
||||
return { |
||||
...prs, |
||||
loading: repo_id !== '', |
||||
}) |
||||
|
||||
if (sub) sub.stop() |
||||
if (sub_statuses) sub_statuses.stop() |
||||
authors_unsubscribers.forEach((u) => u()) |
||||
authors_unsubscribers = [] |
||||
|
||||
selected_repo_id = repo_id |
||||
|
||||
const repo = await ensureSelectedRepo(repo_id) |
||||
|
||||
sub = ndk.subscribe( |
||||
{ |
||||
kinds: [pr_kind], |
||||
'#r': [`r-${repo_id}`], |
||||
limit: 50, |
||||
}, |
||||
{ |
||||
closeOnEose: false, |
||||
}, |
||||
NDKRelaySet.fromRelayUrls(repo.relays, ndk) |
||||
) |
||||
|
||||
sub.on('event', (event: NDKEvent) => { |
||||
try { |
||||
if ( |
||||
event.kind == pr_kind && |
||||
event.getMatchingTags('r').find((t) => t[1] === `r-${repo_id}`) |
||||
) { |
||||
pr_summaries.update((prs) => { |
||||
return { |
||||
...prs, |
||||
summaries: [ |
||||
...prs.summaries, |
||||
{ |
||||
...summary_defaults, |
||||
id: event.id, |
||||
repo_id: repo_id, |
||||
title: event.tagValue('name') || '', |
||||
descritpion: event.tagValue('description') || '', |
||||
created_at: event.created_at, |
||||
comments: 0, |
||||
author: { |
||||
hexpubkey: event.pubkey, |
||||
loading: true, |
||||
npub: '', |
||||
}, |
||||
loading: false, |
||||
}; |
||||
}); |
||||
}); |
||||
}, |
||||
], |
||||
} |
||||
}) |
||||
|
||||
authors_unsubscribers.push( |
||||
ensureUser(event.pubkey).subscribe((u: User) => { |
||||
pr_summaries.update((prs) => { |
||||
return { |
||||
...prs, |
||||
summaries: prs.summaries.map((o) => ({ |
||||
...o, |
||||
author: u, |
||||
})), |
||||
} |
||||
}) |
||||
}) |
||||
) |
||||
} |
||||
} catch {} |
||||
}) |
||||
sub.on('eose', () => { |
||||
pr_summaries.update((prs) => { |
||||
getAndUpdatePRStatus(prs, repo) |
||||
return { |
||||
...prs, |
||||
loading: false, |
||||
} |
||||
}) |
||||
}) |
||||
} |
||||
|
||||
let sub_statuses: NDKSubscription; |
||||
let sub_statuses: NDKSubscription |
||||
|
||||
function getAndUpdatePRStatus(prs: PRSummaries, repo: Repo): void { |
||||
if (sub_statuses) sub_statuses.stop(); |
||||
sub_statuses = ndk.subscribe( |
||||
{ |
||||
kinds: [pr_status_kind], |
||||
"#e": prs.summaries.map(pr => pr.id), |
||||
'#r': [`r-${prs.id}`], |
||||
}, |
||||
{ |
||||
closeOnEose: false, |
||||
}, |
||||
NDKRelaySet.fromRelayUrls(repo.relays, ndk), |
||||
); |
||||
sub_statuses.on("event", (event: NDKEvent) => { |
||||
let tagged_pr_event = event.tagValue('e'); |
||||
if (event.kind == pr_status_kind |
||||
&& tagged_pr_event |
||||
&& event.created_at |
||||
&& event.getMatchingTags("l").length === 1 |
||||
&& event.getMatchingTags("l")[0].length > 1 |
||||
) { |
||||
let potential_status = event.getMatchingTags("l")[0][1]; |
||||
|
||||
if (isPRStatus(potential_status)) { |
||||
pr_summaries.update(prs => { |
||||
return { |
||||
...prs, |
||||
summaries: prs.summaries.map(o => { |
||||
if ( |
||||
o.id === tagged_pr_event |
||||
&& event.created_at |
||||
&& o.status_date < event.created_at |
||||
) { |
||||
return { |
||||
...o, |
||||
status: potential_status as PRStatus, |
||||
status_date: event.created_at, |
||||
} |
||||
} |
||||
|
||||
return o; |
||||
}), |
||||
} |
||||
}); |
||||
} |
||||
} |
||||
}); |
||||
|
||||
sub_statuses.on("eose", () => { |
||||
pr_summaries.update(prs => { |
||||
return { |
||||
...prs, |
||||
summaries: prs.summaries.map(o => ({ |
||||
...o, |
||||
status: o.status || "Open", |
||||
})), |
||||
} |
||||
}); |
||||
}); |
||||
|
||||
} |
||||
if (sub_statuses) sub_statuses.stop() |
||||
sub_statuses = ndk.subscribe( |
||||
{ |
||||
kinds: [pr_status_kind], |
||||
'#e': prs.summaries.map((pr) => pr.id), |
||||
'#r': [`r-${prs.id}`], |
||||
}, |
||||
{ |
||||
closeOnEose: false, |
||||
}, |
||||
NDKRelaySet.fromRelayUrls(repo.relays, ndk) |
||||
) |
||||
sub_statuses.on('event', (event: NDKEvent) => { |
||||
const tagged_pr_event = event.tagValue('e') |
||||
if ( |
||||
event.kind == pr_status_kind && |
||||
tagged_pr_event && |
||||
event.created_at && |
||||
event.getMatchingTags('l').length === 1 && |
||||
event.getMatchingTags('l')[0].length > 1 |
||||
) { |
||||
const potential_status = event.getMatchingTags('l')[0][1] |
||||
|
||||
if (isPRStatus(potential_status)) { |
||||
pr_summaries.update((prs) => { |
||||
return { |
||||
...prs, |
||||
summaries: prs.summaries.map((o) => { |
||||
if ( |
||||
o.id === tagged_pr_event && |
||||
event.created_at && |
||||
o.status_date < event.created_at |
||||
) { |
||||
return { |
||||
...o, |
||||
status: potential_status as PRStatus, |
||||
status_date: event.created_at, |
||||
} |
||||
} |
||||
|
||||
return o |
||||
}), |
||||
} |
||||
}) |
||||
} |
||||
} |
||||
}) |
||||
|
||||
sub_statuses.on('eose', () => { |
||||
pr_summaries.update((prs) => { |
||||
return { |
||||
...prs, |
||||
summaries: prs.summaries.map((o) => ({ |
||||
...o, |
||||
status: o.status || 'Open', |
||||
})), |
||||
} |
||||
}) |
||||
}) |
||||
} |
||||
|
||||
@ -1,24 +1,31 @@
@@ -1,24 +1,31 @@
|
||||
import NDKSvelte from '@nostr-dev-kit/ndk-svelte'; |
||||
import NDKSvelte from '@nostr-dev-kit/ndk-svelte' |
||||
|
||||
export let base_relays = import.meta.env.DEV |
||||
? [ |
||||
"ws://localhost:8080", |
||||
] |
||||
: [ |
||||
"wss://relayable.org", |
||||
"wss://relay.f7z.io", |
||||
"wss://relay.damus.io", |
||||
"wss://nos.lol" |
||||
// "wss://nostr.wine/",
|
||||
// "wss://eden.nostr.land/",
|
||||
// "wss://relay.nostr.band/",
|
||||
// export let base_relays = import.meta.env.DEV
|
||||
// ? ["ws://localhost:8080"]
|
||||
// : [
|
||||
// "wss://relayable.org",
|
||||
// "wss://relay.f7z.io",
|
||||
// "wss://relay.damus.io",
|
||||
// "wss://nos.lol",
|
||||
// "wss://nostr.wine/",
|
||||
// "wss://eden.nostr.land/",
|
||||
// "wss://relay.nostr.band/",
|
||||
// ];
|
||||
|
||||
]; |
||||
export const base_relays = [ |
||||
'wss://relayable.org', |
||||
'wss://relay.f7z.io', |
||||
'wss://relay.damus.io', |
||||
'wss://nos.lol', |
||||
'wss://nostr.wine/', |
||||
'wss://eden.nostr.land/', |
||||
'wss://relay.nostr.band/', |
||||
] |
||||
|
||||
// TODO: fallback_relays for if profile cannot be found
|
||||
|
||||
export const ndk = new NDKSvelte({ |
||||
explicitRelayUrls: [...base_relays], |
||||
}); |
||||
explicitRelayUrls: [...base_relays], |
||||
}) |
||||
|
||||
ndk.connect(); |
||||
ndk.connect() |
||||
|
||||
@ -1,103 +1,99 @@
@@ -1,103 +1,99 @@
|
||||
import { NDKRelaySet, NDKSubscription } from "@nostr-dev-kit/ndk"; |
||||
import { writable, type Unsubscriber, type Writable, get } from "svelte/store" |
||||
import { base_relays, ndk } from "./ndk"; |
||||
import type { Repo } from "$lib/components/repo/type"; |
||||
import { defaults } from "$lib/components/repo/type"; |
||||
import type { User } from "$lib/components/users/type"; |
||||
import { ensureUser, users } from "./users"; |
||||
import { repo_kind } from "$lib/kinds"; |
||||
import { NDKRelaySet, NDKSubscription } from '@nostr-dev-kit/ndk' |
||||
import { writable, type Unsubscriber, type Writable } from 'svelte/store' |
||||
import { base_relays, ndk } from './ndk' |
||||
import type { Repo } from '$lib/components/repo/type' |
||||
import { defaults } from '$lib/components/repo/type' |
||||
import type { User } from '$lib/components/users/type' |
||||
import { ensureUser } from './users' |
||||
import { repo_kind } from '$lib/kinds' |
||||
|
||||
export let selected_repo: Writable<Repo> = writable({ ...defaults }); |
||||
let selected_repo_id: string = ""; |
||||
export const selected_repo: Writable<Repo> = writable({ ...defaults }) |
||||
let selected_repo_id: string = '' |
||||
|
||||
let maintainers_unsubscribers: Unsubscriber[] = []; |
||||
|
||||
let sub: NDKSubscription; |
||||
export let ensureSelectedRepo = async (repo_id: string): Promise<Repo> => { |
||||
if (selected_repo_id == repo_id) { |
||||
return new Promise(r => { |
||||
let unsubscriber = selected_repo.subscribe(repo => { |
||||
if (repo.repo_id === repo_id && !repo.loading) { |
||||
setTimeout(() => { |
||||
unsubscriber(); |
||||
}, 5); |
||||
r({ ...repo }); |
||||
} |
||||
}); |
||||
|
||||
}) |
||||
} |
||||
selected_repo_id = repo_id; |
||||
|
||||
if (sub) sub.stop(); |
||||
sub = ndk.subscribe( |
||||
{ |
||||
kinds: [repo_kind], |
||||
'#d': [repo_id], |
||||
limit: 1, |
||||
}, |
||||
{ |
||||
closeOnEose: false, |
||||
}, |
||||
NDKRelaySet.fromRelayUrls(base_relays, ndk), |
||||
); |
||||
let maintainers_unsubscribers: Unsubscriber[] = [] |
||||
|
||||
let sub: NDKSubscription |
||||
export const ensureSelectedRepo = async (repo_id: string): Promise<Repo> => { |
||||
if (selected_repo_id == repo_id) { |
||||
return new Promise((r) => { |
||||
sub.on("event", (event) => { |
||||
try { |
||||
if (event.kind == repo_kind && event.tagValue("d") == repo_id) { |
||||
selected_repo.set({ |
||||
loading: false, |
||||
repo_id: event.replaceableDTag(), |
||||
name: event.tagValue("name") || "", |
||||
description: event.tagValue("description") || "", |
||||
git_server: event.tagValue("git-server") || "", |
||||
tags: event.getMatchingTags("t") || [], |
||||
maintainers: event.getMatchingTags("p").map( |
||||
(t: string[]) => |
||||
({ |
||||
hexpubkey: t[1], |
||||
loading: true, |
||||
npub: "", |
||||
}) as User, |
||||
), |
||||
relays: event |
||||
.getMatchingTags("relay") |
||||
.map((t: string[]) => t[1]), |
||||
}); |
||||
let old_unsubscribers = maintainers_unsubscribers; |
||||
maintainers_unsubscribers = event |
||||
.getMatchingTags("p") |
||||
.map((t: string[]) => { |
||||
return ensureUser(t[1]).subscribe((u: User) => { |
||||
selected_repo.update((repo) => { |
||||
return { |
||||
...repo, |
||||
maintainers: repo.maintainers.map((m) => { |
||||
if (m.hexpubkey == u.hexpubkey) return { ...u }; |
||||
else return { ...m }; |
||||
}), |
||||
}; |
||||
}); |
||||
}) |
||||
}); |
||||
old_unsubscribers.forEach((unsubscriber) => unsubscriber()); |
||||
} |
||||
} catch { } |
||||
}); |
||||
const unsubscriber = selected_repo.subscribe((repo) => { |
||||
if (repo.repo_id === repo_id && !repo.loading) { |
||||
setTimeout(() => { |
||||
unsubscriber() |
||||
}, 5) |
||||
r({ ...repo }) |
||||
} |
||||
}) |
||||
}) |
||||
} |
||||
selected_repo_id = repo_id |
||||
|
||||
sub.on("eose", () => { |
||||
selected_repo.update((repo) => { |
||||
r({ |
||||
...repo, |
||||
loading: false, |
||||
}); |
||||
return { |
||||
if (sub) sub.stop() |
||||
sub = ndk.subscribe( |
||||
{ |
||||
kinds: [repo_kind], |
||||
'#d': [repo_id], |
||||
limit: 1, |
||||
}, |
||||
{ |
||||
closeOnEose: false, |
||||
}, |
||||
NDKRelaySet.fromRelayUrls(base_relays, ndk) |
||||
) |
||||
|
||||
return new Promise((r) => { |
||||
sub.on('event', (event) => { |
||||
try { |
||||
if (event.kind == repo_kind && event.tagValue('d') == repo_id) { |
||||
selected_repo.set({ |
||||
loading: false, |
||||
repo_id: event.replaceableDTag(), |
||||
name: event.tagValue('name') || '', |
||||
description: event.tagValue('description') || '', |
||||
git_server: event.tagValue('git-server') || '', |
||||
tags: event.getMatchingTags('t') || [], |
||||
maintainers: event.getMatchingTags('p').map( |
||||
(t: string[]) => |
||||
({ |
||||
hexpubkey: t[1], |
||||
loading: true, |
||||
npub: '', |
||||
}) as User |
||||
), |
||||
relays: event.getMatchingTags('relay').map((t: string[]) => t[1]), |
||||
}) |
||||
const old_unsubscribers = maintainers_unsubscribers |
||||
maintainers_unsubscribers = event |
||||
.getMatchingTags('p') |
||||
.map((t: string[]) => { |
||||
return ensureUser(t[1]).subscribe((u: User) => { |
||||
selected_repo.update((repo) => { |
||||
return { |
||||
...repo, |
||||
loading: false, |
||||
} |
||||
maintainers: repo.maintainers.map((m) => { |
||||
if (m.hexpubkey == u.hexpubkey) return { ...u } |
||||
else return { ...m } |
||||
}), |
||||
} |
||||
}) |
||||
}) |
||||
}) |
||||
}); |
||||
}); |
||||
old_unsubscribers.forEach((unsubscriber) => unsubscriber()) |
||||
} |
||||
} catch {} |
||||
}) |
||||
|
||||
sub.on('eose', () => { |
||||
selected_repo.update((repo) => { |
||||
r({ |
||||
...repo, |
||||
loading: false, |
||||
}) |
||||
return { |
||||
...repo, |
||||
loading: false, |
||||
} |
||||
}) |
||||
}) |
||||
}) |
||||
} |
||||
|
||||
@ -1,137 +1,137 @@
@@ -1,137 +1,137 @@
|
||||
import { defaults as user_defaults, type User } from "$lib/components/users/type"; |
||||
import { NDKNip07Signer, NDKRelayList } from "@nostr-dev-kit/ndk"; |
||||
import { get, writable, type Unsubscriber, type Writable } from "svelte/store" |
||||
import { ndk } from "./ndk"; |
||||
import { |
||||
defaults as user_defaults, |
||||
type User, |
||||
} from '$lib/components/users/type' |
||||
import { NDKNip07Signer, NDKRelayList } from '@nostr-dev-kit/ndk' |
||||
import { get, writable, type Unsubscriber, type Writable } from 'svelte/store' |
||||
import { ndk } from './ndk' |
||||
|
||||
export let users: { [hexpubkey: string]: Writable<User>; } = {}; |
||||
export const users: { [hexpubkey: string]: Writable<User> } = {} |
||||
|
||||
export let ensureUser = (hexpubkey: string): Writable<User> => { |
||||
if (!users[hexpubkey]) { |
||||
let u = ndk.getUser({ hexpubkey }); |
||||
export const ensureUser = (hexpubkey: string): Writable<User> => { |
||||
if (!users[hexpubkey]) { |
||||
const u = ndk.getUser({ hexpubkey }) |
||||
|
||||
let base: User = { |
||||
loading: false, |
||||
hexpubkey, |
||||
npub: u.npub, |
||||
}; |
||||
|
||||
users[hexpubkey] = writable(base); |
||||
getUserRelays(hexpubkey); |
||||
u.fetchProfile().then( |
||||
(p) => { |
||||
users[hexpubkey].update((u) => ({ |
||||
...u, |
||||
loading: false, |
||||
profile: p === null ? undefined : p, |
||||
})); |
||||
}, |
||||
() => { |
||||
users[hexpubkey].update((u) => ({ |
||||
...u, |
||||
loading: false, |
||||
})); |
||||
} |
||||
); |
||||
const base: User = { |
||||
loading: false, |
||||
hexpubkey, |
||||
npub: u.npub, |
||||
} |
||||
return users[hexpubkey]; |
||||
|
||||
users[hexpubkey] = writable(base) |
||||
getUserRelays(hexpubkey) |
||||
u.fetchProfile().then( |
||||
(p) => { |
||||
users[hexpubkey].update((u) => ({ |
||||
...u, |
||||
loading: false, |
||||
profile: p === null ? undefined : p, |
||||
})) |
||||
}, |
||||
() => { |
||||
users[hexpubkey].update((u) => ({ |
||||
...u, |
||||
loading: false, |
||||
})) |
||||
} |
||||
) |
||||
} |
||||
return users[hexpubkey] |
||||
} |
||||
|
||||
export let returnUser = async (hexpubkey: string): Promise<User> => { |
||||
return new Promise((r) => { |
||||
let unsubscriber = ensureUser(hexpubkey).subscribe((u) => { |
||||
if (!u.loading) { |
||||
unsubscriber(); |
||||
r(u); |
||||
} |
||||
}); |
||||
}); |
||||
export const returnUser = async (hexpubkey: string): Promise<User> => { |
||||
return new Promise((r) => { |
||||
const unsubscriber = ensureUser(hexpubkey).subscribe((u) => { |
||||
if (!u.loading) { |
||||
unsubscriber() |
||||
r(u) |
||||
} |
||||
}) |
||||
}) |
||||
} |
||||
|
||||
// nip07_plugin is set in Navbar component
|
||||
export let nip07_plugin: Writable<undefined | boolean> = writable(undefined); |
||||
|
||||
export let checkForNip07Plugin = () => { |
||||
if (window.nostr) { |
||||
nip07_plugin.set(true); |
||||
} else { |
||||
let timerId: NodeJS.Timeout; |
||||
const intervalId = setInterval(() => { |
||||
if (window.nostr) { |
||||
clearTimeout(timerId); |
||||
clearInterval(intervalId); |
||||
nip07_plugin.set(true); |
||||
} |
||||
}, 100); |
||||
timerId = setTimeout(() => { |
||||
clearInterval(intervalId); |
||||
nip07_plugin.set(false); |
||||
}, 5000); |
||||
} |
||||
export const nip07_plugin: Writable<undefined | boolean> = writable(undefined) |
||||
|
||||
}; |
||||
export const checkForNip07Plugin = () => { |
||||
if (window.nostr) { |
||||
nip07_plugin.set(true) |
||||
} else { |
||||
let timerId: NodeJS.Timeout | undefined = undefined |
||||
const intervalId = setInterval(() => { |
||||
if (window.nostr) { |
||||
clearTimeout(timerId) |
||||
clearInterval(intervalId) |
||||
nip07_plugin.set(true) |
||||
} |
||||
}, 100) |
||||
timerId = setTimeout(() => { |
||||
clearInterval(intervalId) |
||||
nip07_plugin.set(false) |
||||
}, 5000) |
||||
} |
||||
} |
||||
|
||||
let signer = new NDKNip07Signer(2000); |
||||
const signer = new NDKNip07Signer(2000) |
||||
|
||||
export let logged_in_user: Writable<undefined | User> = writable(undefined); |
||||
export const logged_in_user: Writable<undefined | User> = writable(undefined) |
||||
|
||||
export let login = async (): Promise<void> => { |
||||
return new Promise(async (res, rej) => { |
||||
let user = get(logged_in_user); |
||||
if (user) return res(); |
||||
if (get(nip07_plugin)) { |
||||
try { |
||||
let ndk_user = await signer.blockUntilReady(); |
||||
logged_in_user.set({ |
||||
...user_defaults, |
||||
hexpubkey: ndk_user.pubkey, |
||||
}); |
||||
ndk.signer = signer; |
||||
ensureUser(ndk_user.pubkey).subscribe(user => { |
||||
logged_in_user.set({ ...user }); |
||||
}); |
||||
return res(); |
||||
} |
||||
catch (e) { |
||||
alert(e); |
||||
rej(); |
||||
} |
||||
} |
||||
else { |
||||
rej(); |
||||
} |
||||
}); |
||||
}; |
||||
export const login = async (): Promise<void> => { |
||||
return new Promise(async (res, rej) => { |
||||
const user = get(logged_in_user) |
||||
if (user) return res() |
||||
if (get(nip07_plugin)) { |
||||
try { |
||||
const ndk_user = await signer.blockUntilReady() |
||||
logged_in_user.set({ |
||||
...user_defaults, |
||||
hexpubkey: ndk_user.pubkey, |
||||
}) |
||||
ndk.signer = signer |
||||
ensureUser(ndk_user.pubkey).subscribe((user) => { |
||||
logged_in_user.set({ ...user }) |
||||
}) |
||||
return res() |
||||
} catch (e) { |
||||
alert(e) |
||||
rej() |
||||
} |
||||
} else { |
||||
rej() |
||||
} |
||||
}) |
||||
} |
||||
|
||||
interface UserRelays { |
||||
loading: boolean; |
||||
ndk_relays: NDKRelayList | undefined; |
||||
loading: boolean |
||||
ndk_relays: NDKRelayList | undefined |
||||
} |
||||
|
||||
export let user_relays: { [hexpubkey: string]: Writable<UserRelays>; } = {}; |
||||
export const user_relays: { [hexpubkey: string]: Writable<UserRelays> } = {} |
||||
|
||||
export let getUserRelays = async (hexpubkey: string): Promise<UserRelays> => { |
||||
return new Promise(async (res, rej) => { |
||||
if (user_relays[hexpubkey]) { |
||||
let unsubscriber: Unsubscriber; |
||||
unsubscriber = user_relays[hexpubkey].subscribe(querying_user_relays => { |
||||
if (querying_user_relays && !querying_user_relays.loading) { |
||||
res(querying_user_relays); |
||||
if (unsubscriber) unsubscriber(); |
||||
} |
||||
}); |
||||
} |
||||
else { |
||||
user_relays[hexpubkey] = writable({ |
||||
loading: true, |
||||
ndk_relays: undefined, |
||||
}); |
||||
let relay_list = await ndk.getUser({ hexpubkey }).relayList(); |
||||
let querying_user_relays = { |
||||
loading: false, |
||||
ndk_relays: relay_list, |
||||
}; |
||||
user_relays[hexpubkey].set({ ...querying_user_relays }); |
||||
res(querying_user_relays); |
||||
export const getUserRelays = async (hexpubkey: string): Promise<UserRelays> => { |
||||
return new Promise(async (res, _) => { |
||||
if (user_relays[hexpubkey]) { |
||||
const unsubscriber: Unsubscriber = user_relays[hexpubkey].subscribe( |
||||
(querying_user_relays) => { |
||||
if (querying_user_relays && !querying_user_relays.loading) { |
||||
res(querying_user_relays) |
||||
if (unsubscriber) unsubscriber() |
||||
} |
||||
} |
||||
}); |
||||
}; |
||||
) |
||||
} else { |
||||
user_relays[hexpubkey] = writable({ |
||||
loading: true, |
||||
ndk_relays: undefined, |
||||
}) |
||||
const relay_list = await ndk.getUser({ hexpubkey }).relayList() |
||||
const querying_user_relays = { |
||||
loading: false, |
||||
ndk_relays: relay_list, |
||||
} |
||||
user_relays[hexpubkey].set({ ...querying_user_relays }) |
||||
res(querying_user_relays) |
||||
} |
||||
}) |
||||
} |
||||
|
||||
@ -1,76 +1,74 @@
@@ -1,76 +1,74 @@
|
||||
<script lang="ts"> |
||||
import { ndk } from "$lib/stores/ndk"; |
||||
import { NDKEvent, NDKRelaySet, type NDKTag } from "@nostr-dev-kit/ndk"; |
||||
import { reply_kind } from "$lib/kinds"; |
||||
import { getUserRelays, logged_in_user } from "$lib/stores/users"; |
||||
import { selected_repo } from "$lib/stores/repo"; |
||||
import Compose from "$lib/components/events/Compose.svelte"; |
||||
import { selected_pr_full } from "$lib/stores/PR"; |
||||
import { ndk } from '$lib/stores/ndk' |
||||
import { NDKEvent, NDKRelaySet } from '@nostr-dev-kit/ndk' |
||||
import { reply_kind } from '$lib/kinds' |
||||
import { getUserRelays, logged_in_user } from '$lib/stores/users' |
||||
import { selected_repo } from '$lib/stores/repo' |
||||
import Compose from '$lib/components/events/Compose.svelte' |
||||
import { selected_pr_full } from '$lib/stores/PR' |
||||
|
||||
export let reply_to_event_id = ""; |
||||
export let reply_to_event_id = '' |
||||
|
||||
let repo_id: string; |
||||
let pr_id: string; |
||||
let repo_id: string |
||||
let pr_id: string |
||||
|
||||
let submitting = false; |
||||
let submitted = false; |
||||
let edit_mode = false; |
||||
$: { |
||||
repo_id = $selected_repo.repo_id; |
||||
pr_id = $selected_pr_full.summary.id; |
||||
let submitting = false |
||||
let submitted = false |
||||
let edit_mode = false |
||||
$: { |
||||
repo_id = $selected_repo.repo_id |
||||
pr_id = $selected_pr_full.summary.id |
||||
|
||||
edit_mode = |
||||
$logged_in_user !== undefined && |
||||
repo_id.length > 0 && |
||||
pr_id.length > 0 && |
||||
!submitted; |
||||
} |
||||
edit_mode = |
||||
$logged_in_user !== undefined && |
||||
repo_id.length > 0 && |
||||
pr_id.length > 0 && |
||||
!submitted |
||||
} |
||||
|
||||
async function sendReply(content: string) { |
||||
if (!$logged_in_user) return; |
||||
let event = new NDKEvent(ndk); |
||||
event.kind = reply_kind; |
||||
event.tags.push(["e", pr_id, "root"]); |
||||
if (reply_to_event_id.length > 0) { |
||||
event.tags.push(["e", pr_id, "reply"]); |
||||
} |
||||
event.tags.push(["r", `r-${repo_id}`]); |
||||
event.content = content; |
||||
submitting = true; |
||||
let relays = [...$selected_repo.relays]; |
||||
try { |
||||
event.sign(); |
||||
} catch { |
||||
alert("failed to sign event"); |
||||
} |
||||
try { |
||||
let user_relays = await getUserRelays($logged_in_user.hexpubkey); |
||||
relays = [ |
||||
...relays, |
||||
...(user_relays.ndk_relays |
||||
? user_relays.ndk_relays.writeRelayUrls |
||||
: []), |
||||
// TODO: pr event pubkey relays |
||||
]; |
||||
} catch { |
||||
alert("failed to get user relays"); |
||||
} |
||||
try { |
||||
let res = await event.publish( |
||||
NDKRelaySet.fromRelayUrls(relays, ndk), |
||||
); |
||||
submitting = false; |
||||
submitted = true; |
||||
setTimeout(() => { |
||||
submitted = false; |
||||
}, 5000); |
||||
} catch {} |
||||
async function sendReply(content: string) { |
||||
if (!$logged_in_user) return |
||||
let event = new NDKEvent(ndk) |
||||
event.kind = reply_kind |
||||
event.tags.push(['e', pr_id, 'root']) |
||||
if (reply_to_event_id.length > 0) { |
||||
event.tags.push(['e', pr_id, 'reply']) |
||||
} |
||||
event.tags.push(['r', `r-${repo_id}`]) |
||||
event.content = content |
||||
submitting = true |
||||
let relays = [...$selected_repo.relays] |
||||
try { |
||||
event.sign() |
||||
} catch { |
||||
alert('failed to sign event') |
||||
} |
||||
try { |
||||
let user_relays = await getUserRelays($logged_in_user.hexpubkey) |
||||
relays = [ |
||||
...relays, |
||||
...(user_relays.ndk_relays |
||||
? user_relays.ndk_relays.writeRelayUrls |
||||
: []), |
||||
// TODO: pr event pubkey relays |
||||
] |
||||
} catch { |
||||
alert('failed to get user relays') |
||||
} |
||||
try { |
||||
let _ = await event.publish(NDKRelaySet.fromRelayUrls(relays, ndk)) |
||||
submitting = false |
||||
submitted = true |
||||
setTimeout(() => { |
||||
submitted = false |
||||
}, 5000) |
||||
} catch {} |
||||
} |
||||
</script> |
||||
|
||||
{#if edit_mode} |
||||
<Compose {sendReply} {submitting} /> |
||||
<Compose {sendReply} {submitting} /> |
||||
{/if} |
||||
{#if submitted} |
||||
<div>sent!</div> |
||||
<div>sent!</div> |
||||
{/if} |
||||
|
||||
@ -1,37 +1,36 @@
@@ -1,37 +1,36 @@
|
||||
<script lang="ts"> |
||||
import EventWrapper from "$lib/components/events/EventWrapper.svelte"; |
||||
import Kind19851985 from "$lib/components/events/content/Kind19851985.svelte"; |
||||
import Kind317 from "$lib/components/events/content/Kind317.svelte"; |
||||
import ParsedContent from "$lib/components/events/content/ParsedContent.svelte"; |
||||
import type { User } from "$lib/components/users/type"; |
||||
import { defaults as user_defaults } from "$lib/components/users/type"; |
||||
import { patch_kind, pr_status_kind } from "$lib/kinds"; |
||||
import { ensureUser } from "$lib/stores/users"; |
||||
import type { NDKEvent } from "@nostr-dev-kit/ndk"; |
||||
import { onDestroy } from "svelte"; |
||||
import { writable } from "svelte/store"; |
||||
import EventWrapper from '$lib/components/events/EventWrapper.svelte' |
||||
import Kind19851985 from '$lib/components/events/content/Kind19851985.svelte' |
||||
import Kind317 from '$lib/components/events/content/Kind317.svelte' |
||||
import ParsedContent from '$lib/components/events/content/ParsedContent.svelte' |
||||
import { defaults as user_defaults } from '$lib/components/users/type' |
||||
import { patch_kind, pr_status_kind } from '$lib/kinds' |
||||
import { ensureUser } from '$lib/stores/users' |
||||
import type { NDKEvent } from '@nostr-dev-kit/ndk' |
||||
import { onDestroy } from 'svelte' |
||||
import { writable } from 'svelte/store' |
||||
|
||||
export let event: NDKEvent; |
||||
export let event: NDKEvent |
||||
|
||||
let author = writable({ ...user_defaults }); |
||||
let author_unsubsriber = ensureUser(event.pubkey).subscribe((u) => { |
||||
author.set({ ...u }); |
||||
}); |
||||
onDestroy(() => { |
||||
author_unsubsriber(); |
||||
}); |
||||
let author = writable({ ...user_defaults }) |
||||
let author_unsubsriber = ensureUser(event.pubkey).subscribe((u) => { |
||||
author.set({ ...u }) |
||||
}) |
||||
onDestroy(() => { |
||||
author_unsubsriber() |
||||
}) |
||||
</script> |
||||
|
||||
<EventWrapper |
||||
author={$author} |
||||
created_at={event.created_at} |
||||
event_id={event.id} |
||||
author={$author} |
||||
created_at={event.created_at} |
||||
event_id={event.id} |
||||
> |
||||
{#if event.kind == patch_kind} |
||||
<Kind317 content={event.content} tags={event.tags} /> |
||||
{:else if event.kind === pr_status_kind} |
||||
<Kind19851985 tags={event.tags} /> |
||||
{:else} |
||||
<ParsedContent content={event.content} tags={event.tags} /> |
||||
{/if} |
||||
{#if event.kind == patch_kind} |
||||
<Kind317 content={event.content} tags={event.tags} /> |
||||
{:else if event.kind === pr_status_kind} |
||||
<Kind19851985 tags={event.tags} /> |
||||
{:else} |
||||
<ParsedContent content={event.content} tags={event.tags} /> |
||||
{/if} |
||||
</EventWrapper> |
||||
|
||||
@ -1,25 +1,23 @@
@@ -1,25 +1,23 @@
|
||||
<script lang="ts"> |
||||
import { |
||||
checkForNip07Plugin, |
||||
logged_in_user, |
||||
login, |
||||
nip07_plugin, |
||||
} from "$lib/stores/users"; |
||||
import { onMount } from "svelte"; |
||||
import Navbar from "$lib/components/Navbar.svelte"; |
||||
import { |
||||
checkForNip07Plugin, |
||||
logged_in_user, |
||||
login, |
||||
nip07_plugin, |
||||
} from '$lib/stores/users' |
||||
import { onMount } from 'svelte' |
||||
import Navbar from '$lib/components/Navbar.svelte' |
||||
|
||||
let singup_function: Function = () => { |
||||
alert( |
||||
"a NIP-07 browser extension is required. currently no signup page", |
||||
); |
||||
}; |
||||
let singup_function = () => { |
||||
alert('a NIP-07 browser extension is required. currently no signup page') |
||||
} |
||||
|
||||
onMount(checkForNip07Plugin); |
||||
onMount(checkForNip07Plugin) |
||||
</script> |
||||
|
||||
<Navbar |
||||
logged_in_user={$logged_in_user} |
||||
nip07_plugin={$nip07_plugin} |
||||
login_function={login} |
||||
{singup_function} |
||||
logged_in_user={$logged_in_user} |
||||
nip07_plugin={$nip07_plugin} |
||||
login_function={login} |
||||
{singup_function} |
||||
/> |
||||
|
||||
@ -1,14 +1,14 @@
@@ -1,14 +1,14 @@
|
||||
<script lang="ts"> |
||||
import PRsList from "$lib/components/prs/PRsList.svelte"; |
||||
import { ensurePRSummaries, pr_summaries } from "$lib/stores/PRs"; |
||||
import PRsList from '$lib/components/prs/PRsList.svelte' |
||||
import { ensurePRSummaries, pr_summaries } from '$lib/stores/PRs' |
||||
|
||||
export let repo_id: string = ""; |
||||
export let repo_id: string = '' |
||||
|
||||
ensurePRSummaries(repo_id); |
||||
ensurePRSummaries(repo_id) |
||||
</script> |
||||
|
||||
<PRsList |
||||
title="Open PRs" |
||||
prs={$pr_summaries.summaries} |
||||
loading={$pr_summaries.loading} |
||||
title="Open PRs" |
||||
prs={$pr_summaries.summaries} |
||||
loading={$pr_summaries.loading} |
||||
/> |
||||
|
||||
@ -1,10 +1,10 @@
@@ -1,10 +1,10 @@
|
||||
<script lang="ts"> |
||||
import RepoDetails from "$lib/components/repo/RepoDetails.svelte"; |
||||
import { ensureSelectedRepo, selected_repo } from "$lib/stores/repo"; |
||||
import RepoDetails from '$lib/components/repo/RepoDetails.svelte' |
||||
import { ensureSelectedRepo, selected_repo } from '$lib/stores/repo' |
||||
|
||||
export let repo_id = ""; |
||||
export let repo_id = '' |
||||
|
||||
ensureSelectedRepo(repo_id); |
||||
ensureSelectedRepo(repo_id) |
||||
</script> |
||||
|
||||
<RepoDetails {...$selected_repo} /> |
||||
|
||||
@ -1,55 +1,55 @@
@@ -1,55 +1,55 @@
|
||||
<script lang="ts"> |
||||
import type { Args } from "$lib/components/RepoSummaryCard.svelte"; |
||||
import ReposSummaryList from "$lib/components/ReposSummaryList.svelte"; |
||||
import { repo_kind } from "$lib/kinds"; |
||||
import { ndk } from "$lib/stores/ndk"; |
||||
import type { NDKEvent } from "@nostr-dev-kit/ndk"; |
||||
import { onDestroy } from "svelte"; |
||||
import type { Args } from '$lib/components/RepoSummaryCard.svelte' |
||||
import ReposSummaryList from '$lib/components/ReposSummaryList.svelte' |
||||
import { repo_kind } from '$lib/kinds' |
||||
import { ndk } from '$lib/stores/ndk' |
||||
import type { NDKEvent } from '@nostr-dev-kit/ndk' |
||||
import { onDestroy } from 'svelte' |
||||
|
||||
export let limit: number = 10; |
||||
export let limit: number = 10 |
||||
|
||||
let repos: Args[] = []; |
||||
let loading: boolean = true; |
||||
let sub = ndk.subscribe({ |
||||
kinds: [repo_kind], |
||||
limit, |
||||
}); |
||||
sub.on("event", (event: NDKEvent) => { |
||||
if (repos.length < limit) { |
||||
try { |
||||
if ( |
||||
event.kind == repo_kind && |
||||
!repos.some( |
||||
(r) => |
||||
r.repo_id == event.replaceableDTag() && |
||||
event.created_at && |
||||
r.created_at > event.created_at, |
||||
) |
||||
) |
||||
repos = [ |
||||
...repos.filter( |
||||
(r) => |
||||
!event.created_at || |
||||
r.repo_id !== event.replaceableDTag() || |
||||
r.created_at > event.created_at, |
||||
), |
||||
{ |
||||
name: event.tagValue("name") || "", |
||||
description: event.tagValue("description") || "", |
||||
repo_id: event.replaceableDTag(), |
||||
created_at: event.created_at || 0, |
||||
}, |
||||
]; |
||||
} catch {} |
||||
} else if (loading == true) loading = false; |
||||
}); |
||||
sub.on("eose", () => { |
||||
if (loading == true) loading = false; |
||||
}); |
||||
let repos: Args[] = [] |
||||
let loading: boolean = true |
||||
let sub = ndk.subscribe({ |
||||
kinds: [repo_kind], |
||||
limit, |
||||
}) |
||||
sub.on('event', (event: NDKEvent) => { |
||||
if (repos.length < limit) { |
||||
try { |
||||
if ( |
||||
event.kind == repo_kind && |
||||
!repos.some( |
||||
(r) => |
||||
r.repo_id == event.replaceableDTag() && |
||||
event.created_at && |
||||
r.created_at > event.created_at |
||||
) |
||||
) |
||||
repos = [ |
||||
...repos.filter( |
||||
(r) => |
||||
!event.created_at || |
||||
r.repo_id !== event.replaceableDTag() || |
||||
r.created_at > event.created_at |
||||
), |
||||
{ |
||||
name: event.tagValue('name') || '', |
||||
description: event.tagValue('description') || '', |
||||
repo_id: event.replaceableDTag(), |
||||
created_at: event.created_at || 0, |
||||
}, |
||||
] |
||||
} catch {} |
||||
} else if (loading == true) loading = false |
||||
}) |
||||
sub.on('eose', () => { |
||||
if (loading == true) loading = false |
||||
}) |
||||
|
||||
onDestroy(() => { |
||||
sub.stop(); |
||||
}); |
||||
onDestroy(() => { |
||||
sub.stop() |
||||
}) |
||||
</script> |
||||
|
||||
<ReposSummaryList title="Latest Repositories" {repos} {loading} /> |
||||
|
||||
@ -1,32 +1,28 @@
@@ -1,32 +1,28 @@
|
||||
<script lang="ts"> |
||||
import type { User } from "$lib/components/users/type"; |
||||
import { defaults as user_defaults } from "$lib/components/users/type"; |
||||
import { ndk } from "$lib/stores/ndk"; |
||||
import { ensureUser } from "$lib/stores/users"; |
||||
import type { NDKEvent } from "@nostr-dev-kit/ndk"; |
||||
import { onDestroy } from "svelte"; |
||||
import EventCard from "./EventCard.svelte"; |
||||
import ThreadWrapper from "$lib/components/events/ThreadWrapper.svelte"; |
||||
import { writable } from "svelte/store"; |
||||
import { ndk } from '$lib/stores/ndk' |
||||
import type { NDKEvent } from '@nostr-dev-kit/ndk' |
||||
import EventCard from './EventCard.svelte' |
||||
import ThreadWrapper from '$lib/components/events/ThreadWrapper.svelte' |
||||
import { writable } from 'svelte/store' |
||||
|
||||
export let event: NDKEvent; |
||||
export let event: NDKEvent |
||||
|
||||
export let replies: NDKEvent[] | undefined = undefined; |
||||
export let replies: NDKEvent[] | undefined = undefined |
||||
|
||||
let replies_store = replies |
||||
? writable(replies) |
||||
: ndk.storeSubscribe({ |
||||
"#e": [event.id], |
||||
}); |
||||
$: { |
||||
if (replies) replies_store.set(replies); |
||||
} |
||||
let replies_store = replies |
||||
? writable(replies) |
||||
: ndk.storeSubscribe({ |
||||
'#e': [event.id], |
||||
}) |
||||
$: { |
||||
if (replies) replies_store.set(replies) |
||||
} |
||||
</script> |
||||
|
||||
<EventCard {event} /> |
||||
|
||||
<ThreadWrapper> |
||||
{#each $replies_store as event} |
||||
<EventCard {event} /> |
||||
{/each} |
||||
{#each $replies_store as event} |
||||
<EventCard {event} /> |
||||
{/each} |
||||
</ThreadWrapper> |
||||
|
||||
@ -1,87 +1,84 @@
@@ -1,87 +1,84 @@
|
||||
<script lang="ts"> |
||||
import Container from "$lib/components/Container.svelte"; |
||||
import Container from '$lib/components/Container.svelte' |
||||
</script> |
||||
|
||||
<Container> |
||||
<div class="prose mt-8 m-auto"> |
||||
<h2 class=""><span class="text-purple-600">n</span>git</h2> |
||||
<p> |
||||
a command-line tool to create, list and apply PRs and patches to git |
||||
repositories via nostr events. |
||||
</p> |
||||
It is designed to work seemlessly with this site gitworkshop.dev. |
||||
<div class="prose m-auto mt-8"> |
||||
<h2 class=""><span class="text-purple-600">n</span>git</h2> |
||||
<p> |
||||
a command-line tool to create, list and apply PRs and patches to git |
||||
repositories via nostr events. |
||||
</p> |
||||
It is designed to work seemlessly with this site gitworkshop.dev. |
||||
|
||||
<h3>Install</h3> |
||||
<p> |
||||
If you have cargo installed run<code>cargo install ngit</code>. |
||||
Alternatively download: |
||||
</p> |
||||
<a |
||||
href="https://github.com/DanConwayDev/ngit-cli/releases/download/v0.1.1/ngit-x86_64-unknown-linux-gnu.tar.gz" |
||||
class="btn btn-neutral">Linux</a |
||||
> |
||||
<a |
||||
href="https://github.com/DanConwayDev/ngit-cli/releases/download/v0.1.1/ngit-x86_64-apple-darwin.tar.gz" |
||||
class="btn btn-neutral">Mac</a |
||||
> |
||||
<a |
||||
href="https://github.com/DanConwayDev/ngit-cli/releases/download/v0.1.1/ngit-x86_64-pc-windows-msvc.zip" |
||||
class="btn btn-neutral">Windows</a |
||||
> |
||||
v.0.1.1 |
||||
<p>add the binary to a directory from which it can be run globally.</p> |
||||
<h3>Commands</h3> |
||||
<p> |
||||
All commands should be run from the directory of the git repository |
||||
</p> |
||||
<p> |
||||
<span class="rounded p-2 bg-neutral font-mono" |
||||
><span class="py-5">ngit prs create</span></span |
||||
> |
||||
use this when you are on a feature branch to propose it gets merged into |
||||
the remote branch by repository maintainer. |
||||
</p> |
||||
<p> |
||||
It creates a PR event and patch events for each commit that isn't |
||||
also in the main or master branch. |
||||
</p> |
||||
<p> |
||||
<span class="rounded p-2 bg-neutral font-mono" |
||||
><span class="py-5">ngit prs list</span></span |
||||
> |
||||
fetches a selectable list of open PRs and, if one is selected and the |
||||
repository is clean, will create a branch for it and apply the commits. |
||||
If the branch already exists it will pull any updates. |
||||
</p> |
||||
<p> |
||||
<span class="rounded p-2 bg-neutral font-mono" |
||||
><span class="py-5">ngit pull</span></span |
||||
> |
||||
if the repository is currently in a branch related to an existing PR, |
||||
it will pull any updates from nostr. |
||||
</p> |
||||
<p> |
||||
<span class="rounded p-2 bg-neutral font-mono" |
||||
><span class="py-5">ngit push</span></span |
||||
> |
||||
if the repository is currently in a branch related to an existing PR, |
||||
it will create patch events for any new commits. |
||||
</p> |
||||
<p> |
||||
<span class="rounded p-2 bg-neutral font-mono" |
||||
><span class="py-5">ngit claim</span></span |
||||
> |
||||
issue a repository nostr event which lists it on gitworkshop.dev and |
||||
indicates you are accepting patches and PRs via nostr. This can be run |
||||
many times to update the details. The relays will reflect your user relays |
||||
plus blaster. |
||||
</p> |
||||
<p> |
||||
It also creates an optional <span class="font-mono p-2 bg-base-200" |
||||
>maintainers.yaml</span |
||||
> file in the root of your repo that lists maintainers and relays. If |
||||
commited it will make it smoother for contributors to find your repository |
||||
event and also makes any future transfer of repository ownership easier. |
||||
</p> |
||||
</div> |
||||
<h3>Install</h3> |
||||
<p> |
||||
If you have cargo installed run<code>cargo install ngit</code>. |
||||
Alternatively download: |
||||
</p> |
||||
<a |
||||
href="https://github.com/DanConwayDev/ngit-cli/releases/download/v0.1.1/ngit-x86_64-unknown-linux-gnu.tar.gz" |
||||
class="btn btn-neutral">Linux</a |
||||
> |
||||
<a |
||||
href="https://github.com/DanConwayDev/ngit-cli/releases/download/v0.1.1/ngit-x86_64-apple-darwin.tar.gz" |
||||
class="btn btn-neutral">Mac</a |
||||
> |
||||
<a |
||||
href="https://github.com/DanConwayDev/ngit-cli/releases/download/v0.1.1/ngit-x86_64-pc-windows-msvc.zip" |
||||
class="btn btn-neutral">Windows</a |
||||
> |
||||
v.0.1.1 |
||||
<p>add the binary to a directory from which it can be run globally.</p> |
||||
<h3>Commands</h3> |
||||
<p>All commands should be run from the directory of the git repository</p> |
||||
<p> |
||||
<span class="rounded bg-neutral p-2 font-mono" |
||||
><span class="py-5">ngit prs create</span></span |
||||
> |
||||
use this when you are on a feature branch to propose it gets merged into the |
||||
remote branch by repository maintainer. |
||||
</p> |
||||
<p> |
||||
It creates a PR event and patch events for each commit that isn't also in |
||||
the main or master branch. |
||||
</p> |
||||
<p> |
||||
<span class="rounded bg-neutral p-2 font-mono" |
||||
><span class="py-5">ngit prs list</span></span |
||||
> |
||||
fetches a selectable list of open PRs and, if one is selected and the repository |
||||
is clean, will create a branch for it and apply the commits. If the branch |
||||
already exists it will pull any updates. |
||||
</p> |
||||
<p> |
||||
<span class="rounded bg-neutral p-2 font-mono" |
||||
><span class="py-5">ngit pull</span></span |
||||
> |
||||
if the repository is currently in a branch related to an existing PR, it will |
||||
pull any updates from nostr. |
||||
</p> |
||||
<p> |
||||
<span class="rounded bg-neutral p-2 font-mono" |
||||
><span class="py-5">ngit push</span></span |
||||
> |
||||
if the repository is currently in a branch related to an existing PR, it will |
||||
create patch events for any new commits. |
||||
</p> |
||||
<p> |
||||
<span class="rounded bg-neutral p-2 font-mono" |
||||
><span class="py-5">ngit claim</span></span |
||||
> |
||||
issue a repository nostr event which lists it on gitworkshop.dev and indicates |
||||
you are accepting patches and PRs via nostr. This can be run many times to |
||||
update the details. The relays will reflect your user relays plus blaster. |
||||
</p> |
||||
<p> |
||||
It also creates an optional <span class="bg-base-200 p-2 font-mono" |
||||
>maintainers.yaml</span |
||||
> file in the root of your repo that lists maintainers and relays. If commited |
||||
it will make it smoother for contributors to find your repository event and |
||||
also makes any future transfer of repository ownership easier. |
||||
</p> |
||||
</div> |
||||
</Container> |
||||
|
||||
@ -1,51 +1,50 @@
@@ -1,51 +1,50 @@
|
||||
<script lang="ts"> |
||||
import RepoDetails from "$lib/wrappers/RepoDetails.svelte"; |
||||
import OpenPRs from "$lib/wrappers/OpenPRs.svelte"; |
||||
import { ensureSelectedRepo, selected_repo } from "$lib/stores/repo"; |
||||
import RepoHeader from "$lib/components/repo/RepoHeader.svelte"; |
||||
import Container from "$lib/components/Container.svelte"; |
||||
import RepoDetails from '$lib/wrappers/RepoDetails.svelte' |
||||
import OpenPRs from '$lib/wrappers/OpenPRs.svelte' |
||||
import { ensureSelectedRepo, selected_repo } from '$lib/stores/repo' |
||||
import RepoHeader from '$lib/components/repo/RepoHeader.svelte' |
||||
import Container from '$lib/components/Container.svelte' |
||||
|
||||
export let data: { repo_id: string }; |
||||
let repo_id = data.repo_id; |
||||
export let data: { repo_id: string } |
||||
let repo_id = data.repo_id |
||||
|
||||
ensureSelectedRepo(repo_id); |
||||
ensureSelectedRepo(repo_id) |
||||
|
||||
let repo_error = false; |
||||
$: { |
||||
repo_error = |
||||
!$selected_repo.loading && $selected_repo.name.length === 0; |
||||
} |
||||
let repo_error = false |
||||
$: { |
||||
repo_error = !$selected_repo.loading && $selected_repo.name.length === 0 |
||||
} |
||||
</script> |
||||
|
||||
{#if repo_error} |
||||
<Container> |
||||
<div role="alert" class="alert alert-error mt-6 w-full max-w-xs m-auto"> |
||||
<svg |
||||
xmlns="http://www.w3.org/2000/svg" |
||||
class="stroke-current shrink-0 h-6 w-6" |
||||
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 repository event</span> |
||||
</div> |
||||
</Container> |
||||
<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 repository event</span> |
||||
</div> |
||||
</Container> |
||||
{:else} |
||||
<RepoHeader {...$selected_repo} /> |
||||
<RepoHeader {...$selected_repo} /> |
||||
|
||||
<Container> |
||||
<div class="md:flex mt-2"> |
||||
<div class="md:w-2/3 md:mr-2"> |
||||
<OpenPRs {repo_id} /> |
||||
</div> |
||||
<div class="w-1/3 ml-2 prose hidden md:flex"> |
||||
<RepoDetails {repo_id} /> |
||||
</div> |
||||
</div> |
||||
</Container> |
||||
<Container> |
||||
<div class="mt-2 md:flex"> |
||||
<div class="md:mr-2 md:w-2/3"> |
||||
<OpenPRs {repo_id} /> |
||||
</div> |
||||
<div class="prose ml-2 hidden w-1/3 md:flex"> |
||||
<RepoDetails {repo_id} /> |
||||
</div> |
||||
</div> |
||||
</Container> |
||||
{/if} |
||||
|
||||
@ -1,5 +1,5 @@
@@ -1,5 +1,5 @@
|
||||
export const load = ({ params }) => { |
||||
return { |
||||
repo_id: params.repo_id, |
||||
} |
||||
return { |
||||
repo_id: params.repo_id, |
||||
} |
||||
} |
||||
|
||||
@ -1,121 +1,107 @@
@@ -1,121 +1,107 @@
|
||||
<script lang="ts"> |
||||
import { ensureSelectedRepo, selected_repo } from "$lib/stores/repo"; |
||||
import { |
||||
ensurePRFull, |
||||
selected_pr_full, |
||||
selected_pr_replies, |
||||
} from "$lib/stores/PR"; |
||||
import PrHeader from "$lib/components/prs/PRHeader.svelte"; |
||||
import RepoHeader from "$lib/components/repo/RepoHeader.svelte"; |
||||
import Thread from "$lib/wrappers/Thread.svelte"; |
||||
import PrDetails from "$lib/components/prs/PRDetails.svelte"; |
||||
import Container from "$lib/components/Container.svelte"; |
||||
import ParsedContent from "$lib/components/events/content/ParsedContent.svelte"; |
||||
import Compose from "$lib/wrappers/Compose.svelte"; |
||||
import type { NDKEvent } from "@nostr-dev-kit/ndk"; |
||||
import { ensureSelectedRepo, selected_repo } from '$lib/stores/repo' |
||||
import { |
||||
ensurePRFull, |
||||
selected_pr_full, |
||||
selected_pr_replies, |
||||
} from '$lib/stores/PR' |
||||
import PrHeader from '$lib/components/prs/PRHeader.svelte' |
||||
import RepoHeader from '$lib/components/repo/RepoHeader.svelte' |
||||
import Thread from '$lib/wrappers/Thread.svelte' |
||||
import PrDetails from '$lib/components/prs/PRDetails.svelte' |
||||
import Container from '$lib/components/Container.svelte' |
||||
import ParsedContent from '$lib/components/events/content/ParsedContent.svelte' |
||||
import Compose from '$lib/wrappers/Compose.svelte' |
||||
|
||||
export let data: { |
||||
repo_id: string; |
||||
pr_id: string; |
||||
}; |
||||
export let data: { |
||||
repo_id: string |
||||
pr_id: string |
||||
} |
||||
|
||||
let repo_id = data.repo_id; |
||||
let pr_id = data.pr_id; |
||||
let repo_id = data.repo_id |
||||
let pr_id = data.pr_id |
||||
|
||||
ensureSelectedRepo(repo_id); |
||||
ensurePRFull(repo_id, pr_id); |
||||
ensureSelectedRepo(repo_id) |
||||
ensurePRFull(repo_id, pr_id) |
||||
|
||||
let replies: NDKEvent[] = []; |
||||
|
||||
$: { |
||||
replies = $selected_pr_replies.sort((a, b) => |
||||
a.created_at && b.created_at ? a.created_at - b.created_at : 1, |
||||
); |
||||
} |
||||
|
||||
let repo_error = false; |
||||
let pr_error = false; |
||||
$: { |
||||
repo_error = |
||||
!$selected_repo.loading && $selected_repo.name.length === 0; |
||||
pr_error = |
||||
!$selected_pr_full.summary.loading && |
||||
$selected_pr_full.summary.created_at === 0; |
||||
} |
||||
let repo_error = false |
||||
let pr_error = false |
||||
$: { |
||||
repo_error = !$selected_repo.loading && $selected_repo.name.length === 0 |
||||
pr_error = |
||||
!$selected_pr_full.summary.loading && |
||||
$selected_pr_full.summary.created_at === 0 |
||||
} |
||||
</script> |
||||
|
||||
{#if !repo_error} |
||||
<RepoHeader {...$selected_repo} /> |
||||
<RepoHeader {...$selected_repo} /> |
||||
{/if} |
||||
|
||||
{#if pr_error} |
||||
<Container> |
||||
<div role="alert" class="alert alert-error mt-6 w-full max-w-xs m-auto"> |
||||
<svg |
||||
xmlns="http://www.w3.org/2000/svg" |
||||
class="stroke-current shrink-0 h-6 w-6" |
||||
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 PR {repo_error ? "or repo " : ""}event</span |
||||
> |
||||
</div> |
||||
</Container> |
||||
<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 PR {repo_error ? 'or repo ' : ''}event</span> |
||||
</div> |
||||
</Container> |
||||
{:else} |
||||
<PrHeader {...$selected_pr_full.summary} /> |
||||
<Container> |
||||
<div class="md:flex"> |
||||
<div class="md:w-2/3 md:mr-2"> |
||||
<div class="prose my-3"> |
||||
<ParsedContent |
||||
content={$selected_pr_full.summary.descritpion} |
||||
/> |
||||
</div> |
||||
<div role="alert" class="alert"> |
||||
<svg |
||||
xmlns="http://www.w3.org/2000/svg" |
||||
fill="none" |
||||
viewBox="0 0 24 24" |
||||
class="stroke-info shrink-0 w-6 h-6" |
||||
><path |
||||
stroke-linecap="round" |
||||
stroke-linejoin="round" |
||||
stroke-width="2" |
||||
d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" |
||||
></path></svg |
||||
> |
||||
<div> |
||||
<h3 class="text-xs prose"> |
||||
to view the latest patches from this PR <a |
||||
href="/ngit">install ngit</a |
||||
>, run |
||||
<span class="rounded p-1 bg-neutral font-mono" |
||||
><span class="py-3">ngit prs list</span></span |
||||
> from the local repository and select this PR title |
||||
</h3> |
||||
</div> |
||||
</div> |
||||
{#each $selected_pr_replies as event} |
||||
<Thread {event} replies={[]} /> |
||||
{/each} |
||||
<div class="my-3"> |
||||
<Compose /> |
||||
</div> |
||||
</div> |
||||
<div class="w-1/3 ml-2 prose hidden md:flex"> |
||||
<PrDetails |
||||
summary={$selected_pr_full.summary} |
||||
labels={$selected_pr_full.labels} |
||||
loading={$selected_pr_full.loading} |
||||
/> |
||||
</div> |
||||
<PrHeader {...$selected_pr_full.summary} /> |
||||
<Container> |
||||
<div class="md:flex"> |
||||
<div class="md:mr-2 md:w-2/3"> |
||||
<div class="prose my-3"> |
||||
<ParsedContent content={$selected_pr_full.summary.descritpion} /> |
||||
</div> |
||||
<div role="alert" class="alert"> |
||||
<svg |
||||
xmlns="http://www.w3.org/2000/svg" |
||||
fill="none" |
||||
viewBox="0 0 24 24" |
||||
class="h-6 w-6 shrink-0 stroke-info" |
||||
><path |
||||
stroke-linecap="round" |
||||
stroke-linejoin="round" |
||||
stroke-width="2" |
||||
d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" |
||||
></path></svg |
||||
> |
||||
<div> |
||||
<h3 class="prose text-xs"> |
||||
to view the latest patches from this PR <a href="/ngit" |
||||
>install ngit</a |
||||
>, run |
||||
<span class="rounded bg-neutral p-1 font-mono" |
||||
><span class="py-3">ngit prs list</span></span |
||||
> from the local repository and select this PR title |
||||
</h3> |
||||
</div> |
||||
</div> |
||||
{#each $selected_pr_replies as event} |
||||
<Thread {event} replies={[]} /> |
||||
{/each} |
||||
<div class="my-3"> |
||||
<Compose /> |
||||
</div> |
||||
</Container> |
||||
</div> |
||||
<div class="prose ml-2 hidden w-1/3 md:flex"> |
||||
<PrDetails |
||||
summary={$selected_pr_full.summary} |
||||
labels={$selected_pr_full.labels} |
||||
loading={$selected_pr_full.loading} |
||||
/> |
||||
</div> |
||||
</div> |
||||
</Container> |
||||
{/if} |
||||
|
||||
@ -1,6 +1,6 @@
@@ -1,6 +1,6 @@
|
||||
export const load = ({ params }) => { |
||||
return { |
||||
repo_id: params.repo_id, |
||||
pr_id: params.pr_id, |
||||
} |
||||
return { |
||||
repo_id: params.repo_id, |
||||
pr_id: params.pr_id, |
||||
} |
||||
} |
||||
|
||||
@ -1,13 +1,13 @@
@@ -1,13 +1,13 @@
|
||||
{ |
||||
"extends": "./.svelte-kit/tsconfig.json", |
||||
"compilerOptions": { |
||||
"allowJs": true, |
||||
"checkJs": true, |
||||
"esModuleInterop": true, |
||||
"forceConsistentCasingInFileNames": true, |
||||
"resolveJsonModule": true, |
||||
"skipLibCheck": true, |
||||
"sourceMap": true, |
||||
"strict": true |
||||
} |
||||
"extends": "./.svelte-kit/tsconfig.json", |
||||
"compilerOptions": { |
||||
"allowJs": true, |
||||
"checkJs": true, |
||||
"esModuleInterop": true, |
||||
"forceConsistentCasingInFileNames": true, |
||||
"resolveJsonModule": true, |
||||
"skipLibCheck": true, |
||||
"sourceMap": true, |
||||
"strict": true, |
||||
}, |
||||
} |
||||
|
||||
Loading…
Reference in new issue