Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions apps/app-frontend/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -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)
Expand Down
60 changes: 23 additions & 37 deletions apps/app-frontend/src/components/ui/QuickInstanceSwitcher.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand All @@ -17,25 +17,42 @@ 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([])

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)
Expand Down Expand Up @@ -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(() => {
Expand Down
60 changes: 60 additions & 0 deletions apps/app-frontend/src/composables/use-instance-metadata-refresh.ts
Original file line number Diff line number Diff line change
@@ -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)
}
18 changes: 0 additions & 18 deletions apps/app-frontend/src/locales/ar-SA/index.json
Original file line number Diff line number Diff line change
Expand Up @@ -1151,9 +1151,6 @@
"app.nav.modrinth-hosting": {
"message": "استضافة Modrinth"
},
"app.nav.sign-in-to-modrinth-account": {
"message": "تسجيل الدخول إلى حساب Modrinth"
},
"app.nav.upgrade-to-modrinth-plus": {
"message": "الترقية إلى Modrinth+"
},
Expand Down Expand Up @@ -2270,18 +2267,6 @@
"instance.settings.tabs.installation.locked": {
"message": "إعدادات التثبيت غير متاحة أثناء قفل هذه النسخة."
},
"instance.settings.tabs.java.custom-environment-variables": {
"message": "متغيرات البيئة المخصصة"
},
"instance.settings.tabs.java.custom-java-arguments": {
"message": "معلمات Java المخصصة"
},
"instance.settings.tabs.java.custom-java-installation": {
"message": "تثبيت Java المخصص"
},
"instance.settings.tabs.java.custom-memory-allocation": {
"message": "تخصيص الذاكرة حسب الطلب"
},
"instance.settings.tabs.java.enter-environment-variables": {
"message": "أدخل المتغيرات البيئية..."
},
Expand Down Expand Up @@ -2312,9 +2297,6 @@
"instance.settings.tabs.window": {
"message": "النافذة"
},
"instance.settings.tabs.window.custom-window-settings": {
"message": "إعدادات نافذة مخصّصة"
},
"instance.settings.tabs.window.fullscreen": {
"message": "ملء الشاشة"
},
Expand Down
Loading
Loading