Skip to content

Commit df2314d

Browse files
authored
feat(app): content cache store (#7490)
* prototype: content store * fix: rebase issue * chore: cleanup * feat: download manager * fix: clippy/fmt/lint * feat: qa * fix: qa * fix: lint + fmt * fix: use reflink > hardlink > sym > copy * chore: cleanup * chore: cleanup * fix: locks for windows * fix: remove sha1 for content store * fix: rev comments aecsocket * fix: check * chore: cleanup frontend * fix: qa * fix: tombi * fix: tombi
1 parent 04fb1a0 commit df2314d

188 files changed

Lines changed: 13032 additions & 2504 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎Cargo.lock‎

Lines changed: 15 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎Cargo.toml‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ p256 = "0.13.2"
146146
parking_lot = "0.12.5"
147147
paste = "1.0.15"
148148
path-util = { path = "packages/path-util" }
149+
pathdiff = "0.2.3"
149150
phf = { version = "0.13.1", features = ["macros"] }
150151
png = "0.18.0"
151152
postcard = { version = "1.1.3", default-features = false, features = ["alloc"] }
@@ -160,6 +161,7 @@ rand = "=0.8.5" # Locked on 0.8 until argon2 and p256 update to 0.9
160161
rand_chacha = "=0.3.1" # Locked on 0.3 until we can update rand to 0.9
161162
rdkafka = { version = "0.36.2", features = ["cmake-build"] }
162163
redis = "1.4.1"
164+
reflink-copy = "0.1.30"
163165
regex = "1.12.2"
164166
reqwest = { version = "0.12.24", default-features = false }
165167
rgb = "0.8.52"
@@ -173,6 +175,7 @@ rust-s3 = { version = "0.37.0", default-features = false, features = [
173175
rustls = "0.23.32"
174176
rustrict = { version = "0.7.39", default-features = false, features = ["censor"] }
175177
rusty-money = "0.4.1"
178+
same-file = "1.0.6"
176179
scalar_api_reference = { version = "0.2.2", default-features = false }
177180
secrecy = "0.10.3"
178181
sentry = { version = "0.45.0", default-features = false, features = [

‎apps/app-frontend/package.json‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
"@floating-ui/vue": "^2.0.1",
3838
"fuse.js": "^6.6.2",
3939
"intl-messageformat": "^10.7.7",
40+
"motion-v": "2.2.1",
4041
"ofetch": "^1.3.4",
4142
"overlayscrollbars": "^2.15.1",
4243
"posthog-js": "^1.158.2",

‎apps/app-frontend/src/components/ui/AppActionBar.vue‎

Lines changed: 19 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
<template>
22
<div class="flex gap-2 items-center">
3+
<DownloadManager />
34
<div v-if="downloadState.total > 0 || hasActiveLoadingBars" class="relative">
45
<IconButton
56
v-tooltip="downloadToggleLabel"
@@ -17,7 +18,7 @@
1718
</div>
1819
<AppUpdateButton />
1920
<div
20-
class="flex border-solid border-surface-5 text-sm items-center gap-2 py-1.5 px-3 rounded-xl border"
21+
class="flex border-solid border-surface-5 text-sm font-medium items-center gap-2 py-1.5 px-3 rounded-xl border"
2122
>
2223
<template v-if="selectedProcess">
2324
<OnlineIndicatorIcon />
@@ -130,10 +131,10 @@ import {
130131
TerminalSquareIcon,
131132
UnplugIcon,
132133
} from '@modrinth/assets'
133-
import { IconButton } from '@modrinth/ui'
134134
import {
135135
defineMessages,
136136
FloatingMenu,
137+
IconButton,
137138
injectNotificationManager,
138139
injectPopupNotificationManager,
139140
type PopupNotificationProgressItem,
@@ -145,10 +146,9 @@ import { computed, onBeforeUnmount, onMounted, ref } from 'vue'
145146
import { useRouter } from 'vue-router'
146147
147148
import AppUpdateButton from '@/components/ui/app-update-button/index.vue'
148-
import { useInstallJobNotifications } from '@/composables/browse/install-job-notifications'
149+
import DownloadManager from '@/components/ui/download-manager/index.vue'
149150
import { useAppEvent } from '@/composables/use-app-event'
150151
import { trackEvent } from '@/helpers/analytics'
151-
import { toError } from '@/helpers/errors'
152152
import { get_many as getInstances } from '@/helpers/instance'
153153
import { get_all as getRunningProcesses, kill as killProcess } from '@/helpers/process'
154154
import type { LoadingBar } from '@/helpers/state'
@@ -323,7 +323,6 @@ function goToTerminal(instanceId?: string) {
323323
const currentLoadingBars = ref<LoadingBar[]>([])
324324
const currentLoadingBarIconUrls = ref<Record<string, string | null>>({})
325325
const notificationId = ref<string | number | null>(null)
326-
const terminalNotificationIds = new Map<string, string | number>()
327326
const dismissed = ref(false)
328327
329328
function getLoadingBarKey(loadingBar: LoadingBar): string {
@@ -371,79 +370,28 @@ function removeNotification(): void {
371370
notificationId.value = null
372371
}
373372
374-
function syncTerminalNotifications(): void {
375-
const terminalNotifications = installJobNotifications.terminalNotifications.value
376-
const currentJobIds = new Set(terminalNotifications.map((notification) => notification.id))
377-
378-
for (const terminal of terminalNotifications) {
379-
const popupId = terminalNotificationIds.get(terminal.id)
380-
let notification = popupId
381-
? popupNotificationManager
382-
.getNotifications()
383-
.find(
384-
(candidate): candidate is PopupNotificationStandard =>
385-
candidate.id === popupId && candidate.contentType === 'standard',
386-
)
387-
: undefined
388-
389-
if (!notification) {
390-
notification = popupNotificationManager.addPopupNotification({
391-
contentType: 'standard',
392-
title: terminal.title,
393-
text: terminal.text,
394-
type: terminal.type,
395-
buttons: terminal.buttons,
396-
onDismiss: terminal.onDismiss,
397-
autoCloseMs: null,
398-
})
399-
terminalNotificationIds.set(terminal.id, notification.id)
400-
continue
401-
}
402-
403-
notification.title = terminal.title
404-
notification.text = terminal.text
405-
notification.type = terminal.type
406-
notification.buttons = terminal.buttons
407-
notification.onDismiss = terminal.onDismiss
408-
}
409-
410-
for (const [jobId, popupId] of terminalNotificationIds) {
411-
if (!currentJobIds.has(jobId)) {
412-
popupNotificationManager.removeNotification(popupId)
413-
terminalNotificationIds.delete(jobId)
414-
}
415-
}
416-
}
417-
418373
function buildDownloadItems(): PopupNotificationProgressItem[] {
419-
return [
420-
...installJobNotifications.progressItems.value,
421-
...currentLoadingBars.value.map<PopupNotificationProgressItem>((bar) => ({
422-
id: getLoadingBarKey(bar),
423-
title: bar.title ?? '',
424-
text: getLoadingText(bar),
425-
iconUrl: currentLoadingBarIconUrls.value[getLoadingBarKey(bar)] ?? null,
426-
progress: getLoadingProgress(bar),
427-
waiting: !bar.total || bar.total <= 0,
428-
progressType: bar.bar_type?.type === 'pack_import' ? 'bytes' : 'percentage',
429-
progressCurrent: bar.current,
430-
progressTotal: bar.total,
431-
})),
432-
]
374+
return currentLoadingBars.value.map((bar) => ({
375+
id: getLoadingBarKey(bar),
376+
title: bar.title ?? '',
377+
text: getLoadingText(bar),
378+
iconUrl: currentLoadingBarIconUrls.value[getLoadingBarKey(bar)] ?? null,
379+
progress: getLoadingProgress(bar),
380+
waiting: !bar.total || bar.total <= 0,
381+
progressType: bar.bar_type?.type === 'pack_import' ? 'bytes' : 'percentage',
382+
progressCurrent: bar.current,
383+
progressTotal: bar.total,
384+
}))
433385
}
434386
435-
const hasActiveLoadingBars = computed(
436-
() => currentLoadingBars.value.length > 0 || installJobNotifications.active.value,
437-
)
387+
const hasActiveLoadingBars = computed(() => currentLoadingBars.value.length > 0)
438388
439389
function updateNotification(resummon = false): void {
440-
syncTerminalNotifications()
441-
442390
if (resummon) {
443391
dismissed.value = false
444392
}
445393
446-
if (currentLoadingBars.value.length === 0 && !installJobNotifications.active.value) {
394+
if (currentLoadingBars.value.length === 0) {
447395
removeNotification()
448396
dismissed.value = false
449397
return
@@ -462,19 +410,15 @@ function updateNotification(resummon = false): void {
462410
const progressItems = buildDownloadItems()
463411
464412
if (notif) {
465-
notif.title = installJobNotifications.active.value
466-
? installJobNotifications.title.value
467-
: formatMessage(messages.downloads)
413+
notif.title = formatMessage(messages.downloads)
468414
notif.text = undefined
469415
notif.progressItems = progressItems
470416
notif.progress = undefined
471417
notif.waiting = undefined
472418
} else {
473419
const notification = popupNotificationManager.addPopupNotification({
474420
contentType: 'standard',
475-
title: installJobNotifications.active.value
476-
? installJobNotifications.title.value
477-
: formatMessage(messages.downloads),
421+
title: formatMessage(messages.downloads),
478422
type: 'download',
479423
autoCloseMs: null,
480424
progressItems,
@@ -554,12 +498,6 @@ async function refreshLoadingBars() {
554498
updateNotification()
555499
}
556500
557-
const installJobNotifications = await useInstallJobNotifications({
558-
router,
559-
handleError: (error) => handleError(toError(error)),
560-
onChange: updateNotification,
561-
})
562-
563501
await refreshLoadingBars()
564502
565503
useAppEvent('loading', async () => {
@@ -576,11 +514,8 @@ function selectProcess(process: RunningProcess) {
576514
577515
onBeforeUnmount(() => {
578516
removeNotification()
579-
terminalNotificationIds.forEach((id) => popupNotificationManager.removeNotification(id))
580-
terminalNotificationIds.clear()
581517
dismissed.value = false
582518
window.removeEventListener('offline', handleOffline)
583519
window.removeEventListener('online', handleOnline)
584-
installJobNotifications.dispose()
585520
})
586521
</script>

‎apps/app-frontend/src/components/ui/QuickInstanceSwitcher.vue‎

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ const runningInstances = ref([])
3636
const { formatMessage } = useVIntl()
3737
3838
const container = ref()
39+
const footer = ref()
3940
let resizeObserver
4041
const maxAuto = ref(0)
4142
const allInstances = computed(() =>
@@ -74,9 +75,12 @@ const updateMaxAuto = () => {
7475
const rem = Number.parseFloat(getComputedStyle(document.documentElement).fontSize)
7576
const dividerHeight = rem + 1
7677
const gap = rem / 4
78+
const footerHeight = (footer.value?.clientHeight ?? 0) + gap
7779
maxAuto.value = Math.max(
7880
0,
79-
Math.floor((container.value.clientHeight - 2 * dividerHeight - gap) / (3 * rem + gap)),
81+
Math.floor(
82+
(container.value.clientHeight - footerHeight - 2 * dividerHeight - gap) / (3 * rem + gap),
83+
),
8084
)
8185
}
8286
@@ -176,6 +180,7 @@ useAppEvent('process', checkProcesses)
176180
onMounted(() => {
177181
resizeObserver = new ResizeObserver(updateMaxAuto)
178182
resizeObserver.observe(container.value)
183+
resizeObserver.observe(footer.value)
179184
updateMaxAuto()
180185
checkProcesses()
181186
})
@@ -349,7 +354,9 @@ function openContextMenu(event, instance) {
349354
"
350355
></div>
351356
</div>
352-
<slot />
357+
<div ref="footer" class="flex shrink-0 flex-col items-center">
358+
<slot />
359+
</div>
353360
</div>
354361
</template>
355362

0 commit comments

Comments
 (0)