import type { Did } from "@atcute/lexicons/syntax";
import type { OAuthUserAgent } from "@atcute/oauth-browser-client";
import { getOrCreateDelegatedSession } from "#lib/api/actAs.ts";
import type { BobbinContext } from "#lib/api/client.ts";
import { PROFILE_COLLECTION } from "#lib/api/profile.ts";
import { loadProfile, writeProfile } from "#lib/api/profileSettings.ts";
import type { ProfileRecord } from "#lib/api/records.ts";
export const PINNED_LIMIT = 6;
export type PinnedKey = NonNullable<ProfileRecord["pinnedRepositories"]>[number];
// legacy repos are pinned by at-uri, which the lexicon still types as a did
export const asPinnedKey = (key: string) => key as PinnedKey;
export const pinKeyOf = (repo: { repoDid: string; uri?: string }): PinnedKey =>
asPinnedKey(
repo.uri && !repo.uri.includes("/org.tangled.repo.manifest/") ? repo.uri : repo.repoDid
);
export const addPinned = <K extends string>(keys: readonly K[], key: K): K[] => {
const existing = keys.filter((entry) => entry !== key);
if (existing.length >= PINNED_LIMIT) {
throw new Error(
`Unpin a repository first. Profiles show up to ${PINNED_LIMIT} pinned repositories.`
);
}
return [...existing, key];
};
export const removePinned = <K extends string>(keys: readonly K[], key: K): K[] =>
keys.filter((entry) => entry !== key);
export const movePinned = <K extends string>(keys: readonly K[], from: number, to: number): K[] => {
if (from === to || from < 0 || from >= keys.length) return [...keys];
const next = [...keys];
const [moved] = next.splice(from, 1);
next.splice(Math.min(Math.max(to, 0), next.length), 0, moved);
return next;
};
export const pinnedWith = (
current: ProfileRecord | null,
key: string,
pinned: boolean
): ProfileRecord => {
const keys = current?.pinnedRepositories ?? [];
const pin = asPinnedKey(key);
const next = pinned ? addPinned(keys, pin) : removePinned(keys, pin);
const { pinnedRepositories: _pinnedRepositories, ...rest } = current ?? {};
return {
...rest,
$type: PROFILE_COLLECTION,
...(next.length > 0 ? { pinnedRepositories: next } : {})
};
};
export const setRepositoryPinned = async (
ctx: BobbinContext,
agent: OAuthUserAgent,
ownerDid: Did,
key: string,
pinned: boolean
): Promise<ProfileRecord> => {
const ownerAgent =
agent.sub === ownerDid ? agent : await getOrCreateDelegatedSession(agent, ownerDid);
const current = await loadProfile(ctx, ownerDid);
return writeProfile(ctx, ownerAgent, pinnedWith(current, key, pinned));
};