Skip to content

Commit

Permalink
fix: Paginate library (#3)
Browse files Browse the repository at this point in the history
* fix: Paginate library

* fix: Preview selected library in settings

* fix: Format
  • Loading branch information
pettermachado authored Sep 26, 2024
1 parent 74a30a5 commit dd591e0
Show file tree
Hide file tree
Showing 3 changed files with 146 additions and 27 deletions.
3 changes: 3 additions & 0 deletions app/routes/settings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { ExclamationTriangleIcon } from "@radix-ui/react-icons";
import { toast } from "sonner";
import { MetaFunction } from "@remix-run/node";
import { pageTitle } from "~/lib/utils";
import AssignableSelect from "~/components/AssignableSelect";

export const meta: MetaFunction = () => {
return [{ title: pageTitle("Settings") }];
Expand Down Expand Up @@ -73,6 +74,8 @@ export default function Settings() {
})}
</DropdownMenuContent>
</DropdownMenu>
<h3 className="mt-3 mb-2 text-sm">Preview selected library</h3>
<AssignableSelect value={null} onChange={() => {}} />
</Page>
);
}
14 changes: 7 additions & 7 deletions lib/soundtrack-api/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@ const defaultOpts = {} as RunOptions;
* @param options See: RunOption
* @returns The query response as T
*/
export async function runQuery<T>(
export async function runQuery<T, A>(
document: string,
variables: unknown,
variables: A,
options?: RunOptions,
): Promise<QueryResponse<T>> {
return await run(document, variables, options);
Expand All @@ -37,17 +37,17 @@ export async function runQuery<T>(
* @param options See: RunOption
* @returns The mutation response as T
*/
export async function runMutation<T>(
export async function runMutation<T, A>(
document: string,
variables: unknown,
variables: A,
options?: RunOptions,
): Promise<QueryResponse<T>> {
return await run(document, variables, options);
}

async function run<T>(
async function run<T, A>(
document: string,
variables: unknown,
variables: A,
options?: RunOptions,
): Promise<QueryResponse<T>> {
if (!process.env.SOUNDTRACK_API_URL) {
Expand All @@ -60,7 +60,7 @@ async function run<T>(
const opts = options ?? defaultOpts;

const body = JSON.stringify({ query: document, variables });
logger.debug("GraphQL request body: " + body);
logger.info("GraphQL request body: " + body);
const res = await fetch(process.env.SOUNDTRACK_API_URL, {
method: "POST",
headers: {
Expand Down
156 changes: 136 additions & 20 deletions lib/soundtrack-api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,16 @@ const logger = pino();

export class Api {
async getAccounts(): Promise<Account[]> {
const res = await runQuery<AccountsQuery>(accountsQuery, {});
const res = await runQuery<AccountsQuery, AccountsQueryArgs>(
accountsQuery,
undefined,
);
return res.data.me.accounts.edges.map(({ node }) => node);
}
async getAccount(accountId: string): Promise<Account> {
const res = await runQuery<AccountQuery>(accountQuery, { id: accountId });
const res = await runQuery<AccountQuery, AccountQueryArgs>(accountQuery, {
id: accountId,
});
return res.data.account;
}
async getAccountZones(accountId: string): Promise<Zone[]> {
Expand All @@ -27,10 +32,13 @@ export class Api {
cursor: string | null,
acc: Zone[],
): Promise<Zone[]> {
const res = await runQuery<AccountZonesQuery>(accountZonesQuery, {
id: accountId,
cursor,
});
const res = await runQuery<AccountZonesQuery, AccountZonesQueryArgs>(
accountZonesQuery,
{
id: accountId,
cursor,
},
);
const zoneFn = toZoneFn(accountId);
const zones: Zone[] = acc.concat(
res.data.account.soundZones.edges.map(({ node }) => zoneFn(node)),
Expand All @@ -43,7 +51,9 @@ export class Api {
}
}
async getZone(zoneId: string): Promise<Zone> {
const res = await runQuery<ZoneQuery>(zoneQuery, { id: zoneId });
const res = await runQuery<ZoneQuery, ZoneQueryArgs>(zoneQuery, {
id: zoneId,
});
return res.data.soundZone;
}
async getZones(): Promise<Zone[]> {
Expand All @@ -54,10 +64,13 @@ export class Api {
return zones.flat();
}
async assignMusic(zoneId: string, playFromId: string): Promise<void> {
await runMutation<AssignMutation>(assignMutation, { zoneId, playFromId });
await runMutation<AssignMutation, AssignMutationArgs>(assignMutation, {
zoneId,
playFromId,
});
}
async getAssignable(assignableId: string): Promise<Assignable | null> {
const res = await runQuery<AssignableQuery>(
const res = await runQuery<AssignableQuery, AssignableQueryArgs>(
assignableQuery,
{ assignableId },
{ errorPolicy: "all" },
Expand All @@ -70,18 +83,73 @@ export class Api {
return item ? toAssignable(item) : null;
}
async getLibrary(accountId: string): Promise<AccountLibrary> {
const res = await runQuery<LibraryQuery>(libraryQuery, { accountId });
const res = await this.getLibraryPage(
accountId,
{ playlists: null, schedules: null },
{ playlists: [], schedules: [] },
);
return res;
}
private async getLibraryPage(
accountId: string,
opts: LibraryPageOpts,
acc: AccountLibrary,
): Promise<AccountLibrary> {
const res = await runQuery<LibraryQuery, LibraryQueryArgs>(libraryQuery, {
accountId,
playlists: opts.playlists !== false,
playlistCursor: libraryOptToCursor(opts.playlists),
schedules: opts.schedules !== false,
scheduleCursor: libraryOptToCursor(opts.schedules),
});

const musicLibrary = res.data.account.musicLibrary;
const playlists: Assignable[] =
res.data.account.musicLibrary.playlists.edges.map(toAssignableNode);
musicLibrary.playlists?.edges.map(toAssignableNode) ?? [];
const schedules: Assignable[] =
res.data.account.musicLibrary.schedules.edges.map(toAssignableNode);
return {
playlists,
schedules,
musicLibrary.schedules?.edges.map(toAssignableNode) ?? [];

const nextOpts: LibraryPageOpts = {
playlists: libraryOptFromPageInfo(musicLibrary.playlists?.pageInfo),
schedules: libraryOptFromPageInfo(musicLibrary.schedules?.pageInfo),
};

const library: AccountLibrary = {
playlists: acc.playlists.concat(playlists),
schedules: acc.schedules.concat(schedules),
};

if (libraryPageOptsIsEmpty(nextOpts)) {
return library;
}

return this.getLibraryPage(accountId, nextOpts, library);
}
}

type LibraryPageOpts = {
playlists: boolean | string | null;
schedules: boolean | string | null;
};

function libraryPageOptsIsEmpty(opts: LibraryPageOpts): boolean {
return opts.playlists === false && opts.schedules === false;
}

function libraryOptToCursor(opt: boolean | string | null): string | null {
if (typeof opt === "boolean") return null;
return opt;
}

function libraryOptFromPageInfo(
pageInfo: PageInfo | undefined,
): boolean | string {
if (!pageInfo) return false;
return pageInfo.hasNextPage && pageInfo.endCursor
? pageInfo.endCursor
: false;
}

function toZoneFn(accountId: string) {
return function toZone(zone: AccountZone): Zone {
return { ...zone, account: { id: accountId } };
Expand Down Expand Up @@ -114,6 +182,8 @@ type AccountsQuery = {
};
};

type AccountsQueryArgs = undefined;

const accountsQuery = `
query SchedulerAccounts {
me {
Expand All @@ -135,6 +205,10 @@ type AccountQuery = {
account: Account;
};

type AccountQueryArgs = {
id: string;
};

const accountQuery = `
query SchedulerAccount($id: ID!) {
account(id: $id) {
Expand All @@ -156,6 +230,11 @@ type AccountZonesQuery = {
};
};

type AccountZonesQueryArgs = {
id: string;
cursor: string | null;
};

const accountZonesQuery = `
query Scheduler_Zones($id: ID!, $cursor: String) {
account(id: $id) {
Expand Down Expand Up @@ -184,6 +263,10 @@ type ZoneQuery = {
soundZone: Zone;
};

type ZoneQueryArgs = {
id: string;
};

const zoneQuery = `
query Scheduler_Zone($id: ID!) {
soundZone(id: $id) {
Expand Down Expand Up @@ -219,10 +302,12 @@ type MusicLibraryNode = {
};

type MusicLibrary = {
playlists: {
playlists?: {
pageInfo: PageInfo;
edges: MusicLibraryNode[];
};
schedules: {
schedules?: {
pageInfo: PageInfo;
edges: MusicLibraryNode[];
};
};
Expand Down Expand Up @@ -261,21 +346,43 @@ type LibraryQuery = {
};
};

type LibraryQueryArgs = {
accountId: string;
playlistCursor: string | null;
playlists: boolean;
scheduleCursor: string | null;
schedules: boolean;
};

const libraryQuery = `
${displayFragment}
${playlistFragment}
${scheduleFragment}
query Scheduler_Library($accountId: ID!) {
query Scheduler_Library(
$accountId: ID!
$playlistCursor: String
$playlists: Boolean!
$scheduleCursor: String
$schedules: Boolean!
) {
account(id: $accountId) {
musicLibrary {
playlists(first:1000) {
playlists(first:1000, after: $playlistCursor) @include(if: $playlists) {
pageInfo {
hasNextPage
endCursor
}
edges {
node {
...PlaylistFragment
}
}
}
schedules(first:1000) {
schedules(first:1000, after: $scheduleCursor) @include(if: $schedules) {
pageInfo {
hasNextPage
endCursor
}
edges {
node {
...ScheduleFragment
Expand All @@ -290,6 +397,11 @@ type AssignMutation = {
soundZones: string[];
};

type AssignMutationArgs = {
zoneId: string;
playFromId: string;
};

const assignMutation = `
mutation Scheduler_Assign($zoneId: ID!, $playFromId: ID!) {
soundZoneAssignSource(input: { soundZones: [$zoneId], source: $playFromId }) {
Expand All @@ -303,6 +415,10 @@ type AssignableQuery = {
schedule: LibraryItem | null;
};

type AssignableQueryArgs = {
assignableId: string;
};

const assignableQuery = `
${displayFragment}
${playlistFragment}
Expand Down

0 comments on commit dd591e0

Please sign in to comment.