mirror of
https://github.com/versia-pub/frontend.git
synced 2026-03-13 11:39:16 +01:00
chore: ⬆️ Upgrade to Nuxt 4
Some checks failed
Some checks failed
This commit is contained in:
parent
8debe97f63
commit
7f7cf20311
386 changed files with 2376 additions and 2332 deletions
37
app/components/timelines/account.vue
Normal file
37
app/components/timelines/account.vue
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
<template>
|
||||
<Timeline type="status" :items="items" :is-loading="isLoading" :has-reached-end="hasReachedEnd"
|
||||
:error="error" :load-next="loadNext" :load-prev="loadPrev" :remove-item="removeItem"
|
||||
:update-item="updateItem" />
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import type { z } from "zod";
|
||||
import Timeline from "./timeline.vue";
|
||||
|
||||
const props = defineProps<{
|
||||
id: string;
|
||||
}>();
|
||||
|
||||
const {
|
||||
error,
|
||||
hasReachedEnd,
|
||||
isLoading,
|
||||
items,
|
||||
loadNext,
|
||||
loadPrev,
|
||||
removeItem,
|
||||
updateItem,
|
||||
} = useAccountTimeline(client.value, props.id);
|
||||
|
||||
useListen("note:delete", ({ id }) => {
|
||||
removeItem(id);
|
||||
});
|
||||
|
||||
useListen("note:edit", (updatedNote) => {
|
||||
updateItem(updatedNote);
|
||||
});
|
||||
|
||||
useListen("composer:send", () => {
|
||||
loadPrev();
|
||||
});
|
||||
</script>
|
||||
34
app/components/timelines/global.vue
Normal file
34
app/components/timelines/global.vue
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
<template>
|
||||
<Timeline type="status" :items="items" :is-loading="isLoading" :has-reached-end="hasReachedEnd"
|
||||
:error="error" :load-next="loadNext" :load-prev="loadPrev" :remove-item="removeItem"
|
||||
:update-item="updateItem" />
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import type { z } from "zod";
|
||||
import { useGlobalTimeline } from "~/composables/GlobalTimeline";
|
||||
import Timeline from "./timeline.vue";
|
||||
|
||||
const {
|
||||
error,
|
||||
hasReachedEnd,
|
||||
isLoading,
|
||||
items,
|
||||
loadNext,
|
||||
loadPrev,
|
||||
removeItem,
|
||||
updateItem,
|
||||
} = useGlobalTimeline(client.value);
|
||||
|
||||
useListen("note:delete", ({ id }) => {
|
||||
removeItem(id);
|
||||
});
|
||||
|
||||
useListen("note:edit", (updatedNote) => {
|
||||
updateItem(updatedNote);
|
||||
});
|
||||
|
||||
useListen("composer:send", () => {
|
||||
loadPrev();
|
||||
});
|
||||
</script>
|
||||
33
app/components/timelines/home.vue
Normal file
33
app/components/timelines/home.vue
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
<template>
|
||||
<Timeline type="status" :items="items" :is-loading="isLoading" :has-reached-end="hasReachedEnd"
|
||||
:error="error" :load-next="loadNext" :load-prev="loadPrev" :remove-item="removeItem"
|
||||
:update-item="updateItem" />
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>import { useHomeTimeline } from "~/composables/HomeTimeline";
|
||||
import Timeline from "./timeline.vue";
|
||||
import type { z } from "zod";
|
||||
|
||||
const {
|
||||
error,
|
||||
hasReachedEnd,
|
||||
isLoading,
|
||||
items,
|
||||
loadNext,
|
||||
loadPrev,
|
||||
removeItem,
|
||||
updateItem,
|
||||
} = useHomeTimeline(client.value);
|
||||
|
||||
useListen("note:delete", ({ id }) => {
|
||||
removeItem(id);
|
||||
});
|
||||
|
||||
useListen("note:edit", (updatedNote) => {
|
||||
updateItem(updatedNote);
|
||||
});
|
||||
|
||||
useListen("composer:send", () => {
|
||||
loadPrev();
|
||||
});
|
||||
</script>
|
||||
34
app/components/timelines/local.vue
Normal file
34
app/components/timelines/local.vue
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
<template>
|
||||
<Timeline type="status" :items="items" :is-loading="isLoading" :has-reached-end="hasReachedEnd"
|
||||
:error="error" :load-next="loadNext" :load-prev="loadPrev" :remove-item="removeItem"
|
||||
:update-item="updateItem" />
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import type { z } from "zod";
|
||||
import { useLocalTimeline } from "~/composables/LocalTimeline";
|
||||
import Timeline from "./timeline.vue";
|
||||
|
||||
const {
|
||||
error,
|
||||
hasReachedEnd,
|
||||
isLoading,
|
||||
items,
|
||||
loadNext,
|
||||
loadPrev,
|
||||
removeItem,
|
||||
updateItem,
|
||||
} = useLocalTimeline(client.value);
|
||||
|
||||
useListen("note:delete", ({ id }) => {
|
||||
removeItem(id);
|
||||
});
|
||||
|
||||
useListen("note:edit", (updatedNote) => {
|
||||
updateItem(updatedNote);
|
||||
});
|
||||
|
||||
useListen("composer:send", () => {
|
||||
loadPrev();
|
||||
});
|
||||
</script>
|
||||
21
app/components/timelines/notifications.vue
Normal file
21
app/components/timelines/notifications.vue
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
<template>
|
||||
<Timeline type="notification" :items="items" :is-loading="isLoading"
|
||||
:has-reached-end="hasReachedEnd" :error="error" :load-next="loadNext" :load-prev="loadPrev"
|
||||
:remove-item="removeItem" :update-item="updateItem" />
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useNotificationTimeline } from "~/composables/NotificationTimeline";
|
||||
import Timeline from "./timeline.vue";
|
||||
|
||||
const {
|
||||
error,
|
||||
hasReachedEnd,
|
||||
isLoading,
|
||||
items,
|
||||
loadNext,
|
||||
loadPrev,
|
||||
removeItem,
|
||||
updateItem,
|
||||
} = useNotificationTimeline(client.value);
|
||||
</script>
|
||||
33
app/components/timelines/public.vue
Normal file
33
app/components/timelines/public.vue
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
<template>
|
||||
<Timeline type="status" :items="items" :is-loading="isLoading" :has-reached-end="hasReachedEnd"
|
||||
:error="error" :load-next="loadNext" :load-prev="loadPrev" :remove-item="removeItem"
|
||||
:update-item="updateItem" />
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { usePublicTimeline } from "~/composables/PublicTimeline";
|
||||
import Timeline from "./timeline.vue";
|
||||
|
||||
const {
|
||||
error,
|
||||
hasReachedEnd,
|
||||
isLoading,
|
||||
items,
|
||||
loadNext,
|
||||
loadPrev,
|
||||
removeItem,
|
||||
updateItem,
|
||||
} = usePublicTimeline(client.value);
|
||||
|
||||
useListen("note:delete", ({ id }) => {
|
||||
removeItem(id);
|
||||
});
|
||||
|
||||
useListen("note:edit", (updatedNote) => {
|
||||
updateItem(updatedNote);
|
||||
});
|
||||
|
||||
useListen("composer:send", () => {
|
||||
loadPrev();
|
||||
});
|
||||
</script>
|
||||
28
app/components/timelines/timeline-item.vue
Normal file
28
app/components/timelines/timeline-item.vue
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
<template>
|
||||
<component :is="itemComponent" :note="type === 'status' ? item : undefined" :notification="type === 'notification' ? item : (undefined as any)" @update="$emit('update', $event)"
|
||||
@delete="$emit('delete', item?.id)" />
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import type { Notification, Status } from "@versia/client/schemas";
|
||||
import type { z } from "zod";
|
||||
import Thread from "../notes/thread.vue";
|
||||
import NotificationItem from "../notifications/notification.vue";
|
||||
|
||||
const props = defineProps<{
|
||||
item?: z.infer<typeof Status> | z.infer<typeof Notification>;
|
||||
type: "status" | "notification";
|
||||
}>();
|
||||
|
||||
const itemComponent = computed(() => {
|
||||
if (props.type === "status") {
|
||||
return Thread;
|
||||
}
|
||||
|
||||
if (props.type === "notification") {
|
||||
return NotificationItem;
|
||||
}
|
||||
|
||||
return null;
|
||||
});
|
||||
</script>
|
||||
26
app/components/timelines/timeline-scroller.vue
Normal file
26
app/components/timelines/timeline-scroller.vue
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
<template>
|
||||
<slot />
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
const root = useParentElement(useParentElement());
|
||||
// Store and keep y to restore it on page change
|
||||
const route = useRoute();
|
||||
const yStored = useLocalStorage("versia:scroll", {
|
||||
[route.fullPath]: 0,
|
||||
});
|
||||
const { y } = useScroll(root);
|
||||
|
||||
useEventListener("popstate", async () => {
|
||||
if (yStored.value[route.fullPath] !== undefined) {
|
||||
// Wait for the Vue component to load
|
||||
await new Promise((resolve) => setTimeout(resolve, 100));
|
||||
y.value = yStored.value[route.fullPath] ?? 0;
|
||||
}
|
||||
});
|
||||
|
||||
onBeforeRouteLeave(() => {
|
||||
yStored.value[route.fullPath] = y.value;
|
||||
yStored.value = { ...yStored.value };
|
||||
});
|
||||
</script>
|
||||
87
app/components/timelines/timeline.vue
Normal file
87
app/components/timelines/timeline.vue
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
<template>
|
||||
<div
|
||||
role="status"
|
||||
class="flex flex-col gap-4 items-center *:max-w-2xl *:w-full p-4"
|
||||
>
|
||||
<TimelineItem
|
||||
:type="type"
|
||||
v-for="item in items"
|
||||
:key="item.id"
|
||||
:item="item"
|
||||
@update="updateItem"
|
||||
@delete="removeItem"
|
||||
/>
|
||||
|
||||
<Spinner v-if="isLoading" />
|
||||
|
||||
<div v-if="error" class="timeline-error">
|
||||
{{ error.message }}
|
||||
</div>
|
||||
|
||||
<!-- If there are some posts, but the user scrolled to the end -->
|
||||
<ReachedEnd v-if="hasReachedEnd && items.length > 0" />
|
||||
|
||||
<!-- If there are no posts at all -->
|
||||
<NoPosts v-else-if="hasReachedEnd && items.length === 0" />
|
||||
|
||||
<div v-else-if="!preferences.infinite_scroll" class="py-10 px-4">
|
||||
<Button
|
||||
variant="secondary"
|
||||
@click="loadNext"
|
||||
:disabled="isLoading"
|
||||
class="w-full"
|
||||
>
|
||||
{{ m.gaudy_bland_gorilla_talk() }}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div v-else ref="loadMoreTrigger" class="h-20"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import type { Notification, Status } from "@versia/client/schemas";
|
||||
import { useIntersectionObserver } from "@vueuse/core";
|
||||
import type { z } from "zod";
|
||||
import * as m from "~~/paraglide/messages.js";
|
||||
import NoPosts from "../errors/NoPosts.vue";
|
||||
import ReachedEnd from "../errors/ReachedEnd.vue";
|
||||
import Spinner from "../graphics/spinner.vue";
|
||||
import { Button } from "../ui/button";
|
||||
import TimelineItem from "./timeline-item.vue";
|
||||
|
||||
const props = defineProps<{
|
||||
items: z.infer<typeof Status>[] | z.infer<typeof Notification>[];
|
||||
type: "status" | "notification";
|
||||
isLoading: boolean;
|
||||
hasReachedEnd: boolean;
|
||||
error: Error | null;
|
||||
loadNext: () => void;
|
||||
loadPrev: () => void;
|
||||
removeItem: (id: string) => void;
|
||||
updateItem:
|
||||
| ((item: z.infer<typeof Status>) => void)
|
||||
| ((item: z.infer<typeof Notification>) => void);
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<(e: "update") => void>();
|
||||
|
||||
const loadMoreTrigger = ref<HTMLElement | null>(null);
|
||||
|
||||
useIntersectionObserver(loadMoreTrigger, ([observer]) => {
|
||||
if (observer?.isIntersecting && !props.isLoading && !props.hasReachedEnd) {
|
||||
props.loadNext();
|
||||
}
|
||||
});
|
||||
|
||||
watch(
|
||||
() => props.items,
|
||||
() => {
|
||||
emit("update");
|
||||
},
|
||||
);
|
||||
|
||||
onMounted(() => {
|
||||
props.loadNext();
|
||||
});
|
||||
</script>
|
||||
Loading…
Add table
Add a link
Reference in a new issue