Files
AstralRinth/packages/ui/src/components/project/ProjectSidebarServerInfo.vue
T
aecsocket 155f4091a6 Tweak search sorting (#5464)
* Tweak search sorting

* Tweak search sorting

* fix ping impl

* remove port field, add server regions

* fix compile

* fix tests

* update frontend banner upload size limit

* feat: use server project region instead of country

* remove java and bedrock port in frontend

* add helper text

* allow filtering by if server is online

* add server status online offline filter

* use region in instance

* pre-collapse status in app discovery

* pnpm prepr

* remove server discovery flag

* add servers into mobile nav tabs

* parse port from address if present

---------

Co-authored-by: tdgao <mr.trumgao@gmail.com>
2026-03-03 23:20:48 +01:00

231 lines
7.3 KiB
Vue

<template>
<div v-if="hasContent" class="flex flex-col gap-3">
<h2 class="text-lg m-0">{{ formatMessage(messages.title) }}</h2>
<div
v-if="ipAddress"
v-tooltip="`Copy Java IP: ${ipAddress}`"
class="bg-button-bg flex gap-2 justify-between rounded-2xl items-center px-3 pr-1.5 h-12 cursor-pointer hover:bg-button-bg-hover hover:brightness-125 transition-all active:scale-95"
@click="handleCopyIP"
>
<div class="font-semibold truncate">
{{ ipAddress }}
</div>
<div class="w-9 h-9 grid place-content-center">
<CopyIcon class="shrink-0" />
</div>
</div>
<section v-if="requiredContent" class="flex flex-col gap-2">
<h3 class="text-primary text-base m-0">Required content</h3>
<ServerModpackContentCard
:name="requiredContent.name"
:version-number="requiredContent.versionNumber ?? ''"
:icon="requiredContent.icon"
:onclick-name="requiredContent.onclickName"
:onclick-version="requiredContent.onclickVersion"
:onclick-download="requiredContent.onclickDownload"
:show-custom-modpack-tooltip="requiredContent.showCustomModpackTooltip"
/>
</section>
<section v-if="recommendedVersions.length" class="flex flex-col gap-2">
<h3 class="text-primary text-base m-0">Minecraft: Java Edition</h3>
<div class="flex flex-wrap gap-1.5">
<TagItem
v-for="version in formatVersionsForDisplay(recommendedVersions, tags.gameVersions)"
:key="`recommended-tag-${version}`"
>
{{ version }}
<template v-if="supportedVersions.length > 0"> (Recommended) </template>
</TagItem>
<TagItem
v-for="version in formatVersionsForDisplay(supportedVersionsList, tags.gameVersions)"
:key="`supported-tag-${version}`"
>
{{ version }}
</TagItem>
<TagItem
v-for="loader in loaders ?? []"
:key="`loader-${loader}`"
class="border !border-solid border-surface-5"
:style="`--_color: var(--color-platform-${loader})`"
>
<component :is="getLoaderIcon(loader)" v-if="getLoaderIcon(loader)" />
<FormattedTag :tag="loader" enforce-type="loader" />
</TagItem>
</div>
</section>
<section v-if="props.ping !== undefined || region" class="flex flex-col gap-2">
<h3 class="text-primary text-base m-0">Region</h3>
<div class="flex flex-wrap gap-1.5 items-center">
<ServerRegion v-if="region" :region="region" />
<ServerPing :ping="props.ping" :status-online="props.statusOnline" />
</div>
</section>
<section v-if="languages.length > 0" class="flex flex-col gap-2">
<h3 class="text-primary text-base m-0">Languages</h3>
<div class="flex flex-wrap gap-1.5">
<TagItem v-for="language in languages" :key="`${language}`">
{{ languageDisplay.find((l) => l.value === language)?.label ?? language }}
</TagItem>
</div>
</section>
</div>
</template>
<script setup lang="ts">
import type { Labrinth } from '@modrinth/api-client'
import { CopyIcon, getLoaderIcon } from '@modrinth/assets'
import { formatVersionsForDisplay, type GameVersionTag, type PlatformTag } from '@modrinth/utils'
import { computed } from 'vue'
import { defineMessages, useVIntl } from '../../composables'
import { injectNotificationManager } from '../../providers'
import FormattedTag from '../base/FormattedTag.vue'
import TagItem from '../base/TagItem.vue'
import ServerModpackContentCard from './server/ServerModpackContentCard.vue'
import ServerPing from './server/ServerPing.vue'
import ServerRegion from './server/ServerRegion.vue'
interface RequiredContent {
name: string
versionNumber?: string
icon?: string
onclickName?: () => void
onclickVersion?: () => void
onclickDownload?: () => void
showCustomModpackTooltip?: boolean
}
interface Props {
projectV3: Labrinth.Projects.v3.Project | null
tags: {
gameVersions: GameVersionTag[]
loaders: PlatformTag[]
}
requiredContent?: RequiredContent | null
recommendedVersion?: string | null
supportedVersions?: string[]
loaders?: string[]
ping?: number
statusOnline?: boolean
}
const props = withDefaults(defineProps<Props>(), {
requiredContent: null,
recommendedVersion: null,
supportedVersions: () => [],
loaders: () => [],
ping: undefined,
})
const ipAddress = computed(() => props.projectV3?.minecraft_java_server?.address ?? '')
const languages = computed(() => props.projectV3?.minecraft_server?.languages ?? [])
const region = computed(() => props.projectV3?.minecraft_server?.region)
const recommendedVersions = computed(() => {
if (props.recommendedVersion) return [props.recommendedVersion]
const content = props.projectV3?.minecraft_java_server?.content
if (content?.kind === 'vanilla' && content.recommended_game_version) {
return [content.recommended_game_version]
}
return []
})
const hasContent = computed(
() =>
!!ipAddress.value ||
!!props.requiredContent ||
recommendedVersions.value.length > 0 ||
supportedVersionsList.value.length > 0 ||
languages.value.length > 0 ||
props.ping !== undefined,
)
const supportedVersionsList = computed(() => {
if (props.supportedVersions.length > 0) return props.supportedVersions
const content = props.projectV3?.minecraft_java_server?.content
if (content?.kind === 'vanilla' && content.supported_game_versions?.length) {
return content.supported_game_versions.filter((v): v is string => !!v)
}
return []
})
const { addNotification } = injectNotificationManager()
const { formatMessage } = useVIntl()
function handleCopyIP() {
navigator.clipboard.writeText(ipAddress.value).then(() => {
addNotification({
type: 'success',
title: formatMessage(messages.copied),
text: formatMessage(messages.copiedText),
})
})
}
const languageDisplay = [
{ value: 'en', label: 'English' },
{ value: 'es', label: 'Spanish' },
{ value: 'pt', label: 'Portuguese' },
{ value: 'fr', label: 'French' },
{ value: 'de', label: 'German' },
{ value: 'it', label: 'Italian' },
{ value: 'nl', label: 'Dutch' },
{ value: 'ru', label: 'Russian' },
{ value: 'uk', label: 'Ukrainian' },
{ value: 'pl', label: 'Polish' },
{ value: 'cs', label: 'Czech' },
{ value: 'sk', label: 'Slovak' },
{ value: 'hu', label: 'Hungarian' },
{ value: 'ro', label: 'Romanian' },
{ value: 'bg', label: 'Bulgarian' },
{ value: 'hr', label: 'Croatian' },
{ value: 'sr', label: 'Serbian' },
{ value: 'el', label: 'Greek' },
{ value: 'tr', label: 'Turkish' },
{ value: 'ar', label: 'Arabic' },
{ value: 'he', label: 'Hebrew' },
{ value: 'hi', label: 'Hindi' },
{ value: 'bn', label: 'Bengali' },
{ value: 'ur', label: 'Urdu' },
{ value: 'zh', label: 'Chinese' },
{ value: 'ja', label: 'Japanese' },
{ value: 'ko', label: 'Korean' },
{ value: 'th', label: 'Thai' },
{ value: 'vi', label: 'Vietnamese' },
{ value: 'id', label: 'Indonesian' },
{ value: 'ms', label: 'Malay' },
{ value: 'tl', label: 'Filipino' },
{ value: 'sv', label: 'Swedish' },
{ value: 'no', label: 'Norwegian' },
{ value: 'da', label: 'Danish' },
{ value: 'fi', label: 'Finnish' },
{ value: 'lt', label: 'Lithuanian' },
{ value: 'lv', label: 'Latvian' },
{ value: 'et', label: 'Estonian' },
]
const messages = defineMessages({
copied: {
id: `project.about.server.copied`,
defaultMessage: 'Copied!',
},
copiedText: {
id: `project.about.server.copiedText`,
defaultMessage: 'IP address copied to clipboard',
},
title: {
id: `project.about.server.title`,
defaultMessage: 'Server details',
},
latency: {
id: `project.about.server.latency`,
defaultMessage: 'Latency',
},
})
</script>