mirror of
https://github.com/modrinth/code.git
synced 2026-08-26 09:34:50 +00:00
* 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>
190 lines
5.2 KiB
Vue
190 lines
5.2 KiB
Vue
<template>
|
|
<ContentDiffModal
|
|
ref="publishReviewModal"
|
|
:header="formatMessage(messages.header)"
|
|
:admonition-header="formatMessage(messages.admonitionHeader)"
|
|
:description="formatMessage(messages.description)"
|
|
:diffs="publishDiffs"
|
|
:confirm-label="formatMessage(messages.publishButton)"
|
|
:confirm-icon="UploadIcon"
|
|
:confirm-disabled="publishDiffs.length === 0 && selectedConfigPaths.length === 0"
|
|
:added-label="formatMessage(messages.addedLabel)"
|
|
:removed-label="formatMessage(messages.removedLabel)"
|
|
@confirm="publishChanges"
|
|
@cancel="finishReview"
|
|
>
|
|
<template #additional-content>
|
|
<Accordion
|
|
class="min-w-0"
|
|
overflow-visible
|
|
button-class="w-full bg-transparent m-0 p-0 border-none"
|
|
>
|
|
<template #title>
|
|
<FileCogIcon class="size-4 shrink-0 text-primary" />
|
|
<span class="text-lg font-semibold text-contrast">
|
|
{{ formatMessage(messages.configTitle) }}
|
|
</span>
|
|
</template>
|
|
<div class="flex min-w-0 flex-col gap-3 pt-4">
|
|
<div class="max-h-[292px] overflow-y-auto rounded-[20px]">
|
|
<FileTreeSelect
|
|
v-model="selectedConfigPaths"
|
|
:items="configFileItems"
|
|
:show-size="false"
|
|
:show-modified="false"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</Accordion>
|
|
</template>
|
|
</ContentDiffModal>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { FileCogIcon, UploadIcon } from '@modrinth/assets'
|
|
import {
|
|
Accordion,
|
|
type ContentDiffItem,
|
|
ContentDiffModal,
|
|
defineMessages,
|
|
FileTreeSelect,
|
|
type FileTreeSelectItem,
|
|
useVIntl,
|
|
} from '@modrinth/ui'
|
|
import { computed, ref } from 'vue'
|
|
|
|
import {
|
|
getSharedInstanceUnavailableReason,
|
|
isSharedInstanceUnavailableError,
|
|
} from '@/helpers/install'
|
|
import { get_shared_instance_publish_preview, publish_shared_instance } from '@/helpers/instance'
|
|
import { useSharedInstanceErrors } from '@/helpers/shared-instance-errors'
|
|
import type { GameInstance } from '@/helpers/types'
|
|
|
|
type SharedInstancePublishState = 'idle' | 'reviewing' | 'publishing'
|
|
|
|
const props = defineProps<{
|
|
instance: GameInstance
|
|
}>()
|
|
|
|
const emit = defineEmits<{
|
|
published: []
|
|
'state-change': [state: SharedInstancePublishState]
|
|
}>()
|
|
|
|
const { formatMessage } = useVIntl()
|
|
const { notifySharedInstanceError, notifySharedInstanceUnavailable } = useSharedInstanceErrors()
|
|
const publishReviewModal = ref<InstanceType<typeof ContentDiffModal>>()
|
|
const publishDiffs = ref<ContentDiffItem[]>([])
|
|
const configFilePaths = ref<string[]>([])
|
|
const selectedConfigPaths = ref<string[]>([])
|
|
const state = ref<SharedInstancePublishState>('idle')
|
|
const configFileItems = computed<FileTreeSelectItem[]>(() =>
|
|
configFilePaths.value.map((path) => ({ path, type: 'file' })),
|
|
)
|
|
|
|
async function show(e?: MouseEvent) {
|
|
if (state.value !== 'idle') return
|
|
|
|
setState('reviewing')
|
|
let reviewOpened = false
|
|
try {
|
|
const preview = await get_shared_instance_publish_preview(props.instance.id)
|
|
if (!preview) {
|
|
notifySharedInstanceUnavailable()
|
|
emit('published')
|
|
return
|
|
}
|
|
|
|
publishDiffs.value = preview.diffs.map((diff) => ({
|
|
type: diff.type,
|
|
projectName: diff.projectName ?? undefined,
|
|
fileName: diff.fileName ?? undefined,
|
|
currentVersionName: diff.currentVersionName ?? undefined,
|
|
newVersionName: diff.newVersionName ?? undefined,
|
|
fileCount: diff.configFileCount ?? undefined,
|
|
disabled: diff.disabled,
|
|
}))
|
|
configFilePaths.value = preview.configFiles
|
|
selectedConfigPaths.value = []
|
|
if (!publishReviewModal.value) return
|
|
|
|
publishReviewModal.value.show(e)
|
|
reviewOpened = true
|
|
} catch (error) {
|
|
handlePublishError(error)
|
|
} finally {
|
|
if (!reviewOpened) finishReview()
|
|
}
|
|
}
|
|
|
|
async function publishChanges() {
|
|
if (state.value !== 'reviewing') return
|
|
|
|
setState('publishing')
|
|
try {
|
|
await publish_shared_instance(props.instance.id, selectedConfigPaths.value)
|
|
emit('published')
|
|
} catch (error) {
|
|
handlePublishError(error)
|
|
} finally {
|
|
setState('idle')
|
|
}
|
|
}
|
|
|
|
function finishReview() {
|
|
if (state.value === 'reviewing') {
|
|
setState('idle')
|
|
}
|
|
}
|
|
|
|
function handlePublishError(error: unknown) {
|
|
if (isSharedInstanceUnavailableError(error)) {
|
|
notifySharedInstanceUnavailable(getSharedInstanceUnavailableReason(error))
|
|
emit('published')
|
|
return
|
|
}
|
|
|
|
notifySharedInstanceError(error)
|
|
}
|
|
|
|
function setState(nextState: SharedInstancePublishState) {
|
|
state.value = nextState
|
|
emit('state-change', nextState)
|
|
}
|
|
|
|
const messages = defineMessages({
|
|
header: {
|
|
id: 'instance.shared-instance.publish-review.header',
|
|
defaultMessage: 'Review changes',
|
|
},
|
|
admonitionHeader: {
|
|
id: 'instance.shared-instance.publish-review.admonition-header',
|
|
defaultMessage: 'Push update to players',
|
|
},
|
|
description: {
|
|
id: 'instance.shared-instance.publish-review.description',
|
|
defaultMessage:
|
|
'Review the content changes and choose any config files to include in this update.',
|
|
},
|
|
publishButton: {
|
|
id: 'instance.shared-instance.publish-review.publish-button',
|
|
defaultMessage: 'Push update',
|
|
},
|
|
addedLabel: {
|
|
id: 'instance.shared-instance.publish-review.added-label',
|
|
defaultMessage: 'Added',
|
|
},
|
|
removedLabel: {
|
|
id: 'instance.shared-instance.publish-review.removed-label',
|
|
defaultMessage: 'Removed',
|
|
},
|
|
configTitle: {
|
|
id: 'instance.shared-instance.publish-review.config-title-v2',
|
|
defaultMessage: 'Select config files',
|
|
},
|
|
})
|
|
|
|
defineExpose({ show })
|
|
</script>
|