diff --git a/Cargo.lock b/Cargo.lock index 459048aa57..4c94019842 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11172,7 +11172,6 @@ dependencies = [ "thiserror 2.0.17", "tokio", "tracing", - "tracing-error", "url", "urlencoding", "uuid 1.23.3", diff --git a/apps/app-frontend/src/App.vue b/apps/app-frontend/src/App.vue index 668c557a86..52f5c6d5c7 100644 --- a/apps/app-frontend/src/App.vue +++ b/apps/app-frontend/src/App.vue @@ -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' @@ -125,6 +126,7 @@ import { get as getInstance, get_global_synced_options, run, + set_global_synced_option, } from '@/helpers/instance' import { get as getCreds, @@ -151,7 +153,7 @@ import { } from '@/helpers/utils.js' import { start_join_server, start_join_singleplayer_world } from '@/helpers/worlds.ts' import i18n from '@/i18n.config' -import { instanceKeys } from '@/pages/instance/query-options' +import { instanceKeys, screenshotKeys } from '@/pages/instance/query-options' import { appUpdateState, downloadAvailableAppUpdate, @@ -190,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) @@ -1116,6 +1119,25 @@ watch( settings.hide_nametag_skins_page = behavior.hide_nametag settingsChanged = true } + + const showAllScreenshots = behavior.show_all_screenshots + if (typeof showAllScreenshots === 'boolean') { + const globalSyncedOptions = + globalSyncedOptionsQuery.data.value ?? + (await queryClient.fetchQuery({ + queryKey: ['global-synced-options'], + queryFn: get_global_synced_options, + })) + if (globalSyncedOptions.screenshots !== showAllScreenshots) { + const updatedGlobalSyncedOptions = await set_global_synced_option( + 'screenshots', + showAllScreenshots, + ) + queryClient.setQueryData(['global-synced-options'], updatedGlobalSyncedOptions) + await queryClient.invalidateQueries({ queryKey: screenshotKeys.all }) + } + } + for (const [flag, value] of Object.entries(behaviorFeatureFlags)) { if (settings.feature_flags[flag] !== value) { settings.feature_flags[flag] = value diff --git a/apps/app-frontend/src/components/ui/QuickInstanceSwitcher.vue b/apps/app-frontend/src/components/ui/QuickInstanceSwitcher.vue index d926b34069..99cc39a147 100644 --- a/apps/app-frontend/src/components/ui/QuickInstanceSwitcher.vue +++ b/apps/app-frontend/src/components/ui/QuickInstanceSwitcher.vue @@ -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(() => { diff --git a/apps/app-frontend/src/components/ui/screenshots-page/card.vue b/apps/app-frontend/src/components/ui/screenshots-page/card.vue index adf1222ef8..f1de4c6532 100644 --- a/apps/app-frontend/src/components/ui/screenshots-page/card.vue +++ b/apps/app-frontend/src/components/ui/screenshots-page/card.vue @@ -3,11 +3,16 @@ @@ -91,7 +166,7 @@ watch( ref="card" role="button" tabindex="0" - class="group relative aspect-video min-w-0 cursor-pointer overflow-hidden rounded-xl border border-solid border-surface-5 bg-surface-2 p-0 text-left shadow-sm transition-[filter] hover:brightness-110 focus-visible:outline focus-visible:outline-2 focus-visible:outline-brand" + class="group relative isolate aspect-video min-w-0 cursor-pointer overflow-hidden rounded-xl border border-solid border-surface-5 bg-surface-2 p-0 text-left shadow-sm transition-[filter] hover:brightness-110 focus-visible:outline focus-visible:outline-2 focus-visible:outline-brand" :class="{ '!border-contrast brightness-110': selected, '!border-brand ring-2 ring-brand animate-pulse': highlighted, @@ -115,7 +190,7 @@ watch( > -