mirror of
https://github.com/modrinth/code.git
synced 2026-09-02 13:05:50 +00:00
fix: qa
This commit is contained in:
@@ -54,7 +54,6 @@ export const DEFAULT_FEATURE_FLAGS = validateValues({
|
|||||||
showModeratorProjectMemberUi: false,
|
showModeratorProjectMemberUi: false,
|
||||||
showModeratorPrivateMessageHighlight: true,
|
showModeratorPrivateMessageHighlight: true,
|
||||||
archonApiStaging: false,
|
archonApiStaging: false,
|
||||||
showHostingAccessInstanceAuditLog: false,
|
|
||||||
versionDevInfoCollapsed: true,
|
versionDevInfoCollapsed: true,
|
||||||
alwaysShowVersionDevInfo: false,
|
alwaysShowVersionDevInfo: false,
|
||||||
} as const)
|
} as const)
|
||||||
|
|||||||
@@ -9,74 +9,22 @@ import { useQueryClient } from '@tanstack/vue-query'
|
|||||||
const client = injectModrinthClient()
|
const client = injectModrinthClient()
|
||||||
const { server, serverId } = injectModrinthServerContext()
|
const { server, serverId } = injectModrinthServerContext()
|
||||||
const queryClient = useQueryClient()
|
const queryClient = useQueryClient()
|
||||||
const flags = useFeatureFlags()
|
|
||||||
const ACTION_LOG_PAGE_SIZE = 200
|
|
||||||
const ACTION_LOG_SORT_DIRECTION = 'desc'
|
|
||||||
const actionLogDateFilter = defaultActionLogDateFilter()
|
|
||||||
|
|
||||||
await Promise.allSettled([
|
try {
|
||||||
queryClient.ensureQueryData({
|
await queryClient.ensureQueryData({
|
||||||
queryKey: ['servers', 'users', 'v1', serverId],
|
queryKey: ['servers', 'users', 'v1', serverId],
|
||||||
queryFn: () => client.archon.server_users_v1.list(serverId),
|
queryFn: () => client.archon.server_users_v1.list(serverId),
|
||||||
staleTime: 30_000,
|
staleTime: 30_000,
|
||||||
}),
|
})
|
||||||
queryClient.ensureQueryData({
|
} catch {
|
||||||
queryKey: ['servers', 'v1', 'detail', serverId],
|
// Let mounted layouts' useQuery surface errors; do not fail route setup.
|
||||||
queryFn: () => client.archon.servers_v1.get(serverId),
|
}
|
||||||
staleTime: 30_000,
|
|
||||||
}),
|
|
||||||
queryClient.ensureInfiniteQueryData({
|
|
||||||
queryKey: [
|
|
||||||
'servers',
|
|
||||||
'action-log',
|
|
||||||
'v1',
|
|
||||||
'infinite',
|
|
||||||
serverId,
|
|
||||||
null,
|
|
||||||
actionLogDateFilter.min_datetime,
|
|
||||||
actionLogDateFilter.max_datetime,
|
|
||||||
ACTION_LOG_SORT_DIRECTION,
|
|
||||||
],
|
|
||||||
queryFn: ({ pageParam = 0 }) => {
|
|
||||||
const offset = typeof pageParam === 'number' ? pageParam : 0
|
|
||||||
return client.archon.actions_v1.list(serverId, {
|
|
||||||
limit: ACTION_LOG_PAGE_SIZE,
|
|
||||||
offset,
|
|
||||||
order: ACTION_LOG_SORT_DIRECTION,
|
|
||||||
...actionLogDateFilter,
|
|
||||||
})
|
|
||||||
},
|
|
||||||
getNextPageParam: (lastPage) =>
|
|
||||||
typeof lastPage.next_offset === 'number' ? lastPage.next_offset : undefined,
|
|
||||||
initialPageParam: 0,
|
|
||||||
staleTime: 30_000,
|
|
||||||
}),
|
|
||||||
])
|
|
||||||
|
|
||||||
useHead({
|
useHead({
|
||||||
title: computed(() => `Access - ${server.value?.name ?? 'Server'} - Modrinth`),
|
title: computed(() => `Access - ${server.value?.name ?? 'Server'} - Modrinth`),
|
||||||
})
|
})
|
||||||
|
|
||||||
function defaultActionLogDateFilter() {
|
|
||||||
const endDate = new Date()
|
|
||||||
const startDate = new Date(endDate)
|
|
||||||
startDate.setDate(startDate.getDate() - 6)
|
|
||||||
|
|
||||||
return {
|
|
||||||
min_datetime: startOfDay(startDate).toISOString(),
|
|
||||||
max_datetime: endOfDay(endDate).toISOString(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function startOfDay(date: Date) {
|
|
||||||
return new Date(date.getFullYear(), date.getMonth(), date.getDate())
|
|
||||||
}
|
|
||||||
|
|
||||||
function endOfDay(date: Date) {
|
|
||||||
return new Date(date.getFullYear(), date.getMonth(), date.getDate(), 23, 59, 59, 999)
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<ServersManageAccessPage :show-audit-log-instances="flags.showHostingAccessInstanceAuditLog" />
|
<ServersManageAccessPage />
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -96,9 +96,13 @@ async function loadContentSummary(
|
|||||||
index: number,
|
index: number,
|
||||||
): Promise<ContentSummary> {
|
): Promise<ContentSummary> {
|
||||||
try {
|
try {
|
||||||
const content = await client.archon.content_v1.getAddons(serverId, world.id, {
|
const content = await queryClient.fetchQuery({
|
||||||
addons: true,
|
queryKey: ['content', 'list', 'v1', serverId, world.id],
|
||||||
updates: false,
|
queryFn: () =>
|
||||||
|
client.archon.content_v1.getAddons(serverId, world.id, {
|
||||||
|
from_modpack: false,
|
||||||
|
}),
|
||||||
|
staleTime: 0,
|
||||||
})
|
})
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -106,13 +110,35 @@ async function loadContentSummary(
|
|||||||
loader: content.modloader ?? world.content?.modloader ?? null,
|
loader: content.modloader ?? world.content?.modloader ?? null,
|
||||||
loaderVersion: content.modloader_version ?? world.content?.modloader_version ?? null,
|
loaderVersion: content.modloader_version ?? world.content?.modloader_version ?? null,
|
||||||
linkedModpack: getLinkedModpack(content.modpack),
|
linkedModpack: getLinkedModpack(content.modpack),
|
||||||
installedContentCount: content.addons?.length ?? 0,
|
installedContentCount: await getInstalledContentCount(world.id, content),
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
return createDummyContentSummary(world, index)
|
return createDummyContentSummary(world, index)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function getInstalledContentCount(
|
||||||
|
worldId: string,
|
||||||
|
content: Archon.Content.v1.Addons,
|
||||||
|
): Promise<number> {
|
||||||
|
const addonCount = content.addons?.length ?? 0
|
||||||
|
if (!content.modpack) return addonCount
|
||||||
|
|
||||||
|
try {
|
||||||
|
const modpackContent = await queryClient.fetchQuery({
|
||||||
|
queryKey: ['content', 'list', 'v1', serverId, worldId, 'modpack'],
|
||||||
|
queryFn: () =>
|
||||||
|
client.archon.content_v1.getAddons(serverId, worldId, {
|
||||||
|
from_modpack: true,
|
||||||
|
}),
|
||||||
|
staleTime: 0,
|
||||||
|
})
|
||||||
|
return addonCount + (modpackContent.addons?.length ?? 0)
|
||||||
|
} catch {
|
||||||
|
return addonCount
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function toWorldSlot(world: Archon.Servers.v1.WorldFull, content: ContentSummary): WorldSlot {
|
function toWorldSlot(world: Archon.Servers.v1.WorldFull, content: ContentSummary): WorldSlot {
|
||||||
return {
|
return {
|
||||||
type: 'world',
|
type: 'world',
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ const props = defineProps<{
|
|||||||
ariaLabel?: string
|
ariaLabel?: string
|
||||||
belowModal?: boolean
|
belowModal?: boolean
|
||||||
hideWhenModalOpen?: boolean
|
hideWhenModalOpen?: boolean
|
||||||
|
toolbarMaxWidth?: string
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const INTERCOM_BUBBLE_GAP = 8
|
const INTERCOM_BUBBLE_GAP = 8
|
||||||
@@ -46,6 +47,11 @@ const barStyle = computed(() => ({
|
|||||||
'--floating-action-bar-left-offset': leftOffset.value,
|
'--floating-action-bar-left-offset': leftOffset.value,
|
||||||
'--floating-action-bar-right-offset': rightOffset.value,
|
'--floating-action-bar-right-offset': rightOffset.value,
|
||||||
}))
|
}))
|
||||||
|
const toolbarStyle = computed(() =>
|
||||||
|
props.toolbarMaxWidth
|
||||||
|
? { '--floating-action-bar-toolbar-max-width': props.toolbarMaxWidth }
|
||||||
|
: undefined,
|
||||||
|
)
|
||||||
|
|
||||||
function checkCompact() {
|
function checkCompact() {
|
||||||
const el = toolbarEl.value
|
const el = toolbarEl.value
|
||||||
@@ -203,8 +209,9 @@ onUnmounted(() => {
|
|||||||
ref="toolbarEl"
|
ref="toolbarEl"
|
||||||
role="toolbar"
|
role="toolbar"
|
||||||
:aria-label="ariaLabel"
|
:aria-label="ariaLabel"
|
||||||
class="relative overflow-clip flex items-center gap-1.5 rounded-[20px] bg-surface-3 border border-surface-5 border-solid mx-auto md:max-w-[60vw] px-3 py-2.5 shadow-[0px_1px_3px_0px_rgba(0,0,0,0.3),0px_6px_10px_0px_rgba(0,0,0,0.15)]"
|
class="floating-action-toolbar relative overflow-clip flex items-center gap-1.5 rounded-[20px] bg-surface-3 border border-surface-5 border-solid mx-auto px-3 py-2.5 shadow-[0px_1px_3px_0px_rgba(0,0,0,0.3),0px_6px_10px_0px_rgba(0,0,0,0.15)]"
|
||||||
:class="{ 'bar-compact': compact }"
|
:class="{ 'bar-compact': compact }"
|
||||||
|
:style="toolbarStyle"
|
||||||
>
|
>
|
||||||
<slot />
|
<slot />
|
||||||
</div>
|
</div>
|
||||||
@@ -220,6 +227,12 @@ onUnmounted(() => {
|
|||||||
transition: bottom 0.25s ease-in-out;
|
transition: bottom 0.25s ease-in-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
.floating-action-toolbar {
|
||||||
|
max-width: var(--floating-action-bar-toolbar-max-width, 60vw);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.floating-action-bar-enter-active {
|
.floating-action-bar-enter-active {
|
||||||
transition:
|
transition:
|
||||||
transform 0.25s cubic-bezier(0.15, 1.4, 0.64, 0.96),
|
transform 0.25s cubic-bezier(0.15, 1.4, 0.64, 0.96),
|
||||||
|
|||||||
+1
-1
@@ -5,7 +5,7 @@
|
|||||||
class="flex flex-col gap-2"
|
class="flex flex-col gap-2"
|
||||||
>
|
>
|
||||||
<span class="font-semibold text-contrast">
|
<span class="font-semibold text-contrast">
|
||||||
{{ formatMessage(messages.worldNameLabel) }} <span class="text-red">*</span>
|
{{ formatMessage(messages.worldNameLabel) }}
|
||||||
</span>
|
</span>
|
||||||
<StyledInput
|
<StyledInput
|
||||||
v-model="worldName"
|
v-model="worldName"
|
||||||
|
|||||||
@@ -31,42 +31,6 @@
|
|||||||
row-key="id"
|
row-key="id"
|
||||||
:row-transition-name="rowTransitionName"
|
:row-transition-name="rowTransitionName"
|
||||||
>
|
>
|
||||||
<template #header-world="{ column }">
|
|
||||||
<span class="inline-flex min-w-0 max-w-full items-center gap-1 font-semibold">
|
|
||||||
<span class="min-w-0 truncate">{{ column.label }}</span>
|
|
||||||
<Tooltip
|
|
||||||
theme="dismissable-prompt"
|
|
||||||
class="inline-flex shrink-0"
|
|
||||||
:triggers="['hover', 'focus']"
|
|
||||||
:popper-triggers="['hover', 'focus']"
|
|
||||||
popper-class="v-popper--interactive"
|
|
||||||
placement="top"
|
|
||||||
:delay="{ show: 200, hide: 100 }"
|
|
||||||
no-auto-focus
|
|
||||||
>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
:aria-label="formatMessage(messages.instanceTooltipTitle)"
|
|
||||||
class="inline-flex cursor-help items-center justify-center border-0 bg-transparent p-0 text-secondary transition-colors hover:text-contrast"
|
|
||||||
>
|
|
||||||
<UnknownIcon class="size-4" aria-hidden="true" />
|
|
||||||
</button>
|
|
||||||
<template #popper>
|
|
||||||
<div class="grid !w-64 gap-1">
|
|
||||||
<h3 class="m-0 whitespace-nowrap text-base w-full font-bold text-contrast">
|
|
||||||
{{ formatMessage(messages.instanceTooltipTitle) }}
|
|
||||||
</h3>
|
|
||||||
<p
|
|
||||||
class="m-0 text-wrap text-sm w-full font-medium leading-tight text-secondary"
|
|
||||||
>
|
|
||||||
{{ formatMessage(messages.instanceTooltipDescription) }}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</Tooltip>
|
|
||||||
</span>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template #cell-user="{ row: entry }">
|
<template #cell-user="{ row: entry }">
|
||||||
<AutoLink
|
<AutoLink
|
||||||
v-tooltip="actorName(entry)"
|
v-tooltip="actorName(entry)"
|
||||||
@@ -171,7 +135,7 @@
|
|||||||
class="hidden min-h-14 bg-surface-3 @[800px]:grid @[800px]:h-14"
|
class="hidden min-h-14 bg-surface-3 @[800px]:grid @[800px]:h-14"
|
||||||
:class="
|
:class="
|
||||||
showWorldColumn
|
showWorldColumn
|
||||||
? '@[800px]:grid-cols-[18%_52%_20%_10%]'
|
? '@[800px]:grid-cols-[18%_46%_22%_14%]'
|
||||||
: '@[800px]:grid-cols-[26%_58%_16%]'
|
: '@[800px]:grid-cols-[26%_58%_16%]'
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
@@ -185,37 +149,7 @@
|
|||||||
v-if="showWorldColumn"
|
v-if="showWorldColumn"
|
||||||
class="hidden items-center px-2 font-semibold text-secondary @[800px]:flex"
|
class="hidden items-center px-2 font-semibold text-secondary @[800px]:flex"
|
||||||
>
|
>
|
||||||
<span class="inline-flex min-w-0 max-w-full items-center gap-1 font-semibold">
|
<span class="min-w-0 truncate">{{ formatMessage(messages.worldColumn) }}</span>
|
||||||
<span class="min-w-0 truncate">{{ formatMessage(messages.worldColumn) }}</span>
|
|
||||||
<Tooltip
|
|
||||||
theme="dismissable-prompt"
|
|
||||||
class="inline-flex shrink-0"
|
|
||||||
:triggers="['hover', 'focus']"
|
|
||||||
:popper-triggers="['hover', 'focus']"
|
|
||||||
popper-class="v-popper--interactive"
|
|
||||||
placement="top"
|
|
||||||
:delay="{ show: 200, hide: 100 }"
|
|
||||||
no-auto-focus
|
|
||||||
>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
:aria-label="formatMessage(messages.instanceTooltipTitle)"
|
|
||||||
class="inline-flex cursor-help items-center justify-center border-0 bg-transparent p-0 text-secondary transition-colors hover:text-contrast"
|
|
||||||
>
|
|
||||||
<UnknownIcon class="size-4" aria-hidden="true" />
|
|
||||||
</button>
|
|
||||||
<template #popper>
|
|
||||||
<div class="grid !w-64 gap-1">
|
|
||||||
<h3 class="m-0 whitespace-nowrap text-base font-bold text-contrast">
|
|
||||||
{{ formatMessage(messages.instanceTooltipTitle) }}
|
|
||||||
</h3>
|
|
||||||
<p class="m-0 text-wrap text-sm font-medium leading-tight text-secondary">
|
|
||||||
{{ formatMessage(messages.instanceTooltipDescription) }}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</Tooltip>
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="hidden items-center justify-end pl-2 pr-4 font-semibold text-secondary @[800px]:flex"
|
class="hidden items-center justify-end pl-2 pr-4 font-semibold text-secondary @[800px]:flex"
|
||||||
@@ -250,8 +184,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { IntercomBubbleIcon, UnknownIcon } from '@modrinth/assets'
|
import { IntercomBubbleIcon } from '@modrinth/assets'
|
||||||
import { Tooltip } from 'floating-vue'
|
|
||||||
import { computed, nextTick, onBeforeUnmount, onMounted, ref, useSlots, watch } from 'vue'
|
import { computed, nextTick, onBeforeUnmount, onMounted, ref, useSlots, watch } from 'vue'
|
||||||
|
|
||||||
import { useFormatDateTime, useRelativeTime } from '../../../composables'
|
import { useFormatDateTime, useRelativeTime } from '../../../composables'
|
||||||
@@ -324,15 +257,6 @@ const messages = defineMessages({
|
|||||||
id: 'servers.audit-log.column.world',
|
id: 'servers.audit-log.column.world',
|
||||||
defaultMessage: 'Instance',
|
defaultMessage: 'Instance',
|
||||||
},
|
},
|
||||||
instanceTooltipTitle: {
|
|
||||||
id: 'servers.audit-log.column.world.tooltip-title',
|
|
||||||
defaultMessage: 'Coming soon!',
|
|
||||||
},
|
|
||||||
instanceTooltipDescription: {
|
|
||||||
id: 'servers.audit-log.column.world.tooltip-description',
|
|
||||||
defaultMessage:
|
|
||||||
'Server instances are contained environments with their own installed content and world files.',
|
|
||||||
},
|
|
||||||
eventColumn: {
|
eventColumn: {
|
||||||
id: 'servers.audit-log.column.event',
|
id: 'servers.audit-log.column.event',
|
||||||
defaultMessage: 'Actions',
|
defaultMessage: 'Actions',
|
||||||
@@ -417,7 +341,7 @@ const columns = computed<TableColumn<AuditLogTableColumn>[]>(() => {
|
|||||||
{
|
{
|
||||||
key: 'event',
|
key: 'event',
|
||||||
label: formatMessage(messages.eventColumn),
|
label: formatMessage(messages.eventColumn),
|
||||||
width: showWorldColumn.value ? '52%' : '58%',
|
width: showWorldColumn.value ? '46%' : '58%',
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -425,7 +349,7 @@ const columns = computed<TableColumn<AuditLogTableColumn>[]>(() => {
|
|||||||
tableColumns.push({
|
tableColumns.push({
|
||||||
key: 'world',
|
key: 'world',
|
||||||
label: formatMessage(messages.worldColumn),
|
label: formatMessage(messages.worldColumn),
|
||||||
width: '20%',
|
width: '22%',
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -434,7 +358,7 @@ const columns = computed<TableColumn<AuditLogTableColumn>[]>(() => {
|
|||||||
label: formatMessage(messages.timeColumn),
|
label: formatMessage(messages.timeColumn),
|
||||||
align: 'right',
|
align: 'right',
|
||||||
enableSorting: true,
|
enableSorting: true,
|
||||||
width: showWorldColumn.value ? '10%' : '16%',
|
width: showWorldColumn.value ? '14%' : '16%',
|
||||||
})
|
})
|
||||||
|
|
||||||
return tableColumns
|
return tableColumns
|
||||||
|
|||||||
@@ -112,10 +112,6 @@ const props = withDefaults(
|
|||||||
worlds?: HeaderWorld[]
|
worlds?: HeaderWorld[]
|
||||||
powerDisabled?: boolean
|
powerDisabled?: boolean
|
||||||
settingsLabel?: string
|
settingsLabel?: string
|
||||||
showSettingsHint?: boolean
|
|
||||||
settingsHintTitle?: string
|
|
||||||
settingsHintDescription?: string
|
|
||||||
settingsHintDismissLabel?: string
|
|
||||||
actions?: HeaderAction[]
|
actions?: HeaderAction[]
|
||||||
}>(),
|
}>(),
|
||||||
{
|
{
|
||||||
@@ -131,17 +127,12 @@ const props = withDefaults(
|
|||||||
worlds: () => [],
|
worlds: () => [],
|
||||||
powerDisabled: false,
|
powerDisabled: false,
|
||||||
settingsLabel: 'Server settings',
|
settingsLabel: 'Server settings',
|
||||||
showSettingsHint: false,
|
|
||||||
settingsHintTitle: '',
|
|
||||||
settingsHintDescription: '',
|
|
||||||
settingsHintDismissLabel: "Don't show again",
|
|
||||||
actions: () => [],
|
actions: () => [],
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
openSettings: []
|
openSettings: []
|
||||||
dismissSettingsHint: []
|
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const client = injectModrinthClient()
|
const client = injectModrinthClient()
|
||||||
@@ -408,16 +399,8 @@ const settingsAction = computed<HeaderAction>(() => ({
|
|||||||
label: props.settingsLabel,
|
label: props.settingsLabel,
|
||||||
icon: SettingsIcon,
|
icon: SettingsIcon,
|
||||||
labelHidden: true,
|
labelHidden: true,
|
||||||
tooltip: props.showSettingsHint ? undefined : props.settingsLabel,
|
tooltip: props.settingsLabel,
|
||||||
onClick: () => emit('openSettings'),
|
onClick: () => emit('openSettings'),
|
||||||
prompt: {
|
|
||||||
title: props.settingsHintTitle,
|
|
||||||
description: props.settingsHintDescription,
|
|
||||||
dismissLabel: props.settingsHintDismissLabel,
|
|
||||||
shown: props.showSettingsHint,
|
|
||||||
placement: 'bottom-end',
|
|
||||||
onDismiss: () => emit('dismissSettingsHint'),
|
|
||||||
},
|
|
||||||
}))
|
}))
|
||||||
|
|
||||||
const headerActions = computed<HeaderAction[]>(() => [
|
const headerActions = computed<HeaderAction[]>(() => [
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
<FloatingActionBar
|
<FloatingActionBar
|
||||||
:shown="shown"
|
:shown="shown"
|
||||||
:aria-label="formatMessage(messages.ariaLabel)"
|
:aria-label="formatMessage(messages.ariaLabel)"
|
||||||
|
toolbar-max-width="min(1152px, calc(100vw - 3rem))"
|
||||||
hide-when-modal-open
|
hide-when-modal-open
|
||||||
>
|
>
|
||||||
<div class="flex min-w-0 items-center gap-0.5">
|
<div class="flex min-w-0 items-center gap-0.5">
|
||||||
|
|||||||
@@ -170,16 +170,16 @@ const messages = defineMessages({
|
|||||||
},
|
},
|
||||||
resetWorldFilesButton: {
|
resetWorldFilesButton: {
|
||||||
id: 'server.instance-settings.general.reset-world-files.button',
|
id: 'server.instance-settings.general.reset-world-files.button',
|
||||||
defaultMessage: 'Reset world data',
|
defaultMessage: 'Reset world',
|
||||||
},
|
},
|
||||||
resetWorldFilesDescription: {
|
resetWorldFilesDescription: {
|
||||||
id: 'server.instance-settings.general.reset-world-files.description',
|
id: 'server.instance-settings.general.reset-world-files.description',
|
||||||
defaultMessage:
|
defaultMessage:
|
||||||
'Delete the current world data and generate a new one. Your content and files will stay the same.',
|
'Create a fresh world for this instance. Content, files, settings, and backups stay the same.',
|
||||||
},
|
},
|
||||||
resetWorldFilesModalTitle: {
|
resetWorldFilesModalTitle: {
|
||||||
id: 'server.instance-settings.general.reset-world-files.modal.title',
|
id: 'server.instance-settings.general.reset-world-files.modal.title',
|
||||||
defaultMessage: 'Reset world data',
|
defaultMessage: 'Reset world',
|
||||||
},
|
},
|
||||||
resetWorldFilesModalDescription: {
|
resetWorldFilesModalDescription: {
|
||||||
id: 'server.instance-settings.general.reset-world-files.modal.description',
|
id: 'server.instance-settings.general.reset-world-files.modal.description',
|
||||||
@@ -200,12 +200,12 @@ const messages = defineMessages({
|
|||||||
},
|
},
|
||||||
resetEverythingButton: {
|
resetEverythingButton: {
|
||||||
id: 'server.instance-settings.general.reset-everything.button',
|
id: 'server.instance-settings.general.reset-everything.button',
|
||||||
defaultMessage: 'Reset everything',
|
defaultMessage: 'Reset instance',
|
||||||
},
|
},
|
||||||
resetEverythingDescription: {
|
resetEverythingDescription: {
|
||||||
id: 'server.instance-settings.general.reset-everything.description',
|
id: 'server.instance-settings.general.reset-everything.description',
|
||||||
defaultMessage:
|
defaultMessage:
|
||||||
'Reset your instance completely. This removes world data, content, and any configuration. A backup of the previous instance will remain available.',
|
'Recreate this instance from scratch. World data, content, files, and settings are removed. Previous backups will remain available.',
|
||||||
},
|
},
|
||||||
deleteInstanceButton: {
|
deleteInstanceButton: {
|
||||||
id: 'server.instance-settings.general.delete-instance.button',
|
id: 'server.instance-settings.general.delete-instance.button',
|
||||||
@@ -214,7 +214,7 @@ const messages = defineMessages({
|
|||||||
deleteInstanceDescription: {
|
deleteInstanceDescription: {
|
||||||
id: 'server.instance-settings.general.delete-instance.description',
|
id: 'server.instance-settings.general.delete-instance.description',
|
||||||
defaultMessage:
|
defaultMessage:
|
||||||
'Permanently delete this instance, including its content, files, settings, and backups.',
|
'Permanently delete this instance. World data, content, files, settings, and backups will be removed.',
|
||||||
},
|
},
|
||||||
deleteInstanceModalTitle: {
|
deleteInstanceModalTitle: {
|
||||||
id: 'server.instance-settings.general.delete-instance.modal.title',
|
id: 'server.instance-settings.general.delete-instance.modal.title',
|
||||||
|
|||||||
@@ -63,7 +63,7 @@
|
|||||||
:has-more="hasMoreActionLogEntries"
|
:has-more="hasMoreActionLogEntries"
|
||||||
:loading="isActionLogFiltering"
|
:loading="isActionLogFiltering"
|
||||||
:loading-more="isLoadingMoreActionLogEntries"
|
:loading-more="isLoadingMoreActionLogEntries"
|
||||||
:show-world-column="showAuditLogInstances"
|
show-world-column
|
||||||
:suppress-row-transitions="isActionLogSortTransitioning"
|
:suppress-row-transitions="isActionLogSortTransitioning"
|
||||||
@load-more="loadMoreActionLogEntries"
|
@load-more="loadMoreActionLogEntries"
|
||||||
>
|
>
|
||||||
@@ -145,14 +145,10 @@ type RoleFilter = ServerAccessRole | 'all'
|
|||||||
|
|
||||||
const props = withDefaults(
|
const props = withDefaults(
|
||||||
defineProps<{
|
defineProps<{
|
||||||
showAuditLogInstances?: boolean
|
|
||||||
userProfileLink?: (username: string) => ServerAccessUserProfileLink
|
userProfileLink?: (username: string) => ServerAccessUserProfileLink
|
||||||
}>(),
|
}>(),
|
||||||
{
|
{},
|
||||||
showAuditLogInstances: false,
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
const showAuditLogInstances = computed(() => props.showAuditLogInstances)
|
|
||||||
|
|
||||||
const INVITE_RESEND_COOLDOWN_SECONDS = 2 * 60
|
const INVITE_RESEND_COOLDOWN_SECONDS = 2 * 60
|
||||||
|
|
||||||
@@ -294,7 +290,6 @@ const {
|
|||||||
client,
|
client,
|
||||||
serverId,
|
serverId,
|
||||||
serverFull,
|
serverFull,
|
||||||
showAuditLogInstances,
|
|
||||||
addNotification,
|
addNotification,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -36,7 +36,6 @@ type UseAccessAuditLogOptions = {
|
|||||||
client: AbstractModrinthClient
|
client: AbstractModrinthClient
|
||||||
serverId: string
|
serverId: string
|
||||||
serverFull: ComputedRef<Archon.Servers.v1.ServerFull | null>
|
serverFull: ComputedRef<Archon.Servers.v1.ServerFull | null>
|
||||||
showAuditLogInstances: ComputedRef<boolean>
|
|
||||||
addNotification: AbstractWebNotificationManager['addNotification']
|
addNotification: AbstractWebNotificationManager['addNotification']
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,7 +46,6 @@ export function useAccessAuditLog({
|
|||||||
client,
|
client,
|
||||||
serverId,
|
serverId,
|
||||||
serverFull,
|
serverFull,
|
||||||
showAuditLogInstances,
|
|
||||||
addNotification,
|
addNotification,
|
||||||
}: UseAccessAuditLogOptions) {
|
}: UseAccessAuditLogOptions) {
|
||||||
const { formatMessage } = useVIntl()
|
const { formatMessage } = useVIntl()
|
||||||
@@ -68,9 +66,7 @@ export function useAccessAuditLog({
|
|||||||
const worldOptions = computed(
|
const worldOptions = computed(
|
||||||
() => serverFull.value?.worlds.map((world) => ({ id: world.id, name: world.name })) ?? [],
|
() => serverFull.value?.worlds.map((world) => ({ id: world.id, name: world.name })) ?? [],
|
||||||
)
|
)
|
||||||
const isAuditLogWorldFilterVisible = computed(
|
const isAuditLogWorldFilterVisible = computed(() => worldOptions.value.length > 0)
|
||||||
() => showAuditLogInstances.value && worldOptions.value.length > 0,
|
|
||||||
)
|
|
||||||
|
|
||||||
const worldById = computed(
|
const worldById = computed(
|
||||||
() => new Map(worldOptions.value.map((world) => [world.id, world] as const)),
|
() => new Map(worldOptions.value.map((world) => [world.id, world] as const)),
|
||||||
|
|||||||
@@ -130,12 +130,7 @@
|
|||||||
:worlds="serverFull?.worlds ?? []"
|
:worlds="serverFull?.worlds ?? []"
|
||||||
:power-disabled="!!installError"
|
:power-disabled="!!installError"
|
||||||
:settings-label="formatMessage(messages.serverSettings)"
|
:settings-label="formatMessage(messages.serverSettings)"
|
||||||
:show-settings-hint="showSettingsHint"
|
|
||||||
:settings-hint-title="formatMessage(settingsHintMessages.title)"
|
|
||||||
:settings-hint-description="formatMessage(settingsHintMessages.description)"
|
|
||||||
:settings-hint-dismiss-label="formatMessage(settingsHintMessages.dismiss)"
|
|
||||||
@open-settings="openServerSettingsModal()"
|
@open-settings="openServerSettingsModal()"
|
||||||
@dismiss-settings-hint="dismissSettingsHint"
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<ServerOnboardingPanelPage v-if="isOnboarding" :browse-modpacks="handleBrowseModpacks" />
|
<ServerOnboardingPanelPage v-if="isOnboarding" :browse-modpacks="handleBrowseModpacks" />
|
||||||
@@ -443,21 +438,6 @@ const leaveMessages = defineMessages({
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const settingsHintMessages = defineMessages({
|
|
||||||
title: {
|
|
||||||
id: 'servers.manage.settings-hint.title',
|
|
||||||
defaultMessage: 'Your server settings have moved',
|
|
||||||
},
|
|
||||||
description: {
|
|
||||||
id: 'servers.manage.settings-hint.description',
|
|
||||||
defaultMessage: 'They can now be found here!',
|
|
||||||
},
|
|
||||||
dismiss: {
|
|
||||||
id: 'servers.manage.settings-hint.dismiss',
|
|
||||||
defaultMessage: "Don't show again",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
const instancesHintMessages = defineMessages({
|
const instancesHintMessages = defineMessages({
|
||||||
title: {
|
title: {
|
||||||
id: 'servers.manage.instances-hint.title',
|
id: 'servers.manage.instances-hint.title',
|
||||||
@@ -712,14 +692,6 @@ const errorLog = ref('')
|
|||||||
const errorLogFile = ref('')
|
const errorLogFile = ref('')
|
||||||
const isOnboarding = computed(() => serverData.value?.flows?.intro)
|
const isOnboarding = computed(() => serverData.value?.flows?.intro)
|
||||||
|
|
||||||
const SETTINGS_HINT_KEY = 'server-panel-settings-hint-dismissed'
|
|
||||||
const settingsHintDismissed = useStorage(SETTINGS_HINT_KEY, false)
|
|
||||||
const showSettingsHint = ref(!settingsHintDismissed.value)
|
|
||||||
function dismissSettingsHint() {
|
|
||||||
showSettingsHint.value = false
|
|
||||||
settingsHintDismissed.value = true
|
|
||||||
}
|
|
||||||
|
|
||||||
const INSTANCES_HINT_KEY = 'server-panel-instances-hint-dismissed'
|
const INSTANCES_HINT_KEY = 'server-panel-instances-hint-dismissed'
|
||||||
const instancesHintDismissed = useStorage(INSTANCES_HINT_KEY, false)
|
const instancesHintDismissed = useStorage(INSTANCES_HINT_KEY, false)
|
||||||
const showInstancesHint = ref(!instancesHintDismissed.value)
|
const showInstancesHint = ref(!instancesHintDismissed.value)
|
||||||
|
|||||||
@@ -201,9 +201,13 @@ async function loadContentSummary(
|
|||||||
index: number,
|
index: number,
|
||||||
): Promise<ContentSummary> {
|
): Promise<ContentSummary> {
|
||||||
try {
|
try {
|
||||||
const content = await client.archon.content_v1.getAddons(serverId, world.id, {
|
const content = await queryClient.fetchQuery({
|
||||||
addons: true,
|
queryKey: ['content', 'list', 'v1', serverId, world.id],
|
||||||
updates: false,
|
queryFn: () =>
|
||||||
|
client.archon.content_v1.getAddons(serverId, world.id, {
|
||||||
|
from_modpack: false,
|
||||||
|
}),
|
||||||
|
staleTime: 0,
|
||||||
})
|
})
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -211,13 +215,35 @@ async function loadContentSummary(
|
|||||||
loader: content.modloader ?? world.content?.modloader ?? null,
|
loader: content.modloader ?? world.content?.modloader ?? null,
|
||||||
loaderVersion: content.modloader_version ?? world.content?.modloader_version ?? null,
|
loaderVersion: content.modloader_version ?? world.content?.modloader_version ?? null,
|
||||||
linkedModpack: getLinkedModpack(content.modpack),
|
linkedModpack: getLinkedModpack(content.modpack),
|
||||||
installedContentCount: content.addons?.length ?? 0,
|
installedContentCount: await getInstalledContentCount(world.id, content),
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
return createDummyContentSummary(world, index)
|
return createDummyContentSummary(world, index)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function getInstalledContentCount(
|
||||||
|
worldId: string,
|
||||||
|
content: Archon.Content.v1.Addons,
|
||||||
|
): Promise<number> {
|
||||||
|
const addonCount = content.addons?.length ?? 0
|
||||||
|
if (!content.modpack) return addonCount
|
||||||
|
|
||||||
|
try {
|
||||||
|
const modpackContent = await queryClient.fetchQuery({
|
||||||
|
queryKey: ['content', 'list', 'v1', serverId, worldId, 'modpack'],
|
||||||
|
queryFn: () =>
|
||||||
|
client.archon.content_v1.getAddons(serverId, worldId, {
|
||||||
|
from_modpack: true,
|
||||||
|
}),
|
||||||
|
staleTime: 0,
|
||||||
|
})
|
||||||
|
return addonCount + (modpackContent.addons?.length ?? 0)
|
||||||
|
} catch {
|
||||||
|
return addonCount
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function toWorldSlot(world: Archon.Servers.v1.WorldFull, content: ContentSummary): WorldSlot {
|
function toWorldSlot(world: Archon.Servers.v1.WorldFull, content: ContentSummary): WorldSlot {
|
||||||
return {
|
return {
|
||||||
type: 'world',
|
type: 'world',
|
||||||
|
|||||||
Reference in New Issue
Block a user