You've already forked AstralRinth
forked from didirus/AstralRinth
Threads and more! (#1232)
* Begin UI for threads and moderation overhaul * Hide close button on non-report threads * Fix review age coloring * Add project count * Remove action buttons from queue page and add queued date to project page * Hook up to actual data * Remove unused icon * Get up to 1000 projects in queue * prettier * more prettier * Changed all the things * lint * rebuild * Add omorphia * Workaround formatjs bug in ThreadSummary.vue * Fix notifications page on prod * Fix a few notifications and threads bugs * lockfile * Fix duplicate button styles * more fixes and polishing * More fixes * Remove legacy pages * More bugfixes * Add some error catching for reports and notifications * More error handling * fix lint * Add inbox links * Remove loading component and rename member header * Rely on threads always existing * Handle if project update notifs are not grouped * oops * Fix chips on notifications page * Import ModalModeration * finish threads --------- Co-authored-by: triphora <emma@modrinth.com> Co-authored-by: Jai A <jaiagr+gpg@pm.me>
This commit is contained in:
@@ -94,7 +94,7 @@
|
||||
</aside>
|
||||
</div>
|
||||
<div class="normal-page__content">
|
||||
<ProjectPublishingChecklist
|
||||
<ProjectMemberHeader
|
||||
v-if="currentMember"
|
||||
:project="project"
|
||||
:versions="versions"
|
||||
@@ -104,6 +104,8 @@
|
||||
:set-processing="setProcessing"
|
||||
:collapsed="collapsedChecklist"
|
||||
:toggle-collapsed="() => (collapsedChecklist = !collapsedChecklist)"
|
||||
:all-members="allMembers"
|
||||
:update-members="updateMembers"
|
||||
/>
|
||||
<NuxtPage
|
||||
v-model:project="project"
|
||||
@@ -258,7 +260,7 @@
|
||||
</div>
|
||||
<div class="dates">
|
||||
<div
|
||||
v-tooltip="$dayjs(project.published).format('MMMM D, YYYY [at] h:mm:ss A')"
|
||||
v-tooltip="$dayjs(project.published).format('MMMM D, YYYY [at] h:mm A')"
|
||||
class="date"
|
||||
>
|
||||
<CalendarIcon aria-hidden="true" />
|
||||
@@ -266,13 +268,22 @@
|
||||
<span class="value">{{ fromNow(project.published) }}</span>
|
||||
</div>
|
||||
<div
|
||||
v-tooltip="$dayjs(project.updated).format('MMMM D, YYYY [at] h:mm:ss A')"
|
||||
v-tooltip="$dayjs(project.updated).format('MMMM D, YYYY [at] h:mm A')"
|
||||
class="date"
|
||||
>
|
||||
<UpdateIcon aria-hidden="true" />
|
||||
<span class="label">Updated</span>
|
||||
<span class="value">{{ fromNow(project.updated) }}</span>
|
||||
</div>
|
||||
<div
|
||||
v-if="project.status === 'processing' && project.queued"
|
||||
v-tooltip="$dayjs(project.queued).format('MMMM D, YYYY [at] h:mm A')"
|
||||
class="date"
|
||||
>
|
||||
<QueuedIcon aria-hidden="true" />
|
||||
<span class="label">Submitted</span>
|
||||
<span class="value">{{ fromNow(project.queued) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<hr class="card-divider" />
|
||||
<div class="input-group">
|
||||
@@ -361,17 +372,21 @@
|
||||
</button>
|
||||
<button
|
||||
v-if="
|
||||
$tag.approvedStatuses.includes(project.status) || project.status === 'processing'
|
||||
$tag.approvedStatuses.includes(project.status) ||
|
||||
project.status === 'processing' ||
|
||||
($tag.rejectedStatuses.includes(project.status) && project.status !== 'withheld')
|
||||
"
|
||||
class="iconified-button danger-button"
|
||||
@click="openModerationModal('withheld')"
|
||||
>
|
||||
<EyeIcon />
|
||||
<EyeOffIcon />
|
||||
Withhold
|
||||
</button>
|
||||
<button
|
||||
v-if="
|
||||
$tag.approvedStatuses.includes(project.status) || project.status === 'processing'
|
||||
$tag.approvedStatuses.includes(project.status) ||
|
||||
project.status === 'processing' ||
|
||||
($tag.rejectedStatuses.includes(project.status) && project.status !== 'rejected')
|
||||
"
|
||||
class="iconified-button danger-button"
|
||||
@click="openModerationModal('rejected')"
|
||||
@@ -383,15 +398,19 @@
|
||||
<EditIcon />
|
||||
Edit message
|
||||
</button>
|
||||
<nuxt-link class="iconified-button" to="/moderation">
|
||||
<nuxt-link class="iconified-button" to="/moderation/review">
|
||||
<ModerationIcon />
|
||||
Visit moderation queue
|
||||
Visit review queue
|
||||
</nuxt-link>
|
||||
<nuxt-link class="iconified-button" to="/moderation/reports">
|
||||
<ReportIcon />
|
||||
Visit reports
|
||||
</nuxt-link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<section class="normal-page__content">
|
||||
<ProjectPublishingChecklist
|
||||
<ProjectMemberHeader
|
||||
v-if="currentMember"
|
||||
:project="project"
|
||||
:versions="versions"
|
||||
@@ -401,6 +420,8 @@
|
||||
:set-processing="setProcessing"
|
||||
:collapsed="collapsedChecklist"
|
||||
:toggle-collapsed="() => (collapsedChecklist = !collapsedChecklist)"
|
||||
:all-members="allMembers"
|
||||
:update-members="updateMembers"
|
||||
/>
|
||||
<div v-else-if="project.status === 'withheld'" class="card warning" aria-label="Warning">
|
||||
{{ project.title }} has been removed from search by Modrinth's moderators. Please use
|
||||
@@ -455,6 +476,13 @@
|
||||
}/versions`,
|
||||
shown: versions.length > 0 || !!currentMember,
|
||||
},
|
||||
{
|
||||
label: 'Moderation',
|
||||
href: `/${project.project_type}/${
|
||||
project.slug ? project.slug : project.id
|
||||
}/moderation`,
|
||||
shown: !!currentMember,
|
||||
},
|
||||
]"
|
||||
/>
|
||||
<div v-if="$auth.user && currentMember" class="input-group">
|
||||
@@ -689,6 +717,38 @@
|
||||
<CopyCode :text="project.id" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<a
|
||||
v-if="
|
||||
config.public.apiBaseUrl.startsWith('https://api.modrinth.com') &&
|
||||
config.public.siteUrl !== 'https://modrinth.com'
|
||||
"
|
||||
class="iconified-button"
|
||||
:href="`https://modrinth.com/${project.project_type}/${
|
||||
project.slug ? project.slug : project.id
|
||||
}`"
|
||||
rel="noopener nofollow"
|
||||
target="_blank"
|
||||
>
|
||||
<ExternalIcon aria-hidden="true" />
|
||||
View on modrinth.com
|
||||
</a>
|
||||
<a
|
||||
v-else-if="
|
||||
config.public.apiBaseUrl.startsWith('https://staging-api.modrinth.com') &&
|
||||
config.public.siteUrl !== 'https://staging.modrinth.com'
|
||||
"
|
||||
class="iconified-button"
|
||||
:href="`https://staging.modrinth.com/${project.project_type}/${
|
||||
project.slug ? project.slug : project.id
|
||||
}`"
|
||||
rel="noopener nofollow"
|
||||
target="_blank"
|
||||
>
|
||||
<ExternalIcon aria-hidden="true" />
|
||||
View on staging.modrinth.com
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -700,7 +760,9 @@ import CheckIcon from '~/assets/images/utils/check.svg'
|
||||
import ClearIcon from '~/assets/images/utils/clear.svg'
|
||||
import DownloadIcon from '~/assets/images/utils/download.svg'
|
||||
import UpdateIcon from '~/assets/images/utils/updated.svg'
|
||||
import QueuedIcon from '~/assets/images/utils/list-end.svg'
|
||||
import CodeIcon from '~/assets/images/sidebar/mod.svg'
|
||||
import ExternalIcon from '~/assets/images/utils/external.svg'
|
||||
import ReportIcon from '~/assets/images/utils/report.svg'
|
||||
import HeartIcon from '~/assets/images/utils/heart.svg'
|
||||
import IssuesIcon from '~/assets/images/utils/issues.svg'
|
||||
@@ -713,7 +775,7 @@ import PayPalIcon from '~/assets/images/external/paypal.svg'
|
||||
import OpenCollectiveIcon from '~/assets/images/external/opencollective.svg'
|
||||
import UnknownIcon from '~/assets/images/utils/unknown-donation.svg'
|
||||
import ChevronRightIcon from '~/assets/images/utils/chevron-right.svg'
|
||||
import EyeIcon from '~/assets/images/utils/eye.svg'
|
||||
import EyeOffIcon from '~/assets/images/utils/eye-off.svg'
|
||||
import BoxIcon from '~/assets/images/utils/box.svg'
|
||||
import Promotion from '~/components/ads/Promotion.vue'
|
||||
import Badge from '~/components/ui/Badge.vue'
|
||||
@@ -727,7 +789,7 @@ import CopyCode from '~/components/ui/CopyCode.vue'
|
||||
import Avatar from '~/components/ui/Avatar.vue'
|
||||
import NavStack from '~/components/ui/NavStack.vue'
|
||||
import NavStackItem from '~/components/ui/NavStackItem.vue'
|
||||
import ProjectPublishingChecklist from '~/components/ui/ProjectPublishingChecklist.vue'
|
||||
import ProjectMemberHeader from '~/components/ui/ProjectMemberHeader.vue'
|
||||
import SettingsIcon from '~/assets/images/utils/settings.svg'
|
||||
import UsersIcon from '~/assets/images/utils/users.svg'
|
||||
import CategoriesIcon from '~/assets/images/utils/tags.svg'
|
||||
@@ -744,6 +806,7 @@ import Breadcrumbs from '~/components/ui/Breadcrumbs.vue'
|
||||
|
||||
const data = useNuxtApp()
|
||||
const route = useRoute()
|
||||
const config = useRuntimeConfig()
|
||||
|
||||
const user = await useUser()
|
||||
|
||||
@@ -1070,6 +1133,23 @@ function openModerationModal(status) {
|
||||
modalModeration.value.show()
|
||||
}
|
||||
|
||||
async function updateMembers() {
|
||||
allMembers.value = await useAsyncData(
|
||||
`project/${route.params.id}/members`,
|
||||
() => useBaseFetch(`project/${route.params.id}/members`, data.$defaultHeaders()),
|
||||
{
|
||||
transform: (members) => {
|
||||
members.forEach((it, index) => {
|
||||
members[index].avatar_url = it.user.avatar_url
|
||||
members[index].name = it.user.username
|
||||
})
|
||||
|
||||
return members
|
||||
},
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
const collapsedChecklist = ref(false)
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
|
||||
193
pages/[type]/[id]/moderation.vue
Normal file
193
pages/[type]/[id]/moderation.vue
Normal file
@@ -0,0 +1,193 @@
|
||||
<template>
|
||||
<div>
|
||||
<section class="universal-card">
|
||||
<h2>Project status</h2>
|
||||
<Badge :type="project.status" />
|
||||
<p v-if="isApproved(project)">
|
||||
Your project been approved by the moderators and you may freely change project visibility in
|
||||
<router-link :to="`${getProjectLink(project)}/settings`" class="text-link"
|
||||
>your project's settings</router-link
|
||||
>.
|
||||
</p>
|
||||
<p v-else-if="isUnderReview(project)">
|
||||
Project reviews typically take 24 to 48 hours and they will leave a message below if they
|
||||
have any questions or concerns for you. If your review has taken more than 48 hours, check
|
||||
our Discord or social media for moderation delays.
|
||||
</p>
|
||||
<template v-else-if="isRejected(project)">
|
||||
<p>
|
||||
Your project does not currently meet Modrinth's
|
||||
<nuxt-link to="/legal/rules" class="text-link" target="_blank">content rules</nuxt-link>
|
||||
and the moderators have requested you make changes before it can be approved. Read the
|
||||
messages from the moderators below and address their comments before resubmitting.
|
||||
</p>
|
||||
<p class="warning">
|
||||
Repeated submissions without addressing the moderators' comments may result in an account
|
||||
suspension.
|
||||
</p>
|
||||
</template>
|
||||
<h3>Current visibility</h3>
|
||||
<ul class="visibility-info">
|
||||
<li v-if="isListed(project)">
|
||||
<CheckIcon class="good" />
|
||||
Listed in search results
|
||||
</li>
|
||||
<li v-else>
|
||||
<ExitIcon class="bad" />
|
||||
Not listed in search results
|
||||
</li>
|
||||
<li v-if="isListed(project)">
|
||||
<CheckIcon class="good" />
|
||||
Listed on the profiles of members
|
||||
</li>
|
||||
<li v-else>
|
||||
<ExitIcon class="bad" />
|
||||
Not listed on the profiles of members
|
||||
</li>
|
||||
<li v-if="isPrivate(project)">
|
||||
<ExitIcon class="bad" />
|
||||
Not accessible with a direct link
|
||||
</li>
|
||||
<li v-else>
|
||||
<CheckIcon class="good" />
|
||||
Accessible with a direct link
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
<section id="messages" class="universal-card">
|
||||
<h2>Messages</h2>
|
||||
<p>
|
||||
This is a private conversation thread with the Modrinth moderators. They will message you
|
||||
for issues concerning your project on Modrinth, and you are welcome to message them about
|
||||
things concerning your project.
|
||||
</p>
|
||||
<ConversationThread
|
||||
v-if="thread"
|
||||
:thread="thread"
|
||||
:update-thread="(newThread) => (thread = newThread)"
|
||||
:project="project"
|
||||
:set-status="setStatus"
|
||||
:current-member="currentMember"
|
||||
/>
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import ConversationThread from '~/components/ui/thread/ConversationThread.vue'
|
||||
import Badge from '~/components/ui/Badge.vue'
|
||||
import {
|
||||
getProjectLink,
|
||||
isApproved,
|
||||
isListed,
|
||||
isPrivate,
|
||||
isRejected,
|
||||
isUnderReview,
|
||||
} from '~/helpers/projects.js'
|
||||
import ExitIcon from 'assets/images/utils/x.svg'
|
||||
import CheckIcon from 'assets/images/utils/check.svg'
|
||||
|
||||
const props = defineProps({
|
||||
project: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {}
|
||||
},
|
||||
},
|
||||
currentMember: {
|
||||
type: Object,
|
||||
default() {
|
||||
return null
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
const emit = defineEmits(['update:project'])
|
||||
|
||||
const app = useNuxtApp()
|
||||
|
||||
const { data: thread } = await useAsyncData(`thread/${props.project.thread_id}`, () =>
|
||||
useBaseFetch(`thread/${props.project.thread_id}`, app.$defaultHeaders())
|
||||
)
|
||||
async function setStatus(status) {
|
||||
startLoading()
|
||||
|
||||
try {
|
||||
const data = {}
|
||||
data.status = status
|
||||
await useBaseFetch(`project/${props.project.id}`, {
|
||||
method: 'PATCH',
|
||||
body: data,
|
||||
...app.$defaultHeaders(),
|
||||
})
|
||||
const project = props.project
|
||||
project.status = status
|
||||
emit('update:project', project)
|
||||
thread.value = await useBaseFetch(`thread/${thread.value.id}`, app.$defaultHeaders())
|
||||
} catch (err) {
|
||||
app.$notify({
|
||||
group: 'main',
|
||||
title: 'An error occurred',
|
||||
text: err.data ? err.data.description : err,
|
||||
type: 'error',
|
||||
})
|
||||
}
|
||||
|
||||
stopLoading()
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.stacked {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.status-message {
|
||||
:deep(.badge) {
|
||||
display: contents;
|
||||
|
||||
svg {
|
||||
vertical-align: top;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
p:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.unavailable-error {
|
||||
.code {
|
||||
margin-top: var(--spacing-card-sm);
|
||||
}
|
||||
|
||||
svg {
|
||||
vertical-align: top;
|
||||
}
|
||||
}
|
||||
|
||||
.visibility-info {
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
|
||||
li {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--spacing-card-xs);
|
||||
}
|
||||
}
|
||||
|
||||
svg {
|
||||
&.good {
|
||||
color: var(--color-brand-green);
|
||||
}
|
||||
|
||||
&.bad {
|
||||
color: var(--color-special-red);
|
||||
}
|
||||
}
|
||||
|
||||
.warning {
|
||||
color: var(--color-special-orange);
|
||||
}
|
||||
</style>
|
||||
@@ -147,10 +147,12 @@
|
||||
<div class="adjacent-input">
|
||||
<label for="project-visibility">
|
||||
<span class="label__title">Visibility</span>
|
||||
<span class="label__description">
|
||||
<div class="label__description">
|
||||
Listed and archived projects are visible in search. Unlisted projects are published, but
|
||||
not visible in search or on user profiles. Private projects are only accessible by
|
||||
members of the project.
|
||||
|
||||
<p>If approved by the moderators:</p>
|
||||
<ul class="visibility-info">
|
||||
<li>
|
||||
<CheckIcon
|
||||
@@ -183,7 +185,7 @@
|
||||
{{ hasModifiedVisibility() ? 'Will be v' : 'V' }}isible via URL
|
||||
</li>
|
||||
</ul>
|
||||
</span>
|
||||
</div>
|
||||
</label>
|
||||
<Multiselect
|
||||
id="project-visibility"
|
||||
@@ -408,7 +410,7 @@ export default defineNuxtComponent({
|
||||
...this.$defaultHeaders(),
|
||||
})
|
||||
await initUserProjects()
|
||||
await this.$router.push('/dashboard/projects')
|
||||
await this.$router.push('/dashboard/review')
|
||||
this.$notify({
|
||||
group: 'main',
|
||||
title: 'Project deleted',
|
||||
|
||||
@@ -13,17 +13,39 @@
|
||||
project.
|
||||
</span>
|
||||
</span>
|
||||
<div
|
||||
v-if="(currentMember.permissions & MANAGE_INVITES) === MANAGE_INVITES"
|
||||
class="input-group"
|
||||
>
|
||||
<input id="username" v-model="currentUsername" type="text" placeholder="Username" />
|
||||
<div class="input-group">
|
||||
<input
|
||||
id="username"
|
||||
v-model="currentUsername"
|
||||
type="text"
|
||||
placeholder="Username"
|
||||
:disabled="(currentMember.permissions & MANAGE_INVITES) !== MANAGE_INVITES"
|
||||
@keypress.enter="inviteTeamMember()"
|
||||
/>
|
||||
<label for="username" class="hidden">Username</label>
|
||||
<button class="iconified-button brand-button" @click="inviteTeamMember">
|
||||
<button
|
||||
class="iconified-button brand-button"
|
||||
:disabled="(currentMember.permissions & MANAGE_INVITES) !== MANAGE_INVITES"
|
||||
@click="inviteTeamMember()"
|
||||
>
|
||||
<UserPlusIcon />
|
||||
Invite
|
||||
</button>
|
||||
</div>
|
||||
<div class="adjacent-input">
|
||||
<span class="label">
|
||||
<span class="label__title">Leave project</span>
|
||||
<span class="label__description"> Remove yourself as a member of this project. </span>
|
||||
</span>
|
||||
<button
|
||||
class="iconified-button danger-button"
|
||||
:disabled="currentMember.role === 'Owner'"
|
||||
@click="leaveProject()"
|
||||
>
|
||||
<UserRemoveIcon />
|
||||
Leave project
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-for="(member, index) in allTeamMembers"
|
||||
@@ -227,6 +249,7 @@ import TransferIcon from '~/assets/images/utils/transfer.svg'
|
||||
import UserPlusIcon from '~/assets/images/utils/user-plus.svg'
|
||||
import UserRemoveIcon from '~/assets/images/utils/user-x.svg'
|
||||
import Avatar from '~/components/ui/Avatar.vue'
|
||||
import { removeSelfFromTeam } from '~/helpers/teams.js'
|
||||
|
||||
export default defineNuxtComponent({
|
||||
components: {
|
||||
@@ -282,6 +305,11 @@ export default defineNuxtComponent({
|
||||
this.VIEW_PAYOUTS = 1 << 9
|
||||
},
|
||||
methods: {
|
||||
removeSelfFromTeam,
|
||||
async leaveProject() {
|
||||
await removeSelfFromTeam(project.team)
|
||||
await this.$router.push('/dashboard/projects')
|
||||
},
|
||||
async inviteTeamMember() {
|
||||
startLoading()
|
||||
|
||||
@@ -297,6 +325,7 @@ export default defineNuxtComponent({
|
||||
body: data,
|
||||
...this.$defaultHeaders(),
|
||||
})
|
||||
this.currentUsername = ''
|
||||
await this.updateMembers()
|
||||
} catch (err) {
|
||||
this.$notify({
|
||||
|
||||
@@ -135,8 +135,8 @@
|
||||
</button>
|
||||
<button class="iconified-button" @click="version.featured = !version.featured">
|
||||
<StarIcon aria-hidden="true" />
|
||||
<template v-if="!version.featured"> Feature version </template>
|
||||
<template v-else> Unfeature version </template>
|
||||
<template v-if="!version.featured"> Feature version</template>
|
||||
<template v-else> Unfeature version</template>
|
||||
</button>
|
||||
<nuxt-link
|
||||
v-if="currentMember"
|
||||
@@ -160,11 +160,7 @@
|
||||
<DownloadIcon aria-hidden="true" />
|
||||
Download
|
||||
</a>
|
||||
<button
|
||||
v-if="$auth.user && !currentMember"
|
||||
class="iconified-button"
|
||||
@click="$refs.modal_version_report.show()"
|
||||
>
|
||||
<button class="iconified-button" @click="$refs.modal_version_report.show()">
|
||||
<ReportIcon aria-hidden="true" />
|
||||
Report
|
||||
</button>
|
||||
@@ -240,12 +236,12 @@
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
v-if="version.dependencies.length > 0 || (isEditing && project.project_type !== 'modpack')"
|
||||
v-if="deps.length > 0 || (isEditing && project.project_type !== 'modpack')"
|
||||
class="version-page__dependencies universal-card"
|
||||
>
|
||||
<h3>Dependencies</h3>
|
||||
<div
|
||||
v-for="(dependency, index) in version.dependencies.filter((x) => !x.file_name)"
|
||||
v-for="(dependency, index) in deps.filter((x) => !x.file_name)"
|
||||
:key="index"
|
||||
class="dependency"
|
||||
:class="{ 'button-transparent': !isEditing }"
|
||||
@@ -260,11 +256,11 @@
|
||||
<span class="project-title">
|
||||
{{ dependency.project ? dependency.project.title : 'Unknown Project' }}
|
||||
</span>
|
||||
<span v-if="dependency.version">
|
||||
<span v-if="dependency.version" class="dep-type" :class="dependency.dependency_type">
|
||||
Version {{ dependency.version.version_number }} is
|
||||
{{ dependency.dependency_type }}
|
||||
</span>
|
||||
<span v-else class="dep-type">
|
||||
<span v-else class="dep-type" :class="dependency.dependency_type">
|
||||
{{ dependency.dependency_type }}
|
||||
</span>
|
||||
</nuxt-link>
|
||||
@@ -272,11 +268,11 @@
|
||||
<span class="project-title">
|
||||
{{ dependency.project ? dependency.project.title : 'Unknown Project' }}
|
||||
</span>
|
||||
<span v-if="dependency.version">
|
||||
<span v-if="dependency.version" class="dep-type" :class="dependency.dependency_type">
|
||||
Version {{ dependency.version.version_number }} is
|
||||
{{ dependency.dependency_type }}
|
||||
</span>
|
||||
<span v-else class="dep-type">
|
||||
<span v-else class="dep-type" :class="dependency.dependency_type">
|
||||
{{ dependency.dependency_type }}
|
||||
</span>
|
||||
</div>
|
||||
@@ -290,7 +286,7 @@
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
v-for="(dependency, index) in version.dependencies.filter((x) => x.file_name)"
|
||||
v-for="(dependency, index) in deps.filter((x) => x.file_name)"
|
||||
:key="index"
|
||||
class="dependency"
|
||||
>
|
||||
@@ -299,7 +295,7 @@
|
||||
<span class="project-title">
|
||||
{{ dependency.file_name }}
|
||||
</span>
|
||||
<span>Added via overrides</span>
|
||||
<span class="dep-type" :class="dependency.dependency_type">Added via overrides</span>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="isEditing && project.project_type !== 'modpack'" class="add-dependency">
|
||||
@@ -630,7 +626,7 @@
|
||||
<div v-if="!isEditing">
|
||||
<h4>Publication date</h4>
|
||||
<span>
|
||||
{{ $dayjs(version.date_published).format('MMMM D, YYYY [at] h:mm:ss A') }}
|
||||
{{ $dayjs(version.date_published).format('MMMM D, YYYY [at] h:mm A') }}
|
||||
</span>
|
||||
</div>
|
||||
<div v-if="!isEditing && version.author">
|
||||
@@ -896,6 +892,8 @@ export default defineNuxtComponent({
|
||||
|
||||
oldFileTypes = version.files.map((x) => fileTypes.find((y) => y.value === x.file_type))
|
||||
|
||||
const order = ['required', 'optional', 'incompatible', 'embedded']
|
||||
|
||||
return {
|
||||
fileTypes: ref(fileTypes),
|
||||
oldFileTypes: ref(oldFileTypes),
|
||||
@@ -919,6 +917,11 @@ export default defineNuxtComponent({
|
||||
.$dayjs(version.date_published)
|
||||
.format('MMM D, YYYY')}. ${version.downloads} downloads.`
|
||||
),
|
||||
deps: computed(() =>
|
||||
version.dependencies.sort(
|
||||
(a, b) => order.indexOf(a.dependency_type) - order.indexOf(b.dependency_type)
|
||||
)
|
||||
),
|
||||
}
|
||||
},
|
||||
data() {
|
||||
@@ -1428,6 +1431,11 @@ export default defineNuxtComponent({
|
||||
|
||||
.dep-type {
|
||||
text-transform: capitalize;
|
||||
color: var(--color-text-secondary);
|
||||
|
||||
&.incompatible {
|
||||
color: var(--color-red);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1529,6 +1537,7 @@ export default defineNuxtComponent({
|
||||
h4 {
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
label {
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
|
||||
@@ -290,10 +290,6 @@ async function handleFiles(files) {
|
||||
flex-direction: column;
|
||||
gap: var(--spacing-card-xs);
|
||||
}
|
||||
|
||||
&:active:not(&:disabled) {
|
||||
transform: scale(0.99) !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user