Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/developer.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ jobs:
cancel-in-progress: true
env:
PUBLIC_TRAKT_CLIENT_ID: synthetic-ci-client-id-never-real
PUBLIC_GITHUB_CLIENT_ID: synthetic-ci-client-id-never-real
defaults:
run:
working-directory: projects/developer
Expand All @@ -48,6 +49,7 @@ jobs:
- run: deno task build
env:
PUBLIC_TRAKT_CLIENT_ID: synthetic-ci-client-id-never-real
PUBLIC_GITHUB_CLIENT_ID: synthetic-ci-client-id-never-real

deploy:
needs: verify
Expand Down Expand Up @@ -77,6 +79,7 @@ jobs:
- run: deno task build
env:
PUBLIC_TRAKT_CLIENT_ID: ${{ vars.PUBLIC_TRAKT_CLIENT_ID }}
PUBLIC_GITHUB_CLIENT_ID: ${{ vars.PUBLIC_GITHUB_CLIENT_ID }}
- uses: actions/configure-pages@v6
- uses: actions/upload-pages-artifact@v5
with:
Expand Down
1 change: 1 addition & 0 deletions projects/developer/.env.example
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
# Public, shipped in the browser bundle and in authorization redirects.
PUBLIC_TRAKT_CLIENT_ID=
PUBLIC_GITHUB_CLIENT_ID=
4 changes: 3 additions & 1 deletion projects/developer/src/lib/api/accountRequest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,11 @@ export async function accountRequest(
});
if (!response.ok) {
const messages: Record<number, string> = {
400:
'Your GitHub connection could not be verified. Reconnect and try again.',
401: 'Your session has expired. Refresh your account or sign in again.',
403:
'This account cannot perform this action. Creating an app requires VIP and access to app management.',
'This account cannot perform this action. Check your app limit and your GitHub account connection.',
404: 'This app is no longer available. Reload your apps.',
422: 'Check your app details. The server could not accept these values.',
429: 'Too many requests. Please wait before trying again.',
Expand Down
2 changes: 1 addition & 1 deletion projects/developer/src/lib/auth/accountNavigation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export function selectedSlot(): number | null {
export function rememberSlot(slot: number): void {
globalThis.sessionStorage?.setItem(ACTIVE_SLOT, String(slot));
}
function safeReturnPath(value: string | null): string {
export function safeReturnPath(value: string | null): string {
if (value && /^\/apps(?:\/(?:new|[1-9]\d*(?:\/edit)?))?\/?$/.test(value)) {
return value.replace(/\/$/, '');
}
Expand Down
79 changes: 78 additions & 1 deletion projects/developer/src/lib/features/apps/ApplicationForm.svelte
Original file line number Diff line number Diff line change
@@ -1,13 +1,21 @@
<script lang="ts">
import { onMount } from "svelte";
import type { Application, ApplicationInput } from "./applications.ts";
import { parseApplication } from "./validateApplication.ts";
import {
githubConnectUrl,
takeGithubCode,
takeGithubDraft,
} from "./githubConnect.ts";
const {
app,
linkedGithubUsername,
busy,
onSave,
onCancel,
}: {
app?: Application;
linkedGithubUsername: string | null;
busy: boolean;
onSave: (input: ApplicationInput) => void;
onCancel: () => void;
Expand All @@ -19,10 +27,45 @@
let redirects = $state(initial?.redirect_uri ?? "");
let origins = $state(initial?.origins.join("\n") ?? "");
let error = $state("");
const githubUsername = $derived(
initial?.github_username ?? linkedGithubUsername ?? null,
);
let githubCode = $state<string | null>(null);
onMount(() => {
const code = takeGithubCode();
if (!code) return;
githubCode = code;
const draft = takeGithubDraft();
if (!draft) return;
name = draft.name;
description = draft.description;
redirects = draft.redirects;
origins = draft.origins;
});
function connectGithub() {
globalThis.location.assign(
githubConnectUrl(globalThis.location.pathname, {
name,
description,
redirects,
origins,
}),
);
}
function submit(event: SubmitEvent) {
event.preventDefault();
if (!app && !githubCode && !githubUsername) {
error = "Connect your GitHub account before creating an app.";
return;
}
try {
const input = parseApplication(name, description, redirects, origins);
const input = parseApplication(
name,
description,
redirects,
origins,
githubCode ?? undefined,
);
error = "";
onSave(input);
} catch (cause) {
Expand Down Expand Up @@ -67,6 +110,27 @@
placeholder="https://example.com"
spellcheck="false"></textarea></label
>
<div class="github-connect">
<strong>GitHub account</strong>
{#if githubCode}
<span
>GitHub connected. Save to {app ? "update" : "attach"} your handle.</span
>
{:else if githubUsername}
<span>Connected as <strong>@{githubUsername}</strong></span>
{:else}
<span
>{app
? "Connect a GitHub account to verify this app."
: "Connect a GitHub account once to verify who you are."}</span
>
{/if}
<button type="button" onclick={connectGithub}
>{githubUsername && !githubCode
? "Re-verify"
: "Connect GitHub"}</button
>
</div>
{#if !app}<p>
By creating an app, you agree to the <a
href="/?section=guides&guide=create-an-app">Trakt API requirements</a
Expand Down Expand Up @@ -103,6 +167,19 @@
font-weight: 400;
line-height: 1.6;
}
.github-connect {
display: grid;
gap: 9px;
font-size: 14px;
font-weight: 600;
border: 1px solid var(--color-border);
border-radius: var(--radius-control);
padding: 16px;
}
.github-connect button {
@include action.base;
justify-self: start;
}
input,
textarea {
width: 100%;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,7 @@

{#if session.account}
{#key `${session.account.slot}:${mode}:${appId ?? ""}`}
<Applications
slot={session.account.slot}
vip={session.vip}
{mode}
{appId}
{appName}
/>
<Applications slot={session.account.slot} {mode} {appId} {appName} />
{/key}
{:else}
<section class="welcome">
Expand Down
37 changes: 7 additions & 30 deletions projects/developer/src/lib/features/apps/Applications.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,18 @@

const {
slot,
vip,
mode,
appId,
appName,
}: ApplicationPageProps & {
slot: number;
vip: boolean | null;
} = $props();
let loading = $state(true);
let apps = $state<Application[]>([]);
const selected = $derived(apps.find((app) => app.id === appId));
const linkedGithubUsername = $derived(
apps.find((app) => app.github_username)?.github_username ?? null,
);
const displayName = $derived(
selected?.name ?? appName ?? (loading ? "Loading…" : "App details"),
);
Expand Down Expand Up @@ -89,8 +90,7 @@
}
}
async function save(input: ApplicationInput) {
if (busy || (mode === "new" && !vip) || (mode === "edit" && !selected))
return;
if (busy || (mode === "edit" && !selected)) return;
busy = true;
error = "";
try {
Expand Down Expand Up @@ -167,7 +167,7 @@
: "Manage credentials and settings for this app."}
</p>
</div>
{#if mode === "list" && vip}<a class="button primary" href="/apps/new"
{#if mode === "list"}<a class="button primary" href="/apps/new"
>+ Create app</a
>
{/if}
Expand All @@ -178,18 +178,8 @@
</div>{/if}
{#if notice}<p class="message" role="status">{notice}</p>{/if}
{#if mode === "list"}
{#if vip === false}<aside>
<strong>Creating apps is a VIP feature</strong>
<p>You can still manage your existing apps.</p>
<a href="https://app.trakt.tv/vip" target="_blank" rel="noreferrer"
>Explore Trakt VIP ↗</a
>
</aside>{:else if vip === null}<p class="muted">
Account eligibility is unavailable. Refresh your account to check
whether you can create apps.
</p>{/if}
{#if loading}<div class="empty" role="status">Loading your apps…</div>
{:else if vip && !error && apps.length === 0}<div class="empty">
{:else if !error && apps.length === 0}<div class="empty">
<span class="symbol">&lt;/&gt;</span>
<h2>Your next idea starts here</h2>
<p>Register an app to get your Client ID and Client Secret.</p>
Expand Down Expand Up @@ -224,24 +214,11 @@
<p>This app is unavailable for the selected account.</p>
<a href="/apps">Back to My Apps</a>
</section>
{:else if mode === "new" && !vip}
<section class="panel">
<h2>
{vip === null
? "Checking account eligibility"
: "Creating apps is a VIP feature"}
</h2>
<p>
{vip === null
? "Refresh your account if eligibility remains unavailable."
: "You can still manage your existing apps."}
</p>
<a href="/apps">Back to My Apps</a>
</section>
{:else if mode === "new" || (mode === "edit" && selected)}
<section class="panel">
<ApplicationForm
app={mode === "edit" ? selected : undefined}
{linkedGithubUsername}
{busy}
onSave={save}
onCancel={() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,6 @@ describe('app management transport', () => {
new Response('sensitive-server-output', { status: 403 }),
),
);
await expect(saveApplication(0, input)).rejects.toThrow('VIP');
await expect(saveApplication(0, input)).rejects.toThrow('app limit');
});
});
2 changes: 2 additions & 0 deletions projects/developer/src/lib/features/apps/applications.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export const applicationSchema = z.object({
approved_at: z.string().nullish(),
scopes: z.array(z.string()),
created_at: z.string(),
github_username: z.string().nullish(),
permissions: z.object({
scrobble: z.boolean().nullish(),
checkin: z.boolean().nullish(),
Expand All @@ -25,6 +26,7 @@ export type ApplicationInput = {
description?: string;
redirect_uri: string[];
origins: string[];
github_code?: string;
};

export async function listApplications(slot: number): Promise<Application[]> {
Expand Down
Loading
Loading