diff --git a/src/lib/components/ui/skeleton/skeleton.svelte b/src/lib/components/ui/skeleton/skeleton.svelte
index 7e5d18b4..4089b496 100644
--- a/src/lib/components/ui/skeleton/skeleton.svelte
+++ b/src/lib/components/ui/skeleton/skeleton.svelte
@@ -1,11 +1,17 @@
-
+
diff --git a/src/lib/components/ui/sonner/sonner.svelte b/src/lib/components/ui/sonner/sonner.svelte
index 7d5b2f14..8050e049 100644
--- a/src/lib/components/ui/sonner/sonner.svelte
+++ b/src/lib/components/ui/sonner/sonner.svelte
@@ -2,7 +2,7 @@
import { Toaster as Sonner, type ToasterProps as SonnerProps } from "svelte-sonner";
import { mode } from "mode-watcher";
- type $$Props = SonnerProps;
+ let restProps: SonnerProps = $props();
diff --git a/src/lib/components/ui/tabs/tabs-content.svelte b/src/lib/components/ui/tabs/tabs-content.svelte
index b6115596..f1c05157 100644
--- a/src/lib/components/ui/tabs/tabs-content.svelte
+++ b/src/lib/components/ui/tabs/tabs-content.svelte
@@ -2,20 +2,18 @@
import { Tabs as TabsPrimitive } from "bits-ui";
import { cn } from "$lib/utils.js";
- type $$Props = TabsPrimitive.ContentProps;
-
- let className: $$Props["class"] = undefined;
- export let value: $$Props["value"];
- export { className as class };
+ let {
+ ref = $bindable(null),
+ class: className,
+ ...restProps
+ }: TabsPrimitive.ContentProps = $props();
-
-
+ {...restProps}
+/>
diff --git a/src/lib/components/ui/tabs/tabs-list.svelte b/src/lib/components/ui/tabs/tabs-list.svelte
index 773c7547..f03e5fc7 100644
--- a/src/lib/components/ui/tabs/tabs-list.svelte
+++ b/src/lib/components/ui/tabs/tabs-list.svelte
@@ -2,18 +2,18 @@
import { Tabs as TabsPrimitive } from "bits-ui";
import { cn } from "$lib/utils.js";
- type $$Props = TabsPrimitive.ListProps;
-
- let className: $$Props["class"] = undefined;
- export { className as class };
+ let {
+ ref = $bindable(null),
+ class: className,
+ ...restProps
+ }: TabsPrimitive.ListProps = $props();
-
-
+ {...restProps}
+/>
diff --git a/src/lib/components/ui/tabs/tabs-trigger.svelte b/src/lib/components/ui/tabs/tabs-trigger.svelte
index b99358dc..f1f5825e 100644
--- a/src/lib/components/ui/tabs/tabs-trigger.svelte
+++ b/src/lib/components/ui/tabs/tabs-trigger.svelte
@@ -2,22 +2,18 @@
import { Tabs as TabsPrimitive } from "bits-ui";
import { cn } from "$lib/utils.js";
- type $$Props = TabsPrimitive.TriggerProps;
- type $$Events = TabsPrimitive.TriggerEvents;
-
- let className: $$Props["class"] = undefined;
- export let value: $$Props["value"];
- export { className as class };
+ let {
+ ref = $bindable(null),
+ class: className,
+ ...restProps
+ }: TabsPrimitive.TriggerProps = $props();
-
-
+ {...restProps}
+/>
diff --git a/src/lib/components/ui/tooltip/index.ts b/src/lib/components/ui/tooltip/index.ts
index 7d0444da..e9e1fd73 100644
--- a/src/lib/components/ui/tooltip/index.ts
+++ b/src/lib/components/ui/tooltip/index.ts
@@ -3,13 +3,16 @@ import Content from "./tooltip-content.svelte";
const Root = TooltipPrimitive.Root;
const Trigger = TooltipPrimitive.Trigger;
+const Provider = TooltipPrimitive.Provider;
export {
Root,
Trigger,
Content,
+ Provider,
//
Root as Tooltip,
Content as TooltipContent,
Trigger as TooltipTrigger,
+ Provider as TooltipProvider,
};
diff --git a/src/lib/components/ui/tooltip/tooltip-content.svelte b/src/lib/components/ui/tooltip/tooltip-content.svelte
index af9d1ff4..68ff232b 100644
--- a/src/lib/components/ui/tooltip/tooltip-content.svelte
+++ b/src/lib/components/ui/tooltip/tooltip-content.svelte
@@ -1,28 +1,21 @@
-
-
+ {...restProps}
+/>
diff --git a/src/lib/octokit.ts b/src/lib/octokit.ts
index 83954ec5..46cf44a9 100644
--- a/src/lib/octokit.ts
+++ b/src/lib/octokit.ts
@@ -13,7 +13,7 @@ import { tokenKey } from "./types";
*/
export function getOctokit() {
// TODO: Invert the condition to make the logged in token take precedence over the environment token
- const hasTokenInDev = dev && env.PUBLIC_GITHUB_TOKEN;
+ const hasTokenInDev = dev && !!env.PUBLIC_GITHUB_TOKEN;
const octokit = new Octokit(
hasTokenInDev
? {
diff --git a/src/lib/utils.ts b/src/lib/utils.ts
index 230a1fbd..ac680b30 100644
--- a/src/lib/utils.ts
+++ b/src/lib/utils.ts
@@ -1,62 +1,6 @@
import { type ClassValue, clsx } from "clsx";
import { twMerge } from "tailwind-merge";
-import { cubicOut } from "svelte/easing";
-import type { TransitionConfig } from "svelte/transition";
export function cn(...inputs: ClassValue[]) {
- return twMerge(clsx(inputs));
+ return twMerge(clsx(inputs));
}
-
-type FlyAndScaleParams = {
- y?: number;
- x?: number;
- start?: number;
- duration?: number;
-};
-
-export const flyAndScale = (
- node: Element,
- params: FlyAndScaleParams = { y: -8, x: 0, start: 0.95, duration: 150 }
-): TransitionConfig => {
- const style = getComputedStyle(node);
- const transform = style.transform === "none" ? "" : style.transform;
-
- const scaleConversion = (
- valueA: number,
- scaleA: [number, number],
- scaleB: [number, number]
- ) => {
- const [minA, maxA] = scaleA;
- const [minB, maxB] = scaleB;
-
- const percentage = (valueA - minA) / (maxA - minA);
- const valueB = percentage * (maxB - minB) + minB;
-
- return valueB;
- };
-
- const styleToString = (
- style: Record
- ): string => {
- return Object.keys(style).reduce((str, key) => {
- if (style[key] === undefined) return str;
- return str + `${key}:${style[key]};`;
- }, "");
- };
-
- return {
- duration: params.duration ?? 200,
- delay: 0,
- css: (t) => {
- const y = scaleConversion(t, [0, 1], [params.y ?? 5, 0]);
- const x = scaleConversion(t, [0, 1], [params.x ?? 0, 0]);
- const scale = scaleConversion(t, [0, 1], [params.start ?? 0.95, 1]);
-
- return styleToString({
- transform: `${transform} translate3d(${x}px, ${y}px, 0) scale(${scale})`,
- opacity: t
- });
- },
- easing: cubicOut
- };
-};
\ No newline at end of file
diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte
index c40d7128..766bab42 100644
--- a/src/routes/+layout.svelte
+++ b/src/routes/+layout.svelte
@@ -5,7 +5,7 @@
import { fade } from "svelte/transition";
import { dev } from "$app/environment";
import { env } from "$env/dynamic/public";
- import { page } from "$app/stores";
+ import { page } from "$app/state";
import { ChevronDown, LoaderCircle, LogOut, Monitor, Moon, Sun, X } from "lucide-svelte";
import { ModeWatcher, resetMode, setMode } from "mode-watcher";
import { Octokit } from "octokit";
@@ -28,42 +28,46 @@
initTabState();
const tabState = getTabState();
- export let data;
- $: ({ repos } = data);
+ let { data, children } = $props();
- let scrollY = 0;
+ let scrollY = $state(0);
// User dropdown
const token = persisted(tokenKey, "", {
serializer: plainTextSerializer
});
const toastedTokens = persisted("toastedTokens", []);
- let isAuthenticating = false;
- let authenticatingToastId: string | number | undefined;
- $: if (isAuthenticating && !$toastedTokens.includes($token)) {
- authenticatingToastId = toast.loading("Authenticating...", {
- description: "Logging you in with GitHub"
- });
- }
- let user:
- | Awaited["rest"]["users"]["getAuthenticated"]>>["data"]
- | undefined = undefined;
- $: if (user && !$toastedTokens.includes($token)) {
- if (authenticatingToastId) {
- toast.info("Authenticated", {
- id: authenticatingToastId
+ let isAuthenticating = $state(false);
+ let authenticatingToastId = $state();
+ let user =
+ $state<
+ Awaited["rest"]["users"]["getAuthenticated"]>>["data"]
+ >();
+ toastedTokens.subscribe(tokens => {
+ if (tokens.includes($token)) return;
+ if (isAuthenticating) {
+ authenticatingToastId = toast.loading("Authenticating...", {
+ description: "Logging you in with GitHub"
});
- authenticatingToastId = undefined;
}
- toast.success("Authenticated", {
- description: `Welcome, ${user.login}!`
- });
- toastedTokens.update(toasted => [...new Set([...toasted, $token])]);
- }
- $: if ($token) {
+ if (user) {
+ if (authenticatingToastId) {
+ toast.info("Authenticated", {
+ id: authenticatingToastId
+ });
+ authenticatingToastId = undefined;
+ }
+ toast.success("Authenticated", {
+ description: `Welcome, ${user.login}!`
+ });
+ toastedTokens.update(toasted => [...new Set([...toasted, $token])]);
+ }
+ });
+ token.subscribe(newToken => {
+ if (!newToken) return;
isAuthenticating = true;
user = undefined;
- new Octokit({ auth: $token }).rest.users
+ new Octokit({ auth: newToken }).rest.users
.getAuthenticated()
.then(({ data }) => {
isAuthenticating = false;
@@ -72,8 +76,8 @@
.catch(() => {
isAuthenticating = false;
});
- }
- let userDropdownOpen = false;
+ });
+ let userDropdownOpen = $state(false);
// Theme selector
type Theme = {
@@ -98,11 +102,11 @@
icon: Monitor
}
];
- let theme: "light" | "dark" | "system";
- let themeSwitcherOpen = false;
+ let theme = $state<"light" | "dark" | "system">("system");
+ let themeSwitcherOpen = $state(false);
// News
- let newsToDisplay: (typeof news)[number] | undefined;
+ let newsToDisplay = $state<(typeof news)[number]>();
const closedNewsKey = "closedNews";
function getClosedNewsIds() {
return JSON.parse(localStorage.getItem(closedNewsKey) ?? "[]") as (typeof news)[number]["id"][];
@@ -138,7 +142,7 @@
>
-
tabState.set("svelte")}>
+ tabState.set("svelte")}>
Svelte
@@ -148,14 +152,14 @@
- {#if scrollY > 150 && $page.route.id === "/"}
+ {#if scrollY > 150 && page.route.id === "/"}
- {#each typedEntries(repos) as [id, { name }]}
+ {#each typedEntries(data.repos) as [id, { name }]}
tabState.set(id)}
+ onclick={() => tabState.set(id)}
disabled={$tabState === id}
>
{name}
@@ -180,21 +184,23 @@
{:else}
-
-
-
-
-
- {user.login.charAt(0).toUpperCase()}
-
-
-
- Manage user
-
+
+ {#snippet child({ props })}
+
+
+
+
+ {user?.login.charAt(0).toUpperCase()}
+
+
+
+ Manage user
+
+ {/snippet}
@@ -243,7 +249,7 @@
{
+ onclick={() => {
toastedTokens.update(toasted => toasted.filter(t => t !== $token));
localStorage.removeItem(tokenKey);
user = undefined;
@@ -271,23 +277,25 @@
Visit the repository
-
-
-
-
-
+ {#snippet child({ props })}
+
+
+
+
+
+
-
-
- Change theme
-
+ Change theme
+
+ {/snippet}
Theme
@@ -298,13 +306,13 @@
class="cursor-pointer data-[disabled]:opacity-75"
value={availableTheme.value}
disabled={theme === availableTheme.value}
- on:click={() => {
+ onclick={() => {
return availableTheme.value === "system"
? resetMode()
: setMode(availableTheme.value);
}}
>
-
+
{availableTheme.label}
{/each}
@@ -321,7 +329,7 @@
{
+ onclick={() => {
if (!newsToDisplay) return;
localStorage.setItem(
closedNewsKey,
@@ -338,7 +346,7 @@
-
+{@render children?.()}
diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte
index 33b3635f..f25265cd 100644
--- a/src/routes/+page.svelte
+++ b/src/routes/+page.svelte
@@ -1,7 +1,7 @@
- {repos[currentTab].name}
+ {data.repos[currentTab].name}
Releases
@@ -274,7 +274,7 @@
class="flex flex-col items-start gap-4 xs:flex-row xs:items-center xs:justify-between xs:gap-0"
>
- {#each typedEntries(repos) as [id, { name }]}
+ {#each typedEntries(data.repos) as [id, { name }]}
- Show {repos[currentTab].name} prereleases
+ Show {data.repos[currentTab].name} prereleases
- {#each typedEntries(repos) as [id, { name, repos: repoList }]}
+ {#each typedEntries(data.repos) as [id, { name, repos: repoList }]}
{#await fetchReleases(id)}
@@ -339,12 +339,13 @@
{@const _ = (() => {
// Cache the response
- cachedResponses[id] = releases;
+ // TODO: restore with refactor
+ // cachedResponses[id] = releases;
// Add tab to loaded tabs
- const toSet = new Set(loadedTabs);
- toSet.add(id);
- loadedTabs = [...toSet];
+ // const toSet = new Set(loadedTabs);
+ // toSet.add(id);
+ // loadedTabs = [...toSet];
// Update the most recent date of a release of the list
const latestRelease = releases.sort(
@@ -477,7 +478,7 @@
)
).filter(Boolean)}
{
@@ -560,6 +561,69 @@
}
);
})()}
+
+ {#snippet badges()}
+ {#if isLatestRelease}
+
+
+
+
+ Latest
+
+
+
+ {#if id === "others"}
+ This is a latest stable release
+ {:else}
+ This is the latest stable release of {name}
+ {/if}
+
+
+
+ {/if}
+ {#if isMajorRelease}
+
+
+
+ Major
+
+ Major update (e.g.: 1.0.0, 2.0.0, 3.0.0...)
+
+
+ {:else if release.prerelease}
+
+
+
+
+ Prerelease
+
+
+
+ This version is an alpha or a beta, unstable version{id === "others"
+ ? ""
+ : ` of ${name}`}
+
+
+
+ {:else if isMaintenanceRelease}
+
+
+
+
+ Maintenance
+
+
+
+ An update bringing bug fixes and minor improvements to an older major
+ version
+
+
+
+ {/if}
+ {/snippet}
+
-
+
{#key isLoadingDone}
{#if releaseDate > new Date(lastVisitDateString) && !visitedTabs.includes(id)}
-
+ >
+
{/if}
{/key}
@@ -585,29 +649,31 @@
{@const newReleaseMajor = releaseRepo
?.versionFromTag(release.tag_name)
?.split(".")[0]}
-
-
- {#if index === 0 && currentTab === id}
-
- {/if}
-
- {releaseName}
-
-
-
- {#if newReleaseMajor}
- {name} {newReleaseMajor} is available!
- {:else}
- A new major of {name} is available!
- {/if}
-
-
+
+
+
+ {#if index === 0 && currentTab === id}
+
+ {/if}
+
+ {releaseName}
+
+
+
+ {#if newReleaseMajor}
+ {name} {newReleaseMajor} is available!
+ {:else}
+ A new major of {name} is available!
+ {/if}
+
+
+
{:else}
{releaseName}
@@ -619,135 +685,33 @@
{/if}
- {#if isLatestRelease}
-
-
-
- Latest
-
-
-
- {#if id === "others"}
- This is a latest stable release
- {:else}
- This is the latest stable release of {name}
- {/if}
-
-
- {/if}
- {#if isMajorRelease}
-
-
- Major
-
-
- Major update (e.g.: 1.0.0, 2.0.0, 3.0.0...)
-
-
- {:else if release.prerelease}
-
-
-
- Prerelease
-
-
-
- This version is an alpha or a beta, unstable version{id === "others"
- ? ""
- : ` of ${name}`}
-
-
- {:else if isMaintenanceRelease}
-
-
-
- Maintenance
-
-
-
- An update bringing bug fixes and minor improvements to an older major
- version
-
-
- {/if}
+ {@render badges()}
•
-
-
- {isOlderThanAWeek
- ? releaseDate.toLocaleDateString("en")
- : toRelativeDateString(releaseDate)}
-
-
- {isOlderThanAWeek
- ? toRelativeDateString(releaseDate)
- : new Intl.DateTimeFormat("en", {
- dateStyle: "medium",
- timeStyle: "short"
- }).format(releaseDate)}
-
-
-
-
- {#if isLatestRelease}
-
+
+
-
- Latest
-
+ {isOlderThanAWeek
+ ? releaseDate.toLocaleDateString("en")
+ : toRelativeDateString(releaseDate)}
- {#if id === "others"}
- This is a latest stable release
- {:else}
- This is the latest stable release of {name}
- {/if}
+ {isOlderThanAWeek
+ ? toRelativeDateString(releaseDate)
+ : new Intl.DateTimeFormat("en", {
+ dateStyle: "medium",
+ timeStyle: "short"
+ }).format(releaseDate)}
- {/if}
- {#if isMajorRelease}
-
-
- Major
-
-
- Major update (e.g.: 1.0.0, 2.0.0, 3.0.0...)
-
-
- {:else if release.prerelease}
-
-
-
- Prerelease
-
-
-
- This version is an alpha or a beta, unstable version{id === "others"
- ? ""
- : ` of ${name}`}
-
-
- {:else if isMaintenanceRelease}
-
-
-
- Maintenance
-
-
-
- An update bringing bug fixes and minor improvements to an older major
- version
-
-
- {/if}
+
+
+
+ {@render badges()}
@@ -763,7 +727,7 @@
Open on
+ import type { Issue, Repository } from "@octokit/graphql-schema";
import { LoaderCircle } from "lucide-svelte";
import { getOctokit } from "$lib/octokit";
import type { Issues, LinkedEntity, Pulls } from "./types";
import PageRenderer from "./PageRenderer.svelte";
- export let data;
- $: ({ org: owner, id, repo, pullOrIssue } = data);
+ let { data } = $props();
const octokit = getOctokit();
- async function linkedIssuesForPR(
- owner: string,
- repo: string,
- pr: number
- ): Promise {
+ async function linkedIssuesForPR(owner: string, repo: string, pr: number) {
return octokit
- .graphql(
+ .graphql<{ repository: Repository }>(
`
query closingIssues($number: Int!, $owner: String!, $repo: String!) {
repository(owner: $owner, name: $repo) {
@@ -42,198 +38,199 @@
number: pr
}
)
- .then(
- /* eslint-disable @typescript-eslint/no-explicit-any */ (response: any) =>
- response.repository.pullRequest.closingIssuesReferences.nodes
- )
- .catch(() => []);
+ .then(({ repository }) => {
+ const n = repository.pullRequest?.closingIssuesReferences?.nodes;
+ if (!n) return [] as Issue[];
+ return n.filter(Boolean);
+ })
+ .catch(() => [] as Issue[]);
}
// PR issues or issue PRs
- let linkedPRsOrIssues: LinkedEntity[] | undefined = undefined;
+ let linkedPRsOrIssues = $state();
// PR
- let prInfo: {
+ let prInfo = $state<{
info: Awaited>["data"] | undefined;
comments: Awaited>["data"] | undefined;
commits: Awaited>["data"] | undefined;
files: Awaited>["data"] | undefined;
- } = {
+ }>({
info: undefined,
comments: undefined,
commits: undefined,
files: undefined
- };
+ });
// Issue
- let issueInfo: {
+ let issueInfo = $state<{
info: Awaited>["data"] | undefined;
comments: Awaited>["data"] | undefined;
- } = {
+ }>({
info: undefined,
comments: undefined
- };
- let prsToFetch: number[] = [];
+ });
+ let prsToFetch = $state([]);
- $: info = prInfo.info || issueInfo.info;
+ let info = $derived(prInfo.info || issueInfo.info);
// Data fetching
- $: if (pullOrIssue === "pull") {
- linkedPRsOrIssues = [];
- issueInfo = {
- info: undefined,
- comments: undefined
- };
+ $effect(() => {
+ if (data.pullOrIssue === "pull") {
+ linkedPRsOrIssues = [];
+ issueInfo = {
+ info: undefined,
+ comments: undefined
+ };
- // Fetch PR info
- octokit.rest.pulls
- .get({
- owner,
- repo,
- pull_number: id
- })
- .then(({ data }) => (prInfo.info = data));
- octokit.rest.issues
- .listComments({
- owner,
- repo,
- issue_number: id
- })
- .then(
- ({ data }) =>
- (prInfo.comments = data.sort(
- (a, b) => new Date(a.created_at).getTime() - new Date(b.created_at).getTime()
- ))
- );
- octokit.rest.pulls
- .listCommits({
- owner,
- repo,
- pull_number: id
- })
- .then(({ data }) => (prInfo.commits = data));
- octokit.rest.pulls
- .listFiles({
- owner,
- repo,
- pull_number: id
- })
- .then(({ data }) => (prInfo.files = data));
-
- // Fetch closing issues
- linkedIssuesForPR(owner, repo, id).then(response => (linkedPRsOrIssues = response));
- }
- $: if (pullOrIssue === "issues") {
- linkedPRsOrIssues = [];
- prInfo = {
- info: undefined,
- comments: undefined,
- commits: undefined,
- files: undefined
- };
+ // Fetch PR info
+ octokit.rest.pulls
+ .get({
+ owner: data.org,
+ repo: data.repo,
+ pull_number: data.id
+ })
+ .then(({ data }) => (prInfo.info = data));
+ octokit.rest.issues
+ .listComments({
+ owner: data.org,
+ repo: data.repo,
+ issue_number: data.id
+ })
+ .then(
+ ({ data }) =>
+ (prInfo.comments = data.sort(
+ (a, b) => new Date(a.created_at).getTime() - new Date(b.created_at).getTime()
+ ))
+ );
+ octokit.rest.pulls
+ .listCommits({
+ owner: data.org,
+ repo: data.repo,
+ pull_number: data.id
+ })
+ .then(({ data }) => (prInfo.commits = data));
+ octokit.rest.pulls
+ .listFiles({
+ owner: data.org,
+ repo: data.repo,
+ pull_number: data.id
+ })
+ .then(({ data }) => (prInfo.files = data));
- octokit.rest.issues
- .get({
- owner,
- repo,
- issue_number: id
- })
- .then(({ data }) => (issueInfo.info = data));
- octokit.rest.issues
- .listComments({
- owner,
- repo,
- issue_number: id
- })
- .then(
- ({ data }) =>
- (issueInfo.comments = data.sort(
- (a, b) => new Date(a.created_at).getTime() - new Date(b.created_at).getTime()
- ))
+ // Fetch closing issues
+ linkedIssuesForPR(data.org, data.repo, data.id).then(
+ response => (linkedPRsOrIssues = response)
);
- octokit.rest.issues
- .listEventsForTimeline({
- owner,
- repo,
- issue_number: id
- })
- .then(({ data: events }) =>
- events.filter(
- event =>
- event.event === "cross-referenced" &&
- "source" in event &&
- event.source.issue?.repository?.owner.login === owner &&
- event.source.issue?.repository?.name === repo
+ } else {
+ linkedPRsOrIssues = [];
+ prInfo = {
+ info: undefined,
+ comments: undefined,
+ commits: undefined,
+ files: undefined
+ };
+
+ octokit.rest.issues
+ .get({
+ owner: data.org,
+ repo: data.repo,
+ issue_number: data.id
+ })
+ .then(({ data }) => (issueInfo.info = data));
+ octokit.rest.issues
+ .listComments({
+ owner: data.org,
+ repo: data.repo,
+ issue_number: data.id
+ })
+ .then(
+ ({ data }) =>
+ (issueInfo.comments = data.sort(
+ (a, b) => new Date(a.created_at).getTime() - new Date(b.created_at).getTime()
+ ))
+ );
+ octokit.rest.issues
+ .listEventsForTimeline({
+ owner: data.org,
+ repo: data.repo,
+ issue_number: data.id
+ })
+ .then(({ data: events }) =>
+ events.filter(
+ event =>
+ event.event === "cross-referenced" &&
+ "source" in event &&
+ event.source.issue?.repository?.owner.login === data.org &&
+ event.source.issue?.repository?.name === data.repo
+ )
)
- )
- .then(async crEvents => {
- const prEvents = [];
- for (let event of crEvents) {
- const anyEvent = event as any;
- const doesPRExist = await octokit.rest.pulls
- .get({
- owner,
- repo,
- pull_number: anyEvent.source.issue.number
- })
- .then(() => true)
- .catch(() => false);
- if (!doesPRExist) continue;
+ .then(async crEvents => {
+ const prEvents = [];
+ for (let event of crEvents) {
+ if (!("source" in event)) continue;
+ if (!event.source.issue) continue;
+ const doesPRExist = await octokit.rest.pulls
+ .get({
+ owner: data.org,
+ repo: data.repo,
+ pull_number: event.source.issue.number
+ })
+ .then(() => true)
+ .catch(() => false);
+ if (!doesPRExist) continue;
- const containedInPr = await linkedIssuesForPR(owner, repo, anyEvent.source.issue.number);
- if (containedInPr.map(pr => pr.number).includes(id)) {
- prEvents.push(event);
+ const containedInPr = await linkedIssuesForPR(
+ data.org,
+ data.repo,
+ event.source.issue.number
+ );
+ if (containedInPr.map(pr => pr.number).includes(data.id)) {
+ prEvents.push(event);
+ }
}
- }
- return prEvents;
- })
- .then(prEvents => {
- prsToFetch = prEvents.map(event => (event as any).source.issue.number);
- })
- .catch(() => (linkedPRsOrIssues = []));
- }
- $: if (prsToFetch.length > 0) {
- Promise.all(
- prsToFetch.map(prNumber =>
- octokit.rest.pulls.get({
- owner,
- repo,
- pull_number: prNumber
+ return prEvents;
+ })
+ .then(prEvents => {
+ prsToFetch = prEvents
+ .map(event => event.source.issue?.number || -1)
+ .filter(pr => pr !== -1);
})
+ .catch(() => (linkedPRsOrIssues = []));
+ }
+ });
+ $effect(() => {
+ if (prsToFetch.length > 0) {
+ Promise.all(
+ prsToFetch.map(prNumber =>
+ octokit.rest.pulls.get({
+ owner: data.org,
+ repo: data.repo,
+ pull_number: prNumber
+ })
+ )
)
- )
- .then(prs => {
- if (!linkedPRsOrIssues) {
- linkedPRsOrIssues = [];
- }
- for (let { data } of prs) {
- if (linkedPRsOrIssues.map(i => i.number).includes(data.number)) {
- continue;
+ .then(prs => {
+ if (!linkedPRsOrIssues) {
+ linkedPRsOrIssues = [];
}
- linkedPRsOrIssues.push({
- title: data.title,
- author: {
- login: data.user.login,
- avatarUrl: data.user.avatar_url
- },
- body: data.body ?? "",
- createdAt: data.created_at,
- number: data.number
- });
- }
- linkedPRsOrIssues = linkedPRsOrIssues.sort(
- (a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime()
- );
- prsToFetch = prsToFetch.filter(
- prNumber => !prs.map(pr => pr.data.number).includes(prNumber)
- );
- })
- .catch(() => (prsToFetch = []));
- }
+ for (let { data } of prs) {
+ if (linkedPRsOrIssues.map(i => i.number).includes(data.number)) {
+ continue;
+ }
+ linkedPRsOrIssues.push(data);
+ }
+ prsToFetch = prsToFetch.filter(
+ prNumber => !prs.map(pr => pr.data.number).includes(prNumber)
+ );
+ })
+ .catch(() => (prsToFetch = []));
+ }
+ });
- Detail of {owner}/{repo}#{id} | Svelte Changelog
+ Detail of {data.org}/{data.repo}#{data.id} | Svelte Changelog
{#if info}
diff --git a/src/routes/[pullOrIssue=poi]/[org]/[repo]/[id=number]/BottomCollapsible.svelte b/src/routes/[pullOrIssue=poi]/[org]/[repo]/[id=number]/BottomCollapsible.svelte
index a819f443..22cf63ab 100644
--- a/src/routes/[pullOrIssue=poi]/[org]/[repo]/[id=number]/BottomCollapsible.svelte
+++ b/src/routes/[pullOrIssue=poi]/[org]/[repo]/[id=number]/BottomCollapsible.svelte
@@ -1,24 +1,36 @@
-
+
{#if icon}
-
+ {@const SvelteComponent = icon}
+
{/if}
{label}
@@ -28,7 +40,7 @@
-
+ {@render children?.()}
diff --git a/src/routes/[pullOrIssue=poi]/[org]/[repo]/[id=number]/PageRenderer.svelte b/src/routes/[pullOrIssue=poi]/[org]/[repo]/[id=number]/PageRenderer.svelte
index 0619f319..9308d3a1 100644
--- a/src/routes/[pullOrIssue=poi]/[org]/[repo]/[id=number]/PageRenderer.svelte
+++ b/src/routes/[pullOrIssue=poi]/[org]/[repo]/[id=number]/PageRenderer.svelte
@@ -1,4 +1,4 @@
-
@@ -122,7 +125,7 @@
{type === "pull" ? "Closing issue" : "Development PR"}{linkedEntities.length > 1 ? "s" : ""}
-
+
{#each linkedEntities as entity}
@@ -142,24 +145,28 @@
-
-
-
-
- {entity.author.login.charAt(0).toUpperCase()}
-
-
-
{entity.author.login}
-
-
•
-
{formatToDateTime(entity.createdAt)}
+ {#if "author" in entity}
+
+
+
+
+ {entity.author?.login.charAt(0).toUpperCase()}
+
+
+
{entity.author?.login}
+
+
•
+ {/if}
+ {#if "createdAt" in entity}
+
{formatToDateTime(entity.createdAt)}
+ {/if}
-
+ {#snippet stepIcon()}
+
+ {/snippet}
@@ -349,16 +358,18 @@
Verified
{/if}
{#if commit.sha}
-
-
-
- {commit.sha.slice(0, 7)}
-
-
-
- {commit.sha}
-
-
+
+
+
+
+ {commit.sha.slice(0, 7)}
+
+
+
+ {commit.sha}
+
+
+
{/if}
diff --git a/src/routes/[pullOrIssue=poi]/[org]/[repo]/[id=number]/types.ts b/src/routes/[pullOrIssue=poi]/[org]/[repo]/[id=number]/types.ts
index 7cb90dd0..3ab0c1a9 100644
--- a/src/routes/[pullOrIssue=poi]/[org]/[repo]/[id=number]/types.ts
+++ b/src/routes/[pullOrIssue=poi]/[org]/[repo]/[id=number]/types.ts
@@ -1,15 +1,7 @@
import type { Octokit } from "octokit";
+import type { Issue } from "@octokit/graphql-schema";
-export type LinkedEntity = {
- createdAt: string;
- author: {
- login: string;
- avatarUrl: string;
- };
- number: number;
- body: string;
- title: string;
-};
+export type LinkedEntity = Issue | Awaited>["data"];
export type Issues = InstanceType["rest"]["issues"];
export type Pulls = InstanceType["rest"]["pulls"];
diff --git a/tailwind.config.ts b/tailwind.config.ts
index 51f840cb..54da4062 100644
--- a/tailwind.config.ts
+++ b/tailwind.config.ts
@@ -1,6 +1,7 @@
import type { Config } from "tailwindcss";
import defaultTheme from "tailwindcss/defaultTheme";
import typography from "@tailwindcss/typography";
+import tailwindcssAnimate from "tailwindcss-animate";
export default {
darkMode: ["class"],
@@ -52,9 +53,20 @@ export default {
card: {
DEFAULT: "hsl(var(--card) / )",
foreground: "hsl(var(--card-foreground) / )"
+ },
+ sidebar: {
+ DEFAULT: "hsl(var(--sidebar-background))",
+ foreground: "hsl(var(--sidebar-foreground))",
+ primary: "hsl(var(--sidebar-primary))",
+ "primary-foreground": "hsl(var(--sidebar-primary-foreground))",
+ accent: "hsl(var(--sidebar-accent))",
+ "accent-foreground": "hsl(var(--sidebar-accent-foreground))",
+ border: "hsl(var(--sidebar-border))",
+ ring: "hsl(var(--sidebar-ring))"
}
},
borderRadius: {
+ xl: "calc(var(--radius) + 4px)",
lg: "var(--radius)",
md: "calc(var(--radius) - 2px)",
sm: "calc(var(--radius) - 4px)"
@@ -62,7 +74,7 @@ export default {
fontFamily: {
sans: [...defaultTheme.fontFamily.sans]
},
- typography: () => ({
+ typography: {
DEFAULT: {
css: {
a: {
@@ -70,8 +82,27 @@ export default {
}
}
}
- })
+ },
+ keyframes: {
+ "accordion-down": {
+ from: { height: "0" },
+ to: { height: "var(--bits-accordion-content-height)" }
+ },
+ "accordion-up": {
+ from: { height: "var(--bits-accordion-content-height)" },
+ to: { height: "0" }
+ },
+ "caret-blink": {
+ "0%,70%,100%": { opacity: "1" },
+ "20%,50%": { opacity: "0" }
+ }
+ },
+ animation: {
+ "accordion-down": "accordion-down 0.2s ease-out",
+ "accordion-up": "accordion-up 0.2s ease-out",
+ "caret-blink": "caret-blink 1.25s ease-out infinite"
+ }
}
},
- plugins: [typography]
+ plugins: [typography, tailwindcssAnimate]
} satisfies Config;