* fix: 7371

* fix: continued
This commit is contained in:
Calum H.
2026-08-31 23:12:24 +00:00
committed by GitHub
parent 2a2c21e23a
commit ee68c92201
11 changed files with 98 additions and 100 deletions
Generated
-1
View File
@@ -11172,7 +11172,6 @@ dependencies = [
"thiserror 2.0.17",
"tokio",
"tracing",
"tracing-error",
"url",
"urlencoding",
"uuid 1.23.3",
+2
View File
@@ -105,6 +105,7 @@ import { useCheckDisableMouseover } from '@/composables/macCssFix.js'
import { useAppEvent } from '@/composables/use-app-event'
import { useAppSettings } from '@/composables/use-app-settings.ts'
import { useError } from '@/composables/use-error.js'
import { useInstanceMetadataRefresh } from '@/composables/use-instance-metadata-refresh'
import { isDarkTheme, useTheme } from '@/composables/use-theme.ts'
import { config } from '@/config'
import { getAccountAppearance, rememberAccountAppearance } from '@/helpers/account-appearance.ts'
@@ -191,6 +192,7 @@ const appTheme = useTheme()
const router = useRouter()
const route = useRoute()
const { channel: appEventChannel, events: appEvents } = setupAppEventsProvider()
useInstanceMetadataRefresh(appEvents)
const breadcrumbManager = createBreadcrumbManager()
provideBreadcrumbManager(breadcrumbManager)
const canNavigateBack = ref(false)
@@ -8,7 +8,7 @@ import {
injectNotificationManager,
useVIntl,
} from '@modrinth/ui'
import { useQueryClient } from '@tanstack/vue-query'
import { useQuery } from '@tanstack/vue-query'
import dayjs from 'dayjs'
import { computed, onMounted, onUnmounted, ref } from 'vue'
import { useRouter } from 'vue-router'
@@ -17,17 +17,17 @@ import NavButton from '@/components/ui/NavButton.vue'
import { useAppEvent } from '@/composables/use-app-event'
import { handleSevereError } from '@/composables/use-error.js'
import { trackEvent } from '@/helpers/analytics'
import { getInstanceIconUrl, kill, list, run } from '@/helpers/instance'
import { getInstanceIconUrl, kill, run } from '@/helpers/instance'
import { get_all } from '@/helpers/process'
import { showInstanceInFolder } from '@/helpers/utils'
import { instanceKeys } from '@/pages/instance/query-options'
import { instanceListQueryOptions } from '@/pages/instance/query-options'
const ITEM_SIZE = 52
const APPROX_USED_VERTICAL_SPACE = 475 // doesn't need to be exact lol just close enough so there's a little gap and no overflow
const STORAGE_KEY = 'modrinth-quick-instance-count'
const { handleError } = injectNotificationManager()
const queryClient = useQueryClient()
const instancesQuery = useQuery(instanceListQueryOptions())
const router = useRouter()
const instanceOptions = ref()
const runningInstances = ref([])
@@ -35,7 +35,24 @@ const runningInstances = ref([])
const { formatMessage } = useVIntl()
const maxAuto = ref(0)
const allInstances = ref([])
const allInstances = computed(() =>
(instancesQuery.data.value ?? []).slice().sort((a, b) => {
const dateACreated = dayjs(a.created)
const dateAPlayed = a.last_played ? dayjs(a.last_played) : dayjs(0)
const dateBCreated = dayjs(b.created)
const dateBPlayed = b.last_played ? dayjs(b.last_played) : dayjs(0)
const dateA = dateACreated.isAfter(dateAPlayed) ? dateACreated : dateAPlayed
const dateB = dateBCreated.isAfter(dateBPlayed) ? dateBCreated : dateBPlayed
if (dateA.isSame(dateB)) {
return a.name.localeCompare(b.name)
}
return dateB - dateA
}),
)
const dragging = ref(false)
const stored = localStorage.getItem(STORAGE_KEY)
@@ -134,40 +151,9 @@ const onDividerPointerUp = (event) => {
endDrag(event)
}
const getInstances = async () => {
const instances = await list().catch(handleError)
for (const instance of instances) {
queryClient.setQueryData(instanceKeys.detail(instance.id), instance)
}
allInstances.value = instances.sort((a, b) => {
const dateACreated = dayjs(a.created)
const dateAPlayed = a.last_played ? dayjs(a.last_played) : dayjs(0)
const dateBCreated = dayjs(b.created)
const dateBPlayed = b.last_played ? dayjs(b.last_played) : dayjs(0)
const dateA = dateACreated.isAfter(dateAPlayed) ? dateACreated : dateAPlayed
const dateB = dateBCreated.isAfter(dateBPlayed) ? dateBCreated : dateBPlayed
if (dateA.isSame(dateB)) {
return a.name.localeCompare(b.name)
}
return dateB - dateA
})
}
await getInstances()
await instancesQuery.suspense().catch(handleError)
updateMaxAuto()
useAppEvent('instance', async (event) => {
if (event.event !== 'synced') {
await getInstances()
}
})
useAppEvent('process', checkProcesses)
onMounted(() => {
@@ -0,0 +1,60 @@
import { useQueryClient } from '@tanstack/vue-query'
import type { InstancePayload } from '@/generated/app-events/InstancePayload'
import { instanceKeys, instanceListQueryOptions } from '@/pages/instance/query-options'
import type { AppEvents } from '@/providers/app-events'
import { useAppEvent } from './use-app-event'
const INSTANCE_METADATA_EVENTS = new Set<InstancePayload['event']>([
'created',
'synced',
'edited',
'removed',
])
export function useInstanceMetadataRefresh(events: AppEvents) {
const queryClient = useQueryClient()
let refreshQueued = false
let refreshPromise: Promise<void> | undefined
function queueRefresh() {
refreshQueued = true
if (!refreshPromise) {
refreshPromise = Promise.resolve().then(async () => {
try {
do {
refreshQueued = false
const joinedExistingRequest =
queryClient.isFetching({ queryKey: instanceKeys.list(), exact: true }) > 0
const instances = await queryClient.fetchQuery({
...instanceListQueryOptions(),
staleTime: 0,
})
for (const instance of instances) {
queryClient.setQueryData(instanceKeys.detail(instance.id), instance)
}
if (joinedExistingRequest) {
refreshQueued = true
}
} while (refreshQueued)
} finally {
refreshPromise = undefined
}
})
}
return refreshPromise
}
useAppEvent(
'instance',
(event) => {
if (INSTANCE_METADATA_EVENTS.has(event.event)) return queueRefresh()
},
events,
)
useAppEvent('instance_groups_changed', queueRefresh, events)
}
+8 -28
View File
@@ -1,17 +1,15 @@
<script setup lang="ts">
import { PlayIcon, PlusIcon } from '@modrinth/assets'
import { ContextMenu, defineMessages, injectNotificationManager, useVIntl } from '@modrinth/ui'
import { useQuery } from '@tanstack/vue-query'
import dayjs from 'dayjs'
import { computed, inject, onActivated, ref } from 'vue'
import LibrarySection from '@/components/ui/library/index.vue'
import WelcomeScreen from '@/components/ui/WelcomeScreen.vue'
import RecentWorldsList from '@/components/ui/world/RecentWorldsList.vue'
import { useAppEvent } from '@/composables/use-app-event'
import { useAppSettings } from '@/composables/use-app-settings.ts'
import { toError } from '@/helpers/errors'
import { list } from '@/helpers/instance'
import type { GameInstance } from '@/helpers/types'
import { instanceListQueryOptions } from '@/pages/instance/query-options'
import { useRootBreadcrumb } from '@/providers/breadcrumbs'
import { injectOnboardingChecklist } from '@/providers/onboarding-checklist'
@@ -19,8 +17,8 @@ defineOptions({
name: 'LibraryPage',
})
const { handleError } = injectNotificationManager()
const { formatMessage } = useVIntl()
const { handleError } = injectNotificationManager()
const { hasCreatedInstance, isReady } = injectOnboardingChecklist()
const showCreationModal = inject<() => void>('showCreationModal')
const pageOptions = ref<InstanceType<typeof ContextMenu>>()
@@ -50,8 +48,11 @@ const homeBreadcrumb = useRootBreadcrumb({
})
onActivated(homeBreadcrumb.reset)
const instances = ref<GameInstance[]>([])
let latestInstanceFetch = 0
const instancesQuery = useQuery(instanceListQueryOptions())
const instances = computed(() => instancesQuery.data.value ?? [])
if (hasCreatedInstance.value) {
await instancesQuery.suspense().catch(handleError)
}
const recentInstances = computed(() =>
instances.value
@@ -59,27 +60,6 @@ const recentInstances = computed(() =>
.sort((a, b) => dayjs(b.last_played ?? b.created).diff(dayjs(a.last_played ?? a.created))),
)
async function fetchInstances() {
const fetchId = ++latestInstanceFetch
try {
const nextInstances = await list()
if (fetchId === latestInstanceFetch) {
instances.value = nextInstances
}
} catch (error: unknown) {
if (fetchId === latestInstanceFetch) {
handleError(toError(error))
}
}
}
if (hasCreatedInstance.value) {
await fetchInstances()
}
useAppEvent('instance', fetchInstances)
useAppEvent('instance_groups_changed', fetchInstances)
function openPageContextMenu(event: MouseEvent) {
if (
!(event.target instanceof HTMLElement) ||
@@ -872,15 +872,8 @@ watch(instanceId, (currentInstanceId, previousInstanceId) => {
})
useAppEvent('instance', async (event) => {
if (event.instance_id !== instanceId.value) return
if (event.event === 'removed' || route.path === '/') {
if (route.path !== '/') await router.push({ path: '/' })
return
}
await queryClient.invalidateQueries({
queryKey: instanceKeys.detail(event.instance_id),
exact: true,
})
if (event.instance_id !== instanceId.value || event.event !== 'removed') return
if (route.path !== '/') await router.push({ path: '/' })
})
useAppEvent('process', (event) => {
-1
View File
@@ -35,7 +35,6 @@ theseus = { workspace = true, features = ["tauri"] }
thiserror = { workspace = true }
tokio = { workspace = true, features = ["time"] }
tracing = { workspace = true }
tracing-error = { workspace = true }
url = { workspace = true }
urlencoding = { workspace = true }
uuid = { workspace = true, features = ["serde", "v4"] }
-4
View File
@@ -83,11 +83,7 @@ macro_rules! impl_serialize {
S: Serializer,
{
match self {
// For the Theseus variant, we add a special display for the error,
// to view the spans if subscribed to them (which is information that is lost when serializing)
TheseusSerializableError::Theseus(theseus_error) => {
$crate::error::display_tracing_error(theseus_error);
let unavailable_reason = match theseus_error.raw.as_ref() {
theseus::ErrorKind::SharedInstanceUnavailable(reason) => Some(reason),
_ => None,
-18
View File
@@ -1,18 +0,0 @@
use tracing_error::ExtractSpanTrace;
pub fn display_tracing_error(err: &theseus::Error) {
match get_span_trace(err) {
Some(span_trace) => {
tracing::error!(error = %err, span_trace = %span_trace);
}
None => {
tracing::error!(error = %err);
}
}
}
pub fn get_span_trace<'a>(
error: &'a (dyn std::error::Error + 'static),
) -> Option<&'a tracing_error::SpanTrace> {
error.source().and_then(|e| e.span_trace())
}
-1
View File
@@ -12,7 +12,6 @@ use tauri_plugin_fs::FsExt;
use theseus::prelude::*;
mod api;
mod error;
#[cfg(target_os = "macos")]
mod macos;
+3 -1
View File
@@ -56,7 +56,9 @@ async fn open_app_db_pool(db_path: &Path) -> crate::Result<Pool<Sqlite>> {
.create_if_missing(true);
Ok(SqlitePoolOptions::new()
.max_connections(100)
.max_connections(10)
.idle_timeout(None)
.max_lifetime(None)
.connect_with(conn_options)
.await?)
}