mirror of
https://github.com/modrinth/code.git
synced 2026-08-25 00:55:25 +00:00
feat: add license info to app + prefetch license details (#6831)
* update project sidebar details to use common component + prefetch license * prepr * fix updated date needing to fetch versions
This commit is contained in:
@@ -31,7 +31,7 @@
|
||||
<div
|
||||
v-if="!hideHeader"
|
||||
data-tauri-drag-region
|
||||
class="grid grid-cols-[auto_min-content] items-center gap-4 p-6 border-solid border-0 border-b-[1px] border-surface-5 max-w-full"
|
||||
class="grid grid-cols-[1fr_auto] items-center gap-4 p-6 border-solid border-0 border-b-[1px] border-surface-5 max-w-full"
|
||||
>
|
||||
<div class="flex text-wrap break-words items-center gap-3 min-w-0">
|
||||
<slot name="title">
|
||||
@@ -40,16 +40,19 @@
|
||||
</span>
|
||||
</slot>
|
||||
</div>
|
||||
<ButtonStyled v-if="closable" circular>
|
||||
<button
|
||||
v-tooltip="closeLabel"
|
||||
:aria-label="closeLabel"
|
||||
:disabled="disableClose"
|
||||
@click="hide"
|
||||
>
|
||||
<XIcon aria-hidden="true" />
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<div class="flex items-center gap-2">
|
||||
<slot name="header-actions" />
|
||||
<ButtonStyled v-if="closable" circular>
|
||||
<button
|
||||
v-tooltip="closeLabel"
|
||||
:aria-label="closeLabel"
|
||||
:disabled="disableClose"
|
||||
@click="hide"
|
||||
>
|
||||
<XIcon aria-hidden="true" />
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ButtonStyled
|
||||
|
||||
@@ -1,7 +1,19 @@
|
||||
<template>
|
||||
<div class="flex flex-col gap-3">
|
||||
<NewModal
|
||||
ref="modalLicense"
|
||||
:header="project.license.name ? project.license.name : formatMessage(messages.licenseTitle)"
|
||||
>
|
||||
<template #title>
|
||||
<Avatar :src="project.icon_url" :alt="project.title" class="icon" size="32px" no-shadow />
|
||||
<span class="text-lg font-extrabold text-contrast">
|
||||
{{ project.license.name ? project.license.name : formatMessage(messages.licenseTitle) }}
|
||||
</span>
|
||||
</template>
|
||||
<div class="markdown-body" v-html="licenseHtml" />
|
||||
</NewModal>
|
||||
<h2 class="text-lg m-0">{{ formatMessage(commonMessages.detailsLabel) }}</h2>
|
||||
<div class="flex flex-col gap-3 font-semibold [&>div]:flex [&>div]:gap-2 [&>div]:items-center">
|
||||
<div class="flex flex-col gap-3 [&>div]:flex [&>div]:gap-2 [&>div]:items-center">
|
||||
<div v-if="!hideLicense">
|
||||
<BookTextIcon aria-hidden="true" />
|
||||
<div>
|
||||
@@ -15,13 +27,12 @@
|
||||
rel="noopener nofollow ugc"
|
||||
>
|
||||
{{ licenseIdDisplay }}
|
||||
<ExternalIcon aria-hidden="true" class="external-icon ml-1 mt-[-1px] inline" />
|
||||
</a>
|
||||
<span
|
||||
v-else-if="
|
||||
project.license.id === 'LicenseRef-All-Rights-Reserved' ||
|
||||
!project.license.id.includes('LicenseRef')
|
||||
"
|
||||
v-else-if="canOpenLicenseModal"
|
||||
class="text-link hover:underline cursor-pointer"
|
||||
@mouseenter="enableLicenseFetch"
|
||||
@click="(event) => openLicenseModal(event)"
|
||||
>
|
||||
{{ licenseIdDisplay }}
|
||||
</span>
|
||||
@@ -67,7 +78,10 @@
|
||||
}}
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="hasVersions && project.updated" v-tooltip="formatDateTime(project.updated)">
|
||||
<div
|
||||
v-if="project.versions.length > 0 && project.updated"
|
||||
v-tooltip="formatDateTime(project.updated)"
|
||||
>
|
||||
<VersionIcon aria-hidden="true" />
|
||||
<div>
|
||||
{{
|
||||
@@ -80,23 +94,22 @@
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import type { Labrinth } from '@modrinth/api-client'
|
||||
import {
|
||||
BookTextIcon,
|
||||
CalendarIcon,
|
||||
ExternalIcon,
|
||||
HeartIcon,
|
||||
ScaleIcon,
|
||||
VersionIcon,
|
||||
} from '@modrinth/assets'
|
||||
import { capitalizeString } from '@modrinth/utils'
|
||||
import { computed } from 'vue'
|
||||
import { BookTextIcon, CalendarIcon, HeartIcon, ScaleIcon, VersionIcon } from '@modrinth/assets'
|
||||
import { capitalizeString, renderString } from '@modrinth/utils'
|
||||
import { useQuery } from '@tanstack/vue-query'
|
||||
import { computed, ref, useTemplateRef } from 'vue'
|
||||
|
||||
import { useFormatDateTime, useRelativeTime } from '../../composables'
|
||||
import { defineMessages, useVIntl } from '../../composables/i18n'
|
||||
import { injectModrinthClient } from '../../providers'
|
||||
import { commonMessages } from '../../utils/common-messages'
|
||||
import { IntlFormatted } from '../base'
|
||||
import { Avatar, IntlFormatted } from '../base'
|
||||
import { NewModal } from '../modal'
|
||||
|
||||
const LICENSE_STALE_TIME = 1000 * 60 * 10
|
||||
|
||||
const { formatMessage } = useVIntl()
|
||||
const { labrinth } = injectModrinthClient()
|
||||
const formatRelativeTime = useRelativeTime()
|
||||
const formatDateTime = useFormatDateTime({
|
||||
timeStyle: 'short',
|
||||
@@ -106,11 +119,32 @@ const formatDateTime = useFormatDateTime({
|
||||
const props = defineProps<{
|
||||
project: Labrinth.Projects.v2.Project
|
||||
linkTarget: string
|
||||
hasVersions: boolean
|
||||
hideLicense?: boolean
|
||||
showFollowers?: boolean
|
||||
}>()
|
||||
|
||||
const modalLicense = useTemplateRef('modalLicense')
|
||||
const licenseFetchEnabled = ref(false)
|
||||
|
||||
const messages = defineMessages({
|
||||
licensed: {
|
||||
id: 'project.about.details.licensed',
|
||||
defaultMessage: 'Licensed {license}',
|
||||
},
|
||||
licenseErrorMessage: {
|
||||
id: 'project.license.error',
|
||||
defaultMessage: 'License text could not be retrieved.',
|
||||
},
|
||||
licenseTitle: {
|
||||
id: 'project.license.title',
|
||||
defaultMessage: 'License',
|
||||
},
|
||||
loadingLicenseText: {
|
||||
id: 'project.license.loading',
|
||||
defaultMessage: 'Loading license text...',
|
||||
},
|
||||
})
|
||||
|
||||
const createdDate = computed(() =>
|
||||
props.project.published ? formatRelativeTime(props.project.published) : 'unknown',
|
||||
)
|
||||
@@ -124,8 +158,10 @@ const updatedDate = computed(() =>
|
||||
props.project.updated ? formatRelativeTime(props.project.updated) : 'unknown',
|
||||
)
|
||||
|
||||
const licenseId = computed(() => props.project.license.id)
|
||||
|
||||
const licenseIdDisplay = computed(() => {
|
||||
const id = props.project.license.id
|
||||
const id = licenseId.value
|
||||
|
||||
if (id === 'LicenseRef-All-Rights-Reserved') {
|
||||
return 'ARR'
|
||||
@@ -136,10 +172,47 @@ const licenseIdDisplay = computed(() => {
|
||||
}
|
||||
})
|
||||
|
||||
const messages = defineMessages({
|
||||
licensed: {
|
||||
id: 'project.about.details.licensed',
|
||||
defaultMessage: 'Licensed {license}',
|
||||
},
|
||||
const canOpenLicenseModal = computed(() => {
|
||||
if (props.hideLicense || props.project.license.url) {
|
||||
return false
|
||||
}
|
||||
|
||||
const id = licenseId.value
|
||||
return id === 'LicenseRef-All-Rights-Reserved' || !id.includes('LicenseRef')
|
||||
})
|
||||
|
||||
const { data: licenseBody, isError: isLicenseError } = useQuery({
|
||||
queryKey: computed(() => ['license', 'v2', licenseId.value] as const),
|
||||
queryFn: async () => {
|
||||
const text = await labrinth.tags_v2.getLicenseText(licenseId.value)
|
||||
return text.body
|
||||
},
|
||||
enabled: computed(() => canOpenLicenseModal.value && licenseFetchEnabled.value),
|
||||
staleTime: LICENSE_STALE_TIME,
|
||||
})
|
||||
|
||||
const licenseHtml = computed(() => {
|
||||
if (licenseBody.value) {
|
||||
return renderString(licenseBody.value)
|
||||
}
|
||||
|
||||
if (isLicenseError.value || licenseBody.value === '') {
|
||||
return renderString(formatMessage(messages.licenseErrorMessage))
|
||||
}
|
||||
|
||||
return formatMessage(messages.loadingLicenseText)
|
||||
})
|
||||
|
||||
function enableLicenseFetch() {
|
||||
if (!canOpenLicenseModal.value) {
|
||||
return
|
||||
}
|
||||
|
||||
licenseFetchEnabled.value = true
|
||||
}
|
||||
|
||||
function openLicenseModal(event?: MouseEvent) {
|
||||
enableLicenseFetch()
|
||||
modalLicense.value?.show(event)
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -3101,6 +3101,15 @@
|
||||
"project.follower-count-tooltip": {
|
||||
"defaultMessage": "{count, number} {count, plural, one {follower} other {followers}}"
|
||||
},
|
||||
"project.license.error": {
|
||||
"defaultMessage": "License text could not be retrieved."
|
||||
},
|
||||
"project.license.loading": {
|
||||
"defaultMessage": "Loading license text..."
|
||||
},
|
||||
"project.license.title": {
|
||||
"defaultMessage": "License"
|
||||
},
|
||||
"project.online-player-count": {
|
||||
"defaultMessage": "{count, number} online"
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user