Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use real add-on data in SvelteKit layout #460

Merged
merged 14 commits into from
Mar 6, 2024
4 changes: 2 additions & 2 deletions src/addons/browser/browser.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { writable } from "svelte/store";
import { baseApiUrl } from "../../api/base.js";
import { BASE_API_URL } from "@/config/config.js";

export const filter = writable("featured");

Expand Down Expand Up @@ -52,7 +52,7 @@ export function buildParams({
}

export function buildUrl({ query = "", filters = {}, per_page = 5 }) {
const u = new URL("addons/", baseApiUrl);
const u = new URL("addons/", BASE_API_URL);

u.search = new URLSearchParams({
query,
Expand Down
5 changes: 0 additions & 5 deletions src/addons/types.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,3 @@
interface Author {
name?: string;
avatar?: string;
}

type AddOnCategory = "premium" | string;

interface AddOnProperty {
Expand Down
30 changes: 30 additions & 0 deletions src/lib/api/addons.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { error } from "@sveltejs/kit";
import { BASE_API_URL } from "@/config/config.js";
import { type AddOnListItem } from "@/addons/types";
import { isErrorCode } from "../utils";
import type { Page } from "@/api/types/common";

export async function getPinnedAddons(
fetch = globalThis.fetch,
): Promise<Page<AddOnListItem>> {
const endpoint = new URL(
"/api/addons/?active=true&per_page=100",
BASE_API_URL,
);
const resp = await fetch(endpoint, { credentials: "include" });
if (isErrorCode(resp.status)) {
error(resp.status, resp.statusText);
}
return resp.json();
}

export async function getAddons(
fetch = globalThis.fetch,
): Promise<Page<AddOnListItem>> {
const endpoint = new URL("/api/addons/?per_page=100", BASE_API_URL);
const resp = await fetch(endpoint, { credentials: "include" });
if (isErrorCode(resp.status)) {
error(resp.status, resp.statusText);
}
return resp.json();
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
import { describe, test as base, expect } from "vitest";
import { APP_URL, IMAGE_WIDTHS_ENTRIES } from "@/config/config.js";

import * as documents from "./documents.js";
import * as documents from "./documents";
import type { Document } from "./types";

type Use<T> = (value: T) => Promise<void>;

const test = base.extend({
document: async ({}, use) => {
document: async ({}, use: Use<Document>) => {
const document = await import(
"./fixtures/documents/document-expanded.json"
);

await use(document);
await use(document as unknown as Document);
},
});

Expand Down
30 changes: 10 additions & 20 deletions src/lib/api/documents.js → src/lib/api/documents.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,13 @@
/** API helpers related to documents.
* Lots of duplicated code here that should get consolidated at some point.
*/
import { error } from "@sveltejs/kit";
import { error, type NumericRange } from "@sveltejs/kit";
import { APP_URL, BASE_API_URL } from "@/config/config.js";
import { DEFAULT_EXPAND } from "@/api/common.js";
import { isOrg } from "@/api/types/orgAndUser";
import { isErrorCode } from "../utils";

/**
* Search documents
*
* @async
* @param {query} string
* @param {boolean} highlight
* @param {globalThis.fetch} fetch
* @returns {import('./types').DocumentResults}
*/
/** Search documents */
export async function search(
query = "",
highlight = false,
Expand All @@ -24,11 +17,11 @@ export async function search(

endpoint.searchParams.set("expand", DEFAULT_EXPAND);
endpoint.searchParams.set("q", query);
endpoint.searchParams.set("hl", highlight);
endpoint.searchParams.set("hl", String(highlight));

const resp = await fetch(endpoint, { credentials: "include" });

if (!resp.ok) {
if (isErrorCode(resp.status)) {
error(resp.status, resp.statusText);
}

Expand All @@ -38,21 +31,18 @@ export async function search(
/**
* Load a single document from the API
* Example: https://api.www.documentcloud.org/api/documents/1/
*
* @async
* @export
* @param {number} id
* @param {globalThis.fetch} fetch
* @returns {import('./types').Document}
*/
export async function get(id, fetch) {
export async function get(
id: number,
fetch: typeof globalThis.fetch,
): Promise<Document> {
const endpoint = new URL(`documents/${id}.json`, BASE_API_URL);
const expand = ["user", "organization", "projects", "revisions"];
endpoint.searchParams.set("expand", expand.join(","));

const resp = await fetch(endpoint, { credentials: "include" });

if (!resp.ok) {
if (isErrorCode(resp.status)) {
error(resp.status, resp.statusText);
}

Expand Down
2 changes: 1 addition & 1 deletion src/lib/api/embed.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { test, describe, expect } from "vitest";
import { APP_URL, BASE_API_URL } from "@/config/config.js";
import { embedUrl, getEmbed } from "./embed.js";

import * as documents from "./documents.js";
import * as documents from "./documents";
import * as notes from "./notes.js";

import document from "./fixtures/documents/document.json";
Expand Down
2 changes: 1 addition & 1 deletion src/lib/api/notes.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { error } from "@sveltejs/kit";
import { BASE_API_URL } from "@/config/config.js";
import { DEFAULT_EXPAND } from "@/api/common.js";
import { canonicalUrl } from "./documents.js";
import { canonicalUrl } from "./documents";

/**
* Load notes from a single document from the API
Expand Down
2 changes: 1 addition & 1 deletion src/lib/api/notes.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { test as base, describe, expect } from "vitest";

import { APP_URL } from "@/config/config.js";
import * as notes from "./notes.js";
import * as documents from "./documents.js";
import * as documents from "./documents";

const test = base.extend({
document: async ({}, use) => {
Expand Down
49 changes: 49 additions & 0 deletions src/lib/api/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,55 @@ export type sizes = "thumbnail" | "small" | "normal" | "large" | "xlarge";

export type Highlight = Record<string, string[]>;

type AddOnCategory = "premium" | string;

interface AddOnProperty {
type: string;
title: string;
description?: string;
default?: string;
format?: string;
enum?: string[];
}

interface AddOnParameters {
type: string;
version: number;
title: string;
description: string;
instructions: string;
categories: AddOnCategory[];
documents: string[];
required: string[];
properties: Record<string, AddOnProperty>;
cost: {
amount: number;
price: number;
unit: string;
};
eventOptions: {
name: string;
events: string[];
};
}

// API endpoint https://api.www.documentcloud.org/api/addons/
export interface AddOnListItem {
id: number;
user: number;
organization: number;
access: "public" | "private";
name: string;
repository: string;
parameters: Partial<AddOnParameters>;
created_at: string;
updated_at: string;
active: boolean;
featured: boolean;
default: boolean;
usage?: number;
}

// https://www.documentcloud.org/help/api#documents
export interface Document {
id: number | string;
Expand Down
2 changes: 1 addition & 1 deletion src/lib/components/common/Action.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
class="container"
role="button"
tabindex={0}
on:click|stopPropagation|preventDefault
on:click|stopPropagation
on:keydown|stopPropagation
>
{#if icon}<svelte:component this={icon} height="14" width="14" />{/if}
Expand Down
2 changes: 1 addition & 1 deletion src/lib/components/documents/DocumentListItem.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
pageImageUrl,
canonicalUrl,
userOrgString,
} from "$lib/api/documents.js";
} from "@/lib/api/documents";
import { pageSizesFromSpec } from "@/api/pageSize.js";

export let document: Document;
Expand Down
1 change: 1 addition & 0 deletions src/lib/utils/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { isErrorCode } from "./isErrorCode";
5 changes: 5 additions & 0 deletions src/lib/utils/isErrorCode.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import type { NumericRange } from "@sveltejs/kit";

export function isErrorCode(status: number): status is NumericRange<400, 599> {
return status >= 400 && status <= 599;
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// load a note for embedding
import * as documents from "$lib/api/documents.js";
import * as documents from "@/lib/api/documents";
import * as notesApi from "$lib/api/notes.js";

/** @type {import('./$types').PageLoad} */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import { _ } from "svelte-i18n";

import { informSize } from "@/embed/iframeSizer.js";
import { pageImageUrl } from "$lib/api/documents.js";
import { pageImageUrl } from "@/lib/api/documents";
import * as notes from "$lib/api/notes.js";
import { embedUrl } from "$lib/api/embed.js";
import { canonicalNoteUrl, noteUrl } from "$lib/api/notes.js";
Expand Down
2 changes: 1 addition & 1 deletion src/routes/(embed)/documents/[id]/pages/[page]/+page.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// load data for a single page embed
import * as documents from "$lib/api/documents.js";
import * as documents from "@/lib/api/documents";
import * as notesApi from "$lib/api/notes.js";

/** @type {import('./$types').PageLoad} */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
pageUrl,
textUrl,
userOrgString,
} from "$lib/api/documents.js";
} from "@/lib/api/documents";
import { embedUrl } from "$lib/api/embed.js";

export let data;
Expand Down
28 changes: 27 additions & 1 deletion src/routes/app/+layout.svelte
Original file line number Diff line number Diff line change
@@ -1,9 +1,35 @@
<script lang="ts">
import MainLayout from "@/lib/components/MainLayout.svelte";
import "@/style/kit.css";
import Projects from "./sidebar/Projects.svelte";
import Documents from "./sidebar/Documents.svelte";
import Button from "@/lib/components/common/Button.svelte";
import { PlusCircle16 } from "svelte-octicons";
import Actions from "./sidebar/Actions.svelte";
import AddOns from "./sidebar/AddOns.svelte";
import type { AddOnListItem } from "@/lib/api/types";
import type { Page } from "@/api/types";

export let data: {
pinnedAddons: Promise<Page<AddOnListItem>>;
};
</script>

<svelte:head>
<title>DocumentCloud</title>
</svelte:head>

<slot />
<MainLayout>
<svelte:fragment slot="navigation">
<Documents />
<Projects />
</svelte:fragment>

<slot slot="content" />

<svelte:fragment slot="action">
<Button mode="primary"><PlusCircle16 /> Upload Documents</Button>
<Actions />
<AddOns pinnedAddOns={data.pinnedAddons} />
</svelte:fragment>
</MainLayout>
8 changes: 8 additions & 0 deletions src/routes/app/+layout.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { getPinnedAddons } from "@/lib/api/addons";

export async function load({ url, fetch }) {
const pinnedAddons = getPinnedAddons(fetch);
return {
pinnedAddons,
};
}
Loading
Loading