Merge commit 'eb595cdc3e4a6953cbde00c0e119e476ef767a52' into beta

This commit is contained in:
2025-07-16 14:34:07 +03:00
108 changed files with 4999 additions and 412 deletions

View File

@@ -29,12 +29,11 @@
class="settings-header__icon"
/>
<div class="settings-header__text">
<h1 class="wrap-as-needed">
{{ project.title }}
</h1>
<h1 class="wrap-as-needed">{{ project.title }}</h1>
<ProjectStatusBadge :status="project.status" />
</div>
</div>
<h2>Project settings</h2>
<NavStack>
<NavStackItem
@@ -111,6 +110,7 @@
</NavStack>
</aside>
</div>
<div class="normal-page__content">
<ProjectMemberHeader
v-if="currentMember"
@@ -145,6 +145,7 @@
/>
</div>
</div>
<div v-else class="experimental-styles-within">
<NewModal ref="settingsModal">
<template #title>
@@ -174,9 +175,11 @@
<div
class="animation-ring-3 flex items-center justify-center rounded-full border-4 border-solid border-brand bg-brand-highlight opacity-40"
></div>
<div
class="animation-ring-2 flex items-center justify-center rounded-full border-4 border-solid border-brand bg-brand-highlight opacity-60"
></div>
<div
class="animation-ring-1 flex items-center justify-center rounded-full border-4 border-solid border-brand bg-brand-highlight"
>
@@ -219,8 +222,7 @@
:href="`modrinth://mod/${project.slug}`"
@click="() => installWithApp()"
>
<ModrinthIcon aria-hidden="true" />
Install with Modrinth App
<ModrinthIcon aria-hidden="true" /> Install with Modrinth App
<ExternalIcon aria-hidden="true" />
</a>
</ButtonStyled>
@@ -240,6 +242,7 @@
<div class="flex h-[2px] w-full rounded-2xl bg-button-bg"></div>
</div>
</div>
<div class="mx-auto flex w-fit flex-col gap-2">
<ButtonStyled v-if="project.game_versions.length === 1">
<div class="disabled button-like">
@@ -327,8 +330,7 @@
}
"
>
{{ gameVersion }}
<CheckIcon v-if="userSelectedGameVersion === gameVersion" />
{{ gameVersion }} <CheckIcon v-if="userSelectedGameVersion === gameVersion" />
</button>
</ButtonStyled>
</ScrollablePanel>
@@ -419,7 +421,6 @@
</ScrollablePanel>
</Accordion>
</div>
<AutomaticAccordion div class="flex flex-col gap-2">
<VersionSummary
v-if="filteredRelease"
@@ -470,10 +471,14 @@
class="new-page sidebar"
:class="{
'alt-layout': cosmetics.leftContentLayout,
'ultimate-sidebar':
'checklist-open':
showModerationChecklist &&
!collapsedModerationChecklist &&
!flags.alwaysShowChecklistAsPopup,
'checklist-collapsed':
showModerationChecklist &&
collapsedModerationChecklist &&
!flags.alwaysShowChecklistAsPopup,
}"
>
<div class="normal-page__header relative my-4">
@@ -485,11 +490,11 @@
:color="route.name === 'type-id-version-version' ? `standard` : `brand`"
>
<button @click="(event) => downloadModal.show(event)">
<DownloadIcon aria-hidden="true" />
Download
<DownloadIcon aria-hidden="true" /> Download
</button>
</ButtonStyled>
</div>
<div class="contents sm:hidden">
<ButtonStyled
size="large"
@@ -554,9 +559,11 @@
</button>
</ButtonStyled>
</div>
<p class="m-0 text-wrap text-sm font-medium leading-tight text-secondary">
Modrinth Servers is the easiest way to play with your friends without hassle!
</p>
<p class="m-0 text-wrap text-sm font-bold text-primary">
Starting at $5<span class="text-xs"> / month</span>
</p>
@@ -621,6 +628,7 @@
{{ option.name }}
</Checkbox>
</div>
<div v-else class="menu-text">
<p class="popout-text">No collections found.</p>
</div>
@@ -628,8 +636,7 @@
class="btn collection-button"
@click="(event) => $refs.modal_collection.show(event)"
>
<PlusIcon aria-hidden="true" />
Create new collection
<PlusIcon aria-hidden="true" /> Create new collection
</button>
</template>
</PopoutMenu>
@@ -712,25 +719,14 @@
:dropdown-id="`${baseId}-more-options`"
>
<MoreVerticalIcon aria-hidden="true" />
<template #analytics>
<ChartIcon aria-hidden="true" />
Analytics
</template>
<template #analytics> <ChartIcon aria-hidden="true" /> Analytics </template>
<template #moderation-checklist>
<ScaleIcon aria-hidden="true" />
Review project
</template>
<template #report>
<ReportIcon aria-hidden="true" />
Report
</template>
<template #copy-id>
<ClipboardCopyIcon aria-hidden="true" />
Copy ID
<ScaleIcon aria-hidden="true" /> Review project
</template>
<template #report> <ReportIcon aria-hidden="true" /> Report </template>
<template #copy-id> <ClipboardCopyIcon aria-hidden="true" /> Copy ID </template>
<template #copy-permalink>
<ClipboardCopyIcon aria-hidden="true" />
Copy permanent link
<ClipboardCopyIcon aria-hidden="true" /> Copy permanent link
</template>
</OverflowMenu>
</ButtonStyled>
@@ -756,6 +752,7 @@
updates unless the author decides to unarchive the project.
</MessageBanner>
</div>
<div class="normal-page__sidebar">
<ProjectSidebarCompatibility
:project="project"
@@ -785,6 +782,7 @@
/>
<div class="card flex-card experimental-styles-within">
<h2>{{ formatMessage(detailsMessages.title) }}</h2>
<div class="details-list">
<div class="details-list__item">
<BookTextIcon aria-hidden="true" />
@@ -813,53 +811,48 @@
<span v-else>{{ licenseIdDisplay }}</span>
</div>
</div>
<div
v-if="project.approved"
v-tooltip="$dayjs(project.approved).format('MMMM D, YYYY [at] h:mm A')"
class="details-list__item"
>
<CalendarIcon aria-hidden="true" />
<div>
{{ formatMessage(detailsMessages.published, { date: publishedDate }) }}
</div>
<div>{{ formatMessage(detailsMessages.published, { date: publishedDate }) }}</div>
</div>
<div
v-else
v-tooltip="$dayjs(project.published).format('MMMM D, YYYY [at] h:mm A')"
class="details-list__item"
>
<CalendarIcon aria-hidden="true" />
<div>
{{ formatMessage(detailsMessages.created, { date: createdDate }) }}
</div>
<div>{{ formatMessage(detailsMessages.created, { date: createdDate }) }}</div>
</div>
<div
v-if="project.status === 'processing' && project.queued"
v-tooltip="$dayjs(project.queued).format('MMMM D, YYYY [at] h:mm A')"
class="details-list__item"
>
<ScaleIcon aria-hidden="true" />
<div>
{{ formatMessage(detailsMessages.submitted, { date: submittedDate }) }}
</div>
<div>{{ formatMessage(detailsMessages.submitted, { date: submittedDate }) }}</div>
</div>
<div
v-if="versions.length > 0 && project.updated"
v-tooltip="$dayjs(project.updated).format('MMMM D, YYYY [at] h:mm A')"
class="details-list__item"
>
<VersionIcon aria-hidden="true" />
<div>
{{ formatMessage(detailsMessages.updated, { date: updatedDate }) }}
</div>
<div>{{ formatMessage(detailsMessages.updated, { date: updatedDate }) }}</div>
</div>
</div>
</div>
</div>
<div class="normal-page__content">
<div class="overflow-x-auto">
<NavTabs :links="navLinks" class="mb-4" />
</div>
<div class="overflow-x-auto"><NavTabs :links="navLinks" class="mb-4" /></div>
<NuxtPage
v-model:project="project"
v-model:versions="versions"
@@ -877,8 +870,10 @@
@delete-version="deleteVersion"
/>
</div>
<div class="normal-page__ultimate-sidebar">
<ModerationChecklist
<!-- Uncomment this to enable the old moderation checklist. -->
<!-- <ModerationChecklist
v-if="auth.user && tags.staffRoles.includes(auth.user.role) && showModerationChecklist"
:project="project"
:future-projects="futureProjects"
@@ -886,11 +881,25 @@
:collapsed="collapsedModerationChecklist"
@exit="showModerationChecklist = false"
@toggle-collapsed="collapsedModerationChecklist = !collapsedModerationChecklist"
/>
/> -->
</div>
</div>
</div>
<div
v-if="auth.user && tags.staffRoles.includes(auth.user.role) && showModerationChecklist"
class="moderation-checklist"
>
<NewModerationChecklist
:project="project"
:future-project-ids="futureProjectIds"
:collapsed="collapsedModerationChecklist"
@exit="showModerationChecklist = false"
@toggle-collapsed="collapsedModerationChecklist = !collapsedModerationChecklist"
/>
</div>
</template>
<script setup>
import {
BookmarkIcon,
@@ -950,16 +959,16 @@ import {
isUnderReview,
renderString,
} from "@modrinth/utils";
import { navigateTo } from "#app";
import dayjs from "dayjs";
import { Tooltip } from "floating-vue";
import { useLocalStorage } from "@vueuse/core";
import { navigateTo } from "#app";
import Accordion from "~/components/ui/Accordion.vue";
// import AdPlaceholder from "~/components/ui/AdPlaceholder.vue";
import AutomaticAccordion from "~/components/ui/AutomaticAccordion.vue";
import Breadcrumbs from "~/components/ui/Breadcrumbs.vue";
import CollectionCreateModal from "~/components/ui/CollectionCreateModal.vue";
import MessageBanner from "~/components/ui/MessageBanner.vue";
import ModerationChecklist from "~/components/ui/ModerationChecklist.vue";
import NavStack from "~/components/ui/NavStack.vue";
import NavStackItem from "~/components/ui/NavStackItem.vue";
import NavTabs from "~/components/ui/NavTabs.vue";
@@ -967,6 +976,7 @@ import ProjectMemberHeader from "~/components/ui/ProjectMemberHeader.vue";
import { userCollectProject } from "~/composables/user.js";
import { reportProject } from "~/utils/report-helpers.ts";
import { saveFeatureFlags } from "~/composables/featureFlags.ts";
import NewModerationChecklist from "~/components/ui/moderation/NewModerationChecklist.vue";
const data = useNuxtApp();
const route = useNativeRoute();
@@ -980,6 +990,7 @@ const flags = useFeatureFlags();
const cosmetics = useCosmetics();
const { formatMessage } = useVIntl();
const { setVisible } = useNotificationRightwards();
const settingsModal = ref();
const downloadModal = ref();
@@ -1551,12 +1562,28 @@ async function copyPermalink() {
const collapsedChecklist = ref(false);
const showModerationChecklist = ref(false);
const collapsedModerationChecklist = ref(false);
const futureProjects = ref([]);
const showModerationChecklist = useLocalStorage(
`show-moderation-checklist-${project.value.id}`,
false,
);
const collapsedModerationChecklist = useLocalStorage("collapsed-moderation-checklist", false);
const futureProjectIds = useLocalStorage("moderation-future-projects", []);
watch(futureProjectIds, (newValue) => {
console.log("Future project IDs updated:", newValue);
});
watch(
showModerationChecklist,
(newValue) => {
setVisible(newValue);
},
{ immediate: true },
);
if (import.meta.client && history && history.state && history.state.showChecklist) {
showModerationChecklist.value = true;
futureProjects.value = history.state.projects;
}
function closeDownloadModal(event) {
@@ -1626,6 +1653,7 @@ const navLinks = computed(() => {
];
});
</script>
<style lang="scss" scoped>
.settings-header {
display: flex;
@@ -1781,4 +1809,16 @@ const navLinks = computed(() => {
left: 18px;
}
}
.moderation-checklist {
position: fixed;
bottom: 1rem;
right: 1rem;
overflow-y: auto;
z-index: 50;
> div {
box-shadow: 0 0 15px rgba(0, 0, 0, 0.3);
}
}
</style>

View File

@@ -705,9 +705,9 @@ export default defineNuxtComponent({
}
.gallery-body {
flex-grow: 1;
width: calc(100% - 2 * var(--spacing-card-md));
padding: var(--spacing-card-sm) var(--spacing-card-md);
overflow-wrap: anywhere;
.gallery-info {
h2 {

View File

@@ -1421,7 +1421,8 @@ useSeoMeta({
width: 25rem;
height: 25rem;
opacity: 0.75;
background: radial-gradient(
background:
radial-gradient(
50% 50% at 50% 50%,
rgba(5, 206, 69, 0.19) 0%,
rgba(15, 19, 49, 0.25) 100%

View File

@@ -266,12 +266,12 @@ const getRangeOfMethod = (method) => {
const maxWithdrawAmount = computed(() => {
const interval = selectedMethod.value.interval;
return interval?.standard ? interval.standard.max : interval?.fixed?.values.slice(-1)[0] ?? 0;
return interval?.standard ? interval.standard.max : (interval?.fixed?.values.slice(-1)[0] ?? 0);
});
const minWithdrawAmount = computed(() => {
const interval = selectedMethod.value.interval;
return interval?.standard ? interval.standard.min : interval?.fixed?.values?.[0] ?? fees.value;
return interval?.standard ? interval.standard.min : (interval?.fixed?.values?.[0] ?? fees.value);
});
const withdrawAccount = computed(() => {

View File

@@ -212,6 +212,10 @@ if (projects.value) {
async function goToProjects() {
const project = projectsFiltered.value[0];
const remainingProjectIds = projectsFiltered.value.slice(1).map((p) => p.id);
localStorage.setItem("moderation-future-projects", JSON.stringify(remainingProjectIds));
await router.push({
name: "type-id",
params: {
@@ -220,7 +224,6 @@ async function goToProjects() {
},
state: {
showChecklist: true,
projects: projectsFiltered.value.slice(1).map((x) => (x.slug ? x.slug : x.id)),
},
});
}

View File

@@ -149,7 +149,8 @@ onMounted(() => {
</script>
<style lang="scss" scoped>
.main-hero {
background: linear-gradient(360deg, rgba(199, 138, 255, 0.2) 10.92%, var(--color-bg) 100%),
background:
linear-gradient(360deg, rgba(199, 138, 255, 0.2) 10.92%, var(--color-bg) 100%),
var(--color-accent-contrast);
margin-top: -5rem;
padding: 11.25rem 1rem 8rem;

View File

@@ -427,11 +427,8 @@
Do Modrinth Servers have DDoS protection?
</summary>
<p class="m-0 ml-6 leading-[160%]">
Yes. All Modrinth Servers come with DDoS protection powered by
<a href="https://us.ovhcloud.com/security/anti-ddos/" target="_blank"
>OVHcloud® Anti-DDoS infrastructure</a
>
which has over 17Tbps capacity. Your server is safe on Modrinth.
Yes. All Modrinth Servers come with DDoS protection, with up to 17Tbps capacity in
some locations.
</p>
</details>
@@ -443,8 +440,9 @@
Where are Modrinth Servers located? Can I choose a region?
</summary>
<p class="m-0 ml-6 leading-[160%]">
We have servers in both North America in Vint Hill, Virginia, and Europe in Limburg,
Germany. More regions to come in the future!
We have servers available in North America and Europe at the moment that you can
choose upon purchase. More regions to come in the future! If you'd like to switch
your region, please contact support.
</p>
</details>

View File

@@ -1020,7 +1020,7 @@ const nodeUnavailableDetails = computed(() => [
{
label: "Error message",
value: nodeAccessible.value
? server.moduleErrors?.general?.error.message ?? "Unknown"
? (server.moduleErrors?.general?.error.message ?? "Unknown")
: "Unable to reach node. Ping test failed.",
type: "block" as const,
},
@@ -1277,7 +1277,8 @@ useHead({
background-repeat: no-repeat;
filter: blur(1rem);
content: "";
background-image: linear-gradient(
background-image:
linear-gradient(
to bottom,
rgba(from var(--color-raised-bg) r g b / 0.2),
rgb(from var(--color-raised-bg) r g b / 0.8)

View File

@@ -101,7 +101,7 @@
<span :class="{ invisible: 'current_file' in op && !op.current_file }">
{{
"current_file" in op
? op.current_file?.split("/")?.pop() ?? "unknown"
? (op.current_file?.split("/")?.pop() ?? "unknown")
: "unknown"
}}
</span>