You've already forked AstralRinth
forked from didirus/AstralRinth
Rewrite Parity (#647)
* Rewrite Parity * Update SEO, fix modals, add dashes to changelog * Edit create version title * Cache tags, SEO for search/partial noscript support, notifications fix * Deploy? * Fix vercel config * Fix it again * Finish user editing * Remove broken docker build * Switch reports to modals * Update project card * Navbar line animation in most places * Add chips * Move to navlink query params * remove autogen file * Add copy code * Fix webkit text box outlines, port report modal * Update error page * Switch to avatar component * Make keyboard nav work * Fix team member spacing * improve project ID display (#676) * Bug fixes * Update OG site title * More fixes * Design tweaks * Fix card wrapping on mobile * Darken light theme color a little * Sidebar navigation for settings, notifications, and moderation * Change follow icon from a heart to a bell * Revert "Change follow icon from a heart to a bell" This reverts commit e30b46ec5d93c57df847be88eba123c7419dd03b. * Change follows icon in settings * AaaaUUUUUUUGghghhhhhhhh * Project sidebar transparent button animations * Update file input button styling and change icon remove button text * Fix environments filter condition being inverted * Remove -> revert * Improve readability of warning banners on light mode * Fix mobile menu button colors * Clean up notifications page more * Creator dashboard and monetization work * Add processing fees declarations and acknowledgement box * Beta badges * Downgrade Nuxt Vercel Builder * Update the style of button groups to be more consistent * More button consistency * Remove desktop navbar on mobile * Update home page progress indicators * Fix page jumping (Thanks @stairman06) * Make checkbox checked style consistent with other selection indicators * More home page updates * Properly reset NavRows * Move filters menu on mobile * Stylized checkbox updated to match active styling * Filters icon * Respect prefers-reduced-motion * Add most backend payouts changes (untested) * Finish tested payouts code * Allow monetization unenrolling * No longer use brand color for active highlights on standard nav elements * More consistent button group on project page * Rounded tables * Fix some things (#716) * Team member fixes + re-add changelog/versions stuff * Remove dummy data * The great CSS refactor * Remove commented out css * Give modals the legacy label styles and update profile edit labels * Fix active chip size * Remove shadow from selected chip * Require email set for CMP * Update styles of notifications to universal-card * Equivalent exchange, trading some jank for some less bad jank * Fix all gallery buttons being missing when there is only 1 image * Update project creation modal * Make beta badge less bright * Beta badge heading styling * Update withdraw processing fees info * Remove redundant label * be * Fix inverted logic * 2% is 0.02 * Add toggle to turn off alpha modpacks banner * Why warning button? * Add more footer links (#719) * Add more footer links * Move twitter * Make items on user pages less comically large and move ad above navigation * Bump text down a little on home page * Update favicon colors * Remove task list package and change default description to use bullet points * I don't remember why I made this important but let's not * Ah, yes * this doesn't actually need to be important * Align items in input groups * Adjust some spacings and clear creation modal on opening * Versions now clickable * Add link to edit page to default description * Improve monetization information text * Make wrapped text inputs not shrink * Make chips work better * smol margin on clear mod message button * Allow non-authenticated users to access settings * Remove settings anchors * Fix versions page button style on firefox * Add advanced rendering toggle * Update slug input and icon card in project edit page * Legal sidebar * h1 at beginning of description no longer has top margin * Use universal card for legal pages * Update email addresses on legal pages * Update various page titles and descriptions for consistency * Various fixes and consolidation to API URL retrieval Prevents a bug where it's possible to generate the tags under one API, switch the API, and still have tags leftover from the old API Also finally fixes staging URL being jank * Make the theme button show regardless of login state Also remove the change theme from the user dropdown because it's very redundant with the several other ways of changing theme * Make mobile profile dropdown ordering consistent with desktop * Change the base url back * Revert "Change the base url back" This reverts commit c1da89fddb83776b39f626eab33c8dc67f8a75e4. * constantize * Tiny fixes (#722) * Box-shadow chip outlines * Show settings when signed out * mods -> projects * space * Beta badge border * Slug input overflow fix, scrollable * 🙈 it will all be okay 🙊 this is just temporary 🙉 😭😭 forgive me * Fix minor bugs * fix moderation page * More fixes * Temp fix for download button * BEGONE TABLES * Fix download button Co-authored-by: Ryan Cao <70191398+ryanccn@users.noreply.github.com> Co-authored-by: Prospector <prospectordev@gmail.com> Co-authored-by: stairman06 <36215135+stairman06@users.noreply.github.com> Co-authored-by: triphora <emmaffle@modrinth.com>
This commit is contained in:
1528
pages/_type/_id.vue
1528
pages/_type/_id.vue
File diff suppressed because it is too large
Load Diff
@@ -6,56 +6,63 @@
|
||||
@updateVersions="updateVersions"
|
||||
/>
|
||||
<div class="card">
|
||||
<div v-for="version in filteredVersions" :key="version.id">
|
||||
<div class="version-header">
|
||||
<span :class="'circle ' + version.version_type" />
|
||||
<div class="version-header-text">
|
||||
<h2 class="name title-link">
|
||||
<nuxt-link
|
||||
:to="`/${project.project_type}/${
|
||||
project.slug ? project.slug : project.id
|
||||
}/version/${encodeURI(version.displayUrlEnding)}`"
|
||||
>{{ version.name }}</nuxt-link
|
||||
>
|
||||
</h2>
|
||||
<span v-if="members.find((x) => x.user.id === version.author_id)">
|
||||
by
|
||||
<nuxt-link
|
||||
class="text-link"
|
||||
:to="
|
||||
'/user/' +
|
||||
members.find((x) => x.user.id === version.author_id).user
|
||||
.username
|
||||
"
|
||||
>{{
|
||||
members.find((x) => x.user.id === version.author_id).user
|
||||
.username
|
||||
}}</nuxt-link
|
||||
>
|
||||
</span>
|
||||
<span>
|
||||
on
|
||||
{{ $dayjs(version.date_published).format('MMM D, YYYY') }}</span
|
||||
>
|
||||
</div>
|
||||
<a
|
||||
:href="$parent.findPrimary(version).url"
|
||||
class="iconified-button download"
|
||||
:title="`Download ${version.name}`"
|
||||
>
|
||||
<DownloadIcon aria-hidden="true" />
|
||||
Download
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
v-for="version in filteredVersions"
|
||||
:key="version.id"
|
||||
class="changelog-item"
|
||||
>
|
||||
<div
|
||||
v-highlightjs
|
||||
:class="'markdown-body ' + version.version_type"
|
||||
v-html="
|
||||
version.changelog
|
||||
? $xss($md.render(version.changelog))
|
||||
: 'No changelog specified.'
|
||||
"
|
||||
/>
|
||||
:class="`changelog-bar ${version.version_type} ${
|
||||
version.duplicate ? 'duplicate' : ''
|
||||
}`"
|
||||
></div>
|
||||
<div class="version-wrapper">
|
||||
<div class="version-header">
|
||||
<div class="version-header-text">
|
||||
<h2 class="name">
|
||||
<nuxt-link
|
||||
:to="`/${project.project_type}/${
|
||||
project.slug ? project.slug : project.id
|
||||
}/version/${encodeURI(version.displayUrlEnding)}`"
|
||||
>{{ version.name }}</nuxt-link
|
||||
>
|
||||
</h2>
|
||||
<span v-if="members.find((x) => x.user.id === version.author_id)">
|
||||
by
|
||||
<nuxt-link
|
||||
class="text-link"
|
||||
:to="
|
||||
'/user/' +
|
||||
members.find((x) => x.user.id === version.author_id).user
|
||||
.username
|
||||
"
|
||||
>{{
|
||||
members.find((x) => x.user.id === version.author_id).user
|
||||
.username
|
||||
}}</nuxt-link
|
||||
>
|
||||
</span>
|
||||
<span>
|
||||
on
|
||||
{{ $dayjs(version.date_published).format('MMM D, YYYY') }}</span
|
||||
>
|
||||
</div>
|
||||
<a
|
||||
:href="$parent.findPrimary(version).url"
|
||||
class="iconified-button download"
|
||||
:title="`Download ${version.name}`"
|
||||
>
|
||||
<DownloadIcon aria-hidden="true" />
|
||||
Download
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
v-if="version.changelog && !version.duplicate"
|
||||
v-highlightjs
|
||||
class="markdown-body"
|
||||
v-html="$xss($md.render(version.changelog))"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -66,8 +73,8 @@ import VersionFilterControl from '~/components/ui/VersionFilterControl'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
DownloadIcon,
|
||||
VersionFilterControl,
|
||||
DownloadIcon,
|
||||
},
|
||||
props: {
|
||||
project: {
|
||||
@@ -92,21 +99,145 @@ export default {
|
||||
data() {
|
||||
return {
|
||||
filteredVersions: this.versions,
|
||||
currentPage: 1,
|
||||
}
|
||||
},
|
||||
fetch() {
|
||||
if (this.$route.query.page)
|
||||
this.currentPage = parseInt(this.$route.query.page)
|
||||
|
||||
this.filteredVersions = this.versions.map((version, index) => {
|
||||
const nextVersion = this.versions[index + 1]
|
||||
if (
|
||||
nextVersion &&
|
||||
version.changelog &&
|
||||
nextVersion.changelog === version.changelog
|
||||
) {
|
||||
return { duplicate: true, ...version }
|
||||
} else {
|
||||
return { duplicate: false, ...version }
|
||||
}
|
||||
})
|
||||
},
|
||||
head() {
|
||||
const title = `${this.project.title} - Changelog`
|
||||
const description = `Explore the changelog of ${this.project.title}'s ${this.versions.length} versions.`
|
||||
|
||||
return {
|
||||
title,
|
||||
meta: [
|
||||
{
|
||||
hid: 'og:title',
|
||||
name: 'og:title',
|
||||
content: title,
|
||||
},
|
||||
{
|
||||
hid: 'apple-mobile-web-app-title',
|
||||
name: 'apple-mobile-web-app-title',
|
||||
content: title,
|
||||
},
|
||||
{
|
||||
hid: 'og:description',
|
||||
name: 'og:description',
|
||||
content: description,
|
||||
},
|
||||
{
|
||||
hid: 'description',
|
||||
name: 'description',
|
||||
content: description,
|
||||
},
|
||||
],
|
||||
}
|
||||
},
|
||||
auth: false,
|
||||
methods: {
|
||||
switchPage(page, toTop) {
|
||||
this.currentPage = page
|
||||
this.$router.replace(this.getPageLink(page))
|
||||
|
||||
if (toTop) {
|
||||
setTimeout(() => window.scrollTo({ top: 0, behavior: 'smooth' }), 50)
|
||||
setTimeout(() => window.scrollTo({ top: 0, behavior: 'smooth' }), 50)
|
||||
}
|
||||
},
|
||||
getPageLink(page) {
|
||||
if (page === 1) {
|
||||
return this.$route.path
|
||||
} else {
|
||||
return `${this.$route.path}?page=${this.currentPage}`
|
||||
}
|
||||
},
|
||||
updateVersions(updatedVersions) {
|
||||
this.filteredVersions = updatedVersions
|
||||
},
|
||||
},
|
||||
auth: false,
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.changelog-item {
|
||||
display: flex;
|
||||
margin-bottom: 1rem;
|
||||
position: relative;
|
||||
padding-left: 1.8rem;
|
||||
|
||||
.changelog-bar {
|
||||
--color: var(--color-badge-green-bg);
|
||||
|
||||
&.alpha {
|
||||
--color: var(--color-badge-red-bg);
|
||||
}
|
||||
|
||||
&.release {
|
||||
--color: var(--color-badge-green-bg);
|
||||
}
|
||||
|
||||
&.beta {
|
||||
--color: var(--color-badge-yellow-bg);
|
||||
}
|
||||
|
||||
left: 0;
|
||||
top: 0.5rem;
|
||||
width: 0.2rem;
|
||||
min-width: 0.2rem;
|
||||
position: absolute;
|
||||
margin: 0 0.4rem;
|
||||
border-radius: var(--size-rounded-max);
|
||||
min-height: 100%;
|
||||
background-color: var(--color);
|
||||
|
||||
&:before {
|
||||
content: '';
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: -0.4rem;
|
||||
border-radius: var(--size-rounded-max);
|
||||
background-color: var(--color);
|
||||
}
|
||||
|
||||
&.duplicate {
|
||||
background: linear-gradient(
|
||||
to bottom,
|
||||
transparent,
|
||||
transparent 30%,
|
||||
var(--color) 30%,
|
||||
var(--color)
|
||||
);
|
||||
background-size: 100% 10px;
|
||||
}
|
||||
|
||||
&.duplicate {
|
||||
height: calc(100% + 1.5rem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.version-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-top: 0.2rem;
|
||||
|
||||
.circle {
|
||||
min-width: 0.75rem;
|
||||
@@ -114,24 +245,11 @@ export default {
|
||||
border-radius: 50%;
|
||||
display: inline-block;
|
||||
margin-right: 0.25rem;
|
||||
|
||||
&.alpha {
|
||||
background-color: var(--color-badge-red-bg);
|
||||
}
|
||||
|
||||
&.release {
|
||||
background-color: var(--color-badge-green-bg);
|
||||
}
|
||||
|
||||
&.beta {
|
||||
background-color: var(--color-badge-yellow-bg);
|
||||
}
|
||||
}
|
||||
|
||||
.version-header-text {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
margin: 0 0.75rem;
|
||||
flex-wrap: wrap;
|
||||
|
||||
h2 {
|
||||
@@ -155,20 +273,6 @@ export default {
|
||||
}
|
||||
|
||||
.markdown-body {
|
||||
margin: 0.5rem 0.5rem 1rem calc(0.375rem - 1px);
|
||||
padding-left: 1.275rem;
|
||||
border-left: 2px solid var(--color-text);
|
||||
|
||||
&.alpha {
|
||||
border-left-color: var(--color-badge-red-bg);
|
||||
}
|
||||
|
||||
&.release {
|
||||
border-left-color: var(--color-badge-green-bg);
|
||||
}
|
||||
|
||||
&.beta {
|
||||
border-left-color: var(--color-badge-yellow-bg);
|
||||
}
|
||||
margin: 0.5rem 0.5rem 0 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,40 +1,42 @@
|
||||
<template>
|
||||
<div class="page-contents">
|
||||
<header class="card">
|
||||
<div class="columns">
|
||||
<h3 class="column-grow-1">Edit project</h3>
|
||||
<nuxt-link
|
||||
:to="`/${project.project_type}/${
|
||||
project.slug ? project.slug : project.id
|
||||
}/settings`"
|
||||
class="iconified-button column"
|
||||
>
|
||||
<CrossIcon />
|
||||
Cancel
|
||||
</nuxt-link>
|
||||
<button
|
||||
v-if="
|
||||
project.status === 'rejected' ||
|
||||
project.status === 'draft' ||
|
||||
project.status === 'unlisted'
|
||||
"
|
||||
title="Submit for review"
|
||||
class="iconified-button column"
|
||||
:disabled="!$nuxt.$loading"
|
||||
@click="saveProjectReview"
|
||||
>
|
||||
<CheckIcon />
|
||||
Submit for review
|
||||
</button>
|
||||
<button
|
||||
title="Save"
|
||||
class="iconified-button brand-button-colors column"
|
||||
:disabled="!$nuxt.$loading"
|
||||
@click="saveProjectNotForReview"
|
||||
>
|
||||
<SaveIcon />
|
||||
Save changes
|
||||
</button>
|
||||
<div class="page-contents legacy-label-styles">
|
||||
<header class="header-card">
|
||||
<div class="header__row">
|
||||
<h2 class="header__title">Edit project</h2>
|
||||
<div class="input-group">
|
||||
<nuxt-link
|
||||
:to="`/${project.project_type}/${
|
||||
project.slug ? project.slug : project.id
|
||||
}/settings`"
|
||||
class="iconified-button column"
|
||||
>
|
||||
<CrossIcon />
|
||||
Cancel
|
||||
</nuxt-link>
|
||||
<button
|
||||
v-if="
|
||||
project.status === 'rejected' ||
|
||||
project.status === 'draft' ||
|
||||
project.status === 'unlisted'
|
||||
"
|
||||
title="Submit for review"
|
||||
class="iconified-button column"
|
||||
:disabled="!$nuxt.$loading"
|
||||
@click="saveProjectReview"
|
||||
>
|
||||
<CheckIcon />
|
||||
Submit for review
|
||||
</button>
|
||||
<button
|
||||
title="Save"
|
||||
class="iconified-button brand-button column"
|
||||
:disabled="!$nuxt.$loading"
|
||||
@click="saveProjectNotForReview"
|
||||
>
|
||||
<SaveIcon />
|
||||
Save changes
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="showKnownErrors" class="known-errors">
|
||||
<ul>
|
||||
@@ -43,7 +45,7 @@
|
||||
Your project must have a summary.
|
||||
</li>
|
||||
<li v-if="newProject.slug === ''">
|
||||
Your project must have a vanity URL.
|
||||
Your project cannot have an empty URL suffix.
|
||||
</li>
|
||||
<li v-if="!savingAsDraft && newProject.body === ''">
|
||||
Your project must have a body to submit for review.
|
||||
@@ -74,6 +76,7 @@
|
||||
:class="{ 'known-error': newProject.title === '' && showKnownErrors }"
|
||||
type="text"
|
||||
placeholder="Enter the name"
|
||||
maxlength="64"
|
||||
:disabled="
|
||||
(currentMember.permissions & EDIT_DETAILS) !== EDIT_DETAILS
|
||||
"
|
||||
@@ -94,6 +97,7 @@
|
||||
}"
|
||||
type="text"
|
||||
placeholder="Enter the summary"
|
||||
maxlength="256"
|
||||
:disabled="
|
||||
(currentMember.permissions & EDIT_DETAILS) !== EDIT_DETAILS
|
||||
"
|
||||
@@ -164,45 +168,49 @@
|
||||
@input="setCategories"
|
||||
/>
|
||||
</label>
|
||||
<label class="vertical-input">
|
||||
<span>
|
||||
<h3>Vanity URL (slug)<span class="required">*</span></h3>
|
||||
<span class="slug-description"
|
||||
>https://modrinth.com/{{ project.project_type.toLowerCase() }}/{{
|
||||
newProject.slug ? newProject.slug : 'your-slug'
|
||||
}}
|
||||
</span>
|
||||
</span>
|
||||
<input
|
||||
id="name"
|
||||
v-model="newProject.slug"
|
||||
<div class="universal-labels">
|
||||
<label for="slug">
|
||||
<span class="label__title">URL<span class="required">*</span></span>
|
||||
</label>
|
||||
<div
|
||||
class="text-input-wrapper"
|
||||
:class="{ 'known-error': newProject.slug === '' && showKnownErrors }"
|
||||
type="text"
|
||||
placeholder="Enter the vanity URL"
|
||||
:disabled="
|
||||
(currentMember.permissions & EDIT_DETAILS) !== EDIT_DETAILS
|
||||
"
|
||||
/>
|
||||
</label>
|
||||
>
|
||||
<div class="text-input-wrapper__before">
|
||||
https://modrinth.com/{{ project.project_type.toLowerCase() }}/
|
||||
</div>
|
||||
<!-- this is a textarea so it is horizontally scrollable on mobile -->
|
||||
<textarea
|
||||
id="slug"
|
||||
v-model="newProject.slug"
|
||||
type="text"
|
||||
maxlength="64"
|
||||
autocorrect="off"
|
||||
autocomplete="off"
|
||||
autocapitalize="none"
|
||||
rows="1"
|
||||
:disabled="
|
||||
(currentMember.permissions & EDIT_DETAILS) !== EDIT_DETAILS
|
||||
"
|
||||
@input="manualSlug = true"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section class="card project-icon">
|
||||
<h3>Icon</h3>
|
||||
<img
|
||||
:src="
|
||||
previewImage
|
||||
? previewImage
|
||||
: newProject.icon_url && !iconChanged
|
||||
? newProject.icon_url
|
||||
: 'https://cdn.modrinth.com/placeholder.svg'
|
||||
"
|
||||
<Avatar
|
||||
size="lg"
|
||||
class="avatar"
|
||||
:src="previewImage ? previewImage : newProject.icon_url"
|
||||
alt="preview-image"
|
||||
/>
|
||||
<SmartFileInput
|
||||
<FileInput
|
||||
:max-size="262144"
|
||||
:show-icon="false"
|
||||
:show-icon="true"
|
||||
accept="image/png,image/jpeg,image/gif,image/webp"
|
||||
class="choose-image"
|
||||
prompt="Choose image or drag it here"
|
||||
prompt="Choose image"
|
||||
:disabled="(currentMember.permissions & EDIT_DETAILS) !== EDIT_DETAILS"
|
||||
@change="showPreviewImage"
|
||||
/>
|
||||
@@ -212,11 +220,11 @@
|
||||
@click="
|
||||
icon = null
|
||||
previewImage = null
|
||||
iconChanged = true
|
||||
iconChanged = false
|
||||
"
|
||||
>
|
||||
<TrashIcon />
|
||||
Reset
|
||||
<RevertIcon />
|
||||
Revert
|
||||
</button>
|
||||
</section>
|
||||
<section
|
||||
@@ -289,7 +297,7 @@
|
||||
>. HTML can also be used inside your description, not including styles,
|
||||
scripts, and iframes (though YouTube iframes are allowed).
|
||||
</span>
|
||||
<ThisOrThat
|
||||
<Chips
|
||||
v-model="bodyViewMode"
|
||||
class="separator"
|
||||
:items="['source', 'preview']"
|
||||
@@ -329,6 +337,7 @@
|
||||
v-model="newProject.issues_url"
|
||||
type="url"
|
||||
placeholder="Enter a valid URL"
|
||||
maxlength="2048"
|
||||
:disabled="
|
||||
(currentMember.permissions & EDIT_DETAILS) !== EDIT_DETAILS
|
||||
"
|
||||
@@ -341,6 +350,7 @@
|
||||
<input
|
||||
v-model="newProject.source_url"
|
||||
type="url"
|
||||
maxlength="2048"
|
||||
placeholder="Enter a valid URL"
|
||||
/>
|
||||
</label>
|
||||
@@ -351,17 +361,22 @@
|
||||
<input
|
||||
v-model="newProject.wiki_url"
|
||||
type="url"
|
||||
maxlength="2048"
|
||||
placeholder="Enter a valid URL"
|
||||
:disabled="
|
||||
(currentMember.permissions & EDIT_DETAILS) !== EDIT_DETAILS
|
||||
"
|
||||
/>
|
||||
</label>
|
||||
<label title="An invitation link to your Discord server.">
|
||||
<label
|
||||
class="no-margin"
|
||||
title="An invitation link to your Discord server."
|
||||
>
|
||||
<span>Discord invite</span>
|
||||
<input
|
||||
v-model="newProject.discord_url"
|
||||
type="url"
|
||||
maxlength="2048"
|
||||
placeholder="Enter a valid URL"
|
||||
:disabled="
|
||||
(currentMember.permissions & EDIT_DETAILS) !== EDIT_DETAILS
|
||||
@@ -389,7 +404,7 @@
|
||||
>
|
||||
for more information.
|
||||
</span>
|
||||
<div class="input-group">
|
||||
<div class="legacy-input-group">
|
||||
<Multiselect
|
||||
v-model="license"
|
||||
placeholder="Choose license..."
|
||||
@@ -407,7 +422,11 @@
|
||||
<input
|
||||
v-model="license_url"
|
||||
type="url"
|
||||
maxlength="2048"
|
||||
placeholder="License URL"
|
||||
:class="{
|
||||
'known-error': newProject.license_url === '' && showKnownErrors,
|
||||
}"
|
||||
:disabled="
|
||||
(currentMember.permissions & EDIT_DETAILS) !== EDIT_DETAILS
|
||||
"
|
||||
@@ -415,21 +434,23 @@
|
||||
</div>
|
||||
</label>
|
||||
</section>
|
||||
<section class="card donations">
|
||||
<div class="title">
|
||||
<h3>Donation links</h3>
|
||||
<button
|
||||
title="Add a link"
|
||||
class="iconified-button"
|
||||
:disabled="false"
|
||||
@click="
|
||||
donationPlatforms.push({})
|
||||
donationLinks.push('')
|
||||
"
|
||||
>
|
||||
<PlusIcon />
|
||||
Add a link
|
||||
</button>
|
||||
<section class="header-card donations">
|
||||
<div class="header__row">
|
||||
<h3 class="header__title">Donation links</h3>
|
||||
<div class="input-group">
|
||||
<button
|
||||
title="Add a link"
|
||||
class="iconified-button"
|
||||
:disabled="false"
|
||||
@click="
|
||||
donationPlatforms.push({})
|
||||
donationLinks.push('')
|
||||
"
|
||||
>
|
||||
<PlusIcon />
|
||||
Add a link
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div v-for="(item, index) in donationPlatforms" :key="index">
|
||||
<label title="The donation link.">
|
||||
@@ -438,6 +459,7 @@
|
||||
v-model="donationLinks[index]"
|
||||
type="url"
|
||||
placeholder="Enter a valid URL"
|
||||
class="donation-link-input"
|
||||
/>
|
||||
</label>
|
||||
<label title="The donation platform of the link.">
|
||||
@@ -482,29 +504,24 @@ import CheckIcon from '~/assets/images/utils/check.svg?inline'
|
||||
import PlusIcon from '~/assets/images/utils/plus.svg?inline'
|
||||
import SaveIcon from '~/assets/images/utils/save.svg?inline'
|
||||
import TrashIcon from '~/assets/images/utils/trash.svg?inline'
|
||||
import RevertIcon from '~/assets/images/utils/undo.svg?inline'
|
||||
|
||||
import ThisOrThat from '~/components/ui/ThisOrThat'
|
||||
import SmartFileInput from '~/components/ui/SmartFileInput'
|
||||
import Chips from '~/components/ui/Chips'
|
||||
import FileInput from '~/components/ui/FileInput'
|
||||
import Avatar from '~/components/ui/Avatar'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
SmartFileInput,
|
||||
ThisOrThat,
|
||||
Avatar,
|
||||
FileInput,
|
||||
Chips,
|
||||
Multiselect,
|
||||
CrossIcon,
|
||||
CheckIcon,
|
||||
PlusIcon,
|
||||
SaveIcon,
|
||||
TrashIcon,
|
||||
},
|
||||
beforeRouteLeave(to, from, next) {
|
||||
if (
|
||||
this.isEditing &&
|
||||
!window.confirm('Are you sure that you want to leave without saving?')
|
||||
) {
|
||||
return
|
||||
}
|
||||
next()
|
||||
RevertIcon,
|
||||
},
|
||||
props: {
|
||||
project: {
|
||||
@@ -550,6 +567,7 @@ export default {
|
||||
|
||||
showKnownErrors: false,
|
||||
savingAsDraft: false,
|
||||
manualSlug: false,
|
||||
}
|
||||
},
|
||||
fetch() {
|
||||
@@ -601,16 +619,6 @@ export default {
|
||||
}
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
function preventLeave(e) {
|
||||
e.preventDefault()
|
||||
e.returnValue = ''
|
||||
}
|
||||
window.addEventListener('beforeunload', preventLeave)
|
||||
this.$once('hook:beforeDestroy', () => {
|
||||
window.removeEventListener('beforeunload', preventLeave)
|
||||
})
|
||||
},
|
||||
created() {
|
||||
this.UPLOAD_VERSION = 1 << 0
|
||||
this.DELETE_VERSION = 1 << 1
|
||||
@@ -643,7 +651,7 @@ export default {
|
||||
const reviewConditions =
|
||||
this.newProject.body !== '' && this.newProject.versions.length > 0
|
||||
if (
|
||||
this.newProject.name !== '' &&
|
||||
this.newProject.title !== '' &&
|
||||
this.newProject.description !== '' &&
|
||||
this.newProject.slug !== '' &&
|
||||
this.license.short !== null &&
|
||||
@@ -789,13 +797,13 @@ label {
|
||||
|
||||
input,
|
||||
.multiselect,
|
||||
.input-group {
|
||||
.legacy-input-group {
|
||||
flex: 3;
|
||||
height: fit-content;
|
||||
}
|
||||
}
|
||||
|
||||
.input-group {
|
||||
.legacy-input-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
@@ -849,24 +857,10 @@ label {
|
||||
|
||||
header {
|
||||
grid-area: header;
|
||||
padding: var(--spacing-card-md) var(--spacing-card-lg);
|
||||
|
||||
h3 {
|
||||
margin: auto 0;
|
||||
color: var(--color-text-dark);
|
||||
font-weight: var(--font-weight-extrabold);
|
||||
}
|
||||
|
||||
button {
|
||||
margin-left: 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
section.essentials {
|
||||
grid-area: essentials;
|
||||
label {
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 1024px) {
|
||||
input {
|
||||
@@ -877,15 +871,17 @@ section.essentials {
|
||||
|
||||
section.project-icon {
|
||||
grid-area: project-icon;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: var(--spacing-card-sm);
|
||||
|
||||
img {
|
||||
max-width: 100%;
|
||||
margin-bottom: 0.25rem;
|
||||
border-radius: var(--size-rounded-lg);
|
||||
.avatar {
|
||||
margin-bottom: var(--spacing-card-sm);
|
||||
}
|
||||
|
||||
.iconified-button {
|
||||
margin-top: 0.5rem;
|
||||
margin-top: var(--spacing-card-sm);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -901,10 +897,6 @@ section.game-sides {
|
||||
|
||||
.labeled-control {
|
||||
margin-left: var(--spacing-card-lg);
|
||||
|
||||
h3 {
|
||||
margin-bottom: var(--spacing-card-sm);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -959,10 +951,6 @@ section.donations {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
|
||||
button {
|
||||
margin: 0.5rem 0;
|
||||
}
|
||||
}
|
||||
|
||||
.footer {
|
||||
@@ -973,7 +961,9 @@ section.donations {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.card {
|
||||
.card,
|
||||
.universal-card,
|
||||
.header-card {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
@@ -991,4 +981,35 @@ section.donations {
|
||||
margin-left: 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
.legacy-input-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
* {
|
||||
margin-bottom: var(--spacing-card-sm);
|
||||
}
|
||||
}
|
||||
|
||||
.text-input-wrapper {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
textarea {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin-left: 0 !important;
|
||||
white-space: nowrap;
|
||||
overflow-x: auto;
|
||||
overflow-y: none;
|
||||
resize: none;
|
||||
min-height: 0;
|
||||
}
|
||||
margin-bottom: var(--spacing-card-md);
|
||||
}
|
||||
|
||||
.donation-link-input {
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
</p>
|
||||
</div>
|
||||
<div class="controls">
|
||||
<div v-if="gallery.length > 1" class="buttons">
|
||||
<div class="buttons">
|
||||
<button
|
||||
class="close circle-button"
|
||||
@click="expandedGalleryItem = null"
|
||||
@@ -54,10 +54,18 @@
|
||||
<ExpandIcon v-if="!zoomedIn" aria-hidden="true" />
|
||||
<ContractIcon v-else aria-hidden="true" />
|
||||
</button>
|
||||
<button class="previous circle-button" @click="previousImage()">
|
||||
<button
|
||||
v-if="gallery.length > 1"
|
||||
class="previous circle-button"
|
||||
@click="previousImage()"
|
||||
>
|
||||
<LeftArrowIcon aria-hidden="true" />
|
||||
</button>
|
||||
<button class="next circle-button" @click="nextImage()">
|
||||
<button
|
||||
v-if="gallery.length > 1"
|
||||
class="next circle-button"
|
||||
@click="nextImage()"
|
||||
>
|
||||
<RightArrowIcon aria-hidden="true" />
|
||||
</button>
|
||||
</div>
|
||||
@@ -66,6 +74,32 @@
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="currentMember" class="card buttons header-buttons">
|
||||
<button
|
||||
class="iconified-button"
|
||||
:class="{
|
||||
'brand-button':
|
||||
newGalleryItems.length === 0 &&
|
||||
editGalleryIndexes.length === 0 &&
|
||||
deleteGalleryUrls.length === 0,
|
||||
}"
|
||||
@click="
|
||||
newGalleryItems.push({
|
||||
title: '',
|
||||
description: '',
|
||||
featured: false,
|
||||
url: '',
|
||||
})
|
||||
"
|
||||
>
|
||||
<PlusIcon />
|
||||
{{
|
||||
newGalleryItems.length === 0 &&
|
||||
editGalleryIndexes.length === 0 &&
|
||||
deleteGalleryUrls.length === 0
|
||||
? 'Add an image'
|
||||
: 'Add another image'
|
||||
}}
|
||||
</button>
|
||||
<button
|
||||
v-if="
|
||||
newGalleryItems.length > 0 ||
|
||||
@@ -84,26 +118,12 @@
|
||||
editGalleryIndexes.length > 0 ||
|
||||
deleteGalleryUrls.length > 0
|
||||
"
|
||||
class="action brand-button-colors iconified-button"
|
||||
class="action brand-button iconified-button"
|
||||
@click="saveGallery"
|
||||
>
|
||||
<CheckIcon />
|
||||
Save changes
|
||||
</button>
|
||||
<button
|
||||
class="iconified-button"
|
||||
@click="
|
||||
newGalleryItems.push({
|
||||
title: '',
|
||||
description: '',
|
||||
featured: false,
|
||||
url: '',
|
||||
})
|
||||
"
|
||||
>
|
||||
<PlusIcon />
|
||||
Add an image
|
||||
</button>
|
||||
</div>
|
||||
<div class="items">
|
||||
<div
|
||||
@@ -147,7 +167,7 @@
|
||||
<CalendarIcon />
|
||||
{{ $dayjs(item.created).format('MMMM D, YYYY') }}
|
||||
</div>
|
||||
<div v-if="currentMember" class="gallery-buttons">
|
||||
<div v-if="currentMember" class="gallery-buttons input-group">
|
||||
<button
|
||||
v-if="editGalleryIndexes.includes(index)"
|
||||
class="iconified-button"
|
||||
@@ -213,7 +233,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="gallery-bottom">
|
||||
<SmartFileInput
|
||||
<FileInput
|
||||
:max-size="5242880"
|
||||
accept="image/png,image/jpeg,image/gif,image/webp,.png,.jpeg,.gif,.webp"
|
||||
prompt="Choose image or drag it here"
|
||||
@@ -249,7 +269,7 @@ import ExternalIcon from '~/assets/images/utils/external.svg?inline'
|
||||
import ExpandIcon from '~/assets/images/utils/expand.svg?inline'
|
||||
import ContractIcon from '~/assets/images/utils/contract.svg?inline'
|
||||
|
||||
import SmartFileInput from '~/components/ui/SmartFileInput'
|
||||
import FileInput from '~/components/ui/FileInput'
|
||||
import Checkbox from '~/components/ui/Checkbox'
|
||||
|
||||
export default {
|
||||
@@ -260,7 +280,7 @@ export default {
|
||||
EditIcon,
|
||||
TrashIcon,
|
||||
CheckIcon,
|
||||
SmartFileInput,
|
||||
FileInput,
|
||||
CrossIcon,
|
||||
RightArrowIcon,
|
||||
LeftArrowIcon,
|
||||
@@ -302,6 +322,36 @@ export default {
|
||||
fetch() {
|
||||
this.gallery = JSON.parse(JSON.stringify(this.project.gallery))
|
||||
},
|
||||
head() {
|
||||
const title = `${this.project.title} - Gallery`
|
||||
const description = `View ${this.project.gallery.length} images of ${this.project.title} on Modrinth.`
|
||||
|
||||
return {
|
||||
title,
|
||||
meta: [
|
||||
{
|
||||
hid: 'og:title',
|
||||
name: 'og:title',
|
||||
content: title,
|
||||
},
|
||||
{
|
||||
hid: 'apple-mobile-web-app-title',
|
||||
name: 'apple-mobile-web-app-title',
|
||||
content: title,
|
||||
},
|
||||
{
|
||||
hid: 'og:description',
|
||||
name: 'og:description',
|
||||
content: description,
|
||||
},
|
||||
{
|
||||
hid: 'description',
|
||||
name: 'description',
|
||||
content: description,
|
||||
},
|
||||
],
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this._keyListener = function (e) {
|
||||
if (this.expandedGalleryItem) {
|
||||
@@ -456,6 +506,7 @@ export default {
|
||||
line-height: 1;
|
||||
display: flex;
|
||||
max-width: 2rem;
|
||||
color: var(--color-button-text);
|
||||
background-color: var(--color-button-bg);
|
||||
border-radius: var(--size-rounded-max);
|
||||
margin: 0;
|
||||
@@ -563,18 +614,6 @@ export default {
|
||||
|
||||
button {
|
||||
margin-right: 0.5rem;
|
||||
|
||||
&.brand-button-colors {
|
||||
background-color: var(--color-brand);
|
||||
|
||||
&:hover {
|
||||
background-color: var(--color-brand-hover);
|
||||
}
|
||||
|
||||
&:active {
|
||||
background-color: var(--color-brand-active);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,8 @@ export default {
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.markdown-body {
|
||||
max-width: calc(100% - (2 * var(--spacing-card-lg)));
|
||||
max-width: calc(
|
||||
60rem - 2 * var(--spacing-card-lg) - 9px
|
||||
); // $2.50 to anyone who can figure out why the 9px is needed
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,103 +1,94 @@
|
||||
<template>
|
||||
<div>
|
||||
<ConfirmPopup
|
||||
ref="delete_popup"
|
||||
<ModalConfirm
|
||||
ref="modal_confirm"
|
||||
title="Are you sure you want to delete this project?"
|
||||
description="If you proceed, all versions and any attached data will be removed from our servers. This may break other projects, so be careful."
|
||||
:has-to-type="true"
|
||||
:confirmation-text="project.title"
|
||||
proceed-label="Delete project"
|
||||
proceed-label="Delete"
|
||||
@proceed="deleteProject"
|
||||
/>
|
||||
<div class="card">
|
||||
<h3>General</h3>
|
||||
</div>
|
||||
<section class="card main-settings">
|
||||
<label>
|
||||
<span>
|
||||
<h3>Edit project</h3>
|
||||
<span>
|
||||
This leads you to a page where you can edit your project.
|
||||
<div class="universal-card">
|
||||
<h2>General settings</h2>
|
||||
<div class="adjacent-input">
|
||||
<label>
|
||||
<span class="label__title">Edit project information</span>
|
||||
<span class="label__description">
|
||||
Edit your project's name, description, categories, and more.
|
||||
</span>
|
||||
</span>
|
||||
<div>
|
||||
<nuxt-link class="iconified-button" to="edit"
|
||||
><EditIcon />Edit</nuxt-link
|
||||
>
|
||||
</div>
|
||||
</label>
|
||||
<label>
|
||||
<span>
|
||||
<h3>Create a version</h3>
|
||||
<span>
|
||||
This leads to a page where you can create a version for your
|
||||
project.
|
||||
</span>
|
||||
</span>
|
||||
<div>
|
||||
<nuxt-link
|
||||
class="iconified-button"
|
||||
to="version/create"
|
||||
:disabled="
|
||||
(currentMember.permissions & UPLOAD_VERSION) !== UPLOAD_VERSION
|
||||
"
|
||||
><PlusIcon />Create a version</nuxt-link
|
||||
>
|
||||
</div>
|
||||
</label>
|
||||
<label>
|
||||
<span>
|
||||
<h3>Delete project</h3>
|
||||
<span>
|
||||
</label>
|
||||
<nuxt-link class="iconified-button" to="edit"
|
||||
><EditIcon />Edit</nuxt-link
|
||||
>
|
||||
</div>
|
||||
<div class="adjacent-input">
|
||||
<span class="label">
|
||||
<span class="label__title">Delete project</span>
|
||||
<span class="label__description">
|
||||
Removes your project from Modrinth's servers and search. Clicking on
|
||||
this will delete your project, so be extra careful!
|
||||
</span>
|
||||
</span>
|
||||
<div
|
||||
class="iconified-button"
|
||||
<button
|
||||
class="iconified-button danger-button"
|
||||
:disabled="
|
||||
(currentMember.permissions & DELETE_PROJECT) !== DELETE_PROJECT
|
||||
"
|
||||
@click="showPopup"
|
||||
@click="$refs.modal_confirm.show()"
|
||||
>
|
||||
<TrashIcon />Delete project
|
||||
</div>
|
||||
</label>
|
||||
</section>
|
||||
<div class="card columns team-invite">
|
||||
<h3>Team members</h3>
|
||||
<div
|
||||
v-if="(currentMember.permissions & MANAGE_INVITES) === MANAGE_INVITES"
|
||||
class="column"
|
||||
>
|
||||
<input
|
||||
id="username"
|
||||
v-model="currentUsername"
|
||||
type="text"
|
||||
placeholder="Username"
|
||||
/>
|
||||
<label for="username" class="hidden">Username</label>
|
||||
<button
|
||||
class="iconified-button brand-button-colors column"
|
||||
@click="inviteTeamMember"
|
||||
>
|
||||
<PlusIcon />
|
||||
Invite
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="universal-card">
|
||||
<h2>Manage members</h2>
|
||||
<div class="adjacent-input">
|
||||
<span class="label">
|
||||
<span class="label__title">Invite a member</span>
|
||||
<span class="label__description">
|
||||
Enter the Modrinth username of the person you'd like to invite to be
|
||||
a member of this 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"
|
||||
/>
|
||||
<label for="username" class="hidden">Username</label>
|
||||
<button
|
||||
class="iconified-button brand-button"
|
||||
@click="inviteTeamMember"
|
||||
>
|
||||
<PlusIcon />
|
||||
Invite
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-for="(member, index) in allTeamMembers"
|
||||
:key="member.user.id"
|
||||
class="card member"
|
||||
class="universal-card member"
|
||||
:class="{ open: openTeamMembers.includes(member.user.id) }"
|
||||
>
|
||||
<div class="member-header">
|
||||
<div class="info">
|
||||
<img :src="member.avatar_url" :alt="member.name" />
|
||||
<Avatar
|
||||
:src="member.avatar_url"
|
||||
:alt="member.username"
|
||||
size="sm"
|
||||
circle
|
||||
/>
|
||||
<div class="text">
|
||||
<nuxt-link :to="'/user/' + member.user.username" class="name">
|
||||
<p class="title-link">{{ member.name }}</p>
|
||||
<p>{{ member.name }}</p>
|
||||
</nuxt-link>
|
||||
<p>{{ member.role }}</p>
|
||||
</div>
|
||||
@@ -106,7 +97,6 @@
|
||||
<Badge v-if="member.accepted" type="accepted" color="green" />
|
||||
<Badge v-else type="pending" color="yellow" />
|
||||
<button
|
||||
v-if="member.role !== 'Owner'"
|
||||
class="dropdown-icon"
|
||||
@click="
|
||||
openTeamMembers.indexOf(member.user.id) === -1
|
||||
@@ -121,98 +111,148 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="content">
|
||||
<div class="main-info">
|
||||
<label>
|
||||
Role:
|
||||
<input
|
||||
v-model="allTeamMembers[index].role"
|
||||
type="text"
|
||||
:class="{ 'known-error': member.role === 'Owner' }"
|
||||
:disabled="
|
||||
(currentMember.permissions & EDIT_MEMBER) !== EDIT_MEMBER
|
||||
"
|
||||
/>
|
||||
<div v-if="member.oldRole !== 'Owner'" class="adjacent-input">
|
||||
<label :for="`member-${allTeamMembers[index].user.username}-role`">
|
||||
<span class="label__title">Role</span>
|
||||
<span class="label__description">
|
||||
The title of the role that this member plays for this project.
|
||||
</span>
|
||||
</label>
|
||||
<ul v-if="member.role === 'Owner'" class="known-errors">
|
||||
<li>A project can only have one 'Owner'.</li>
|
||||
</ul>
|
||||
</div>
|
||||
<h3>Permissions</h3>
|
||||
<div class="permissions">
|
||||
<Checkbox
|
||||
:value="(member.permissions & UPLOAD_VERSION) === UPLOAD_VERSION"
|
||||
:disabled="
|
||||
(currentMember.permissions & EDIT_MEMBER) !== EDIT_MEMBER ||
|
||||
(currentMember.permissions & UPLOAD_VERSION) !== UPLOAD_VERSION
|
||||
"
|
||||
label="Upload version"
|
||||
@input="allTeamMembers[index].permissions ^= UPLOAD_VERSION"
|
||||
/>
|
||||
<Checkbox
|
||||
:value="(member.permissions & DELETE_VERSION) === DELETE_VERSION"
|
||||
:disabled="
|
||||
(currentMember.permissions & EDIT_MEMBER) !== EDIT_MEMBER ||
|
||||
(currentMember.permissions & DELETE_VERSION) !== DELETE_VERSION
|
||||
"
|
||||
label="Delete version"
|
||||
@input="allTeamMembers[index].permissions ^= DELETE_VERSION"
|
||||
/>
|
||||
<Checkbox
|
||||
:value="(member.permissions & EDIT_DETAILS) === EDIT_DETAILS"
|
||||
:disabled="
|
||||
(currentMember.permissions & EDIT_MEMBER) !== EDIT_MEMBER ||
|
||||
(currentMember.permissions & EDIT_DETAILS) !== EDIT_DETAILS
|
||||
"
|
||||
label="Edit details"
|
||||
@input="allTeamMembers[index].permissions ^= EDIT_DETAILS"
|
||||
/>
|
||||
<Checkbox
|
||||
:value="(member.permissions & EDIT_BODY) === EDIT_BODY"
|
||||
:disabled="
|
||||
(currentMember.permissions & EDIT_MEMBER) !== EDIT_MEMBER ||
|
||||
(currentMember.permissions & EDIT_BODY) !== EDIT_BODY
|
||||
"
|
||||
label="Edit body"
|
||||
@input="allTeamMembers[index].permissions ^= EDIT_BODY"
|
||||
/>
|
||||
<Checkbox
|
||||
:value="(member.permissions & MANAGE_INVITES) === MANAGE_INVITES"
|
||||
:disabled="
|
||||
(currentMember.permissions & EDIT_MEMBER) !== EDIT_MEMBER ||
|
||||
(currentMember.permissions & MANAGE_INVITES) !== MANAGE_INVITES
|
||||
"
|
||||
label="Manage invites"
|
||||
@input="allTeamMembers[index].permissions ^= MANAGE_INVITES"
|
||||
/>
|
||||
<Checkbox
|
||||
:value="(member.permissions & REMOVE_MEMBER) === REMOVE_MEMBER"
|
||||
:disabled="
|
||||
(currentMember.permissions & EDIT_MEMBER) !== EDIT_MEMBER ||
|
||||
(currentMember.permissions & REMOVE_MEMBER) !== REMOVE_MEMBER
|
||||
"
|
||||
label="Remove member"
|
||||
@input="allTeamMembers[index].permissions ^= REMOVE_MEMBER"
|
||||
/>
|
||||
<Checkbox
|
||||
:value="(member.permissions & EDIT_MEMBER) === EDIT_MEMBER"
|
||||
<input
|
||||
:id="`member-${allTeamMembers[index].user.username}-role`"
|
||||
v-model="allTeamMembers[index].role"
|
||||
type="text"
|
||||
:class="{ 'known-error': member.role === 'Owner' }"
|
||||
:disabled="
|
||||
(currentMember.permissions & EDIT_MEMBER) !== EDIT_MEMBER
|
||||
"
|
||||
label="Edit member"
|
||||
@input="allTeamMembers[index].permissions ^= EDIT_MEMBER"
|
||||
/>
|
||||
<Checkbox
|
||||
:value="(member.permissions & DELETE_PROJECT) === DELETE_PROJECT"
|
||||
:disabled="
|
||||
(currentMember.permissions & EDIT_MEMBER) !== EDIT_MEMBER ||
|
||||
(currentMember.permissions & DELETE_PROJECT) !== DELETE_PROJECT
|
||||
"
|
||||
label="Delete project"
|
||||
@input="allTeamMembers[index].permissions ^= DELETE_PROJECT"
|
||||
/>
|
||||
</div>
|
||||
<div class="actions">
|
||||
<div class="adjacent-input">
|
||||
<label
|
||||
:for="`member-${allTeamMembers[index].user.username}-monetization-weight`"
|
||||
>
|
||||
<span class="label__title">Monetization weight</span>
|
||||
<span class="label__description">
|
||||
Relative to all other members' monetization weights, this
|
||||
determines what portion of this project's revenue goes to this
|
||||
member.
|
||||
</span>
|
||||
</label>
|
||||
<input
|
||||
:id="`member-${allTeamMembers[index].user.username}-monetization-weight`"
|
||||
v-model="allTeamMembers[index].payouts_split"
|
||||
type="number"
|
||||
:disabled="
|
||||
(currentMember.permissions & EDIT_MEMBER) !== EDIT_MEMBER
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
<ul
|
||||
v-if="member.role === 'Owner' && member.oldRole !== 'Owner'"
|
||||
class="known-errors"
|
||||
>
|
||||
<li>A project can only have one 'Owner'.</li>
|
||||
</ul>
|
||||
<template v-if="member.oldRole !== 'Owner'">
|
||||
<span class="label">
|
||||
<span class="label__title">Permissions</span>
|
||||
</span>
|
||||
<div class="permissions">
|
||||
<Checkbox
|
||||
:value="(member.permissions & UPLOAD_VERSION) === UPLOAD_VERSION"
|
||||
:disabled="
|
||||
(currentMember.permissions & EDIT_MEMBER) !== EDIT_MEMBER ||
|
||||
(currentMember.permissions & UPLOAD_VERSION) !== UPLOAD_VERSION
|
||||
"
|
||||
label="Upload version"
|
||||
@input="allTeamMembers[index].permissions ^= UPLOAD_VERSION"
|
||||
/>
|
||||
<Checkbox
|
||||
:value="(member.permissions & DELETE_VERSION) === DELETE_VERSION"
|
||||
:disabled="
|
||||
(currentMember.permissions & EDIT_MEMBER) !== EDIT_MEMBER ||
|
||||
(currentMember.permissions & DELETE_VERSION) !== DELETE_VERSION
|
||||
"
|
||||
label="Delete version"
|
||||
@input="allTeamMembers[index].permissions ^= DELETE_VERSION"
|
||||
/>
|
||||
<Checkbox
|
||||
:value="(member.permissions & EDIT_DETAILS) === EDIT_DETAILS"
|
||||
:disabled="
|
||||
(currentMember.permissions & EDIT_MEMBER) !== EDIT_MEMBER ||
|
||||
(currentMember.permissions & EDIT_DETAILS) !== EDIT_DETAILS
|
||||
"
|
||||
label="Edit details"
|
||||
@input="allTeamMembers[index].permissions ^= EDIT_DETAILS"
|
||||
/>
|
||||
<Checkbox
|
||||
:value="(member.permissions & EDIT_BODY) === EDIT_BODY"
|
||||
:disabled="
|
||||
(currentMember.permissions & EDIT_MEMBER) !== EDIT_MEMBER ||
|
||||
(currentMember.permissions & EDIT_BODY) !== EDIT_BODY
|
||||
"
|
||||
label="Edit body"
|
||||
@input="allTeamMembers[index].permissions ^= EDIT_BODY"
|
||||
/>
|
||||
<Checkbox
|
||||
:value="(member.permissions & MANAGE_INVITES) === MANAGE_INVITES"
|
||||
:disabled="
|
||||
(currentMember.permissions & EDIT_MEMBER) !== EDIT_MEMBER ||
|
||||
(currentMember.permissions & MANAGE_INVITES) !== MANAGE_INVITES
|
||||
"
|
||||
label="Manage invites"
|
||||
@input="allTeamMembers[index].permissions ^= MANAGE_INVITES"
|
||||
/>
|
||||
<Checkbox
|
||||
:value="(member.permissions & REMOVE_MEMBER) === REMOVE_MEMBER"
|
||||
:disabled="
|
||||
(currentMember.permissions & EDIT_MEMBER) !== EDIT_MEMBER ||
|
||||
(currentMember.permissions & REMOVE_MEMBER) !== REMOVE_MEMBER
|
||||
"
|
||||
label="Remove member"
|
||||
@input="allTeamMembers[index].permissions ^= REMOVE_MEMBER"
|
||||
/>
|
||||
<Checkbox
|
||||
:value="(member.permissions & EDIT_MEMBER) === EDIT_MEMBER"
|
||||
:disabled="
|
||||
(currentMember.permissions & EDIT_MEMBER) !== EDIT_MEMBER
|
||||
"
|
||||
label="Edit member"
|
||||
@input="allTeamMembers[index].permissions ^= EDIT_MEMBER"
|
||||
/>
|
||||
<Checkbox
|
||||
:value="(member.permissions & DELETE_PROJECT) === DELETE_PROJECT"
|
||||
:disabled="
|
||||
(currentMember.permissions & EDIT_MEMBER) !== EDIT_MEMBER ||
|
||||
(currentMember.permissions & DELETE_PROJECT) !== DELETE_PROJECT
|
||||
"
|
||||
label="Delete project"
|
||||
@input="allTeamMembers[index].permissions ^= DELETE_PROJECT"
|
||||
/>
|
||||
<Checkbox
|
||||
:value="(member.permissions & VIEW_ANALYTICS) === VIEW_ANALYTICS"
|
||||
:disabled="
|
||||
(currentMember.permissions & EDIT_MEMBER) !== EDIT_MEMBER ||
|
||||
(currentMember.permissions & VIEW_ANALYTICS) !== VIEW_ANALYTICS
|
||||
"
|
||||
label="View analytics"
|
||||
@input="allTeamMembers[index].permissions ^= VIEW_ANALYTICS"
|
||||
/>
|
||||
<Checkbox
|
||||
:value="(member.permissions & VIEW_PAYOUTS) === VIEW_PAYOUTS"
|
||||
:disabled="
|
||||
(currentMember.permissions & EDIT_MEMBER) !== EDIT_MEMBER ||
|
||||
(currentMember.permissions & VIEW_PAYOUTS) !== VIEW_PAYOUTS
|
||||
"
|
||||
label="View revenue"
|
||||
@input="allTeamMembers[index].permissions ^= VIEW_PAYOUTS"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<div class="button-group push-right">
|
||||
<button
|
||||
v-if="member.oldRole !== 'Owner'"
|
||||
class="iconified-button"
|
||||
:disabled="
|
||||
(currentMember.permissions & EDIT_MEMBER) !== EDIT_MEMBER
|
||||
@@ -224,7 +264,7 @@
|
||||
</button>
|
||||
<button
|
||||
v-if="
|
||||
member.role !== 'Owner' &&
|
||||
member.oldRole !== 'Owner' &&
|
||||
currentMember.role === 'Owner' &&
|
||||
member.accepted
|
||||
"
|
||||
@@ -235,10 +275,9 @@
|
||||
Transfer ownership
|
||||
</button>
|
||||
<button
|
||||
class="iconified-button brand-button-colors"
|
||||
class="iconified-button brand-button"
|
||||
:disabled="
|
||||
(currentMember.permissions & EDIT_MEMBER) !== EDIT_MEMBER ||
|
||||
member.role === 'Owner'
|
||||
(currentMember.permissions & EDIT_MEMBER) !== EDIT_MEMBER
|
||||
"
|
||||
@click="updateTeamMember(index)"
|
||||
>
|
||||
@@ -252,7 +291,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ConfirmPopup from '~/components/ui/ConfirmPopup'
|
||||
import ModalConfirm from '~/components/ui/ModalConfirm'
|
||||
import Checkbox from '~/components/ui/Checkbox'
|
||||
import Badge from '~/components/ui/Badge'
|
||||
|
||||
@@ -262,11 +301,13 @@ import CheckIcon from '~/assets/images/utils/check.svg?inline'
|
||||
import EditIcon from '~/assets/images/utils/edit.svg?inline'
|
||||
import TrashIcon from '~/assets/images/utils/trash.svg?inline'
|
||||
import UserIcon from '~/assets/images/utils/user.svg?inline'
|
||||
import Avatar from '~/components/ui/Avatar'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Avatar,
|
||||
DropdownIcon,
|
||||
ConfirmPopup,
|
||||
ModalConfirm,
|
||||
Checkbox,
|
||||
Badge,
|
||||
PlusIcon,
|
||||
@@ -304,6 +345,8 @@ export default {
|
||||
},
|
||||
fetch() {
|
||||
this.allTeamMembers = this.allMembers
|
||||
|
||||
this.allTeamMembers.forEach((x) => (x.oldRole = x.role))
|
||||
},
|
||||
created() {
|
||||
this.UPLOAD_VERSION = 1 << 0
|
||||
@@ -314,6 +357,8 @@ export default {
|
||||
this.REMOVE_MEMBER = 1 << 5
|
||||
this.EDIT_MEMBER = 1 << 6
|
||||
this.DELETE_PROJECT = 1 << 7
|
||||
this.VIEW_ANALYTICS = 1 << 8
|
||||
this.VIEW_PAYOUTS = 1 << 9
|
||||
},
|
||||
methods: {
|
||||
async inviteTeamMember() {
|
||||
@@ -368,10 +413,16 @@ export default {
|
||||
this.$nuxt.$loading.start()
|
||||
|
||||
try {
|
||||
const data = {
|
||||
permissions: this.allTeamMembers[index].permissions,
|
||||
role: this.allTeamMembers[index].role,
|
||||
}
|
||||
const data =
|
||||
this.allTeamMembers[index].oldRole !== 'Owner'
|
||||
? {
|
||||
permissions: this.allTeamMembers[index].permissions,
|
||||
role: this.allTeamMembers[index].role,
|
||||
payouts_split: this.allTeamMembers[index].payouts_split,
|
||||
}
|
||||
: {
|
||||
payouts_split: this.allTeamMembers[index].payouts_split,
|
||||
}
|
||||
|
||||
await this.$axios.patch(
|
||||
`team/${this.project.team}/members/${this.allTeamMembers[index].user.id}`,
|
||||
@@ -413,14 +464,6 @@ export default {
|
||||
|
||||
this.$nuxt.$loading.finish()
|
||||
},
|
||||
showPopup() {
|
||||
if (
|
||||
(this.currentMember.permissions & this.DELETE_PROJECT) ===
|
||||
this.DELETE_PROJECT
|
||||
) {
|
||||
this.$refs.delete_popup.show()
|
||||
}
|
||||
},
|
||||
async deleteProject() {
|
||||
await this.$axios.delete(
|
||||
`project/${this.project.id}`,
|
||||
@@ -444,6 +487,7 @@ export default {
|
||||
).data.map((it) => ({
|
||||
avatar_url: it.user.avatar_url,
|
||||
name: it.user.username,
|
||||
oldRole: it.role,
|
||||
...it,
|
||||
}))
|
||||
},
|
||||
@@ -452,26 +496,12 @@ export default {
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.card {
|
||||
h3 {
|
||||
margin-top: 0.5rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.member {
|
||||
margin-bottom: var(--spacing-card-md);
|
||||
|
||||
.member-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
.info {
|
||||
display: flex;
|
||||
img {
|
||||
border-radius: var(--size-rounded-icon);
|
||||
height: 50px;
|
||||
width: 50px;
|
||||
}
|
||||
.text {
|
||||
margin: auto 0 auto 0.5rem;
|
||||
font-size: var(--font-size-sm);
|
||||
@@ -499,33 +529,18 @@ export default {
|
||||
|
||||
.content {
|
||||
display: none;
|
||||
flex-direction: column;
|
||||
padding-top: var(--spacing-card-md);
|
||||
|
||||
.main-info {
|
||||
margin-bottom: var(--spacing-card-lg);
|
||||
|
||||
@media screen and (min-width: 1024px) {
|
||||
label {
|
||||
align-items: center;
|
||||
input {
|
||||
margin-left: 1rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.permissions {
|
||||
margin: 1rem 0;
|
||||
margin-bottom: var(--spacing-card-md);
|
||||
max-width: 45rem;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(10rem, 1fr));
|
||||
grid-gap: 0.5rem;
|
||||
|
||||
label {
|
||||
flex-direction: row;
|
||||
input {
|
||||
flex: none;
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -536,90 +551,8 @@ export default {
|
||||
}
|
||||
}
|
||||
.content {
|
||||
display: unset;
|
||||
margin: var(--spacing-card-lg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
input,
|
||||
button {
|
||||
&:disabled {
|
||||
cursor: not-allowed !important;
|
||||
}
|
||||
}
|
||||
|
||||
section {
|
||||
margin-bottom: var(--spacing-card-md);
|
||||
|
||||
label {
|
||||
display: flex;
|
||||
|
||||
span {
|
||||
flex: 2;
|
||||
padding-right: var(--spacing-card-lg);
|
||||
}
|
||||
|
||||
div {
|
||||
flex: none;
|
||||
}
|
||||
|
||||
input {
|
||||
flex: 3;
|
||||
height: fit-content;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.team-invite {
|
||||
gap: 0.5rem;
|
||||
@media screen and (max-width: 1024px) {
|
||||
flex-direction: column;
|
||||
h3 {
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
h3 {
|
||||
margin: auto auto auto 0;
|
||||
}
|
||||
|
||||
> div {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
input {
|
||||
margin-right: 1rem;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 500px) {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
input {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
button {
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.actions {
|
||||
display: flex;
|
||||
|
||||
button {
|
||||
margin-right: 0.5rem;
|
||||
|
||||
&:first-child {
|
||||
margin-left: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.main-settings span {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -22,26 +22,31 @@
|
||||
</ul>
|
||||
</div>
|
||||
<div class="content card">
|
||||
<ConfirmPopup
|
||||
ref="delete_version_popup"
|
||||
<ModalConfirm
|
||||
ref="modal_confirm"
|
||||
title="Are you sure you want to delete this version?"
|
||||
description="This will remove this version forever (like really forever)."
|
||||
:has-to-type="false"
|
||||
proceed-label="Delete version"
|
||||
proceed-label="Delete"
|
||||
@proceed="deleteVersion()"
|
||||
/>
|
||||
<ModalReport
|
||||
ref="modal_version_report"
|
||||
:item-id="version.id"
|
||||
item-type="version"
|
||||
/>
|
||||
<div class="columns">
|
||||
<nuxt-link
|
||||
v-if="mode === 'version'"
|
||||
class="iconified-button back-button"
|
||||
:to="`/${project.project_type}/${
|
||||
project.slug ? project.slug : project.id
|
||||
}/${
|
||||
$nuxt.context.from
|
||||
? $nuxt.context.from.name === 'type-id-changelog'
|
||||
? 'changelog'
|
||||
: 'versions'
|
||||
: 'versions'
|
||||
:to="`${
|
||||
$nuxt.context.from &&
|
||||
($nuxt.context.from.name === 'type-id-changelog' ||
|
||||
$nuxt.context.from.name === 'type-id-versions')
|
||||
? $nuxt.context.from.fullPath
|
||||
: `/${project.project_type}/${
|
||||
project.slug ? project.slug : project.id
|
||||
}/versions`
|
||||
}`"
|
||||
>
|
||||
<BackIcon aria-hidden="true" />
|
||||
@@ -73,11 +78,12 @@
|
||||
class="full-width-input"
|
||||
type="text"
|
||||
placeholder="Enter an optional version name..."
|
||||
maxlength="64"
|
||||
/>
|
||||
<Checkbox v-model="version.featured" label="Featured" />
|
||||
<hr class="card-divider" />
|
||||
</div>
|
||||
<div v-if="mode === 'edit'" class="header-buttons buttons columns">
|
||||
<div v-if="mode === 'edit'" class="header-buttons button-group columns">
|
||||
<h3 class="column-grow-1">Edit version</h3>
|
||||
<nuxt-link
|
||||
v-if="$auth.user"
|
||||
@@ -90,7 +96,7 @@
|
||||
Cancel
|
||||
</nuxt-link>
|
||||
<button
|
||||
class="iconified-button brand-button-colors"
|
||||
class="iconified-button brand-button"
|
||||
@click="saveEditedVersion"
|
||||
>
|
||||
<SaveIcon aria-hidden="true" />
|
||||
@@ -99,7 +105,7 @@
|
||||
</div>
|
||||
<div
|
||||
v-else-if="mode === 'create'"
|
||||
class="header-buttons buttons columns"
|
||||
class="header-buttons button-group columns"
|
||||
>
|
||||
<h3 class="column-grow-1">Create version</h3>
|
||||
<nuxt-link
|
||||
@@ -112,42 +118,36 @@
|
||||
<CrossIcon aria-hidden="true" />
|
||||
Cancel
|
||||
</nuxt-link>
|
||||
<button
|
||||
class="iconified-button brand-button-colors"
|
||||
@click="createVersion"
|
||||
>
|
||||
<button class="iconified-button brand-button" @click="createVersion">
|
||||
<CheckIcon aria-hidden="true" />
|
||||
Create
|
||||
</button>
|
||||
</div>
|
||||
<div v-else class="buttons">
|
||||
<div v-else class="button-group">
|
||||
<a
|
||||
v-if="primaryFile"
|
||||
v-tooltip="
|
||||
primaryFile.filename + ' (' + $formatBytes(primaryFile.size) + ')'
|
||||
"
|
||||
:href="primaryFile.url"
|
||||
class="bold-button iconified-button brand-button-colors"
|
||||
class="bold-button iconified-button brand-button"
|
||||
:title="`Download ${primaryFile.filename}`"
|
||||
>
|
||||
<DownloadIcon aria-hidden="true" />
|
||||
Download
|
||||
</a>
|
||||
<nuxt-link
|
||||
:to="`/create/report?id=${version.id}&t=version`"
|
||||
<button
|
||||
v-if="$auth.user"
|
||||
class="action iconified-button"
|
||||
@click="$refs.modal_version_report.show()"
|
||||
>
|
||||
<ReportIcon aria-hidden="true" />
|
||||
Report
|
||||
</nuxt-link>
|
||||
<button
|
||||
v-if="currentMember"
|
||||
class="action iconified-button"
|
||||
@click="$refs.delete_version_popup.show()"
|
||||
>
|
||||
<TrashIcon aria-hidden="true" />
|
||||
Delete
|
||||
</button>
|
||||
<a v-else class="action iconified-button" :href="authUrl">
|
||||
<ReportIcon aria-hidden="true" />
|
||||
Report
|
||||
</a>
|
||||
<nuxt-link
|
||||
v-if="currentMember"
|
||||
class="action iconified-button"
|
||||
@@ -159,11 +159,20 @@
|
||||
<EditIcon aria-hidden="true" />
|
||||
Edit
|
||||
</nuxt-link>
|
||||
<button
|
||||
v-if="currentMember"
|
||||
class="action iconified-button danger-button"
|
||||
@click="$refs.modal_confirm.show()"
|
||||
>
|
||||
<TrashIcon aria-hidden="true" />
|
||||
Delete
|
||||
</button>
|
||||
</div>
|
||||
<section v-if="mode === 'edit' || mode === 'create'">
|
||||
<h3>Changelog</h3>
|
||||
<ThisOrThat
|
||||
<Chips
|
||||
v-model="changelogViewMode"
|
||||
class="separator"
|
||||
:items="['source', 'preview']"
|
||||
/>
|
||||
<div v-if="changelogViewMode === 'source'" class="textarea-wrapper">
|
||||
@@ -289,6 +298,7 @@
|
||||
v-model="version.version_number"
|
||||
type="text"
|
||||
placeholder="Enter the version number..."
|
||||
maxlength="32"
|
||||
/>
|
||||
<p v-else class="value">{{ version.version_number }}</p>
|
||||
</div>
|
||||
@@ -358,7 +368,7 @@
|
||||
</div>
|
||||
<div v-if="mode === 'version'" class="data">
|
||||
<p class="title">Version ID</p>
|
||||
<p class="value">{{ version.id }}</p>
|
||||
<p class="value"><CopyCode :text="version.id" /></p>
|
||||
</div>
|
||||
</div>
|
||||
<hr class="card-divider" />
|
||||
@@ -380,16 +390,10 @@
|
||||
:key="index"
|
||||
class="dependency"
|
||||
>
|
||||
<img
|
||||
class="icon"
|
||||
:src="
|
||||
dependency.project
|
||||
? dependency.project.icon_url
|
||||
? dependency.project.icon_url
|
||||
: 'https://cdn.modrinth.com/placeholder.svg?inline'
|
||||
: 'https://cdn.modrinth.com/placeholder.svg?inline'
|
||||
"
|
||||
<Avatar
|
||||
:src="dependency.project ? dependency.project.icon_url : null"
|
||||
alt="dependency-icon"
|
||||
size="sm"
|
||||
/>
|
||||
<div class="info">
|
||||
<nuxt-link
|
||||
@@ -443,8 +447,9 @@
|
||||
class="edit-dependency"
|
||||
>
|
||||
<h4>Add dependency</h4>
|
||||
<ThisOrThat
|
||||
<Chips
|
||||
v-model="dependencyAddMode"
|
||||
class="separator"
|
||||
:items="['project', 'version']"
|
||||
/>
|
||||
<div class="edit-info">
|
||||
@@ -582,9 +587,10 @@
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<StatelessFileInput
|
||||
<FileInput
|
||||
v-if="mode === 'edit' || mode === 'create'"
|
||||
multiple
|
||||
should-always-reset
|
||||
class="choose-files"
|
||||
:accept="
|
||||
project.actualProjectType.toLowerCase() === 'modpack'
|
||||
@@ -629,8 +635,7 @@
|
||||
</template>
|
||||
<script>
|
||||
import Multiselect from 'vue-multiselect'
|
||||
import ConfirmPopup from '~/components/ui/ConfirmPopup'
|
||||
import StatelessFileInput from '~/components/ui/StatelessFileInput'
|
||||
import FileInput from '~/components/ui/FileInput'
|
||||
|
||||
import InfoIcon from '~/assets/images/utils/info.svg?inline'
|
||||
import TrashIcon from '~/assets/images/utils/trash.svg?inline'
|
||||
@@ -645,11 +650,20 @@ import StarIcon from '~/assets/images/utils/star.svg?inline'
|
||||
import CheckIcon from '~/assets/images/utils/check.svg?inline'
|
||||
import VersionBadge from '~/components/ui/Badge'
|
||||
import Checkbox from '~/components/ui/Checkbox'
|
||||
import ThisOrThat from '~/components/ui/ThisOrThat'
|
||||
import Chips from '~/components/ui/Chips'
|
||||
import ModalConfirm from '~/components/ui/ModalConfirm'
|
||||
import ModalReport from '~/components/ui/ModalReport'
|
||||
import CopyCode from '~/components/ui/CopyCode'
|
||||
import Avatar from '~/components/ui/Avatar'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
ThisOrThat,
|
||||
Avatar,
|
||||
CopyCode,
|
||||
ModalConfirm,
|
||||
ModalReport,
|
||||
FileInput,
|
||||
Chips,
|
||||
Checkbox,
|
||||
VersionBadge,
|
||||
DownloadIcon,
|
||||
@@ -657,29 +671,14 @@ export default {
|
||||
EditIcon,
|
||||
ReportIcon,
|
||||
BackIcon,
|
||||
ConfirmPopup,
|
||||
StarIcon,
|
||||
CheckIcon,
|
||||
Multiselect,
|
||||
SaveIcon,
|
||||
PlusIcon,
|
||||
CrossIcon,
|
||||
StatelessFileInput,
|
||||
InfoIcon,
|
||||
},
|
||||
beforeRouteLeave(to, from, next) {
|
||||
if (this.mode === 'create') {
|
||||
if (
|
||||
!window.confirm('Are you sure that you want to leave without saving?')
|
||||
) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
this.setVersion()
|
||||
|
||||
next()
|
||||
},
|
||||
props: {
|
||||
project: {
|
||||
type: Object,
|
||||
@@ -738,8 +737,57 @@ export default {
|
||||
}
|
||||
},
|
||||
async fetch() {
|
||||
console.log(this.$nuxt.context.from)
|
||||
await this.setVersion()
|
||||
},
|
||||
head() {
|
||||
if (!this.version.game_versions) {
|
||||
return {}
|
||||
}
|
||||
const title = `${
|
||||
this.mode === 'create' ? 'Create Version' : this.version.name
|
||||
} - ${this.project.title}`
|
||||
const description = `Download ${this.project.title} ${
|
||||
this.version.version_number
|
||||
} on Modrinth. Supports ${this.$formatVersion(
|
||||
this.version.game_versions
|
||||
)} ${this.version.loaders
|
||||
.map((x) => x.charAt(0).toUpperCase() + x.slice(1))
|
||||
.join(' & ')}. Published on ${this.$dayjs(
|
||||
this.version.date_published
|
||||
).format('MMM D, YYYY')}. ${this.version.downloads} downloads.`
|
||||
|
||||
return {
|
||||
title,
|
||||
meta: [
|
||||
{
|
||||
hid: 'og:title',
|
||||
name: 'og:title',
|
||||
content: title,
|
||||
},
|
||||
{
|
||||
hid: 'apple-mobile-web-app-title',
|
||||
name: 'apple-mobile-web-app-title',
|
||||
content: title,
|
||||
},
|
||||
{
|
||||
hid: 'og:description',
|
||||
name: 'og:description',
|
||||
content: description,
|
||||
},
|
||||
{
|
||||
hid: 'description',
|
||||
name: 'description',
|
||||
content: description,
|
||||
},
|
||||
],
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
authUrl() {
|
||||
return `${process.env.authURLBase}auth/init?url=${process.env.domain}${this.$route.path}`
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
'$route.path': {
|
||||
async handler() {
|
||||
@@ -747,31 +795,15 @@ export default {
|
||||
},
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
if (this.mode === 'create') {
|
||||
function preventLeave(e) {
|
||||
e.preventDefault()
|
||||
e.returnValue = ''
|
||||
}
|
||||
window.addEventListener('beforeunload', preventLeave)
|
||||
this.$once('hook:beforeDestroy', () => {
|
||||
window.removeEventListener('beforeunload', preventLeave)
|
||||
})
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
checkFields() {
|
||||
if (
|
||||
return !(
|
||||
this.version.version_number === '' ||
|
||||
this.version.game_versions.length === 0 ||
|
||||
(this.version.loaders.length === 0 &&
|
||||
this.project.project_type !== 'resourcepack') ||
|
||||
(this.newFiles.length === 0 && this.version.files.length === 0)
|
||||
) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
)
|
||||
},
|
||||
reset() {
|
||||
this.changelogViewMode = 'source'
|
||||
@@ -1112,8 +1144,12 @@ export default {
|
||||
section {
|
||||
margin: 1rem 0;
|
||||
|
||||
h3 {
|
||||
margin-bottom: 0.5rem;
|
||||
.separator {
|
||||
margin: var(--spacing-card-sm) 0;
|
||||
}
|
||||
|
||||
.choose-files {
|
||||
margin-bottom: var(--spacing-card-sm);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1126,10 +1162,6 @@ section {
|
||||
flex-wrap: wrap;
|
||||
row-gap: 0.5rem;
|
||||
|
||||
.bold-button {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 1024px) {
|
||||
margin-left: auto;
|
||||
}
|
||||
@@ -1186,19 +1218,12 @@ section {
|
||||
.dependency {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
gap: 0.25rem;
|
||||
|
||||
@media screen and (min-width: 800px) {
|
||||
flex-basis: 30%;
|
||||
}
|
||||
|
||||
.icon {
|
||||
width: 3rem;
|
||||
height: 3rem;
|
||||
margin-right: 0.5rem;
|
||||
border-radius: var(--size-rounded-xs);
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -1279,10 +1304,6 @@ section {
|
||||
margin-bottom: var(--spacing-card-sm);
|
||||
}
|
||||
|
||||
.styled-tabs {
|
||||
margin-bottom: var(--spacing-card-sm);
|
||||
}
|
||||
|
||||
.textarea-wrapper {
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
<template>
|
||||
<div class="content">
|
||||
<div v-if="currentMember" class="card header-buttons">
|
||||
<nuxt-link
|
||||
to="version/create"
|
||||
class="brand-button-colors iconified-button"
|
||||
>
|
||||
<nuxt-link to="version/create" class="brand-button iconified-button">
|
||||
<PlusIcon />
|
||||
Create a version
|
||||
</nuxt-link>
|
||||
@@ -14,111 +11,79 @@
|
||||
:versions="versions"
|
||||
@updateVersions="updateVersions"
|
||||
/>
|
||||
<div v-if="versions.length > 0" class="card">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th role="presentation"></th>
|
||||
<th>Version</th>
|
||||
<th>Supports</th>
|
||||
<th>Stats</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="version in filteredVersions" :key="version.id">
|
||||
<td>
|
||||
<a
|
||||
v-tooltip="
|
||||
$parent.findPrimary(version).filename +
|
||||
' (' +
|
||||
$formatBytes($parent.findPrimary(version).size) +
|
||||
')'
|
||||
"
|
||||
:href="$parent.findPrimary(version).url"
|
||||
class="download-button"
|
||||
:class="version.version_type"
|
||||
:title="`Download ${version.name}`"
|
||||
>
|
||||
<DownloadIcon aria-hidden="true" />
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<div class="info">
|
||||
<div class="top title-link">
|
||||
<nuxt-link
|
||||
:to="`/${project.project_type}/${
|
||||
project.slug ? project.slug : project.id
|
||||
}/version/${encodeURI(version.displayUrlEnding)}`"
|
||||
>
|
||||
{{ version.name }}
|
||||
</nuxt-link>
|
||||
</div>
|
||||
<div class="bottom">
|
||||
<VersionBadge
|
||||
v-if="version.version_type === 'release'"
|
||||
type="release"
|
||||
color="green"
|
||||
/>
|
||||
<VersionBadge
|
||||
v-else-if="version.version_type === 'beta'"
|
||||
type="beta"
|
||||
color="yellow"
|
||||
/>
|
||||
<VersionBadge
|
||||
v-else-if="version.version_type === 'alpha'"
|
||||
type="alpha"
|
||||
color="red"
|
||||
/>
|
||||
<span class="divider" />
|
||||
<span class="version_number">{{
|
||||
version.version_number
|
||||
}}</span>
|
||||
</div>
|
||||
<div class="mobile-info">
|
||||
<p>
|
||||
{{
|
||||
version.loaders
|
||||
.map((x) => $formatCategory(x))
|
||||
.join(', ') +
|
||||
' ' +
|
||||
$formatVersion(version.game_versions)
|
||||
}}
|
||||
</p>
|
||||
<p></p>
|
||||
<p>
|
||||
<strong>{{ $formatNumber(version.downloads) }}</strong>
|
||||
downloads
|
||||
</p>
|
||||
<p>
|
||||
Published on
|
||||
<strong>{{
|
||||
$dayjs(version.date_published).format('MMM D, YYYY')
|
||||
}}</strong>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<p>
|
||||
{{ version.loaders.map((x) => $formatCategory(x)).join(', ') }}
|
||||
</p>
|
||||
<p>{{ $formatVersion(version.game_versions) }}</p>
|
||||
</td>
|
||||
<td>
|
||||
<p>
|
||||
<span>{{ $formatNumber(version.downloads) }}</span>
|
||||
downloads
|
||||
</p>
|
||||
<p>
|
||||
Published on
|
||||
<span>{{
|
||||
$dayjs(version.date_published).format('MMM D, YYYY')
|
||||
}}</span>
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div v-if="versions.length > 0" class="universal-card all-versions">
|
||||
<div class="header">
|
||||
<div></div>
|
||||
<div>Version</div>
|
||||
<div>Supports</div>
|
||||
<div>Stats</div>
|
||||
</div>
|
||||
<div
|
||||
v-for="version in filteredVersions"
|
||||
:key="version.id + '-new'"
|
||||
class="version-button button-transparent"
|
||||
@click="
|
||||
$router.push(
|
||||
`/${project.project_type}/${
|
||||
project.slug ? project.slug : project.id
|
||||
}/version/${encodeURI(version.displayUrlEnding)}`
|
||||
)
|
||||
"
|
||||
>
|
||||
<a
|
||||
v-tooltip="
|
||||
$parent.findPrimary(version).filename +
|
||||
' (' +
|
||||
$formatBytes($parent.findPrimary(version).size) +
|
||||
')'
|
||||
"
|
||||
:href="$parent.findPrimary(version).url"
|
||||
class="download-button"
|
||||
:class="version.version_type"
|
||||
:title="`Download ${version.name}`"
|
||||
@click.stop="(event) => event.stopPropagation()"
|
||||
>
|
||||
<DownloadIcon aria-hidden="true" />
|
||||
</a>
|
||||
<span class="version__title">{{ version.name }}</span>
|
||||
<div class="version__metadata">
|
||||
<VersionBadge
|
||||
v-if="version.version_type === 'release'"
|
||||
type="release"
|
||||
color="green"
|
||||
/>
|
||||
<VersionBadge
|
||||
v-else-if="version.version_type === 'beta'"
|
||||
type="beta"
|
||||
color="yellow"
|
||||
/>
|
||||
<VersionBadge
|
||||
v-else-if="version.version_type === 'alpha'"
|
||||
type="alpha"
|
||||
color="red"
|
||||
/>
|
||||
<span class="divider" />
|
||||
<span class="version_number">{{ version.version_number }}</span>
|
||||
</div>
|
||||
<div class="version__supports">
|
||||
<span>
|
||||
{{ version.loaders.map((x) => $formatCategory(x)).join(', ') }}
|
||||
</span>
|
||||
<span>{{ $formatVersion(version.game_versions) }}</span>
|
||||
</div>
|
||||
<div class="version__stats">
|
||||
<span>
|
||||
<strong>{{ $formatNumber(version.downloads) }}</strong>
|
||||
downloads
|
||||
</span>
|
||||
<span>
|
||||
Published on
|
||||
<strong>{{
|
||||
$dayjs(version.date_published).format('MMM D, YYYY')
|
||||
}}</strong>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -161,6 +126,46 @@ export default {
|
||||
filteredVersions: this.versions,
|
||||
}
|
||||
},
|
||||
fetch() {
|
||||
if (this.$route.query.page)
|
||||
this.currentPage = parseInt(this.$route.query.page)
|
||||
},
|
||||
head() {
|
||||
const title = `${this.project.title} - Versions`
|
||||
const description = `Download and browse ${this.versions.length} ${
|
||||
this.project.title
|
||||
} versions. ${this.$formatNumber(
|
||||
this.project.downloads
|
||||
)} total downloads. Last updated ${this.$dayjs(
|
||||
this.versions[0] ? this.versions[0].date_published : null
|
||||
).format('MMM D, YYYY')}.`
|
||||
|
||||
return {
|
||||
title,
|
||||
meta: [
|
||||
{
|
||||
hid: 'og:title',
|
||||
name: 'og:title',
|
||||
content: title,
|
||||
},
|
||||
{
|
||||
hid: 'apple-mobile-web-app-title',
|
||||
name: 'apple-mobile-web-app-title',
|
||||
content: title,
|
||||
},
|
||||
{
|
||||
hid: 'og:description',
|
||||
name: 'og:description',
|
||||
content: description,
|
||||
},
|
||||
{
|
||||
hid: 'description',
|
||||
name: 'description',
|
||||
content: description,
|
||||
},
|
||||
],
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
updateVersions(updatedVersions) {
|
||||
this.filteredVersions = updatedVersions
|
||||
@@ -170,89 +175,116 @@ export default {
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
table {
|
||||
border-collapse: separate;
|
||||
border-spacing: 0 0.75rem;
|
||||
|
||||
th {
|
||||
text-align: left;
|
||||
font-size: var(--font-size-md);
|
||||
|
||||
&:nth-child(3),
|
||||
&:nth-child(4) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
tr {
|
||||
td:nth-child(2) {
|
||||
padding-right: 2rem;
|
||||
min-width: 16rem;
|
||||
.top {
|
||||
font-weight: bold;
|
||||
}
|
||||
.bottom {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
text-overflow: ellipsis;
|
||||
margin-top: 0.25rem;
|
||||
|
||||
.divider {
|
||||
width: 0.25rem;
|
||||
height: 0.25rem;
|
||||
border-radius: 50%;
|
||||
display: inline-block;
|
||||
margin: 0 0.25rem;
|
||||
background-color: var(--color-text);
|
||||
}
|
||||
}
|
||||
|
||||
.mobile-info {
|
||||
p {
|
||||
margin: 0.25rem 0 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
td:nth-child(3) {
|
||||
display: none;
|
||||
width: 100%;
|
||||
p {
|
||||
margin: 0.25rem 0;
|
||||
}
|
||||
}
|
||||
td:nth-child(4) {
|
||||
display: none;
|
||||
min-width: 15rem;
|
||||
p {
|
||||
margin: 0.25rem 0;
|
||||
span {
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width: 1024px) {
|
||||
table {
|
||||
tr {
|
||||
th:nth-child(3),
|
||||
td:nth-child(3),
|
||||
th:nth-child(4),
|
||||
td:nth-child(4) {
|
||||
display: table-cell;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.mobile-info {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.header-buttons {
|
||||
display: flex;
|
||||
justify-content: right;
|
||||
}
|
||||
|
||||
.all-versions {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.header {
|
||||
display: grid;
|
||||
grid-template: 'download title supports stats';
|
||||
grid-template-columns: calc(2.25rem + var(--spacing-card-sm)) 1fr 1fr 1fr;
|
||||
color: var(--color-text-dark);
|
||||
font-size: var(--font-size-md);
|
||||
font-weight: bold;
|
||||
justify-content: left;
|
||||
margin-inline: var(--spacing-card-md);
|
||||
margin-bottom: var(--spacing-card-sm);
|
||||
column-gap: var(--spacing-card-sm);
|
||||
|
||||
div:first-child {
|
||||
grid-area: download;
|
||||
}
|
||||
|
||||
div:nth-child(2) {
|
||||
grid-area: title;
|
||||
}
|
||||
|
||||
div:nth-child(3) {
|
||||
grid-area: supports;
|
||||
}
|
||||
|
||||
div:nth-child(4) {
|
||||
grid-area: stats;
|
||||
}
|
||||
}
|
||||
|
||||
.version-button {
|
||||
display: grid;
|
||||
grid-template: 'download title supports stats' 'download metadata supports stats';
|
||||
grid-template-columns: calc(2.25rem + var(--spacing-card-sm)) 1fr 1fr 1fr;
|
||||
column-gap: var(--spacing-card-sm);
|
||||
justify-content: left;
|
||||
padding: var(--spacing-card-md);
|
||||
|
||||
.download-button {
|
||||
grid-area: download;
|
||||
}
|
||||
.version__title {
|
||||
grid-area: title;
|
||||
font-weight: bold;
|
||||
}
|
||||
.version__metadata {
|
||||
grid-area: metadata;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
gap: var(--spacing-card-xs);
|
||||
margin-top: var(--spacing-card-xs);
|
||||
}
|
||||
.version__supports {
|
||||
grid-area: supports;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--spacing-card-xs);
|
||||
}
|
||||
.version__stats {
|
||||
grid-area: stats;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--spacing-card-xs);
|
||||
}
|
||||
|
||||
&:active:not(&:disabled) {
|
||||
transform: scale(0.99) !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1024px) {
|
||||
.all-versions {
|
||||
.header {
|
||||
grid-template: 'download title';
|
||||
grid-template-columns: calc(2.25rem + var(--spacing-card-sm)) 1fr;
|
||||
|
||||
div:nth-child(3) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
div:nth-child(4) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.version-button {
|
||||
grid-template: 'download title' 'download metadata' 'download supports' 'download stats';
|
||||
grid-template-columns: calc(2.25rem + var(--spacing-card-sm)) 1fr;
|
||||
row-gap: var(--spacing-card-xs);
|
||||
|
||||
.version__supports {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
column-gap: var(--spacing-card-xs);
|
||||
}
|
||||
.version__metadata {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user