mirror of
https://github.com/modrinth/code.git
synced 2026-08-25 00:55:25 +00:00
refactor: introduce PageHeader component (#6629)
* refactor: introduce PageHeader component * feat: split up according to component structure guide * fix: lint * fix: label inconsistencies * feat: refactor PageHeader (again) * refactor: old impls * fix: changes * fix: rescan * fix: prepr * fix: dedupe * fix: rev * fix: lint * fix: lint --------- Co-authored-by: Prospector <6166773+Prospector@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,163 @@
|
||||
<template>
|
||||
<PageHeader :title="organization.name" :summary="organization.description">
|
||||
<template #leading>
|
||||
<Avatar
|
||||
:src="organization.icon_url"
|
||||
:alt="organization.name"
|
||||
:tint-by="organization.id"
|
||||
size="96px"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template #badges>
|
||||
<PageHeaderBadgeItem :icon="OrganizationIcon" class="px-0 text-primary">
|
||||
{{ formatMessage(messages.organizationLabel) }}
|
||||
</PageHeaderBadgeItem>
|
||||
</template>
|
||||
|
||||
<template #metadata>
|
||||
<PageHeaderMetadata>
|
||||
<PageHeaderMetadataNumberItem
|
||||
:icon="UsersIcon"
|
||||
:value="membersCount"
|
||||
:label="formatMessage(messages.membersLabel)"
|
||||
/>
|
||||
<PageHeaderMetadataNumberItem
|
||||
:icon="BoxIcon"
|
||||
:value="projectsCount"
|
||||
:label="formatMessage(messages.projectsLabel)"
|
||||
/>
|
||||
<PageHeaderMetadataNumberItem
|
||||
:icon="DownloadIcon"
|
||||
:value="downloads"
|
||||
:label="formatMessage(messages.downloadsLabel)"
|
||||
:tooltip="formatNumber(downloads)"
|
||||
/>
|
||||
</PageHeaderMetadata>
|
||||
</template>
|
||||
|
||||
<template #actions>
|
||||
<PageHeaderActions>
|
||||
<ButtonStyled v-if="canManage" size="large">
|
||||
<nuxt-link :to="`/organization/${organization.slug}/settings`">
|
||||
<SettingsIcon />
|
||||
{{ formatMessage(messages.manage) }}
|
||||
</nuxt-link>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled circular size="large" type="transparent">
|
||||
<TeleportOverflowMenu
|
||||
:options="moreActions"
|
||||
:tooltip="formatMessage(commonMessages.moreOptionsButton)"
|
||||
:aria-label="formatMessage(commonMessages.moreOptionsButton)"
|
||||
>
|
||||
<MoreVerticalIcon />
|
||||
</TeleportOverflowMenu>
|
||||
</ButtonStyled>
|
||||
</PageHeaderActions>
|
||||
</template>
|
||||
</PageHeader>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {
|
||||
BoxIcon,
|
||||
ClipboardCopyIcon,
|
||||
DownloadIcon,
|
||||
MoreVerticalIcon,
|
||||
OrganizationIcon,
|
||||
SettingsIcon,
|
||||
UsersIcon,
|
||||
} from '@modrinth/assets'
|
||||
import {
|
||||
Avatar,
|
||||
ButtonStyled,
|
||||
commonMessages,
|
||||
defineMessages,
|
||||
PageHeader,
|
||||
PageHeaderActions,
|
||||
PageHeaderBadgeItem,
|
||||
PageHeaderMetadata,
|
||||
PageHeaderMetadataNumberItem,
|
||||
TeleportOverflowMenu,
|
||||
type TeleportOverflowMenuItem,
|
||||
useFormatNumber,
|
||||
useVIntl,
|
||||
} from '@modrinth/ui'
|
||||
import { computed } from 'vue'
|
||||
|
||||
const messages = defineMessages({
|
||||
downloadsLabel: {
|
||||
id: 'organization.label.downloads',
|
||||
defaultMessage: 'downloads',
|
||||
},
|
||||
manage: {
|
||||
id: 'organization.button.manage',
|
||||
defaultMessage: 'Manage',
|
||||
},
|
||||
manageProjects: {
|
||||
id: 'organization.button.manage-projects',
|
||||
defaultMessage: 'Manage projects',
|
||||
},
|
||||
membersLabel: {
|
||||
id: 'organization.label.members',
|
||||
defaultMessage: 'members',
|
||||
},
|
||||
organizationLabel: {
|
||||
id: 'organization.label.organization',
|
||||
defaultMessage: 'Organization',
|
||||
},
|
||||
projectsLabel: {
|
||||
id: 'organization.label.projects',
|
||||
defaultMessage: 'projects',
|
||||
},
|
||||
})
|
||||
|
||||
const props = defineProps<{
|
||||
organization: {
|
||||
id: string
|
||||
name: string
|
||||
slug: string
|
||||
description?: string | null
|
||||
icon_url?: string | null
|
||||
}
|
||||
membersCount: number
|
||||
projectsCount: number
|
||||
downloads: number
|
||||
canManage?: boolean
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
manageProjects: []
|
||||
copyId: []
|
||||
copyPermalink: []
|
||||
}>()
|
||||
|
||||
const { formatMessage } = useVIntl()
|
||||
const formatNumber = useFormatNumber()
|
||||
|
||||
const moreActions = computed<TeleportOverflowMenuItem[]>(() => [
|
||||
{
|
||||
id: 'manage-projects',
|
||||
label: formatMessage(messages.manageProjects),
|
||||
icon: BoxIcon,
|
||||
action: () => emit('manageProjects'),
|
||||
shown: props.canManage,
|
||||
},
|
||||
{
|
||||
divider: true,
|
||||
shown: props.canManage,
|
||||
},
|
||||
{
|
||||
id: 'copy-id',
|
||||
label: formatMessage(commonMessages.copyIdButton),
|
||||
icon: ClipboardCopyIcon,
|
||||
action: () => emit('copyId'),
|
||||
},
|
||||
{
|
||||
id: 'copy-permalink',
|
||||
label: formatMessage(commonMessages.copyPermalinkButton),
|
||||
icon: ClipboardCopyIcon,
|
||||
action: () => emit('copyPermalink'),
|
||||
},
|
||||
])
|
||||
</script>
|
||||
@@ -0,0 +1,127 @@
|
||||
<template>
|
||||
<ButtonStyled size="large" circular>
|
||||
<PopoutMenu
|
||||
v-if="authUser"
|
||||
:tooltip="
|
||||
saved ? formatMessage(commonMessages.savedLabel) : formatMessage(commonMessages.saveButton)
|
||||
"
|
||||
from="top-right"
|
||||
:aria-label="formatMessage(commonMessages.saveButton)"
|
||||
:dropdown-id="`${baseId}-save`"
|
||||
>
|
||||
<BookmarkIcon aria-hidden="true" :fill="saved ? 'currentColor' : 'none'" />
|
||||
<template #menu>
|
||||
<StyledInput
|
||||
v-model="displayCollectionsSearch"
|
||||
:placeholder="formatMessage(commonMessages.searchPlaceholder)"
|
||||
wrapper-class="menu-search"
|
||||
/>
|
||||
<div v-if="filteredCollections.length > 0" class="collections-list text-primary">
|
||||
<Checkbox
|
||||
v-for="option in filteredCollections"
|
||||
:key="option.id"
|
||||
:model-value="option.projects.includes(projectId)"
|
||||
class="popout-checkbox"
|
||||
@update:model-value="() => collectProject(option, projectId)"
|
||||
>
|
||||
{{ option.name }}
|
||||
</Checkbox>
|
||||
</div>
|
||||
|
||||
<div v-else class="menu-text">
|
||||
<p class="popout-text">{{ noCollectionsLabel }}</p>
|
||||
</div>
|
||||
<ButtonStyled>
|
||||
<button class="mx-3 mb-3" @click="createCollection">
|
||||
<PlusIcon aria-hidden="true" />
|
||||
{{ createNewCollectionLabel }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
</template>
|
||||
</PopoutMenu>
|
||||
<nuxt-link
|
||||
v-else
|
||||
v-tooltip="formatMessage(commonMessages.saveButton)"
|
||||
:to="signInRoute"
|
||||
:aria-label="formatMessage(commonMessages.saveButton)"
|
||||
>
|
||||
<BookmarkIcon aria-hidden="true" />
|
||||
</nuxt-link>
|
||||
</ButtonStyled>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { BookmarkIcon, PlusIcon } from '@modrinth/assets'
|
||||
import {
|
||||
ButtonStyled,
|
||||
Checkbox,
|
||||
commonMessages,
|
||||
PopoutMenu,
|
||||
StyledInput,
|
||||
useVIntl,
|
||||
} from '@modrinth/ui'
|
||||
import { computed, ref } from 'vue'
|
||||
import type { RouteLocationRaw } from 'vue-router'
|
||||
|
||||
type CollectionOption = {
|
||||
id: string
|
||||
name: string
|
||||
projects: string[]
|
||||
}
|
||||
|
||||
const props = defineProps<{
|
||||
authUser?: unknown
|
||||
signInRoute: RouteLocationRaw
|
||||
projectId: string
|
||||
collections: CollectionOption[]
|
||||
saved: boolean
|
||||
baseId: string
|
||||
noCollectionsLabel: string
|
||||
createNewCollectionLabel: string
|
||||
collectProject: (option: CollectionOption, projectId: string) => void | Promise<void>
|
||||
createCollection: (event: MouseEvent) => void
|
||||
}>()
|
||||
|
||||
const { formatMessage } = useVIntl()
|
||||
const displayCollectionsSearch = ref('')
|
||||
|
||||
const filteredCollections = computed(() =>
|
||||
props.collections
|
||||
.filter((collection) =>
|
||||
collection.name.toLowerCase().includes(displayCollectionsSearch.value.toLowerCase()),
|
||||
)
|
||||
.slice()
|
||||
.sort((a, b) => a.name.localeCompare(b.name)),
|
||||
)
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.popout-checkbox {
|
||||
padding: var(--gap-sm) var(--gap-md);
|
||||
white-space: nowrap;
|
||||
|
||||
&:hover {
|
||||
filter: brightness(0.95);
|
||||
}
|
||||
}
|
||||
|
||||
.menu-text {
|
||||
padding: 0 var(--gap-md);
|
||||
font-size: var(--font-size-nm);
|
||||
color: var(--color-secondary);
|
||||
}
|
||||
|
||||
.menu-search {
|
||||
margin: var(--gap-sm) var(--gap-md);
|
||||
width: calc(100% - var(--gap-md) * 2);
|
||||
}
|
||||
|
||||
.collections-list {
|
||||
max-height: 40rem;
|
||||
overflow-y: auto;
|
||||
background-color: var(--color-bg);
|
||||
border-radius: var(--radius-md);
|
||||
margin: var(--gap-sm) var(--gap-md);
|
||||
padding: var(--gap-sm);
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,294 @@
|
||||
<template>
|
||||
<PageHeader :title="user.username" :summary="summary">
|
||||
<template #leading>
|
||||
<Avatar
|
||||
:src="user.avatar_url"
|
||||
:alt="user.username"
|
||||
:size="isModrinthUser ? '64px' : '96px'"
|
||||
:tint-by="user.username"
|
||||
circle
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template v-if="isOfficialAccount || showAffiliateBadge" #badges>
|
||||
<PageHeaderBadgeItem
|
||||
v-if="isOfficialAccount"
|
||||
:icon="BadgeCheckIcon"
|
||||
:icon-props="{ fill: 'var(--color-brand-highlight)' }"
|
||||
:tooltip="formatMessage(messages.officialAccount)"
|
||||
class="border-brand-highlight bg-brand-highlight text-brand"
|
||||
>
|
||||
{{ formatMessage(messages.officialAccount) }}
|
||||
</PageHeaderBadgeItem>
|
||||
<PageHeaderBadgeItem
|
||||
v-if="showAffiliateBadge"
|
||||
:icon="AffiliateIcon"
|
||||
class="border-brand-highlight bg-brand-highlight text-brand"
|
||||
>
|
||||
{{ formatMessage(messages.affiliateLabel) }}
|
||||
</PageHeaderBadgeItem>
|
||||
</template>
|
||||
|
||||
<template v-if="$slots.summary" #summary>
|
||||
<slot name="summary" />
|
||||
</template>
|
||||
|
||||
<template v-if="!isModrinthUser" #metadata>
|
||||
<PageHeaderMetadata>
|
||||
<PageHeaderMetadataNumberItem
|
||||
:icon="BoxIcon"
|
||||
:value="projectsCount"
|
||||
:label="formatMessage(messages.profileProjectCountLabel, { count: projectsCount })"
|
||||
/>
|
||||
<PageHeaderMetadataNumberItem
|
||||
:icon="DownloadIcon"
|
||||
:value="downloads"
|
||||
:label="formatMessage(messages.profileDownloadCountLabel, { count: downloads })"
|
||||
:tooltip="downloadsTooltip"
|
||||
/>
|
||||
<PageHeaderMetadataTimeItem
|
||||
:icon="CalendarIcon"
|
||||
:date="user.created"
|
||||
:label="formatMessage(messages.profileJoinedLabel)"
|
||||
:tooltip="joinedTooltip"
|
||||
/>
|
||||
</PageHeaderMetadata>
|
||||
</template>
|
||||
|
||||
<template #actions>
|
||||
<PageHeaderActions>
|
||||
<ButtonStyled v-if="isSelf" size="large">
|
||||
<nuxt-link to="/settings/profile">
|
||||
<EditIcon />
|
||||
{{ formatMessage(commonMessages.editButton) }}
|
||||
</nuxt-link>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled circular size="large" type="transparent">
|
||||
<TeleportOverflowMenu
|
||||
:options="moreActions"
|
||||
:tooltip="formatMessage(commonMessages.moreOptionsButton)"
|
||||
:aria-label="formatMessage(commonMessages.moreOptionsButton)"
|
||||
>
|
||||
<MoreVerticalIcon />
|
||||
</TeleportOverflowMenu>
|
||||
</ButtonStyled>
|
||||
</PageHeaderActions>
|
||||
</template>
|
||||
</PageHeader>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { Labrinth } from '@modrinth/api-client'
|
||||
import {
|
||||
AffiliateIcon,
|
||||
BadgeCheckIcon,
|
||||
BoxIcon,
|
||||
CalendarIcon,
|
||||
ChartIcon,
|
||||
ClipboardCopyIcon,
|
||||
CurrencyIcon,
|
||||
DownloadIcon,
|
||||
EditIcon,
|
||||
InfoIcon,
|
||||
MoreVerticalIcon,
|
||||
ReportIcon,
|
||||
} from '@modrinth/assets'
|
||||
import {
|
||||
Avatar,
|
||||
ButtonStyled,
|
||||
commonMessages,
|
||||
defineMessages,
|
||||
PageHeader,
|
||||
PageHeaderActions,
|
||||
PageHeaderBadgeItem,
|
||||
PageHeaderMetadata,
|
||||
PageHeaderMetadataNumberItem,
|
||||
PageHeaderMetadataTimeItem,
|
||||
TeleportOverflowMenu,
|
||||
type TeleportOverflowMenuItem,
|
||||
useFormatDateTime,
|
||||
useFormatNumber,
|
||||
useVIntl,
|
||||
} from '@modrinth/ui'
|
||||
import { computed } from 'vue'
|
||||
|
||||
const messages = defineMessages({
|
||||
affiliateLabel: {
|
||||
id: 'profile.label.affiliate',
|
||||
defaultMessage: 'Affiliate',
|
||||
},
|
||||
analyticsButton: {
|
||||
id: 'profile.button.analytics',
|
||||
defaultMessage: 'View user analytics',
|
||||
},
|
||||
billingButton: {
|
||||
id: 'profile.button.billing',
|
||||
defaultMessage: 'Manage user billing',
|
||||
},
|
||||
editRoleButton: {
|
||||
id: 'profile.button.edit-role',
|
||||
defaultMessage: 'Edit role',
|
||||
},
|
||||
infoButton: {
|
||||
id: 'profile.button.info',
|
||||
defaultMessage: 'View user details',
|
||||
},
|
||||
officialAccount: {
|
||||
id: 'profile.official-account',
|
||||
defaultMessage: 'Official Modrinth account',
|
||||
},
|
||||
profileJoinedLabel: {
|
||||
id: 'profile.label.joined',
|
||||
defaultMessage: 'Joined',
|
||||
},
|
||||
profileProjectCountLabel: {
|
||||
id: 'profile.label.project-count',
|
||||
defaultMessage: '{count, plural, one {project} other {projects}}',
|
||||
},
|
||||
profileDownloadCountLabel: {
|
||||
id: 'profile.label.download-count',
|
||||
defaultMessage: '{count, plural, one {download} other {downloads}}',
|
||||
},
|
||||
profileManageProjectsButton: {
|
||||
id: 'profile.button.manage-projects',
|
||||
defaultMessage: 'Manage projects',
|
||||
},
|
||||
removeAffiliateButton: {
|
||||
id: 'profile.button.remove-affiliate',
|
||||
defaultMessage: 'Remove as affiliate',
|
||||
},
|
||||
setAffiliateButton: {
|
||||
id: 'profile.button.set-affiliate',
|
||||
defaultMessage: 'Set as affiliate',
|
||||
},
|
||||
})
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
user: Labrinth.Users.v3.User
|
||||
summary?: string | null
|
||||
authUser?: Labrinth.Users.v3.User | null
|
||||
isModrinthUser?: boolean
|
||||
isOfficialAccount?: boolean
|
||||
showAffiliateBadge?: boolean
|
||||
isAffiliate?: boolean
|
||||
isSelf?: boolean
|
||||
isAdmin?: boolean
|
||||
isStaff?: boolean
|
||||
projectsCount?: number
|
||||
downloads?: number
|
||||
}>(),
|
||||
{
|
||||
summary: null,
|
||||
authUser: null,
|
||||
isModrinthUser: false,
|
||||
isOfficialAccount: false,
|
||||
showAffiliateBadge: false,
|
||||
isAffiliate: false,
|
||||
isSelf: false,
|
||||
isAdmin: false,
|
||||
isStaff: false,
|
||||
projectsCount: 0,
|
||||
downloads: 0,
|
||||
},
|
||||
)
|
||||
|
||||
const emit = defineEmits<{
|
||||
manageProjects: []
|
||||
report: []
|
||||
copyId: []
|
||||
copyPermalink: []
|
||||
openBilling: []
|
||||
toggleAffiliate: []
|
||||
openInfo: []
|
||||
openAnalytics: []
|
||||
editRole: []
|
||||
}>()
|
||||
|
||||
const { formatMessage } = useVIntl()
|
||||
|
||||
const formatNumber = useFormatNumber()
|
||||
const formatDateTime = useFormatDateTime({
|
||||
timeStyle: 'short',
|
||||
dateStyle: 'long',
|
||||
})
|
||||
const downloadsTooltip = computed(() => formatNumber(props.downloads))
|
||||
const joinedTooltip = computed(() => formatDateTime(props.user.created))
|
||||
|
||||
const moreActions = computed<TeleportOverflowMenuItem[]>(() => [
|
||||
{
|
||||
id: 'manage-projects',
|
||||
label: formatMessage(messages.profileManageProjectsButton),
|
||||
icon: BoxIcon,
|
||||
action: () => emit('manageProjects'),
|
||||
shown: props.isSelf,
|
||||
},
|
||||
{
|
||||
divider: true,
|
||||
shown: props.isSelf,
|
||||
},
|
||||
{
|
||||
id: 'report',
|
||||
label: formatMessage(commonMessages.reportButton),
|
||||
icon: ReportIcon,
|
||||
action: () => emit('report'),
|
||||
color: 'red',
|
||||
shown: props.authUser?.id !== props.user.id,
|
||||
},
|
||||
{
|
||||
id: 'copy-id',
|
||||
label: formatMessage(commonMessages.copyIdButton),
|
||||
icon: ClipboardCopyIcon,
|
||||
action: () => emit('copyId'),
|
||||
},
|
||||
{
|
||||
id: 'copy-permalink',
|
||||
label: formatMessage(commonMessages.copyPermalinkButton),
|
||||
icon: ClipboardCopyIcon,
|
||||
action: () => emit('copyPermalink'),
|
||||
},
|
||||
{
|
||||
divider: true,
|
||||
shown: props.isAdmin,
|
||||
},
|
||||
{
|
||||
id: 'open-billing',
|
||||
label: formatMessage(messages.billingButton),
|
||||
icon: CurrencyIcon,
|
||||
action: () => emit('openBilling'),
|
||||
shown: props.isStaff,
|
||||
},
|
||||
{
|
||||
id: 'toggle-affiliate',
|
||||
label: props.isAffiliate
|
||||
? formatMessage(messages.removeAffiliateButton)
|
||||
: formatMessage(messages.setAffiliateButton),
|
||||
icon: AffiliateIcon,
|
||||
action: () => emit('toggleAffiliate'),
|
||||
shown: props.isAdmin,
|
||||
remainOnClick: true,
|
||||
color: props.isAffiliate ? 'red' : 'orange',
|
||||
},
|
||||
{
|
||||
id: 'open-info',
|
||||
label: formatMessage(messages.infoButton),
|
||||
icon: InfoIcon,
|
||||
action: () => emit('openInfo'),
|
||||
shown: props.isStaff,
|
||||
},
|
||||
{
|
||||
id: 'open-analytics',
|
||||
label: formatMessage(messages.analyticsButton),
|
||||
icon: ChartIcon,
|
||||
action: () => emit('openAnalytics'),
|
||||
shown: props.isAdmin,
|
||||
},
|
||||
{
|
||||
id: 'edit-role',
|
||||
label: formatMessage(messages.editRoleButton),
|
||||
icon: EditIcon,
|
||||
action: () => emit('editRole'),
|
||||
shown: props.isAdmin,
|
||||
},
|
||||
])
|
||||
</script>
|
||||
@@ -2171,9 +2171,6 @@
|
||||
"profile.label.collection": {
|
||||
"message": "Kolekce"
|
||||
},
|
||||
"profile.label.downloads": {
|
||||
"message": "{count} {countPlural, plural, one {stažení} few {stažení} other {stažení}}"
|
||||
},
|
||||
"profile.label.joined": {
|
||||
"message": "Členem od"
|
||||
},
|
||||
@@ -2192,9 +2189,6 @@
|
||||
"profile.label.organizations": {
|
||||
"message": "Organizace"
|
||||
},
|
||||
"profile.label.projects": {
|
||||
"message": "{count} {countPlural, plural, one {projekt} few {projekty} other {projektů}}"
|
||||
},
|
||||
"profile.label.saving": {
|
||||
"message": "Ukládání..."
|
||||
},
|
||||
@@ -2528,12 +2522,6 @@
|
||||
"project.settings.visit-dashboard": {
|
||||
"message": "Přejít na přehled projektů"
|
||||
},
|
||||
"project.stats.downloads-label": {
|
||||
"message": "{count, plural, one {stažení} few {stažení} other {stažení}}"
|
||||
},
|
||||
"project.stats.followers-label": {
|
||||
"message": "{count, plural, one {# sledující} few {# sledující} other {# sledujících}}"
|
||||
},
|
||||
"project.versions.title": {
|
||||
"message": "Verze"
|
||||
},
|
||||
|
||||
@@ -3068,9 +3068,6 @@
|
||||
"profile.label.collection": {
|
||||
"message": "Kollektion"
|
||||
},
|
||||
"profile.label.downloads": {
|
||||
"message": "{count} {countPlural, plural, one {Download} other {Downloads}}"
|
||||
},
|
||||
"profile.label.joined": {
|
||||
"message": "Beigetreten"
|
||||
},
|
||||
@@ -3089,9 +3086,6 @@
|
||||
"profile.label.organizations": {
|
||||
"message": "Organisationen"
|
||||
},
|
||||
"profile.label.projects": {
|
||||
"message": "{count} {countPlural, plural, one {Projekt} other {Projekte}}"
|
||||
},
|
||||
"profile.label.saving": {
|
||||
"message": "Speichert..."
|
||||
},
|
||||
@@ -3656,12 +3650,6 @@
|
||||
"project.settings.visit-dashboard": {
|
||||
"message": "Projekt-Dashboard besuchen"
|
||||
},
|
||||
"project.stats.downloads-label": {
|
||||
"message": "{count, plural, one {Download} other {Downloads}}"
|
||||
},
|
||||
"project.stats.followers-label": {
|
||||
"message": "{count, plural, one {Follower} other {Follower}}"
|
||||
},
|
||||
"project.status.archived.message": {
|
||||
"message": "{title} wurde archiviert. {title} wird keine weiteren Updates erhalten, es sei denn, der Autor entscheided sich, das Projekt zu Dearchivieren."
|
||||
},
|
||||
|
||||
@@ -3068,9 +3068,6 @@
|
||||
"profile.label.collection": {
|
||||
"message": "Kollektion"
|
||||
},
|
||||
"profile.label.downloads": {
|
||||
"message": "{count} {countPlural, plural, one {Download} other {Downloads}}"
|
||||
},
|
||||
"profile.label.joined": {
|
||||
"message": "Beigetreten"
|
||||
},
|
||||
@@ -3089,9 +3086,6 @@
|
||||
"profile.label.organizations": {
|
||||
"message": "Organisationen"
|
||||
},
|
||||
"profile.label.projects": {
|
||||
"message": "{count} {countPlural, plural, one {Projekt} other {Projekte}}"
|
||||
},
|
||||
"profile.label.saving": {
|
||||
"message": "Speichert..."
|
||||
},
|
||||
@@ -3656,12 +3650,6 @@
|
||||
"project.settings.visit-dashboard": {
|
||||
"message": "Projekt-Dashboard besuchen"
|
||||
},
|
||||
"project.stats.downloads-label": {
|
||||
"message": "{count, plural, one {Download} other {Downloads}}"
|
||||
},
|
||||
"project.stats.followers-label": {
|
||||
"message": "{count, plural, one {Follower} other {Follower}}"
|
||||
},
|
||||
"project.status.archived.message": {
|
||||
"message": "{title} wurde archiviert. {title} wird keine weiteren Updates erhalten, es sei denn, der Autor entscheidet sich das Projekt zu dearchivieren."
|
||||
},
|
||||
|
||||
@@ -3005,6 +3005,24 @@
|
||||
"muralpay.warning.wallet-address": {
|
||||
"message": "Double-check your wallet address. Funds sent to an incorrect address cannot be recovered."
|
||||
},
|
||||
"organization.button.manage": {
|
||||
"message": "Manage"
|
||||
},
|
||||
"organization.button.manage-projects": {
|
||||
"message": "Manage projects"
|
||||
},
|
||||
"organization.label.downloads": {
|
||||
"message": "downloads"
|
||||
},
|
||||
"organization.label.members": {
|
||||
"message": "members"
|
||||
},
|
||||
"organization.label.organization": {
|
||||
"message": "Organization"
|
||||
},
|
||||
"organization.label.projects": {
|
||||
"message": "projects"
|
||||
},
|
||||
"organization.settings.projects.edit-links.affected-projects": {
|
||||
"message": "Changes will be applied to {count, plural, one {# project} other {# projects}}."
|
||||
},
|
||||
@@ -3074,8 +3092,8 @@
|
||||
"profile.label.collection": {
|
||||
"message": "Collection"
|
||||
},
|
||||
"profile.label.downloads": {
|
||||
"message": "{count} {countPlural, plural, one {download} other {downloads}}"
|
||||
"profile.label.download-count": {
|
||||
"message": "{count, plural, one {download} other {downloads}}"
|
||||
},
|
||||
"profile.label.joined": {
|
||||
"message": "Joined"
|
||||
@@ -3095,8 +3113,8 @@
|
||||
"profile.label.organizations": {
|
||||
"message": "Organizations"
|
||||
},
|
||||
"profile.label.projects": {
|
||||
"message": "{count} {countPlural, plural, one {project} other {projects}}"
|
||||
"profile.label.project-count": {
|
||||
"message": "{count, plural, one {project} other {projects}}"
|
||||
},
|
||||
"profile.label.saving": {
|
||||
"message": "Saving..."
|
||||
@@ -3260,6 +3278,12 @@
|
||||
"project.actions.dont-show-again": {
|
||||
"message": "Don't show again"
|
||||
},
|
||||
"project.actions.edit-project": {
|
||||
"message": "Edit project"
|
||||
},
|
||||
"project.actions.rescan-modpack": {
|
||||
"message": "Rescan modpack"
|
||||
},
|
||||
"project.actions.review-project": {
|
||||
"message": "Review project"
|
||||
},
|
||||
@@ -3662,12 +3686,6 @@
|
||||
"project.settings.visit-dashboard": {
|
||||
"message": "Visit projects dashboard"
|
||||
},
|
||||
"project.stats.downloads-label": {
|
||||
"message": "{count, plural, one {download} other {downloads}}"
|
||||
},
|
||||
"project.stats.followers-label": {
|
||||
"message": "{count, plural, one {follower} other {followers}}"
|
||||
},
|
||||
"project.status.archived.message": {
|
||||
"message": "{title} has been archived. {title} will not receive any further updates unless the author decides to unarchive the project."
|
||||
},
|
||||
|
||||
@@ -3068,9 +3068,6 @@
|
||||
"profile.label.collection": {
|
||||
"message": "Colección"
|
||||
},
|
||||
"profile.label.downloads": {
|
||||
"message": "{count} {countPlural, plural, one {descarga} other {descargas}}"
|
||||
},
|
||||
"profile.label.joined": {
|
||||
"message": "Se unió"
|
||||
},
|
||||
@@ -3089,9 +3086,6 @@
|
||||
"profile.label.organizations": {
|
||||
"message": "Organizaciones"
|
||||
},
|
||||
"profile.label.projects": {
|
||||
"message": "{count} {countPlural, plural, one {proyecto} other {proyectos}}"
|
||||
},
|
||||
"profile.label.saving": {
|
||||
"message": "Guardando..."
|
||||
},
|
||||
@@ -3656,12 +3650,6 @@
|
||||
"project.settings.visit-dashboard": {
|
||||
"message": "Ver el panel de control de proyectos"
|
||||
},
|
||||
"project.stats.downloads-label": {
|
||||
"message": "{count, plural, one {descarga} other {descargas}}"
|
||||
},
|
||||
"project.stats.followers-label": {
|
||||
"message": "{count, plural, one {seguidor} other {seguidores}}"
|
||||
},
|
||||
"project.status.archived.message": {
|
||||
"message": "{title} se ha archivado. {title} no recibirá más actualizaciones hasta que el autor decida desarchivar el proyecto."
|
||||
},
|
||||
|
||||
@@ -2891,9 +2891,6 @@
|
||||
"profile.label.collection": {
|
||||
"message": "Colección"
|
||||
},
|
||||
"profile.label.downloads": {
|
||||
"message": "{count} {countPlural, plural, one {descarga} other {descargas}}"
|
||||
},
|
||||
"profile.label.joined": {
|
||||
"message": "Se unió hace"
|
||||
},
|
||||
@@ -2912,9 +2909,6 @@
|
||||
"profile.label.organizations": {
|
||||
"message": "Organizaciones"
|
||||
},
|
||||
"profile.label.projects": {
|
||||
"message": "{count} {countPlural, plural, one {proyecto} other {proyectos}}"
|
||||
},
|
||||
"profile.label.saving": {
|
||||
"message": "Guardando..."
|
||||
},
|
||||
@@ -3377,12 +3371,6 @@
|
||||
"project.settings.visit-dashboard": {
|
||||
"message": "Visita el panel de proyectos"
|
||||
},
|
||||
"project.stats.downloads-label": {
|
||||
"message": "{count, plural, one {descarga} other {descargas}}"
|
||||
},
|
||||
"project.stats.followers-label": {
|
||||
"message": "{count, plural, one {seguidor} other {seguidores}}"
|
||||
},
|
||||
"project.status.archived.message": {
|
||||
"message": "{title} ha sido archivado. {title} no recibirá ninguna futura actualización excepto que el autor decida desarchivar el proyecto."
|
||||
},
|
||||
|
||||
@@ -2213,9 +2213,6 @@
|
||||
"profile.label.collection": {
|
||||
"message": "Koleksiyon"
|
||||
},
|
||||
"profile.label.downloads": {
|
||||
"message": "{count} {countPlural, plural, one {download} other {na download}}"
|
||||
},
|
||||
"profile.label.joined": {
|
||||
"message": "Sumali"
|
||||
},
|
||||
@@ -2234,9 +2231,6 @@
|
||||
"profile.label.organizations": {
|
||||
"message": "Mga organisasyon"
|
||||
},
|
||||
"profile.label.projects": {
|
||||
"message": "{count} {countPlural, plural, one {proyekto} other {na proyekto}}"
|
||||
},
|
||||
"profile.label.saving": {
|
||||
"message": "Sine-save..."
|
||||
},
|
||||
|
||||
@@ -3074,9 +3074,6 @@
|
||||
"profile.label.collection": {
|
||||
"message": "Collection"
|
||||
},
|
||||
"profile.label.downloads": {
|
||||
"message": "{count} {countPlural, plural, one {téléchargement} other {téléchargements}}"
|
||||
},
|
||||
"profile.label.joined": {
|
||||
"message": "Rejoint"
|
||||
},
|
||||
@@ -3095,9 +3092,6 @@
|
||||
"profile.label.organizations": {
|
||||
"message": "Organisations"
|
||||
},
|
||||
"profile.label.projects": {
|
||||
"message": "{count} {countPlural, plural, one {projet} other {projets}}"
|
||||
},
|
||||
"profile.label.saving": {
|
||||
"message": "Sauvegarde en cours..."
|
||||
},
|
||||
@@ -3662,12 +3656,6 @@
|
||||
"project.settings.visit-dashboard": {
|
||||
"message": "Visiter le tableau de contrôle des projets"
|
||||
},
|
||||
"project.stats.downloads-label": {
|
||||
"message": "{count, plural, one {# téléchargement} other {# téléchargements}}"
|
||||
},
|
||||
"project.stats.followers-label": {
|
||||
"message": "{count, plural, one {# suivi} other {# suivis}}"
|
||||
},
|
||||
"project.status.archived.message": {
|
||||
"message": "{title} a été archivé. {title} ne recevra plus de mises à jour jusqu'à ce que l'auteur du projet décide de désarchiver le projet."
|
||||
},
|
||||
|
||||
@@ -1838,9 +1838,6 @@
|
||||
"profile.label.collection": {
|
||||
"message": "אוסף"
|
||||
},
|
||||
"profile.label.downloads": {
|
||||
"message": "{count} {countPlural, plural, one {download} other {downloads}}"
|
||||
},
|
||||
"profile.label.joined": {
|
||||
"message": "הצטרף ב-"
|
||||
},
|
||||
@@ -1859,9 +1856,6 @@
|
||||
"profile.label.organizations": {
|
||||
"message": "ארגונים"
|
||||
},
|
||||
"profile.label.projects": {
|
||||
"message": "{count} {countPlural, plural, one {project} other {projects}}"
|
||||
},
|
||||
"profile.label.saving": {
|
||||
"message": "שומר..."
|
||||
},
|
||||
|
||||
@@ -2966,9 +2966,6 @@
|
||||
"profile.label.collection": {
|
||||
"message": "Gyűjtemény"
|
||||
},
|
||||
"profile.label.downloads": {
|
||||
"message": "{count} {countPlural, plural, one {letöltés} other {letöltések}}"
|
||||
},
|
||||
"profile.label.joined": {
|
||||
"message": "Csatlakozott"
|
||||
},
|
||||
@@ -2987,9 +2984,6 @@
|
||||
"profile.label.organizations": {
|
||||
"message": "Szervezetek"
|
||||
},
|
||||
"profile.label.projects": {
|
||||
"message": "{count} {countPlural, plural, one {projekt} other {projektek}}"
|
||||
},
|
||||
"profile.label.saving": {
|
||||
"message": "Mentés..."
|
||||
},
|
||||
@@ -3476,12 +3470,6 @@
|
||||
"project.settings.visit-dashboard": {
|
||||
"message": "Projektek irányítópult megnyitása"
|
||||
},
|
||||
"project.stats.downloads-label": {
|
||||
"message": "{count} letöltés"
|
||||
},
|
||||
"project.stats.followers-label": {
|
||||
"message": "{count} követő"
|
||||
},
|
||||
"project.status.archived.message": {
|
||||
"message": "A(z) {title} archiválásra került. A(z) {title} nem kap további frissítéseket, kivéve, ha a fejlesztő úgy dönt, hogy visszavonja a projekt archiválását."
|
||||
},
|
||||
|
||||
@@ -2219,9 +2219,6 @@
|
||||
"profile.label.collection": {
|
||||
"message": "Koleksi"
|
||||
},
|
||||
"profile.label.downloads": {
|
||||
"message": "{count} {countPlural, plural, other {pengunduhan}}"
|
||||
},
|
||||
"profile.label.joined": {
|
||||
"message": "Telah bergabung"
|
||||
},
|
||||
@@ -2240,9 +2237,6 @@
|
||||
"profile.label.organizations": {
|
||||
"message": "Organisasi"
|
||||
},
|
||||
"profile.label.projects": {
|
||||
"message": "{count} {countPlural, plural, other {proyek}}"
|
||||
},
|
||||
"profile.label.saving": {
|
||||
"message": "Menyimpan..."
|
||||
},
|
||||
|
||||
@@ -3059,9 +3059,6 @@
|
||||
"profile.label.collection": {
|
||||
"message": "Raccolta"
|
||||
},
|
||||
"profile.label.downloads": {
|
||||
"message": "{count} download"
|
||||
},
|
||||
"profile.label.joined": {
|
||||
"message": "Iscrizione"
|
||||
},
|
||||
@@ -3080,9 +3077,6 @@
|
||||
"profile.label.organizations": {
|
||||
"message": "Organizzazioni"
|
||||
},
|
||||
"profile.label.projects": {
|
||||
"message": "{count} {countPlural, plural, one {progetto} other {progetti}}"
|
||||
},
|
||||
"profile.label.saving": {
|
||||
"message": "Salvando..."
|
||||
},
|
||||
@@ -3647,12 +3641,6 @@
|
||||
"project.settings.visit-dashboard": {
|
||||
"message": "Visita bacheca del progetto"
|
||||
},
|
||||
"project.stats.downloads-label": {
|
||||
"message": "{count} download"
|
||||
},
|
||||
"project.stats.followers-label": {
|
||||
"message": "{count} follower"
|
||||
},
|
||||
"project.status.archived.message": {
|
||||
"message": "{title} è stato archiviato. {title} non riceverà più aggiornamenti a meno che l'autore decida di rimuoverlo dall'archivio."
|
||||
},
|
||||
|
||||
@@ -2597,9 +2597,6 @@
|
||||
"profile.label.collection": {
|
||||
"message": "コレクション"
|
||||
},
|
||||
"profile.label.downloads": {
|
||||
"message": "{count}件のダウンロード"
|
||||
},
|
||||
"profile.label.joined": {
|
||||
"message": "参加: "
|
||||
},
|
||||
@@ -2618,9 +2615,6 @@
|
||||
"profile.label.organizations": {
|
||||
"message": "組織"
|
||||
},
|
||||
"profile.label.projects": {
|
||||
"message": "{count} {countPlural, plural, one {プロジェクト} other {プロジェクト}}"
|
||||
},
|
||||
"profile.label.saving": {
|
||||
"message": "保存中…"
|
||||
},
|
||||
|
||||
@@ -3068,9 +3068,6 @@
|
||||
"profile.label.collection": {
|
||||
"message": "컬렉션"
|
||||
},
|
||||
"profile.label.downloads": {
|
||||
"message": "{count} {countPlural, plural, one {다운로드} other {다운로드}}"
|
||||
},
|
||||
"profile.label.joined": {
|
||||
"message": "가입"
|
||||
},
|
||||
@@ -3089,9 +3086,6 @@
|
||||
"profile.label.organizations": {
|
||||
"message": "조직"
|
||||
},
|
||||
"profile.label.projects": {
|
||||
"message": "{count} {countPlural, plural, one {프로젝트} other {프로젝트}}"
|
||||
},
|
||||
"profile.label.saving": {
|
||||
"message": "저장..."
|
||||
},
|
||||
@@ -3656,12 +3650,6 @@
|
||||
"project.settings.visit-dashboard": {
|
||||
"message": "프로젝트 대시보드 방문"
|
||||
},
|
||||
"project.stats.downloads-label": {
|
||||
"message": "{count, plural, one {{count}회 다운로드} other {{count}회 다운로드}}"
|
||||
},
|
||||
"project.stats.followers-label": {
|
||||
"message": "{count, plural, one {팔로워} other {팔로워}}"
|
||||
},
|
||||
"project.status.archived.message": {
|
||||
"message": "{title} 은(는) 보관되었습니다. 작성자가 프로젝트 보관 해제를 결정하지 않는 한 더 이상 업데이트가 제공되지 않습니다."
|
||||
},
|
||||
|
||||
@@ -2750,9 +2750,6 @@
|
||||
"profile.label.collection": {
|
||||
"message": "Koleksi"
|
||||
},
|
||||
"profile.label.downloads": {
|
||||
"message": "{count} {countPlural, plural, other {muat turun}}"
|
||||
},
|
||||
"profile.label.joined": {
|
||||
"message": "Telah menyertai"
|
||||
},
|
||||
@@ -2771,9 +2768,6 @@
|
||||
"profile.label.organizations": {
|
||||
"message": "Organisasi"
|
||||
},
|
||||
"profile.label.projects": {
|
||||
"message": "{count} {countPlural, plural, other {projek}}"
|
||||
},
|
||||
"profile.label.saving": {
|
||||
"message": "Sedang menyimpan..."
|
||||
},
|
||||
@@ -3188,9 +3182,6 @@
|
||||
"project.settings.visit-dashboard": {
|
||||
"message": "Kunjungi papan pemuka projek"
|
||||
},
|
||||
"project.stats.downloads-label": {
|
||||
"message": "{count, plural, other {muat turun}}"
|
||||
},
|
||||
"project.status.archived.message": {
|
||||
"message": "{title} telah diarkibkan. {title} tidak akan menerima sebarang kemas kini lanjut melainkan pengarang memutuskan untuk menyaharkibkan projek."
|
||||
},
|
||||
|
||||
@@ -3065,9 +3065,6 @@
|
||||
"profile.label.collection": {
|
||||
"message": "Collectie"
|
||||
},
|
||||
"profile.label.downloads": {
|
||||
"message": "{count} {countPlural, plural, one {download} other {downloads}}"
|
||||
},
|
||||
"profile.label.joined": {
|
||||
"message": "Lid geworden"
|
||||
},
|
||||
@@ -3086,9 +3083,6 @@
|
||||
"profile.label.organizations": {
|
||||
"message": "Organisaties"
|
||||
},
|
||||
"profile.label.projects": {
|
||||
"message": "{count} {countPlural, plural, one {project} other {projecten}}"
|
||||
},
|
||||
"profile.label.saving": {
|
||||
"message": "Opslaan..."
|
||||
},
|
||||
|
||||
@@ -2678,9 +2678,6 @@
|
||||
"profile.label.collection": {
|
||||
"message": "Samling"
|
||||
},
|
||||
"profile.label.downloads": {
|
||||
"message": "{count} {countPlural, plural, one {nedlasting} other {nedlastinger}}"
|
||||
},
|
||||
"profile.label.joined": {
|
||||
"message": "Blei med"
|
||||
},
|
||||
@@ -2699,9 +2696,6 @@
|
||||
"profile.label.organizations": {
|
||||
"message": "Organisasjoner"
|
||||
},
|
||||
"profile.label.projects": {
|
||||
"message": "{count} {countPlural, plural, one {projekt} other {projekt}}"
|
||||
},
|
||||
"profile.label.saving": {
|
||||
"message": "Lagrer..."
|
||||
},
|
||||
@@ -3038,12 +3032,6 @@
|
||||
"project.settings.visit-dashboard": {
|
||||
"message": "Besøk prosjekt-dashbordet"
|
||||
},
|
||||
"project.stats.downloads-label": {
|
||||
"message": "{count, plural, one {nedlasting} other {nedlastinger}}"
|
||||
},
|
||||
"project.stats.followers-label": {
|
||||
"message": "{count, plural, one {følger} other {følgere}}"
|
||||
},
|
||||
"project.status.archived.message": {
|
||||
"message": "{title} har blitt arkivert. {title} kommer ikke til å få noen nye oppdateringer, hvis ikke forfatteren bestemmer seg for å dearkivere prosjektet."
|
||||
},
|
||||
|
||||
@@ -3062,9 +3062,6 @@
|
||||
"profile.label.collection": {
|
||||
"message": "Kolekcja"
|
||||
},
|
||||
"profile.label.downloads": {
|
||||
"message": "{count} {countPlural, plural, one {pobranie} few {pobrania} other {pobrań}}"
|
||||
},
|
||||
"profile.label.joined": {
|
||||
"message": "Dołączył(-a)"
|
||||
},
|
||||
@@ -3083,9 +3080,6 @@
|
||||
"profile.label.organizations": {
|
||||
"message": "Organizacje"
|
||||
},
|
||||
"profile.label.projects": {
|
||||
"message": "{count} {countPlural, plural, one {projekt} few {projekty} other {projektów}}"
|
||||
},
|
||||
"profile.label.saving": {
|
||||
"message": "Zapisywanie..."
|
||||
},
|
||||
@@ -3647,12 +3641,6 @@
|
||||
"project.settings.visit-dashboard": {
|
||||
"message": "Odwiedź pulpit projektów"
|
||||
},
|
||||
"project.stats.downloads-label": {
|
||||
"message": "{count, plural, one {pobranie} few {pobrania} other {pobrań}}"
|
||||
},
|
||||
"project.stats.followers-label": {
|
||||
"message": "{count, plural, one {obserwujący} other {obserwujących}}"
|
||||
},
|
||||
"project.status.archived.message": {
|
||||
"message": "{title} zostało zarchiwizowane. {title} nie będzie otrzymywać żadnych dalszych aktualizacji, chyba że autor zdecyduje się przywrócić projekt."
|
||||
},
|
||||
|
||||
@@ -3656,12 +3656,6 @@
|
||||
"project.settings.visit-dashboard": {
|
||||
"message": "Visitar painel de projetos"
|
||||
},
|
||||
"project.stats.downloads-label": {
|
||||
"message": "{count, plural, =0 {Nenhum download} one {# download} other {# downloads}}"
|
||||
},
|
||||
"project.stats.followers-label": {
|
||||
"message": "{count, plural, =0 {Nenhum seguidor} one {# seguidor} other {# seguidores}}"
|
||||
},
|
||||
"project.status.archived.message": {
|
||||
"message": "{title} foi arquivado. {title} não receberá atualizações a menos que o autor decida desarquivar o projeto."
|
||||
},
|
||||
|
||||
@@ -2561,9 +2561,6 @@
|
||||
"profile.label.collection": {
|
||||
"message": "Coleção"
|
||||
},
|
||||
"profile.label.downloads": {
|
||||
"message": "{count} {countPlural, plural, one {transferência} other {transferências}}"
|
||||
},
|
||||
"profile.label.joined": {
|
||||
"message": "Entrou"
|
||||
},
|
||||
@@ -2582,9 +2579,6 @@
|
||||
"profile.label.organizations": {
|
||||
"message": "Organizações"
|
||||
},
|
||||
"profile.label.projects": {
|
||||
"message": "{count} {countPlural, plural,one {projeto} other {projetos}}"
|
||||
},
|
||||
"profile.label.saving": {
|
||||
"message": "A guardar..."
|
||||
},
|
||||
|
||||
@@ -3065,9 +3065,6 @@
|
||||
"profile.label.collection": {
|
||||
"message": "Коллекция"
|
||||
},
|
||||
"profile.label.downloads": {
|
||||
"message": "{count} {countPlural, plural, one {скачивание} few {скачивания} other {скачиваний}}"
|
||||
},
|
||||
"profile.label.joined": {
|
||||
"message": "Регистрация:"
|
||||
},
|
||||
@@ -3086,9 +3083,6 @@
|
||||
"profile.label.organizations": {
|
||||
"message": "Организации"
|
||||
},
|
||||
"profile.label.projects": {
|
||||
"message": "{count} {countPlural, plural, one {проект} few {проекта} other {проектов}}"
|
||||
},
|
||||
"profile.label.saving": {
|
||||
"message": "Сохранение..."
|
||||
},
|
||||
@@ -3653,12 +3647,6 @@
|
||||
"project.settings.visit-dashboard": {
|
||||
"message": "Управление проектами"
|
||||
},
|
||||
"project.stats.downloads-label": {
|
||||
"message": "{count, plural, one {загрузка} few {загрузки} other {загрузок}}"
|
||||
},
|
||||
"project.stats.followers-label": {
|
||||
"message": "{count, plural, one {подписчик} few {подписчика} other {подписчиков}}"
|
||||
},
|
||||
"project.status.archived.message": {
|
||||
"message": "{title} помещён в архив. {title} больше не будет получать обновления, если только автор не решит разархивировать проект."
|
||||
},
|
||||
|
||||
@@ -2738,9 +2738,6 @@
|
||||
"profile.label.collection": {
|
||||
"message": "Samling"
|
||||
},
|
||||
"profile.label.downloads": {
|
||||
"message": "{count} {countPlural, plural, one {nedladdning} other {nedladdningar}}"
|
||||
},
|
||||
"profile.label.joined": {
|
||||
"message": "Gick med"
|
||||
},
|
||||
|
||||
@@ -3068,9 +3068,6 @@
|
||||
"profile.label.collection": {
|
||||
"message": "Koleksiyon"
|
||||
},
|
||||
"profile.label.downloads": {
|
||||
"message": "{count} {countPlural, plural, one {indirme} other {indirmeler}}"
|
||||
},
|
||||
"profile.label.joined": {
|
||||
"message": "Katılma:"
|
||||
},
|
||||
@@ -3089,9 +3086,6 @@
|
||||
"profile.label.organizations": {
|
||||
"message": "Organizasyonlar"
|
||||
},
|
||||
"profile.label.projects": {
|
||||
"message": "{count} {countPlural, plural, one {proje} other {projeler}}"
|
||||
},
|
||||
"profile.label.saving": {
|
||||
"message": "Kaydediliyor..."
|
||||
},
|
||||
@@ -3602,12 +3596,6 @@
|
||||
"project.settings.visit-dashboard": {
|
||||
"message": "Projeler panelini ziyaret et"
|
||||
},
|
||||
"project.stats.downloads-label": {
|
||||
"message": "{count, plural, one {download} other {downloads}}"
|
||||
},
|
||||
"project.stats.followers-label": {
|
||||
"message": "{count, plural, one {follower} other {followers}}"
|
||||
},
|
||||
"project.status.archived.message": {
|
||||
"message": "{title} arşivlenmiş. {title}, yapımcı fikrini değiştirmediği sürece daha fazla güncelleme almayacak."
|
||||
},
|
||||
|
||||
@@ -3071,9 +3071,6 @@
|
||||
"profile.label.collection": {
|
||||
"message": "Добірка"
|
||||
},
|
||||
"profile.label.downloads": {
|
||||
"message": "{count} {countPlural, plural, one {завантаження} few {завантаження} many {завантажень} other {завантажень}}"
|
||||
},
|
||||
"profile.label.joined": {
|
||||
"message": "Приєднався"
|
||||
},
|
||||
@@ -3092,9 +3089,6 @@
|
||||
"profile.label.organizations": {
|
||||
"message": "Організації"
|
||||
},
|
||||
"profile.label.projects": {
|
||||
"message": "{count} {countPlural, plural, one {проєкт} few {проєкти} many {проєктів} other {проєктів}}"
|
||||
},
|
||||
"profile.label.saving": {
|
||||
"message": "Збереження…"
|
||||
},
|
||||
@@ -3659,12 +3653,6 @@
|
||||
"project.settings.visit-dashboard": {
|
||||
"message": "Панель керування проєктами"
|
||||
},
|
||||
"project.stats.downloads-label": {
|
||||
"message": "{count, plural, one {завантаження} few {завантаження} many {завантажень} other {завантаження}}"
|
||||
},
|
||||
"project.stats.followers-label": {
|
||||
"message": "{count, plural, one {стежить} few {стежать} many {стежать} other {стежать}}"
|
||||
},
|
||||
"project.status.archived.message": {
|
||||
"message": "«{title}» було архівовано. «{title}» не отримуватиме подальших оновлень допоки автор не вирішить розархівувати проєкт."
|
||||
},
|
||||
|
||||
@@ -2861,9 +2861,6 @@
|
||||
"profile.label.collection": {
|
||||
"message": "Bộ sưu tập"
|
||||
},
|
||||
"profile.label.downloads": {
|
||||
"message": "{count} {countPlural, plural, one {lượt tải xuống} other {lượt tải xuống}}"
|
||||
},
|
||||
"profile.label.joined": {
|
||||
"message": "Đã tham gia"
|
||||
},
|
||||
@@ -2882,9 +2879,6 @@
|
||||
"profile.label.organizations": {
|
||||
"message": "Tổ chức"
|
||||
},
|
||||
"profile.label.projects": {
|
||||
"message": "{count} {countPlural, plural, one {dự án} other {dự án}}"
|
||||
},
|
||||
"profile.label.saving": {
|
||||
"message": "Đang lưu..."
|
||||
},
|
||||
@@ -3284,12 +3278,6 @@
|
||||
"project.settings.visit-dashboard": {
|
||||
"message": "Truy cập bảng điều khiển dự án"
|
||||
},
|
||||
"project.stats.downloads-label": {
|
||||
"message": "{count, plural, other {lượt tải}}"
|
||||
},
|
||||
"project.stats.followers-label": {
|
||||
"message": "{count, plural, other {lượt theo dõi}}"
|
||||
},
|
||||
"project.status.archived.message": {
|
||||
"message": "{title} đã được lưu trữ. {title} sẽ không nhận được bất kỳ bản cập nhật nào trong tương lai trừ khi tác giả quyết định hủy lưu trữ dự án."
|
||||
},
|
||||
|
||||
@@ -3068,9 +3068,6 @@
|
||||
"profile.label.collection": {
|
||||
"message": "收藏夹"
|
||||
},
|
||||
"profile.label.downloads": {
|
||||
"message": "{count} {countPlural, plural, other {次下载}}"
|
||||
},
|
||||
"profile.label.joined": {
|
||||
"message": "加入于"
|
||||
},
|
||||
@@ -3089,9 +3086,6 @@
|
||||
"profile.label.organizations": {
|
||||
"message": "组织"
|
||||
},
|
||||
"profile.label.projects": {
|
||||
"message": "{count} {countPlural, plural, other {个项目}}"
|
||||
},
|
||||
"profile.label.saving": {
|
||||
"message": "正在保存…"
|
||||
},
|
||||
@@ -3656,12 +3650,6 @@
|
||||
"project.settings.visit-dashboard": {
|
||||
"message": "访问项目的控制面板"
|
||||
},
|
||||
"project.stats.downloads-label": {
|
||||
"message": "{count, plural, other {下载}}"
|
||||
},
|
||||
"project.stats.followers-label": {
|
||||
"message": "{count, plural, other {关注者}}"
|
||||
},
|
||||
"project.status.archived.message": {
|
||||
"message": "{title} 已归档。除非作者决定取消归档,否则 {title} 将不再更新。"
|
||||
},
|
||||
|
||||
@@ -3074,9 +3074,6 @@
|
||||
"profile.label.collection": {
|
||||
"message": "收藏"
|
||||
},
|
||||
"profile.label.downloads": {
|
||||
"message": "下載次數:{count}"
|
||||
},
|
||||
"profile.label.joined": {
|
||||
"message": "加入時間:"
|
||||
},
|
||||
@@ -3095,9 +3092,6 @@
|
||||
"profile.label.organizations": {
|
||||
"message": "組織"
|
||||
},
|
||||
"profile.label.projects": {
|
||||
"message": "{count} 個{countPlural, plural, other {專案}}"
|
||||
},
|
||||
"profile.label.saving": {
|
||||
"message": "儲存中..."
|
||||
},
|
||||
@@ -3662,12 +3656,6 @@
|
||||
"project.settings.visit-dashboard": {
|
||||
"message": "前往專案資訊主頁"
|
||||
},
|
||||
"project.stats.downloads-label": {
|
||||
"message": "{count, plural, other {下載}}"
|
||||
},
|
||||
"project.stats.followers-label": {
|
||||
"message": "{count, plural, other {追蹤者}}"
|
||||
},
|
||||
"project.status.archived.message": {
|
||||
"message": "{title} 已封存。{title} 將不會再收到任何後續更新,除非作者決定解除封存該專案。"
|
||||
},
|
||||
|
||||
@@ -130,29 +130,27 @@
|
||||
@set-processing="setProcessing"
|
||||
/>
|
||||
</div>
|
||||
<ProjectHeader
|
||||
<ProjectPageHeader
|
||||
v-if="projectV3Loaded"
|
||||
:project="project"
|
||||
:project-v3="projectV3"
|
||||
:member="!!currentMember"
|
||||
:show-status-badge="!!currentMember || project.status !== 'approved'"
|
||||
@category="(category) => router.push(`${projectSearchUrl}?f=categories:${category}`)"
|
||||
>
|
||||
<template #actions>
|
||||
<ButtonStyled v-if="auth.user && currentMember" size="large" color="brand" circular>
|
||||
<nuxt-link
|
||||
v-tooltip="'Edit project'"
|
||||
:to="`/${project.project_type}/${project.slug ? project.slug : project.id}/settings`"
|
||||
v-tooltip="formatMessage(messages.editProject)"
|
||||
:to="`${projectPath}/settings`"
|
||||
class="!font-bold lg:!hidden"
|
||||
>
|
||||
<SettingsIcon aria-hidden="true" />
|
||||
<SettingsIcon />
|
||||
</nuxt-link>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled v-if="auth.user && currentMember" size="large" color="brand">
|
||||
<nuxt-link
|
||||
:to="`/${project.project_type}/${project.slug ? project.slug : project.id}/settings`"
|
||||
class="!font-bold max-lg:!hidden"
|
||||
>
|
||||
<SettingsIcon aria-hidden="true" />
|
||||
Edit project
|
||||
<nuxt-link :to="`${projectPath}/settings`" class="!font-bold max-lg:!hidden">
|
||||
<SettingsIcon />
|
||||
{{ formatMessage(messages.editProject) }}
|
||||
</nuxt-link>
|
||||
</ButtonStyled>
|
||||
|
||||
@@ -160,20 +158,20 @@
|
||||
<ButtonStyled
|
||||
v-if="!isServerProject"
|
||||
size="large"
|
||||
:color="
|
||||
(auth.user && currentMember) || route.name === 'type-project-version-version'
|
||||
? `standard`
|
||||
: `brand`
|
||||
"
|
||||
:color="projectHeaderPrimaryColor"
|
||||
:circular="!!auth.user && !!currentMember"
|
||||
>
|
||||
<button
|
||||
v-tooltip="
|
||||
auth.user && currentMember ? formatMessage(commonMessages.downloadButton) : ''
|
||||
auth.user && currentMember
|
||||
? formatMessage(commonMessages.downloadButton)
|
||||
: undefined
|
||||
"
|
||||
@click="(event) => downloadModal.show(event)"
|
||||
type="button"
|
||||
:aria-label="formatMessage(commonMessages.downloadButton)"
|
||||
@click="handleProjectHeaderPrimary"
|
||||
>
|
||||
<DownloadIcon aria-hidden="true" />
|
||||
<DownloadIcon />
|
||||
{{
|
||||
auth.user && currentMember ? '' : formatMessage(commonMessages.downloadButton)
|
||||
}}
|
||||
@@ -182,19 +180,21 @@
|
||||
<ButtonStyled
|
||||
v-else
|
||||
size="large"
|
||||
:color="
|
||||
(auth.user && currentMember) || route.name === 'type-project-version-version'
|
||||
? `standard`
|
||||
: `brand`
|
||||
"
|
||||
:color="projectHeaderPrimaryColor"
|
||||
:circular="!!auth.user && !!currentMember"
|
||||
>
|
||||
<button
|
||||
v-tooltip="auth.user && currentMember && !openInAppModal?.open ? 'Play' : ''"
|
||||
@click="handlePlayServerProject"
|
||||
v-tooltip="
|
||||
auth.user && currentMember
|
||||
? formatMessage(commonMessages.playButton)
|
||||
: undefined
|
||||
"
|
||||
type="button"
|
||||
:aria-label="formatMessage(commonMessages.playButton)"
|
||||
@click="handleProjectHeaderPrimary"
|
||||
>
|
||||
<PlayIcon aria-hidden="true" />
|
||||
{{ auth.user && currentMember ? '' : 'Play' }}
|
||||
<PlayIcon />
|
||||
{{ auth.user && currentMember ? '' : formatMessage(commonMessages.playButton) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
</div>
|
||||
@@ -204,109 +204,100 @@
|
||||
v-if="!isServerProject"
|
||||
size="large"
|
||||
circular
|
||||
:color="
|
||||
route.name === 'type-project-version-version' || (auth.user && currentMember)
|
||||
? `standard`
|
||||
: `brand`
|
||||
"
|
||||
:color="projectHeaderPrimaryColor"
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
:aria-label="formatMessage(commonMessages.downloadButton)"
|
||||
class="flex sm:hidden"
|
||||
@click="(event) => downloadModal.show(event)"
|
||||
@click="handleProjectHeaderPrimary"
|
||||
>
|
||||
<DownloadIcon aria-hidden="true" />
|
||||
<DownloadIcon />
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled
|
||||
v-else
|
||||
size="large"
|
||||
circular
|
||||
:color="
|
||||
route.name === 'type-project-version-version' || (auth.user && currentMember)
|
||||
? `standard`
|
||||
: `brand`
|
||||
"
|
||||
>
|
||||
<button aria-label="Play" class="flex sm:hidden" @click="handlePlayServerProject">
|
||||
<PlayIcon aria-hidden="true" />
|
||||
<ButtonStyled v-else size="large" circular :color="projectHeaderPrimaryColor">
|
||||
<button
|
||||
type="button"
|
||||
:aria-label="formatMessage(commonMessages.playButton)"
|
||||
class="flex sm:hidden"
|
||||
@click="handleProjectHeaderPrimary"
|
||||
>
|
||||
<PlayIcon />
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
</div>
|
||||
|
||||
<Tooltip
|
||||
v-if="canCreateServerFrom && flags.showProjectPageQuickServerButton"
|
||||
v-if="
|
||||
showProjectHeaderCreateServerAction && flags.showProjectPageCreateServersTooltip
|
||||
"
|
||||
theme="dismissable-prompt"
|
||||
class="inline-flex"
|
||||
:triggers="[]"
|
||||
:shown="flags.showProjectPageCreateServersTooltip"
|
||||
:auto-hide="false"
|
||||
placement="bottom-start"
|
||||
>
|
||||
<ButtonStyled size="large" circular>
|
||||
<ButtonStyled circular size="large">
|
||||
<nuxt-link
|
||||
v-tooltip="formatMessage(messages.createServerTooltip)"
|
||||
:to="`/hosting?project=${project.id}#plan`"
|
||||
@click="
|
||||
() => {
|
||||
flags.showProjectPageCreateServersTooltip = false
|
||||
saveFeatureFlags()
|
||||
}
|
||||
"
|
||||
:to="projectHeaderCreateServerTo"
|
||||
:aria-label="formatMessage(messages.serversPromoTitle)"
|
||||
@click="dismissProjectHeaderCreateServerPrompt"
|
||||
>
|
||||
<ServerPlusIcon aria-hidden="true" />
|
||||
<ServerPlusIcon />
|
||||
</nuxt-link>
|
||||
</ButtonStyled>
|
||||
<template #popper>
|
||||
<div class="grid grid-cols-[min-content] gap-1">
|
||||
<div class="flex min-w-60 items-center justify-between gap-4">
|
||||
<h3
|
||||
class="m-0 flex items-center gap-2 whitespace-nowrap text-base font-bold text-contrast"
|
||||
>
|
||||
{{ formatMessage(messages.serversPromoTitle) }}
|
||||
<TagItem
|
||||
:style="{
|
||||
'--_color': 'var(--color-brand)',
|
||||
'--_bg-color': 'var(--color-brand-highlight)',
|
||||
}"
|
||||
>{{ formatMessage(commonMessages.newBadge) }}</TagItem
|
||||
<div class="grid max-w-[18rem] gap-2">
|
||||
<div class="flex items-center justify-between gap-4">
|
||||
<div class="flex items-center gap-2">
|
||||
<h3 class="m-0 text-base font-bold text-contrast">
|
||||
{{ formatMessage(messages.serversPromoTitle) }}
|
||||
</h3>
|
||||
<span
|
||||
class="rounded-full bg-brand-highlight px-2 py-0.5 text-xs font-bold text-brand"
|
||||
>
|
||||
</h3>
|
||||
{{ formatMessage(commonMessages.newBadge) }}
|
||||
</span>
|
||||
</div>
|
||||
<ButtonStyled size="small" circular>
|
||||
<button
|
||||
v-tooltip="formatMessage(messages.dontShowAgain)"
|
||||
@click="
|
||||
() => {
|
||||
flags.showProjectPageCreateServersTooltip = false
|
||||
saveFeatureFlags()
|
||||
}
|
||||
"
|
||||
@click="dismissProjectHeaderCreateServerPrompt"
|
||||
>
|
||||
<XIcon aria-hidden="true" />
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
</div>
|
||||
|
||||
<p class="m-0 text-wrap text-sm font-medium leading-tight text-secondary">
|
||||
<p class="m-0 text-sm font-medium leading-tight text-secondary">
|
||||
{{ formatMessage(messages.serversPromoDescription) }}
|
||||
</p>
|
||||
|
||||
<p class="m-0 text-wrap text-sm font-bold text-primary">
|
||||
<p class="m-0 text-sm font-semibold text-contrast">
|
||||
<IntlFormatted
|
||||
:message-id="messages.serversPromoPricing"
|
||||
:values="{
|
||||
price: formatPrice(500, 'USD', true),
|
||||
}"
|
||||
:values="{ price: formatPrice(500, 'USD', true) }"
|
||||
>
|
||||
<template #small="{ children }">
|
||||
<span class="text-xs">
|
||||
<component :is="() => children" />
|
||||
</span>
|
||||
<small><component :is="() => children" /></small>
|
||||
</template>
|
||||
</IntlFormatted>
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
</Tooltip>
|
||||
<ButtonStyled size="large" circular>
|
||||
<ButtonStyled v-else-if="showProjectHeaderCreateServerAction" circular size="large">
|
||||
<nuxt-link
|
||||
v-tooltip="formatMessage(messages.createServerTooltip)"
|
||||
:to="projectHeaderCreateServerTo"
|
||||
:aria-label="formatMessage(messages.serversPromoTitle)"
|
||||
@click="dismissProjectHeaderCreateServerPrompt"
|
||||
>
|
||||
<ServerPlusIcon />
|
||||
</nuxt-link>
|
||||
</ButtonStyled>
|
||||
|
||||
<ButtonStyled circular size="large">
|
||||
<ClientOnly>
|
||||
<button
|
||||
v-if="auth.user"
|
||||
@@ -315,14 +306,15 @@
|
||||
? formatMessage(commonMessages.unfollowButton)
|
||||
: formatMessage(commonMessages.followButton)
|
||||
"
|
||||
type="button"
|
||||
:aria-label="
|
||||
following
|
||||
? formatMessage(commonMessages.unfollowButton)
|
||||
: formatMessage(commonMessages.followButton)
|
||||
"
|
||||
@click="userFollowProject(project)"
|
||||
@click="followProjectFromHeader"
|
||||
>
|
||||
<HeartIcon :fill="following ? 'currentColor' : 'none'" aria-hidden="true" />
|
||||
<HeartIcon :fill="following ? 'currentColor' : 'none'" />
|
||||
</button>
|
||||
<nuxt-link
|
||||
v-else
|
||||
@@ -343,156 +335,31 @@
|
||||
</template>
|
||||
</ClientOnly>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled size="large" circular>
|
||||
<PopoutMenu
|
||||
v-if="auth.user"
|
||||
:tooltip="
|
||||
collections.some((x) => x.projects.includes(project.id))
|
||||
? formatMessage(commonMessages.savedLabel)
|
||||
: formatMessage(commonMessages.saveButton)
|
||||
"
|
||||
from="top-right"
|
||||
:aria-label="formatMessage(commonMessages.saveButton)"
|
||||
:dropdown-id="`${baseId}-save`"
|
||||
>
|
||||
<BookmarkIcon
|
||||
aria-hidden="true"
|
||||
:fill="
|
||||
collections.some((x) => x.projects.includes(project.id))
|
||||
? 'currentColor'
|
||||
: 'none'
|
||||
"
|
||||
/>
|
||||
<template #menu>
|
||||
<StyledInput
|
||||
v-model="displayCollectionsSearch"
|
||||
:placeholder="formatMessage(commonMessages.searchPlaceholder)"
|
||||
wrapper-class="menu-search"
|
||||
/>
|
||||
<div v-if="collections.length > 0" class="collections-list text-primary">
|
||||
<Checkbox
|
||||
v-for="option in collections
|
||||
.slice()
|
||||
.sort((a, b) => a.name.localeCompare(b.name))"
|
||||
:key="option.id"
|
||||
:model-value="option.projects.includes(project.id)"
|
||||
class="popout-checkbox"
|
||||
@update:model-value="() => onUserCollectProject(option, project.id)"
|
||||
>
|
||||
{{ option.name }}
|
||||
</Checkbox>
|
||||
</div>
|
||||
|
||||
<div v-else class="menu-text">
|
||||
<p class="popout-text">{{ formatMessage(messages.noCollectionsFound) }}</p>
|
||||
</div>
|
||||
<ButtonStyled>
|
||||
<button
|
||||
class="mx-3 mb-3"
|
||||
@click="(event) => $refs.modal_collection.show(event)"
|
||||
>
|
||||
<PlusIcon aria-hidden="true" />
|
||||
{{ formatMessage(messages.createNewCollection) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
</template>
|
||||
</PopoutMenu>
|
||||
<nuxt-link v-else v-tooltip="'Save'" :to="signInRouteObj" aria-label="Save">
|
||||
<BookmarkIcon aria-hidden="true" />
|
||||
</nuxt-link>
|
||||
</ButtonStyled>
|
||||
<ProjectCollectionSaveButton
|
||||
:auth-user="auth.user"
|
||||
:sign-in-route="signInRouteObj"
|
||||
:project-id="project.id"
|
||||
:collections="collections"
|
||||
:saved="collections.some((x) => x.projects.includes(project.id))"
|
||||
:base-id="baseId"
|
||||
:no-collections-label="formatMessage(messages.noCollectionsFound)"
|
||||
:create-new-collection-label="formatMessage(messages.createNewCollection)"
|
||||
:collect-project="onUserCollectProject"
|
||||
:create-collection="(event) => modalCollection?.show(event)"
|
||||
/>
|
||||
|
||||
<ButtonStyled size="large" circular type="transparent">
|
||||
<OverflowMenu
|
||||
<ButtonStyled circular size="large" type="transparent">
|
||||
<TeleportOverflowMenu
|
||||
:options="projectHeaderMoreActions"
|
||||
:tooltip="formatMessage(commonMessages.moreOptionsButton)"
|
||||
:options="[
|
||||
{
|
||||
id: 'analytics',
|
||||
link: `/${project.project_type}/${project.slug ? project.slug : project.id}/settings/analytics`,
|
||||
hoverOnly: true,
|
||||
shown: auth.user && !!currentMember,
|
||||
},
|
||||
{
|
||||
divider: true,
|
||||
shown: auth.user && !!currentMember,
|
||||
},
|
||||
{
|
||||
id: 'moderation-checklist',
|
||||
action: openModerationChecklistFromMenu,
|
||||
color: 'orange',
|
||||
hoverOnly: true,
|
||||
shown:
|
||||
auth.user &&
|
||||
tags.staffRoles.includes(auth.user.role) &&
|
||||
!showModerationChecklist,
|
||||
},
|
||||
{
|
||||
id: 'tech-review',
|
||||
link: `/moderation/technical-review/${project.id}`,
|
||||
color: 'orange',
|
||||
hoverOnly: true,
|
||||
shown: auth.user && tags.staffRoles.includes(auth.user.role),
|
||||
},
|
||||
{
|
||||
id: 'moderation-modpack-rescan',
|
||||
action: () => scanModal.show(),
|
||||
color: 'orange',
|
||||
hoverOnly: true,
|
||||
shown:
|
||||
auth.user &&
|
||||
tags.staffRoles.includes(auth.user.role) &&
|
||||
project.actualProjectType === 'modpack',
|
||||
},
|
||||
{
|
||||
divider: true,
|
||||
shown: auth.user && tags.staffRoles.includes(auth.user.role),
|
||||
},
|
||||
{
|
||||
id: 'report',
|
||||
action: () =>
|
||||
auth.user
|
||||
? reportProject(project.id)
|
||||
: navigateTo(
|
||||
getSignInRouteObj(route, getReportPath('project', project.id)),
|
||||
),
|
||||
color: 'red',
|
||||
hoverOnly: true,
|
||||
shown: !isMember,
|
||||
},
|
||||
{ id: 'copy-id', action: () => copyId() },
|
||||
{ id: 'copy-permalink', action: () => copyPermalink() },
|
||||
]"
|
||||
:aria-label="formatMessage(commonMessages.moreOptionsButton)"
|
||||
:dropdown-id="`${baseId}-more-options`"
|
||||
>
|
||||
<MoreVerticalIcon aria-hidden="true" />
|
||||
<template #analytics>
|
||||
<ChartIcon aria-hidden="true" />
|
||||
{{ formatMessage(commonMessages.analyticsButton) }}
|
||||
</template>
|
||||
<template #moderation-checklist>
|
||||
<ScaleIcon aria-hidden="true" /> {{ formatMessage(messages.reviewProject) }}
|
||||
</template>
|
||||
<template #tech-review> <ScanEyeIcon aria-hidden="true" /> Tech review </template>
|
||||
<template #moderation-modpack-rescan>
|
||||
<FolderSearchIcon aria-hidden="true" /> Rescan modpack
|
||||
</template>
|
||||
<template #report>
|
||||
<ReportIcon aria-hidden="true" />
|
||||
{{ formatMessage(commonMessages.reportButton) }}
|
||||
</template>
|
||||
<template #copy-id>
|
||||
<ClipboardCopyIcon aria-hidden="true" />
|
||||
{{ formatMessage(commonMessages.copyIdButton) }}
|
||||
</template>
|
||||
<template #copy-permalink>
|
||||
<ClipboardCopyIcon aria-hidden="true" />
|
||||
{{ formatMessage(commonMessages.copyPermalinkButton) }}
|
||||
</template>
|
||||
</OverflowMenu>
|
||||
<MoreVerticalIcon />
|
||||
</TeleportOverflowMenu>
|
||||
</ButtonStyled>
|
||||
</template>
|
||||
</ProjectHeader>
|
||||
</ProjectPageHeader>
|
||||
<ProjectMemberHeader
|
||||
v-if="currentMember"
|
||||
:project="project"
|
||||
@@ -726,7 +593,6 @@
|
||||
|
||||
<script setup>
|
||||
import {
|
||||
BookmarkIcon,
|
||||
BookTextIcon,
|
||||
CalendarIcon,
|
||||
ChartIcon,
|
||||
@@ -739,7 +605,6 @@ import {
|
||||
ListIcon,
|
||||
MoreVerticalIcon,
|
||||
PlayIcon,
|
||||
PlusIcon,
|
||||
ReportIcon,
|
||||
ScaleIcon,
|
||||
ScanEyeIcon,
|
||||
@@ -752,7 +617,6 @@ import {
|
||||
Admonition,
|
||||
Avatar,
|
||||
ButtonStyled,
|
||||
Checkbox,
|
||||
commonMessages,
|
||||
defineMessages,
|
||||
injectModrinthClient,
|
||||
@@ -761,12 +625,10 @@ import {
|
||||
NavTabs,
|
||||
NewModal,
|
||||
OpenInAppModal,
|
||||
OverflowMenu,
|
||||
PopoutMenu,
|
||||
PROJECT_DEP_MARKER_QUERY,
|
||||
ProjectBackgroundGradient,
|
||||
ProjectEnvironmentModal,
|
||||
ProjectHeader,
|
||||
ProjectPageHeader,
|
||||
ProjectSidebarCompatibility,
|
||||
ProjectSidebarCreators,
|
||||
ProjectSidebarDetails,
|
||||
@@ -774,8 +636,7 @@ import {
|
||||
ProjectSidebarServerInfo,
|
||||
ProjectSidebarTags,
|
||||
provideProjectPageContext,
|
||||
StyledInput,
|
||||
TagItem,
|
||||
TeleportOverflowMenu,
|
||||
useDebugLogger,
|
||||
useFormatDateTime,
|
||||
useFormatPrice,
|
||||
@@ -795,6 +656,7 @@ import MessageBanner from '~/components/ui/MessageBanner.vue'
|
||||
import ModerationChecklist from '~/components/ui/moderation/checklist/ModerationChecklist.vue'
|
||||
import ModerationProjectNags from '~/components/ui/moderation/ModerationProjectNags.vue'
|
||||
import ModpackScanModal from '~/components/ui/moderation/ModpackScanModal.vue'
|
||||
import ProjectCollectionSaveButton from '~/components/ui/ProjectCollectionSaveButton.vue'
|
||||
import ProjectDownloadModal from '~/components/ui/ProjectDownloadModal/index.vue'
|
||||
import ProjectMemberHeader from '~/components/ui/ProjectMemberHeader.vue'
|
||||
import { getSignInRouteObj } from '~/composables/auth.ts'
|
||||
@@ -851,11 +713,11 @@ const flags = useFeatureFlags()
|
||||
const cosmetics = useCosmetics()
|
||||
|
||||
const { formatMessage } = useVIntl()
|
||||
const formatPrice = useFormatPrice()
|
||||
const formatDateTime = useFormatDateTime({
|
||||
timeStyle: 'short',
|
||||
dateStyle: 'long',
|
||||
})
|
||||
const formatPrice = useFormatPrice()
|
||||
|
||||
const debug = useDebugLogger('DownloadModal')
|
||||
|
||||
@@ -925,10 +787,6 @@ const messages = defineMessages({
|
||||
id: 'project.navigation.changelog',
|
||||
defaultMessage: 'Changelog',
|
||||
},
|
||||
createNewCollection: {
|
||||
id: 'project.collections.create-new',
|
||||
defaultMessage: 'Create new collection',
|
||||
},
|
||||
createServer: {
|
||||
id: 'project.actions.create-server',
|
||||
defaultMessage: 'Create a server',
|
||||
@@ -937,6 +795,10 @@ const messages = defineMessages({
|
||||
id: 'project.actions.create-server-tooltip',
|
||||
defaultMessage: 'Create a server',
|
||||
},
|
||||
createNewCollection: {
|
||||
id: 'project.collections.create-new',
|
||||
defaultMessage: 'Create new collection',
|
||||
},
|
||||
descriptionTab: {
|
||||
id: 'project.description.title',
|
||||
defaultMessage: 'Description',
|
||||
@@ -945,9 +807,9 @@ const messages = defineMessages({
|
||||
id: 'project.actions.dont-show-again',
|
||||
defaultMessage: "Don't show again",
|
||||
},
|
||||
downloadsStat: {
|
||||
id: 'project.stats.downloads-label',
|
||||
defaultMessage: '{count, plural, one {download} other {downloads}}',
|
||||
editProject: {
|
||||
id: 'project.actions.edit-project',
|
||||
defaultMessage: 'Edit project',
|
||||
},
|
||||
errorLoadingProject: {
|
||||
id: 'project.error.loading',
|
||||
@@ -975,10 +837,6 @@ const messages = defineMessages({
|
||||
id: 'project.environment.migration.learn-more',
|
||||
defaultMessage: 'Learn more about this change',
|
||||
},
|
||||
followersStat: {
|
||||
id: 'project.stats.followers-label',
|
||||
defaultMessage: '{count, plural, one {follower} other {followers}}',
|
||||
},
|
||||
galleryTab: {
|
||||
id: 'project.gallery.title',
|
||||
defaultMessage: 'Gallery',
|
||||
@@ -1039,6 +897,10 @@ const messages = defineMessages({
|
||||
id: 'project.actions.review-project',
|
||||
defaultMessage: 'Review project',
|
||||
},
|
||||
rescanModpack: {
|
||||
id: 'project.actions.rescan-modpack',
|
||||
defaultMessage: 'Rescan modpack',
|
||||
},
|
||||
serversPromoDescription: {
|
||||
id: 'project.actions.servers-promo.description',
|
||||
defaultMessage: 'Modrinth Hosting is the easiest way to play with your friends without hassle!',
|
||||
@@ -1066,6 +928,7 @@ const messages = defineMessages({
|
||||
})
|
||||
|
||||
const modalLicense = ref(null)
|
||||
const modalCollection = useTemplateRef('modal_collection')
|
||||
const licenseText = ref('')
|
||||
|
||||
const createdDate = computed(() =>
|
||||
@@ -1104,13 +967,8 @@ async function getLicenseData(event) {
|
||||
}
|
||||
}
|
||||
|
||||
const displayCollectionsSearch = ref('')
|
||||
const collections = computed(() =>
|
||||
user.value && user.value.collections
|
||||
? user.value.collections.filter((x) =>
|
||||
x.name.toLowerCase().includes(displayCollectionsSearch.value.toLowerCase()),
|
||||
)
|
||||
: [],
|
||||
user.value && user.value.collections ? user.value.collections : [],
|
||||
)
|
||||
|
||||
if (
|
||||
@@ -1844,6 +1702,89 @@ const canCreateServerFrom = computed(() => {
|
||||
return project.value.project_type === 'modpack' && project.value.server_side !== 'unsupported'
|
||||
})
|
||||
|
||||
const projectSearchUrl = computed(
|
||||
() => `/discover/${isServerProject.value ? 'servers' : `${project.value?.project_type}s`}`,
|
||||
)
|
||||
const projectPath = computed(() =>
|
||||
project.value
|
||||
? `/${project.value.project_type}/${project.value.slug ? project.value.slug : project.value.id}`
|
||||
: '',
|
||||
)
|
||||
const projectHeaderPrimaryColor = computed(() =>
|
||||
currentMember.value || route.name === 'type-project-version-version' ? 'standard' : 'brand',
|
||||
)
|
||||
const showProjectHeaderCreateServerAction = computed(
|
||||
() => canCreateServerFrom.value && flags.value.showProjectPageQuickServerButton,
|
||||
)
|
||||
const projectHeaderCreateServerTo = computed(() =>
|
||||
project.value ? `/hosting?project=${project.value.id}#plan` : '/hosting',
|
||||
)
|
||||
const projectHeaderMoreActions = computed(() => {
|
||||
const isStaff = !!(auth.value.user && tags.value.staffRoles.includes(auth.value.user.role))
|
||||
|
||||
return [
|
||||
{
|
||||
id: 'analytics',
|
||||
label: formatMessage(commonMessages.analyticsButton),
|
||||
icon: ChartIcon,
|
||||
link: `${projectPath.value}/settings/analytics`,
|
||||
shown: !!auth.value.user && !!currentMember.value,
|
||||
},
|
||||
{
|
||||
divider: true,
|
||||
shown: !!auth.value.user && !!currentMember.value,
|
||||
},
|
||||
{
|
||||
id: 'moderation-checklist',
|
||||
label: formatMessage(messages.reviewProject),
|
||||
icon: ScaleIcon,
|
||||
action: openModerationChecklistFromMenu,
|
||||
color: 'orange',
|
||||
shown: !!auth.value.user && isStaff && !showModerationChecklist.value,
|
||||
},
|
||||
{
|
||||
id: 'tech-review',
|
||||
label: 'Tech review',
|
||||
icon: ScanEyeIcon,
|
||||
link: `/moderation/technical-review/${project.value?.id}`,
|
||||
color: 'orange',
|
||||
shown: !!auth.value.user && isStaff,
|
||||
},
|
||||
{
|
||||
id: 'moderation-modpack-rescan',
|
||||
label: formatMessage(messages.rescanModpack),
|
||||
icon: FolderSearchIcon,
|
||||
action: () => scanModal.value?.show(),
|
||||
color: 'orange',
|
||||
shown: !!auth.value.user && isStaff && project.value?.actualProjectType === 'modpack',
|
||||
},
|
||||
{
|
||||
divider: true,
|
||||
shown: !!auth.value.user && isStaff,
|
||||
},
|
||||
{
|
||||
id: 'report',
|
||||
label: formatMessage(commonMessages.reportButton),
|
||||
icon: ReportIcon,
|
||||
action: reportProjectFromHeader,
|
||||
color: 'red',
|
||||
shown: !isMember.value,
|
||||
},
|
||||
{
|
||||
id: 'copy-id',
|
||||
label: formatMessage(commonMessages.copyIdButton),
|
||||
icon: ClipboardCopyIcon,
|
||||
action: copyId,
|
||||
},
|
||||
{
|
||||
id: 'copy-permalink',
|
||||
label: formatMessage(commonMessages.copyPermalinkButton),
|
||||
icon: ClipboardCopyIcon,
|
||||
action: copyPermalink,
|
||||
},
|
||||
]
|
||||
})
|
||||
|
||||
const createCanonicalUrl = () =>
|
||||
project.value ? `https://modrinth.com/project/${project.value.id}` : undefined
|
||||
|
||||
@@ -1878,6 +1819,33 @@ if (!route.name.startsWith('type-project-settings')) {
|
||||
|
||||
const onUserCollectProject = useClientTry(userCollectProject)
|
||||
|
||||
function handleProjectHeaderPrimary(event) {
|
||||
if (isServerProject.value) {
|
||||
handlePlayServerProject()
|
||||
} else {
|
||||
downloadModal.value?.show(event)
|
||||
}
|
||||
}
|
||||
|
||||
function dismissProjectHeaderCreateServerPrompt() {
|
||||
flags.value.showProjectPageCreateServersTooltip = false
|
||||
saveFeatureFlags()
|
||||
}
|
||||
|
||||
function followProjectFromHeader() {
|
||||
if (!project.value) return
|
||||
userFollowProject(project.value)
|
||||
}
|
||||
|
||||
function reportProjectFromHeader() {
|
||||
if (!project.value) return
|
||||
if (auth.value.user) {
|
||||
reportProject(project.value.id)
|
||||
} else {
|
||||
navigateTo(getSignInRouteObj(route, getReportPath('project', project.value.id)))
|
||||
}
|
||||
}
|
||||
|
||||
watch(
|
||||
[versionsV3, _versionsV3Error],
|
||||
([data, error]) => {
|
||||
|
||||
@@ -62,86 +62,16 @@
|
||||
</template>
|
||||
<template v-else>
|
||||
<div class="normal-page__header py-4">
|
||||
<ContentPageHeader>
|
||||
<template #icon>
|
||||
<Avatar :src="organization.icon_url" :alt="organization.name" size="96px" />
|
||||
</template>
|
||||
<template #title>
|
||||
{{ organization.name }}
|
||||
</template>
|
||||
<template #title-suffix>
|
||||
<div class="ml-1 flex items-center gap-2 font-semibold">
|
||||
<OrganizationIcon />
|
||||
Organization
|
||||
</div>
|
||||
</template>
|
||||
<template #summary>
|
||||
{{ organization.description }}
|
||||
</template>
|
||||
<template #stats>
|
||||
<div
|
||||
class="flex items-center gap-2 border-0 border-r border-solid border-divider pr-4 font-semibold"
|
||||
>
|
||||
<UsersIcon class="h-6 w-6 text-secondary" />
|
||||
{{ formatCompactNumber(acceptedMembers?.length || 0) }}
|
||||
members
|
||||
</div>
|
||||
<div
|
||||
class="flex items-center gap-2 border-0 border-r border-solid border-divider pr-4 font-semibold"
|
||||
>
|
||||
<BoxIcon class="h-6 w-6 text-secondary" />
|
||||
{{ formatCompactNumber(projects?.length || 0) }}
|
||||
projects
|
||||
</div>
|
||||
<div
|
||||
v-tooltip="formatNumber(sumDownloads)"
|
||||
class="flex items-center gap-2 font-semibold"
|
||||
>
|
||||
<DownloadIcon class="h-6 w-6 text-secondary" />
|
||||
{{ formatCompactNumber(sumDownloads) }}
|
||||
downloads
|
||||
</div>
|
||||
</template>
|
||||
<template #actions>
|
||||
<ButtonStyled v-if="auth.user && currentMember" size="large">
|
||||
<NuxtLink :to="`/organization/${organization.slug}/settings`">
|
||||
<SettingsIcon aria-hidden="true" />
|
||||
Manage
|
||||
</NuxtLink>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled size="large" circular type="transparent">
|
||||
<OverflowMenu
|
||||
:options="[
|
||||
{
|
||||
id: 'manage-projects',
|
||||
action: () =>
|
||||
router.push('/organization/' + organization?.slug + '/settings/projects'),
|
||||
hoverFilledOnly: true,
|
||||
shown: !!(auth.user && currentMember),
|
||||
},
|
||||
{ divider: true, shown: !!(auth?.user && currentMember) },
|
||||
{ id: 'copy-id', action: () => copyId() },
|
||||
{ id: 'copy-permalink', action: () => copyPermalink() },
|
||||
]"
|
||||
aria-label="More options"
|
||||
>
|
||||
<MoreVerticalIcon aria-hidden="true" />
|
||||
<template #manage-projects>
|
||||
<BoxIcon aria-hidden="true" />
|
||||
Manage projects
|
||||
</template>
|
||||
<template #copy-id>
|
||||
<ClipboardCopyIcon aria-hidden="true" />
|
||||
{{ formatMessage(commonMessages.copyIdButton) }}
|
||||
</template>
|
||||
<template #copy-permalink>
|
||||
<ClipboardCopyIcon aria-hidden="true" />
|
||||
{{ formatMessage(commonMessages.copyPermalinkButton) }}
|
||||
</template>
|
||||
</OverflowMenu>
|
||||
</ButtonStyled>
|
||||
</template>
|
||||
</ContentPageHeader>
|
||||
<OrganizationPageHeader
|
||||
:organization="organization"
|
||||
:members-count="acceptedMembers?.length || 0"
|
||||
:projects-count="projects?.length || 0"
|
||||
:downloads="sumDownloads"
|
||||
:can-manage="!!(auth.user && currentMember)"
|
||||
@manage-projects="router.push(`/organization/${organization.slug}/settings/projects`)"
|
||||
@copy-id="copyId"
|
||||
@copy-permalink="copyPermalink"
|
||||
/>
|
||||
</div>
|
||||
<div class="normal-page__sidebar">
|
||||
<AdPlaceholder v-if="!auth.user" />
|
||||
@@ -293,11 +223,7 @@ import {
|
||||
BoxIcon,
|
||||
ChartIcon,
|
||||
CheckIcon,
|
||||
ClipboardCopyIcon,
|
||||
CrownIcon,
|
||||
DownloadIcon,
|
||||
MoreVerticalIcon,
|
||||
OrganizationIcon,
|
||||
SettingsIcon,
|
||||
SpinnerIcon,
|
||||
UsersIcon,
|
||||
@@ -307,16 +233,13 @@ import {
|
||||
Avatar,
|
||||
ButtonStyled,
|
||||
commonMessages,
|
||||
ContentPageHeader,
|
||||
injectModrinthClient,
|
||||
NavTabs,
|
||||
OverflowMenu,
|
||||
PROJECT_DEP_MARKER_QUERY,
|
||||
ProjectCard,
|
||||
ProjectCardList,
|
||||
SidebarCard,
|
||||
useCompactNumber,
|
||||
useFormatNumber,
|
||||
useVIntl,
|
||||
} from '@modrinth/ui'
|
||||
import type { Organization, ProjectStatus, ProjectType } from '@modrinth/utils'
|
||||
@@ -326,6 +249,7 @@ import UpToDate from '~/assets/images/illustrations/up_to_date.svg?component'
|
||||
import AdPlaceholder from '~/components/ui/AdPlaceholder.vue'
|
||||
import ModalCreation from '~/components/ui/create/ProjectCreateModal.vue'
|
||||
import NavStack from '~/components/ui/NavStack.vue'
|
||||
import OrganizationPageHeader from '~/components/ui/OrganizationPageHeader.vue'
|
||||
import { acceptTeamInvite, removeTeamMember } from '~/helpers/teams.js'
|
||||
import {
|
||||
OrganizationContext,
|
||||
@@ -342,7 +266,6 @@ type ProjectV3 = Labrinth.Projects.v3.Project & {
|
||||
const vintl = useVIntl()
|
||||
const { formatMessage } = vintl
|
||||
|
||||
const formatNumber = useFormatNumber()
|
||||
const { formatCompactNumber } = useCompactNumber()
|
||||
|
||||
const auth: { user: any } & any = await useAuth()
|
||||
|
||||
@@ -120,35 +120,34 @@
|
||||
</NewModal>
|
||||
<div class="new-page sidebar" :class="{ 'alt-layout': cosmetics.leftContentLayout }">
|
||||
<div class="normal-page__header py-4">
|
||||
<ContentPageHeader>
|
||||
<template #icon>
|
||||
<Avatar
|
||||
:src="user.avatar_url"
|
||||
:alt="user.username"
|
||||
:size="isModrinthUser ? '64px' : '96px'"
|
||||
circle
|
||||
/>
|
||||
</template>
|
||||
<template #title>
|
||||
<span class="flex items-center gap-2">
|
||||
{{ user.username }}
|
||||
<BadgeCheckIcon
|
||||
v-if="isOfficialAccount"
|
||||
v-tooltip="formatMessage(messages.officialAccount)"
|
||||
class="size-5 text-brand"
|
||||
fill="var(--color-brand-highlight)"
|
||||
/>
|
||||
<TagItem
|
||||
v-if="isAdminViewing && isAffiliate"
|
||||
:style="{
|
||||
'--_color': 'var(--color-brand)',
|
||||
'--_bg-color': 'var(--color-brand-highlight)',
|
||||
}"
|
||||
>
|
||||
<AffiliateIcon /> Affiliate
|
||||
</TagItem>
|
||||
</span>
|
||||
</template>
|
||||
<UserPageHeader
|
||||
:user="user"
|
||||
:summary="isModrinthUser ? null : profileHeaderSummary"
|
||||
:auth-user="auth.user"
|
||||
:is-modrinth-user="isModrinthUser"
|
||||
:is-official-account="isOfficialAccount"
|
||||
:show-affiliate-badge="isAdminViewing && !!isAffiliate"
|
||||
:is-affiliate="!!isAffiliate"
|
||||
:is-self="auth.user?.id === user.id"
|
||||
:is-admin="isAdminViewing"
|
||||
:is-staff="!!(auth.user && isStaff(auth.user))"
|
||||
:projects-count="projects?.length || 0"
|
||||
:downloads="sumDownloads"
|
||||
@manage-projects="navigateTo('/dashboard/projects')"
|
||||
@report="reportProfileFromHeader"
|
||||
@copy-id="copyId"
|
||||
@copy-permalink="copyPermalink"
|
||||
@open-billing="navigateTo(`/admin/billing/${user.id}`)"
|
||||
@toggle-affiliate="toggleAffiliate(user.id)"
|
||||
@open-info="userDetailsModal?.show()"
|
||||
@open-analytics="
|
||||
navigateTo({
|
||||
path: '/dashboard/analytics',
|
||||
query: { user: user.username || user.id },
|
||||
})
|
||||
"
|
||||
@edit-role="openRoleEditModal"
|
||||
>
|
||||
<template v-if="isModrinthUser" #summary>
|
||||
<IntlFormatted :message-id="messages.officialAccountBio">
|
||||
<template #support-link>
|
||||
@@ -173,159 +172,7 @@
|
||||
</template>
|
||||
</IntlFormatted>
|
||||
</template>
|
||||
<template v-else #summary>
|
||||
{{
|
||||
user.bio
|
||||
? user.bio
|
||||
: projects?.length > 0
|
||||
? formatMessage(messages.bioFallbackCreator)
|
||||
: formatMessage(messages.bioFallbackUser)
|
||||
}}
|
||||
</template>
|
||||
<template v-if="!isModrinthUser" #stats>
|
||||
<div
|
||||
class="flex items-center gap-2 border-0 border-r border-solid border-divider pr-4 font-semibold"
|
||||
>
|
||||
<BoxIcon class="h-6 w-6 text-secondary" />
|
||||
{{
|
||||
formatMessage(messages.profileProjectsLabel, {
|
||||
count: formatCompactNumber(projects?.length || 0),
|
||||
countPlural: formatCompactNumberPlural(projects?.length || 0),
|
||||
})
|
||||
}}
|
||||
</div>
|
||||
<div
|
||||
v-tooltip="formatNumber(sumDownloads)"
|
||||
class="flex items-center gap-2 border-0 border-r border-solid border-divider pr-4 font-semibold"
|
||||
>
|
||||
<DownloadIcon class="h-6 w-6 text-secondary" />
|
||||
{{
|
||||
formatMessage(messages.profileDownloadsLabel, {
|
||||
count: formatCompactNumber(sumDownloads),
|
||||
countPlural: formatCompactNumberPlural(sumDownloads),
|
||||
})
|
||||
}}
|
||||
</div>
|
||||
<div
|
||||
v-tooltip="formatDateTime(user.created)"
|
||||
class="flex items-center gap-2 font-semibold"
|
||||
>
|
||||
<CalendarIcon class="h-6 w-6 text-secondary" />
|
||||
{{ formatMessage(messages.profileJoinedLabel) }}
|
||||
{{ formatRelativeTime(user.created) }}
|
||||
</div>
|
||||
</template>
|
||||
<template #actions>
|
||||
<ButtonStyled size="large">
|
||||
<NuxtLink v-if="auth.user && auth.user.id === user.id" to="/settings/profile">
|
||||
<EditIcon aria-hidden="true" />
|
||||
{{ formatMessage(commonMessages.editButton) }}
|
||||
</NuxtLink>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled size="large" circular type="transparent">
|
||||
<OverflowMenu
|
||||
:options="[
|
||||
{
|
||||
id: 'manage-projects',
|
||||
action: () => navigateTo('/dashboard/projects'),
|
||||
hoverOnly: true,
|
||||
shown: auth.user && auth.user.id === user.id,
|
||||
},
|
||||
{ divider: true, shown: auth.user && auth.user.id === user.id },
|
||||
{
|
||||
id: 'report',
|
||||
action: () =>
|
||||
auth.user ? reportUser(user.id) : navigateTo(getSignInRouteObj(route)),
|
||||
color: 'red',
|
||||
hoverOnly: true,
|
||||
shown: auth.user?.id !== user.id,
|
||||
},
|
||||
{ id: 'copy-id', action: () => copyId() },
|
||||
{ id: 'copy-permalink', action: () => copyPermalink() },
|
||||
{
|
||||
divider: true,
|
||||
shown: auth.user && isAdmin(auth.user),
|
||||
},
|
||||
{
|
||||
id: 'open-billing',
|
||||
action: () => navigateTo(`/admin/billing/${user.id}`),
|
||||
shown: auth.user && isStaff(auth.user),
|
||||
},
|
||||
{
|
||||
id: 'toggle-affiliate',
|
||||
action: () => toggleAffiliate(user.id),
|
||||
shown: isAdminViewing,
|
||||
remainOnClick: true,
|
||||
color: isAffiliate ? 'red' : 'orange',
|
||||
},
|
||||
{
|
||||
id: 'open-info',
|
||||
action: () => $refs.userDetailsModal.show(),
|
||||
shown: auth.user && isStaff(auth.user),
|
||||
},
|
||||
{
|
||||
id: 'open-analytics',
|
||||
action: () =>
|
||||
navigateTo({
|
||||
path: '/dashboard/analytics',
|
||||
query: { user: user.username || user.id },
|
||||
}),
|
||||
shown: auth.user && isAdmin(auth.user),
|
||||
},
|
||||
{
|
||||
id: 'edit-role',
|
||||
action: () => openRoleEditModal(),
|
||||
shown: auth.user && isAdmin(auth.user),
|
||||
},
|
||||
]"
|
||||
aria-label="More options"
|
||||
:dropdown-id="`${baseId}-more-options`"
|
||||
>
|
||||
<MoreVerticalIcon aria-hidden="true" />
|
||||
<template #manage-projects>
|
||||
<BoxIcon aria-hidden="true" />
|
||||
{{ formatMessage(messages.profileManageProjectsButton) }}
|
||||
</template>
|
||||
<template #report>
|
||||
<ReportIcon aria-hidden="true" />
|
||||
{{ formatMessage(commonMessages.reportButton) }}
|
||||
</template>
|
||||
<template #copy-id>
|
||||
<ClipboardCopyIcon aria-hidden="true" />
|
||||
{{ formatMessage(commonMessages.copyIdButton) }}
|
||||
</template>
|
||||
<template #copy-permalink>
|
||||
<ClipboardCopyIcon aria-hidden="true" />
|
||||
{{ formatMessage(commonMessages.copyPermalinkButton) }}
|
||||
</template>
|
||||
<template #open-billing>
|
||||
<CurrencyIcon aria-hidden="true" />
|
||||
{{ formatMessage(messages.billingButton) }}
|
||||
</template>
|
||||
<template #open-info>
|
||||
<InfoIcon aria-hidden="true" />
|
||||
{{ formatMessage(messages.infoButton) }}
|
||||
</template>
|
||||
<template #open-analytics>
|
||||
<ChartIcon aria-hidden="true" />
|
||||
{{ formatMessage(messages.analyticsButton) }}
|
||||
</template>
|
||||
<template #toggle-affiliate>
|
||||
<AffiliateIcon aria-hidden="true" />
|
||||
{{
|
||||
formatMessage(
|
||||
isAffiliate ? messages.removeAffiliateButton : messages.setAffiliateButton,
|
||||
)
|
||||
}}
|
||||
</template>
|
||||
<template #edit-role>
|
||||
<EditIcon aria-hidden="true" />
|
||||
{{ formatMessage(messages.editRoleButton) }}
|
||||
</template>
|
||||
</OverflowMenu>
|
||||
</ButtonStyled>
|
||||
</template>
|
||||
</ContentPageHeader>
|
||||
</UserPageHeader>
|
||||
</div>
|
||||
<div class="normal-page__content">
|
||||
<div v-if="navLinks.length > 2" class="mb-4 max-w-full overflow-x-auto">
|
||||
@@ -509,23 +356,12 @@
|
||||
</template>
|
||||
<script setup>
|
||||
import {
|
||||
AffiliateIcon,
|
||||
BadgeCheckIcon,
|
||||
BoxIcon,
|
||||
CalendarIcon,
|
||||
ChartIcon,
|
||||
CheckIcon,
|
||||
ClipboardCopyIcon,
|
||||
CurrencyIcon,
|
||||
DownloadIcon,
|
||||
EditIcon,
|
||||
GlobeIcon,
|
||||
InfoIcon,
|
||||
LibraryIcon,
|
||||
LinkIcon,
|
||||
LockIcon,
|
||||
MoreVerticalIcon,
|
||||
ReportIcon,
|
||||
SaveIcon,
|
||||
SpinnerIcon,
|
||||
XIcon,
|
||||
@@ -535,22 +371,15 @@ import {
|
||||
ButtonStyled,
|
||||
Combobox,
|
||||
commonMessages,
|
||||
ContentPageHeader,
|
||||
defineMessages,
|
||||
injectModrinthClient,
|
||||
injectNotificationManager,
|
||||
IntlFormatted,
|
||||
NavTabs,
|
||||
NewModal,
|
||||
OverflowMenu,
|
||||
ProjectCard,
|
||||
ProjectCardList,
|
||||
TagItem,
|
||||
useCompactNumber,
|
||||
useFormatDateTime,
|
||||
useFormatNumber,
|
||||
UserBadges,
|
||||
useRelativeTime,
|
||||
useVIntl,
|
||||
} from '@modrinth/ui'
|
||||
import { isAdmin, isStaff, UserBadge } from '@modrinth/utils'
|
||||
@@ -561,6 +390,7 @@ import UpToDate from '~/assets/images/illustrations/up_to_date.svg?component'
|
||||
import AdPlaceholder from '~/components/ui/AdPlaceholder.vue'
|
||||
import CollectionCreateModal from '~/components/ui/create/CollectionCreateModal.vue'
|
||||
import ModalCreation from '~/components/ui/create/ProjectCreateModal.vue'
|
||||
import UserPageHeader from '~/components/ui/UserPageHeader.vue'
|
||||
import { getSignInRouteObj } from '~/composables/auth.ts'
|
||||
import { projectUserSorting } from '~/utils/projects.ts'
|
||||
import { reportUser } from '~/utils/report-helpers.ts'
|
||||
@@ -575,35 +405,14 @@ const config = useRuntimeConfig()
|
||||
const queryClient = useQueryClient()
|
||||
|
||||
const { formatMessage } = useVIntl()
|
||||
const formatNumber = useFormatNumber()
|
||||
const { formatCompactNumber, formatCompactNumberPlural } = useCompactNumber()
|
||||
const formatRelativeTime = useRelativeTime()
|
||||
const formatDateTime = useFormatDateTime({
|
||||
timeStyle: 'short',
|
||||
dateStyle: 'long',
|
||||
})
|
||||
|
||||
const { addNotification } = injectNotificationManager()
|
||||
|
||||
const baseId = useId()
|
||||
|
||||
const messages = defineMessages({
|
||||
profileProjectsLabel: {
|
||||
id: 'profile.label.projects',
|
||||
defaultMessage: '{count} {countPlural, plural, one {project} other {projects}}',
|
||||
},
|
||||
profileDownloadsLabel: {
|
||||
id: 'profile.label.downloads',
|
||||
defaultMessage: '{count} {countPlural, plural, one {download} other {downloads}}',
|
||||
},
|
||||
collectionProjectsCount: {
|
||||
id: 'profile.collection.projects-count',
|
||||
defaultMessage: '{count, plural, one {# project} other {# projects}}',
|
||||
},
|
||||
profileJoinedLabel: {
|
||||
id: 'profile.label.joined',
|
||||
defaultMessage: 'Joined',
|
||||
},
|
||||
savingLabel: {
|
||||
id: 'profile.label.saving',
|
||||
defaultMessage: 'Saving...',
|
||||
@@ -669,10 +478,6 @@ const messages = defineMessages({
|
||||
id: 'profile.label.badges',
|
||||
defaultMessage: 'Badges',
|
||||
},
|
||||
profileManageProjectsButton: {
|
||||
id: 'profile.button.manage-projects',
|
||||
defaultMessage: 'Manage projects',
|
||||
},
|
||||
profileMetaDescription: {
|
||||
id: 'profile.meta.description',
|
||||
defaultMessage: "Download {username}'s projects on Modrinth",
|
||||
@@ -699,42 +504,10 @@ const messages = defineMessages({
|
||||
defaultMessage:
|
||||
"You don't have any collections.\nWould you like to <create-link>create one</create-link>?",
|
||||
},
|
||||
billingButton: {
|
||||
id: 'profile.button.billing',
|
||||
defaultMessage: 'Manage user billing',
|
||||
},
|
||||
infoButton: {
|
||||
id: 'profile.button.info',
|
||||
defaultMessage: 'View user details',
|
||||
},
|
||||
analyticsButton: {
|
||||
id: 'profile.button.analytics',
|
||||
defaultMessage: 'View user analytics',
|
||||
},
|
||||
setAffiliateButton: {
|
||||
id: 'profile.button.set-affiliate',
|
||||
defaultMessage: 'Set as affiliate',
|
||||
},
|
||||
removeAffiliateButton: {
|
||||
id: 'profile.button.remove-affiliate',
|
||||
defaultMessage: 'Remove as affiliate',
|
||||
},
|
||||
affiliateLabel: {
|
||||
id: 'profile.label.affiliate',
|
||||
defaultMessage: 'Affiliate',
|
||||
},
|
||||
editRoleButton: {
|
||||
id: 'profile.button.edit-role',
|
||||
defaultMessage: 'Edit role',
|
||||
},
|
||||
userNotFoundError: {
|
||||
id: 'profile.error.not-found',
|
||||
defaultMessage: 'User not found',
|
||||
},
|
||||
officialAccount: {
|
||||
id: 'profile.official-account',
|
||||
defaultMessage: 'Official Modrinth account',
|
||||
},
|
||||
officialAccountBio: {
|
||||
id: 'profile.official-account.bio',
|
||||
defaultMessage:
|
||||
@@ -891,14 +664,32 @@ async function copyPermalink() {
|
||||
await navigator.clipboard.writeText(`${config.public.siteUrl}/user/${user.value.id}`)
|
||||
}
|
||||
|
||||
function reportProfileFromHeader() {
|
||||
if (!user.value) return
|
||||
if (auth.value.user) {
|
||||
reportUser(user.value.id)
|
||||
} else {
|
||||
navigateTo(getSignInRouteObj(route))
|
||||
}
|
||||
}
|
||||
|
||||
const isAffiliate = computed(() => user.value?.badges & UserBadge.AFFILIATE)
|
||||
const isAdminViewing = computed(() => isAdmin(auth.value.user))
|
||||
const userDetailsModal = useTemplateRef('userDetailsModal')
|
||||
|
||||
async function toggleAffiliate(id) {
|
||||
await client.labrinth.users_v2.patch(id, { badges: user.value.badges ^ (1 << 7) })
|
||||
queryClient.invalidateQueries({ queryKey: ['user', userId] })
|
||||
}
|
||||
|
||||
const profileHeaderSummary = computed(() =>
|
||||
user.value?.bio
|
||||
? user.value.bio
|
||||
: (projects.value?.length ?? 0) === 0
|
||||
? formatMessage(messages.bioFallbackUser)
|
||||
: formatMessage(messages.bioFallbackCreator),
|
||||
)
|
||||
|
||||
const navLinks = computed(() => [
|
||||
{
|
||||
label: formatMessage(commonMessages.allProjectType),
|
||||
@@ -950,7 +741,7 @@ function saveRoleEdit() {
|
||||
|
||||
editRoleModal.value?.hide()
|
||||
})
|
||||
.catch(() => {
|
||||
.catch((error) => {
|
||||
console.error('Failed to update user role:', error)
|
||||
|
||||
addNotification({
|
||||
|
||||
Reference in New Issue
Block a user