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

@@ -890,6 +890,7 @@
</template>
<script setup>
import { navigateTo } from "#app";
import {
BookmarkIcon,
BookTextIcon,
@@ -906,6 +907,7 @@ import {
HeartIcon,
InfoIcon,
LinkIcon as LinksIcon,
ModrinthIcon,
MoreVerticalIcon,
PlusIcon,
ReportIcon,
@@ -917,13 +919,13 @@ import {
UsersIcon,
VersionIcon,
WrenchIcon,
ModrinthIcon,
XIcon,
} from "@modrinth/assets";
import {
Avatar,
ButtonStyled,
Checkbox,
injectNotificationManager,
NewModal,
OverflowMenu,
PopoutMenu,
@@ -935,36 +937,37 @@ import {
ProjectSidebarLinks,
ProjectStatusBadge,
ScrollablePanel,
TagItem,
ServersPromo,
TagItem,
useRelativeTime,
} from "@modrinth/ui";
import VersionSummary from "@modrinth/ui/src/components/version/VersionSummary.vue";
import { formatCategory, formatProjectType, renderString } from "@modrinth/utils";
import { useLocalStorage } from "@vueuse/core";
import dayjs from "dayjs";
import { Tooltip } from "floating-vue";
import { useLocalStorage } from "@vueuse/core";
import { navigateTo } from "#app";
import Accordion from "~/components/ui/Accordion.vue";
import AdPlaceholder from "~/components/ui/AdPlaceholder.vue";
import AutomaticAccordion from "~/components/ui/AutomaticAccordion.vue";
import Breadcrumbs from "~/components/ui/Breadcrumbs.vue";
import CollectionCreateModal from "~/components/ui/CollectionCreateModal.vue";
import MessageBanner from "~/components/ui/MessageBanner.vue";
import ModerationChecklist from "~/components/ui/moderation/checklist/ModerationChecklist.vue";
import NavStack from "~/components/ui/NavStack.vue";
import NavStackItem from "~/components/ui/NavStackItem.vue";
import NavTabs from "~/components/ui/NavTabs.vue";
import ProjectMemberHeader from "~/components/ui/ProjectMemberHeader.vue";
import { userCollectProject } from "~/composables/user.js";
import { reportProject } from "~/utils/report-helpers.ts";
import { saveFeatureFlags } from "~/composables/featureFlags.ts";
import ModerationChecklist from "~/components/ui/moderation/checklist/ModerationChecklist.vue";
import { userCollectProject } from "~/composables/user.js";
import { useModerationStore } from "~/store/moderation.ts";
import { reportProject } from "~/utils/report-helpers.ts";
const data = useNuxtApp();
const route = useNativeRoute();
const config = useRuntimeConfig();
const moderationStore = useModerationStore();
const notifications = injectNotificationManager();
const { addNotification } = notifications;
const auth = await useAuth();
const user = await useUser();
@@ -974,7 +977,6 @@ const flags = useFeatureFlags();
const cosmetics = useCosmetics();
const { formatMessage } = useVIntl();
const { setVisible } = useNotificationRightwards();
const settingsModal = ref();
const downloadModal = ref();
@@ -1425,8 +1427,7 @@ async function setProcessing() {
project.value.status = "processing";
} catch (err) {
data.$notify({
group: "main",
addNotification({
title: "An error occurred",
text: err.data ? err.data.description : err,
type: "error",
@@ -1459,8 +1460,7 @@ async function patchProject(resData, quiet = false) {
result = true;
if (!quiet) {
data.$notify({
group: "main",
addNotification({
title: "Project updated",
text: "Your project has been updated.",
type: "success",
@@ -1468,8 +1468,7 @@ async function patchProject(resData, quiet = false) {
window.scrollTo({ top: 0, behavior: "smooth" });
}
} catch (err) {
data.$notify({
group: "main",
addNotification({
title: "An error occurred",
text: err.data ? err.data.description : err,
type: "error",
@@ -1498,15 +1497,13 @@ async function patchIcon(icon) {
);
await resetProject();
result = true;
data.$notify({
group: "main",
addNotification({
title: "Project icon updated",
text: "Your project's icon has been updated.",
type: "success",
});
} catch (err) {
data.$notify({
group: "main",
addNotification({
title: "An error occurred",
text: err.data ? err.data.description : err,
type: "error",
@@ -1555,11 +1552,15 @@ const collapsedModerationChecklist = useLocalStorage("collapsed-moderation-check
watch(
showModerationChecklist,
(newValue) => {
setVisible(newValue);
notifications.setNotificationLocation(newValue ? "left" : "right");
},
{ immediate: true },
);
onUnmounted(() => {
notifications.setNotificationLocation("right");
});
if (import.meta.client && history && history.state && history.state.showChecklist) {
showModerationChecklist.value = true;
}

View File

@@ -278,26 +278,26 @@
<script setup>
import {
PlusIcon,
CalendarIcon,
ContractIcon,
EditIcon,
TrashIcon,
ExpandIcon,
ExternalIcon,
ImageIcon,
InfoIcon,
LeftArrowIcon,
PlusIcon,
RightArrowIcon,
SaveIcon,
StarIcon,
XIcon,
RightArrowIcon,
LeftArrowIcon,
ExternalIcon,
ExpandIcon,
ContractIcon,
UploadIcon,
InfoIcon,
ImageIcon,
TransferIcon,
TrashIcon,
UploadIcon,
XIcon,
} from "@modrinth/assets";
import { ConfirmModal } from "@modrinth/ui";
import FileInput from "~/components/ui/FileInput.vue";
import { ConfirmModal, injectNotificationManager } from "@modrinth/ui";
import DropArea from "~/components/ui/DropArea.vue";
import FileInput from "~/components/ui/FileInput.vue";
import Modal from "~/components/ui/Modal.vue";
import { isPermission } from "~/utils/permissions.ts";
@@ -425,6 +425,8 @@ export default defineNuxtComponent({
this.shouldPreventActions = true;
startLoading();
const { addNotification } = injectNotificationManager();
try {
let url = `project/${this.project.id}/gallery?ext=${
this.editFile
@@ -450,8 +452,7 @@ export default defineNuxtComponent({
this.$refs.modal_edit_item.hide();
} catch (err) {
this.$notify({
group: "main",
addNotification({
title: "An error occurred",
text: err.data ? err.data.description : err,
type: "error",
@@ -465,6 +466,8 @@ export default defineNuxtComponent({
this.shouldPreventActions = true;
startLoading();
const { addNotification } = injectNotificationManager();
try {
let url = `project/${this.project.id}/gallery?url=${encodeURIComponent(
this.project.gallery[this.editIndex].url,
@@ -487,8 +490,7 @@ export default defineNuxtComponent({
await this.resetProject();
this.$refs.modal_edit_item.hide();
} catch (err) {
this.$notify({
group: "main",
addNotification({
title: "An error occurred",
text: err.data ? err.data.description : err,
type: "error",
@@ -501,6 +503,8 @@ export default defineNuxtComponent({
async deleteGalleryImage() {
startLoading();
const { addNotification } = injectNotificationManager();
try {
await useBaseFetch(
`project/${this.project.id}/gallery?url=${encodeURIComponent(
@@ -513,8 +517,7 @@ export default defineNuxtComponent({
await this.resetProject();
} catch (err) {
this.$notify({
group: "main",
addNotification({
title: "An error occurred",
text: err.data ? err.data.description : err,
type: "error",

View File

@@ -99,8 +99,8 @@
</div>
</template>
<script setup>
import { XIcon, CheckIcon, IssuesIcon } from "@modrinth/assets";
import { Badge } from "@modrinth/ui";
import { CheckIcon, IssuesIcon, XIcon } from "@modrinth/assets";
import { Badge, injectNotificationManager } from "@modrinth/ui";
import ConversationThread from "~/components/ui/thread/ConversationThread.vue";
import {
getProjectLink,
@@ -111,6 +111,7 @@ import {
isUnderReview,
} from "~/helpers/projects.js";
const { addNotification } = injectNotificationManager();
const props = defineProps({
project: {
type: Object,
@@ -131,7 +132,6 @@ const props = defineProps({
},
});
const app = useNuxtApp();
const auth = await useAuth();
const { data: thread } = await useAsyncData(`thread/${props.project.thread_id}`, () =>
@@ -153,8 +153,7 @@ async function setStatus(status) {
await props.resetProject();
thread.value = await useBaseFetch(`thread/${thread.value.id}`);
} catch (err) {
app.$notify({
group: "main",
addNotification({
title: "An error occurred",
text: err.data ? err.data.description : err,
type: "error",

View File

@@ -243,21 +243,23 @@
</template>
<script setup>
import { formatProjectStatus, formatProjectType } from "@modrinth/utils";
import {
UploadIcon,
CheckIcon,
IssuesIcon,
SaveIcon,
TrashIcon,
UploadIcon,
XIcon,
IssuesIcon,
CheckIcon,
TriangleAlertIcon,
} from "@modrinth/assets";
import { Avatar, ConfirmModal, injectNotificationManager } from "@modrinth/ui";
import { formatProjectStatus, formatProjectType } from "@modrinth/utils";
import { Multiselect } from "vue-multiselect";
import { ConfirmModal, Avatar } from "@modrinth/ui";
import { MIN_SUMMARY_CHARS } from "@modrinth/moderation";
import FileInput from "~/components/ui/FileInput.vue";
const { addNotification } = injectNotificationManager();
const props = defineProps({
project: {
type: Object,
@@ -398,7 +400,6 @@ const deleteProject = async () => {
await initUserProjects();
await router.push("/dashboard/projects");
addNotification({
group: "main",
title: "Project deleted",
text: "Your project has been deleted.",
type: "success",
@@ -417,7 +418,6 @@ const deleteIcon = async () => {
});
await props.resetProject();
addNotification({
group: "main",
title: "Project icon removed",
text: "Your project's icon has been removed.",
type: "success",

View File

@@ -518,21 +518,30 @@
</template>
<script setup>
import { Multiselect } from "vue-multiselect";
import {
TransferIcon,
CheckIcon,
UsersIcon,
DropdownIcon,
SaveIcon,
UserPlusIcon,
UserXIcon,
OrganizationIcon,
CrownIcon,
DropdownIcon,
OrganizationIcon,
SaveIcon,
TransferIcon,
UserPlusIcon,
UsersIcon,
UserXIcon,
} from "@modrinth/assets";
import { Avatar, Badge, Card, Checkbox, ConfirmModal } from "@modrinth/ui";
import {
Avatar,
Badge,
Card,
Checkbox,
ConfirmModal,
injectNotificationManager,
} from "@modrinth/ui";
import { Multiselect } from "vue-multiselect";
import { removeSelfFromTeam } from "~/helpers/teams.js";
const { addNotification } = injectNotificationManager();
const props = defineProps({
project: {
type: Object,
@@ -654,7 +663,6 @@ const onAddToOrg = useClientTry(async () => {
await updateMembers();
addNotification({
group: "main",
title: "Project transferred",
text: "Your project has been transferred to the organization.",
type: "success",
@@ -675,7 +683,6 @@ const onRemoveFromOrg = useClientTry(async () => {
await updateMembers();
addNotification({
group: "main",
title: "Project removed",
text: "Your project has been removed from the organization.",
type: "success",
@@ -703,7 +710,6 @@ const inviteTeamMember = async () => {
await updateMembers();
} catch (err) {
addNotification({
group: "main",
title: "An error occurred",
text: err?.data?.description || err?.message || err || "Unknown error",
type: "error",
@@ -726,7 +732,6 @@ const removeTeamMember = async (index) => {
await updateMembers();
} catch (err) {
addNotification({
group: "main",
title: "An error occurred",
text: err?.data?.description || err?.message || err || "Unknown error",
type: "error",
@@ -760,14 +765,12 @@ const updateTeamMember = async (index) => {
);
await updateMembers();
addNotification({
group: "main",
title: "Member(s) updated",
text: "Your project's member(s) has been updated.",
type: "success",
});
} catch (err) {
addNotification({
group: "main",
title: "An error occurred",
text: err?.data?.description || err?.message || err || "Unknown error",
type: "error",
@@ -790,7 +793,6 @@ const transferOwnership = async (index) => {
await updateMembers();
} catch (err) {
addNotification({
group: "main",
title: "An error occurred",
text: err?.data?.description || err?.message || err || "Unknown error",
type: "error",
@@ -837,7 +839,6 @@ async function updateOrgMember(index) {
await updateMembers();
} catch (err) {
addNotification({
group: "main",
title: "An error occurred",
text: err?.data?.description || err?.message || err || "Unknown error",
type: "error",

View File

@@ -631,45 +631,46 @@
</template>
<script>
import {
Avatar,
Badge,
CopyCode,
Checkbox,
ButtonStyled,
ConfirmModal,
MarkdownEditor,
} from "@modrinth/ui";
import {
FileIcon,
TrashIcon,
EditIcon,
BoxIcon,
ChevronRightIcon,
DownloadIcon,
StarIcon,
ReportIcon,
SaveIcon,
XIcon,
EditIcon,
FileIcon,
HashIcon,
PlusIcon,
TransferIcon,
UploadIcon,
BoxIcon,
ReportIcon,
RightArrowIcon,
ChevronRightIcon,
SaveIcon,
StarIcon,
TransferIcon,
TrashIcon,
UploadIcon,
XIcon,
} from "@modrinth/assets";
import { Multiselect } from "vue-multiselect";
import {
Avatar,
Badge,
ButtonStyled,
Checkbox,
ConfirmModal,
CopyCode,
injectNotificationManager,
MarkdownEditor,
} from "@modrinth/ui";
import { formatBytes, formatCategory } from "@modrinth/utils";
import { Multiselect } from "vue-multiselect";
import { useImageUpload } from "~/composables/image-upload.ts";
import { acceptFileFromProjectType } from "~/helpers/fileUtils.js";
import { renderHighlightedString } from "~/helpers/highlight.js";
import { inferVersionInfo } from "~/helpers/infer.js";
import { createDataPackVersion } from "~/helpers/package.js";
import { renderHighlightedString } from "~/helpers/highlight.js";
import { reportVersion } from "~/utils/report-helpers.ts";
import { useImageUpload } from "~/composables/image-upload.ts";
import AdPlaceholder from "~/components/ui/AdPlaceholder.vue";
import Breadcrumbs from "~/components/ui/Breadcrumbs.vue";
import Categories from "~/components/ui/search/Categories.vue";
import FileInput from "~/components/ui/FileInput.vue";
import Modal from "~/components/ui/Modal.vue";
import Categories from "~/components/ui/search/Categories.vue";
export default defineNuxtComponent({
components: {
@@ -997,8 +998,8 @@ export default defineNuxtComponent({
const project = await useBaseFetch(`project/${newDependencyId}`);
if (this.version.dependencies.some((dep) => project.id === dep.project_id)) {
this.$notify({
group: "main",
const { addNotification } = injectNotificationManager();
addNotification({
title: "Dependency already added",
text: "You cannot add the same dependency twice.",
type: "error",
@@ -1022,8 +1023,8 @@ export default defineNuxtComponent({
const project = await useBaseFetch(`project/${version.project_id}`);
if (this.version.dependencies.some((dep) => version.id === dep.version_id)) {
this.$notify({
group: "main",
const { addNotification } = injectNotificationManager();
addNotification({
title: "Dependency already added",
text: "You cannot add the same dependency twice.",
type: "error",
@@ -1050,8 +1051,8 @@ export default defineNuxtComponent({
this.newDependencyId = "";
} catch {
if (!hideErrors) {
this.$notify({
group: "main",
const { addNotification } = injectNotificationManager();
addNotification({
title: "Invalid Dependency",
text: "The specified dependency could not be found",
type: "error",
@@ -1144,8 +1145,8 @@ export default defineNuxtComponent({
)}`,
);
} catch (err) {
this.$notify({
group: "main",
const { addNotification } = injectNotificationManager();
addNotification({
title: "An error occurred",
text: err.data ? err.data.description : err,
type: "error",
@@ -1169,8 +1170,8 @@ export default defineNuxtComponent({
try {
await this.createVersionRaw(this.version);
} catch (err) {
this.$notify({
group: "main",
const { addNotification } = injectNotificationManager();
addNotification({
title: "An error occurred",
text: err.data ? err.data.description : err,
type: "error",
@@ -1293,15 +1294,15 @@ export default defineNuxtComponent({
this.$refs.modal_package_mod.hide();
this.$notify({
group: "main",
const { addNotification } = injectNotificationManager();
addNotification({
title: "Packaging Success",
text: "Your data pack was successfully packaged as a mod! Make sure to playtest to check for errors.",
type: "success",
});
} catch (err) {
this.$notify({
group: "main",
const { addNotification } = injectNotificationManager();
addNotification({
title: "An error occurred",
text: err.data ? err.data.description : err,
type: "error",

View File

@@ -251,28 +251,31 @@
</div>
</template>
<script setup>
import {
CheckIcon,
CurrencyIcon,
ExternalIcon,
ModrinthPlusIcon,
ServerIcon,
UserIcon,
XIcon,
} from "@modrinth/assets";
import {
Avatar,
ButtonStyled,
CopyCode,
DropdownSelect,
injectNotificationManager,
NewModal,
Toggle,
useRelativeTime,
} from "@modrinth/ui";
import { formatCategory, formatPrice } from "@modrinth/utils";
import {
CheckIcon,
XIcon,
UserIcon,
ModrinthPlusIcon,
ServerIcon,
ExternalIcon,
CurrencyIcon,
} from "@modrinth/assets";
import dayjs from "dayjs";
import { products } from "~/generated/state.json";
import ModrinthServersIcon from "~/components/ui/servers/ModrinthServersIcon.vue";
import { products } from "~/generated/state.json";
const { addNotification } = injectNotificationManager();
const route = useRoute();
const vintl = useVIntl();

View File

@@ -258,31 +258,31 @@
</div>
</template>
<script setup lang="ts">
import { EditIcon, PlusIcon, SaveIcon, SettingsIcon, TrashIcon, XIcon } from "@modrinth/assets";
import {
CopyCode,
TagItem,
ButtonStyled,
ServerNotice,
commonMessages,
CopyCode,
injectNotificationManager,
NewModal,
ServerNotice,
TagItem,
TeleportDropdownMenu,
Toggle,
useRelativeTime,
} from "@modrinth/ui";
import { SettingsIcon, PlusIcon, SaveIcon, TrashIcon, EditIcon, XIcon } from "@modrinth/assets";
import dayjs from "dayjs";
import { useVIntl } from "@vintl/vintl";
import type { ServerNotice as ServerNoticeType } from "@modrinth/utils";
import { computed } from "vue";
import { NOTICE_LEVELS } from "@modrinth/ui/src/utils/notices.ts";
import { useServersFetch } from "~/composables/servers/servers-fetch.ts";
import type { ServerNotice as ServerNoticeType } from "@modrinth/utils";
import { useVIntl } from "@vintl/vintl";
import dayjs from "dayjs";
import { computed } from "vue";
import AssignNoticeModal from "~/components/ui/servers/notice/AssignNoticeModal.vue";
import { useServersFetch } from "~/composables/servers/servers-fetch.ts";
const { addNotification } = injectNotificationManager();
const { formatMessage } = useVIntl();
const formatRelativeTime = useRelativeTime();
const app = useNuxtApp() as unknown as { $notify: any };
const notices = ref<ServerNoticeType[]>([]);
const createNoticeModal = ref<InstanceType<typeof NewModal>>();
const assignNoticeModal = ref<InstanceType<typeof AssignNoticeModal>>();
@@ -351,15 +351,13 @@ async function deleteNotice(notice: ServerNoticeType) {
method: "DELETE",
})
.then(() => {
app.$notify({
group: "main",
addNotification({
title: `Successfully deleted notice #${notice.id}`,
type: "success",
});
})
.catch((err) => {
app.$notify({
group: "main",
addNotification({
title: "Error deleting notice",
text: err,
type: "error",
@@ -386,7 +384,6 @@ const noticeSubmitError = computed(() => {
function validateSubmission(message: string) {
if (noticeSubmitError.value) {
addNotification({
group: "main",
title: message,
text: noticeSubmitError.value,
type: "error",
@@ -416,8 +413,7 @@ async function saveChanges() {
: undefined,
},
}).catch((err) => {
app.$notify({
group: "main",
addNotification({
title: "Error saving changes to notice",
text: err,
type: "error",
@@ -447,8 +443,7 @@ async function createNotice() {
: undefined,
},
}).catch((err) => {
app.$notify({
group: "main",
addNotification({
title: "Error creating notice",
text: err,
type: "error",

View File

@@ -32,8 +32,10 @@
</div>
</template>
<script setup lang="ts">
import { ButtonStyled } from "@modrinth/ui";
import { MailIcon } from "@modrinth/assets";
import { ButtonStyled, injectNotificationManager } from "@modrinth/ui";
const { addNotification } = injectNotificationManager();
const userEmail = ref("");
@@ -50,7 +52,6 @@ async function getUserFromEmail() {
} catch (err) {
console.error(err);
addNotification({
group: "main",
title: "An error occurred",
text: err.data.description,
type: "error",

View File

@@ -80,13 +80,14 @@
</template>
<script setup>
import { Button, Avatar, commonMessages } from "@modrinth/ui";
import { XIcon, CheckIcon } from "@modrinth/assets";
import { useBaseFetch } from "@/composables/fetch.js";
import { CheckIcon, XIcon } from "@modrinth/assets";
import { Avatar, Button, commonMessages, injectNotificationManager } from "@modrinth/ui";
import { useAuth } from "@/composables/auth.js";
import { useBaseFetch } from "@/composables/fetch.js";
import { useScopes } from "@/composables/auth/scopes.ts";
const { addNotification } = injectNotificationManager();
const { formatMessage } = useVIntl();
const messages = defineMessages({
@@ -117,8 +118,6 @@ const messages = defineMessages({
},
});
const data = useNuxtApp();
const router = useNativeRoute();
const auth = await useAuth();
const { scopesToDefinitions } = useScopes();
@@ -196,8 +195,7 @@ const onAuthorize = async () => {
throw new Error(formatMessage(messages.noRedirectUrlError));
} catch {
data.$notify({
group: "main",
addNotification({
title: formatMessage(commonMessages.errorNotificationTitle),
text: err.data ? err.data.description : err,
type: "error",
@@ -223,8 +221,7 @@ const onReject = async () => {
throw new Error(formatMessage(messages.noRedirectUrlError));
} catch {
data.$notify({
group: "main",
addNotification({
title: formatMessage(commonMessages.errorNotificationTitle),
text: err.data ? err.data.description : err,
type: "error",

View File

@@ -67,10 +67,11 @@
</div>
</template>
<script setup>
import { SendIcon, MailIcon, KeyIcon } from "@modrinth/assets";
import { commonMessages } from "@modrinth/ui";
import { KeyIcon, MailIcon, SendIcon } from "@modrinth/assets";
import { commonMessages, injectNotificationManager } from "@modrinth/ui";
import HCaptcha from "@/components/ui/HCaptcha.vue";
const { addNotification } = injectNotificationManager();
const { formatMessage } = useVIntl();
const methodChoiceMessages = defineMessages({
@@ -179,14 +180,12 @@ async function recovery() {
});
addNotification({
group: "main",
title: formatMessage(emailSentNotificationMessages.title),
text: formatMessage(emailSentNotificationMessages.text),
type: "success",
});
} catch (err) {
addNotification({
group: "main",
title: formatMessage(commonMessages.errorNotificationTitle),
text: err.data ? err.data.description : err,
type: "error",
@@ -211,7 +210,6 @@ async function changePassword() {
});
addNotification({
group: "main",
title: formatMessage(passwordResetNotificationMessages.title),
text: formatMessage(passwordResetNotificationMessages.text),
type: "success",
@@ -219,7 +217,6 @@ async function changePassword() {
await navigateTo("/auth/sign-in");
} catch (err) {
addNotification({
group: "main",
title: formatMessage(commonMessages.errorNotificationTitle),
text: err.data ? err.data.description : err,
type: "error",

View File

@@ -130,19 +130,20 @@
<script setup>
import {
RightArrowIcon,
SSOGitHubIcon,
SSOMicrosoftIcon,
SSOSteamIcon,
SSOGoogleIcon,
SSODiscordIcon,
SSOGitLabIcon,
KeyIcon,
MailIcon,
RightArrowIcon,
SSODiscordIcon,
SSOGitHubIcon,
SSOGitLabIcon,
SSOGoogleIcon,
SSOMicrosoftIcon,
SSOSteamIcon,
} from "@modrinth/assets";
import { commonMessages } from "@modrinth/ui";
import { commonMessages, injectNotificationManager } from "@modrinth/ui";
import HCaptcha from "@/components/ui/HCaptcha.vue";
const { addNotification } = injectNotificationManager();
const { formatMessage } = useVIntl();
const messages = defineMessages({
@@ -232,7 +233,6 @@ async function beginPasswordSignIn() {
}
} catch (err) {
addNotification({
group: "main",
title: formatMessage(commonMessages.errorNotificationTitle),
text: err.data ? err.data.description : err,
type: "error",
@@ -257,7 +257,6 @@ async function begin2FASignIn() {
await finishSignIn(res.session);
} catch (err) {
addNotification({
group: "main",
title: formatMessage(commonMessages.errorNotificationTitle),
text: err.data ? err.data.description : err,
type: "error",

View File

@@ -134,20 +134,21 @@
<script setup>
import {
RightArrowIcon,
UserIcon,
SSOGitHubIcon,
SSOMicrosoftIcon,
SSOGoogleIcon,
SSOSteamIcon,
SSODiscordIcon,
KeyIcon,
MailIcon,
RightArrowIcon,
SSODiscordIcon,
SSOGitHubIcon,
SSOGitLabIcon,
SSOGoogleIcon,
SSOMicrosoftIcon,
SSOSteamIcon,
UserIcon,
} from "@modrinth/assets";
import { Checkbox, commonMessages } from "@modrinth/ui";
import { Checkbox, commonMessages, injectNotificationManager } from "@modrinth/ui";
import HCaptcha from "@/components/ui/HCaptcha.vue";
const { addNotification } = injectNotificationManager();
const { formatMessage } = useVIntl();
const messages = defineMessages({
@@ -225,7 +226,6 @@ async function createAccount() {
try {
if (confirmPassword.value !== password.value) {
addNotification({
group: "main",
title: formatMessage(commonMessages.errorNotificationTitle),
text: formatMessage({
id: "auth.sign-up.notification.password-mismatch.text",
@@ -262,7 +262,6 @@ async function createAccount() {
}
} catch (err) {
addNotification({
group: "main",
title: formatMessage(commonMessages.errorNotificationTitle),
text: err.data ? err.data.description : err,
type: "error",

View File

@@ -367,6 +367,7 @@ import {
BoxIcon,
CalendarIcon,
EditIcon,
GlobeIcon,
GridIcon,
ImageIcon,
LibraryIcon,
@@ -378,7 +379,6 @@ import {
UpdatedIcon,
UploadIcon,
XIcon,
GlobeIcon,
} from "@modrinth/assets";
import {
Avatar,
@@ -387,17 +387,17 @@ import {
ConfirmModal,
DropdownSelect,
FileInput,
injectNotificationManager,
PopoutMenu,
useRelativeTime,
} from "@modrinth/ui";
import { isAdmin } from "@modrinth/utils";
import UpToDate from "assets/images/illustrations/up_to_date.svg";
import { addNotification } from "~/composables/notifs.js";
import AdPlaceholder from "~/components/ui/AdPlaceholder.vue";
import NavRow from "~/components/ui/NavRow.vue";
import ProjectCard from "~/components/ui/ProjectCard.vue";
import AdPlaceholder from "~/components/ui/AdPlaceholder.vue";
const { addNotification } = injectNotificationManager();
const vintl = useVIntl();
const { formatMessage } = vintl;
const formatRelativeTime = useRelativeTime();
@@ -664,7 +664,6 @@ async function saveChanges() {
isEditing.value = false;
} catch (err) {
addNotification({
group: "main",
title: formatMessage(commonMessages.errorNotificationTitle),
text: err,
type: "error",
@@ -688,7 +687,6 @@ async function deleteCollection() {
}
} catch (err) {
addNotification({
group: "main",
title: formatMessage(commonMessages.errorNotificationTitle),
text: err.data ? err.data.description : err,
type: "error",

View File

@@ -99,7 +99,10 @@
import { ChevronRightIcon, HistoryIcon } from "@modrinth/assets";
import { Avatar } from "@modrinth/ui";
import NotificationItem from "~/components/ui/NotificationItem.vue";
import { fetchExtraNotificationData, groupNotifications } from "~/helpers/notifications.ts";
import {
fetchExtraNotificationData,
groupNotifications,
} from "~/helpers/platform-notifications.ts";
useHead({
title: "Dashboard - Modrinth",

View File

@@ -56,16 +56,16 @@
</div>
</template>
<script setup>
import { Button, Pagination, Chips } from "@modrinth/ui";
import { HistoryIcon, CheckCheckIcon } from "@modrinth/assets";
import { CheckCheckIcon, HistoryIcon } from "@modrinth/assets";
import { Button, Chips, Pagination } from "@modrinth/ui";
import { formatProjectType } from "@modrinth/utils";
import Breadcrumbs from "~/components/ui/Breadcrumbs.vue";
import NotificationItem from "~/components/ui/NotificationItem.vue";
import {
fetchExtraNotificationData,
groupNotifications,
markAsRead,
} from "~/helpers/notifications.ts";
import NotificationItem from "~/components/ui/NotificationItem.vue";
import Breadcrumbs from "~/components/ui/Breadcrumbs.vue";
} from "~/helpers/platform-notifications.ts";
useHead({
title: "Notifications - Modrinth",

View File

@@ -301,17 +301,16 @@
</template>
<script>
import { Multiselect } from "vue-multiselect";
import {
SettingsIcon,
TrashIcon,
PlusIcon,
XIcon,
IssuesIcon,
EditIcon,
IssuesIcon,
PlusIcon,
SaveIcon,
SettingsIcon,
SortAscIcon,
SortDescIcon,
TrashIcon,
XIcon,
} from "@modrinth/assets";
import {
Avatar,
@@ -320,8 +319,10 @@ import {
CopyCode,
ProjectStatusBadge,
commonMessages,
injectNotificationManager,
} from "@modrinth/ui";
import { formatProjectType } from "@modrinth/utils";
import { Multiselect } from "vue-multiselect";
import Modal from "~/components/ui/Modal.vue";
import ModalCreation from "~/components/ui/ModalCreation.vue";
@@ -444,6 +445,8 @@ export default defineNuxtComponent({
return sortedArray;
},
async bulkEditLinks() {
const { addNotification } = injectNotificationManager();
try {
const baseData = {
issues_url: this.editLinks.issues.clear ? null : this.editLinks.issues.val.trim(),
@@ -477,8 +480,7 @@ export default defineNuxtComponent({
);
this.$refs.editLinksModal.hide();
this.$notify({
group: "main",
addNotification({
title: "Success",
text: "Bulk edited selected project's links.",
type: "success",
@@ -494,8 +496,7 @@ export default defineNuxtComponent({
this.editLinks.wiki.clear = false;
this.editLinks.discord.clear = false;
} catch (e) {
this.$notify({
group: "main",
addNotification({
title: "An error occurred",
text: e,
type: "error",

View File

@@ -149,10 +149,12 @@ import {
UnknownIcon,
XIcon,
} from "@modrinth/assets";
import { injectNotificationManager } from "@modrinth/ui";
import { formatDate } from "@modrinth/utils";
import dayjs from "dayjs";
import { computed } from "vue";
const { addNotification } = injectNotificationManager();
const auth = await useAuth();
const minWithdraw = ref(0.01);
@@ -201,9 +203,7 @@ async function updateVenmo() {
});
await useAuth(auth.value.token);
} catch (err) {
const data = useNuxtApp();
data.$notify({
group: "main",
addNotification({
title: "An error occurred",
text: err.data ? err.data.description : err,
type: "error",

View File

@@ -94,13 +94,14 @@
</div>
</template>
<script setup>
import { XIcon, PayPalIcon, UnknownIcon } from "@modrinth/assets";
import { PayPalIcon, UnknownIcon, XIcon } from "@modrinth/assets";
import { Badge, Breadcrumbs, DropdownSelect, injectNotificationManager } from "@modrinth/ui";
import { capitalizeString, formatWallet } from "@modrinth/utils";
import { Badge, Breadcrumbs, DropdownSelect } from "@modrinth/ui";
import dayjs from "dayjs";
import TremendousIcon from "~/assets/images/external/tremendous.svg?component";
import VenmoIcon from "~/assets/images/external/venmo-small.svg?component";
const { addNotification } = injectNotificationManager();
const vintl = useVIntl();
const { formatMessage } = vintl;
@@ -108,7 +109,6 @@ useHead({
title: "Transfer history - Modrinth",
});
const data = await useNuxtApp();
const auth = await useAuth();
const { data: payouts, refresh } = await useAsyncData(`payout`, () =>
@@ -155,8 +155,7 @@ async function cancelPayout(id) {
await refresh();
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",

View File

@@ -187,20 +187,21 @@
</template>
<script setup>
import { Multiselect } from "vue-multiselect";
import {
PayPalIcon,
SearchIcon,
RadioButtonIcon,
RadioButtonCheckedIcon,
XIcon,
RadioButtonIcon,
SearchIcon,
TransferIcon,
XIcon,
} from "@modrinth/assets";
import { Chips, Checkbox, Breadcrumbs } from "@modrinth/ui";
import { all } from "iso-3166-1";
import { Breadcrumbs, Checkbox, Chips, injectNotificationManager } from "@modrinth/ui";
import { formatMoney, formatWallet } from "@modrinth/utils";
import { all } from "iso-3166-1";
import { Multiselect } from "vue-multiselect";
import VenmoIcon from "~/assets/images/external/venmo.svg?component";
const { addNotification } = injectNotificationManager();
const auth = await useAuth();
const data = useNuxtApp();
@@ -355,8 +356,7 @@ async function withdraw() {
});
await useAuth(auth.value.token);
await navigateTo("/dashboard/revenue");
data.$notify({
group: "main",
addNotification({
title: "Withdrawal complete",
text:
selectedMethod.value.type === "tremendous"
@@ -365,8 +365,7 @@ async function withdraw() {
type: "success",
});
} catch (err) {
data.$notify({
group: "main",
addNotification({
title: "An error occurred",
text: err.data ? err.data.description : err,
type: "error",

View File

@@ -28,7 +28,7 @@
</nuxt-link>
</h2>
<span>
{{ $formatNumber(acceptedMembers?.length || 0) }}
{{ formatNumber(acceptedMembers?.length || 0) }}
member<template v-if="acceptedMembers?.length !== 1">s</template>
</span>
</div>
@@ -120,11 +120,11 @@
{
id: 'manage-projects',
action: () =>
navigateTo('/organization/' + organization.slug + '/settings/projects'),
hoverOnly: true,
shown: auth.user && currentMember,
router.push('/organization/' + organization?.slug + '/settings/projects'),
hoverFilledOnly: true,
shown: !!(auth.user && currentMember),
},
{ divider: true, shown: auth.user && currentMember },
{ divider: true, shown: !!(auth?.user && currentMember) },
{ id: 'copy-id', action: () => copyId() },
{ id: 'copy-permalink', action: () => copyPermalink() },
]"
@@ -157,20 +157,20 @@
<template v-for="member in acceptedMembers" :key="member.user.id">
<nuxt-link
class="details-list__item details-list__item--type-large"
:to="`/user/${member.user.username}`"
:to="`/user/${member?.user?.username}`"
>
<Avatar :src="member.user.avatar_url" circle />
<Avatar :src="member?.user.avatar_url" circle />
<div class="rows">
<span class="flex items-center gap-1">
{{ member.user.username }}
{{ member?.user?.username }}
<CrownIcon
v-if="member.is_owner"
v-if="member?.is_owner"
v-tooltip="'Organization owner'"
class="text-brand-orange"
/>
</span>
<span class="details-list__item__text--style-secondary">
{{ member.role ? member.role : "Member" }}
{{ member?.role ? member.role : "Member" }}
</span>
</div>
</nuxt-link>
@@ -196,16 +196,21 @@
<div v-if="navLinks.length > 2" class="mb-4 max-w-full overflow-x-auto">
<NavTabs :links="navLinks" />
</div>
<template v-if="projects?.length > 0">
<template v-if="projects && projects.length > 0">
<div class="project-list display-mode--list">
<ProjectCard
v-for="project in (route.params.projectType !== undefined
? projects.filter((x) =>
? (projects ?? []).filter((x) =>
x.project_types.includes(
route.params.projectType.substr(0, route.params.projectType.length - 1),
typeof route.params.projectType === 'string'
? route.params.projectType.slice(0, route.params.projectType.length - 1)
: route.params.projectType[0]?.slice(
0,
route.params.projectType[0].length - 1,
) || '',
),
)
: projects
: (projects ?? [])
)
.slice()
.sort((a, b) => b.downloads - a.downloads)"
@@ -225,9 +230,10 @@
:client-side="project.client_side"
:server-side="project.server_side"
:status="
auth.user && (auth.user.id === user.id || tags.staffRoles.includes(auth.user.role))
? project.status
: null
auth.user &&
(auth.user.id! === (user as any).id || tags.staffRoles.includes(auth.user.role))
? (project.status as ProjectStatus)
: undefined
"
:type="project.project_types[0] ?? 'project'"
:color="project.color"
@@ -240,9 +246,9 @@
<br />
<span class="preserve-lines text">
This organization doesn't have any projects yet.
<template v-if="isPermission(currentMember?.organization_permissions, 1 << 4)">
<template v-if="isPermission(currentMember?.permissions, 1 << 4)">
Would you like to
<a class="link" @click="$refs.modal_creation.show()">create one</a>?
<a class="link" @click="($refs as any).modal_creation?.show()">create one</a>?
</template>
</span>
</div>
@@ -251,50 +257,58 @@
</div>
</template>
<script setup>
<script setup lang="ts">
import {
BoxIcon,
MoreVerticalIcon,
UsersIcon,
SettingsIcon,
ChartIcon,
CheckIcon,
XIcon,
ClipboardCopyIcon,
OrganizationIcon,
DownloadIcon,
CrownIcon,
DownloadIcon,
MoreVerticalIcon,
OrganizationIcon,
SettingsIcon,
UsersIcon,
XIcon,
} from "@modrinth/assets";
import {
Avatar,
ButtonStyled,
Breadcrumbs,
ButtonStyled,
commonMessages,
ContentPageHeader,
OverflowMenu,
commonMessages,
} from "@modrinth/ui";
import type { Organization, ProjectStatus, ProjectType, ProjectV3 } from "@modrinth/utils";
import { formatNumber } from "@modrinth/utils";
import UpToDate from "~/assets/images/illustrations/up_to_date.svg?component";
import AdPlaceholder from "~/components/ui/AdPlaceholder.vue";
import ModalCreation from "~/components/ui/ModalCreation.vue";
import NavStack from "~/components/ui/NavStack.vue";
import NavStackItem from "~/components/ui/NavStackItem.vue";
import ModalCreation from "~/components/ui/ModalCreation.vue";
import UpToDate from "~/assets/images/illustrations/up_to_date.svg?component";
import ProjectCard from "~/components/ui/ProjectCard.vue";
import AdPlaceholder from "~/components/ui/AdPlaceholder.vue";
import { acceptTeamInvite, removeTeamMember } from "~/helpers/teams.js";
import NavTabs from "~/components/ui/NavTabs.vue";
import ProjectCard from "~/components/ui/ProjectCard.vue";
import { acceptTeamInvite, removeTeamMember } from "~/helpers/teams.js";
import {
OrganizationContext,
provideOrganizationContext,
} from "~/providers/organization-context.ts";
import { isPermission } from "~/utils/permissions.ts";
const vintl = useVIntl();
const { formatMessage } = vintl;
const formatCompactNumber = useCompactNumber(true);
const auth = await useAuth();
const auth: { user: any } & any = await useAuth();
const user = await useUser();
const cosmetics = useCosmetics();
const route = useNativeRoute();
const router = useRouter();
const tags = useTags();
const config = useRuntimeConfig();
let orgId = useRouteId();
const orgId = useRouteId();
// hacky way to show the edit button on the corner of the card.
const routeHasSettings = computed(() => route.path.includes("settings"));
@@ -303,12 +317,13 @@ const [
{ data: organization, refresh: refreshOrganization },
{ data: projects, refresh: refreshProjects },
] = await Promise.all([
useAsyncData(`organization/${orgId}`, () =>
useBaseFetch(`organization/${orgId}`, { apiVersion: 3 }),
useAsyncData(
`organization/${orgId}`,
() => useBaseFetch(`organization/${orgId}`, { apiVersion: 3 }) as Promise<Organization>,
),
useAsyncData(
`organization/${orgId}/projects`,
() => useBaseFetch(`organization/${orgId}/projects`, { apiVersion: 3 }),
() => useBaseFetch(`organization/${orgId}/projects`, { apiVersion: 3 }) as Promise<ProjectV3[]>,
{
transform: (projects) => {
for (const project of projects) {
@@ -359,7 +374,7 @@ if (!organization.value) {
// Filter accepted, sort by role, then by name and Owner role always goes first
const acceptedMembers = computed(() => {
const acceptedMembers = organization.value.members?.filter((x) => x.accepted);
const acceptedMembers = organization.value?.members?.filter((x) => x.accepted) ?? [];
const owner = acceptedMembers.find((x) => x.is_owner);
const rest = acceptedMembers.filter((x) => !x.is_owner) || [];
@@ -374,43 +389,14 @@ const acceptedMembers = computed(() => {
return [owner, ...rest];
});
const currentMember = computed(() => {
if (auth.value.user && organization.value) {
const member = organization.value.members.find((x) => x.user.id === auth.value.user.id);
if (member) {
return member;
}
if (tags.value.staffRoles.includes(auth.value.user.role)) {
return {
user: auth.value.user,
role: auth.value.user.role,
permissions: auth.value.user.role === "admin" ? 1023 : 12,
accepted: true,
payouts_split: 0,
avatar_url: auth.value.user.avatar_url,
name: auth.value.user.username,
};
}
}
return null;
});
const hasPermission = computed(() => {
const EDIT_DETAILS = 1 << 2;
return currentMember.value && (currentMember.value.permissions & EDIT_DETAILS) === EDIT_DETAILS;
});
const isInvited = computed(() => {
return currentMember.value?.accepted === false;
});
const projectTypes = computed(() => {
const obj = {};
const obj: Record<string, boolean> = {};
for (const project of projects.value) {
for (const project of projects.value ?? []) {
obj[project.project_types[0] ?? "project"] = true;
}
@@ -421,62 +407,27 @@ const projectTypes = computed(() => {
const sumDownloads = computed(() => {
let sum = 0;
for (const project of projects.value) {
for (const project of projects.value ?? []) {
sum += project.downloads;
}
return sum;
});
const patchIcon = async (icon) => {
const ext = icon.name.split(".").pop();
await useBaseFetch(`organization/${organization.value.id}/icon`, {
method: "PATCH",
body: icon,
query: { ext },
apiVersion: 3,
});
};
const deleteIcon = async () => {
await useBaseFetch(`organization/${organization.value.id}/icon`, {
method: "DELETE",
apiVersion: 3,
});
};
const patchOrganization = async (id, newData) => {
await useBaseFetch(`organization/${id}`, {
method: "PATCH",
body: newData,
apiVersion: 3,
});
if (newData.slug) {
orgId = newData.slug;
}
};
const onAcceptInvite = useClientTry(async () => {
await acceptTeamInvite(organization.value.team_id);
await acceptTeamInvite(organization.value?.team_id);
await refreshOrganization();
});
const onDeclineInvite = useClientTry(async () => {
await removeTeamMember(organization.value.team_id, auth.value?.user.id);
await removeTeamMember(organization.value?.team_id, auth.value?.user?.id);
await refreshOrganization();
});
provide("organizationContext", {
organization,
projects,
refresh,
currentMember,
hasPermission,
patchIcon,
deleteIcon,
patchOrganization,
});
const organizationContext = new OrganizationContext(organization, projects, auth, tags, refresh);
const { currentMember } = organizationContext;
provideOrganizationContext(organizationContext);
const title = `${organization.value.name} - Organization`;
const description = `${organization.value.description} - View the organization ${organization.value.name} on Modrinth`;
@@ -492,13 +443,13 @@ useSeoMeta({
const navLinks = computed(() => [
{
label: formatMessage(commonMessages.allProjectType),
href: `/organization/${organization.value.slug}`,
href: `/organization/${organization.value?.slug}`,
},
...projectTypes.value
.map((x) => {
return {
label: formatMessage(getProjectTypeMessage(x, true)),
href: `/organization/${organization.value.slug}/${x}s`,
label: formatMessage(getProjectTypeMessage(x as ProjectType, true)),
href: `/organization/${organization.value?.slug}/${x}s`,
};
})
.slice()
@@ -506,12 +457,12 @@ const navLinks = computed(() => [
]);
async function copyId() {
await navigator.clipboard.writeText(organization.value.id);
await navigator.clipboard.writeText(organization.value?.id ?? "");
}
async function copyPermalink() {
await navigator.clipboard.writeText(
`${config.public.siteUrl}/organization/${organization.value.id}`,
`${config.public.siteUrl}/organization/${organization.value?.id}`,
);
}
</script>

View File

@@ -16,8 +16,9 @@
<script setup>
import ChartDisplay from "~/components/ui/charts/ChartDisplay.vue";
import { injectOrganizationContext } from "~/providers/organization-context.ts";
const { projects } = inject("organizationContext");
const { projects } = injectOrganizationContext();
</script>
<style scoped lang="scss">

View File

@@ -1,7 +1,9 @@
<script setup>
import { Button, FileInput, Avatar, ConfirmModal } from "@modrinth/ui";
import { UploadIcon, SaveIcon, TrashIcon } from "@modrinth/assets";
import { SaveIcon, TrashIcon, UploadIcon } from "@modrinth/assets";
import { Avatar, Button, ConfirmModal, FileInput, injectNotificationManager } from "@modrinth/ui";
import { injectOrganizationContext } from "~/providers/organization-context.ts";
const { addNotification } = injectNotificationManager();
const {
organization,
refresh: refreshOrganization,
@@ -9,7 +11,7 @@ const {
deleteIcon,
patchIcon,
patchOrganization,
} = inject("organizationContext");
} = injectOrganizationContext();
const icon = ref(null);
const deletedIcon = ref(false);
@@ -74,7 +76,6 @@ const onSaveChanges = useClientTry(async () => {
await refreshOrganization();
addNotification({
group: "main",
title: "Organization updated",
text: "Your organization has been updated.",
type: "success",
@@ -88,7 +89,6 @@ const onDeleteOrganization = useClientTry(async () => {
});
addNotification({
group: "main",
title: "Organization deleted",
text: "Your organization has been deleted.",
type: "success",

View File

@@ -220,19 +220,21 @@
<script setup>
import {
CrownIcon,
DropdownIcon,
SaveIcon,
TransferIcon,
UserPlusIcon,
UserXIcon as UserRemoveIcon,
DropdownIcon,
CrownIcon,
} from "@modrinth/assets";
import { Button, Badge, Avatar, Checkbox } from "@modrinth/ui";
import { Avatar, Badge, Button, Checkbox, injectNotificationManager } from "@modrinth/ui";
import { ref } from "vue";
import { removeTeamMember } from "~/helpers/teams.js";
import { injectOrganizationContext } from "~/providers/organization-context.ts";
import { isPermission } from "~/utils/permissions.ts";
const { organization, refresh: refreshOrganization, currentMember } = inject("organizationContext");
const { addNotification } = injectNotificationManager();
const { organization, refresh: refreshOrganization, currentMember } = injectOrganizationContext();
const auth = await useAuth();
@@ -296,7 +298,6 @@ const onInviteTeamMember = useClientTry(async (teamId, username) => {
await refreshOrganization();
currentUsername.value = "";
addNotification({
group: "main",
title: "Member invited",
text: `${user.username} has been invited to the organization.`,
type: "success",
@@ -307,7 +308,6 @@ const onRemoveMember = useClientTry(async (teamId, member) => {
await removeTeamMember(teamId, member.user.id);
await refreshOrganization();
addNotification({
group: "main",
title: "Member removed",
text: `${member.user.username} has been removed from the organization.`,
type: "success",
@@ -332,7 +332,6 @@ const onUpdateTeamMember = useClientTry(async (teamId, member) => {
});
await refreshOrganization();
addNotification({
group: "main",
title: "Member updated",
text: `${member.user.username} has been updated.`,
type: "success",
@@ -349,7 +348,6 @@ const onTransferOwnership = useClientTry(async (teamId, uid) => {
});
await refreshOrganization();
addNotification({
group: "main",
title: "Ownership transferred",
text: `The ownership of ${organization.value.name} has been successfully transferred.`,
type: "success",

View File

@@ -298,28 +298,38 @@
</template>
<script setup>
import { Multiselect } from "vue-multiselect";
import {
BoxIcon,
SettingsIcon,
TrashIcon,
EditIcon,
IssuesIcon,
PlusIcon,
XIcon,
EditIcon,
SaveIcon,
SettingsIcon,
SortAscIcon,
SortDescIcon,
TrashIcon,
XIcon,
} from "@modrinth/assets";
import { Button, Modal, Avatar, CopyCode, Badge, Checkbox, commonMessages } from "@modrinth/ui";
import {
Avatar,
Badge,
Button,
Checkbox,
commonMessages,
CopyCode,
injectNotificationManager,
Modal,
} from "@modrinth/ui";
import { formatProjectType } from "@modrinth/utils";
import { Multiselect } from "vue-multiselect";
import ModalCreation from "~/components/ui/ModalCreation.vue";
import OrganizationProjectTransferModal from "~/components/ui/OrganizationProjectTransferModal.vue";
import { injectOrganizationContext } from "~/providers/organization-context.ts";
const { addNotification } = injectNotificationManager();
const { formatMessage } = useVIntl();
const { organization, projects, refresh } = inject("organizationContext");
const { organization, projects, refresh } = injectOrganizationContext();
const auth = await useAuth();
@@ -375,14 +385,12 @@ const onProjectTransferSubmit = async (projects) => {
await refreshUserProjects();
addNotification({
group: "main",
title: "Success",
text: "Transferred selected projects to organization.",
type: "success",
});
} catch (err) {
addNotification({
group: "main",
title: "An error occurred",
text: err?.data?.description || err?.message || err || "Unknown error",
type: "error",
@@ -511,7 +519,6 @@ const onBulkEditLinks = useClientTry(async () => {
editLinksModal.value.hide();
addNotification({
group: "main",
title: "Success",
text: "Bulk edited selected project's links.",
type: "success",

View File

@@ -11,8 +11,7 @@
:fetch-payment-data="fetchPaymentData"
:on-error="
(err) =>
data.$notify({
group: 'main',
addNotification({
title: 'An error occurred',
type: 'error',
text: err.message ?? (err.data ? err.data.description : err),
@@ -87,16 +86,18 @@
</template>
<script setup>
import {
ModrinthPlusIcon,
HeartIcon,
ModrinthPlusIcon,
SettingsIcon,
SparklesIcon,
StarIcon,
SettingsIcon,
} from "@modrinth/assets";
import { PurchaseModal } from "@modrinth/ui";
import { injectNotificationManager, PurchaseModal } from "@modrinth/ui";
import { calculateSavings, formatPrice, getCurrency } from "@modrinth/utils";
import { products } from "~/generated/state.json";
const { addNotification } = injectNotificationManager();
const title = "Subscribe to Modrinth Plus!";
const description =
"Subscribe to Modrinth Plus to go ad-free, support Modrinth's development, and get an exclusive profile badge! Half your subscription goes directly to Modrinth creators.";
@@ -120,7 +121,6 @@ useHead({
const vintl = useVIntl();
const data = useNuxtApp();
const config = useRuntimeConfig();
const auth = await useAuth();

View File

@@ -243,30 +243,33 @@
<script setup lang="ts">
import {
CheckCircleIcon,
CheckIcon,
ExternalIcon,
IssuesIcon,
LeftArrowIcon,
RightArrowIcon,
ScaleIcon,
SendIcon,
SpinnerIcon,
VersionIcon,
XCircleIcon,
} from "@modrinth/assets";
import {
AutoLink,
Avatar,
ButtonStyled,
injectNotificationManager,
MarkdownEditor,
RadialHeader,
RadioButtons,
ButtonStyled,
Avatar,
AutoLink,
} from "@modrinth/ui";
import {
ExternalIcon,
LeftArrowIcon,
RightArrowIcon,
CheckIcon,
SpinnerIcon,
SendIcon,
IssuesIcon,
CheckCircleIcon,
XCircleIcon,
ScaleIcon,
VersionIcon,
} from "@modrinth/assets";
import type { User, Version, Report } from "@modrinth/utils";
import { useVIntl, defineMessages, type MessageDescriptor } from "@vintl/vintl";
import type { Project, Report, User, Version } from "@modrinth/utils";
import { defineMessages, useVIntl, type MessageDescriptor } from "@vintl/vintl";
import { useImageUpload } from "~/composables/image-upload.ts";
const { addNotification } = injectNotificationManager();
const tags = useTags();
const route = useNativeRoute();
const router = useRouter();
@@ -439,7 +442,6 @@ const submitReport = async () => {
if (error instanceof Error) {
addNotification({
group: "main",
title: "An error occurred",
text: error.message,
type: "error",
@@ -465,7 +467,6 @@ const submitReport = async () => {
if (error instanceof Error) {
addNotification({
group: "main",
title: "An error occurred",
text: error.message,
type: "error",

View File

@@ -632,26 +632,31 @@
</template>
<script setup>
import { ButtonStyled, ModrinthServersPurchaseModal } from "@modrinth/ui";
import {
BoxIcon,
GameIcon,
RightArrowIcon,
ServerIcon,
TerminalSquareIcon,
TransferIcon,
VersionIcon,
ServerIcon,
} from "@modrinth/assets";
import { computed } from "vue";
import {
ButtonStyled,
injectNotificationManager,
ModrinthServersPurchaseModal,
} from "@modrinth/ui";
import { monthsInInterval } from "@modrinth/ui/src/utils/billing.ts";
import { formatPrice } from "@modrinth/utils";
import { useVIntl } from "@vintl/vintl";
import { products } from "~/generated/state.json";
import { useServersFetch } from "~/composables/servers/servers-fetch.ts";
import { computed } from "vue";
import OptionGroup from "~/components/ui/OptionGroup.vue";
import LoaderIcon from "~/components/ui/servers/icons/LoaderIcon.vue";
import ServerPlanSelector from "~/components/ui/servers/marketing/ServerPlanSelector.vue";
import OptionGroup from "~/components/ui/OptionGroup.vue";
import { useServersFetch } from "~/composables/servers/servers-fetch.ts";
import { products } from "~/generated/state.json";
const { addNotification } = injectNotificationManager();
const { locale } = useVIntl();
const billingPeriods = ref(["monthly", "quarterly"]);
@@ -812,7 +817,6 @@ const startTyping = () => {
const handleError = (err) => {
addNotification({
group: "main",
title: "An error occurred",
type: "error",
text: err.message ?? (err.data ? err.data.description : err),
@@ -831,7 +835,6 @@ async function fetchPaymentData() {
} catch (error) {
console.error("Error fetching payment data:", error);
addNotification({
group: "main",
title: "Error fetching payment data",
type: "error",
text: error.message || "An unexpected error occurred",
@@ -886,7 +889,6 @@ const selectProduct = async (product) => {
if ((product === "custom" && isCustomAtCapacity.value) || isAtCapacity.value) {
addNotification({
group: "main",
title: "Server Capacity Full",
type: "error",
text: "We are currently at capacity. Please try again later.",
@@ -902,7 +904,6 @@ const selectProduct = async (product) => {
(product !== "custom" && !selectedPlan.metadata)
) {
addNotification({
group: "main",
title: "Invalid product",
type: "error",
text: "The selected product was found but lacks necessary data. Please contact support.",

View File

@@ -350,38 +350,43 @@
</template>
<script setup lang="ts">
import { ref, computed, onMounted, onUnmounted, type Reactive } from "vue";
import { reloadNuxtApp } from "#app";
import { Intercom, shutdown } from "@intercom/messenger-js-sdk";
import {
SettingsIcon,
CheckIcon,
CopyIcon,
FileIcon,
IssuesIcon,
LeftArrowIcon,
RightArrowIcon,
CheckIcon,
FileIcon,
TransferIcon,
LockIcon,
RightArrowIcon,
SettingsIcon,
TransferIcon,
} from "@modrinth/assets";
import DOMPurify from "dompurify";
import { ButtonStyled, ErrorInformationCard, ServerNotice } from "@modrinth/ui";
import { Intercom, shutdown } from "@intercom/messenger-js-sdk";
import type { MessageDescriptor } from "@vintl/vintl";
import {
ButtonStyled,
ErrorInformationCard,
injectNotificationManager,
ServerNotice,
} from "@modrinth/ui";
import {
type Backup,
type PowerAction,
type ServerState,
type Stats,
type WSEvent,
type WSInstallationResultEvent,
type Backup,
type PowerAction,
} from "@modrinth/utils";
import { reloadNuxtApp } from "#app";
import { useModrinthServersConsole } from "~/store/console.ts";
import { useServersFetch } from "~/composables/servers/servers-fetch.ts";
import { ModrinthServer, useModrinthServers } from "~/composables/servers/modrinth-servers.ts";
import type { MessageDescriptor } from "@vintl/vintl";
import DOMPurify from "dompurify";
import { computed, onMounted, onUnmounted, ref, type Reactive } from "vue";
import ServerInstallation from "~/components/ui/servers/ServerInstallation.vue";
import PanelErrorIcon from "~/components/ui/servers/icons/PanelErrorIcon.vue";
import { ModrinthServer, useModrinthServers } from "~/composables/servers/modrinth-servers.ts";
import { useServersFetch } from "~/composables/servers/servers-fetch.ts";
import { useModrinthServersConsole } from "~/store/console.ts";
const app = useNuxtApp() as unknown as { $notify: any };
const { addNotification } = injectNotificationManager();
const socket = ref<WebSocket | null>(null);
const isReconnecting = ref(false);
@@ -964,7 +969,6 @@ const sendPowerAction = async (action: PowerAction) => {
const notifyError = (title: string, text: string) => {
addNotification({
group: "server",
title,
text,
type: "error",
@@ -1149,8 +1153,7 @@ async function dismissNotice(noticeId: number) {
await useServersFetch(`servers/${serverId}/notices/${noticeId}/dismiss`, {
method: "POST",
}).catch((err) => {
app.$notify({
group: "main",
addNotification({
title: "Error dismissing notice",
text: err,
type: "error",

View File

@@ -150,19 +150,20 @@
</template>
<script setup lang="ts">
import { ButtonStyled, TagItem } from "@modrinth/ui";
import { useStorage } from "@vueuse/core";
import { SpinnerIcon, PlusIcon, DownloadIcon, SettingsIcon, IssuesIcon } from "@modrinth/assets";
import { ref, computed } from "vue";
import { DownloadIcon, IssuesIcon, PlusIcon, SettingsIcon, SpinnerIcon } from "@modrinth/assets";
import { ButtonStyled, injectNotificationManager, TagItem } from "@modrinth/ui";
import type { Backup } from "@modrinth/utils";
import { useStorage } from "@vueuse/core";
import { computed, ref } from "vue";
import BackupCreateModal from "~/components/ui/servers/BackupCreateModal.vue";
import BackupDeleteModal from "~/components/ui/servers/BackupDeleteModal.vue";
import BackupItem from "~/components/ui/servers/BackupItem.vue";
import BackupRenameModal from "~/components/ui/servers/BackupRenameModal.vue";
import BackupCreateModal from "~/components/ui/servers/BackupCreateModal.vue";
import BackupRestoreModal from "~/components/ui/servers/BackupRestoreModal.vue";
import BackupDeleteModal from "~/components/ui/servers/BackupDeleteModal.vue";
import BackupSettingsModal from "~/components/ui/servers/BackupSettingsModal.vue";
import { ModrinthServer } from "~/composables/servers/modrinth-servers.ts";
const { addNotification } = injectNotificationManager();
const props = defineProps<{
server: ModrinthServer;
isServerRunning: boolean;
@@ -236,7 +237,11 @@ const prepareDownload = async (backupId: string) => {
await props.server.backups?.prepare(backupId);
} catch (error) {
console.error("Failed to prepare download:", error);
addNotification({ type: "error", title: "Failed to prepare backup for download", text: error });
addNotification({
type: "error",
title: "Failed to prepare backup for download",
text: error as string,
});
}
};

View File

@@ -335,28 +335,29 @@
<script setup lang="ts">
import {
SearchIcon,
EditIcon,
TrashIcon,
PackageClosedIcon,
FilterIcon,
DropdownIcon,
PlusIcon,
MoreVerticalIcon,
CompassIcon,
WrenchIcon,
ListIcon,
DropdownIcon,
EditIcon,
FileIcon,
FilterIcon,
IssuesIcon,
ListIcon,
MoreVerticalIcon,
PackageClosedIcon,
PlusIcon,
SearchIcon,
TrashIcon,
WrenchIcon,
} from "@modrinth/assets";
import { Avatar, ButtonStyled } from "@modrinth/ui";
import { ref, computed, watch, onMounted, onUnmounted } from "vue";
import { Avatar, ButtonStyled, injectNotificationManager } from "@modrinth/ui";
import type { Mod } from "@modrinth/utils";
import { computed, onMounted, onUnmounted, ref, watch } from "vue";
import FilesUploadDragAndDrop from "~/components/ui/servers/FilesUploadDragAndDrop.vue";
import FilesUploadDropdown from "~/components/ui/servers/FilesUploadDropdown.vue";
import { acceptFileFromProjectType } from "~/helpers/fileUtils.js";
import { ModrinthServer } from "~/composables/servers/modrinth-servers.ts";
import { acceptFileFromProjectType } from "~/helpers/fileUtils.js";
const { addNotification } = injectNotificationManager();
const props = defineProps<{
server: ModrinthServer;
}>();

View File

@@ -266,26 +266,27 @@
</template>
<script setup lang="ts">
import { useInfiniteScroll } from "@vueuse/core";
import {
UnknownIcon,
XIcon,
SpinnerIcon,
PackageOpenIcon,
CheckIcon,
UploadIcon,
FolderOpenIcon,
PackageOpenIcon,
SpinnerIcon,
UnknownIcon,
UploadIcon,
XIcon,
} from "@modrinth/assets";
import { computed } from "vue";
import { ButtonStyled, ProgressBar } from "@modrinth/ui";
import { ButtonStyled, injectNotificationManager, ProgressBar } from "@modrinth/ui";
import type { DirectoryItem, DirectoryResponse, FilesystemOp, FSQueuedOp } from "@modrinth/utils";
import { formatBytes, ModrinthServersFetchError } from "@modrinth/utils";
import type { FilesystemOp, FSQueuedOp, DirectoryItem, DirectoryResponse } from "@modrinth/utils";
import { handleError, ModrinthServer } from "~/composables/servers/modrinth-servers.ts";
import { useInfiniteScroll } from "@vueuse/core";
import { computed } from "vue";
import FilesUploadConflictModal from "~/components/ui/servers/FilesUploadConflictModal.vue";
import FilesUploadDragAndDrop from "~/components/ui/servers/FilesUploadDragAndDrop.vue";
import FilesUploadDropdown from "~/components/ui/servers/FilesUploadDropdown.vue";
import FilesUploadZipUrlModal from "~/components/ui/servers/FilesUploadZipUrlModal.vue";
import FilesUploadConflictModal from "~/components/ui/servers/FilesUploadConflictModal.vue";
import { handleError, ModrinthServer } from "~/composables/servers/modrinth-servers.ts";
const { addNotification } = injectNotificationManager();
const flags = useFeatureFlags();
const baseId = useId();
@@ -449,7 +450,6 @@ const undoLastOperation = async () => {
refreshList();
addNotification({
group: "files",
title: `${lastOperation.type === "move" ? "Move" : "Rename"} undone`,
text: `${lastOperation.fileName} has been restored to its original ${lastOperation.type === "move" ? "location" : "name"}`,
type: "success",
@@ -457,7 +457,6 @@ const undoLastOperation = async () => {
} catch (error) {
console.error(`Error undoing ${lastOperation.type}:`, error);
addNotification({
group: "files",
title: "Undo failed",
text: `Failed to undo the last ${lastOperation.type} operation`,
type: "error",
@@ -489,7 +488,6 @@ const redoLastOperation = async () => {
refreshList();
addNotification({
group: "files",
title: `${lastOperation.type === "move" ? "Move" : "Rename"} redone`,
text: `${lastOperation.fileName} has been ${lastOperation.type === "move" ? "moved" : "renamed"} again`,
type: "success",
@@ -497,7 +495,6 @@ const redoLastOperation = async () => {
} catch (error) {
console.error(`Error redoing ${lastOperation.type}:`, error);
addNotification({
group: "files",
title: "Redo failed",
text: `Failed to redo the last ${lastOperation.type} operation`,
type: "error",
@@ -513,7 +510,6 @@ const handleCreateNewItem = async (name: string) => {
refreshList();
addNotification({
group: "files",
title: `${newItemType.value === "directory" ? "Folder" : "File"} created`,
text: `New ${newItemType.value === "directory" ? "folder" : "file"} ${name} has been created.`,
type: "success",
@@ -549,7 +545,6 @@ const handleRenameItem = async (newName: string) => {
}
addNotification({
group: "files",
title: `${selectedItem.value.type === "directory" ? "Folder" : "File"} renamed`,
text: `${selectedItem.value.name} has been renamed to ${newName}`,
type: "success",
@@ -559,7 +554,6 @@ const handleRenameItem = async (newName: string) => {
if (error instanceof ModrinthServersFetchError) {
if (error.statusCode === 400) {
addNotification({
group: "files",
title: "Could not rename",
text: `An item named "${newName}" already exists in this location`,
type: "error",
@@ -567,7 +561,6 @@ const handleRenameItem = async (newName: string) => {
return;
}
addNotification({
group: "files",
title: "Could not rename item",
text: "An unexpected error occurred",
type: "error",
@@ -625,7 +618,6 @@ const handleMoveItem = async (destination: string) => {
refreshList();
addNotification({
group: "files",
title: `${itemType === "directory" ? "Folder" : "File"} moved`,
text: `${selectedItem.value.name} has been moved to ${newPath}`,
type: "success",
@@ -658,7 +650,6 @@ const handleDirectMove = async (moveData: {
refreshList();
addNotification({
group: "files",
title: `${moveData.type === "directory" ? "Folder" : "File"} moved`,
text: `${moveData.name} has been moved to ${newPath}`,
type: "success",
@@ -675,7 +666,6 @@ const handleDeleteItem = async () => {
refreshList();
addNotification({
group: "files",
title: "File deleted",
text: "Your file has been deleted.",
type: "success",
@@ -717,14 +707,12 @@ const handleCreateError = (error: any) => {
if (error instanceof ModrinthServersFetchError) {
if (error.statusCode === 400) {
addNotification({
group: "files",
title: "Error creating item",
text: "Invalid file",
type: "error",
});
} else if (error.statusCode === 500) {
addNotification({
group: "files",
title: "Error creating item",
text: "Something went wrong. The file may already exist.",
type: "error",
@@ -1010,7 +998,6 @@ const requestShareLink = async () => {
if (response.success) {
await navigator.clipboard.writeText(response.url);
addNotification({
group: "files",
title: "Log URL copied",
text: "Your log file URL has been copied to your clipboard.",
type: "success",
@@ -1080,7 +1067,6 @@ const saveFileContent = async (exit: boolean = true) => {
}
addNotification({
group: "files",
title: "File saved",
text: "Your file has been saved.",
type: "success",
@@ -1095,7 +1081,6 @@ const saveFileContentRestart = async () => {
await props.server.general?.power("Restart");
addNotification({
group: "files",
title: "Server restarted",
text: "Your server has been restarted.",
type: "success",

View File

@@ -113,9 +113,11 @@
<script setup lang="ts">
import { EditIcon, TransferIcon } from "@modrinth/assets";
import { injectNotificationManager } from "@modrinth/ui";
import ButtonStyled from "@modrinth/ui/src/components/base/ButtonStyled.vue";
import { ModrinthServer } from "~/composables/servers/modrinth-servers.ts";
const { addNotification } = injectNotificationManager();
const props = defineProps<{
server: ModrinthServer;
}>();
@@ -161,7 +163,6 @@ const saveGeneral = async () => {
if (!available) {
addNotification({
group: "serverOptions",
type: "error",
title: "Subdomain not available",
text: "The subdomain you entered is already in use.",
@@ -173,7 +174,6 @@ const saveGeneral = async () => {
} catch (error) {
console.error("Error checking subdomain availability:", error);
addNotification({
group: "serverOptions",
type: "error",
title: "Error checking availability",
text: "Failed to verify if the subdomain is available.",
@@ -184,7 +184,6 @@ const saveGeneral = async () => {
await new Promise((resolve) => setTimeout(resolve, 500));
await props.server.refresh();
addNotification({
group: "serverOptions",
type: "success",
title: "Server settings updated",
text: "Your server settings were successfully changed.",
@@ -192,7 +191,6 @@ const saveGeneral = async () => {
} catch (error) {
console.error(error);
addNotification({
group: "serverOptions",
type: "error",
title: "Failed to update server settings",
text: "An error occurred while attempting to update your server settings.",
@@ -211,7 +209,6 @@ const uploadFile = async (e: Event) => {
const file = (e.target as HTMLInputElement).files?.[0];
if (!file) {
addNotification({
group: "serverOptions",
type: "error",
title: "No file selected",
text: "Please select a file to upload.",
@@ -267,7 +264,6 @@ const uploadFile = async (e: Event) => {
});
addNotification({
group: "serverOptions",
type: "success",
title: "Server icon updated",
text: "Your server icon was successfully changed.",
@@ -275,7 +271,6 @@ const uploadFile = async (e: Event) => {
} catch (error) {
console.error("Error uploading icon:", error);
addNotification({
group: "serverOptions",
type: "error",
title: "Upload failed",
text: "Failed to upload server icon.",
@@ -295,7 +290,6 @@ const resetIcon = async () => {
await props.server.refresh(["general"]);
addNotification({
group: "serverOptions",
type: "success",
title: "Server icon reset",
text: "Your server icon was successfully reset.",
@@ -303,7 +297,6 @@ const resetIcon = async () => {
} catch (error) {
console.error("Error resetting icon:", error);
addNotification({
group: "serverOptions",
type: "error",
title: "Reset failed",
text: "Failed to reset server icon.",

View File

@@ -115,10 +115,11 @@
</template>
<script setup lang="ts">
import { ButtonStyled, CopyCode } from "@modrinth/ui";
import { CopyIcon, ExternalIcon, EyeIcon, EyeOffIcon } from "@modrinth/assets";
import { ButtonStyled, CopyCode, injectNotificationManager } from "@modrinth/ui";
import { ModrinthServer } from "~/composables/servers/modrinth-servers.ts";
const { addNotification } = injectNotificationManager();
const props = defineProps<{
server: ModrinthServer;
}>();

View File

@@ -264,19 +264,26 @@
<script setup lang="ts">
import {
PlusIcon,
TrashIcon,
EditIcon,
VersionIcon,
SaveIcon,
InfoIcon,
UploadIcon,
IssuesIcon,
PlusIcon,
SaveIcon,
TrashIcon,
UploadIcon,
VersionIcon,
} from "@modrinth/assets";
import { ButtonStyled, NewModal, ConfirmModal, CopyCode } from "@modrinth/ui";
import { ref, computed, nextTick } from "vue";
import {
ButtonStyled,
ConfirmModal,
CopyCode,
injectNotificationManager,
NewModal,
} from "@modrinth/ui";
import { computed, nextTick, ref } from "vue";
import { ModrinthServer } from "~/composables/servers/modrinth-servers.ts";
const { addNotification } = injectNotificationManager();
const props = defineProps<{
server: ModrinthServer;
}>();
@@ -317,7 +324,6 @@ const addNewAllocation = async () => {
newAllocationName.value = "";
addNotification({
group: "serverOptions",
type: "success",
title: "Allocation reserved",
text: "Your allocation has been reserved.",
@@ -359,7 +365,6 @@ const confirmDeleteAllocation = async () => {
await props.server.refresh(["network"]);
addNotification({
group: "serverOptions",
type: "success",
title: "Allocation removed",
text: "Your allocation has been removed.",
@@ -379,7 +384,6 @@ const editAllocation = async () => {
newAllocationName.value = "";
addNotification({
group: "serverOptions",
type: "success",
title: "Allocation updated",
text: "Your allocation has been updated.",
@@ -397,7 +401,6 @@ const saveNetwork = async () => {
const available = await props.server.network?.checkSubdomainAvailability(serverSubdomain.value);
if (!available) {
addNotification({
group: "serverOptions",
type: "error",
title: "Subdomain not available",
text: "The subdomain you entered is already in use.",
@@ -416,7 +419,6 @@ const saveNetwork = async () => {
await new Promise((resolve) => setTimeout(resolve, 500));
await props.server.refresh();
addNotification({
group: "serverOptions",
type: "success",
title: "Server settings updated",
text: "Your server settings were successfully changed.",
@@ -424,7 +426,6 @@ const saveNetwork = async () => {
} catch (error) {
console.error(error);
addNotification({
group: "serverOptions",
type: "error",
title: "Failed to update server settings",
text: "An error occurred while attempting to update your server settings.",
@@ -483,7 +484,6 @@ const exportDnsRecords = () => {
const copyText = (text: string) => {
navigator.clipboard.writeText(text);
addNotification({
group: "serverOptions",
type: "success",
title: "Text copied",
text: `${text} has been copied to your clipboard`,

View File

@@ -42,9 +42,11 @@
</template>
<script setup lang="ts">
import { injectNotificationManager } from "@modrinth/ui";
import { useStorage } from "@vueuse/core";
import { ModrinthServer } from "~/composables/servers/modrinth-servers.ts";
const { addNotification } = injectNotificationManager();
const route = useNativeRoute();
const serverId = route.params.id as string;
@@ -109,7 +111,6 @@ const hasUnsavedChanges = computed(() => {
const savePreferences = () => {
userPreferences.value = { ...newUserPreferences.value };
addNotification({
group: "serverOptions",
type: "success",
title: "Preferences saved",
text: "Your preferences have been saved.",

View File

@@ -143,11 +143,13 @@
</template>
<script setup lang="ts">
import { ref, watch, computed, inject } from "vue";
import { EyeIcon, SearchIcon, IssuesIcon } from "@modrinth/assets";
import { EyeIcon, IssuesIcon, SearchIcon } from "@modrinth/assets";
import { injectNotificationManager } from "@modrinth/ui";
import Fuse from "fuse.js";
import { computed, inject, ref, watch } from "vue";
import { ModrinthServer } from "~/composables/servers/modrinth-servers.ts";
const { addNotification } = injectNotificationManager();
const props = defineProps<{
server: ModrinthServer;
}>();
@@ -281,7 +283,6 @@ const saveProperties = async () => {
originalProperties.value = JSON.parse(JSON.stringify(liveProperties.value));
await props.server.refresh();
addNotification({
group: "serverOptions",
type: "success",
title: "Server properties updated",
text: "Your server properties were successfully changed.",
@@ -289,7 +290,6 @@ const saveProperties = async () => {
} catch (error) {
console.error("Error updating server properties:", error);
addNotification({
group: "serverOptions",
type: "error",
title: "Failed to update server properties",
text: "An error occurred while attempting to update your server properties.",

View File

@@ -112,10 +112,11 @@
</template>
<script setup lang="ts">
import { UpdatedIcon, IssuesIcon } from "@modrinth/assets";
import { ButtonStyled } from "@modrinth/ui";
import { IssuesIcon, UpdatedIcon } from "@modrinth/assets";
import { ButtonStyled, injectNotificationManager } from "@modrinth/ui";
import { ModrinthServer } from "~/composables/servers/modrinth-servers.ts";
const { addNotification } = injectNotificationManager();
const props = defineProps<{
server: ModrinthServer;
}>();
@@ -199,7 +200,6 @@ async function saveStartup() {
}
addNotification({
group: "serverOptions",
type: "success",
title: "Server settings updated",
text: "Your server settings were successfully changed.",
@@ -207,7 +207,6 @@ async function saveStartup() {
} catch (error) {
console.error(error);
addNotification({
group: "serverOptions",
type: "error",
title: "Failed to update server arguments",
text: "Please try again later.",

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",