feat: introduce dependency injection framework (#4091)

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

* fix: lint

* fix: issues

* fix: compile error + notif binding issue

* refactor: move org context to new DI setup

* feat: migrate app notifications to DI + frontend styling

* fix: sidebar issues

* fix: dont use delete in computed

* fix: import and prop issue

* refactor: move handleError to main notification manager class

* fix: lint & build

* fix: merge issues

* fix: lint issues

* fix: lint issues

---------

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

View File

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

View File

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

View File

@@ -249,6 +249,30 @@
</div>
</template>
<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 {
CheckCircleIcon,
ClipboardCopyIcon,
@@ -271,44 +295,22 @@ import {
Button,
ButtonStyled,
ContentListPanel,
injectNotificationManager,
OverflowMenu,
Pagination,
RadialHeader,
Toggle,
} from '@modrinth/ui'
import type { ContentItem } from '@modrinth/ui/src/components/content/ContentListItem.vue'
import type { Organization, Project, TeamMember, Version } 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 { computed, onUnmounted, ref, watch } from 'vue'
import { defineMessages, useVIntl } from '@vintl/vintl'
import {
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 { handleError } = injectNotificationManager()
const props = defineProps<{
instance: GameInstance

View File

@@ -121,55 +121,55 @@
</div>
</template>
<script setup lang="ts">
import { ref, computed, onUnmounted, watch } from 'vue'
import { useRoute } from 'vue-router'
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,
hasWorldQuickPlaySupport,
refreshWorlds,
handleDefaultProfileUpdateEvent,
showWorldInFolder,
hasServerQuickPlaySupport,
} from '@/helpers/worlds.ts'
import type ContextMenu from '@/components/ui/ContextMenu.vue'
import ConfirmModalWrapper from '@/components/ui/modal/ConfirmModalWrapper.vue'
import AddServerModal from '@/components/ui/world/modal/AddServerModal.vue'
import EditServerModal from '@/components/ui/world/modal/EditServerModal.vue'
import EditWorldModal from '@/components/ui/world/modal/EditSingleplayerWorldModal.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 { 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,
hasServerQuickPlaySupport,
hasWorldQuickPlaySupport,
refreshServerData,
refreshServers,
refreshWorld,
refreshWorlds,
remove_server_from_profile,
showWorldInFolder,
sortWorlds,
start_join_server,
start_join_singleplayer_world,
} from '@/helpers/worlds.ts'
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 { computed, onUnmounted, ref, watch } from 'vue'
import { useRoute } from 'vue-router'
const { handleError } = injectNotificationManager()
const route = useRoute()
const addServerModal = ref<InstanceType<typeof AddServerModal>>()
@@ -282,7 +282,7 @@ async function editServer(server: ServerWorld) {
await refreshServer(server.address)
}
} else {
handleError(`Error refreshing server, refreshing all worlds`)
handleError(new Error(`Error refreshing server, refreshing all worlds`))
await refreshAllWorlds()
}
}
@@ -301,7 +301,7 @@ async function editWorld(path: string, name: string, removeIcon: boolean) {
}
sortWorlds(worlds.value)
} else {
handleError(`Error finding world in list, refreshing all worlds`)
handleError(new Error(`Error finding world in list, refreshing all worlds`))
await refreshAllWorlds()
}
}
@@ -311,7 +311,7 @@ async function deleteWorld(world: SingleplayerWorld) {
worlds.value = worlds.value.filter((w) => w.type !== 'singleplayer' || w.path !== world.path)
}
function handleJoinError(err: unknown) {
function handleJoinError(err: Error) {
handleError(err)
startingInstance.value = false
worldPlaying.value = undefined
@@ -436,7 +436,7 @@ function promptToRemoveWorld(world: World): boolean {
async function proceedRemoveServer() {
if (!serverToRemove.value) {
handleError(`Error removing server, no server marked for removal.`)
handleError(new Error(`Error removing server, no server marked for removal.`))
return
}
await removeServer(serverToRemove.value)
@@ -445,7 +445,7 @@ async function proceedRemoveServer() {
async function proceedDeleteWorld() {
if (!worldToDelete.value) {
handleError(`Error deleting world, no world marked for removal.`)
handleError(new Error(`Error deleting world, no world marked for removal.`))
return
}
await deleteWorld(worldToDelete.value)