Frontend: Magnetar endpoint pagination
ci/woodpecker/push/ociImagePush Pipeline was successful Details

This commit is contained in:
Natty 2024-01-12 03:56:22 +01:00
parent 80a29f771f
commit 4834daceec
Signed by: natty
GPG Key ID: BF6CB659ADEE60EC
11 changed files with 719 additions and 125 deletions

View File

@ -0,0 +1,399 @@
<template>
<transition :name="$store.state.animation ? 'fade' : ''" mode="out-in">
<MkLoading v-if="initialFetching" />
<MkError v-else-if="error" @retry="init()" />
<div v-else-if="empty" key="_empty_" class="empty">
<slot name="empty">
<div class="_fullinfo">
<img
src="/static-assets/badges/info.png"
class="_ghost"
alt="Error"
/>
<div>{{ i18n.ts.nothing }}</div>
</div>
</slot>
</div>
<div v-else ref="rootEl" class="list">
<div
v-show="pagination.reversed && next"
key="_more_"
class="cxiknjgy _gap"
>
<MkButton
v-if="!moreFetching"
class="button"
:disabled="moreFetching"
:style="{ cursor: moreFetching ? 'wait' : 'pointer' }"
primary
@click="fetchMore"
>
{{ i18n.ts.loadMore }}
</MkButton>
<MkLoading v-else class="loading" />
</div>
<slot name="items" :items="items"></slot>
<div
v-show="!pagination.reversed && next"
key="_more_"
class="cxiknjgy _gap"
>
<MkButton
v-if="!moreFetching"
v-appear="
$store.state.enableInfiniteScroll && !disableAutoLoad
? fetchMore
: null
"
class="button"
:disabled="moreFetching"
:style="{ cursor: moreFetching ? 'wait' : 'pointer' }"
primary
@click="fetchMore"
>
{{ i18n.ts.loadMore }}
</MkButton>
<MkLoading v-else class="loading" />
</div>
</div>
</transition>
</template>
<script
lang="ts"
setup
generic="T extends BackendApiEndpoint<
T['method'] & Method,
T['pathParams'] & string[],
T['request'],
T['response'],
T['paginated'] & true
>"
>
import {
computed,
ComputedRef,
isRef,
onActivated,
onDeactivated,
ref,
watch,
} from "vue";
import * as os from "@/os";
import {
getScrollContainer,
getScrollPosition,
isTopVisible,
onScrollTop,
} from "@/scripts/scroll";
import MkButton from "@/components/MkButton.vue";
import {
BackendApiEndpoint,
Method,
PaginatedResult,
types,
} from "magnetar-common";
import { SpanFilter } from "magnetar-common/built/types/SpanFilter";
import { i18n } from "@/i18n";
type PathParams = {
[K in keyof T["pathParams"] as T["pathParams"][K] & string]:
| string
| number;
};
export type Paging = {
endpoint: T;
pathParams: PathParams | ComputedRef<PathParams>;
params?: T["request"] | ComputedRef<T["request"]>;
limit?: number;
reversed?: boolean;
};
const props = withDefaults(
defineProps<{
pagination: Paging;
disableAutoLoad?: boolean;
displayLimit?: number;
}>(),
{
displayLimit: 30,
}
);
const emit = defineEmits<{
(ev: "queue", count: number): void;
}>();
type PageItem = PaginatedResult<T["response"]>["data"][number] & { id: string };
const rootEl = ref<HTMLElement>();
const items = ref<PageItem[]>([]);
const queue = ref<PageItem[]>([]);
const initialFetching = ref(true);
const moreFetching = ref(false);
const next = ref<URL | null>(null);
const backed = ref(false);
const isBackTop = ref(false);
const empty = computed(() => items.value.length === 0);
const error = ref(false);
const fetch = async (
pagination: SpanFilter
): Promise<PaginatedResult<T["response"]> & { data: { id: string } }[]> => {
const pathParams = isRef(props.pagination.pathParams)
? props.pagination.pathParams.value
: props.pagination.pathParams;
const params = isRef(props.pagination.params)
? props.pagination.params.value
: props.pagination.params;
return os
.magApi(
props.pagination.endpoint,
{
...params,
pagination,
limit: props.pagination.limit,
},
pathParams,
undefined
)
.then(
(res) =>
res as PaginatedResult<T["response"]> &
{ data: { id: string } }[]
);
};
const init = async (): Promise<void> => {
queue.value = [];
initialFetching.value = true;
fetch({}).then(
(res) => {
items.value = props.pagination.reversed
? [...res.data].reverse()
: res.data;
next.value = res.next ?? null;
error.value = false;
initialFetching.value = false;
},
(err) => {
error.value = true;
initialFetching.value = false;
}
);
};
const reload = (): void => {
items.value = [];
init();
};
const refresh = async (): Promise<void> => {
fetch({}).then(
(res) => {
let ids = new Set(items.value.map((i) => i.id));
for (let i = 0; i < res.data.length; i++) {
const item = res.data[i];
if (!updateItem(item.id, (old) => item)) {
append(item);
}
ids.delete(item.id);
}
for (const id in ids) {
removeItem((i) => i.id === id);
}
},
(err) => {
error.value = true;
initialFetching.value = false;
}
);
};
const fetchMore = async (): Promise<void> => {
if (
!next.value ||
initialFetching.value ||
moreFetching.value ||
items.value.length === 0
)
return;
if (next.value.searchParams.has("pagination")) return;
const nextCursorRaw = next.value.searchParams.get("pagination") as string;
const nextCursorDecoded = {
...Object.fromEntries(new URLSearchParams(nextCursorRaw).entries()),
} as types.PaginationShape["pagination"];
moreFetching.value = true;
backed.value = true;
await fetch(nextCursorDecoded).then(
(res) => {
items.value = props.pagination.reversed
? [...res.data].reverse().concat(items.value)
: items.value.concat(res.data);
next.value = res.next ?? null;
moreFetching.value = false;
},
(err) => {
moreFetching.value = false;
}
);
};
const prepend = (item: PageItem): void => {
if (props.pagination.reversed) {
if (rootEl.value) {
const container = getScrollContainer(rootEl.value);
if (container == null) {
// TODO?
} else {
const pos = getScrollPosition(rootEl.value);
const viewHeight = container.clientHeight;
const height = container.scrollHeight;
const isBottom = pos + viewHeight > height - 32;
if (isBottom) {
items.value = items.value.slice(-props.displayLimit);
// TODO
// next.value = true;
}
}
}
items.value.push(item);
// TODO
} else {
// unshiftOK
if (!rootEl.value) {
items.value.unshift(item);
return;
}
const isTop =
isBackTop.value ||
(document.body.contains(rootEl.value) &&
isTopVisible(rootEl.value));
if (isTop) {
// Prepend the item
items.value = [item, ...items.value].slice(0, props.displayLimit);
} else {
if (!queue.value.length) {
onScrollTop(rootEl.value, () => {
items.value = [
...queue.value.reverse(),
...items.value,
].slice(0, props.displayLimit);
queue.value = [];
});
}
queue.value = [...queue.value, item].slice(-props.displayLimit);
}
}
};
const append = (item: PageItem): void => {
items.value.push(item);
};
const removeItem = (finder: (item: PageItem) => boolean): boolean => {
const i = items.value.findIndex(finder);
if (i === -1) {
return false;
}
items.value.splice(i, 1);
return true;
};
const updateItem = (
id: PageItem["id"],
replacer: (old: PageItem) => PageItem
): boolean => {
const i = items.value.findIndex((item) => item.id === id);
if (i === -1) {
return false;
}
items.value[i] = replacer(items.value[i]);
return true;
};
if (props.pagination.params && isRef(props.pagination.params)) {
watch(props.pagination.params, init, { deep: true });
}
watch(
queue,
(a, b) => {
if (a.length === 0 && b.length === 0) return;
emit("queue", queue.value.length);
},
{ deep: true }
);
init();
onActivated(() => {
isBackTop.value = false;
});
onDeactivated(() => {
isBackTop.value = window.scrollY === 0;
});
defineSlots<{
empty(props: {}): any;
items(props: { items: PageItem[] }): any;
}>();
defineExpose({
items,
queue,
backed,
reload,
refresh,
prepend,
append,
removeItem,
updateItem,
});
</script>
<style lang="scss" scoped>
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.125s ease;
}
.fade-enter-from,
.fade-leave-to {
opacity: 0;
}
.cxiknjgy {
> .button {
margin-left: auto;
margin-right: auto;
}
}
.list > :deep(._button) {
margin-inline: auto;
margin-bottom: 16px;
&:last-of-type:not(:first-child) {
margin-top: 16px;
}
}
</style>

View File

@ -3,7 +3,13 @@
<div <div
class="banner" class="banner"
:style=" :style="
user.bannerUrl ? `background-image: url(${user.bannerUrl})` : '' magTransProperty(user, 'banner_url', 'bannerUrl')
? `background-image: url(${magTransProperty(
user,
'banner_url',
'bannerUrl'
)})`
: ''
" "
></div> ></div>
<MagAvatarResolvingProxy <MagAvatarResolvingProxy
@ -21,6 +27,7 @@
<div class="description"> <div class="description">
<div v-if="user.description" class="mfm"> <div v-if="user.description" class="mfm">
<Mfm <Mfm
:mm="magMaybeProperty(user, 'description_mm')"
:text="user.description" :text="user.description"
:author="user" :author="user"
:i="$i" :i="$i"
@ -34,15 +41,33 @@
<div class="status"> <div class="status">
<div> <div>
<p>{{ i18n.ts.notes }}</p> <p>{{ i18n.ts.notes }}</p>
<MkNumber :value="user.notesCount" /> <MkNumber
:value="magTransProperty(user, 'note_count', 'notesCount')"
/>
</div> </div>
<div> <div>
<p>{{ i18n.ts.following }}</p> <p>{{ i18n.ts.following }}</p>
<MkNumber :value="user.followingCount" /> <MkNumber
:value="
magTransProperty(
user,
'following_count',
'followingCount'
)
"
/>
</div> </div>
<div> <div>
<p>{{ i18n.ts.followers }}</p> <p>{{ i18n.ts.followers }}</p>
<MkNumber :value="user.followersCount" /> <MkNumber
:value="
magTransProperty(
user,
'follower_count',
'followersCount'
)
"
/>
</div> </div>
</div> </div>
<div class="koudoku-button"> <div class="koudoku-button">
@ -58,9 +83,11 @@ import MkNumber from "@/components/MkNumber.vue";
import { userPage } from "@/filters/user"; import { userPage } from "@/filters/user";
import { i18n } from "@/i18n"; import { i18n } from "@/i18n";
import { $i } from "@/account"; import { $i } from "@/account";
import { packed } from "magnetar-common";
import { magMaybeProperty, magTransProperty } from "@/scripts-mag/mag-util";
defineProps<{ defineProps<{
user: misskey.entities.UserDetailed; user: packed.PackUserMaybeAll | misskey.entities.UserDetailed;
}>(); }>();
</script> </script>

View File

@ -10,6 +10,10 @@ export const userName = (user: misskey.entities.User) => {
return user.name || user.username; return user.name || user.username;
}; };
export const userPage = (user: misskey.Acct, path?, absolute = false) => { export const userPage = (
user: misskey.Acct,
path?: string,
absolute = false
) => {
return `${absolute ? url : ""}/@${acct(user)}${path ? `/${path}` : ""}`; return `${absolute ? url : ""}/@${acct(user)}${path ? `/${path}` : ""}`;
}; };

View File

@ -18,6 +18,7 @@ import {
FrontendApiEndpoints, FrontendApiEndpoints,
MagApiClient, MagApiClient,
Method, Method,
PaginatedResult,
types, types,
} from "magnetar-common"; } from "magnetar-common";
import { magReactionToLegacy } from "@/scripts-mag/mag-util"; import { magReactionToLegacy } from "@/scripts-mag/mag-util";
@ -48,7 +49,9 @@ export async function magApi<
| number; | number;
}, },
token?: string | null | undefined token?: string | null | undefined
): Promise<T["response"]> { ): Promise<
T["paginated"] extends true ? PaginatedResult<T["response"]> : T["response"]
> {
pendingApiRequestsCount.value++; pendingApiRequestsCount.value++;
try { try {

View File

@ -2,7 +2,10 @@
<MkStickyContainer> <MkStickyContainer>
<template #header><MkPageHeader /></template> <template #header><MkPageHeader /></template>
<MkSpacer :content-max="800"> <MkSpacer :content-max="800">
<MkPagination ref="paginationComponent" :pagination="pagination"> <MagPagination
ref="paginationComponent"
:pagination="followRequestPagination"
>
<template #empty> <template #empty>
<div class="_fullinfo"> <div class="_fullinfo">
<img <img
@ -13,88 +16,165 @@
<div>{{ i18n.ts.noFollowRequests }}</div> <div>{{ i18n.ts.noFollowRequests }}</div>
</div> </div>
</template> </template>
<template #default="{ items }"> <template #items="{ items: users }">
<MkInfo v-if="$i?.isLocked === false" warn class="info" <MkInfo v-if="$i?.isLocked === false" warn class="info"
>{{ i18n.ts.silencedWarning }} >{{ i18n.ts.silencedWarning }}
</MkInfo> </MkInfo>
<div class="mk-follow-requests"> <div class="mk-follow-requests">
<div <div
v-for="req in items" v-for="user in users"
:key="req.id" :key="user.id"
class="user _panel" class="user _panel"
> >
<MagAvatarResolvingProxy <div class="header">
class="avatar" <MagAvatar class="avatar" :user="user" />
:user="req.follower"
:show-indicator="true"
disableLink
/>
<div class="body">
<div class="name"> <div class="name">
<MkA <MkA
v-user-preview="req.follower.id" v-user-preview="user.id"
class="name" class="name"
:to="userPage(req.follower)" :to="userPage(user)"
><MkUserName :user="req.follower" ><MkUserName :user="user"
/></MkA> /></MkA>
<p class="acct"> <p class="acct">@{{ acct(user) }}</p>
@{{ acct(req.follower) }}
</p>
</div>
<div
v-if="req.follower.description"
class="description"
:title="req.follower.description"
>
<Mfm
:text="req.follower.description"
:is-note="false"
:author="req.follower"
:i="$i"
:custom-emojis="req.follower.emojis"
:plain="true"
:nowrap="true"
/>
</div> </div>
<div class="actions"> <div class="actions">
<button <button
class="_button" class="_button"
@click="accept(req.follower)" :aria-label="i18n.ts.accept"
@click="accept(user)"
> >
<i class="ph-check ph-bold ph-lg"></i> <i class="ph-check ph-bold ph-lg"></i>
</button> </button>
<button <button
class="_button" class="_button"
@click="reject(req.follower)" :aria-label="i18n.ts.reject"
@click="reject(user)"
> >
<i class="ph-x ph-bold ph-lg"></i> <i class="ph-x ph-bold ph-lg"></i>
</button> </button>
</div> </div>
</div> </div>
<div class="moved" v-if="user.moved_to">
{{ i18n.ts.accountMoved }}
<MagMention
class="link"
:username="user.moved_to.username"
:host="user.moved_to.host"
/>
</div>
<div class="remote" v-if="user.host != null">
{{ i18n.ts.remoteUserCaution }}
<a
class="link"
:href="user.url!"
rel="nofollow noopener"
target="_blank"
>{{ i18n.ts.showOnRemote }}</a
>
</div>
<div class="description">
<div v-if="user.description" class="mfm">
<Mfm
:mm="user.description_mm"
:text="user.description"
:author="user"
:i="$i"
:custom-emojis="user.emojis"
/>
</div>
<span v-else style="opacity: 0.7">{{
i18n.ts.noAccountDescription
}}</span>
</div>
<div class="fields">
<dl class="field">
<dt class="name">
<i
class="ph-calendar-blank ph-bold ph-lg ph-fw ph-lg"
></i>
{{ i18n.ts.registeredDate }}
</dt>
<dd class="value">
{{
new Date(
user.created_at
).toLocaleString()
}}
(<MkTime :time="user.created_at" />)
</dd>
</dl>
</div>
<div v-if="user.fields?.length > 0" class="fields">
<dl
v-for="(field, i) in user.fields"
:key="i"
class="field"
>
<dt class="name">
<Mfm
:text="field.name"
:plain="true"
:custom-emojis="user.emojis"
:colored="false"
/>
</dt>
<dd class="value">
<Mfm
:mm="field.value_mm ?? undefined"
:text="field.value"
:author="user"
:i="$i"
:custom-emojis="user.emojis"
:colored="false"
/>
</dd>
</dl>
</div>
<div class="status">
<div>
<p>{{ i18n.ts.notes }}</p>
<MkNumber :value="user.note_count" />
</div>
<div>
<p>{{ i18n.ts.following }}</p>
<MkNumber :value="user.following_count" />
</div>
<div>
<p>{{ i18n.ts.followers }}</p>
<MkNumber :value="user.follower_count" />
</div>
</div>
</div> </div>
</div> </div>
</template> </template>
</MkPagination> </MagPagination>
</MkSpacer> </MkSpacer>
</MkStickyContainer> </MkStickyContainer>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { computed, onBeforeUnmount, onMounted, ref } from "vue"; import { computed, onBeforeUnmount, onMounted, ref } from "vue";
import MkPagination from "@/components/MkPagination.vue";
import { acct, userPage } from "@/filters/user"; import { acct, userPage } from "@/filters/user";
import * as os from "@/os"; import * as os from "@/os";
import { i18n } from "@/i18n"; import { i18n } from "@/i18n";
import { definePageMetadata } from "@/scripts/page-metadata"; import { definePageMetadata } from "@/scripts/page-metadata";
import { $i } from "@/account"; import { $i } from "@/account";
import { globalEvents } from "@/events"; import { globalEvents } from "@/events";
import MagPagination, { Paging } from "@/components/MagPagination.vue";
import { endpoints, types } from "magnetar-common";
import MkInfo from "@/components/MkInfo.vue";
import MkNumber from "@/components/MkNumber.vue";
import MkTime from "@/components/global/MkTime.vue";
import Mfm from "@/components/mfm.vue";
import MagMention from "@/components/MagMention.vue";
const paginationComponent = ref<InstanceType<typeof MkPagination>>(); const paginationComponent = ref<InstanceType<typeof MagPagination>>();
const pagination = { const followRequestPagination: Paging = {
endpoint: "following/requests/list" as const, endpoint: endpoints.GetFollowRequestsSelf,
limit: 10, limit: 20,
noPaging: true, pathParams: {},
params: {},
}; };
onMounted(() => { onMounted(() => {
@ -113,13 +193,13 @@ onBeforeUnmount(() => {
); );
}); });
function accept(user) { function accept(user: types.Id) {
os.api("following/requests/accept", { userId: user.id }).then(() => { os.api("following/requests/accept", { userId: user.id }).then(() => {
globalEvents.emit("followeeProcessed", user); globalEvents.emit("followeeProcessed", user);
}); });
} }
function reject(user) { function reject(user: types.Id) {
os.api("following/requests/reject", { userId: user.id }).then(() => { os.api("following/requests/reject", { userId: user.id }).then(() => {
globalEvents.emit("followeeProcessed", user); globalEvents.emit("followeeProcessed", user);
}); });
@ -141,9 +221,14 @@ definePageMetadata(
.mk-follow-requests { .mk-follow-requests {
> .user { > .user {
display: flex; display: flex;
padding: 16px; flex-direction: column;
margin: 10px 0 auto; margin: 10px 0 auto;
> .header {
display: flex;
padding: 16px;
flex-direction: row;
> .avatar { > .avatar {
display: block; display: block;
flex-shrink: 0; flex-shrink: 0;
@ -153,17 +238,8 @@ definePageMetadata(
border-radius: 8px; border-radius: 8px;
} }
> .body {
display: flex;
width: calc(100% - 54px);
position: relative;
> .name { > .name {
width: 45%; flex-grow: 1;
@media (max-width: 500px) {
width: 100%;
}
> .name, > .name,
> .acct { > .acct {
@ -186,35 +262,103 @@ definePageMetadata(
} }
} }
> .description {
width: 55%;
line-height: 42px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
opacity: 0.7;
font-size: 14px;
padding-right: 40px;
padding-left: 8px;
box-sizing: border-box;
@media (max-width: 500px) {
display: none;
}
}
> .actions { > .actions {
position: absolute; flex-shrink: 0;
top: 0;
bottom: 0;
right: 0;
margin: auto 0;
> button { > button {
padding: 12px; padding: 12px;
} }
} }
} }
> .moved,
> .remote {
padding: 16px;
font-size: 0.8em;
border-top: solid 0.5px var(--divider);
color: var(--infoWarnFg);
> .link {
margin-left: 4px;
text-decoration: underline;
color: var(--accent);
&:hover {
text-decoration: none;
}
}
}
> .description {
padding: 16px;
font-size: 0.8em;
border-top: solid 0.5px var(--divider);
> .mfm {
display: -webkit-box;
-webkit-line-clamp: 10;
-webkit-box-orient: vertical;
overflow: hidden;
}
}
> .fields {
border-top: solid 0.5px var(--divider);
padding: 10px 16px;
font-size: 0.9em;
> .field {
display: flex;
padding: 0;
margin: 0;
align-items: center;
&:not(:last-child) {
margin-bottom: 8px;
}
> .name {
flex-grow: 1;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
font-weight: bold;
text-align: center;
}
> .value {
flex-grow: 2;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
margin: 0;
text-align: center;
}
}
}
> .status {
padding: 10px 16px 16px;
border-top: solid 0.5px var(--divider);
> div {
display: inline-flex;
flex-direction: column;
align-items: center;
width: 33%;
> p {
margin: 0;
font-size: 0.7em;
color: var(--fg);
}
> span {
font-size: 1em;
color: var(--accent);
}
}
}
} }
} }
</style> </style>

View File

@ -1,52 +1,53 @@
<template> <template>
<div> <div>
<MkPagination <MagPagination
v-slot="{ items }"
ref="list" ref="list"
:pagination=" :pagination="
type === 'following' ? followingPagination : followersPagination type === 'following' ? followingPagination : followersPagination
" "
class="mk-following-or-followers" class="mk-following-or-followers"
> >
<template #items="{ items: users }">
<div class="users"> <div class="users">
<MkUserInfo <MkUserInfo
v-for="user in items.map((x) => v-for="user in users as typeof endpoints.GetFollowingById.response"
type === 'following' ? x.followee : x.follower
)"
:key="user.id" :key="user.id"
class="user" class="user"
:user="user" :user="user"
/> />
</div> </div>
</MkPagination> </template>
</MagPagination>
</div> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { computed } from "vue"; import { computed } from "vue";
import MkUserInfo from "@/components/MkUserInfo.vue"; import MkUserInfo from "@/components/MkUserInfo.vue";
import MkPagination from "@/components/MkPagination.vue"; import { endpoints, packed } from "magnetar-common";
import { packed } from "magnetar-common"; import MagPagination, { Paging } from "@/components/MagPagination.vue";
const props = defineProps<{ const props = defineProps<{
user: packed.PackUserBase; user: packed.PackUserBase;
type: "following" | "followers"; type: "following" | "followers";
}>(); }>();
const followingPagination = { const followingPagination: Paging = {
endpoint: "users/following" as const, endpoint: endpoints.GetFollowingById,
limit: 20, limit: 20,
params: computed(() => ({ pathParams: computed(() => ({
userId: props.user.id, id: props.user.id,
})), })),
params: {},
}; };
const followersPagination = { const followersPagination: Paging = {
endpoint: "users/followers" as const, endpoint: endpoints.GetFollowersById,
limit: 20, limit: 20,
params: computed(() => ({ pathParams: computed(() => ({
userId: props.user.id, id: props.user.id,
})), })),
params: {},
}; };
</script> </script>

View File

@ -13,7 +13,7 @@ export interface BackendApiEndpoint<
endpoint: string; endpoint: string;
pathParams: PP; pathParams: PP;
request?: T; request?: T;
response?: R; response?: R & (PG extends true ? any[] : any);
paginated: PG; paginated: PG;
} }
@ -58,7 +58,7 @@ export interface MagApiError {
export interface PaginatedResult<T> { export interface PaginatedResult<T> {
prev?: URL; prev?: URL;
data: T; data: T & any[];
next?: URL; next?: URL;
} }
@ -81,7 +81,12 @@ function extractHeaderRel(
const relMatch = relPar.match(/rel="(.+?)"/)?.[1]; const relMatch = relPar.match(/rel="(.+?)"/)?.[1];
if (relMatch == rel && urlMatch) { if (relMatch == rel && urlMatch) {
try {
return new URL(urlMatch); return new URL(urlMatch);
} catch (e) {
console.error(e);
return undefined;
}
} }
} }
} }

View File

@ -4,6 +4,7 @@ import {
MagApiError, MagApiError,
MagApiErrorCode, MagApiErrorCode,
Method, Method,
PaginatedResult,
} from "./be-api"; } from "./be-api";
import { import {
@ -19,6 +20,7 @@ export * as endpoints from "./endpoints";
export { export {
Method, Method,
BackendApiEndpoint, BackendApiEndpoint,
PaginatedResult,
MagApiError, MagApiError,
MagApiClient, MagApiClient,
MagApiErrorCode, MagApiErrorCode,

View File

@ -1,4 +1,4 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
import type { SpanFilter } from "./SpanFilter"; import type { SpanFilter } from "./SpanFilter";
export interface PaginationShape { pagination: SpanFilter, limit: bigint, } export interface PaginationShape { pagination: SpanFilter, limit: number, }

View File

@ -426,6 +426,14 @@ packages:
'@babel/types': 7.22.5 '@babel/types': 7.22.5
dev: true dev: true
/@babel/parser@7.23.6:
resolution: {integrity: sha512-Z2uID7YJ7oNvAI20O9X0bblw7Qqs8Q2hFy0R9tAfnfLkp5MW0UH9eUvnDSnFwKZ0AvgS1ucqR4KzvVHgnke1VQ==}
engines: {node: '>=6.0.0'}
hasBin: true
dependencies:
'@babel/types': 7.22.5
dev: true
/@babel/runtime@7.20.7: /@babel/runtime@7.20.7:
resolution: {integrity: sha512-UF0tvkUtxwAgZ5W/KrkHf0Rn0fdnLDU9ScxBrEVNUprE/MzirjK4MJUX1/BVDv00Sv8cljtukVK1aky++X1SjQ==} resolution: {integrity: sha512-UF0tvkUtxwAgZ5W/KrkHf0Rn0fdnLDU9ScxBrEVNUprE/MzirjK4MJUX1/BVDv00Sv8cljtukVK1aky++X1SjQ==}
engines: {node: '>=6.9.0'} engines: {node: '>=6.9.0'}
@ -1445,7 +1453,7 @@ packages:
/@vue/compiler-core@3.3.4: /@vue/compiler-core@3.3.4:
resolution: {integrity: sha512-cquyDNvZ6jTbf/+x+AgM2Arrp6G4Dzbb0R64jiG804HRMfRiFXWI6kqUVqZ6ZR0bQhIoQjB4+2bhNtVwndW15g==} resolution: {integrity: sha512-cquyDNvZ6jTbf/+x+AgM2Arrp6G4Dzbb0R64jiG804HRMfRiFXWI6kqUVqZ6ZR0bQhIoQjB4+2bhNtVwndW15g==}
dependencies: dependencies:
'@babel/parser': 7.22.7 '@babel/parser': 7.23.6
'@vue/shared': 3.3.4 '@vue/shared': 3.3.4
estree-walker: 2.0.2 estree-walker: 2.0.2
source-map-js: 1.0.2 source-map-js: 1.0.2
@ -1461,7 +1469,7 @@ packages:
/@vue/compiler-sfc@2.7.14: /@vue/compiler-sfc@2.7.14:
resolution: {integrity: sha512-aNmNHyLPsw+sVvlQFQ2/8sjNuLtK54TC6cuKnVzAY93ks4ZBrvwQSnkkIh7bsbNhum5hJBS00wSDipQ937f5DA==} resolution: {integrity: sha512-aNmNHyLPsw+sVvlQFQ2/8sjNuLtK54TC6cuKnVzAY93ks4ZBrvwQSnkkIh7bsbNhum5hJBS00wSDipQ937f5DA==}
dependencies: dependencies:
'@babel/parser': 7.22.7 '@babel/parser': 7.23.6
postcss: 8.4.25 postcss: 8.4.25
source-map: 0.6.1 source-map: 0.6.1
dev: true dev: true
@ -1516,7 +1524,7 @@ packages:
dependencies: dependencies:
'@vue/runtime-core': 3.3.4 '@vue/runtime-core': 3.3.4
'@vue/shared': 3.3.4 '@vue/shared': 3.3.4
csstype: 3.1.2 csstype: 3.1.3
dev: true dev: true
/@vue/server-renderer@3.3.4(vue@3.3.4): /@vue/server-renderer@3.3.4(vue@3.3.4):
@ -2793,8 +2801,8 @@ packages:
source-map: 0.5.7 source-map: 0.5.7
dev: true dev: true
/csstype@3.1.2: /csstype@3.1.3:
resolution: {integrity: sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==} resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
dev: true dev: true
/custom-event-polyfill@1.0.7: /custom-event-polyfill@1.0.7:
@ -7444,9 +7452,10 @@ packages:
/vue@2.7.14: /vue@2.7.14:
resolution: {integrity: sha512-b2qkFyOM0kwqWFuQmgd4o+uHGU7T+2z3T+WQp8UBjADfEv2n4FEMffzBmCKNP0IGzOEEfYjvtcC62xaSKeQDrQ==} resolution: {integrity: sha512-b2qkFyOM0kwqWFuQmgd4o+uHGU7T+2z3T+WQp8UBjADfEv2n4FEMffzBmCKNP0IGzOEEfYjvtcC62xaSKeQDrQ==}
deprecated: Vue 2 has reached EOL and is no longer actively maintained. See https://v2.vuejs.org/eol/ for more details.
dependencies: dependencies:
'@vue/compiler-sfc': 2.7.14 '@vue/compiler-sfc': 2.7.14
csstype: 3.1.2 csstype: 3.1.3
dev: true dev: true
/vue@3.3.4: /vue@3.3.4:

View File

@ -11,11 +11,11 @@ impl<const MIN: u64, const MAX: u64> TS for U64Range<MIN, MAX> {
const EXPORT_TO: Option<&'static str> = Some("bindings/util/u64_range.ts"); const EXPORT_TO: Option<&'static str> = Some("bindings/util/u64_range.ts");
fn decl() -> String { fn decl() -> String {
<u64 as TS>::decl() <usize as TS>::decl()
} }
fn name() -> String { fn name() -> String {
<u64 as TS>::name() <usize as TS>::name()
} }
fn dependencies() -> Vec<ts_rs::Dependency> { fn dependencies() -> Vec<ts_rs::Dependency> {