From 1de9ebdfa5bdec8aa4e2c2bd0677867ed1823270 Mon Sep 17 00:00:00 2001
From: DecDuck
Date: Fri, 14 Mar 2025 13:12:04 +1100
Subject: [PATCH] feat: refactor news and migrate rest of useFetch to
$dropFetch
---
components/DeleteNewsModal.vue | 14 +-
...Create.vue => NewsArticleCreateButton.vue} | 20 +-
components/NewsDirectory.vue | 16 +-
composables/collection.ts | 10 +-
composables/news.ts | 63 ++--
composables/request.ts | 6 +-
composables/user.ts | 3 +-
pages/admin/index.vue | 3 +-
pages/admin/library/[id]/import.vue | 6 +-
pages/admin/library/[id]/index.vue | 13 +-
pages/admin/library/import.vue | 3 +-
pages/admin/library/index.vue | 3 +-
pages/admin/users/auth/index.vue | 5 +-
pages/admin/users/auth/simple/index.vue | 8 +-
pages/admin/users/index.vue | 3 +-
pages/client/[id]/callback.vue | 30 +-
pages/library/index.vue | 23 +-
pages/news.vue | 299 +++++++++---------
pages/news/[id]/index.vue | 18 +-
pages/news/index.vue | 19 +-
pages/store/[id]/index.vue | 4 +-
pages/store/index.vue | 18 +-
server/internal/news/index.ts | 9 +
23 files changed, 299 insertions(+), 297 deletions(-)
rename components/{NewsArticleCreate.vue => NewsArticleCreateButton.vue} (96%)
diff --git a/components/DeleteNewsModal.vue b/components/DeleteNewsModal.vue
index 475c8bd..27692e4 100644
--- a/components/DeleteNewsModal.vue
+++ b/components/DeleteNewsModal.vue
@@ -46,16 +46,22 @@ const article = defineModel();
const deleteLoading = ref(false);
const router = useRouter();
const news = useNews();
+if (!news.value) {
+ news.value = await fetchNews();
+}
async function deleteArticle() {
try {
- if (!article.value) return;
+ if (!article.value || !news.value) return;
deleteLoading.value = true;
- await news.remove(article.value.id);
+ await $dropFetch(`/api/v1/admin/news/${article.value.id}`, { method: "DELETE" });
+
+ const index = news.value.findIndex((e) => e.id == article.value?.id);
+ news.value.splice(index, 1);
article.value = undefined;
- await router.push('/news');
+ router.push("/news");
} catch (e: any) {
createModal(
ModalType.Notification,
@@ -69,4 +75,4 @@ async function deleteArticle() {
deleteLoading.value = false;
}
}
-
+
diff --git a/components/NewsArticleCreate.vue b/components/NewsArticleCreateButton.vue
similarity index 96%
rename from components/NewsArticleCreate.vue
rename to components/NewsArticleCreateButton.vue
index da29f58..ac49083 100644
--- a/components/NewsArticleCreate.vue
+++ b/components/NewsArticleCreateButton.vue
@@ -4,13 +4,13 @@
- New Article
+ New article
@@ -207,14 +207,16 @@ import {
XCircleIcon,
XMarkIcon,
} from "@heroicons/vue/24/solid";
+import type { Article } from "@prisma/client";
import { micromark } from "micromark";
+import type { SerializeObject } from "nitropack/types";
-const emit = defineEmits<{
- refresh: [];
-}>();
+const news = useNews();
+if(!news.value){
+ news.value = await fetchNews();
+}
const user = useUser();
-const news = useNews();
const modalOpen = ref(false);
const loading = ref(false);
@@ -348,11 +350,13 @@ async function createArticle() {
formData.append("content", newArticle.value.content);
formData.append("tags", JSON.stringify(newArticle.value.tags));
- await $dropFetch("/api/v1/admin/news", {
+ const createdArticle = await $dropFetch("/api/v1/admin/news", {
method: "POST",
body: formData,
});
+ news.value?.push(createdArticle);
+
// Reset form
newArticle.value = {
title: "",
@@ -361,8 +365,6 @@ async function createArticle() {
tags: [],
};
- emit("refresh");
-
modalOpen.value = false;
} catch (e) {
error.value = (e as any)?.statusMessage ?? "An unknown error occured.";
diff --git a/components/NewsDirectory.vue b/components/NewsDirectory.vue
index 58a6c3d..bf33ac9 100644
--- a/components/NewsDirectory.vue
+++ b/components/NewsDirectory.vue
@@ -116,19 +116,21 @@ import { ref, computed } from "vue";
import { MagnifyingGlassIcon } from "@heroicons/vue/24/solid";
import { micromark } from "micromark";
+const news = useNews();
+if(!news.value){
+ news.value = await fetchNews();
+}
+
const route = useRoute();
const searchQuery = ref("");
const dateFilter = ref("all");
const selectedTags = ref([]);
-const { data: articles, refresh: refreshArticles } = await useNews().getAll();
-
-defineExpose({ refresh: refreshArticles });
// Get unique tags from all articles
const availableTags = computed(() => {
- if (!articles.value) return [];
+ if (!news.value) return [];
const tags = new Set();
- articles.value.forEach((article) => {
+ news.value.forEach((article) => {
article.tags.forEach((tag) => tags.add(tag.name));
});
return Array.from(tags);
@@ -159,10 +161,10 @@ const formatExcerpt = (excerpt: string) => {
};
const filteredArticles = computed(() => {
- if (!articles.value) return [];
+ if (!news.value) return [];
// filter articles based on search, date, and tags
- return articles.value.filter((article) => {
+ return news.value.filter((article) => {
const matchesSearch =
article.title.toLowerCase().includes(searchQuery.value.toLowerCase()) ||
article.description
diff --git a/composables/collection.ts b/composables/collection.ts
index 320d00c..afc5142 100644
--- a/composables/collection.ts
+++ b/composables/collection.ts
@@ -9,10 +9,7 @@ export const useCollections = async () => {
// @ts-expect-error
const state = useState("collections", () => undefined);
if (state.value === undefined) {
- const headers = useRequestHeaders(["cookie"]);
- state.value = await $dropFetch("/api/v1/collection", {
- headers,
- });
+ state.value = await $dropFetch("/api/v1/collection");
}
return state;
@@ -41,8 +38,5 @@ export const useLibrary = async () => {
export async function refreshLibrary() {
const state = useState("library");
- const headers = useRequestHeaders(["cookie"]);
- state.value = await $dropFetch("/api/v1/collection/default", {
- headers,
- });
+ state.value = await $dropFetch("/api/v1/collection/default");
}
diff --git a/composables/news.ts b/composables/news.ts
index cda4c77..620d8b7 100644
--- a/composables/news.ts
+++ b/composables/news.ts
@@ -1,35 +1,40 @@
-export const useNews = () => {
- const getAll = async (options?: {
- limit?: number;
- skip?: number;
- orderBy?: "asc" | "desc";
- tags?: string[];
- search?: string;
- }) => {
- const query = new URLSearchParams();
+import type { Article } from "@prisma/client";
+import type { SerializeObject } from "nitropack";
- if (options?.limit) query.set("limit", options.limit.toString());
- if (options?.skip) query.set("skip", options.skip.toString());
- if (options?.orderBy) query.set("order", options.orderBy);
- if (options?.tags?.length) query.set("tags", options.tags.join(","));
- if (options?.search) query.set("search", options.search);
+export const useNews = () =>
+ useState<
+ | Array<
+ SerializeObject<
+ Article & {
+ tags: Array<{ id: string; name: string }>;
+ author: { displayName: string; id: string } | null;
+ }
+ >
+ >
+ | undefined
+ >("news", () => undefined);
- return await useFetch(`/api/v1/news?${query.toString()}`);
- };
+export const fetchNews = async (options?: {
+ limit?: number;
+ skip?: number;
+ orderBy?: "asc" | "desc";
+ tags?: string[];
+ search?: string;
+}) => {
+ const query = new URLSearchParams();
- const getById = async (id: string) => {
- return await useFetch(`/api/v1/news/${id}`);
- };
+ if (options?.limit) query.set("limit", options.limit.toString());
+ if (options?.skip) query.set("skip", options.skip.toString());
+ if (options?.orderBy) query.set("order", options.orderBy);
+ if (options?.tags?.length) query.set("tags", options.tags.join(","));
+ if (options?.search) query.set("search", options.search);
- const remove = async (id: string) => {
- return await $dropFetch(`/api/v1/admin/news/${id}`, {
- method: "DELETE",
- });
- };
+ const news = useNews();
- return {
- getAll,
- getById,
- remove,
- };
+ // @ts-ignore
+ const newValue = await $dropFetch(`/api/v1/news?${query.toString()}`);
+
+ news.value = newValue;
+
+ return newValue;
};
diff --git a/composables/request.ts b/composables/request.ts
index bfc2b54..b8b6c79 100644
--- a/composables/request.ts
+++ b/composables/request.ts
@@ -31,7 +31,11 @@ export const $dropFetch: DropFetch = async (request, opts) => {
if (!getCurrentInstance()?.proxy) {
return (await $fetch(request, opts)) as any;
}
- const { data, error } = await useFetch(request, opts as any);
+ const headers = useRequestHeaders(["cookie"]);
+ const { data, error } = await useFetch(request, {
+ ...opts,
+ headers: { ...opts?.headers, ...headers },
+ } as any);
if (error.value) throw error.value;
return data.value as any;
};
diff --git a/composables/user.ts b/composables/user.ts
index 6fe8d85..7c1f189 100644
--- a/composables/user.ts
+++ b/composables/user.ts
@@ -6,11 +6,10 @@ import type { User } from "@prisma/client";
export const useUser = () => useState(undefined);
export const updateUser = async () => {
- const headers = useRequestHeaders(["cookie"]);
const user = useUser();
if (user.value === null) return;
// SSR calls have to be after uses
- user.value = await $dropFetch("/api/v1/user", { headers });
+ user.value = await $dropFetch("/api/v1/user");
};
diff --git a/pages/admin/index.vue b/pages/admin/index.vue
index 6068318..4e233b6 100644
--- a/pages/admin/index.vue
+++ b/pages/admin/index.vue
@@ -176,6 +176,5 @@ useHead({
title: "Home",
});
-const headers = useRequestHeaders(["cookie"]);
-const libraryState = await $dropFetch("/api/v1/admin/library", { headers });
+const libraryState = await $dropFetch("/api/v1/admin/library");
diff --git a/pages/admin/library/[id]/import.vue b/pages/admin/library/[id]/import.vue
index 47c1219..348de3b 100644
--- a/pages/admin/library/[id]/import.vue
+++ b/pages/admin/library/[id]/import.vue
@@ -551,13 +551,9 @@ definePageMeta({
const router = useRouter();
const route = useRoute();
-const headers = useRequestHeaders(["cookie"]);
const gameId = route.params.id.toString();
const versions = await $dropFetch(
- `/api/v1/admin/import/version?id=${encodeURIComponent(gameId)}`,
- {
- headers,
- }
+ `/api/v1/admin/import/version?id=${encodeURIComponent(gameId)}`
);
const currentlySelectedVersion = ref(-1);
const versionSettings = ref<{
diff --git a/pages/admin/library/[id]/index.vue b/pages/admin/library/[id]/index.vue
index d8beecb..986fe76 100644
--- a/pages/admin/library/[id]/index.vue
+++ b/pages/admin/library/[id]/index.vue
@@ -321,7 +321,10 @@
{{ item.delta ? "Upgrade mode" : "" }}
-
+
deleteVersion(item.versionName)">
@@ -345,7 +348,7 @@
:options="{ id: game.id }"
accept="image/*"
endpoint="/api/v1/admin/game/image"
- @upload="(result) => uploadAfterImageUpload(result)"
+ @upload="(result: Game) => uploadAfterImageUpload(result)"
/>
@@ -529,12 +532,8 @@ const mobileShowFinalDescription = ref(true);
const route = useRoute();
const gameId = route.params.id.toString();
-const headers = useRequestHeaders(["cookie"]);
const { game: rawGame, unimportedVersions } = await $dropFetch(
- `/api/v1/admin/game?id=${encodeURIComponent(gameId)}`,
- {
- headers,
- }
+ `/api/v1/admin/game?id=${encodeURIComponent(gameId)}`
);
const game = ref(rawGame);
diff --git a/pages/admin/library/import.vue b/pages/admin/library/import.vue
index 9baee28..e68ac81 100644
--- a/pages/admin/library/import.vue
+++ b/pages/admin/library/import.vue
@@ -157,8 +157,7 @@ definePageMeta({
layout: "admin",
});
-const headers = useRequestHeaders(["cookie"]);
-const games = await $dropFetch("/api/v1/admin/import/game", { headers });
+const games = await $dropFetch("/api/v1/admin/import/game");
const currentlySelectedGame = ref(-1);
const gameSearchResultsLoading = ref(false);
diff --git a/pages/admin/library/index.vue b/pages/admin/library/index.vue
index fcdb834..ba75b83 100644
--- a/pages/admin/library/index.vue
+++ b/pages/admin/library/index.vue
@@ -179,8 +179,7 @@ useHead({
const searchQuery = ref("");
-const headers = useRequestHeaders(["cookie"]);
-const libraryState = await $dropFetch("/api/v1/admin/library", { headers });
+const libraryState = await $dropFetch("/api/v1/admin/library");
const libraryGames = ref(
libraryState.games.map((e) => {
const noVersions = e.status.noVersions;
diff --git a/pages/admin/users/auth/index.vue b/pages/admin/users/auth/index.vue
index 7ac31f0..d501b19 100644
--- a/pages/admin/users/auth/index.vue
+++ b/pages/admin/users/auth/index.vue
@@ -110,10 +110,7 @@ definePageMeta({
layout: "admin",
});
-const headers = useRequestHeaders(["cookie"]);
-const enabledMechanisms = await $dropFetch("/api/v1/admin/auth", {
- headers,
-});
+const enabledMechanisms = await $dropFetch("/api/v1/admin/auth");
const authenticationMechanisms: Array<{
name: string;
diff --git a/pages/admin/users/auth/simple/index.vue b/pages/admin/users/auth/simple/index.vue
index e854dd4..0ba6c9b 100644
--- a/pages/admin/users/auth/simple/index.vue
+++ b/pages/admin/users/auth/simple/index.vue
@@ -391,12 +391,10 @@ useHead({
title: "Simple authentication",
});
-const headers = useRequestHeaders(["cookie"]);
-const { data } = await useFetch>>(
- "/api/v1/admin/auth/invitation",
- { headers }
+const data = await $dropFetch>>(
+ "/api/v1/admin/auth/invitation"
);
-const invitations = ref(data.value ?? []);
+const invitations = ref(data ?? []);
const generateInvitationUrl = (id: string) =>
`${window.location.protocol}//${window.location.host}/register?id=${id}`;
diff --git a/pages/admin/users/index.vue b/pages/admin/users/index.vue
index 4a4c0d4..25a2cfc 100644
--- a/pages/admin/users/index.vue
+++ b/pages/admin/users/index.vue
@@ -106,6 +106,5 @@ definePageMeta({
layout: "admin",
});
-const headers = useRequestHeaders(["cookie"]);
-const { data: users } = await useFetch("/api/v1/admin/users", { headers });
+const users = await $dropFetch("/api/v1/admin/users");
diff --git a/pages/client/[id]/callback.vue b/pages/client/[id]/callback.vue
index 9d99224..807ebe1 100644
--- a/pages/client/[id]/callback.vue
+++ b/pages/client/[id]/callback.vue
@@ -47,7 +47,7 @@
@@ -58,7 +58,7 @@
Authorize client?
- "{{ clientData.data.value.name }}" has requested access to your Drop
+ "{{ clientData.name }}" has requested access to your Drop
account.
- Accepting this request will allow "{{ clientData.data.value.name }}"
- on "{{ clientData.data.value.platform }}" to:
+ Accepting this request will allow "{{ clientData.name }}"
+ on "{{ clientData.platform }}" to:
@@ -132,22 +132,6 @@
-
-
-
400
-
- Invalid or expired request
-
-
- Unfortunately, we couldn't load the authorization request.
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/news/[id]/index.vue b/pages/news/[id]/index.vue
index 0f52c4a..8bc3da9 100644
--- a/pages/news/[id]/index.vue
+++ b/pages/news/[id]/index.vue
@@ -70,10 +70,7 @@
-
+
@@ -85,16 +82,19 @@ import { TrashIcon } from "@heroicons/vue/24/outline";
import { micromark } from "micromark";
const route = useRoute();
-const { data: article } = await useNews().getById(route.params.id as string);
const currentlyDeleting = ref();
const user = useUser();
-
-if (!article.value) {
+const news = useNews();
+if (!news.value) {
+ news.value = await fetchNews();
+}
+const article = computed(() => news.value?.find((e) => e.id == route.params.id));
+if (!article.value)
throw createError({
statusCode: 404,
- message: "Article not found",
+ statusMessage: "Article not found",
+ fatal: true,
});
-}
// Render markdown content
const renderedContent = computed(() => {
diff --git a/pages/news/index.vue b/pages/news/index.vue
index 99cb4c1..f36648e 100644
--- a/pages/news/index.vue
+++ b/pages/news/index.vue
@@ -10,8 +10,6 @@
Stay up to date with the latest updates and announcements.
-
-
@@ -83,9 +81,17 @@