feat: migrate app notifications to DI + frontend styling

This commit is contained in:
IMB11 2025-07-31 13:20:39 +01:00
parent 718a35737a
commit d7ea034963
53 changed files with 699 additions and 593 deletions

View File

@ -6,6 +6,5 @@
"editor.codeActionsOnSave": { "editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit", "source.fixAll.eslint": "explicit",
"source.organizeImports": "always", "source.organizeImports": "always",
"source.sortImports": "always"
} }
} }

View File

@ -1,6 +1,35 @@
<script setup> <script setup>
import { computed, onMounted, onUnmounted, ref, watch, provide } from 'vue' import ModrinthAppLogo from '@/assets/modrinth_app.svg?component'
import { RouterView, useRoute, useRouter } from 'vue-router' import ModrinthLoadingIndicator from '@/components/LoadingIndicatorBar.vue'
import AccountsCard from '@/components/ui/AccountsCard.vue'
import Breadcrumbs from '@/components/ui/Breadcrumbs.vue'
import ErrorModal from '@/components/ui/ErrorModal.vue'
import FriendsList from '@/components/ui/friends/FriendsList.vue'
import IncompatibilityWarningModal from '@/components/ui/install_flow/IncompatibilityWarningModal.vue'
import InstallConfirmModal from '@/components/ui/install_flow/InstallConfirmModal.vue'
import ModInstallModal from '@/components/ui/install_flow/ModInstallModal.vue'
import InstanceCreationModal from '@/components/ui/InstanceCreationModal.vue'
import AppSettingsModal from '@/components/ui/modal/AppSettingsModal.vue'
import AuthGrantFlowWaitModal from '@/components/ui/modal/AuthGrantFlowWaitModal.vue'
import NavButton from '@/components/ui/NavButton.vue'
import PromotionWrapper from '@/components/ui/PromotionWrapper.vue'
import QuickInstanceSwitcher from '@/components/ui/QuickInstanceSwitcher.vue'
import RunningAppBar from '@/components/ui/RunningAppBar.vue'
import SplashScreen from '@/components/ui/SplashScreen.vue'
import URLConfirmModal from '@/components/ui/URLConfirmModal.vue'
import { useCheckDisableMouseover } from '@/composables/macCssFix.js'
import { hide_ads_window, init_ads_window } from '@/helpers/ads.js'
import { debugAnalytics, initAnalytics, optOutAnalytics, trackEvent } from '@/helpers/analytics'
import { get_user } from '@/helpers/cache.js'
import { command_listener, warning_listener } from '@/helpers/events.js'
import { useFetch } from '@/helpers/fetch.js'
import { cancelLogin, get as getCreds, login, logout } from '@/helpers/mr_auth.js'
import { get } from '@/helpers/settings.ts'
import { get_opening_command, initialize_state } from '@/helpers/state'
import { getOS, isDev, restartApp } from '@/helpers/utils.js'
import { useError } from '@/store/error.js'
import { useInstall } from '@/store/install.js'
import { useLoading, useTheming } from '@/store/state'
import { import {
ArrowBigUpDashIcon, ArrowBigUpDashIcon,
ChangeSkinIcon, ChangeSkinIcon,
@ -13,68 +42,44 @@ import {
LogOutIcon, LogOutIcon,
MaximizeIcon, MaximizeIcon,
MinimizeIcon, MinimizeIcon,
NewspaperIcon,
PlusIcon, PlusIcon,
RestoreIcon, RestoreIcon,
RightArrowIcon, RightArrowIcon,
SettingsIcon, SettingsIcon,
WorldIcon, WorldIcon,
XIcon, XIcon,
NewspaperIcon,
} from '@modrinth/assets' } from '@modrinth/assets'
import { import {
Avatar, Avatar,
Button, Button,
ButtonStyled, ButtonStyled,
Notifications,
OverflowMenu,
NewsArticleCard, NewsArticleCard,
NotificationPanel,
OverflowMenu,
provideNotificationManager,
} from '@modrinth/ui' } from '@modrinth/ui'
import { useLoading, useTheming } from '@/store/state'
import ModrinthAppLogo from '@/assets/modrinth_app.svg?component'
import AccountsCard from '@/components/ui/AccountsCard.vue'
import InstanceCreationModal from '@/components/ui/InstanceCreationModal.vue'
import { get } from '@/helpers/settings.ts'
import Breadcrumbs from '@/components/ui/Breadcrumbs.vue'
import RunningAppBar from '@/components/ui/RunningAppBar.vue'
import SplashScreen from '@/components/ui/SplashScreen.vue'
import ErrorModal from '@/components/ui/ErrorModal.vue'
import ModrinthLoadingIndicator from '@/components/LoadingIndicatorBar.vue'
import { handleError, useNotifications } from '@/store/notifications.js'
import { command_listener, warning_listener } from '@/helpers/events.js'
import { type } from '@tauri-apps/plugin-os'
import { getOS, isDev, restartApp } from '@/helpers/utils.js'
import { debugAnalytics, initAnalytics, optOutAnalytics, trackEvent } from '@/helpers/analytics'
import { getCurrentWindow } from '@tauri-apps/api/window'
import { getVersion } from '@tauri-apps/api/app'
import URLConfirmModal from '@/components/ui/URLConfirmModal.vue'
import { create_profile_and_install_from_file } from './helpers/pack'
import { useError } from '@/store/error.js'
import { useCheckDisableMouseover } from '@/composables/macCssFix.js'
import ModInstallModal from '@/components/ui/install_flow/ModInstallModal.vue'
import IncompatibilityWarningModal from '@/components/ui/install_flow/IncompatibilityWarningModal.vue'
import InstallConfirmModal from '@/components/ui/install_flow/InstallConfirmModal.vue'
import { useInstall } from '@/store/install.js'
import { invoke } from '@tauri-apps/api/core'
import { get_opening_command, initialize_state } from '@/helpers/state'
import { saveWindowState, StateFlags } from '@tauri-apps/plugin-window-state'
import { renderString } from '@modrinth/utils' import { renderString } from '@modrinth/utils'
import { useFetch } from '@/helpers/fetch.js' import { getVersion } from '@tauri-apps/api/app'
import { check } from '@tauri-apps/plugin-updater' import { invoke } from '@tauri-apps/api/core'
import NavButton from '@/components/ui/NavButton.vue' import { getCurrentWindow } from '@tauri-apps/api/window'
import { cancelLogin, get as getCreds, login, logout } from '@/helpers/mr_auth.js'
import { get_user } from '@/helpers/cache.js'
import AppSettingsModal from '@/components/ui/modal/AppSettingsModal.vue'
import AuthGrantFlowWaitModal from '@/components/ui/modal/AuthGrantFlowWaitModal.vue'
import PromotionWrapper from '@/components/ui/PromotionWrapper.vue'
import { hide_ads_window, init_ads_window } from '@/helpers/ads.js'
import FriendsList from '@/components/ui/friends/FriendsList.vue'
import { openUrl } from '@tauri-apps/plugin-opener' import { openUrl } from '@tauri-apps/plugin-opener'
import QuickInstanceSwitcher from '@/components/ui/QuickInstanceSwitcher.vue' import { type } from '@tauri-apps/plugin-os'
import { get_available_capes, get_available_skins } from './helpers/skins' import { check } from '@tauri-apps/plugin-updater'
import { saveWindowState, StateFlags } from '@tauri-apps/plugin-window-state'
import { computed, onMounted, onUnmounted, provide, ref, watch } from 'vue'
import { RouterView, useRoute, useRouter } from 'vue-router'
import { create_profile_and_install_from_file } from './helpers/pack'
import { generateSkinPreviews } from './helpers/rendering/batch-skin-renderer' import { generateSkinPreviews } from './helpers/rendering/batch-skin-renderer'
import { get_available_capes, get_available_skins } from './helpers/skins'
import { AppNotificationManager } from './providers/app-notifications'
const themeStore = useTheming() const themeStore = useTheming()
const notificationManager = new AppNotificationManager()
provideNotificationManager(notificationManager)
const { handleError, addNotification } = notificationManager
const news = ref([]) const news = ref([])
const urlModal = ref(null) const urlModal = ref(null)
@ -167,7 +172,7 @@ async function setupApp() {
} }
await warning_listener((e) => await warning_listener((e) =>
notificationsWrapper.value.addNotification({ addNotification({
title: 'Warning', title: 'Warning',
text: e.message, text: e.message,
type: 'warn', type: 'warn',
@ -251,9 +256,6 @@ const route = useRoute()
const loading = useLoading() const loading = useLoading()
loading.setEnabled(false) loading.setEnabled(false)
const notifications = useNotifications()
const notificationsWrapper = ref()
const error = useError() const error = useError()
const errorModal = ref() const errorModal = ref()
@ -335,8 +337,6 @@ watch(
onMounted(() => { onMounted(() => {
invoke('show_window') invoke('show_window')
notifications.setNotifs(notificationsWrapper.value)
error.setErrorModal(errorModal.value) error.setErrorModal(errorModal.value)
install.setIncompatibilityWarningModal(incompatibilityWarningModal) install.setIncompatibilityWarningModal(incompatibilityWarningModal)
@ -657,7 +657,7 @@ function handleAuxClick(e) {
</div> </div>
</div> </div>
<URLConfirmModal ref="urlModal" /> <URLConfirmModal ref="urlModal" />
<Notifications ref="notificationsWrapper" sidebar /> <NotificationPanel />
<ErrorModal ref="errorModal" /> <ErrorModal ref="errorModal" />
<ModInstallModal ref="modInstallModal" /> <ModInstallModal ref="modInstallModal" />
<IncompatibilityWarningModal ref="incompatibilityWarningModal" /> <IncompatibilityWarningModal ref="incompatibilityWarningModal" />

View File

@ -1,24 +1,25 @@
<script setup> <script setup>
import ContextMenu from '@/components/ui/ContextMenu.vue'
import Instance from '@/components/ui/Instance.vue' import Instance from '@/components/ui/Instance.vue'
import { computed, ref } from 'vue' import ConfirmModalWrapper from '@/components/ui/modal/ConfirmModalWrapper.vue'
import { duplicate, remove } from '@/helpers/profile.js'
import { import {
ClipboardCopyIcon, ClipboardCopyIcon,
EyeIcon,
FolderOpenIcon, FolderOpenIcon,
PlayIcon, PlayIcon,
PlusIcon, PlusIcon,
TrashIcon,
StopCircleIcon,
EyeIcon,
SearchIcon, SearchIcon,
StopCircleIcon,
TrashIcon,
XIcon, XIcon,
} from '@modrinth/assets' } from '@modrinth/assets'
import { Button, DropdownSelect } from '@modrinth/ui' import { Button, DropdownSelect, injectNotificationManager } from '@modrinth/ui'
import { formatCategoryHeader } from '@modrinth/utils' import { formatCategoryHeader } from '@modrinth/utils'
import ContextMenu from '@/components/ui/ContextMenu.vue'
import dayjs from 'dayjs' import dayjs from 'dayjs'
import { duplicate, remove } from '@/helpers/profile.js' import { computed, ref } from 'vue'
import { handleError } from '@/store/notifications.js'
import ConfirmModalWrapper from '@/components/ui/modal/ConfirmModalWrapper.vue' const { handleError } = injectNotificationManager()
const props = defineProps({ const props = defineProps({
instances: { instances: {

View File

@ -1,31 +1,32 @@
<script setup> <script setup>
import {
ClipboardCopyIcon,
FolderOpenIcon,
PlayIcon,
PlusIcon,
TrashIcon,
DownloadIcon,
GlobeIcon,
StopCircleIcon,
ExternalIcon,
EyeIcon,
} from '@modrinth/assets'
import ConfirmModalWrapper from '@/components/ui/modal/ConfirmModalWrapper.vue'
import Instance from '@/components/ui/Instance.vue'
import { computed, onMounted, onUnmounted, ref } from 'vue'
import ContextMenu from '@/components/ui/ContextMenu.vue' import ContextMenu from '@/components/ui/ContextMenu.vue'
import Instance from '@/components/ui/Instance.vue'
import ConfirmModalWrapper from '@/components/ui/modal/ConfirmModalWrapper.vue'
import ProjectCard from '@/components/ui/ProjectCard.vue' import ProjectCard from '@/components/ui/ProjectCard.vue'
import { get_by_profile_path } from '@/helpers/process.js'
import { handleError } from '@/store/notifications.js'
import { duplicate, kill, remove, run } from '@/helpers/profile.js'
import { useRouter } from 'vue-router'
import { showProfileInFolder } from '@/helpers/utils.js'
import { trackEvent } from '@/helpers/analytics' import { trackEvent } from '@/helpers/analytics'
import { get_by_profile_path } from '@/helpers/process.js'
import { duplicate, kill, remove, run } from '@/helpers/profile.js'
import { showProfileInFolder } from '@/helpers/utils.js'
import { handleSevereError } from '@/store/error.js' import { handleSevereError } from '@/store/error.js'
import { install as installVersion } from '@/store/install.js' import { install as installVersion } from '@/store/install.js'
import {
ClipboardCopyIcon,
DownloadIcon,
ExternalIcon,
EyeIcon,
FolderOpenIcon,
GlobeIcon,
PlayIcon,
PlusIcon,
StopCircleIcon,
TrashIcon,
} from '@modrinth/assets'
import { HeadingLink, injectNotificationManager } from '@modrinth/ui'
import { openUrl } from '@tauri-apps/plugin-opener' import { openUrl } from '@tauri-apps/plugin-opener'
import { HeadingLink } from '@modrinth/ui' import { computed, onMounted, onUnmounted, ref } from 'vue'
import { useRouter } from 'vue-router'
const { handleError } = injectNotificationManager()
const router = useRouter() const router = useRouter()

View File

@ -73,22 +73,23 @@
</template> </template>
<script setup> <script setup>
import { DropdownIcon, PlusIcon, TrashIcon, LogInIcon, SpinnerIcon } from '@modrinth/assets' import { trackEvent } from '@/helpers/analytics'
import { Avatar, Button, Card } from '@modrinth/ui'
import { ref, computed, onMounted, onBeforeUnmount, onUnmounted } from 'vue'
import { import {
users, get_default_user,
login as login_flow,
remove_user, remove_user,
set_default_user, set_default_user,
login as login_flow, users,
get_default_user,
} from '@/helpers/auth' } from '@/helpers/auth'
import { handleError } from '@/store/state.js'
import { trackEvent } from '@/helpers/analytics'
import { process_listener } from '@/helpers/events' import { process_listener } from '@/helpers/events'
import { handleSevereError } from '@/store/error.js'
import { get_available_skins } from '@/helpers/skins'
import { getPlayerHeadUrl } from '@/helpers/rendering/batch-skin-renderer.ts' import { getPlayerHeadUrl } from '@/helpers/rendering/batch-skin-renderer.ts'
import { get_available_skins } from '@/helpers/skins'
import { handleSevereError } from '@/store/error.js'
import { DropdownIcon, LogInIcon, PlusIcon, SpinnerIcon, TrashIcon } from '@modrinth/assets'
import { Avatar, Button, Card, injectNotificationManager } from '@modrinth/ui'
import { computed, onBeforeUnmount, onMounted, onUnmounted, ref } from 'vue'
const { handleError } = injectNotificationManager()
defineProps({ defineProps({
mode: { mode: {

View File

@ -1,11 +1,13 @@
<script setup lang="ts"> <script setup lang="ts">
import { DropdownIcon, PlusIcon, FolderOpenIcon } from '@modrinth/assets'
import { ButtonStyled, OverflowMenu } from '@modrinth/ui'
import { open } from '@tauri-apps/plugin-dialog'
import { add_project_from_path } from '@/helpers/profile.js' import { add_project_from_path } from '@/helpers/profile.js'
import { handleError } from '@/store/notifications.js' import type { AppNotificationManager } from '@/providers/app-notifications'
import { DropdownIcon, FolderOpenIcon, PlusIcon } from '@modrinth/assets'
import { ButtonStyled, injectNotificationManager, OverflowMenu } from '@modrinth/ui'
import { open } from '@tauri-apps/plugin-dialog'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
const { handleError } = injectNotificationManager() as AppNotificationManager
const props = defineProps({ const props = defineProps({
instance: { instance: {
type: Object, type: Object,

View File

@ -1,23 +1,24 @@
<script setup> <script setup>
import { ChatIcon } from '@/assets/icons'
import ModalWrapper from '@/components/ui/modal/ModalWrapper.vue'
import { trackEvent } from '@/helpers/analytics'
import { login as login_flow, set_default_user } from '@/helpers/auth.js'
import { install } from '@/helpers/profile.js'
import { cancel_directory_change } from '@/helpers/settings.ts'
import { handleSevereError } from '@/store/error.js'
import { import {
CheckIcon, CheckIcon,
CopyIcon,
DropdownIcon, DropdownIcon,
XIcon,
HammerIcon, HammerIcon,
LogInIcon, LogInIcon,
UpdatedIcon, UpdatedIcon,
CopyIcon, XIcon,
} from '@modrinth/assets' } from '@modrinth/assets'
import { ChatIcon } from '@/assets/icons' import { ButtonStyled, Collapsible, injectNotificationManager } from '@modrinth/ui'
import { ButtonStyled, Collapsible } from '@modrinth/ui' import { computed, ref } from 'vue'
import { ref, computed } from 'vue'
import { login as login_flow, set_default_user } from '@/helpers/auth.js' const { handleError } = injectNotificationManager()
import { handleError } from '@/store/notifications.js'
import { handleSevereError } from '@/store/error.js'
import { cancel_directory_change } from '@/helpers/settings.ts'
import { install } from '@/helpers/profile.js'
import { trackEvent } from '@/helpers/analytics'
import ModalWrapper from '@/components/ui/modal/ModalWrapper.vue'
const errorModal = ref() const errorModal = ref()
const error = ref() const error = ref()

View File

@ -1,12 +1,13 @@
<script setup> <script setup>
import { XIcon, PlusIcon } from '@modrinth/assets'
import { Button, Checkbox } from '@modrinth/ui'
import { PackageIcon, VersionIcon } from '@/assets/icons' import { PackageIcon, VersionIcon } from '@/assets/icons'
import { ref } from 'vue'
import { export_profile_mrpack, get_pack_export_candidates } from '@/helpers/profile.js'
import { open } from '@tauri-apps/plugin-dialog'
import { handleError } from '@/store/notifications.js'
import ModalWrapper from '@/components/ui/modal/ModalWrapper.vue' import ModalWrapper from '@/components/ui/modal/ModalWrapper.vue'
import { export_profile_mrpack, get_pack_export_candidates } from '@/helpers/profile.js'
import { PlusIcon, XIcon } from '@modrinth/assets'
import { Button, Checkbox, injectNotificationManager } from '@modrinth/ui'
import { open } from '@tauri-apps/plugin-dialog'
import { ref } from 'vue'
const { handleError } = injectNotificationManager()
const props = defineProps({ const props = defineProps({
instance: { instance: {

View File

@ -1,6 +1,10 @@
<script setup> <script setup>
import { computed, onMounted, onUnmounted, ref } from 'vue' import { trackEvent } from '@/helpers/analytics'
import { useRouter } from 'vue-router' import { process_listener } from '@/helpers/events'
import { get_by_profile_path } from '@/helpers/process'
import { finish_install, kill, run } from '@/helpers/profile'
import { showProfileInFolder } from '@/helpers/utils.js'
import { handleSevereError } from '@/store/error.js'
import { import {
DownloadIcon, DownloadIcon,
GameIcon, GameIcon,
@ -9,17 +13,13 @@ import {
StopCircleIcon, StopCircleIcon,
TimerIcon, TimerIcon,
} from '@modrinth/assets' } from '@modrinth/assets'
import { Avatar, ButtonStyled, useRelativeTime } from '@modrinth/ui' import { Avatar, ButtonStyled, injectNotificationManager, useRelativeTime } from '@modrinth/ui'
import { convertFileSrc } from '@tauri-apps/api/core' import { convertFileSrc } from '@tauri-apps/api/core'
import { finish_install, kill, run } from '@/helpers/profile'
import { get_by_profile_path } from '@/helpers/process'
import { process_listener } from '@/helpers/events'
import { handleError } from '@/store/state.js'
import { showProfileInFolder } from '@/helpers/utils.js'
import { handleSevereError } from '@/store/error.js'
import { trackEvent } from '@/helpers/analytics'
import dayjs from 'dayjs' import dayjs from 'dayjs'
import { computed, onMounted, onUnmounted, ref } from 'vue'
import { useRouter } from 'vue-router'
const { handleError } = injectNotificationManager()
const formatRelativeTime = useRelativeTime() const formatRelativeTime = useRelativeTime()
const props = defineProps({ const props = defineProps({

View File

@ -198,6 +198,17 @@
<script setup> <script setup>
import ModalWrapper from '@/components/ui/modal/ModalWrapper.vue' import ModalWrapper from '@/components/ui/modal/ModalWrapper.vue'
import ProgressBar from '@/components/ui/ProgressBar.vue'
import { trackEvent } from '@/helpers/analytics'
import {
get_default_launcher_path,
get_importable_instances,
import_instance,
} from '@/helpers/import.js'
import { get_game_versions, get_loader_versions } from '@/helpers/metadata'
import { create_profile_and_install_from_file } from '@/helpers/pack.js'
import { create } from '@/helpers/profile'
import { get_loaders } from '@/helpers/tags'
import { import {
CodeIcon, CodeIcon,
FolderOpenIcon, FolderOpenIcon,
@ -208,24 +219,14 @@ import {
UploadIcon, UploadIcon,
XIcon, XIcon,
} from '@modrinth/assets' } from '@modrinth/assets'
import { Avatar, Button, Checkbox, Chips } from '@modrinth/ui' import { Avatar, Button, Checkbox, Chips, injectNotificationManager } from '@modrinth/ui'
import { computed, onUnmounted, ref, shallowRef } from 'vue'
import { get_loaders } from '@/helpers/tags'
import { create } from '@/helpers/profile'
import { open } from '@tauri-apps/plugin-dialog'
import { convertFileSrc } from '@tauri-apps/api/core' import { convertFileSrc } from '@tauri-apps/api/core'
import { get_game_versions, get_loader_versions } from '@/helpers/metadata'
import { handleError } from '@/store/notifications.js'
import Multiselect from 'vue-multiselect'
import { trackEvent } from '@/helpers/analytics'
import { create_profile_and_install_from_file } from '@/helpers/pack.js'
import {
get_default_launcher_path,
get_importable_instances,
import_instance,
} from '@/helpers/import.js'
import ProgressBar from '@/components/ui/ProgressBar.vue'
import { getCurrentWebview } from '@tauri-apps/api/webview' import { getCurrentWebview } from '@tauri-apps/api/webview'
import { open } from '@tauri-apps/plugin-dialog'
import { computed, onUnmounted, ref, shallowRef } from 'vue'
import Multiselect from 'vue-multiselect'
const { handleError } = injectNotificationManager()
const profile_name = ref('') const profile_name = ref('')
const game_version = ref('') const game_version = ref('')

View File

@ -35,13 +35,14 @@
</ModalWrapper> </ModalWrapper>
</template> </template>
<script setup> <script setup>
import { PlusIcon, CheckIcon, XIcon } from '@modrinth/assets'
import { Button } from '@modrinth/ui'
import { ref } from 'vue'
import { find_filtered_jres } from '@/helpers/jre.js'
import { handleError } from '@/store/notifications.js'
import { trackEvent } from '@/helpers/analytics'
import ModalWrapper from '@/components/ui/modal/ModalWrapper.vue' import ModalWrapper from '@/components/ui/modal/ModalWrapper.vue'
import { trackEvent } from '@/helpers/analytics'
import { find_filtered_jres } from '@/helpers/jre.js'
import { CheckIcon, PlusIcon, XIcon } from '@modrinth/assets'
import { Button, injectNotificationManager } from '@modrinth/ui'
import { ref } from 'vue'
const { handleError } = injectNotificationManager()
const chosenInstallOptions = ref([]) const chosenInstallOptions = ref([])
const detectJavaModal = ref(null) const detectJavaModal = ref(null)

View File

@ -52,21 +52,22 @@
</template> </template>
<script setup> <script setup>
import {
SearchIcon,
PlayIcon,
CheckIcon,
XIcon,
FolderSearchIcon,
DownloadIcon,
} from '@modrinth/assets'
import { Button } from '@modrinth/ui'
import { auto_install_java, find_filtered_jres, get_jre, test_jre } from '@/helpers/jre.js'
import { ref } from 'vue'
import { open } from '@tauri-apps/plugin-dialog'
import JavaDetectionModal from '@/components/ui/JavaDetectionModal.vue' import JavaDetectionModal from '@/components/ui/JavaDetectionModal.vue'
import { handleError } from '@/store/state.js'
import { trackEvent } from '@/helpers/analytics' import { trackEvent } from '@/helpers/analytics'
import { auto_install_java, find_filtered_jres, get_jre, test_jre } from '@/helpers/jre.js'
import {
CheckIcon,
DownloadIcon,
FolderSearchIcon,
PlayIcon,
SearchIcon,
XIcon,
} from '@modrinth/assets'
import { Button, injectNotificationManager } from '@modrinth/ui'
import { open } from '@tauri-apps/plugin-dialog'
import { ref } from 'vue'
const { handleError } = injectNotificationManager()
const props = defineProps({ const props = defineProps({
version: { version: {

View File

@ -1,13 +1,14 @@
<script setup> <script setup>
import NavButton from '@/components/ui/NavButton.vue'
import { profile_listener } from '@/helpers/events.js'
import { list } from '@/helpers/profile' import { list } from '@/helpers/profile'
import { handleError } from '@/store/notifications' import { SpinnerIcon } from '@modrinth/assets'
import { Avatar, injectNotificationManager } from '@modrinth/ui'
import { convertFileSrc } from '@tauri-apps/api/core'
import dayjs from 'dayjs' import dayjs from 'dayjs'
import { onUnmounted, ref } from 'vue' import { onUnmounted, ref } from 'vue'
import { profile_listener } from '@/helpers/events.js'
import NavButton from '@/components/ui/NavButton.vue' const { handleError } = injectNotificationManager()
import { Avatar } from '@modrinth/ui'
import { convertFileSrc } from '@tauri-apps/api/core'
import { SpinnerIcon } from '@modrinth/assets'
const recentInstances = ref([]) const recentInstances = ref([])
const getInstances = async () => { const getInstances = async () => {

View File

@ -94,23 +94,24 @@
</template> </template>
<script setup> <script setup>
import ProgressBar from '@/components/ui/ProgressBar.vue'
import { trackEvent } from '@/helpers/analytics'
import { loading_listener, process_listener } from '@/helpers/events'
import { get_all as getRunningProcesses, kill as killProcess } from '@/helpers/process'
import { get_many } from '@/helpers/profile.js'
import { progress_bars_list } from '@/helpers/state.js'
import { import {
DownloadIcon, DownloadIcon,
DropdownIcon,
StopCircleIcon, StopCircleIcon,
TerminalSquareIcon, TerminalSquareIcon,
DropdownIcon,
UnplugIcon, UnplugIcon,
} from '@modrinth/assets' } from '@modrinth/assets'
import { Button, ButtonStyled, Card } from '@modrinth/ui' import { Button, ButtonStyled, Card, injectNotificationManager } from '@modrinth/ui'
import { onBeforeUnmount, onMounted, ref } from 'vue' import { onBeforeUnmount, onMounted, ref } from 'vue'
import { get_all as getRunningProcesses, kill as killProcess } from '@/helpers/process'
import { loading_listener, process_listener } from '@/helpers/events'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import { progress_bars_list } from '@/helpers/state.js'
import ProgressBar from '@/components/ui/ProgressBar.vue' const { handleError } = injectNotificationManager()
import { handleError } from '@/store/notifications.js'
import { get_many } from '@/helpers/profile.js'
import { trackEvent } from '@/helpers/analytics'
const router = useRouter() const router = useRouter()
const card = ref(null) const card = ref(null)

View File

@ -1,12 +1,13 @@
<script setup> <script setup>
import { Button } from '@modrinth/ui'
import { ref } from 'vue'
import SearchCard from '@/components/ui/SearchCard.vue'
import { get_categories } from '@/helpers/tags.js'
import { handleError } from '@/store/notifications.js'
import { get_version, get_project } from '@/helpers/cache.js'
import { install as installVersion } from '@/store/install.js'
import ModalWrapper from '@/components/ui/modal/ModalWrapper.vue' import ModalWrapper from '@/components/ui/modal/ModalWrapper.vue'
import SearchCard from '@/components/ui/SearchCard.vue'
import { get_project, get_version } from '@/helpers/cache.js'
import { get_categories } from '@/helpers/tags.js'
import { install as installVersion } from '@/store/install.js'
import { Button, injectNotificationManager } from '@modrinth/ui'
import { ref } from 'vue'
const { handleError } = injectNotificationManager()
const confirmModal = ref(null) const confirmModal = ref(null)
const project = ref(null) const project = ref(null)

View File

@ -1,23 +1,30 @@
<script setup lang="ts"> <script setup lang="ts">
import { Avatar, ButtonStyled, OverflowMenu, useRelativeTime } from '@modrinth/ui' import ContextMenu from '@/components/ui/ContextMenu.vue'
import ModalWrapper from '@/components/ui/modal/ModalWrapper.vue'
import { get_user_many } from '@/helpers/cache'
import { friend_listener } from '@/helpers/events'
import { add_friend, friend_statuses, friends, remove_friend } from '@/helpers/friends'
import type { AppNotificationManager } from '@/providers/app-notifications'
import { import {
UserPlusIcon,
MoreVerticalIcon,
MailIcon, MailIcon,
MoreVerticalIcon,
SettingsIcon, SettingsIcon,
TrashIcon, TrashIcon,
UserPlusIcon,
XIcon, XIcon,
} from '@modrinth/assets' } from '@modrinth/assets'
import { ref, onUnmounted, watch, computed } from 'vue' import {
import { friend_listener } from '@/helpers/events' Avatar,
import { friends, friend_statuses, add_friend, remove_friend } from '@/helpers/friends' ButtonStyled,
import { get_user_many } from '@/helpers/cache' injectNotificationManager,
import { handleError } from '@/store/notifications.js' OverflowMenu,
import ContextMenu from '@/components/ui/ContextMenu.vue' useRelativeTime,
} from '@modrinth/ui'
import type { Dayjs } from 'dayjs' import type { Dayjs } from 'dayjs'
import dayjs from 'dayjs' import dayjs from 'dayjs'
import ModalWrapper from '@/components/ui/modal/ModalWrapper.vue' import { computed, onUnmounted, ref, watch } from 'vue'
const { handleError } = injectNotificationManager() as AppNotificationManager
const formatRelativeTime = useRelativeTime() const formatRelativeTime = useRelativeTime()
const props = defineProps<{ const props = defineProps<{

View File

@ -57,15 +57,16 @@
<script setup> <script setup>
import ModalWrapper from '@/components/ui/modal/ModalWrapper.vue' import ModalWrapper from '@/components/ui/modal/ModalWrapper.vue'
import { XIcon, DownloadIcon } from '@modrinth/assets'
import { Button } from '@modrinth/ui'
import { formatCategory } from '@modrinth/utils'
import { add_project_from_version as installMod } from '@/helpers/profile'
import { ref } from 'vue'
import { handleError } from '@/store/state.js'
import { trackEvent } from '@/helpers/analytics' import { trackEvent } from '@/helpers/analytics'
import { add_project_from_version as installMod } from '@/helpers/profile'
import { DownloadIcon, XIcon } from '@modrinth/assets'
import { Button, injectNotificationManager } from '@modrinth/ui'
import { formatCategory } from '@modrinth/utils'
import { ref } from 'vue'
import Multiselect from 'vue-multiselect' import Multiselect from 'vue-multiselect'
const { handleError } = injectNotificationManager()
const instance = ref(null) const instance = ref(null)
const project = ref(null) const project = ref(null)
const versions = ref(null) const versions = ref(null)

View File

@ -1,11 +1,12 @@
<script setup> <script setup>
import { DownloadIcon, XIcon } from '@modrinth/assets'
import { Button } from '@modrinth/ui'
import { create_profile_and_install as pack_install } from '@/helpers/pack'
import { ref } from 'vue'
import { trackEvent } from '@/helpers/analytics'
import { handleError } from '@/store/state.js'
import ModalWrapper from '@/components/ui/modal/ModalWrapper.vue' import ModalWrapper from '@/components/ui/modal/ModalWrapper.vue'
import { trackEvent } from '@/helpers/analytics'
import { create_profile_and_install as pack_install } from '@/helpers/pack'
import { DownloadIcon, XIcon } from '@modrinth/assets'
import { Button, injectNotificationManager } from '@modrinth/ui'
import { ref } from 'vue'
const { handleError } = injectNotificationManager()
const versionId = ref() const versionId = ref()
const project = ref() const project = ref()

View File

@ -1,29 +1,29 @@
<script setup> <script setup>
import ModalWrapper from '@/components/ui/modal/ModalWrapper.vue'
import { trackEvent } from '@/helpers/analytics'
import { import {
check_installed,
create,
get,
add_project_from_version as installMod,
list,
} from '@/helpers/profile'
import { installVersionDependencies } from '@/store/install.js'
import {
CheckIcon,
DownloadIcon, DownloadIcon,
PlusIcon, PlusIcon,
RightArrowIcon,
UploadIcon, UploadIcon,
XIcon, XIcon,
RightArrowIcon,
CheckIcon,
} from '@modrinth/assets' } from '@modrinth/assets'
import { Avatar, Button, Card } from '@modrinth/ui' import { Avatar, Button, Card, injectNotificationManager } from '@modrinth/ui'
import { computed, ref } from 'vue'
import {
add_project_from_version as installMod,
check_installed,
get,
list,
create,
} from '@/helpers/profile'
import { open } from '@tauri-apps/plugin-dialog'
import { installVersionDependencies } from '@/store/install.js'
import { handleError } from '@/store/notifications.js'
import { useRouter } from 'vue-router'
import { convertFileSrc } from '@tauri-apps/api/core' import { convertFileSrc } from '@tauri-apps/api/core'
import { trackEvent } from '@/helpers/analytics' import { open } from '@tauri-apps/plugin-dialog'
import ModalWrapper from '@/components/ui/modal/ModalWrapper.vue' import { computed, ref } from 'vue'
import { useRouter } from 'vue-router'
const { handleError } = injectNotificationManager()
const router = useRouter() const router = useRouter()
const versions = ref() const versions = ref()

View File

@ -1,17 +1,24 @@
<script setup lang="ts"> <script setup lang="ts">
import { convertFileSrc } from '@tauri-apps/api/core' import ConfirmModalWrapper from '@/components/ui/modal/ConfirmModalWrapper.vue'
import { SpinnerIcon, TrashIcon, UploadIcon, PlusIcon, EditIcon, CopyIcon } from '@modrinth/assets'
import { Avatar, ButtonStyled, OverflowMenu, Checkbox } from '@modrinth/ui'
import { computed, ref, type Ref, watch } from 'vue'
import { duplicate, edit, edit_icon, list, remove } from '@/helpers/profile'
import { handleError } from '@/store/notifications'
import { trackEvent } from '@/helpers/analytics' import { trackEvent } from '@/helpers/analytics'
import { duplicate, edit, edit_icon, list, remove } from '@/helpers/profile'
import type { AppNotificationManager } from '@/providers/app-notifications'
import { CopyIcon, EditIcon, PlusIcon, SpinnerIcon, TrashIcon, UploadIcon } from '@modrinth/assets'
import {
Avatar,
ButtonStyled,
Checkbox,
injectNotificationManager,
OverflowMenu,
} from '@modrinth/ui'
import { convertFileSrc } from '@tauri-apps/api/core'
import { open } from '@tauri-apps/plugin-dialog' import { open } from '@tauri-apps/plugin-dialog'
import { defineMessages, useVIntl } from '@vintl/vintl' import { defineMessages, useVIntl } from '@vintl/vintl'
import { computed, ref, type Ref, watch } from 'vue'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import ConfirmModalWrapper from '@/components/ui/modal/ConfirmModalWrapper.vue' import type { GameInstance, InstanceSettingsTabProps } from '../../../helpers/types'
import type { InstanceSettingsTabProps, GameInstance } from '../../../helpers/types'
const { handleError } = injectNotificationManager() as AppNotificationManager
const { formatMessage } = useVIntl() const { formatMessage } = useVIntl()
const router = useRouter() const router = useRouter()

View File

@ -1,12 +1,13 @@
<script setup lang="ts"> <script setup lang="ts">
import { Checkbox } from '@modrinth/ui'
import { computed, ref, watch } from 'vue'
import { handleError } from '@/store/notifications'
import { defineMessages, useVIntl } from '@vintl/vintl'
import { get } from '@/helpers/settings.ts'
import { edit } from '@/helpers/profile' import { edit } from '@/helpers/profile'
import type { InstanceSettingsTabProps, AppSettings, Hooks } from '../../../helpers/types' import { get } from '@/helpers/settings.ts'
import type { AppNotificationManager } from '@/providers/app-notifications'
import { Checkbox, injectNotificationManager } from '@modrinth/ui'
import { defineMessages, useVIntl } from '@vintl/vintl'
import { computed, ref, watch } from 'vue'
import type { AppSettings, Hooks, InstanceSettingsTabProps } from '../../../helpers/types'
const { handleError } = injectNotificationManager() as AppNotificationManager
const { formatMessage } = useVIntl() const { formatMessage } = useVIntl()
const props = defineProps<InstanceSettingsTabProps>() const props = defineProps<InstanceSettingsTabProps>()

View File

@ -1,23 +1,31 @@
<script setup lang="ts"> <script setup lang="ts">
import { import ConfirmModalWrapper from '@/components/ui/modal/ConfirmModalWrapper.vue'
TransferIcon, import ModpackVersionModal from '@/components/ui/ModpackVersionModal.vue'
IssuesIcon,
HammerIcon,
DownloadIcon,
WrenchIcon,
UndoIcon,
SpinnerIcon,
UnplugIcon,
UnlinkIcon,
} from '@modrinth/assets'
import { Avatar, Checkbox, Chips, ButtonStyled, TeleportDropdownMenu } from '@modrinth/ui'
import { computed, type ComputedRef, type Ref, ref, shallowRef, watch } from 'vue'
import { edit, install, update_repair_modrinth } from '@/helpers/profile'
import { handleError } from '@/store/notifications'
import { trackEvent } from '@/helpers/analytics' import { trackEvent } from '@/helpers/analytics'
import { defineMessages, useVIntl } from '@vintl/vintl' import { get_project, get_version_many } from '@/helpers/cache'
import { get_loader_versions } from '@/helpers/metadata' import { get_loader_versions } from '@/helpers/metadata'
import { edit, install, update_repair_modrinth } from '@/helpers/profile'
import { get_game_versions, get_loaders } from '@/helpers/tags' import { get_game_versions, get_loaders } from '@/helpers/tags'
import type { AppNotificationManager } from '@/providers/app-notifications'
import {
DownloadIcon,
HammerIcon,
IssuesIcon,
SpinnerIcon,
TransferIcon,
UndoIcon,
UnlinkIcon,
UnplugIcon,
WrenchIcon,
} from '@modrinth/assets'
import {
Avatar,
ButtonStyled,
Checkbox,
Chips,
TeleportDropdownMenu,
injectNotificationManager,
} from '@modrinth/ui'
import { import {
formatCategory, formatCategory,
type GameVersionTag, type GameVersionTag,
@ -25,16 +33,16 @@ import {
type Project, type Project,
type Version, type Version,
} from '@modrinth/utils' } from '@modrinth/utils'
import ConfirmModalWrapper from '@/components/ui/modal/ConfirmModalWrapper.vue' import { defineMessages, useVIntl } from '@vintl/vintl'
import { get_project, get_version_many } from '@/helpers/cache'
import ModpackVersionModal from '@/components/ui/ModpackVersionModal.vue'
import dayjs from 'dayjs' import dayjs from 'dayjs'
import { computed, ref, shallowRef, watch, type ComputedRef, type Ref } from 'vue'
import type { import type {
InstanceSettingsTabProps, InstanceSettingsTabProps,
ManifestLoaderVersion,
Manifest, Manifest,
ManifestLoaderVersion,
} from '../../../helpers/types' } from '../../../helpers/types'
const { handleError } = injectNotificationManager() as AppNotificationManager
const { formatMessage } = useVIntl() const { formatMessage } = useVIntl()
const repairConfirmModal = ref() const repairConfirmModal = ref()

View File

@ -1,15 +1,16 @@
<script setup lang="ts"> <script setup lang="ts">
import { Checkbox, Slider } from '@modrinth/ui'
import { CheckCircleIcon, XCircleIcon } from '@modrinth/assets'
import { computed, readonly, ref, watch } from 'vue'
import { edit, get_optimal_jre_key } from '@/helpers/profile'
import { handleError } from '@/store/notifications'
import { defineMessages, useVIntl } from '@vintl/vintl'
import JavaSelector from '@/components/ui/JavaSelector.vue' import JavaSelector from '@/components/ui/JavaSelector.vue'
import { get } from '@/helpers/settings.ts'
import type { InstanceSettingsTabProps, AppSettings, MemorySettings } from '../../../helpers/types'
import useMemorySlider from '@/composables/useMemorySlider' import useMemorySlider from '@/composables/useMemorySlider'
import { edit, get_optimal_jre_key } from '@/helpers/profile'
import { get } from '@/helpers/settings.ts'
import type { AppNotificationManager } from '@/providers/app-notifications'
import { CheckCircleIcon, XCircleIcon } from '@modrinth/assets'
import { Checkbox, injectNotificationManager, Slider } from '@modrinth/ui'
import { defineMessages, useVIntl } from '@vintl/vintl'
import { computed, readonly, ref, watch } from 'vue'
import type { AppSettings, InstanceSettingsTabProps, MemorySettings } from '../../../helpers/types'
const { handleError } = injectNotificationManager() as AppNotificationManager
const { formatMessage } = useVIntl() const { formatMessage } = useVIntl()
const props = defineProps<InstanceSettingsTabProps>() const props = defineProps<InstanceSettingsTabProps>()

View File

@ -1,12 +1,13 @@
<script setup lang="ts"> <script setup lang="ts">
import { Checkbox, Toggle } from '@modrinth/ui'
import { computed, ref, type Ref, watch } from 'vue'
import { handleError } from '@/store/notifications'
import { defineMessages, useVIntl } from '@vintl/vintl'
import { get } from '@/helpers/settings.ts'
import { edit } from '@/helpers/profile' import { edit } from '@/helpers/profile'
import { get } from '@/helpers/settings.ts'
import type { AppNotificationManager } from '@/providers/app-notifications'
import { Checkbox, injectNotificationManager, Toggle } from '@modrinth/ui'
import { defineMessages, useVIntl } from '@vintl/vintl'
import { computed, ref, type Ref, watch } from 'vue'
import type { AppSettings, InstanceSettingsTabProps } from '../../../helpers/types' import type { AppSettings, InstanceSettingsTabProps } from '../../../helpers/types'
const { handleError } = injectNotificationManager() as AppNotificationManager
const { formatMessage } = useVIntl() const { formatMessage } = useVIntl()
const props = defineProps<InstanceSettingsTabProps>() const props = defineProps<InstanceSettingsTabProps>()

View File

@ -1,8 +1,10 @@
<script setup> <script setup>
import { ref } from 'vue'
import { get_java_versions, set_java_version } from '@/helpers/jre'
import { handleError } from '@/store/notifications'
import JavaSelector from '@/components/ui/JavaSelector.vue' import JavaSelector from '@/components/ui/JavaSelector.vue'
import { get_java_versions, set_java_version } from '@/helpers/jre'
import { injectNotificationManager } from '@modrinth/ui'
import { ref } from 'vue'
const { handleError } = injectNotificationManager()
const javaVersions = ref(await get_java_versions().catch(handleError)) const javaVersions = ref(await get_java_versions().catch(handleError))
async function updateJavaVersion(version) { async function updateJavaVersion(version) {

View File

@ -1,13 +1,13 @@
<script setup> <script setup>
import { Button, Slider } from '@modrinth/ui'
import { ref, watch } from 'vue'
import { get, set } from '@/helpers/settings.ts'
import { purge_cache_types } from '@/helpers/cache.js'
import { handleError } from '@/store/notifications.js'
import { BoxIcon, FolderSearchIcon, TrashIcon } from '@modrinth/assets'
import ConfirmModalWrapper from '@/components/ui/modal/ConfirmModalWrapper.vue' import ConfirmModalWrapper from '@/components/ui/modal/ConfirmModalWrapper.vue'
import { purge_cache_types } from '@/helpers/cache.js'
import { get, set } from '@/helpers/settings.ts'
import { BoxIcon, FolderSearchIcon, TrashIcon } from '@modrinth/assets'
import { Button, Slider, injectNotificationManager } from '@modrinth/ui'
import { open } from '@tauri-apps/plugin-dialog' import { open } from '@tauri-apps/plugin-dialog'
import { ref, watch } from 'vue'
const { handleError } = injectNotificationManager()
const settings = ref(await get()) const settings = ref(await get())
watch( watch(

View File

@ -100,37 +100,40 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, computed, watch, useTemplateRef } from 'vue' import ModalWrapper from '@/components/ui/modal/ModalWrapper.vue'
import SelectCapeModal from '@/components/ui/skin/SelectCapeModal.vue' import SelectCapeModal from '@/components/ui/skin/SelectCapeModal.vue'
import { import UploadSkinModal from '@/components/ui/skin/UploadSkinModal.vue'
SkinPreviewRenderer,
Button,
RadioButtons,
CapeButton,
CapeLikeTextButton,
ButtonStyled,
} from '@modrinth/ui'
import { import {
add_and_equip_custom_skin, add_and_equip_custom_skin,
determineModelType,
get_normalized_skin_texture,
remove_custom_skin, remove_custom_skin,
unequip_skin, unequip_skin,
type Skin,
type Cape, type Cape,
type Skin,
type SkinModel, type SkinModel,
get_normalized_skin_texture,
determineModelType,
} from '@/helpers/skins.ts' } from '@/helpers/skins.ts'
import { handleError } from '@/store/notifications' import type { AppNotificationManager } from '@/providers/app-notifications'
import { import {
UploadIcon,
CheckIcon, CheckIcon,
SaveIcon,
XIcon,
ChevronRightIcon, ChevronRightIcon,
SaveIcon,
SpinnerIcon, SpinnerIcon,
UploadIcon,
XIcon,
} from '@modrinth/assets' } from '@modrinth/assets'
import ModalWrapper from '@/components/ui/modal/ModalWrapper.vue' import {
import UploadSkinModal from '@/components/ui/skin/UploadSkinModal.vue' Button,
ButtonStyled,
CapeButton,
CapeLikeTextButton,
injectNotificationManager,
RadioButtons,
SkinPreviewRenderer,
} from '@modrinth/ui'
import { computed, ref, useTemplateRef, watch } from 'vue'
const { handleError } = injectNotificationManager() as AppNotificationManager
const modal = useTemplateRef('modal') const modal = useTemplateRef('modal')
const selectCapeModal = useTemplateRef('selectCapeModal') const selectCapeModal = useTemplateRef('selectCapeModal')

View File

@ -27,14 +27,14 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, onBeforeUnmount, watch } from 'vue'
import { UploadIcon } from '@modrinth/assets'
import { useNotifications } from '@/store/state'
import { getCurrentWebview } from '@tauri-apps/api/webview'
import ModalWrapper from '@/components/ui/modal/ModalWrapper.vue' import ModalWrapper from '@/components/ui/modal/ModalWrapper.vue'
import { get_dragged_skin_data } from '@/helpers/skins' import { get_dragged_skin_data } from '@/helpers/skins'
import { UploadIcon } from '@modrinth/assets'
import { injectNotificationManager } from '@modrinth/ui'
import { getCurrentWebview } from '@tauri-apps/api/webview'
import { onBeforeUnmount, ref, watch } from 'vue'
const notifications = useNotifications() const { addNotification } = injectNotificationManager()
const modal = ref() const modal = ref()
const fileInput = ref<HTMLInputElement>() const fileInput = ref<HTMLInputElement>()
@ -99,7 +99,7 @@ async function setupDragDropListener() {
const data = await get_dragged_skin_data(filePath) const data = await get_dragged_skin_data(filePath)
await processData(data.buffer) await processData(data.buffer)
} catch (error) { } catch (error) {
notifications.addNotification({ addNotification({
title: 'Error processing file', title: 'Error processing file',
text: error instanceof Error ? error.message : 'Failed to read the dropped file.', text: error instanceof Error ? error.message : 'Failed to read the dropped file.',
type: 'error', type: 'error',

View File

@ -1,6 +1,13 @@
<script setup lang="ts"> <script setup lang="ts">
import type { Dayjs } from 'dayjs' import { trackEvent } from '@/helpers/analytics'
import dayjs from 'dayjs' import { get_project } from '@/helpers/cache'
import { process_listener } from '@/helpers/events'
import { get_by_profile_path } from '@/helpers/process'
import { kill, run } from '@/helpers/profile'
import type { GameInstance } from '@/helpers/types'
import { showProfileInFolder } from '@/helpers/utils'
import type { AppNotificationManager } from '@/providers/app-notifications'
import { handleSevereError } from '@/store/error'
import { import {
EyeIcon, EyeIcon,
FolderOpenIcon, FolderOpenIcon,
@ -13,25 +20,20 @@ import {
Avatar, Avatar,
ButtonStyled, ButtonStyled,
commonMessages, commonMessages,
injectNotificationManager,
OverflowMenu, OverflowMenu,
SmartClickable, SmartClickable,
useRelativeTime, useRelativeTime,
} from '@modrinth/ui' } from '@modrinth/ui'
import { useVIntl } from '@vintl/vintl'
import { computed, nextTick, ref, onMounted, onUnmounted } from 'vue'
import { showProfileInFolder } from '@/helpers/utils'
import { convertFileSrc } from '@tauri-apps/api/core'
import { useRouter } from 'vue-router'
import type { GameInstance } from '@/helpers/types'
import { get_project } from '@/helpers/cache'
import { capitalizeString } from '@modrinth/utils' import { capitalizeString } from '@modrinth/utils'
import { kill, run } from '@/helpers/profile' import { convertFileSrc } from '@tauri-apps/api/core'
import { handleSevereError } from '@/store/error' import { useVIntl } from '@vintl/vintl'
import { trackEvent } from '@/helpers/analytics' import type { Dayjs } from 'dayjs'
import { get_by_profile_path } from '@/helpers/process' import dayjs from 'dayjs'
import { handleError } from '@/store/notifications' import { computed, nextTick, onMounted, onUnmounted, ref } from 'vue'
import { process_listener } from '@/helpers/events' import { useRouter } from 'vue-router'
const { handleError } = injectNotificationManager() as AppNotificationManager
const { formatMessage } = useVIntl() const { formatMessage } = useVIntl()
const formatRelativeTime = useRelativeTime() const formatRelativeTime = useRelativeTime()

View File

@ -1,30 +1,32 @@
<script setup lang="ts"> <script setup lang="ts">
import InstanceItem from '@/components/ui/world/InstanceItem.vue'
import WorldItem from '@/components/ui/world/WorldItem.vue'
import { trackEvent } from '@/helpers/analytics'
import { process_listener, profile_listener } from '@/helpers/events'
import { get_all } from '@/helpers/process'
import { kill, run } from '@/helpers/profile'
import type { GameInstance } from '@/helpers/types'
import { import {
type ProtocolVersion, type ProtocolVersion,
type ServerWorld,
type ServerData, type ServerData,
type ServerWorld,
type WorldWithProfile, type WorldWithProfile,
get_recent_worlds,
getWorldIdentifier, getWorldIdentifier,
get_profile_protocol_version, get_profile_protocol_version,
get_recent_worlds,
refreshServerData, refreshServerData,
start_join_server, start_join_server,
start_join_singleplayer_world, start_join_singleplayer_world,
} from '@/helpers/worlds.ts' } from '@/helpers/worlds.ts'
import { HeadingLink, GAME_MODES } from '@modrinth/ui' import type { AppNotificationManager } from '@/providers/app-notifications'
import WorldItem from '@/components/ui/world/WorldItem.vue' import { handleSevereError } from '@/store/error'
import InstanceItem from '@/components/ui/world/InstanceItem.vue' import { useTheming } from '@/store/theme.ts'
import { watch, onMounted, onUnmounted, ref, computed } from 'vue' import { GAME_MODES, HeadingLink, injectNotificationManager } from '@modrinth/ui'
import type { Dayjs } from 'dayjs' import type { Dayjs } from 'dayjs'
import dayjs from 'dayjs' import dayjs from 'dayjs'
import { useTheming } from '@/store/theme.ts' import { computed, onMounted, onUnmounted, ref, watch } from 'vue'
import { kill, run } from '@/helpers/profile'
import { handleError } from '@/store/notifications' const { handleError } = injectNotificationManager() as AppNotificationManager
import { trackEvent } from '@/helpers/analytics'
import { process_listener, profile_listener } from '@/helpers/events'
import { get_all } from '@/helpers/process'
import type { GameInstance } from '@/helpers/types'
import { handleSevereError } from '@/store/error'
const props = defineProps<{ const props = defineProps<{
recentInstances: GameInstance[] recentInstances: GameInstance[]

View File

@ -1,15 +1,16 @@
<script setup lang="ts"> <script setup lang="ts">
import { PlayIcon, PlusIcon, XIcon } from '@modrinth/assets'
import { ButtonStyled, commonMessages } from '@modrinth/ui'
import { ref } from 'vue'
import ModalWrapper from '@/components/ui/modal/ModalWrapper.vue'
import type { GameInstance } from '@/helpers/types'
import InstanceModalTitlePrefix from '@/components/ui/modal/InstanceModalTitlePrefix.vue' import InstanceModalTitlePrefix from '@/components/ui/modal/InstanceModalTitlePrefix.vue'
import { add_server_to_profile, type ServerPackStatus, type ServerWorld } from '@/helpers/worlds.ts' import ModalWrapper from '@/components/ui/modal/ModalWrapper.vue'
import { defineMessages, useVIntl } from '@vintl/vintl'
import { handleError } from '@/store/notifications'
import ServerModalBody from '@/components/ui/world/modal/ServerModalBody.vue' import ServerModalBody from '@/components/ui/world/modal/ServerModalBody.vue'
import type { GameInstance } from '@/helpers/types'
import { add_server_to_profile, type ServerPackStatus, type ServerWorld } from '@/helpers/worlds.ts'
import type { AppNotificationManager } from '@/providers/app-notifications'
import { PlayIcon, PlusIcon, XIcon } from '@modrinth/assets'
import { ButtonStyled, commonMessages, injectNotificationManager } from '@modrinth/ui'
import { defineMessages, useVIntl } from '@vintl/vintl'
import { ref } from 'vue'
const { handleError } = injectNotificationManager() as AppNotificationManager
const { formatMessage } = useVIntl() const { formatMessage } = useVIntl()
const emit = defineEmits<{ const emit = defineEmits<{

View File

@ -1,21 +1,22 @@
<script setup lang="ts"> <script setup lang="ts">
import { SaveIcon, XIcon } from '@modrinth/assets'
import { ButtonStyled, commonMessages } from '@modrinth/ui'
import { computed, ref } from 'vue'
import ModalWrapper from '@/components/ui/modal/ModalWrapper.vue' import ModalWrapper from '@/components/ui/modal/ModalWrapper.vue'
import HideFromHomeOption from '@/components/ui/world/modal/HideFromHomeOption.vue'
import ServerModalBody from '@/components/ui/world/modal/ServerModalBody.vue'
import type { GameInstance } from '@/helpers/types' import type { GameInstance } from '@/helpers/types'
import { import {
type ServerPackStatus,
edit_server_in_profile, edit_server_in_profile,
type ServerWorld,
set_world_display_status, set_world_display_status,
type DisplayStatus, type DisplayStatus,
type ServerPackStatus,
type ServerWorld,
} from '@/helpers/worlds.ts' } from '@/helpers/worlds.ts'
import type { AppNotificationManager } from '@/providers/app-notifications'
import { SaveIcon, XIcon } from '@modrinth/assets'
import { ButtonStyled, commonMessages, injectNotificationManager } from '@modrinth/ui'
import { defineMessage, useVIntl } from '@vintl/vintl' import { defineMessage, useVIntl } from '@vintl/vintl'
import { handleError } from '@/store/notifications' import { computed, ref } from 'vue'
import ServerModalBody from '@/components/ui/world/modal/ServerModalBody.vue'
import HideFromHomeOption from '@/components/ui/world/modal/HideFromHomeOption.vue'
const { handleError } = injectNotificationManager() as AppNotificationManager
const { formatMessage } = useVIntl() const { formatMessage } = useVIntl()
const emit = defineEmits<{ const emit = defineEmits<{

View File

@ -1,15 +1,16 @@
<script setup lang="ts"> <script setup lang="ts">
import { ChevronRightIcon, SaveIcon, XIcon, UndoIcon } from '@modrinth/assets'
import { Avatar, ButtonStyled, commonMessages } from '@modrinth/ui'
import { computed, ref } from 'vue'
import ModalWrapper from '@/components/ui/modal/ModalWrapper.vue' import ModalWrapper from '@/components/ui/modal/ModalWrapper.vue'
import HideFromHomeOption from '@/components/ui/world/modal/HideFromHomeOption.vue'
import type { GameInstance } from '@/helpers/types' import type { GameInstance } from '@/helpers/types'
import type { DisplayStatus, SingleplayerWorld } from '@/helpers/worlds.ts' import type { DisplayStatus, SingleplayerWorld } from '@/helpers/worlds.ts'
import { set_world_display_status, rename_world, reset_world_icon } from '@/helpers/worlds.ts' import { rename_world, reset_world_icon, set_world_display_status } from '@/helpers/worlds.ts'
import type { AppNotificationManager } from '@/providers/app-notifications'
import { ChevronRightIcon, SaveIcon, UndoIcon, XIcon } from '@modrinth/assets'
import { Avatar, ButtonStyled, commonMessages, injectNotificationManager } from '@modrinth/ui'
import { defineMessages, useVIntl } from '@vintl/vintl' import { defineMessages, useVIntl } from '@vintl/vintl'
import { handleError } from '@/store/notifications' import { computed, ref } from 'vue'
import HideFromHomeOption from '@/components/ui/world/modal/HideFromHomeOption.vue'
const { handleError } = injectNotificationManager() as AppNotificationManager
const { formatMessage } = useVIntl() const { formatMessage } = useVIntl()
const emit = defineEmits<{ const emit = defineEmits<{

View File

@ -1,8 +1,9 @@
import { ref, computed } from 'vue'
import { get_max_memory } from '@/helpers/jre.js' import { get_max_memory } from '@/helpers/jre.js'
import { handleError } from '@/store/notifications.js' import { injectNotificationManager } from '@modrinth/ui'
import { computed, ref } from 'vue'
export default async function () { export default async function () {
const { handleError } = injectNotificationManager()
const maxMemory = ref(Math.floor((await get_max_memory().catch(handleError)) / 1024)) const maxMemory = ref(Math.floor((await get_max_memory().catch(handleError)) / 1024))
const snapPoints = computed(() => { const snapPoints = computed(() => {

View File

@ -1,6 +1,6 @@
import { fetch } from '@tauri-apps/plugin-http' import { injectNotificationManager } from '@modrinth/ui'
import { handleError } from '@/store/state.js'
import { getVersion } from '@tauri-apps/api/app' import { getVersion } from '@tauri-apps/api/app'
import { fetch } from '@tauri-apps/plugin-http'
export const useFetch = async (url, item, isSilent) => { export const useFetch = async (url, item, isSilent) => {
try { try {
@ -11,6 +11,7 @@ export const useFetch = async (url, item, isSilent) => {
}) })
} catch (err) { } catch (err) {
if (!isSilent) { if (!isSilent) {
const { handleError } = injectNotificationManager()
handleError({ message: `Error fetching ${item}` }) handleError({ message: `Error fetching ${item}` })
} }
console.error(err) console.error(err)

View File

@ -3,9 +3,9 @@
* So, for example, addDefaultInstance creates a blank Profile object, where the Rust struct is serialized, * So, for example, addDefaultInstance creates a blank Profile object, where the Rust struct is serialized,
* and deserialized into a usable JS object. * and deserialized into a usable JS object.
*/ */
import { invoke } from '@tauri-apps/api/core'
import { install_to_existing_profile } from '@/helpers/pack.js' import { install_to_existing_profile } from '@/helpers/pack.js'
import { handleError } from '@/store/notifications.js' import { injectNotificationManager } from '@modrinth/ui'
import { invoke } from '@tauri-apps/api/core'
/// Add instance /// Add instance
/* /*
@ -190,6 +190,7 @@ export async function edit_icon(path, iconPath) {
} }
export async function finish_install(instance) { export async function finish_install(instance) {
const { handleError } = injectNotificationManager()
if (instance.install_stage !== 'pack_installed') { if (instance.install_stage !== 'pack_installed') {
let linkedData = instance.linked_data let linkedData = instance.linked_data
await install_to_existing_profile( await install_to_existing_profile(

View File

@ -1,6 +1,7 @@
import { invoke } from '@tauri-apps/api/core' import type { AppNotificationManager } from '@/providers/app-notifications'
import { handleError } from '@/store/notifications' import { injectNotificationManager } from '@modrinth/ui'
import { arrayBufferToBase64 } from '@modrinth/utils' import { arrayBufferToBase64 } from '@modrinth/utils'
import { invoke } from '@tauri-apps/api/core'
export interface Cape { export interface Cape {
id: string id: string
@ -39,6 +40,7 @@ export const DEFAULT_MODELS: Record<string, SkinModel> = {
export function filterSavedSkins(list: Skin[]) { export function filterSavedSkins(list: Skin[]) {
const customSkins = list.filter((s) => s.source !== 'default') const customSkins = list.filter((s) => s.source !== 'default')
const { handleError } = injectNotificationManager() as AppNotificationManager
fixUnknownSkins(customSkins).catch(handleError) fixUnknownSkins(customSkins).catch(handleError)
return customSkins return customSkins
} }

View File

@ -1,12 +1,12 @@
import { createApp } from 'vue'
import router from '@/routes'
import App from '@/App.vue' import App from '@/App.vue'
import { createPinia } from 'pinia' import router from '@/routes'
import FloatingVue from 'floating-vue'
import 'floating-vue/dist/style.css'
import { createPlugin } from '@vintl/vintl/plugin'
import * as Sentry from '@sentry/vue' import * as Sentry from '@sentry/vue'
import { VueScanPlugin } from '@taijased/vue-render-tracker' import { VueScanPlugin } from '@taijased/vue-render-tracker'
import { createPlugin } from '@vintl/vintl/plugin'
import FloatingVue from 'floating-vue'
import 'floating-vue/dist/style.css'
import { createPinia } from 'pinia'
import { createApp } from 'vue'
const VIntlPlugin = createPlugin({ const VIntlPlugin = createPlugin({
controllerOpts: { controllerOpts: {

View File

@ -1,33 +1,35 @@
<script setup lang="ts"> <script setup lang="ts">
import { computed, nextTick, ref, shallowRef, watch } from 'vue' import ContextMenu from '@/components/ui/ContextMenu.vue'
import type { Ref } from 'vue' import type Instance from '@/components/ui/Instance.vue'
import { SearchIcon, XIcon, ClipboardCopyIcon, GlobeIcon, ExternalIcon } from '@modrinth/assets' import InstanceIndicator from '@/components/ui/InstanceIndicator.vue'
import NavTabs from '@/components/ui/NavTabs.vue'
import SearchCard from '@/components/ui/SearchCard.vue'
import { get_search_results } from '@/helpers/cache.js'
import { get as getInstance, get_projects as getInstanceProjects } from '@/helpers/profile.js'
import { get_categories, get_game_versions, get_loaders } from '@/helpers/tags'
import type { AppNotificationManager } from '@/providers/app-notifications'
import { useBreadcrumbs } from '@/store/breadcrumbs'
import { ClipboardCopyIcon, ExternalIcon, GlobeIcon, SearchIcon, XIcon } from '@modrinth/assets'
import type { Category, GameVersion, Platform, ProjectType, SortType, Tags } from '@modrinth/ui' import type { Category, GameVersion, Platform, ProjectType, SortType, Tags } from '@modrinth/ui'
import { import {
SearchFilterControl,
SearchSidebarFilter,
Button, Button,
Checkbox, Checkbox,
DropdownSelect, DropdownSelect,
injectNotificationManager,
LoadingIndicator, LoadingIndicator,
Pagination, Pagination,
SearchFilterControl,
SearchSidebarFilter,
useSearch, useSearch,
} from '@modrinth/ui' } from '@modrinth/ui'
import { handleError } from '@/store/state' import { openUrl } from '@tauri-apps/plugin-opener'
import { useBreadcrumbs } from '@/store/breadcrumbs' import { defineMessages, useVIntl } from '@vintl/vintl'
import { get_categories, get_game_versions, get_loaders } from '@/helpers/tags' import type { Ref } from 'vue'
import { computed, nextTick, ref, shallowRef, watch } from 'vue'
import type { LocationQuery } from 'vue-router' import type { LocationQuery } from 'vue-router'
import { useRoute, useRouter } from 'vue-router' import { useRoute, useRouter } from 'vue-router'
import SearchCard from '@/components/ui/SearchCard.vue'
import { get as getInstance, get_projects as getInstanceProjects } from '@/helpers/profile.js'
import { get_search_results } from '@/helpers/cache.js'
import NavTabs from '@/components/ui/NavTabs.vue'
import type Instance from '@/components/ui/Instance.vue'
import InstanceIndicator from '@/components/ui/InstanceIndicator.vue'
import { defineMessages, useVIntl } from '@vintl/vintl'
import ContextMenu from '@/components/ui/ContextMenu.vue'
import { openUrl } from '@tauri-apps/plugin-opener'
const { handleError } = injectNotificationManager() as AppNotificationManager
const { formatMessage } = useVIntl() const { formatMessage } = useVIntl()
const router = useRouter() const router = useRouter()

View File

@ -1,17 +1,19 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, onUnmounted, computed } from 'vue'
import { useRoute } from 'vue-router'
import RowDisplay from '@/components/RowDisplay.vue' import RowDisplay from '@/components/RowDisplay.vue'
import { list } from '@/helpers/profile.js'
import { profile_listener } from '@/helpers/events'
import { useBreadcrumbs } from '@/store/breadcrumbs'
import { handleError } from '@/store/notifications.js'
import dayjs from 'dayjs'
import { get_search_results } from '@/helpers/cache.js'
import type { SearchResult } from '@modrinth/utils'
import RecentWorldsList from '@/components/ui/world/RecentWorldsList.vue' import RecentWorldsList from '@/components/ui/world/RecentWorldsList.vue'
import { get_search_results } from '@/helpers/cache.js'
import { profile_listener } from '@/helpers/events'
import { list } from '@/helpers/profile.js'
import type { GameInstance } from '@/helpers/types' import type { GameInstance } from '@/helpers/types'
import type { AppNotificationManager } from '@/providers/app-notifications'
import { useBreadcrumbs } from '@/store/breadcrumbs'
import { injectNotificationManager } from '@modrinth/ui'
import type { SearchResult } from '@modrinth/utils'
import dayjs from 'dayjs'
import { computed, onUnmounted, ref } from 'vue'
import { useRoute } from 'vue-router'
const { handleError } = injectNotificationManager() as AppNotificationManager
const route = useRoute() const route = useRoute()
const breadcrumbs = useBreadcrumbs() const breadcrumbs = useBreadcrumbs()

View File

@ -1,4 +1,27 @@
<script setup lang="ts"> <script setup lang="ts">
import type AccountsCard from '@/components/ui/AccountsCard.vue'
import EditSkinModal from '@/components/ui/skin/EditSkinModal.vue'
import SelectCapeModal from '@/components/ui/skin/SelectCapeModal.vue'
import UploadSkinModal from '@/components/ui/skin/UploadSkinModal.vue'
import { trackEvent } from '@/helpers/analytics'
import { get_default_user, login as login_flow, users } from '@/helpers/auth'
import type { RenderResult } from '@/helpers/rendering/batch-skin-renderer.ts'
import { generateSkinPreviews, skinBlobUrlMap } from '@/helpers/rendering/batch-skin-renderer.ts'
import { get as getSettings } from '@/helpers/settings.ts'
import type { Cape, Skin } from '@/helpers/skins.ts'
import {
equip_skin,
filterDefaultSkins,
filterSavedSkins,
get_available_capes,
get_available_skins,
get_normalized_skin_texture,
normalize_skin_texture,
remove_custom_skin,
set_default_cape,
} from '@/helpers/skins.ts'
import type { AppNotificationManager } from '@/providers/app-notifications'
import { handleSevereError } from '@/store/error'
import { import {
EditIcon, EditIcon,
ExcitedRinthbot, ExcitedRinthbot,
@ -12,42 +35,21 @@ import {
Button, Button,
ButtonStyled, ButtonStyled,
ConfirmModal, ConfirmModal,
injectNotificationManager,
SkinButton, SkinButton,
SkinLikeTextButton, SkinLikeTextButton,
SkinPreviewRenderer, SkinPreviewRenderer,
} from '@modrinth/ui' } from '@modrinth/ui'
import { arrayBufferToBase64 } from '@modrinth/utils'
import { computedAsync } from '@vueuse/core' import { computedAsync } from '@vueuse/core'
import type { Ref } from 'vue' import type { Ref } from 'vue'
import { computed, inject, onMounted, onUnmounted, ref, useTemplateRef, watch } from 'vue' import { computed, inject, onMounted, onUnmounted, ref, useTemplateRef, watch } from 'vue'
import EditSkinModal from '@/components/ui/skin/EditSkinModal.vue'
import SelectCapeModal from '@/components/ui/skin/SelectCapeModal.vue'
import UploadSkinModal from '@/components/ui/skin/UploadSkinModal.vue'
import { handleError, useNotifications } from '@/store/notifications'
import type { Cape, Skin } from '@/helpers/skins.ts'
import {
normalize_skin_texture,
equip_skin,
filterDefaultSkins,
filterSavedSkins,
get_available_capes,
get_available_skins,
get_normalized_skin_texture,
remove_custom_skin,
set_default_cape,
} from '@/helpers/skins.ts'
import { get as getSettings } from '@/helpers/settings.ts'
import { get_default_user, login as login_flow, users } from '@/helpers/auth'
import type { RenderResult } from '@/helpers/rendering/batch-skin-renderer.ts'
import { generateSkinPreviews, skinBlobUrlMap } from '@/helpers/rendering/batch-skin-renderer.ts'
import { handleSevereError } from '@/store/error'
import { trackEvent } from '@/helpers/analytics'
import type AccountsCard from '@/components/ui/AccountsCard.vue'
import { arrayBufferToBase64 } from '@modrinth/utils'
const editSkinModal = useTemplateRef('editSkinModal') const editSkinModal = useTemplateRef('editSkinModal')
const selectCapeModal = useTemplateRef('selectCapeModal') const selectCapeModal = useTemplateRef('selectCapeModal')
const uploadSkinModal = useTemplateRef('uploadSkinModal') const uploadSkinModal = useTemplateRef('uploadSkinModal')
const notifications = useNotifications() const notifications = injectNotificationManager() as AppNotificationManager
const { handleError } = notifications
const settings = ref(await getSettings()) const settings = ref(await getSettings())
const skins = ref<Skin[]>([]) const skins = ref<Skin[]>([])
@ -113,7 +115,7 @@ async function loadCapes() {
defaultCape.value = capes.value.find((c) => c.is_equipped) defaultCape.value = capes.value.find((c) => c.is_equipped)
originalDefaultCape.value = defaultCape.value originalDefaultCape.value = defaultCape.value
} catch (error) { } catch (error) {
if (currentUser.value) { if (currentUser.value && error instanceof Error) {
handleError(error) handleError(error)
} }
} }
@ -126,7 +128,7 @@ async function loadSkins() {
selectedSkin.value = skins.value.find((s) => s.is_equipped) ?? null selectedSkin.value = skins.value.find((s) => s.is_equipped) ?? null
originalSelectedSkin.value = selectedSkin.value originalSelectedSkin.value = selectedSkin.value
} catch (error) { } catch (error) {
if (currentUser.value) { if (currentUser.value && error instanceof Error) {
handleError(error) handleError(error)
} }
} }
@ -161,7 +163,7 @@ async function changeSkin(newSkin: Skin) {
text: "You're changing your skin too frequently. Mojang's servers have temporarily blocked further requests. Please wait a moment before trying again.", text: "You're changing your skin too frequently. Mojang's servers have temporarily blocked further requests. Please wait a moment before trying again.",
}) })
} else { } else {
handleError(error) handleError(error as Error)
} }
} }
} }
@ -190,7 +192,7 @@ async function handleCapeSelected(cape: Cape | undefined) {
text: "You're changing your cape too frequently. Mojang's servers have temporarily blocked further requests. Please wait a moment before trying again.", text: "You're changing your cape too frequently. Mojang's servers have temporarily blocked further requests. Please wait a moment before trying again.",
}) })
} else { } else {
handleError(error) handleError(error as Error)
} }
} }
} }
@ -207,7 +209,7 @@ async function loadCurrentUser() {
const allAccounts = await users() const allAccounts = await users()
currentUser.value = allAccounts.find((acc) => acc.profile.id === defaultId) currentUser.value = allAccounts.find((acc) => acc.profile.id === defaultId)
} catch (e) { } catch (e) {
handleError(e) handleError(e as Error)
currentUser.value = undefined currentUser.value = undefined
currentUserId.value = undefined currentUserId.value = undefined
} }
@ -276,7 +278,7 @@ async function checkUserChanges() {
await loadSkins() await loadSkins()
} }
} catch (error) { } catch (error) {
if (currentUser.value) { if (currentUser.value && error instanceof Error) {
handleError(error) handleError(error)
} }
} }
@ -376,7 +378,7 @@ await Promise.all([loadCapes(), loadSkins(), loadCurrentUser()])
color="green" color="green"
aria-label="Edit skin" aria-label="Edit skin"
class="pointer-events-auto" class="pointer-events-auto"
@click.stop="(e) => editSkinModal?.show(e, skin)" @click.stop="(e: MouseEvent) => editSkinModal?.show(e, skin)"
> >
<EditIcon /> Edit <EditIcon /> Edit
</Button> </Button>

View File

@ -157,13 +157,18 @@
</div> </div>
</template> </template>
<script setup> <script setup>
import { import ContextMenu from '@/components/ui/ContextMenu.vue'
Avatar, import ExportModal from '@/components/ui/ExportModal.vue'
ButtonStyled, import InstanceSettingsModal from '@/components/ui/modal/InstanceSettingsModal.vue'
ContentPageHeader, import NavTabs from '@/components/ui/NavTabs.vue'
LoadingIndicator, import { trackEvent } from '@/helpers/analytics'
OverflowMenu, import { get_project, get_version_many } from '@/helpers/cache.js'
} from '@modrinth/ui' import { process_listener, profile_listener } from '@/helpers/events'
import { get_by_profile_path } from '@/helpers/process'
import { finish_install, get, get_full_path, kill, run } from '@/helpers/profile'
import { showProfileInFolder } from '@/helpers/utils.js'
import { handleSevereError } from '@/store/error.js'
import { useBreadcrumbs, useLoading } from '@/store/state'
import { import {
CheckCircleIcon, CheckCircleIcon,
ClipboardCopyIcon, ClipboardCopyIcon,
@ -187,28 +192,25 @@ import {
UserPlusIcon, UserPlusIcon,
XIcon, XIcon,
} from '@modrinth/assets' } from '@modrinth/assets'
import { finish_install, get, get_full_path, kill, run } from '@/helpers/profile' import {
import { get_by_profile_path } from '@/helpers/process' Avatar,
import { process_listener, profile_listener } from '@/helpers/events' ButtonStyled,
import { useRoute, useRouter } from 'vue-router' ContentPageHeader,
import { computed, onUnmounted, ref, watch } from 'vue' injectNotificationManager,
import { handleError, useBreadcrumbs, useLoading } from '@/store/state' LoadingIndicator,
import { showProfileInFolder } from '@/helpers/utils.js' OverflowMenu,
import ContextMenu from '@/components/ui/ContextMenu.vue' } from '@modrinth/ui'
import NavTabs from '@/components/ui/NavTabs.vue'
import { trackEvent } from '@/helpers/analytics'
import { convertFileSrc } from '@tauri-apps/api/core' import { convertFileSrc } from '@tauri-apps/api/core'
import { handleSevereError } from '@/store/error.js'
import { get_project, get_version_many } from '@/helpers/cache.js'
import dayjs from 'dayjs' import dayjs from 'dayjs'
import duration from 'dayjs/plugin/duration' import duration from 'dayjs/plugin/duration'
import relativeTime from 'dayjs/plugin/relativeTime' import relativeTime from 'dayjs/plugin/relativeTime'
import ExportModal from '@/components/ui/ExportModal.vue' import { computed, onUnmounted, ref, watch } from 'vue'
import InstanceSettingsModal from '@/components/ui/modal/InstanceSettingsModal.vue' import { useRoute, useRouter } from 'vue-router'
dayjs.extend(duration) dayjs.extend(duration)
dayjs.extend(relativeTime) dayjs.extend(relativeTime)
const { handleError } = injectNotificationManager()
const route = useRoute() const route = useRoute()
const router = useRouter() const router = useRouter()

View File

@ -88,30 +88,30 @@
</template> </template>
<script setup> <script setup>
import { CheckIcon, ClipboardCopyIcon, ShareIcon, TrashIcon } from '@modrinth/assets' import ShareModalWrapper from '@/components/ui/modal/ShareModalWrapper.vue'
import { Button, Card, Checkbox, DropdownSelect } from '@modrinth/ui' import { process_listener } from '@/helpers/events.js'
import { import {
delete_logs_by_filename, delete_logs_by_filename,
get_latest_log_cursor,
get_logs, get_logs,
get_output_by_filename, get_output_by_filename,
get_latest_log_cursor,
} from '@/helpers/logs.js' } from '@/helpers/logs.js'
import { computed, nextTick, onBeforeUnmount, onMounted, onUnmounted, ref, watch } from 'vue' import { get_by_profile_path } from '@/helpers/process.js'
import { CheckIcon, ClipboardCopyIcon, ShareIcon, TrashIcon } from '@modrinth/assets'
import { Button, Card, Checkbox, DropdownSelect, injectNotificationManager } from '@modrinth/ui'
import dayjs from 'dayjs' import dayjs from 'dayjs'
import isToday from 'dayjs/plugin/isToday' import isToday from 'dayjs/plugin/isToday'
import isYesterday from 'dayjs/plugin/isYesterday' import isYesterday from 'dayjs/plugin/isYesterday'
import { get_by_profile_path } from '@/helpers/process.js'
import { useRoute } from 'vue-router'
import { process_listener } from '@/helpers/events.js'
import { handleError } from '@/store/notifications.js'
import { ofetch } from 'ofetch' import { ofetch } from 'ofetch'
import { computed, nextTick, onBeforeUnmount, onMounted, onUnmounted, ref, watch } from 'vue'
import { useRoute } from 'vue-router'
import { RecycleScroller } from 'vue-virtual-scroller' import { RecycleScroller } from 'vue-virtual-scroller'
import 'vue-virtual-scroller/dist/vue-virtual-scroller.css' import 'vue-virtual-scroller/dist/vue-virtual-scroller.css'
import ShareModalWrapper from '@/components/ui/modal/ShareModalWrapper.vue'
dayjs.extend(isToday) dayjs.extend(isToday)
dayjs.extend(isYesterday) dayjs.extend(isYesterday)
const { handleError } = injectNotificationManager()
const route = useRoute() const route = useRoute()
const props = defineProps({ const props = defineProps({

View File

@ -249,6 +249,31 @@
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { TextInputIcon } from '@/assets/icons'
import AddContentButton from '@/components/ui/AddContentButton.vue'
import type ContextMenu from '@/components/ui/ContextMenu.vue'
import ExportModal from '@/components/ui/ExportModal.vue'
import ShareModalWrapper from '@/components/ui/modal/ShareModalWrapper.vue'
import ModpackVersionModal from '@/components/ui/ModpackVersionModal.vue'
import { trackEvent } from '@/helpers/analytics'
import {
get_organization_many,
get_project_many,
get_team_many,
get_version_many,
} from '@/helpers/cache.js'
import { profile_listener } from '@/helpers/events.js'
import {
add_project_from_path,
get_projects,
remove_project,
toggle_disable_project,
update_all,
update_project,
} from '@/helpers/profile.js'
import type { CacheBehaviour, ContentFile, GameInstance } from '@/helpers/types'
import { highlightModInProfile } from '@/helpers/utils.js'
import type { AppNotificationManager } from '@/providers/app-notifications'
import { import {
CheckCircleIcon, CheckCircleIcon,
ClipboardCopyIcon, ClipboardCopyIcon,
@ -271,44 +296,22 @@ import {
Button, Button,
ButtonStyled, ButtonStyled,
ContentListPanel, ContentListPanel,
injectNotificationManager,
OverflowMenu, OverflowMenu,
Pagination, Pagination,
RadialHeader, RadialHeader,
Toggle, Toggle,
} from '@modrinth/ui' } from '@modrinth/ui'
import type { ContentItem } from '@modrinth/ui/src/components/content/ContentListItem.vue'
import type { Organization, Project, TeamMember, Version } from '@modrinth/utils' import type { Organization, Project, TeamMember, Version } from '@modrinth/utils'
import { formatProjectType } from '@modrinth/utils' import { formatProjectType } from '@modrinth/utils'
import { getCurrentWebview } from '@tauri-apps/api/webview'
import { defineMessages, useVIntl } from '@vintl/vintl'
import dayjs from 'dayjs'
import type { ComputedRef } from 'vue' import type { ComputedRef } from 'vue'
import { computed, onUnmounted, ref, watch } from 'vue' import { computed, onUnmounted, ref, watch } from 'vue'
import { defineMessages, useVIntl } from '@vintl/vintl'
import { const { handleError } = injectNotificationManager() as AppNotificationManager
add_project_from_path,
get_projects,
remove_project,
toggle_disable_project,
update_all,
update_project,
} from '@/helpers/profile.js'
import { handleError } from '@/store/notifications.js'
import { trackEvent } from '@/helpers/analytics'
import { highlightModInProfile } from '@/helpers/utils.js'
import { TextInputIcon } from '@/assets/icons'
import ExportModal from '@/components/ui/ExportModal.vue'
import ModpackVersionModal from '@/components/ui/ModpackVersionModal.vue'
import AddContentButton from '@/components/ui/AddContentButton.vue'
import {
get_organization_many,
get_project_many,
get_team_many,
get_version_many,
} from '@/helpers/cache.js'
import { profile_listener } from '@/helpers/events.js'
import ShareModalWrapper from '@/components/ui/modal/ShareModalWrapper.vue'
import { getCurrentWebview } from '@tauri-apps/api/webview'
import dayjs from 'dayjs'
import type { CacheBehaviour, ContentFile, GameInstance } from '@/helpers/types'
import type ContextMenu from '@/components/ui/ContextMenu.vue'
import type { ContentItem } from '@modrinth/ui/src/components/content/ContentListItem.vue'
const props = defineProps<{ const props = defineProps<{
instance: GameInstance instance: GameInstance

View File

@ -120,54 +120,55 @@
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, computed, onUnmounted, watch } from 'vue' import type ContextMenu from '@/components/ui/ContextMenu.vue'
import { useRoute } from 'vue-router' import ConfirmModalWrapper from '@/components/ui/modal/ConfirmModalWrapper.vue'
import type { GameInstance } from '@/helpers/types'
import {
Button,
ButtonStyled,
RadialHeader,
FilterBar,
type FilterBarOption,
type GameVersion,
GAME_MODES,
} from '@modrinth/ui'
import { PlusIcon, SpinnerIcon, UpdatedIcon, SearchIcon, XIcon } from '@modrinth/assets'
import {
type ProtocolVersion,
type SingleplayerWorld,
type World,
type ServerWorld,
type ServerData,
type ProfileEvent,
get_profile_protocol_version,
remove_server_from_profile,
delete_world,
start_join_server,
start_join_singleplayer_world,
getWorldIdentifier,
refreshServerData,
refreshWorld,
sortWorlds,
refreshServers,
hasQuickPlaySupport,
refreshWorlds,
handleDefaultProfileUpdateEvent,
showWorldInFolder,
} from '@/helpers/worlds.ts'
import AddServerModal from '@/components/ui/world/modal/AddServerModal.vue' import AddServerModal from '@/components/ui/world/modal/AddServerModal.vue'
import EditServerModal from '@/components/ui/world/modal/EditServerModal.vue' import EditServerModal from '@/components/ui/world/modal/EditServerModal.vue'
import EditWorldModal from '@/components/ui/world/modal/EditSingleplayerWorldModal.vue' import EditWorldModal from '@/components/ui/world/modal/EditSingleplayerWorldModal.vue'
import WorldItem from '@/components/ui/world/WorldItem.vue' import WorldItem from '@/components/ui/world/WorldItem.vue'
import ConfirmModalWrapper from '@/components/ui/modal/ConfirmModalWrapper.vue'
import { handleError } from '@/store/notifications'
import type ContextMenu from '@/components/ui/ContextMenu.vue'
import type { Version } from '@modrinth/utils'
import { profile_listener } from '@/helpers/events' import { profile_listener } from '@/helpers/events'
import { get_game_versions } from '@/helpers/tags' import { get_game_versions } from '@/helpers/tags'
import type { GameInstance } from '@/helpers/types'
import {
type ProfileEvent,
type ProtocolVersion,
type ServerData,
type ServerWorld,
type SingleplayerWorld,
type World,
delete_world,
getWorldIdentifier,
get_profile_protocol_version,
handleDefaultProfileUpdateEvent,
hasQuickPlaySupport,
refreshServerData,
refreshServers,
refreshWorld,
refreshWorlds,
remove_server_from_profile,
showWorldInFolder,
sortWorlds,
start_join_server,
start_join_singleplayer_world,
} from '@/helpers/worlds.ts'
import type { AppNotificationManager } from '@/providers/app-notifications'
import { PlusIcon, SearchIcon, SpinnerIcon, UpdatedIcon, XIcon } from '@modrinth/assets'
import {
Button,
ButtonStyled,
FilterBar,
type FilterBarOption,
GAME_MODES,
type GameVersion,
RadialHeader,
injectNotificationManager,
} from '@modrinth/ui'
import type { Version } from '@modrinth/utils'
import { defineMessages } from '@vintl/vintl' import { defineMessages } from '@vintl/vintl'
import { computed, onUnmounted, ref, watch } from 'vue'
import { useRoute } from 'vue-router'
const { handleError } = injectNotificationManager() as AppNotificationManager
const route = useRoute() const route = useRoute()
const addServerModal = ref<InstanceType<typeof AddServerModal>>() const addServerModal = ref<InstanceType<typeof AddServerModal>>()

View File

@ -1,16 +1,16 @@
<script setup> <script setup>
import { onUnmounted, ref, shallowRef } from 'vue'
import { list } from '@/helpers/profile.js'
import { useRoute } from 'vue-router'
import { useBreadcrumbs } from '@/store/breadcrumbs.js'
import { profile_listener } from '@/helpers/events.js'
import { handleError } from '@/store/notifications.js'
import { Button } from '@modrinth/ui'
import { PlusIcon } from '@modrinth/assets'
import InstanceCreationModal from '@/components/ui/InstanceCreationModal.vue'
import { NewInstanceImage } from '@/assets/icons' import { NewInstanceImage } from '@/assets/icons'
import InstanceCreationModal from '@/components/ui/InstanceCreationModal.vue'
import NavTabs from '@/components/ui/NavTabs.vue' import NavTabs from '@/components/ui/NavTabs.vue'
import { profile_listener } from '@/helpers/events.js'
import { list } from '@/helpers/profile.js'
import { useBreadcrumbs } from '@/store/breadcrumbs.js'
import { PlusIcon } from '@modrinth/assets'
import { Button, injectNotificationManager } from '@modrinth/ui'
import { onUnmounted, ref, shallowRef } from 'vue'
import { useRoute } from 'vue-router'
const { handleError } = injectNotificationManager()
const route = useRoute() const route = useRoute()
const breadcrumbs = useBreadcrumbs() const breadcrumbs = useBreadcrumbs()

View File

@ -129,46 +129,46 @@
</template> </template>
<script setup> <script setup>
import ContextMenu from '@/components/ui/ContextMenu.vue'
import InstanceIndicator from '@/components/ui/InstanceIndicator.vue'
import NavTabs from '@/components/ui/NavTabs.vue'
import { get_project, get_team, get_version_many } from '@/helpers/cache.js'
import { get as getInstance, get_projects as getInstanceProjects } from '@/helpers/profile'
import { get_categories, get_game_versions, get_loaders } from '@/helpers/tags'
import { useBreadcrumbs } from '@/store/breadcrumbs'
import { install as installVersion } from '@/store/install.js'
import { useTheming } from '@/store/state.js'
import { import {
BookmarkIcon, BookmarkIcon,
MoreVerticalIcon,
DownloadIcon,
ReportIcon,
HeartIcon,
ExternalIcon,
CheckIcon, CheckIcon,
GlobeIcon,
ClipboardCopyIcon, ClipboardCopyIcon,
DownloadIcon,
ExternalIcon,
GlobeIcon,
HeartIcon,
MoreVerticalIcon,
ReportIcon,
} from '@modrinth/assets' } from '@modrinth/assets'
import { import {
ProjectHeader,
ProjectSidebarCompatibility,
ButtonStyled, ButtonStyled,
OverflowMenu, OverflowMenu,
ProjectSidebarLinks, ProjectBackgroundGradient,
ProjectHeader,
ProjectSidebarCompatibility,
ProjectSidebarCreators, ProjectSidebarCreators,
ProjectSidebarDetails, ProjectSidebarDetails,
ProjectBackgroundGradient, ProjectSidebarLinks,
injectNotificationManager,
} from '@modrinth/ui' } from '@modrinth/ui'
import { openUrl } from '@tauri-apps/plugin-opener'
import { get_categories, get_game_versions, get_loaders } from '@/helpers/tags'
import { get as getInstance, get_projects as getInstanceProjects } from '@/helpers/profile'
import dayjs from 'dayjs' import dayjs from 'dayjs'
import relativeTime from 'dayjs/plugin/relativeTime' import relativeTime from 'dayjs/plugin/relativeTime'
import { useRoute, useRouter } from 'vue-router'
import { ref, shallowRef, watch } from 'vue' import { ref, shallowRef, watch } from 'vue'
import { useBreadcrumbs } from '@/store/breadcrumbs' import { useRoute, useRouter } from 'vue-router'
import { handleError } from '@/store/notifications.js'
import ContextMenu from '@/components/ui/ContextMenu.vue'
import { install as installVersion } from '@/store/install.js'
import { get_project, get_team, get_version_many } from '@/helpers/cache.js'
import NavTabs from '@/components/ui/NavTabs.vue'
import { useTheming } from '@/store/state.js'
import InstanceIndicator from '@/components/ui/InstanceIndicator.vue'
import { openUrl } from '@tauri-apps/plugin-opener'
dayjs.extend(relativeTime) dayjs.extend(relativeTime)
const { handleError } = injectNotificationManager()
const route = useRoute() const route = useRoute()
const router = useRouter() const router = useRouter()
const breadcrumbs = useBreadcrumbs() const breadcrumbs = useBreadcrumbs()

View File

@ -65,12 +65,16 @@
</template> </template>
<script setup> <script setup>
import { ProjectPageVersions, ButtonStyled, OverflowMenu } from '@modrinth/ui'
import { CheckIcon, DownloadIcon, ExternalIcon, MoreVerticalIcon } from '@modrinth/assets'
import { ref } from 'vue'
import { SwapIcon } from '@/assets/icons/index.js' import { SwapIcon } from '@/assets/icons/index.js'
import { get_game_versions, get_loaders } from '@/helpers/tags.js' import { get_game_versions, get_loaders } from '@/helpers/tags.js'
import { handleError } from '@/store/notifications.js' import { CheckIcon, DownloadIcon, ExternalIcon, MoreVerticalIcon } from '@modrinth/assets'
import {
ButtonStyled,
OverflowMenu,
ProjectPageVersions,
injectNotificationManager,
} from '@modrinth/ui'
import { ref } from 'vue'
defineProps({ defineProps({
project: { project: {
@ -103,6 +107,8 @@ defineProps({
}, },
}) })
const { handleError } = injectNotificationManager()
const [loaders, gameVersions] = await Promise.all([ const [loaders, gameVersions] = await Promise.all([
get_loaders().catch(handleError).then(ref), get_loaders().catch(handleError).then(ref),
get_game_versions().catch(handleError).then(ref), get_game_versions().catch(handleError).then(ref),

View File

@ -0,0 +1,56 @@
import {
AbstractWebNotificationManager,
type WebNotification,
type WebNotificationLocation,
} from '@modrinth/ui'
import { ref, type Ref } from 'vue'
export class AppNotificationManager extends AbstractWebNotificationManager {
private readonly state: Ref<WebNotification[]>
private readonly locationState: Ref<WebNotificationLocation>
public constructor() {
super()
this.state = ref<WebNotification[]>([])
this.locationState = ref<WebNotificationLocation>('right')
}
public handleError = (error: Error): void => {
this.addNotification({
title: 'An error occurred',
text: error.message ?? error,
type: 'error',
})
}
public getNotificationLocation(): WebNotificationLocation {
return this.locationState.value
}
public setNotificationLocation(location: WebNotificationLocation): void {
this.locationState.value = location
}
public getNotifications(): WebNotification[] {
return this.state.value
}
protected addNotificationToStorage(notification: WebNotification): void {
this.state.value.push(notification)
}
protected removeNotificationFromStorage(id: string | number): void {
const index = this.state.value.findIndex((n) => n.id === id)
if (index > -1) {
this.state.value.splice(index, 1)
}
}
protected removeNotificationFromStorageByIndex(index: number): void {
this.state.value.splice(index, 1)
}
protected clearAllNotificationsFromStorage(): void {
this.state.value.splice(0)
}
}

View File

@ -1,4 +1,6 @@
import { defineStore } from 'pinia' import { trackEvent } from '@/helpers/analytics.js'
import { get_project, get_version_many } from '@/helpers/cache.js'
import { create_profile_and_install as packInstall } from '@/helpers/pack.js'
import { import {
add_project_from_version, add_project_from_version,
check_installed, check_installed,
@ -7,11 +9,9 @@ import {
list, list,
remove_project, remove_project,
} from '@/helpers/profile.js' } from '@/helpers/profile.js'
import { handleError } from '@/store/notifications.js' import { injectNotificationManager } from '@modrinth/ui'
import { get_project, get_version_many } from '@/helpers/cache.js'
import { create_profile_and_install as packInstall } from '@/helpers/pack.js'
import { trackEvent } from '@/helpers/analytics.js'
import dayjs from 'dayjs' import dayjs from 'dayjs'
import { defineStore } from 'pinia'
export const useInstall = defineStore('installStore', { export const useInstall = defineStore('installStore', {
state: () => ({ state: () => ({
@ -49,6 +49,7 @@ export const install = async (
callback = () => {}, callback = () => {},
createInstanceCallback = () => {}, createInstanceCallback = () => {},
) => { ) => {
const { handleError } = injectNotificationManager()
const project = await get_project(projectId, 'must_revalidate').catch(handleError) const project = await get_project(projectId, 'must_revalidate').catch(handleError)
if (project.project_type === 'modpack') { if (project.project_type === 'modpack') {
@ -160,6 +161,7 @@ export const install = async (
} }
export const installVersionDependencies = async (profile, version) => { export const installVersionDependencies = async (profile, version) => {
const { handleError } = injectNotificationManager()
for (const dep of version.dependencies) { for (const dep of version.dependencies) {
if (dep.dependency_type !== 'required') continue if (dep.dependency_type !== 'required') continue
// disallow fabric api install on quilt // disallow fabric api install on quilt

View File

@ -1,25 +0,0 @@
import { defineStore } from 'pinia'
export const useNotifications = defineStore('notificationsStore', {
state: () => ({
notificationsWrapper: null,
}),
actions: {
setNotifs(notifs) {
this.notificationsWrapper = notifs
},
addNotification(notif) {
this.notificationsWrapper.addNotification(notif)
},
},
})
export const handleError = (err) => {
const notifs = useNotifications()
notifs.addNotification({
title: 'An error occurred',
text: err.message ?? err,
type: 'error',
})
console.error(err)
}

View File

@ -1,7 +1,6 @@
import { useTheming } from './theme.ts'
import { useBreadcrumbs } from './breadcrumbs' import { useBreadcrumbs } from './breadcrumbs'
import { useLoading } from './loading'
import { useNotifications, handleError } from './notifications'
import { useInstall } from './install' import { useInstall } from './install'
import { useLoading } from './loading'
import { useTheming } from './theme.ts'
export { useTheming, useBreadcrumbs, useLoading, useNotifications, handleError, useInstall } export { useBreadcrumbs, useInstall, useLoading, useTheming }