mirror of
https://codeberg.org/yeentown/barkey.git
synced 2025-07-07 12:36:57 +00:00
merge: Add delay and retry to Page's embedded note loading (!1072)
View MR for information: https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/1072 Closes #853 Approved-by: Hazelnoot <acomputerdog@gmail.com> Approved-by: dakkar <dakkar@thenautilus.net>
This commit is contained in:
commit
1ba4ca95af
5 changed files with 64 additions and 9 deletions
31
packages/frontend-shared/js/retry-on-throttled.ts
Normal file
31
packages/frontend-shared/js/retry-on-throttled.ts
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: outvi and other Sharkey contributors
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
async function sleep(ms: number): Promise<void> {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
window.setTimeout(() => {
|
||||||
|
resolve();
|
||||||
|
}, ms);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function retryOnThrottled<T>(f: () => Promise<T>, retryCount = 5): Promise<T> {
|
||||||
|
let lastError;
|
||||||
|
for (let i = 0; i < Math.min(retryCount, 1); i++) {
|
||||||
|
try {
|
||||||
|
return await f();
|
||||||
|
} catch (err: any) {
|
||||||
|
// RATE_LIMIT_EXCEEDED
|
||||||
|
if (typeof err === 'object' && err?.id === 'd5826d14-3982-4d2e-8011-b9e9f02499ef') {
|
||||||
|
lastError = err;
|
||||||
|
await sleep(err?.info?.fullResetMs ?? 1000);
|
||||||
|
} else {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw lastError;
|
||||||
|
}
|
|
@ -11,8 +11,9 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { onMounted, ref } from 'vue';
|
import { onMounted, onUnmounted, ref } from 'vue';
|
||||||
import * as Misskey from 'misskey-js';
|
import * as Misskey from 'misskey-js';
|
||||||
|
import { retryOnThrottled } from '@@/js/retry-on-throttled.js';
|
||||||
import MkNote from '@/components/MkNote.vue';
|
import MkNote from '@/components/MkNote.vue';
|
||||||
import MkNoteDetailed from '@/components/MkNoteDetailed.vue';
|
import MkNoteDetailed from '@/components/MkNoteDetailed.vue';
|
||||||
import { misskeyApi } from '@/utility/misskey-api.js';
|
import { misskeyApi } from '@/utility/misskey-api.js';
|
||||||
|
@ -20,16 +21,25 @@ import { misskeyApi } from '@/utility/misskey-api.js';
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
block: Misskey.entities.PageBlock,
|
block: Misskey.entities.PageBlock,
|
||||||
page: Misskey.entities.Page,
|
page: Misskey.entities.Page,
|
||||||
|
index: number;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const note = ref<Misskey.entities.Note | null>(null);
|
const note = ref<Misskey.entities.Note | null>(null);
|
||||||
|
|
||||||
|
// eslint-disable-next-line id-denylist
|
||||||
|
let timeoutId: ReturnType<typeof window.setTimeout> | null = null;
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
if (props.block.note == null) return;
|
if (props.block.note == null) return;
|
||||||
misskeyApi('notes/show', { noteId: props.block.note })
|
timeoutId = window.setTimeout(async () => {
|
||||||
.then(result => {
|
note.value = await retryOnThrottled(() => misskeyApi('notes/show', { noteId: props.block.note }));
|
||||||
note.value = result;
|
}, 500 * props.index); // rate limit is 2 reqs per sec
|
||||||
});
|
});
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
if (timeoutId !== null) {
|
||||||
|
window.clearTimeout(timeoutId);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div :class="{ [$style.center]: page.alignCenter, [$style.serif]: page.font === 'serif' }" class="_gaps">
|
<div :class="{ [$style.center]: page.alignCenter, [$style.serif]: page.font === 'serif' }" class="_gaps">
|
||||||
<XBlock v-for="child in page.content" :key="child.id" :page="page" :block="child" :h="2"/>
|
<XBlock v-for="(child, index) in page.content" :key="child.id" :index="index" :page="page" :block="child" :h="2"/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
|
@ -25,6 +25,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
/* eslint-disable vue/no-mutating-props */
|
/* eslint-disable vue/no-mutating-props */
|
||||||
import { watch, ref } from 'vue';
|
import { watch, ref } from 'vue';
|
||||||
import * as Misskey from 'misskey-js';
|
import * as Misskey from 'misskey-js';
|
||||||
|
import { retryOnThrottled } from '@@/js/retry-on-throttled.js';
|
||||||
import XContainer from '../page-editor.container.vue';
|
import XContainer from '../page-editor.container.vue';
|
||||||
import MkInput from '@/components/MkInput.vue';
|
import MkInput from '@/components/MkInput.vue';
|
||||||
import MkSwitch from '@/components/MkSwitch.vue';
|
import MkSwitch from '@/components/MkSwitch.vue';
|
||||||
|
@ -35,6 +36,7 @@ import { i18n } from '@/i18n.js';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
modelValue: Misskey.entities.PageBlock & { type: 'note' };
|
modelValue: Misskey.entities.PageBlock & { type: 'note' };
|
||||||
|
index: number;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
|
@ -58,7 +60,13 @@ watch(id, async () => {
|
||||||
...props.modelValue,
|
...props.modelValue,
|
||||||
note: id.value,
|
note: id.value,
|
||||||
});
|
});
|
||||||
note.value = await misskeyApi('notes/show', { noteId: id.value });
|
const timeoutId = window.setTimeout(async () => {
|
||||||
|
note.value = await retryOnThrottled(() => misskeyApi('notes/show', { noteId: id.value }));
|
||||||
|
}, 500 * props.index); // rate limit is 2 reqs per sec
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
window.clearTimeout(timeoutId);
|
||||||
|
};
|
||||||
}, {
|
}, {
|
||||||
immediate: true,
|
immediate: true,
|
||||||
});
|
});
|
||||||
|
|
|
@ -5,10 +5,16 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Sortable :modelValue="modelValue" tag="div" itemKey="id" handle=".drag-handle" :group="{ name: 'blocks' }" :animation="150" :swapThreshold="0.5" @update:modelValue="v => emit('update:modelValue', v)">
|
<Sortable :modelValue="modelValue" tag="div" itemKey="id" handle=".drag-handle" :group="{ name: 'blocks' }" :animation="150" :swapThreshold="0.5" @update:modelValue="v => emit('update:modelValue', v)">
|
||||||
<template #item="{element}">
|
<template #item="{element, index}">
|
||||||
<div :class="$style.item">
|
<div :class="$style.item">
|
||||||
<!-- divが無いとエラーになる https://github.com/SortableJS/vue.draggable.next/issues/189 -->
|
<!-- divが無いとエラーになる https://github.com/SortableJS/vue.draggable.next/issues/189 -->
|
||||||
<component :is="getComponent(element.type)" :modelValue="element" @update:modelValue="updateItem" @remove="() => removeItem(element)"/>
|
<component
|
||||||
|
:is="getComponent(element.type)"
|
||||||
|
:modelValue="element"
|
||||||
|
:index="index"
|
||||||
|
@update:modelValue="updateItem"
|
||||||
|
@remove="() => removeItem(element)"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</Sortable>
|
</Sortable>
|
||||||
|
|
Loading…
Add table
Reference in a new issue