mirror of
https://github.com/modrinth/code.git
synced 2026-08-31 03:55:59 +00:00
feat: instance sharing thru shared-instances service (#6569)
* feat: implement instance share page + search_users backend call * feat: invite players modal * feat: use tanstack queries for friends sync across app pages * feat: base shared instances implementation * fix: admon style * feat: impl instance admonitions like server panel * fix: impl get + del usage * feat: support modpack links * feat: invite notif accepting * fix: lint + fmt * feat: impl install to play * feat: impl usage of UpdateToPlayModal * feat: warnings on deleting/disabling shared-instance version content * fix: send instance name * feat: align with backend * feat: shared instances qa * feat: wrong account protection * feat: qa * fix: smartly apply updates * fix: install bug * fix: 401/404 differentiation * fix: fmt+prepr * feat: qa * feat: qa * fix: signing out messes up revoke/deleted checks * feat: qa * fix: fmt + lint * feat: lock content if part of shared instance * fix: lint * [do not merge] feat: rough invite links impl temp (#6666) * fix: wrong cmd * feat: invite page * fix: server-manager DTO mismatch * fix: drop anonymous invite link acceptance * refactor: structured shared-instance unavailable errors * refactor: centralise error presentations * refactor: dedupe shared instance diff detection * fix: logging in reqwests * refactor: move app.vue shared instances into handler * refactor: break up Share.vue * refactor: split up shared instances state outside of instance index * refactor: dedicated shared instances install/update modals + split up page * refactor: centralized managed content * refactor: split up install shared to own runner + shared.rs split up * refactor: dedupe sql for instance metadata enrichmnt * refactor: friends composable + dedupe friends logic across usages * chore: reduced unused code * fix: align with backend * fix: lint * fix: file sha changes * fix: invite links not working due to icon signed * feat: qa * feat: reporting frontend dummy * fix: try use header * remove: file hash field * fix: pin box * feat: malware warning for shared instances * fix: cache rule * feat: config files syncing * feat: disable config sharing * fix: header * fix: use mark ready * fix: dont cause push update for configs * fix: lint * feat: sharing page in settings * feat: move config + change flow * fix: qa * fix: lint prepr * feat: proxy file upload thru shared instances backend * fix: use collapisible * fix: push config * fix: config * feat: swap out sign in modal for new one * fix: report flow * fix: exclude configs.zip from external warnings * fix: nuxi init * fix: config bundle downloading * fix: error notif * fix: polling * fix: qa * fix: lint + prepr * feat: shared instances moderation frontend + hook up report flow * fix: report copy * fix: lint * fix: lint * fix: modrinth ids being undefined * feat: instance quarantining * fix: prepr + fmt * fix: quarantined -> locked terminology * fix: missing endpoint impls + fmt * fix: missing api in build.rs * fix: share tab jittery * fix: fmt *PT bug * fix: invites count as users even if pending * fix: prepr * fix: invite page owner in users list * fix: lint * fix: qa * fix: lint * fix: members stale not clearing * fix: invite use joined_at field * fix: lint * fix: qa --------- Co-authored-by: sychic <47618543+Sychic@users.noreply.github.com>
This commit is contained in:
@@ -7,7 +7,6 @@ import {
|
||||
injectFilePicker,
|
||||
injectNotificationManager,
|
||||
InstallationSettingsLayout,
|
||||
provideAppBackup,
|
||||
provideInstallationSettings,
|
||||
useDebugLogger,
|
||||
useVIntl,
|
||||
@@ -16,24 +15,25 @@ import type { GameVersionTag, PlatformTag } from '@modrinth/utils'
|
||||
import { useQuery, useQueryClient } from '@tanstack/vue-query'
|
||||
import { computed, ref } from 'vue'
|
||||
|
||||
import SharedInstanceInstallationSettingsControls from '@/components/ui/shared-instances/SharedInstanceInstallationSettingsControls.vue'
|
||||
import { useManagedContentPolicy } from '@/composables/instances/use-managed-content-policy'
|
||||
import { trackEvent } from '@/helpers/analytics'
|
||||
import { get_project_versions, get_version } from '@/helpers/cache'
|
||||
import {
|
||||
install_duplicate_instance,
|
||||
install_existing_instance,
|
||||
install_pack_to_existing_instance,
|
||||
installJobInstanceId,
|
||||
wait_for_install_job,
|
||||
} from '@/helpers/install'
|
||||
import {
|
||||
edit,
|
||||
get_linked_modpack_info,
|
||||
list,
|
||||
unlink_shared_instance,
|
||||
update_managed_modrinth_version,
|
||||
update_repair_modrinth,
|
||||
} from '@/helpers/instance'
|
||||
import { get_loader_versions } from '@/helpers/metadata'
|
||||
import { get_game_versions, get_loaders } from '@/helpers/tags'
|
||||
import { provideInstanceBackup } from '@/providers/instance-backup'
|
||||
import { injectInstanceSettings } from '@/providers/instance-settings'
|
||||
import { useTheming } from '@/store/state'
|
||||
|
||||
@@ -47,6 +47,7 @@ const debug = useDebugLogger('AppInstallationSettings')
|
||||
const themeStore = useTheming()
|
||||
|
||||
const { instance, offline, isMinecraftServer, onUnlinked, closeModal } = injectInstanceSettings()
|
||||
const managedContentPolicy = useManagedContentPolicy(instance)
|
||||
const skipNonEssentialWarnings = computed(() =>
|
||||
themeStore.getFeatureFlag('skip_non_essential_warnings'),
|
||||
)
|
||||
@@ -111,9 +112,14 @@ debug('metadata queries configured', {
|
||||
const isModrinthLinkedModpack = computed(
|
||||
() =>
|
||||
instance.value.link?.type === 'modrinth_modpack' ||
|
||||
instance.value.link?.type === 'server_project_modpack',
|
||||
instance.value.link?.type === 'server_project_modpack' ||
|
||||
(instance.value.link?.type === 'shared_instance' &&
|
||||
!!instance.value.link.modpack_project_id &&
|
||||
!!instance.value.link.modpack_version_id),
|
||||
)
|
||||
const isImportedModpack = computed(() => instance.value.link?.type === 'imported_modpack')
|
||||
const isSharedInstanceManagedModpack = managedContentPolicy.isManagedModpack
|
||||
const canUnlinkSharedInstance = managedContentPolicy.canUnlink
|
||||
|
||||
const modpackInfoQuery = useQuery({
|
||||
queryKey: computed(() => ['linkedModpackInfo', instance.value.id]),
|
||||
@@ -124,12 +130,43 @@ const modpackInfo = modpackInfoQuery.data
|
||||
|
||||
const repairing = ref(false)
|
||||
const reinstalling = ref(false)
|
||||
const unlinkingSharedInstance = ref(false)
|
||||
const installationSettingsBusy = computed(
|
||||
() =>
|
||||
instance.value.quarantined ||
|
||||
instance.value.install_stage !== 'installed' ||
|
||||
repairing.value ||
|
||||
reinstalling.value ||
|
||||
unlinkingSharedInstance.value ||
|
||||
!!offline,
|
||||
)
|
||||
const installationSettingsBusyMessage = computed(() =>
|
||||
instance.value.quarantined ? formatMessage(messages.locked) : null,
|
||||
)
|
||||
|
||||
async function unlinkSharedInstance() {
|
||||
unlinkingSharedInstance.value = true
|
||||
try {
|
||||
await unlink_shared_instance(instance.value.id)
|
||||
await queryClient.invalidateQueries({ queryKey: ['sharedInstanceUsers', instance.value.id] })
|
||||
await queryClient.invalidateQueries({ queryKey: ['linkedModpackInfo', instance.value.id] })
|
||||
onUnlinked()
|
||||
} catch (error) {
|
||||
handleError(error)
|
||||
} finally {
|
||||
unlinkingSharedInstance.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const messages = defineMessages({
|
||||
loaderVersion: {
|
||||
id: 'instance.settings.tabs.installation.loader-version',
|
||||
defaultMessage: '{loader} version',
|
||||
},
|
||||
locked: {
|
||||
id: 'instance.settings.tabs.installation.locked',
|
||||
defaultMessage: 'Installation settings are unavailable while this instance is locked.',
|
||||
},
|
||||
})
|
||||
|
||||
function getManifest(loader: string) {
|
||||
@@ -162,27 +199,7 @@ async function installLocalModpackFromPicker() {
|
||||
return !!completed
|
||||
}
|
||||
|
||||
provideAppBackup({
|
||||
async createBackup() {
|
||||
debug('createBackup: start', {
|
||||
instanceId: instance.value.id,
|
||||
instanceName: instance.value.name,
|
||||
})
|
||||
const allInstances = await list()
|
||||
const prefix = `${instance.value.name} - Backup #`
|
||||
const existingNums = allInstances
|
||||
.filter((p) => p.name.startsWith(prefix))
|
||||
.map((p) => parseInt(p.name.slice(prefix.length), 10))
|
||||
.filter((n) => !isNaN(n))
|
||||
const nextNum = existingNums.length > 0 ? Math.max(...existingNums) + 1 : 1
|
||||
const job = await install_duplicate_instance(instance.value.id)
|
||||
const newInstanceId = installJobInstanceId(job)
|
||||
if (newInstanceId) {
|
||||
await edit(newInstanceId, { name: `${prefix}${nextNum}` })
|
||||
}
|
||||
debug('createBackup: done', { newInstanceId, backupName: `${prefix}${nextNum}` })
|
||||
},
|
||||
})
|
||||
provideInstanceBackup(instance)
|
||||
|
||||
provideInstallationSettings({
|
||||
closeSettings: closeModal,
|
||||
@@ -208,14 +225,14 @@ provideInstallationSettings({
|
||||
}
|
||||
return rows
|
||||
}),
|
||||
isLinked: computed(() => isModrinthLinkedModpack.value || isImportedModpack.value),
|
||||
isBusy: computed(
|
||||
isLinked: computed(
|
||||
() =>
|
||||
instance.value.install_stage !== 'installed' ||
|
||||
repairing.value ||
|
||||
reinstalling.value ||
|
||||
!!offline,
|
||||
isModrinthLinkedModpack.value ||
|
||||
isImportedModpack.value ||
|
||||
isSharedInstanceManagedModpack.value,
|
||||
),
|
||||
isBusy: installationSettingsBusy,
|
||||
busyMessage: installationSettingsBusyMessage,
|
||||
skipNonEssentialWarnings,
|
||||
modpack: computed(() => {
|
||||
if (isImportedModpack.value && instance.value.link?.type === 'imported_modpack') {
|
||||
@@ -452,14 +469,28 @@ provideInstallationSettings({
|
||||
isServer: false,
|
||||
isApp: true,
|
||||
showModpackVersionActions: computed(
|
||||
() => isModrinthLinkedModpack.value && !isMinecraftServer.value,
|
||||
() =>
|
||||
isModrinthLinkedModpack.value &&
|
||||
!isMinecraftServer.value &&
|
||||
!isSharedInstanceManagedModpack.value,
|
||||
),
|
||||
isLocalFile: isImportedModpack,
|
||||
isManagedModpack: isSharedInstanceManagedModpack,
|
||||
managedModpackWarning: managedContentPolicy.managedModpackWarning,
|
||||
repairing,
|
||||
reinstalling,
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<InstallationSettingsLayout />
|
||||
<InstallationSettingsLayout>
|
||||
<template #extra>
|
||||
<SharedInstanceInstallationSettingsControls
|
||||
:can-unlink="canUnlinkSharedInstance"
|
||||
:busy="installationSettingsBusy"
|
||||
:unlinking="unlinkingSharedInstance"
|
||||
:unlink="unlinkSharedInstance"
|
||||
/>
|
||||
</template>
|
||||
</InstallationSettingsLayout>
|
||||
</template>
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
<template>
|
||||
<div>
|
||||
<SharedInstanceInstallationSettingsControls
|
||||
can-unpublish
|
||||
:busy="isBusy"
|
||||
:unpublishing="unpublishing"
|
||||
:unpublish="unpublishSharedInstance"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { injectNotificationManager } from '@modrinth/ui'
|
||||
import { useQueryClient } from '@tanstack/vue-query'
|
||||
import { computed, ref } from 'vue'
|
||||
|
||||
import SharedInstanceInstallationSettingsControls from '@/components/ui/shared-instances/SharedInstanceInstallationSettingsControls.vue'
|
||||
import { unpublish_shared_instance } from '@/helpers/instance'
|
||||
import { injectInstanceSettings } from '@/providers/instance-settings'
|
||||
|
||||
const { instance, offline, onUnlinked } = injectInstanceSettings()
|
||||
const { handleError } = injectNotificationManager()
|
||||
const queryClient = useQueryClient()
|
||||
const unpublishing = ref(false)
|
||||
const isBusy = computed(
|
||||
() => instance.value.install_stage !== 'installed' || unpublishing.value || !!offline,
|
||||
)
|
||||
|
||||
async function unpublishSharedInstance() {
|
||||
unpublishing.value = true
|
||||
try {
|
||||
await unpublish_shared_instance(instance.value.id)
|
||||
queryClient.setQueryData(['sharedInstanceUsers', instance.value.id], [])
|
||||
await queryClient.invalidateQueries({ queryKey: ['linkedModpackInfo', instance.value.id] })
|
||||
onUnlinked()
|
||||
} catch (error) {
|
||||
handleError(error)
|
||||
} finally {
|
||||
unpublishing.value = false
|
||||
}
|
||||
}
|
||||
</script>
|
||||
Reference in New Issue
Block a user