You've already forked AstralRinth
forked from didirus/AstralRinth
App redesign (#2946)
* Start of app redesign * format * continue progress * Content page nearly done * Fix recursion issues with content page * Fix update all alignment * Discover page progress * Settings progress * Removed unlocked-size hack that breaks web * Revamp project page, refactor web project page to share code with app, fixed loading bar, misc UI/UX enhancements, update ko-fi logo, update arrow icons, fix web issues caused by floating-vue migration, fix tooltip issues, update web tooltips, clean up web hydration issues * Ads + run prettier * Begin auth refactor, move common messages to ui lib, add i18n extraction to all apps, begin Library refactor * fix ads not hiding when plus log in * rev lockfile changes/conflicts * Fix sign in page * Add generated * (mostly) Data driven search * Fix search mobile issue * profile fixes * Project versions page, fix typescript on UI lib and misc fixes * Remove unused gallery component * Fix linkfunction err * Search filter controls at top, localization for locked filters * Fix provided filter names * Fix navigating from instance browse to main browse * Friends frontend (#2995) * Friends system frontend * (almost) finish frontend * finish friends, fix lint * Fix lint --------- Signed-off-by: Geometrically <18202329+Geometrically@users.noreply.github.com> * Refresh macOS app icon * Update web search UI more * Fix link opens * Fix frontend build --------- Signed-off-by: Geometrically <18202329+Geometrically@users.noreply.github.com> Co-authored-by: Jai A <jaiagr+gpg@pm.me> Co-authored-by: Geometrically <18202329+Geometrically@users.noreply.github.com>
This commit is contained in:
@@ -1,4 +1,7 @@
|
||||
<template>
|
||||
<Teleport v-if="flags.projectBackground" to="#fixed-background-teleport">
|
||||
<ProjectBackgroundGradient :project="project" />
|
||||
</Teleport>
|
||||
<div v-if="route.name.startsWith('type-id-settings')" class="normal-page">
|
||||
<div class="normal-page__sidebar">
|
||||
<aside class="universal-card">
|
||||
@@ -430,47 +433,7 @@
|
||||
}"
|
||||
>
|
||||
<div class="normal-page__header relative my-4">
|
||||
<ContentPageHeader>
|
||||
<template #icon>
|
||||
<Avatar :src="project.icon_url" :alt="project.title" size="96px" />
|
||||
</template>
|
||||
<template #title>
|
||||
{{ project.title }}
|
||||
</template>
|
||||
<template #title-suffix>
|
||||
<Badge v-if="auth.user && currentMember" :type="project.status" class="status-badge" />
|
||||
</template>
|
||||
<template #summary>
|
||||
{{ project.description }}
|
||||
</template>
|
||||
<template #stats>
|
||||
<div
|
||||
class="flex items-center gap-2 border-0 border-r border-solid border-button-bg pr-4 font-semibold"
|
||||
>
|
||||
<DownloadIcon class="h-6 w-6 text-secondary" />
|
||||
{{ $formatNumber(project.downloads) }}
|
||||
</div>
|
||||
<div
|
||||
class="flex items-center gap-2 border-0 border-solid border-button-bg pr-4 md:border-r"
|
||||
>
|
||||
<HeartIcon class="h-6 w-6 text-secondary" />
|
||||
<span class="font-semibold">
|
||||
{{ $formatNumber(project.followers) }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="hidden items-center gap-2 md:flex">
|
||||
<TagsIcon class="h-6 w-6 text-secondary" />
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<div
|
||||
v-for="(category, index) in project.categories"
|
||||
:key="index"
|
||||
class="tag-list__item"
|
||||
>
|
||||
{{ formatCategory(category) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<ProjectHeader :project="project" :member="!!currentMember">
|
||||
<template #actions>
|
||||
<div class="hidden sm:contents">
|
||||
<ButtonStyled
|
||||
@@ -498,73 +461,104 @@
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
</div>
|
||||
<ButtonStyled
|
||||
size="large"
|
||||
circular
|
||||
:color="following ? 'red' : 'standard'"
|
||||
color-fill="none"
|
||||
hover-color-fill="background"
|
||||
>
|
||||
<button
|
||||
v-if="auth.user"
|
||||
v-tooltip="following ? `Unfollow` : `Follow`"
|
||||
:aria-label="following ? `Unfollow` : `Follow`"
|
||||
@click="userFollowProject(project)"
|
||||
<ClientOnly>
|
||||
<ButtonStyled
|
||||
size="large"
|
||||
circular
|
||||
:color="following ? 'red' : 'standard'"
|
||||
color-fill="none"
|
||||
hover-color-fill="background"
|
||||
>
|
||||
<HeartIcon :fill="following ? 'currentColor' : 'none'" aria-hidden="true" />
|
||||
</button>
|
||||
<nuxt-link v-else v-tooltip="'Follow'" to="/auth/sign-in" aria-label="Follow">
|
||||
<HeartIcon aria-hidden="true" />
|
||||
</nuxt-link>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled size="large" circular>
|
||||
<PopoutMenu v-if="auth.user" v-tooltip="'Save'" from="top-right" aria-label="Save">
|
||||
<BookmarkIcon
|
||||
aria-hidden="true"
|
||||
:fill="
|
||||
collections.some((x) => x.projects.includes(project.id))
|
||||
? 'currentColor'
|
||||
: 'none'
|
||||
<button
|
||||
v-if="auth.user"
|
||||
v-tooltip="following ? `Unfollow` : `Follow`"
|
||||
:aria-label="following ? `Unfollow` : `Follow`"
|
||||
@click="userFollowProject(project)"
|
||||
>
|
||||
<HeartIcon :fill="following ? 'currentColor' : 'none'" aria-hidden="true" />
|
||||
</button>
|
||||
<nuxt-link v-else v-tooltip="'Follow'" to="/auth/sign-in" aria-label="Follow">
|
||||
<HeartIcon aria-hidden="true" />
|
||||
</nuxt-link>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled size="large" circular>
|
||||
<PopoutMenu
|
||||
v-if="auth.user"
|
||||
:tooltip="
|
||||
collections.some((x) => x.projects.includes(project.id)) ? 'Saved' : 'Save'
|
||||
"
|
||||
/>
|
||||
<template #menu>
|
||||
<input
|
||||
v-model="displayCollectionsSearch"
|
||||
type="text"
|
||||
placeholder="Search collections..."
|
||||
class="search-input menu-search"
|
||||
from="top-right"
|
||||
aria-label="Save"
|
||||
:dropdown-id="`${baseId}-save`"
|
||||
>
|
||||
<BookmarkIcon
|
||||
aria-hidden="true"
|
||||
:fill="
|
||||
collections.some((x) => x.projects.includes(project.id))
|
||||
? 'currentColor'
|
||||
: 'none'
|
||||
"
|
||||
/>
|
||||
<div v-if="collections.length > 0" class="collections-list">
|
||||
<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)"
|
||||
<template #menu>
|
||||
<input
|
||||
v-model="displayCollectionsSearch"
|
||||
type="text"
|
||||
placeholder="Search collections..."
|
||||
class="search-input menu-search"
|
||||
/>
|
||||
<div v-if="collections.length > 0" class="collections-list">
|
||||
<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">No collections found.</p>
|
||||
</div>
|
||||
<button
|
||||
class="btn collection-button"
|
||||
@click="(event) => $refs.modal_collection.show(event)"
|
||||
>
|
||||
{{ option.name }}
|
||||
</Checkbox>
|
||||
</div>
|
||||
<div v-else class="menu-text">
|
||||
<p class="popout-text">No collections found.</p>
|
||||
</div>
|
||||
<PlusIcon aria-hidden="true" />
|
||||
Create new collection
|
||||
</button>
|
||||
</template>
|
||||
</PopoutMenu>
|
||||
<nuxt-link v-else v-tooltip="'Save'" to="/auth/sign-in" aria-label="Save">
|
||||
<BookmarkIcon aria-hidden="true" />
|
||||
</nuxt-link>
|
||||
</ButtonStyled>
|
||||
<template #fallback>
|
||||
<ButtonStyled size="large" circular>
|
||||
<button
|
||||
class="btn collection-button"
|
||||
@click="(event) => $refs.modal_collection.show(event)"
|
||||
v-if="auth.user"
|
||||
v-tooltip="`Follow`"
|
||||
:aria-label="`Follow`"
|
||||
@click="userFollowProject(project)"
|
||||
>
|
||||
<PlusIcon aria-hidden="true" />
|
||||
Create new collection
|
||||
<HeartIcon aria-hidden="true" />
|
||||
</button>
|
||||
</template>
|
||||
</PopoutMenu>
|
||||
<nuxt-link v-else v-tooltip="'Save'" to="/auth/sign-in" aria-label="Save">
|
||||
<BookmarkIcon aria-hidden="true" />
|
||||
</nuxt-link>
|
||||
</ButtonStyled>
|
||||
<nuxt-link v-else v-tooltip="'Follow'" to="/auth/sign-in" aria-label="Follow">
|
||||
<HeartIcon aria-hidden="true" />
|
||||
</nuxt-link>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled size="large" circular>
|
||||
<nuxt-link v-tooltip="'Save'" to="/auth/sign-in" aria-label="Save">
|
||||
<BookmarkIcon aria-hidden="true" />
|
||||
</nuxt-link>
|
||||
</ButtonStyled>
|
||||
</template>
|
||||
</ClientOnly>
|
||||
<ButtonStyled v-if="auth.user && currentMember" size="large" circular>
|
||||
<nuxt-link
|
||||
v-tooltip="'Settings'"
|
||||
:to="`/${project.project_type}/${project.slug ? project.slug : project.id}/settings`"
|
||||
>
|
||||
<SettingsIcon aria-hidden="true" />
|
||||
@@ -572,6 +566,7 @@
|
||||
</ButtonStyled>
|
||||
<ButtonStyled size="large" circular type="transparent">
|
||||
<OverflowMenu
|
||||
:tooltip="'More options'"
|
||||
:options="[
|
||||
{
|
||||
id: 'analytics',
|
||||
@@ -611,6 +606,7 @@
|
||||
{ id: 'copy-id', action: () => copyId() },
|
||||
]"
|
||||
aria-label="More options"
|
||||
:dropdown-id="`${baseId}-more-options`"
|
||||
>
|
||||
<MoreVerticalIcon aria-hidden="true" />
|
||||
<template #analytics>
|
||||
@@ -632,7 +628,7 @@
|
||||
</OverflowMenu>
|
||||
</ButtonStyled>
|
||||
</template>
|
||||
</ContentPageHeader>
|
||||
</ProjectHeader>
|
||||
<ProjectMemberHeader
|
||||
v-if="currentMember"
|
||||
:project="project"
|
||||
@@ -654,227 +650,37 @@
|
||||
</MessageBanner>
|
||||
</div>
|
||||
<div class="normal-page__sidebar">
|
||||
<div v-if="versions.length > 0" class="card flex-card experimental-styles-within">
|
||||
<h2>{{ formatMessage(compatibilityMessages.title) }}</h2>
|
||||
<section>
|
||||
<h3>{{ formatMessage(compatibilityMessages.minecraftJava) }}</h3>
|
||||
<div class="tag-list">
|
||||
<div
|
||||
v-for="version in getVersionsToDisplay(project)"
|
||||
:key="`version-tag-${version}`"
|
||||
class="tag-list__item"
|
||||
>
|
||||
{{ version }}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section v-if="project.project_type !== 'resourcepack'">
|
||||
<h3>{{ formatMessage(compatibilityMessages.platforms) }}</h3>
|
||||
<div class="tag-list">
|
||||
<div
|
||||
v-for="platform in project.loaders"
|
||||
:key="`platform-tag-${platform}`"
|
||||
:class="`tag-list__item`"
|
||||
:style="`--_color: var(--color-platform-${platform})`"
|
||||
>
|
||||
<svg v-html="tags.loaders.find((x) => x.name === platform).icon"></svg>
|
||||
{{ formatCategory(platform) }}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section
|
||||
v-if="
|
||||
(project.actualProjectType === 'mod' || project.project_type === 'modpack') &&
|
||||
!(project.client_side === 'unsupported' && project.server_side === 'unsupported') &&
|
||||
!(project.client_side === 'unknown' && project.server_side === 'unknown')
|
||||
"
|
||||
>
|
||||
<h3>{{ formatMessage(compatibilityMessages.environments) }}</h3>
|
||||
<div class="tag-list">
|
||||
<div
|
||||
v-if="
|
||||
(project.client_side === 'required' && project.server_side !== 'required') ||
|
||||
(project.client_side === 'optional' && project.server_side === 'optional')
|
||||
"
|
||||
class="tag-list__item"
|
||||
>
|
||||
<ClientIcon aria-hidden="true" />
|
||||
Client-side
|
||||
</div>
|
||||
<div
|
||||
v-if="
|
||||
(project.server_side === 'required' && project.client_side !== 'required') ||
|
||||
(project.client_side === 'optional' && project.server_side === 'optional')
|
||||
"
|
||||
class="tag-list__item"
|
||||
>
|
||||
<ServerIcon aria-hidden="true" />
|
||||
Server-side
|
||||
</div>
|
||||
<div v-if="false" class="tag-list__item">
|
||||
<UserIcon aria-hidden="true" />
|
||||
Singleplayer
|
||||
</div>
|
||||
<div
|
||||
v-if="
|
||||
project.project_type !== 'datapack' &&
|
||||
((project.client_side === 'required' && project.server_side === 'required') ||
|
||||
project.client_side === 'optional' ||
|
||||
(project.client_side === 'required' && project.server_side === 'optional') ||
|
||||
project.server_side === 'optional' ||
|
||||
(project.server_side === 'required' && project.client_side === 'optional'))
|
||||
"
|
||||
class="tag-list__item"
|
||||
>
|
||||
<MonitorSmartphoneIcon aria-hidden="true" />
|
||||
Client and server
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
<ProjectSidebarCompatibility
|
||||
:project="project"
|
||||
:tags="tags"
|
||||
class="card flex-card experimental-styles-within"
|
||||
/>
|
||||
<AdPlaceholder
|
||||
v-if="
|
||||
(!auth.user || !isPermission(auth.user.badges, 1 << 0) || flags.showAdsWithPlus) &&
|
||||
tags.approvedStatuses.includes(project.status)
|
||||
"
|
||||
/>
|
||||
<div
|
||||
v-if="
|
||||
project.issues_url ||
|
||||
project.source_url ||
|
||||
project.wiki_url ||
|
||||
project.discord_url ||
|
||||
project.donation_urls.length > 0
|
||||
"
|
||||
<ProjectSidebarLinks
|
||||
:project="project"
|
||||
:link-target="$external()"
|
||||
class="card flex-card experimental-styles-within"
|
||||
>
|
||||
<h2>{{ formatMessage(linksMessages.title) }}</h2>
|
||||
<div class="links-list">
|
||||
<a
|
||||
v-if="project.issues_url"
|
||||
:href="project.issues_url"
|
||||
:target="$external()"
|
||||
rel="noopener nofollow ugc"
|
||||
>
|
||||
<IssuesIcon aria-hidden="true" />
|
||||
{{ formatMessage(linksMessages.issues) }}
|
||||
<ExternalIcon aria-hidden="true" class="external-icon" />
|
||||
</a>
|
||||
<a
|
||||
v-if="project.source_url"
|
||||
:href="project.source_url"
|
||||
:target="$external()"
|
||||
rel="noopener nofollow ugc"
|
||||
>
|
||||
<CodeIcon aria-hidden="true" />
|
||||
{{ formatMessage(linksMessages.source) }}
|
||||
<ExternalIcon aria-hidden="true" class="external-icon" />
|
||||
</a>
|
||||
<a
|
||||
v-if="project.wiki_url"
|
||||
:href="project.wiki_url"
|
||||
:target="$external()"
|
||||
rel="noopener nofollow ugc"
|
||||
>
|
||||
<WikiIcon aria-hidden="true" />
|
||||
{{ formatMessage(linksMessages.wiki) }}
|
||||
<ExternalIcon aria-hidden="true" class="external-icon" />
|
||||
</a>
|
||||
<a
|
||||
v-if="project.discord_url"
|
||||
:href="project.discord_url"
|
||||
:target="$external()"
|
||||
rel="noopener nofollow ugc"
|
||||
>
|
||||
<DiscordIcon class="shrink" aria-hidden="true" />
|
||||
{{ formatMessage(linksMessages.discord) }}
|
||||
<ExternalIcon aria-hidden="true" class="external-icon" />
|
||||
</a>
|
||||
<hr
|
||||
v-if="
|
||||
(project.issues_url ||
|
||||
project.source_url ||
|
||||
project.wiki_url ||
|
||||
project.discord_url) &&
|
||||
project.donation_urls.length > 0
|
||||
"
|
||||
/>
|
||||
<a
|
||||
v-for="(donation, index) in project.donation_urls"
|
||||
:key="index"
|
||||
:href="donation.url"
|
||||
:target="$external()"
|
||||
rel="noopener nofollow ugc"
|
||||
>
|
||||
<BuyMeACoffeeIcon v-if="donation.id === 'bmac'" aria-hidden="true" />
|
||||
<PatreonIcon v-else-if="donation.id === 'patreon'" aria-hidden="true" />
|
||||
<KoFiIcon v-else-if="donation.id === 'ko-fi'" aria-hidden="true" />
|
||||
<PayPalIcon v-else-if="donation.id === 'paypal'" aria-hidden="true" />
|
||||
<OpenCollectiveIcon
|
||||
v-else-if="donation.id === 'open-collective'"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<HeartIcon v-else-if="donation.id === 'github'" />
|
||||
<CurrencyIcon v-else />
|
||||
<span v-if="donation.id === 'bmac'">{{
|
||||
formatMessage(linksMessages.donateBmac)
|
||||
}}</span>
|
||||
<span v-else-if="donation.id === 'patreon'">{{
|
||||
formatMessage(linksMessages.donatePatreon)
|
||||
}}</span>
|
||||
<span v-else-if="donation.id === 'paypal'">{{
|
||||
formatMessage(linksMessages.donatePayPal)
|
||||
}}</span>
|
||||
<span v-else-if="donation.id === 'ko-fi'">{{
|
||||
formatMessage(linksMessages.donateKoFi)
|
||||
}}</span>
|
||||
<span v-else-if="donation.id === 'github'">{{
|
||||
formatMessage(linksMessages.donateGithub)
|
||||
}}</span>
|
||||
<span v-else>{{ formatMessage(linksMessages.donateGeneric) }}</span>
|
||||
<ExternalIcon aria-hidden="true" class="external-icon" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card flex-card experimental-styles-within">
|
||||
<h2>{{ formatMessage(creatorsMessages.title) }}</h2>
|
||||
<div class="details-list">
|
||||
<template v-if="organization">
|
||||
<nuxt-link
|
||||
class="details-list__item details-list__item--type-large"
|
||||
:to="`/organization/${organization.slug}`"
|
||||
>
|
||||
<Avatar :src="organization.icon_url" :alt="organization.name" size="32px" />
|
||||
<div class="rows">
|
||||
<span>
|
||||
{{ organization.name }}
|
||||
</span>
|
||||
<span class="details-list__item__text--style-secondary">Organization</span>
|
||||
</div>
|
||||
</nuxt-link>
|
||||
<hr v-if="members.length > 0" />
|
||||
</template>
|
||||
<nuxt-link
|
||||
v-for="member in members"
|
||||
:key="`member-${member.id}`"
|
||||
class="details-list__item details-list__item--type-large"
|
||||
:to="'/user/' + member.user.username"
|
||||
>
|
||||
<Avatar :src="member.avatar_url" :alt="member.name" size="32px" circle />
|
||||
<div class="rows">
|
||||
<span class="flex items-center gap-1">
|
||||
{{ member.name }}
|
||||
<CrownIcon
|
||||
v-if="member.is_owner"
|
||||
v-tooltip="formatMessage(creatorsMessages.owner)"
|
||||
class="text-brand-orange"
|
||||
/>
|
||||
</span>
|
||||
<span class="details-list__item__text--style-secondary">{{ member.role }}</span>
|
||||
</div>
|
||||
</nuxt-link>
|
||||
</div>
|
||||
</div>
|
||||
/>
|
||||
<ProjectSidebarCreators
|
||||
:organization="organization"
|
||||
:members="members"
|
||||
:org-link="(slug) => `/organization/${slug}`"
|
||||
:user-link="(username) => `/user/${username}`"
|
||||
class="card flex-card experimental-styles-within"
|
||||
/>
|
||||
<!-- TODO: Finish license modal and enable -->
|
||||
<ProjectSidebarDetails
|
||||
v-if="false"
|
||||
:project="project"
|
||||
:has-versions="versions.length > 0"
|
||||
:link-target="$external()"
|
||||
class="card flex-card experimental-styles-within"
|
||||
/>
|
||||
<div class="card flex-card experimental-styles-within">
|
||||
<h2>{{ formatMessage(detailsMessages.title) }}</h2>
|
||||
<div class="details-list">
|
||||
@@ -1002,23 +808,8 @@ import {
|
||||
UsersIcon,
|
||||
VersionIcon,
|
||||
WrenchIcon,
|
||||
ClientIcon,
|
||||
BookTextIcon,
|
||||
MonitorSmartphoneIcon,
|
||||
WikiIcon,
|
||||
DiscordIcon,
|
||||
CalendarIcon,
|
||||
KoFiIcon,
|
||||
BuyMeACoffeeIcon,
|
||||
IssuesIcon,
|
||||
UserIcon,
|
||||
PayPalIcon,
|
||||
ServerIcon,
|
||||
PatreonIcon,
|
||||
CrownIcon,
|
||||
OpenCollectiveIcon,
|
||||
CodeIcon,
|
||||
CurrencyIcon,
|
||||
} from "@modrinth/assets";
|
||||
import {
|
||||
Avatar,
|
||||
@@ -1028,10 +819,16 @@ import {
|
||||
OverflowMenu,
|
||||
PopoutMenu,
|
||||
ScrollablePanel,
|
||||
ContentPageHeader,
|
||||
ProjectHeader,
|
||||
ProjectSidebarCompatibility,
|
||||
ProjectSidebarCreators,
|
||||
ProjectSidebarLinks,
|
||||
ProjectSidebarDetails,
|
||||
ProjectBackgroundGradient,
|
||||
} from "@modrinth/ui";
|
||||
import { formatCategory, isRejected, isStaff, isUnderReview, renderString } from "@modrinth/utils";
|
||||
import dayjs from "dayjs";
|
||||
import VersionSummary from "@modrinth/ui/src/components/version/VersionSummary.vue";
|
||||
import Badge from "~/components/ui/Badge.vue";
|
||||
import NavTabs from "~/components/ui/NavTabs.vue";
|
||||
import NavStack from "~/components/ui/NavStack.vue";
|
||||
@@ -1045,9 +842,7 @@ import CollectionCreateModal from "~/components/ui/CollectionCreateModal.vue";
|
||||
import ModerationChecklist from "~/components/ui/ModerationChecklist.vue";
|
||||
import Accordion from "~/components/ui/Accordion.vue";
|
||||
import ModrinthIcon from "~/assets/images/utils/modrinth.svg?component";
|
||||
import VersionSummary from "~/components/ui/VersionSummary.vue";
|
||||
import AutomaticAccordion from "~/components/ui/AutomaticAccordion.vue";
|
||||
import { getVersionsToDisplay } from "~/helpers/projects.js";
|
||||
import AdPlaceholder from "~/components/ui/AdPlaceholder.vue";
|
||||
|
||||
const data = useNuxtApp();
|
||||
@@ -1074,6 +869,8 @@ const gameVersionFilterInput = ref();
|
||||
|
||||
const versionFilter = ref("");
|
||||
|
||||
const baseId = useId();
|
||||
|
||||
const currentGameVersion = computed(() => {
|
||||
return (
|
||||
userSelectedGameVersion.value ||
|
||||
@@ -1111,84 +908,6 @@ const getModrinthAppAccordion = ref();
|
||||
|
||||
const formatRelativeTime = useRelativeTime();
|
||||
|
||||
const compatibilityMessages = defineMessages({
|
||||
title: {
|
||||
id: "project.about.compatibility.title",
|
||||
defaultMessage: "Compatibility",
|
||||
},
|
||||
minecraftJava: {
|
||||
id: "project.about.compatibility.game.minecraftJava",
|
||||
defaultMessage: "Minecraft: Java Edition",
|
||||
},
|
||||
platforms: {
|
||||
id: "project.about.compatibility.platforms",
|
||||
defaultMessage: "Platforms",
|
||||
},
|
||||
environments: {
|
||||
id: "project.about.compatibility.environments",
|
||||
defaultMessage: "Supported environments",
|
||||
},
|
||||
});
|
||||
const linksMessages = defineMessages({
|
||||
title: {
|
||||
id: "project.about.links.title",
|
||||
defaultMessage: "Links",
|
||||
},
|
||||
issues: {
|
||||
id: "project.about.links.issues",
|
||||
defaultMessage: "Report issues",
|
||||
},
|
||||
source: {
|
||||
id: "project.about.links.source",
|
||||
defaultMessage: "View source",
|
||||
},
|
||||
wiki: {
|
||||
id: "project.about.links.wiki",
|
||||
defaultMessage: "Visit wiki",
|
||||
},
|
||||
discord: {
|
||||
id: "project.about.links.discord",
|
||||
defaultMessage: "Join Discord server",
|
||||
},
|
||||
donateGeneric: {
|
||||
id: "project.about.links.donate.generic",
|
||||
defaultMessage: "Donate",
|
||||
},
|
||||
donateGitHub: {
|
||||
id: "project.about.links.donate.github",
|
||||
defaultMessage: "Sponsor on GitHub",
|
||||
},
|
||||
donateBmac: {
|
||||
id: "project.about.links.donate.bmac",
|
||||
defaultMessage: "Buy Me a Coffee",
|
||||
},
|
||||
donatePatreon: {
|
||||
id: "project.about.links.donate.patreon",
|
||||
defaultMessage: "Donate on Patreon",
|
||||
},
|
||||
donatePayPal: {
|
||||
id: "project.about.links.donate.paypal",
|
||||
defaultMessage: "Donate on PayPal",
|
||||
},
|
||||
donateKoFi: {
|
||||
id: "project.about.links.donate.kofi",
|
||||
defaultMessage: "Donate on Ko-fi",
|
||||
},
|
||||
donateGithub: {
|
||||
id: "project.about.links.donate.github",
|
||||
defaultMessage: "Sponsor on GitHub",
|
||||
},
|
||||
});
|
||||
const creatorsMessages = defineMessages({
|
||||
title: {
|
||||
id: "project.about.creators.title",
|
||||
defaultMessage: "Creators",
|
||||
},
|
||||
owner: {
|
||||
id: "project.about.creators.owner",
|
||||
defaultMessage: "Project owner",
|
||||
},
|
||||
});
|
||||
const detailsMessages = defineMessages({
|
||||
title: {
|
||||
id: "project.about.details.title",
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
<template>
|
||||
<div class="content">
|
||||
<div class="mb-3 flex">
|
||||
<VersionFilterControl :versions="props.versions" @switch-page="switchPage" />
|
||||
<VersionFilterControl
|
||||
:versions="props.versions"
|
||||
:game-versions="tags.gameVersions"
|
||||
@update:query="updateQuery"
|
||||
/>
|
||||
<Pagination
|
||||
:page="currentPage"
|
||||
:count="Math.ceil(filteredVersions.length / 20)"
|
||||
@@ -72,8 +76,8 @@
|
||||
import { Pagination } from "@modrinth/ui";
|
||||
import { DownloadIcon } from "@modrinth/assets";
|
||||
|
||||
import VersionFilterControl from "@modrinth/ui/src/components/version/VersionFilterControl.vue";
|
||||
import { renderHighlightedString } from "~/helpers/highlight.js";
|
||||
import VersionFilterControl from "~/components/ui/VersionFilterControl.vue";
|
||||
|
||||
const props = defineProps({
|
||||
project: {
|
||||
@@ -108,6 +112,7 @@ useSeoMeta({
|
||||
|
||||
const router = useNativeRouter();
|
||||
const route = useNativeRoute();
|
||||
const tags = useTags();
|
||||
|
||||
const currentPage = ref(Number(route.query.page ?? 1));
|
||||
const filteredVersions = computed(() => {
|
||||
@@ -138,6 +143,21 @@ function switchPage(page) {
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function updateQuery(newQueries) {
|
||||
if (newQueries.page) {
|
||||
currentPage.value = Number(newQueries.page);
|
||||
} else if (newQueries.page === undefined) {
|
||||
currentPage.value = 1;
|
||||
}
|
||||
|
||||
router.replace({
|
||||
query: {
|
||||
...route.query,
|
||||
...newQueries,
|
||||
},
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
|
||||
@@ -1,15 +1,13 @@
|
||||
<template>
|
||||
<section class="normal-page__content">
|
||||
<div
|
||||
v-if="project.body"
|
||||
class="markdown-body card"
|
||||
v-html="renderHighlightedString(project.body || '')"
|
||||
/>
|
||||
<div v-if="project.body" class="card">
|
||||
<ProjectPageDescription :description="project.body" />
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { renderHighlightedString } from "~/helpers/highlight.js";
|
||||
import { ProjectPageDescription } from "@modrinth/ui";
|
||||
|
||||
defineProps({
|
||||
project: {
|
||||
|
||||
@@ -19,283 +19,133 @@
|
||||
</span>
|
||||
<DropArea :accept="acceptFileFromProjectType(project.project_type)" @change="handleFiles" />
|
||||
</div>
|
||||
<div class="mb-3 flex flex-wrap gap-2">
|
||||
<VersionFilterControl
|
||||
ref="versionFilters"
|
||||
:versions="props.versions"
|
||||
@switch-page="switchPage"
|
||||
/>
|
||||
<Pagination
|
||||
:page="currentPage"
|
||||
class="ml-auto mt-auto"
|
||||
:count="Math.ceil(filteredVersions.length / 20)"
|
||||
:link-function="(page) => `?page=${currentPage}`"
|
||||
@switch-page="switchPage"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
v-if="versions.length > 0"
|
||||
class="flex flex-col gap-4 rounded-2xl bg-bg-raised px-6 pb-8 pt-4 supports-[grid-template-columns:subgrid]:grid supports-[grid-template-columns:subgrid]:grid-cols-[1fr_min-content] sm:px-8 supports-[grid-template-columns:subgrid]:sm:grid-cols-[min-content_auto_auto_auto_min-content] supports-[grid-template-columns:subgrid]:xl:grid-cols-[min-content_auto_auto_auto_auto_auto_min-content]"
|
||||
<ProjectPageVersions
|
||||
:project="project"
|
||||
:versions="versions"
|
||||
:show-files="flags.showVersionFilesInTable"
|
||||
:current-member="!!currentMember"
|
||||
:loaders="tags.loaders"
|
||||
:game-versions="tags.gameVersions"
|
||||
:base-id="baseDropdownId"
|
||||
:version-link="
|
||||
(version) =>
|
||||
`/${project.project_type}/${
|
||||
project.slug ? project.slug : project.id
|
||||
}/version/${encodeURI(version.displayUrlEnding)}`
|
||||
"
|
||||
>
|
||||
<div class="versions-grid-row">
|
||||
<div class="w-9 max-sm:hidden"></div>
|
||||
<div class="text-sm font-bold text-contrast max-sm:hidden">Name</div>
|
||||
<div
|
||||
class="text-sm font-bold text-contrast max-sm:hidden sm:max-xl:collapse sm:max-xl:hidden"
|
||||
>
|
||||
Game version
|
||||
</div>
|
||||
<div
|
||||
class="text-sm font-bold text-contrast max-sm:hidden sm:max-xl:collapse sm:max-xl:hidden"
|
||||
>
|
||||
Platforms
|
||||
</div>
|
||||
<div
|
||||
class="text-sm font-bold text-contrast max-sm:hidden sm:max-xl:collapse sm:max-xl:hidden"
|
||||
>
|
||||
Published
|
||||
</div>
|
||||
<div
|
||||
class="text-sm font-bold text-contrast max-sm:hidden sm:max-xl:collapse sm:max-xl:hidden"
|
||||
>
|
||||
Downloads
|
||||
</div>
|
||||
<div class="text-sm font-bold text-contrast max-sm:hidden xl:collapse xl:hidden">
|
||||
Compatibility
|
||||
</div>
|
||||
<div class="text-sm font-bold text-contrast max-sm:hidden xl:collapse xl:hidden">Stats</div>
|
||||
<div class="w-9 max-sm:hidden"></div>
|
||||
</div>
|
||||
<template
|
||||
v-for="(version, index) in filteredVersions.slice((currentPage - 1) * 20, currentPage * 20)"
|
||||
:key="index"
|
||||
>
|
||||
<div
|
||||
:class="`versions-grid-row h-px w-full bg-button-bg ${index === 0 ? `max-sm:!hidden` : ``}`"
|
||||
></div>
|
||||
<div class="versions-grid-row group relative">
|
||||
<nuxt-link
|
||||
class="absolute inset-[calc(-1rem-2px)_-2rem] before:absolute before:inset-0 before:transition-all before:content-[''] hover:before:backdrop-brightness-110"
|
||||
:to="`/${project.project_type}/${
|
||||
project.slug ? project.slug : project.id
|
||||
}/version/${encodeURI(version.displayUrlEnding)}`"
|
||||
></nuxt-link>
|
||||
<div class="flex flex-col justify-center gap-2 sm:contents">
|
||||
<div class="flex flex-row items-center gap-2 sm:contents">
|
||||
<div class="self-center">
|
||||
<div class="relative z-[1] cursor-pointer">
|
||||
<VersionChannelIndicator
|
||||
v-tooltip="`Toggle filter for ${version.version_type}`"
|
||||
:channel="version.version_type"
|
||||
@click="versionFilters.toggleFilter('channel', version.version_type)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="pointer-events-none relative z-[1] flex flex-col justify-center group-hover:underline"
|
||||
>
|
||||
<div class="font-bold text-contrast">{{ version.version_number }}</div>
|
||||
<div class="text-xs font-medium">{{ version.name }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-col justify-center gap-2 sm:contents">
|
||||
<div class="flex flex-row flex-wrap items-center gap-1 xl:contents">
|
||||
<div class="flex items-center">
|
||||
<div class="tag-list">
|
||||
<div
|
||||
v-for="gameVersion in formatVersionsForDisplay(version.game_versions)"
|
||||
:key="`version-tag-${gameVersion}`"
|
||||
v-tooltip="`Toggle filter for ${gameVersion}`"
|
||||
class="tag-list__item z-[1] cursor-pointer hover:underline"
|
||||
@click="versionFilters.toggleFilters('gameVersion', version.game_versions)"
|
||||
>
|
||||
{{ gameVersion }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center">
|
||||
<div class="tag-list">
|
||||
<div
|
||||
v-for="platform in version.loaders"
|
||||
:key="`platform-tag-${platform}`"
|
||||
v-tooltip="`Toggle filter for ${platform}`"
|
||||
:class="`tag-list__item z-[1] cursor-pointer hover:underline`"
|
||||
:style="`--_color: var(--color-platform-${platform})`"
|
||||
@click="versionFilters.toggleFilter('platform', platform)"
|
||||
>
|
||||
<svg v-html="tags.loaders.find((x) => x.name === platform).icon"></svg>
|
||||
{{ formatCategory(platform) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="flex flex-col justify-center gap-1 max-sm:flex-row max-sm:justify-start max-sm:gap-3 xl:contents"
|
||||
>
|
||||
<div
|
||||
v-tooltip="
|
||||
formatMessage(commonMessages.dateAtTimeTooltip, {
|
||||
date: new Date(version.date_published),
|
||||
time: new Date(version.date_published),
|
||||
})
|
||||
"
|
||||
class="z-[1] flex cursor-help items-center gap-1 text-nowrap font-medium xl:self-center"
|
||||
>
|
||||
<CalendarIcon class="xl:hidden" />
|
||||
{{ formatRelativeTime(version.date_published) }}
|
||||
</div>
|
||||
<div
|
||||
class="pointer-events-none z-[1] flex items-center gap-1 font-medium xl:self-center"
|
||||
>
|
||||
<DownloadIcon class="xl:hidden" />
|
||||
{{ formatCompactNumber(version.downloads) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-start justify-end gap-1 sm:items-center">
|
||||
<ButtonStyled circular type="transparent">
|
||||
<a
|
||||
v-tooltip="`Download`"
|
||||
:href="getPrimaryFile(version).url"
|
||||
class="z-[1] group-hover:!bg-brand group-hover:!text-brand-inverted"
|
||||
aria-label="Download"
|
||||
@click="emits('onDownload')"
|
||||
>
|
||||
<DownloadIcon aria-hidden="true" />
|
||||
</a>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled circular type="transparent">
|
||||
<OverflowMenu
|
||||
class="group-hover:!bg-button-bg"
|
||||
:options="[
|
||||
{
|
||||
id: 'download',
|
||||
color: 'primary',
|
||||
hoverFilled: true,
|
||||
link: getPrimaryFile(version).url,
|
||||
action: () => {
|
||||
emits('onDownload');
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'new-tab',
|
||||
action: () => {},
|
||||
link: `/${project.project_type}/${
|
||||
<template #actions="{ version }">
|
||||
<ButtonStyled circular type="transparent">
|
||||
<a
|
||||
v-tooltip="`Download`"
|
||||
:href="getPrimaryFile(version).url"
|
||||
class="group-hover:!bg-brand group-hover:[&>svg]:!text-brand-inverted"
|
||||
aria-label="Download"
|
||||
@click="emits('onDownload')"
|
||||
>
|
||||
<DownloadIcon aria-hidden="true" />
|
||||
</a>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled circular type="transparent">
|
||||
<OverflowMenu
|
||||
class="group-hover:!bg-button-bg"
|
||||
:dropdown-id="`${baseDropdownId}-${version.id}`"
|
||||
:options="[
|
||||
{
|
||||
id: 'download',
|
||||
color: 'primary',
|
||||
hoverFilled: true,
|
||||
link: getPrimaryFile(version).url,
|
||||
action: () => {
|
||||
emits('onDownload');
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'new-tab',
|
||||
action: () => {},
|
||||
link: `/${project.project_type}/${
|
||||
project.slug ? project.slug : project.id
|
||||
}/version/${encodeURI(version.displayUrlEnding)}`,
|
||||
external: true,
|
||||
},
|
||||
{
|
||||
id: 'copy-link',
|
||||
action: () =>
|
||||
copyToClipboard(
|
||||
`https://modrinth.com/${project.project_type}/${
|
||||
project.slug ? project.slug : project.id
|
||||
}/version/${encodeURI(version.displayUrlEnding)}`,
|
||||
external: true,
|
||||
},
|
||||
{
|
||||
id: 'copy-link',
|
||||
action: () =>
|
||||
copyToClipboard(
|
||||
`https://modrinth.com/${project.project_type}/${
|
||||
project.slug ? project.slug : project.id
|
||||
}/version/${encodeURI(version.displayUrlEnding)}`,
|
||||
),
|
||||
},
|
||||
{
|
||||
id: 'share',
|
||||
action: () => {},
|
||||
shown: false,
|
||||
},
|
||||
{
|
||||
id: 'report',
|
||||
color: 'red',
|
||||
hoverFilled: true,
|
||||
action: () =>
|
||||
auth.user ? reportVersion(version.id) : navigateTo('/auth/sign-in'),
|
||||
shown: !currentMember,
|
||||
},
|
||||
{ divider: true, shown: currentMember },
|
||||
{
|
||||
id: 'edit',
|
||||
link: `/${project.project_type}/${
|
||||
project.slug ? project.slug : project.id
|
||||
}/version/${encodeURI(version.displayUrlEnding)}/edit`,
|
||||
shown: currentMember,
|
||||
},
|
||||
{
|
||||
id: 'delete',
|
||||
color: 'red',
|
||||
hoverFilled: true,
|
||||
action: () => {},
|
||||
shown: currentMember && false,
|
||||
},
|
||||
]"
|
||||
aria-label="More options"
|
||||
>
|
||||
<MoreVerticalIcon aria-hidden="true" />
|
||||
<template #download>
|
||||
<DownloadIcon aria-hidden="true" />
|
||||
Download
|
||||
</template>
|
||||
<template #new-tab>
|
||||
<ExternalIcon aria-hidden="true" />
|
||||
Open in new tab
|
||||
</template>
|
||||
<template #copy-link>
|
||||
<LinkIcon aria-hidden="true" />
|
||||
Copy link
|
||||
</template>
|
||||
<template #share>
|
||||
<ShareIcon aria-hidden="true" />
|
||||
Share
|
||||
</template>
|
||||
<template #report>
|
||||
<ReportIcon aria-hidden="true" />
|
||||
Report
|
||||
</template>
|
||||
<template #edit>
|
||||
<EditIcon aria-hidden="true" />
|
||||
Edit
|
||||
</template>
|
||||
<template #delete>
|
||||
<TrashIcon aria-hidden="true" />
|
||||
Delete
|
||||
</template>
|
||||
</OverflowMenu>
|
||||
</ButtonStyled>
|
||||
</div>
|
||||
<div
|
||||
v-if="flags.showVersionFilesInTable"
|
||||
class="tag-list pointer-events-none relative z-[1] col-span-full"
|
||||
),
|
||||
},
|
||||
{
|
||||
id: 'share',
|
||||
action: () => {},
|
||||
shown: false,
|
||||
},
|
||||
{
|
||||
id: 'report',
|
||||
color: 'red',
|
||||
hoverFilled: true,
|
||||
action: () => (auth.user ? reportVersion(version.id) : navigateTo('/auth/sign-in')),
|
||||
shown: !currentMember,
|
||||
},
|
||||
{ divider: true, shown: currentMember },
|
||||
{
|
||||
id: 'edit',
|
||||
link: `/${project.project_type}/${
|
||||
project.slug ? project.slug : project.id
|
||||
}/version/${encodeURI(version.displayUrlEnding)}/edit`,
|
||||
shown: currentMember,
|
||||
},
|
||||
{
|
||||
id: 'delete',
|
||||
color: 'red',
|
||||
hoverFilled: true,
|
||||
action: () => {},
|
||||
shown: currentMember && false,
|
||||
},
|
||||
]"
|
||||
aria-label="More options"
|
||||
>
|
||||
<div
|
||||
v-for="(file, fileIdx) in version.files"
|
||||
:key="`platform-tag-${fileIdx}`"
|
||||
:class="`flex items-center gap-1 text-wrap rounded-full bg-button-bg px-2 py-0.5 text-xs font-medium ${file.primary || fileIdx === 0 ? 'bg-brand-highlight text-contrast' : 'text-primary'}`"
|
||||
>
|
||||
<StarIcon v-if="file.primary || fileIdx === 0" class="shrink-0" />
|
||||
{{ file.filename }} - {{ formatBytes(file.size) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<MoreVerticalIcon aria-hidden="true" />
|
||||
<template #download>
|
||||
<DownloadIcon aria-hidden="true" />
|
||||
Download
|
||||
</template>
|
||||
<template #new-tab>
|
||||
<ExternalIcon aria-hidden="true" />
|
||||
Open in new tab
|
||||
</template>
|
||||
<template #copy-link>
|
||||
<LinkIcon aria-hidden="true" />
|
||||
Copy link
|
||||
</template>
|
||||
<template #share>
|
||||
<ShareIcon aria-hidden="true" />
|
||||
Share
|
||||
</template>
|
||||
<template #report>
|
||||
<ReportIcon aria-hidden="true" />
|
||||
Report
|
||||
</template>
|
||||
<template #edit>
|
||||
<EditIcon aria-hidden="true" />
|
||||
Edit
|
||||
</template>
|
||||
<template #delete>
|
||||
<TrashIcon aria-hidden="true" />
|
||||
Delete
|
||||
</template>
|
||||
</OverflowMenu>
|
||||
</ButtonStyled>
|
||||
</template>
|
||||
</div>
|
||||
<div class="my-3 flex justify-end">
|
||||
<Pagination
|
||||
:page="currentPage"
|
||||
:count="Math.ceil(filteredVersions.length / 20)"
|
||||
:link-function="(page) => `?page=${currentPage}`"
|
||||
@switch-page="switchPage"
|
||||
/>
|
||||
</div>
|
||||
</ProjectPageVersions>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ButtonStyled, OverflowMenu, FileInput, ProjectPageVersions } from "@modrinth/ui";
|
||||
import {
|
||||
ButtonStyled,
|
||||
OverflowMenu,
|
||||
Pagination,
|
||||
VersionChannelIndicator,
|
||||
FileInput,
|
||||
} from "@modrinth/ui";
|
||||
import {
|
||||
StarIcon,
|
||||
CalendarIcon,
|
||||
DownloadIcon,
|
||||
MoreVerticalIcon,
|
||||
TrashIcon,
|
||||
@@ -307,15 +157,9 @@ import {
|
||||
UploadIcon,
|
||||
InfoIcon,
|
||||
} from "@modrinth/assets";
|
||||
import { formatBytes, formatCategory } from "@modrinth/utils";
|
||||
import { formatVersionsForDisplay } from "~/helpers/projects.js";
|
||||
import VersionFilterControl from "~/components/ui/VersionFilterControl.vue";
|
||||
import DropArea from "~/components/ui/DropArea.vue";
|
||||
import { acceptFileFromProjectType } from "~/helpers/fileUtils.js";
|
||||
|
||||
const formatCompactNumber = useCompactNumber();
|
||||
const { formatMessage } = useVIntl();
|
||||
|
||||
const props = defineProps({
|
||||
project: {
|
||||
type: Object,
|
||||
@@ -339,58 +183,18 @@ const props = defineProps({
|
||||
|
||||
const tags = useTags();
|
||||
const flags = useFeatureFlags();
|
||||
const formatRelativeTime = useRelativeTime();
|
||||
const auth = await useAuth();
|
||||
|
||||
const emits = defineEmits(["onDownload"]);
|
||||
|
||||
const route = useNativeRoute();
|
||||
const router = useNativeRouter();
|
||||
|
||||
const currentPage = ref(route.query.page ?? 1);
|
||||
|
||||
function switchPage(page) {
|
||||
currentPage.value = page;
|
||||
|
||||
router.replace({
|
||||
query: {
|
||||
...route.query,
|
||||
page: currentPage.value !== 1 ? currentPage.value : undefined,
|
||||
},
|
||||
});
|
||||
}
|
||||
const baseDropdownId = useId();
|
||||
|
||||
function getPrimaryFile(version) {
|
||||
return version.files.find((x) => x.primary) || version.files[0];
|
||||
}
|
||||
|
||||
const selectedGameVersions = computed(() => {
|
||||
return getArrayOrString(route.query.g) ?? [];
|
||||
});
|
||||
|
||||
const selectedPlatforms = computed(() => {
|
||||
return getArrayOrString(route.query.l) ?? [];
|
||||
});
|
||||
|
||||
const selectedVersionChannels = computed(() => {
|
||||
return getArrayOrString(route.query.c) ?? [];
|
||||
});
|
||||
|
||||
const versionFilters = ref(null);
|
||||
const filteredVersions = computed(() => {
|
||||
return props.versions.filter(
|
||||
(projectVersion) =>
|
||||
(selectedGameVersions.value.length === 0 ||
|
||||
selectedGameVersions.value.some((gameVersion) =>
|
||||
projectVersion.game_versions.includes(gameVersion),
|
||||
)) &&
|
||||
(selectedPlatforms.value.length === 0 ||
|
||||
selectedPlatforms.value.some((loader) => projectVersion.loaders.includes(loader))) &&
|
||||
(selectedVersionChannels.value.length === 0 ||
|
||||
selectedVersionChannels.value.includes(projectVersion.version_type)),
|
||||
);
|
||||
});
|
||||
|
||||
async function handleFiles(files) {
|
||||
await router.push({
|
||||
name: "type-id-version-version",
|
||||
@@ -409,8 +213,3 @@ async function copyToClipboard(text) {
|
||||
await navigator.clipboard.writeText(text);
|
||||
}
|
||||
</script>
|
||||
<style scoped>
|
||||
.versions-grid-row {
|
||||
@apply grid grid-cols-[1fr_min-content] gap-4 supports-[grid-template-columns:subgrid]:col-span-full supports-[grid-template-columns:subgrid]:!grid-cols-subgrid sm:grid-cols-[min-content_1fr_1fr_1fr_min-content] xl:grid-cols-[min-content_1fr_1fr_1fr_1fr_1fr_min-content];
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -83,4 +83,20 @@ definePageMeta({
|
||||
gap: var(--gap-md);
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.turnstile {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
overflow: hidden;
|
||||
border-radius: var(--radius-md);
|
||||
border: 2px solid var(--color-button-bg);
|
||||
height: 65px;
|
||||
width: 100%;
|
||||
|
||||
> div {
|
||||
position: relative;
|
||||
top: -2px;
|
||||
min-width: calc(100% + 4px);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -80,7 +80,7 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { Button, Avatar } from "@modrinth/ui";
|
||||
import { Button, Avatar, commonMessages } from "@modrinth/ui";
|
||||
import { XIcon, CheckIcon } from "@modrinth/assets";
|
||||
import { useBaseFetch } from "@/composables/fetch.js";
|
||||
import { useAuth } from "@/composables/auth.js";
|
||||
|
||||
@@ -68,6 +68,7 @@
|
||||
</template>
|
||||
<script setup>
|
||||
import { SendIcon, MailIcon, KeyIcon } from "@modrinth/assets";
|
||||
import { commonMessages } from "@modrinth/ui";
|
||||
import HCaptcha from "@/components/ui/HCaptcha.vue";
|
||||
|
||||
const { formatMessage } = useVIntl();
|
||||
|
||||
@@ -134,6 +134,7 @@ import {
|
||||
KeyIcon,
|
||||
MailIcon,
|
||||
} from "@modrinth/assets";
|
||||
import { commonMessages } from "@modrinth/ui";
|
||||
import HCaptcha from "@/components/ui/HCaptcha.vue";
|
||||
|
||||
const { formatMessage } = useVIntl();
|
||||
|
||||
@@ -145,7 +145,7 @@ import {
|
||||
MailIcon,
|
||||
SSOGitLabIcon,
|
||||
} from "@modrinth/assets";
|
||||
import { Checkbox } from "@modrinth/ui";
|
||||
import { Checkbox, commonMessages } from "@modrinth/ui";
|
||||
import HCaptcha from "@/components/ui/HCaptcha.vue";
|
||||
|
||||
const { formatMessage } = useVIntl();
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import { Checkbox } from "@modrinth/ui";
|
||||
import { Checkbox, commonMessages } from "@modrinth/ui";
|
||||
import { RightArrowIcon } from "@modrinth/assets";
|
||||
|
||||
const { formatMessage } = useVIntl();
|
||||
|
||||
@@ -380,7 +380,14 @@ import {
|
||||
LibraryIcon,
|
||||
BoxIcon,
|
||||
} from "@modrinth/assets";
|
||||
import { PopoutMenu, FileInput, DropdownSelect, Avatar, Button } from "@modrinth/ui";
|
||||
import {
|
||||
PopoutMenu,
|
||||
FileInput,
|
||||
DropdownSelect,
|
||||
Avatar,
|
||||
Button,
|
||||
commonMessages,
|
||||
} from "@modrinth/ui";
|
||||
|
||||
import WorldIcon from "assets/images/utils/world.svg";
|
||||
import UpToDate from "assets/images/illustrations/up_to_date.svg";
|
||||
|
||||
@@ -42,17 +42,20 @@
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import { LibraryIcon, ChartIcon } from "@modrinth/assets";
|
||||
import {
|
||||
DashboardIcon,
|
||||
CurrencyIcon,
|
||||
ListIcon,
|
||||
ReportIcon,
|
||||
BellIcon as NotificationsIcon,
|
||||
OrganizationIcon,
|
||||
LibraryIcon,
|
||||
ChartIcon,
|
||||
} from "@modrinth/assets";
|
||||
import { commonMessages } from "@modrinth/ui";
|
||||
import NavStack from "~/components/ui/NavStack.vue";
|
||||
import NavStackItem from "~/components/ui/NavStackItem.vue";
|
||||
|
||||
import DashboardIcon from "~/assets/images/utils/dashboard.svg?component";
|
||||
import CurrencyIcon from "~/assets/images/utils/currency.svg?component";
|
||||
import ListIcon from "~/assets/images/utils/list.svg?component";
|
||||
import ReportIcon from "~/assets/images/utils/report.svg?component";
|
||||
import NotificationsIcon from "~/assets/images/utils/bell.svg?component";
|
||||
import OrganizationIcon from "~/assets/images/utils/organization.svg?component";
|
||||
|
||||
const { formatMessage } = useVIntl();
|
||||
|
||||
definePageMeta({
|
||||
|
||||
@@ -95,7 +95,7 @@
|
||||
</template>
|
||||
<script setup>
|
||||
import { BoxIcon, SearchIcon, XIcon, PlusIcon, LinkIcon, LockIcon } from "@modrinth/assets";
|
||||
import { Avatar, Button } from "@modrinth/ui";
|
||||
import { Avatar, Button, commonMessages } from "@modrinth/ui";
|
||||
import WorldIcon from "~/assets/images/utils/world.svg?component";
|
||||
import CollectionCreateModal from "~/components/ui/CollectionCreateModal.vue";
|
||||
|
||||
|
||||
@@ -301,6 +301,18 @@
|
||||
|
||||
<script>
|
||||
import { Multiselect } from "vue-multiselect";
|
||||
import {
|
||||
SettingsIcon,
|
||||
TrashIcon,
|
||||
PlusIcon,
|
||||
XIcon as CrossIcon,
|
||||
IssuesIcon,
|
||||
EditIcon,
|
||||
SaveIcon,
|
||||
SortAscendingIcon as AscendingIcon,
|
||||
SortDescendingIcon as DescendingIcon,
|
||||
} from "@modrinth/assets";
|
||||
import { commonMessages } from "@modrinth/ui";
|
||||
|
||||
import Badge from "~/components/ui/Badge.vue";
|
||||
import Checkbox from "~/components/ui/Checkbox.vue";
|
||||
@@ -309,16 +321,6 @@ import Avatar from "~/components/ui/Avatar.vue";
|
||||
import ModalCreation from "~/components/ui/ModalCreation.vue";
|
||||
import CopyCode from "~/components/ui/CopyCode.vue";
|
||||
|
||||
import SettingsIcon from "~/assets/images/utils/settings.svg?component";
|
||||
import TrashIcon from "~/assets/images/utils/trash.svg?component";
|
||||
import IssuesIcon from "~/assets/images/utils/issues.svg?component";
|
||||
import PlusIcon from "~/assets/images/utils/plus.svg?component";
|
||||
import CrossIcon from "~/assets/images/utils/x.svg?component";
|
||||
import EditIcon from "~/assets/images/utils/edit.svg?component";
|
||||
import SaveIcon from "~/assets/images/utils/save.svg?component";
|
||||
import AscendingIcon from "~/assets/images/utils/sort-asc.svg?component";
|
||||
import DescendingIcon from "~/assets/images/utils/sort-desc.svg?component";
|
||||
|
||||
export default defineNuxtComponent({
|
||||
components: {
|
||||
Avatar,
|
||||
@@ -337,6 +339,7 @@ export default defineNuxtComponent({
|
||||
CopyCode,
|
||||
AscendingIcon,
|
||||
DescendingIcon,
|
||||
commonMessages,
|
||||
},
|
||||
async setup() {
|
||||
const { formatMessage } = useVIntl();
|
||||
|
||||
@@ -84,14 +84,14 @@
|
||||
</template>
|
||||
<template #stats>
|
||||
<div
|
||||
class="flex items-center gap-2 border-0 border-r border-solid border-button-bg pr-4 font-semibold"
|
||||
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-button-bg pr-4 font-semibold"
|
||||
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) }}
|
||||
@@ -252,7 +252,14 @@ import {
|
||||
XIcon,
|
||||
ClipboardCopyIcon,
|
||||
} from "@modrinth/assets";
|
||||
import { Avatar, ButtonStyled, Breadcrumbs, ContentPageHeader, OverflowMenu } from "@modrinth/ui";
|
||||
import {
|
||||
Avatar,
|
||||
ButtonStyled,
|
||||
Breadcrumbs,
|
||||
ContentPageHeader,
|
||||
OverflowMenu,
|
||||
commonMessages,
|
||||
} from "@modrinth/ui";
|
||||
import NavStack from "~/components/ui/NavStack.vue";
|
||||
import NavStackItem from "~/components/ui/NavStackItem.vue";
|
||||
import ModalCreation from "~/components/ui/ModalCreation.vue";
|
||||
|
||||
@@ -311,7 +311,7 @@ import {
|
||||
SortAscendingIcon,
|
||||
SortDescendingIcon,
|
||||
} from "@modrinth/assets";
|
||||
import { Button, Modal, Avatar, CopyCode, Badge, Checkbox } from "@modrinth/ui";
|
||||
import { Button, Modal, Avatar, CopyCode, Badge, Checkbox, commonMessages } from "@modrinth/ui";
|
||||
|
||||
import ModalCreation from "~/components/ui/ModalCreation.vue";
|
||||
import OrganizationProjectTransferModal from "~/components/ui/OrganizationProjectTransferModal.vue";
|
||||
|
||||
@@ -98,6 +98,13 @@ import { useImageUpload } from "~/composables/image-upload.ts";
|
||||
|
||||
const tags = useTags();
|
||||
const route = useNativeRoute();
|
||||
const router = useRouter();
|
||||
|
||||
const auth = await useAuth();
|
||||
|
||||
if (!auth.value.user) {
|
||||
router.push("/auth/sign-in?redirect=" + encodeURIComponent(route.fullPath));
|
||||
}
|
||||
|
||||
const accessQuery = (id: string): string => {
|
||||
return route.query?.[id]?.toString() || "";
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -183,7 +183,7 @@
|
||||
<MoreHorizontalIcon class="h-5 w-5 bg-transparent" />
|
||||
<template #rename> <EditIcon /> Rename </template>
|
||||
<template #restore> <ClipboardCopyIcon /> Restore </template>
|
||||
<template v-if="backup.locked" #lock> <OpenLockIcon /> Unlock </template>
|
||||
<template v-if="backup.locked" #lock> <LockOpenIcon /> Unlock </template>
|
||||
<template v-else #lock> <LockIcon /> Lock </template>
|
||||
<template #download> <DownloadIcon /> Download </template>
|
||||
<template #delete> <TrashIcon /> Delete </template>
|
||||
@@ -231,7 +231,7 @@ import {
|
||||
SettingsIcon,
|
||||
BoxIcon,
|
||||
LockIcon,
|
||||
OpenLockIcon,
|
||||
LockOpenIcon,
|
||||
} from "@modrinth/assets";
|
||||
import { ref, computed } from "vue";
|
||||
import type { Server } from "~/composables/pyroServers";
|
||||
|
||||
@@ -89,12 +89,11 @@ import {
|
||||
LanguagesIcon,
|
||||
CardIcon,
|
||||
} from "@modrinth/assets";
|
||||
import { commonMessages, commonSettingsMessages } from "@modrinth/ui";
|
||||
import NavStack from "~/components/ui/NavStack.vue";
|
||||
import NavStackItem from "~/components/ui/NavStackItem.vue";
|
||||
import MonitorSmartphoneIcon from "~/assets/images/utils/monitor-smartphone.svg?component";
|
||||
|
||||
import { commonMessages, commonSettingsMessages } from "~/utils/common-messages.ts";
|
||||
|
||||
const { formatMessage } = useVIntl();
|
||||
|
||||
const route = useNativeRoute();
|
||||
|
||||
@@ -216,7 +216,15 @@
|
||||
</template>
|
||||
<script setup>
|
||||
import { UploadIcon, PlusIcon, XIcon, TrashIcon, EditIcon, SaveIcon } from "@modrinth/assets";
|
||||
import { CopyCode, ConfirmModal, Button, Checkbox, Avatar, FileInput } from "@modrinth/ui";
|
||||
import {
|
||||
CopyCode,
|
||||
ConfirmModal,
|
||||
Button,
|
||||
Checkbox,
|
||||
Avatar,
|
||||
FileInput,
|
||||
commonSettingsMessages,
|
||||
} from "@modrinth/ui";
|
||||
import Modal from "~/components/ui/Modal.vue";
|
||||
|
||||
import {
|
||||
@@ -226,7 +234,6 @@ import {
|
||||
useScopes,
|
||||
getScopeValue,
|
||||
} from "~/composables/auth/scopes.ts";
|
||||
import { commonSettingsMessages } from "~/utils/common-messages.ts";
|
||||
|
||||
const { formatMessage } = useVIntl();
|
||||
|
||||
|
||||
@@ -88,9 +88,8 @@
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import { Button, ConfirmModal, Avatar } from "@modrinth/ui";
|
||||
import { Button, ConfirmModal, Avatar, commonSettingsMessages } from "@modrinth/ui";
|
||||
import { TrashIcon, CheckIcon } from "@modrinth/assets";
|
||||
import { commonSettingsMessages } from "~/utils/common-messages.ts";
|
||||
import { useScopes } from "~/composables/auth/scopes.ts";
|
||||
|
||||
const { formatMessage } = useVIntl();
|
||||
|
||||
@@ -486,6 +486,7 @@ import {
|
||||
PurchaseModal,
|
||||
ButtonStyled,
|
||||
CopyCode,
|
||||
commonMessages,
|
||||
} from "@modrinth/ui";
|
||||
import {
|
||||
PlusIcon,
|
||||
|
||||
@@ -16,38 +16,12 @@
|
||||
<section class="universal-card">
|
||||
<h2 class="text-2xl">{{ formatMessage(colorTheme.title) }}</h2>
|
||||
<p>{{ formatMessage(colorTheme.description) }}</p>
|
||||
<div class="theme-options mt-4">
|
||||
<button
|
||||
v-for="option in themeOptions"
|
||||
:key="option"
|
||||
class="preview-radio button-base"
|
||||
:class="{ selected: theme.preferred === option }"
|
||||
@click="() => updateColorTheme(option)"
|
||||
>
|
||||
<div class="preview" :class="`${option === 'system' ? systemTheme : option}-mode`">
|
||||
<div class="example-card card card">
|
||||
<div class="example-icon"></div>
|
||||
<div class="example-text-1"></div>
|
||||
<div class="example-text-2"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="label">
|
||||
<RadioButtonChecked v-if="theme.preferred === option" class="radio" />
|
||||
<RadioButtonIcon v-else class="radio" />
|
||||
{{ colorTheme[option] ? formatMessage(colorTheme[option]) : option }}
|
||||
<SunIcon
|
||||
v-if="theme.preferences.light === option"
|
||||
v-tooltip="formatMessage(colorTheme.preferredLight)"
|
||||
class="theme-icon"
|
||||
/>
|
||||
<MoonIcon
|
||||
v-else-if="theme.preferences.dark === option"
|
||||
v-tooltip="formatMessage(colorTheme.preferredDark)"
|
||||
class="theme-icon"
|
||||
/>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
<ThemeSelector
|
||||
:update-color-theme="updateColorTheme"
|
||||
:current-theme="theme.preferred"
|
||||
:theme-options="themeOptions"
|
||||
:system-theme-color="systemTheme"
|
||||
/>
|
||||
</section>
|
||||
<section class="universal-card">
|
||||
<h2 class="text-2xl">{{ formatMessage(projectListLayouts.title) }}</h2>
|
||||
@@ -224,8 +198,8 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { CodeIcon, MoonIcon, RadioButtonChecked, RadioButtonIcon, SunIcon } from "@modrinth/assets";
|
||||
import { Button } from "@modrinth/ui";
|
||||
import { CodeIcon, RadioButtonChecked, RadioButtonIcon } from "@modrinth/assets";
|
||||
import { Button, ThemeSelector } from "@modrinth/ui";
|
||||
import MessageBanner from "~/components/ui/MessageBanner.vue";
|
||||
import type { DisplayLocation } from "~/plugins/cosmetics";
|
||||
import { formatProjectType } from "~/plugins/shorthands.js";
|
||||
@@ -258,34 +232,6 @@ const colorTheme = defineMessages({
|
||||
id: "settings.display.theme.description",
|
||||
defaultMessage: "Select your preferred color theme for Modrinth on this device.",
|
||||
},
|
||||
system: {
|
||||
id: "settings.display.theme.system",
|
||||
defaultMessage: "Sync with system",
|
||||
},
|
||||
light: {
|
||||
id: "settings.display.theme.light",
|
||||
defaultMessage: "Light",
|
||||
},
|
||||
dark: {
|
||||
id: "settings.display.theme.dark",
|
||||
defaultMessage: "Dark",
|
||||
},
|
||||
oled: {
|
||||
id: "settings.display.theme.oled",
|
||||
defaultMessage: "OLED",
|
||||
},
|
||||
retro: {
|
||||
id: "settings.display.theme.retro",
|
||||
defaultMessage: "Retro",
|
||||
},
|
||||
preferredLight: {
|
||||
id: "settings.display.theme.preferred-light-theme",
|
||||
defaultMessage: "Preferred light theme",
|
||||
},
|
||||
preferredDark: {
|
||||
id: "settings.display.theme.preferred-dark-theme",
|
||||
defaultMessage: "Preferred dark theme",
|
||||
},
|
||||
});
|
||||
|
||||
const projectListLayouts = defineMessages({
|
||||
@@ -457,107 +403,6 @@ const listTypes = computed(() => {
|
||||
});
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
.preview-radio {
|
||||
width: 100%;
|
||||
border-radius: var(--radius-md);
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
border: 1px solid var(--color-divider);
|
||||
background-color: var(--color-button-bg);
|
||||
color: var(--color-base);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
outline: 2px solid transparent;
|
||||
|
||||
&.selected {
|
||||
color: var(--color-contrast);
|
||||
|
||||
.label {
|
||||
.radio {
|
||||
color: var(--color-brand);
|
||||
}
|
||||
|
||||
.theme-icon {
|
||||
color: var(--color-text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.preview {
|
||||
background-color: var(--color-bg);
|
||||
padding: 1.5rem;
|
||||
outline: 2px solid transparent;
|
||||
width: 100%;
|
||||
|
||||
.example-card {
|
||||
margin: 0;
|
||||
padding: 1rem;
|
||||
outline: 2px solid transparent;
|
||||
min-height: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
text-align: left;
|
||||
flex-grow: 1;
|
||||
padding: var(--gap-md) var(--gap-lg);
|
||||
|
||||
.radio {
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
|
||||
.theme-icon {
|
||||
color: var(--color-secondary);
|
||||
margin-left: 0.25rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
.theme-options {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(12.5rem, 1fr));
|
||||
gap: var(--gap-lg);
|
||||
|
||||
.preview .example-card {
|
||||
margin: 0;
|
||||
padding: 1rem;
|
||||
display: grid;
|
||||
grid-template: "icon text1" "icon text2";
|
||||
grid-template-columns: auto 1fr;
|
||||
gap: 0.5rem;
|
||||
outline: 2px solid transparent;
|
||||
|
||||
.example-icon {
|
||||
grid-area: icon;
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
background-color: var(--color-button-bg);
|
||||
border-radius: var(--radius-sm);
|
||||
outline: 2px solid transparent;
|
||||
}
|
||||
|
||||
.example-text-1,
|
||||
.example-text-2 {
|
||||
height: 0.5rem;
|
||||
border-radius: var(--radius-sm);
|
||||
outline: 2px solid transparent;
|
||||
}
|
||||
|
||||
.example-text-1 {
|
||||
grid-area: text1;
|
||||
width: 100%;
|
||||
background-color: var(--color-base);
|
||||
}
|
||||
|
||||
.example-text-2 {
|
||||
grid-area: text2;
|
||||
width: 60%;
|
||||
background-color: var(--color-secondary);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.project-lists {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
<script setup lang="ts">
|
||||
import Fuse from "fuse.js/dist/fuse.basic";
|
||||
import { commonSettingsMessages } from "@modrinth/ui";
|
||||
import RadioButtonIcon from "~/assets/images/utils/radio-button.svg?component";
|
||||
import RadioButtonCheckedIcon from "~/assets/images/utils/radio-button-checked.svg?component";
|
||||
import WarningIcon from "~/assets/images/utils/issues.svg?component";
|
||||
import { isModifierKeyDown } from "~/helpers/events.ts";
|
||||
import { commonSettingsMessages } from "~/utils/common-messages.ts";
|
||||
|
||||
const vintl = useVIntl();
|
||||
const { formatMessage } = vintl;
|
||||
|
||||
@@ -203,9 +203,8 @@
|
||||
</template>
|
||||
<script setup>
|
||||
import { PlusIcon, XIcon, TrashIcon, EditIcon, SaveIcon } from "@modrinth/assets";
|
||||
import { Checkbox, ConfirmModal } from "@modrinth/ui";
|
||||
import { Checkbox, ConfirmModal, commonSettingsMessages, commonMessages } from "@modrinth/ui";
|
||||
|
||||
import { commonSettingsMessages } from "~/utils/common-messages.ts";
|
||||
import {
|
||||
hasScope,
|
||||
scopeList,
|
||||
|
||||
@@ -87,8 +87,7 @@
|
||||
|
||||
<script setup>
|
||||
import { UserIcon, SaveIcon, UploadIcon, UndoIcon, XIcon } from "@modrinth/assets";
|
||||
import { Avatar, FileInput, Button } from "@modrinth/ui";
|
||||
import { commonMessages } from "~/utils/common-messages.ts";
|
||||
import { Avatar, FileInput, Button, commonMessages } from "@modrinth/ui";
|
||||
|
||||
useHead({
|
||||
title: "Profile settings - Modrinth",
|
||||
|
||||
@@ -57,7 +57,7 @@
|
||||
</template>
|
||||
<script setup>
|
||||
import { XIcon } from "@modrinth/assets";
|
||||
import { commonSettingsMessages } from "~/utils/common-messages.ts";
|
||||
import { commonMessages, commonSettingsMessages } from "@modrinth/ui";
|
||||
|
||||
definePageMeta({
|
||||
middleware: "auth",
|
||||
|
||||
@@ -22,14 +22,14 @@
|
||||
</template>
|
||||
<template #stats>
|
||||
<div
|
||||
class="flex items-center gap-2 border-0 border-r border-solid border-button-bg pr-4 font-semibold"
|
||||
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
|
||||
class="flex items-center gap-2 border-0 border-r border-solid border-button-bg pr-4 font-semibold"
|
||||
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" />
|
||||
{{ formatCompactNumber(sumDownloads) }}
|
||||
@@ -265,7 +265,7 @@ import {
|
||||
ClipboardCopyIcon,
|
||||
MoreVerticalIcon,
|
||||
} from "@modrinth/assets";
|
||||
import { OverflowMenu, ButtonStyled, ContentPageHeader } from "@modrinth/ui";
|
||||
import { OverflowMenu, ButtonStyled, ContentPageHeader, commonMessages } from "@modrinth/ui";
|
||||
import NavTabs from "~/components/ui/NavTabs.vue";
|
||||
import ProjectCard from "~/components/ui/ProjectCard.vue";
|
||||
import { reportUser } from "~/utils/report-helpers.ts";
|
||||
|
||||
Reference in New Issue
Block a user