mirror of
https://github.com/modrinth/code.git
synced 2026-09-05 06:19:11 +00:00
feat: instances v2 (#6431)
* feat: base of instances v2 * feat: use old profiles with compat layer * prototype: instances v2 * fix: install_from using profile * fix: skins migration fix * fix: frontend still using profile path * fix: add update proj multiselect guard * fix: cargo fmt * fix: content missing fields * feat: break up app-lib/api/instance.rs * fix: check_content_updates mismatch * fix: updater modal cleanup w/new structure * feat: better update all handling * fix: remove preview_update_all * fix: feedback on bulk update + lint * fix: rem transitions * fix: change to jsonb * feat: app db backup after update * fix: lint * fix: sqlx prepare + use sqlx macros * fix: lint * fix: bugs * feat: defuck the installing process up * fix: bug of hell * fix: shear * fix: fmt * fix: install progress spacing + change mc/content/overrides to bytes * fix: lint * fix: prepr * fix: navtabs anim not working in app * fix: worlds.vue improvements + browse page fixes * feat: optimise queries + adapter fns * fix: lint * fix: lint * feat: shared modrinth-content-management crate (#6469) * feat: disable warnings setting * feat: add instances shortcuts (#6329) * Add modrinth://launch deep link to start a profile Support external profile launching via modrinth://launch/{profile_path} for integrations such as Stream Deck. * Change route to /launch/profile/{id} for future extensibility * fix: ensure profile path is url decoded * fix: URL-decode profile path from deep link * fix: use urlencoding crate for URL decoding * feat: implement app instance shortcuts * feat: change windows shortcut creation to use windows api instead * feat: implement creating a shortcut launching world/server * format * fmt * fix multiline inline tables * pnpm prepr * feat: move create shortcut to last item * refactor: split up shortcuts.rs for individual platforms * refactor: turn profile launch url into url type * use string literal and add safety comment * pt2 * refactor: rename anything that's profile into instance * update mac shortcut --------- Co-authored-by: DJCheesusReal <134006619+DJCheesusReal@users.noreply.github.com> --------- Co-authored-by: Truman Gao <106889354+tdgao@users.noreply.github.com> Co-authored-by: DJCheesusReal <134006619+DJCheesusReal@users.noreply.github.com>
This commit is contained in:
co-authored by
DJCheesusReal
Truman Gao
parent
ef4044534f
commit
734720e11e
@@ -41,14 +41,14 @@ import {
|
||||
get_search_results_v3,
|
||||
get_version_many,
|
||||
} from '@/helpers/cache.js'
|
||||
import { profile_listener } from '@/helpers/events.js'
|
||||
import { get_loader_versions as getLoaderManifest } from '@/helpers/metadata'
|
||||
import { instance_listener } from '@/helpers/events.js'
|
||||
import {
|
||||
get as getInstance,
|
||||
get_installed_project_ids as getInstalledProjectIds,
|
||||
} from '@/helpers/profile.js'
|
||||
} from '@/helpers/instance'
|
||||
import { get_loader_versions as getLoaderManifest } from '@/helpers/metadata'
|
||||
import { get_categories, get_game_versions, get_loaders } from '@/helpers/tags'
|
||||
import { get_profile_worlds } from '@/helpers/worlds'
|
||||
import { get_instance_worlds } from '@/helpers/worlds'
|
||||
import { injectContentInstall } from '@/providers/content-install'
|
||||
import { injectServerInstall } from '@/providers/server-install'
|
||||
import {
|
||||
@@ -56,6 +56,7 @@ import {
|
||||
provideServerInstallContent,
|
||||
} from '@/providers/setup/server-install-content'
|
||||
import { useBreadcrumbs } from '@/store/breadcrumbs'
|
||||
import { useTheming } from '@/store/state'
|
||||
|
||||
const { handleError } = injectNotificationManager()
|
||||
const { formatMessage } = useVIntl()
|
||||
@@ -67,6 +68,7 @@ const debugLog = useDebugLogger('Browse')
|
||||
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
const themeStore = useTheming()
|
||||
const serverSetupModalRef = ref<InstanceType<typeof CreationFlowModal> | null>(null)
|
||||
const serverInstallContent = createServerInstallContent({ serverSetupModalRef })
|
||||
provideServerInstallContent(serverInstallContent)
|
||||
@@ -131,10 +133,10 @@ type Instance = {
|
||||
install_stage: string
|
||||
icon_path?: string
|
||||
name: string
|
||||
linked_data?: {
|
||||
link?: {
|
||||
type: string
|
||||
project_id: string
|
||||
version_id: string
|
||||
locked: boolean
|
||||
}
|
||||
}
|
||||
|
||||
@@ -186,7 +188,7 @@ async function refreshInstalledProjectIds() {
|
||||
if (!route.query.i) return
|
||||
|
||||
if (route.query.from === 'worlds') {
|
||||
const worlds = await get_profile_worlds(route.query.i as string).catch(handleError)
|
||||
const worlds = await get_instance_worlds(route.query.i as string).catch(handleError)
|
||||
if (!worlds) return
|
||||
|
||||
const serverProjectIds = worlds
|
||||
@@ -224,10 +226,10 @@ async function initInstanceContext() {
|
||||
|
||||
await refreshInstalledProjectIds()
|
||||
|
||||
if (instance.value?.linked_data?.project_id) {
|
||||
debugLog('checking linked project for server status', instance.value.linked_data.project_id)
|
||||
if (instance.value?.link?.project_id) {
|
||||
debugLog('checking linked project for server status', instance.value.link.project_id)
|
||||
const projectV3 = await get_project_v3(
|
||||
instance.value.linked_data.project_id,
|
||||
instance.value.link.project_id,
|
||||
'must_revalidate',
|
||||
).catch(handleError)
|
||||
if (projectV3?.minecraft_server != null) {
|
||||
@@ -448,7 +450,7 @@ const browseTitle = computed(() =>
|
||||
)
|
||||
breadcrumbs.setName('BrowseTitle', browseTitle.value)
|
||||
if (instance.value) {
|
||||
const instanceLink = `/instance/${encodeURIComponent(instance.value.path)}`
|
||||
const instanceLink = `/instance/${encodeURIComponent(instance.value.id)}`
|
||||
breadcrumbs.setContext({
|
||||
name: instance.value.name,
|
||||
link: isFromWorlds.value ? `${instanceLink}/worlds` : instanceLink,
|
||||
@@ -467,6 +469,21 @@ onBeforeRouteLeave(() => {
|
||||
|
||||
const projectType = ref<ProjectType>(route.params.projectType as ProjectType)
|
||||
|
||||
function resetInstanceContext() {
|
||||
if (!instance.value) return
|
||||
|
||||
debugLog('instance context removed, resetting')
|
||||
instance.value = null
|
||||
installedProjectIds.value = null
|
||||
instanceHideInstalled.value = false
|
||||
newlyInstalled.value = []
|
||||
hiddenInstanceProjectIds.value = new Set()
|
||||
hiddenInstanceProjectIdsInitialized.value = false
|
||||
isServerInstance.value = false
|
||||
breadcrumbs.setName('BrowseTitle', formatMessage(messages.discoverContent))
|
||||
breadcrumbs.setContext(null)
|
||||
}
|
||||
|
||||
watch(
|
||||
() => route.params.projectType as ProjectType,
|
||||
async (newType) => {
|
||||
@@ -479,16 +496,14 @@ watch(
|
||||
|
||||
debugLog('projectType route param changed', { from: projectType.value, to: newType })
|
||||
projectType.value = newType
|
||||
},
|
||||
)
|
||||
|
||||
if (!route.query.i && instance.value) {
|
||||
debugLog('instance context removed, resetting')
|
||||
instance.value = null
|
||||
installedProjectIds.value = null
|
||||
instanceHideInstalled.value = false
|
||||
newlyInstalled.value = []
|
||||
isServerInstance.value = false
|
||||
breadcrumbs.setName('BrowseTitle', formatMessage(messages.discoverContent))
|
||||
breadcrumbs.setContext(null)
|
||||
watch(
|
||||
() => route.query.i,
|
||||
(instanceId) => {
|
||||
if (!instanceId && route.path.startsWith('/browse')) {
|
||||
resetInstanceContext()
|
||||
}
|
||||
},
|
||||
)
|
||||
@@ -564,6 +579,7 @@ const installContext = computed(() => {
|
||||
queuedCount: queuedServerInstallCount.value,
|
||||
selectedProjects: selectedServerInstallProjects.value,
|
||||
isInstallingSelected: isInstallingQueuedServerInstalls.value,
|
||||
skipNonEssentialWarnings: themeStore.getFeatureFlag('skip_non_essential_warnings'),
|
||||
installProgress: queuedInstallProgress.value,
|
||||
clearQueued: clearQueuedServerInstalls,
|
||||
clearSelected: clearQueuedServerInstalls,
|
||||
@@ -578,7 +594,7 @@ const installContext = computed(() => {
|
||||
loader: instance.value.loader,
|
||||
gameVersion: instance.value.game_version,
|
||||
iconSrc: instance.value.icon_path ? convertFileSrc(instance.value.icon_path) : null,
|
||||
backUrl: `/instance/${encodeURIComponent(instance.value.path)}${isFromWorlds.value ? '/worlds' : ''}`,
|
||||
backUrl: `/instance/${encodeURIComponent(instance.value.id)}${isFromWorlds.value ? '/worlds' : ''}`,
|
||||
backLabel: formatMessage(messages.backToInstance),
|
||||
heading: formatMessage(
|
||||
isFromWorlds.value ? messages.addServersToInstance : commonMessages.installingContentLabel,
|
||||
@@ -809,7 +825,7 @@ function getCardActions(
|
||||
await installVersion(
|
||||
projectResult.project_id,
|
||||
selectedInstall.versionId,
|
||||
instance.value ? instance.value.path : null,
|
||||
instance.value ? instance.value.id : null,
|
||||
'SearchCard',
|
||||
(versionId, installedProjectIds) => {
|
||||
setProjectInstalling(projectResult.project_id, false)
|
||||
@@ -982,20 +998,16 @@ if (instance.value?.game_version) {
|
||||
}
|
||||
}
|
||||
|
||||
await searchState.refreshSearch()
|
||||
void searchState.refreshSearch()
|
||||
|
||||
type UnlistenFn = () => void
|
||||
|
||||
let isUnmounted = false
|
||||
let unlistenProfiles: UnlistenFn | null = null
|
||||
let unlistenInstances: UnlistenFn | null = null
|
||||
|
||||
onMounted(() => {
|
||||
profile_listener(async (event: { event: string; profile_path_id: string }) => {
|
||||
if (
|
||||
instance.value &&
|
||||
event.profile_path_id === instance.value.path &&
|
||||
event.event === 'synced'
|
||||
) {
|
||||
instance_listener(async (event: { event: string; instance_id: string }) => {
|
||||
if (instance.value && event.instance_id === instance.value.id && event.event === 'synced') {
|
||||
await refreshInstalledProjectIds()
|
||||
await searchState.refreshSearch()
|
||||
}
|
||||
@@ -1006,14 +1018,14 @@ onMounted(() => {
|
||||
return
|
||||
}
|
||||
|
||||
unlistenProfiles = unlisten
|
||||
unlistenInstances = unlisten
|
||||
})
|
||||
.catch(handleError)
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
isUnmounted = true
|
||||
unlistenProfiles?.()
|
||||
unlistenInstances?.()
|
||||
})
|
||||
|
||||
function getProjectBrowseQuery() {
|
||||
|
||||
Reference in New Issue
Block a user