feat: introduce dependency injection framework (#4091)

* feat: migrate frontend notifications to dependency injection based notificaton manager

* fix: lint

* fix: issues

* fix: compile error + notif binding issue

* refactor: move org context to new DI setup

* feat: migrate app notifications to DI + frontend styling

* fix: sidebar issues

* fix: dont use delete in computed

* fix: import and prop issue

* refactor: move handleError to main notification manager class

* fix: lint & build

* fix: merge issues

* fix: lint issues

* fix: lint issues

---------

Signed-off-by: IMB11 <hendersoncal117@gmail.com>
Signed-off-by: Cal H. <hendersoncal117@gmail.com>
This commit is contained in:
Cal H.
2025-08-13 21:48:52 +01:00
committed by GitHub
parent 9ea43a12fd
commit b81e727204
136 changed files with 2024 additions and 1719 deletions

View File

@@ -408,6 +408,7 @@
<script setup>
import {
CheckIcon,
DownloadIcon,
EditIcon,
ExternalIcon,
LeftArrowIcon,
@@ -418,17 +419,16 @@ import {
TrashIcon,
UpdatedIcon,
XIcon,
DownloadIcon,
} from "@modrinth/assets";
import { ConfirmModal, injectNotificationManager } from "@modrinth/ui";
import QrcodeVue from "qrcode.vue";
import { ConfirmModal } from "@modrinth/ui";
import GithubIcon from "assets/icons/auth/sso-github.svg";
import MicrosoftIcon from "assets/icons/auth/sso-microsoft.svg";
import GoogleIcon from "assets/icons/auth/sso-google.svg";
import SteamIcon from "assets/icons/auth/sso-steam.svg";
import DiscordIcon from "assets/icons/auth/sso-discord.svg";
import KeyIcon from "assets/icons/auth/key.svg";
import DiscordIcon from "assets/icons/auth/sso-discord.svg";
import GithubIcon from "assets/icons/auth/sso-github.svg";
import GitLabIcon from "assets/icons/auth/sso-gitlab.svg";
import GoogleIcon from "assets/icons/auth/sso-google.svg";
import MicrosoftIcon from "assets/icons/auth/sso-microsoft.svg";
import SteamIcon from "assets/icons/auth/sso-steam.svg";
import Modal from "~/components/ui/Modal.vue";
useHead({
@@ -439,7 +439,7 @@ definePageMeta({
middleware: "auth",
});
const data = useNuxtApp();
const { addNotification } = injectNotificationManager();
const auth = await useAuth();
const changeEmailModal = ref();
@@ -460,8 +460,7 @@ async function saveEmail() {
changeEmailModal.value.hide();
await useAuth(auth.value.token);
} catch (err) {
data.$notify({
group: "main",
addNotification({
title: "An error occurred",
text: err.data ? err.data.description : err,
type: "error",
@@ -492,8 +491,7 @@ async function savePassword() {
managePasswordModal.value.hide();
await useAuth(auth.value.token);
} catch (err) {
data.$notify({
group: "main",
addNotification({
title: "An error occurred",
text: err.data ? err.data.description : err,
type: "error",
@@ -529,8 +527,7 @@ async function showTwoFactorModal() {
twoFactorSecret.value = res.secret;
twoFactorFlow.value = res.flow;
} catch (err) {
data.$notify({
group: "main",
addNotification({
title: "An error occurred",
text: err.data ? err.data.description : err,
type: "error",
@@ -619,8 +616,7 @@ async function deleteAccount() {
method: "DELETE",
});
} catch (err) {
data.$notify({
group: "main",
addNotification({
title: "An error occurred",
text: err.data ? err.data.description : err,
type: "error",
@@ -649,8 +645,7 @@ async function exportData() {
const blob = new Blob([jsonString], { type: "application/json" });
generated.value = URL.createObjectURL(blob);
} catch (err) {
data.$notify({
group: "main",
addNotification({
title: "An error occurred",
text: err.data ? err.data.description : err,
type: "error",

View File

@@ -215,26 +215,27 @@
</div>
</template>
<script setup>
import { UploadIcon, PlusIcon, XIcon, TrashIcon, EditIcon, SaveIcon } from "@modrinth/assets";
import { EditIcon, PlusIcon, SaveIcon, TrashIcon, UploadIcon, XIcon } from "@modrinth/assets";
import {
CopyCode,
ConfirmModal,
Avatar,
Button,
Checkbox,
Avatar,
ConfirmModal,
CopyCode,
FileInput,
commonSettingsMessages,
injectNotificationManager,
} from "@modrinth/ui";
import Modal from "~/components/ui/Modal.vue";
import {
scopeList,
getScopeValue,
hasScope,
scopeList,
toggleScope,
useScopes,
getScopeValue,
} from "~/composables/auth/scopes.ts";
const { addNotification } = injectNotificationManager();
const { formatMessage } = useVIntl();
definePageMeta({
@@ -245,7 +246,6 @@ useHead({
title: "Applications - Modrinth",
});
const data = useNuxtApp();
const { scopesToLabels } = useScopes();
const appModal = ref();
@@ -343,8 +343,7 @@ async function onImageSelection(files) {
setForm(app);
}
data.$notify({
group: "main",
addNotification({
title: "Icon updated",
text: "Your application icon has been updated.",
type: "success",
@@ -374,8 +373,7 @@ async function createApp() {
await refresh();
} catch (err) {
data.$notify({
group: "main",
addNotification({
title: "An error occurred",
text: err.data ? err.data.description : err,
type: "error",
@@ -443,8 +441,7 @@ async function editApp() {
appModal.value.hide();
} catch (err) {
data.$notify({
group: "main",
addNotification({
title: "An error occurred",
text: err.data ? err.data.description : err,
type: "error",
@@ -467,8 +464,7 @@ async function removeApp() {
await refresh();
editingId.value = null;
} catch (err) {
data.$notify({
group: "main",
addNotification({
title: "An error occurred",
text: err.data ? err.data.description : err,
type: "error",

View File

@@ -88,10 +88,17 @@
</div>
</template>
<script setup>
import { Button, ConfirmModal, Avatar, commonSettingsMessages } from "@modrinth/ui";
import { TrashIcon, CheckIcon } from "@modrinth/assets";
import { CheckIcon, TrashIcon } from "@modrinth/assets";
import {
Avatar,
Button,
commonSettingsMessages,
ConfirmModal,
injectNotificationManager,
} from "@modrinth/ui";
import { useScopes } from "~/composables/auth/scopes.ts";
const { addNotification } = injectNotificationManager();
const { formatMessage } = useVIntl();
const { scopesToDefinitions } = useScopes();
@@ -163,8 +170,7 @@ async function revokeApp(id) {
revokingId.value = null;
await refresh();
} catch (err) {
data.$notify({
group: "main",
addNotification({
title: "An error occurred",
text: err.data ? err.data.description : err,
type: "error",

View File

@@ -392,8 +392,7 @@
"
:on-error="
(err) =>
data.$notify({
group: 'main',
addNotification({
title: 'An error occurred',
type: 'error',
text: err.message ?? (err.data ? err.data.description : err),
@@ -422,8 +421,7 @@
:renewal-date="currentSubRenewalDate"
:on-error="
(err) =>
data.$notify({
group: 'main',
addNotification({
title: 'An error occurred',
type: 'error',
text: err.message ?? (err.data ? err.data.description : err),
@@ -554,43 +552,44 @@
<script setup>
import {
ConfirmModal,
ArrowBigUpDashIcon,
CardIcon,
CheckCircleIcon,
CurrencyIcon,
EditIcon,
HistoryIcon,
ModrinthPlusIcon,
MoreVerticalIcon,
PayPalIcon,
PlusIcon,
RightArrowIcon,
SpinnerIcon,
StarIcon,
TransferIcon,
TrashIcon,
UpdatedIcon,
XIcon,
} from "@modrinth/assets";
import {
AddPaymentMethodModal,
ButtonStyled,
ConfirmModal,
CopyCode,
OverflowMenu,
PurchaseModal,
ButtonStyled,
CopyCode,
commonMessages,
injectNotificationManager,
} from "@modrinth/ui";
import {
PlusIcon,
TransferIcon,
SpinnerIcon,
ArrowBigUpDashIcon,
XIcon,
CardIcon,
MoreVerticalIcon,
TrashIcon,
EditIcon,
StarIcon,
PayPalIcon,
CurrencyIcon,
CheckCircleIcon,
RightArrowIcon,
ModrinthPlusIcon,
UpdatedIcon,
HistoryIcon,
} from "@modrinth/assets";
import { calculateSavings, formatPrice, getCurrency } from "@modrinth/utils";
import { ref, computed } from "vue";
import { computed, ref } from "vue";
import { useServersFetch } from "~/composables/servers/servers-fetch.ts";
import { products } from "~/generated/state.json";
const { addNotification } = injectNotificationManager();
definePageMeta({
middleware: "auth",
});
const app = useNuxtApp();
const auth = await useAuth();
const baseId = useId();
@@ -604,7 +603,6 @@ useHead({
],
});
const data = useNuxtApp();
const config = useRuntimeConfig();
const vintl = useVIntl();
@@ -845,8 +843,7 @@ async function editPaymentMethod(index, primary) {
});
await refresh();
} catch (err) {
data.$notify({
group: "main",
addNotification({
title: "An error occurred",
text: err.data ? err.data.description : err,
type: "error",
@@ -864,8 +861,7 @@ async function removePaymentMethod(index) {
});
await refresh();
} catch (err) {
data.$notify({
group: "main",
addNotification({
title: "An error occurred",
text: err.data ? err.data.description : err,
type: "error",
@@ -887,8 +883,7 @@ async function cancelSubscription(id, cancelled) {
});
await refresh();
} catch (err) {
data.$notify({
group: "main",
addNotification({
title: "An error occurred",
text: err.data ? err.data.description : err,
type: "error",
@@ -955,8 +950,7 @@ const showPyroUpgradeModal = async (subscription) => {
if (!currentProduct.value) {
console.error("Could not find product for current subscription");
data.$notify({
group: "main",
addNotification({
title: "An error occurred",
text: "Could not find product for current subscription",
type: "error",
@@ -988,8 +982,7 @@ async function fetchCapacityStatuses(serverId, product) {
};
} catch (error) {
console.error("Error checking server capacities:", error);
app.$notify({
group: "main",
addNotification({
title: "Error checking server capacities",
text: error,
type: "error",
@@ -1015,23 +1008,20 @@ const resubscribePyro = async (subscriptionId, wasSuspended) => {
});
await refresh();
if (wasSuspended) {
data.$notify({
group: "main",
addNotification({
title: "Resubscription request submitted",
text: "If the server is currently suspended, it may take up to 10 minutes for another charge attempt to be made.",
type: "success",
});
} else {
data.$notify({
group: "main",
addNotification({
title: "Success",
text: "Server subscription resubscribed successfully",
type: "success",
});
}
} catch {
data.$notify({
group: "main",
addNotification({
title: "Error resubscribing",
text: "An error occurred while resubscribing to your Modrinth server.",
type: "error",

View File

@@ -199,7 +199,7 @@
<script setup lang="ts">
import { CodeIcon, RadioButtonCheckedIcon, RadioButtonIcon } from "@modrinth/assets";
import { Button, ThemeSelector } from "@modrinth/ui";
import { Button, injectNotificationManager, ThemeSelector } from "@modrinth/ui";
import { formatProjectType } from "@modrinth/utils";
import MessageBanner from "~/components/ui/MessageBanner.vue";
import type { DisplayLocation } from "~/plugins/cosmetics";
@@ -209,6 +209,7 @@ useHead({
title: "Display settings - Modrinth",
});
const { addNotification } = injectNotificationManager();
const { formatMessage } = useVIntl();
const developerModeBanner = defineMessages({
@@ -377,7 +378,6 @@ function disableDeveloperMode() {
flags.value.developerMode = !flags.value.developerMode;
saveFeatureFlags();
addNotification({
group: "main",
title: "Developer mode deactivated",
text: "Developer mode has been disabled",
type: "success",

View File

@@ -202,26 +202,26 @@
</div>
</template>
<script setup>
import { PlusIcon, XIcon, TrashIcon, EditIcon, SaveIcon } from "@modrinth/assets";
import { EditIcon, PlusIcon, SaveIcon, TrashIcon, XIcon } from "@modrinth/assets";
import {
Checkbox,
CopyCode,
ConfirmModal,
commonSettingsMessages,
CopyCode,
commonMessages,
commonSettingsMessages,
injectNotificationManager,
useRelativeTime,
} from "@modrinth/ui";
import Modal from "~/components/ui/Modal.vue";
import {
getScopeValue,
hasScope,
scopeList,
toggleScope,
useScopes,
getScopeValue,
} from "~/composables/auth/scopes.ts";
import Modal from "~/components/ui/Modal.vue";
const { addNotification } = injectNotificationManager();
const { formatMessage } = useVIntl();
const formatRelativeTime = useRelativeTime();
@@ -346,8 +346,7 @@ async function createPat() {
pats.value.push(res);
patModal.value.hide();
} catch (err) {
data.$notify({
group: "main",
addNotification({
title: formatMessage(commonMessages.errorNotificationTitle),
text: err.data ? err.data.description : err,
type: "error",
@@ -372,8 +371,7 @@ async function editPat() {
await refresh();
patModal.value.hide();
} catch (err) {
data.$notify({
group: "main",
addNotification({
title: formatMessage(commonMessages.errorNotificationTitle),
text: err.data ? err.data.description : err,
type: "error",
@@ -392,8 +390,7 @@ async function removePat(id) {
});
await refresh();
} catch (err) {
data.$notify({
group: "main",
addNotification({
title: formatMessage(commonMessages.errorNotificationTitle),
text: err.data ? err.data.description : err,
type: "error",

View File

@@ -90,8 +90,11 @@
</template>
<script setup>
import { UserIcon, SaveIcon, UploadIcon, UndoIcon, XIcon, TrashIcon } from "@modrinth/assets";
import { Avatar, FileInput, Button, commonMessages } from "@modrinth/ui";
import { SaveIcon, TrashIcon, UndoIcon, UploadIcon, UserIcon, XIcon } from "@modrinth/assets";
import { Avatar, Button, commonMessages, FileInput, injectNotificationManager } from "@modrinth/ui";
const { addNotification } = injectNotificationManager();
const { formatMessage } = useVIntl();
useHead({
title: "Profile settings - Modrinth",
@@ -101,8 +104,6 @@ definePageMeta({
middleware: "auth",
});
const { formatMessage } = useVIntl();
const messages = defineMessages({
title: {
id: "settings.profile.profile-info",
@@ -222,7 +223,6 @@ async function saveChanges() {
saved.value = true;
} catch (err) {
addNotification({
group: "main",
title: "An error occurred",
text: err
? err.data

View File

@@ -57,12 +57,18 @@
</template>
<script setup>
import { XIcon } from "@modrinth/assets";
import { commonMessages, commonSettingsMessages, useRelativeTime } from "@modrinth/ui";
import {
commonMessages,
commonSettingsMessages,
injectNotificationManager,
useRelativeTime,
} from "@modrinth/ui";
definePageMeta({
middleware: "auth",
});
const { addNotification } = injectNotificationManager();
const { formatMessage } = useVIntl();
const formatRelativeTime = useRelativeTime();
@@ -102,7 +108,6 @@ useHead({
title: () => `${formatMessage(commonSettingsMessages.sessions)} - Modrinth`,
});
const data = useNuxtApp();
const { data: sessions, refresh } = await useAsyncData("session/list", () =>
useBaseFetch("session/list"),
);
@@ -116,8 +121,7 @@ async function revokeSession(id) {
});
await refresh();
} catch (err) {
data.$notify({
group: "main",
addNotification({
title: formatMessage(commonMessages.errorNotificationTitle),
text: err.data ? err.data.description : err,
type: "error",