Frontend: SSE and pagination fixes
ci/woodpecker/push/ociImagePush Pipeline was successful
Details
ci/woodpecker/push/ociImagePush Pipeline was successful
Details
This commit is contained in:
parent
ff6458ea2e
commit
8aa2a4dac4
|
@ -67,9 +67,11 @@ import {
|
||||||
computed,
|
computed,
|
||||||
ComputedRef,
|
ComputedRef,
|
||||||
isRef,
|
isRef,
|
||||||
|
nextTick,
|
||||||
onActivated,
|
onActivated,
|
||||||
onDeactivated,
|
onDeactivated,
|
||||||
ref,
|
ref,
|
||||||
|
shallowRef,
|
||||||
watch,
|
watch,
|
||||||
} from "vue";
|
} from "vue";
|
||||||
import * as misskey from "calckey-js";
|
import * as misskey from "calckey-js";
|
||||||
|
@ -78,6 +80,7 @@ import {
|
||||||
getScrollContainer,
|
getScrollContainer,
|
||||||
getScrollPosition,
|
getScrollPosition,
|
||||||
isTopVisible,
|
isTopVisible,
|
||||||
|
onScrollBottom,
|
||||||
onScrollTop,
|
onScrollTop,
|
||||||
} from "@/scripts/scroll";
|
} from "@/scripts/scroll";
|
||||||
import MkButton from "@/components/MkButton.vue";
|
import MkButton from "@/components/MkButton.vue";
|
||||||
|
@ -85,7 +88,7 @@ import { magTransProperty } from "@/scripts-mag/mag-util";
|
||||||
import { i18n } from "@/i18n";
|
import { i18n } from "@/i18n";
|
||||||
|
|
||||||
export type Paging<
|
export type Paging<
|
||||||
E extends keyof misskey.Endpoints = keyof misskey.Endpoints,
|
E extends keyof misskey.Endpoints = keyof misskey.Endpoints
|
||||||
> = {
|
> = {
|
||||||
endpoint: E;
|
endpoint: E;
|
||||||
limit: number;
|
limit: number;
|
||||||
|
@ -119,7 +122,7 @@ const props = withDefaults(
|
||||||
}>(),
|
}>(),
|
||||||
{
|
{
|
||||||
displayLimit: 30,
|
displayLimit: 30,
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
|
@ -129,8 +132,8 @@ const emit = defineEmits<{
|
||||||
type Item = { id: string; createdAt?: string; created_at?: string } & any;
|
type Item = { id: string; createdAt?: string; created_at?: string } & any;
|
||||||
|
|
||||||
const rootEl = ref<HTMLElement>();
|
const rootEl = ref<HTMLElement>();
|
||||||
const items = ref<Item[]>([]);
|
const items = shallowRef<Item[]>([]);
|
||||||
const queue = ref<Item[]>([]);
|
const queue = shallowRef<Item[]>([]);
|
||||||
const offset = ref(0);
|
const offset = ref(0);
|
||||||
const fetching = ref(true);
|
const fetching = ref(true);
|
||||||
const moreFetching = ref(false);
|
const moreFetching = ref(false);
|
||||||
|
@ -166,7 +169,7 @@ const init = async (): Promise<void> => {
|
||||||
(err) => {
|
(err) => {
|
||||||
error.value = true;
|
error.value = true;
|
||||||
fetching.value = false;
|
fetching.value = false;
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -206,7 +209,7 @@ const refresh = async (): Promise<void> => {
|
||||||
(err) => {
|
(err) => {
|
||||||
error.value = true;
|
error.value = true;
|
||||||
fetching.value = false;
|
fetching.value = false;
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -241,8 +244,8 @@ const fetchMore = async (): Promise<void> => {
|
||||||
magTransProperty(
|
magTransProperty(
|
||||||
lastItem,
|
lastItem,
|
||||||
"createdAt",
|
"createdAt",
|
||||||
"created_at",
|
"created_at"
|
||||||
),
|
)
|
||||||
).getTime()
|
).getTime()
|
||||||
: undefined,
|
: undefined,
|
||||||
untilId: lastItem?.id ?? undefined,
|
untilId: lastItem?.id ?? undefined,
|
||||||
|
@ -259,7 +262,7 @@ const fetchMore = async (): Promise<void> => {
|
||||||
},
|
},
|
||||||
(err) => {
|
(err) => {
|
||||||
moreFetching.value = false;
|
moreFetching.value = false;
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -276,53 +279,66 @@ const isFresh = (): boolean => {
|
||||||
const pos = getScrollPosition(rootEl.value);
|
const pos = getScrollPosition(rootEl.value);
|
||||||
const viewHeight = container.clientHeight;
|
const viewHeight = container.clientHeight;
|
||||||
const height = container.scrollHeight;
|
const height = container.scrollHeight;
|
||||||
const isBottom = pos + viewHeight > height - 32;
|
return pos + viewHeight > height - 32;
|
||||||
return isBottom;
|
|
||||||
} else {
|
} else {
|
||||||
const isTop =
|
return (
|
||||||
isBackTop.value ||
|
isBackTop.value ||
|
||||||
(document.body.contains(rootEl.value) &&
|
(document.body.contains(rootEl.value) && isTopVisible(rootEl.value))
|
||||||
isTopVisible(rootEl.value));
|
);
|
||||||
return isTop;
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const unqueue = () => {
|
||||||
|
const queueRemoved = [...queue.value].reverse();
|
||||||
|
queue.value = [];
|
||||||
|
if (props.pagination.reversed) {
|
||||||
|
items.value = [...items.value, ...queueRemoved].slice(
|
||||||
|
0,
|
||||||
|
-props.displayLimit
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
items.value = [...queueRemoved, ...items.value].slice(
|
||||||
|
0,
|
||||||
|
props.displayLimit
|
||||||
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const prepend = (item: Item): void => {
|
const prepend = (item: Item): void => {
|
||||||
if (props.pagination.reversed) {
|
if (!rootEl.value) {
|
||||||
if (isFresh()) {
|
queue.value = [...queue.value, item].slice(-props.displayLimit);
|
||||||
items.value = items.value.slice(-props.displayLimit);
|
return;
|
||||||
hasMore.value = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
items.value.push(item);
|
|
||||||
} else {
|
|
||||||
if (isFresh()) {
|
|
||||||
// Prepend the item
|
|
||||||
items.value = [item, ...items.value].slice(0, props.displayLimit);
|
|
||||||
} else {
|
|
||||||
if (!rootEl.value) {
|
|
||||||
items.value.unshift(item);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!queue.value.length) {
|
|
||||||
onScrollTop(rootEl.value, () => {
|
|
||||||
const queueRemoved = [...queue.value].reverse();
|
|
||||||
queue.value = [];
|
|
||||||
items.value = [...queueRemoved, ...items.value].slice(
|
|
||||||
0,
|
|
||||||
props.displayLimit,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
queue.value = [...queue.value, item].slice(-props.displayLimit);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
const append = (item: Item): void => {
|
if (!isFresh()) {
|
||||||
items.value.push(item);
|
if (!queue.value.length) {
|
||||||
|
(props.pagination.reversed ? onScrollBottom : onScrollTop)(
|
||||||
|
rootEl.value,
|
||||||
|
() => {
|
||||||
|
nextTick(unqueue);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
queue.value = [...queue.value, item].slice(-props.displayLimit);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (items.value.length > props.displayLimit) {
|
||||||
|
queue.value = [...queue.value, item].slice(-props.displayLimit);
|
||||||
|
if (!queue.value.length) {
|
||||||
|
nextTick(unqueue);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (props.pagination.reversed) {
|
||||||
|
items.value = [...items.value, item];
|
||||||
|
} else {
|
||||||
|
items.value = [item, ...items.value];
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const removeItem = (finder: (item: Item) => boolean): boolean => {
|
const removeItem = (finder: (item: Item) => boolean): boolean => {
|
||||||
|
@ -331,7 +347,7 @@ const removeItem = (finder: (item: Item) => boolean): boolean => {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
items.value.splice(i, 1);
|
items.value = items.value.toSpliced(i, 1);
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -341,7 +357,9 @@ const updateItem = (id: Item["id"], replacer: (old: Item) => Item): boolean => {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
items.value[i] = replacer(items.value[i]);
|
const newItems = [...items.value];
|
||||||
|
newItems[i] = replacer(items.value[i]);
|
||||||
|
items.value = newItems;
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -349,14 +367,10 @@ if (props.pagination.params && isRef(props.pagination.params)) {
|
||||||
watch(props.pagination.params, init, { deep: true });
|
watch(props.pagination.params, init, { deep: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
watch(
|
watch(queue, (a, b) => {
|
||||||
queue,
|
if (a.length === 0 && b.length === 0) return;
|
||||||
(a, b) => {
|
emit("queue", queue.value.length);
|
||||||
if (a.length === 0 && b.length === 0) return;
|
});
|
||||||
emit("queue", queue.value.length);
|
|
||||||
},
|
|
||||||
{ deep: true },
|
|
||||||
);
|
|
||||||
|
|
||||||
init();
|
init();
|
||||||
|
|
||||||
|
@ -375,7 +389,6 @@ defineExpose({
|
||||||
reload,
|
reload,
|
||||||
refresh,
|
refresh,
|
||||||
prepend,
|
prepend,
|
||||||
append,
|
|
||||||
removeItem,
|
removeItem,
|
||||||
updateItem,
|
updateItem,
|
||||||
isFresh,
|
isFresh,
|
||||||
|
@ -387,6 +400,7 @@ defineExpose({
|
||||||
.fade-leave-active {
|
.fade-leave-active {
|
||||||
transition: opacity 0.125s ease;
|
transition: opacity 0.125s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.fade-enter-from,
|
.fade-enter-from,
|
||||||
.fade-leave-to {
|
.fade-leave-to {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
|
@ -398,9 +412,11 @@ defineExpose({
|
||||||
margin-right: auto;
|
margin-right: auto;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.list > :deep(._button) {
|
.list > :deep(._button) {
|
||||||
margin-inline: auto;
|
margin-inline: auto;
|
||||||
margin-bottom: 16px;
|
margin-bottom: 16px;
|
||||||
|
|
||||||
&:last-of-type:not(:first-child) {
|
&:last-of-type:not(:first-child) {
|
||||||
margin-top: 16px;
|
margin-top: 16px;
|
||||||
}
|
}
|
||||||
|
|
|
@ -113,7 +113,16 @@ export class MagEventChannel extends EventEmitter<{
|
||||||
let buf = "";
|
let buf = "";
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
const res = await Promise.race([reader.read(), this.closePromise]);
|
const res = await Promise.race([
|
||||||
|
reader.read().catch((e) => {
|
||||||
|
console.error(e);
|
||||||
|
return {
|
||||||
|
done: true,
|
||||||
|
value: undefined,
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
this.closePromise,
|
||||||
|
]);
|
||||||
|
|
||||||
if (res === "cancelled") break;
|
if (res === "cancelled") break;
|
||||||
|
|
||||||
|
@ -122,7 +131,7 @@ export class MagEventChannel extends EventEmitter<{
|
||||||
setTimeout(
|
setTimeout(
|
||||||
() => this.connect(),
|
() => this.connect(),
|
||||||
this.backoffBase *
|
this.backoffBase *
|
||||||
Math.pow(this.backoffFactor, this.attempts)
|
Math.pow(this.backoffFactor, this.attempts)
|
||||||
);
|
);
|
||||||
this.attempts++;
|
this.attempts++;
|
||||||
break;
|
break;
|
||||||
|
|
Loading…
Reference in New Issue