Version editing, user, mod, version deletion

This commit is contained in:
Geometrically
2021-01-14 21:43:58 -07:00
parent 888057f64a
commit ce1786f128
13 changed files with 879 additions and 326 deletions

View File

@@ -79,6 +79,7 @@
img {
max-width: 100%;
height: auto;
}
pre {

View File

@@ -59,13 +59,6 @@
<ExternalIcon />
Source
</a>
<nuxt-link
v-if="currentMember"
:to="'/mod/' + mod.id + '/edit'"
class="tab"
>
Edit
</nuxt-link>
<nuxt-link
v-if="currentMember"
:to="'/mod/' + mod.id + '/settings'"

View File

@@ -47,6 +47,7 @@ export default {
},
{ hid: 'twitter:card', name: 'twitter:card', content: 'summary' },
{ hid: 'twitter:site', name: 'twitter:site', content: '@modrinth' },
{ name: 'propeller', content: '9542802736cdc223303e7a97b071550c' },
],
link: [
{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' },

View File

@@ -26,10 +26,10 @@
:is-modrinth="true"
>
<nuxt-link
class="button buttonse column"
:to="'/mod/' + mod.id + '/edit'"
class="button buttons column"
:to="'/mod/' + mod.id + '/settings'"
>
Edit
Settings
</nuxt-link>
</ModCard>
</DashboardPage>

View File

@@ -5,7 +5,7 @@
<h3 class="column-grow-1">Settings</h3>
<button class="brand-button column" @click="editProfile">Save</button>
</div>
<section class="essentials">
<section>
<h3>Username</h3>
<label>
<span>
@@ -42,7 +42,7 @@
<input v-model="bio" type="text" placeholder="Enter your bio" />
</label>
</section>
<section class="essentials pad-maker">
<section class="pad-maker">
<h3>Theme</h3>
<label>
<span
@@ -58,7 +58,7 @@
/>
</label>
</section>
<section class="essentials pad-maker">
<section class="pad-maker">
<h3>Authorization token</h3>
<label>
<span>
@@ -68,7 +68,7 @@
<input
type="button"
class="button pad-rem"
value="Copy to clipboard"
value="Copy to Clipboard"
@click="copyToken"
/>
</label>
@@ -81,10 +81,25 @@
<input
type="button"
class="button"
value="Revoke token"
value="Revoke Token"
@click="gotoRevoke"
/>
</label>
<h3>Delete your account</h3>
<label>
<span
>Clicking on this WILL delete your user. Do not click on this unless
you want your user deleted. If you delete your user, all attached data
will be removed from our servers. This cannot be reversed, so be
careful!</span
>
<input
type="button"
class="button"
value="Delete Account"
@click="deleteAccount"
/>
</label>
</section>
</DashboardPage>
</template>
@@ -169,6 +184,31 @@ export default {
})
}
this.$nuxt.$loading.finish()
},
async deleteAccount() {
const config = {
headers: {
Authorization: this.$auth.getToken('local'),
},
}
this.$nuxt.$loading.start()
try {
await axios.delete(
`https://api.modrinth.com/api/v1/user/${this.$auth.user.id}`,
config
)
} catch (err) {
this.$notify({
group: 'main',
title: 'An Error Occurred',
text: err.response.data.description,
type: 'error',
})
}
this.$nuxt.$loading.finish()
},
},

View File

@@ -3,6 +3,12 @@
<div class="page-contents">
<header class="columns">
<h2 class="column-grow-1">Edit Mod</h2>
<nuxt-link
:to="'/mod/' + (mod.slug ? mod.slug : mod.id)"
class="button column"
>
Back
</nuxt-link>
<button
v-if="mod.status === 'rejected' || mod.status === 'draft'"
title="Submit for Review"
@@ -174,9 +180,9 @@
</span>
<div class="columns">
<div class="textarea-wrapper">
<textarea id="body" v-model="body"></textarea>
<textarea id="body" v-model="mod.body"></textarea>
</div>
<div v-compiled-markdown="body" class="markdown-body"></div>
<div v-compiled-markdown="mod.body" class="markdown-body"></div>
</div>
</section>
<section class="extra-links">
@@ -309,15 +315,12 @@ export default {
name: mod.license.name,
}
const reg = /.+?:\/\/.+?(\/.+?)(?:#|\?|$)/
const urlPath = reg.exec(mod.body_url)[1]
const res = await axios.get(
`https://modrinth-cdn.nyc3.digitaloceanspaces.com${urlPath}`
)
if (mod.body_url && !mod.body) {
mod.body = (await axios.get(mod.body_url)).data
}
return {
mod,
body: res.data,
clientSideType: {
label: mod.client_side,
id: mod.client_side,

View File

@@ -5,7 +5,11 @@
:members="members"
:current-member="currentMember"
>
<div v-compiled-markdown="body" v-highlightjs class="markdown-body"></div>
<div
v-compiled-markdown="mod.body"
v-highlightjs
class="markdown-body"
></div>
</ModPage>
</template>
@@ -16,15 +20,6 @@ import ModPage from '@/components/ModPage'
export default {
components: { ModPage },
auth: false,
async fetch() {
const reg = /.+?:\/\/.+?(\/.+?)(?:#|\?|$)/
const urlPath = reg.exec(this.mod.body_url)[1]
this.body = (
await axios.get(
`https://modrinth-cdn.nyc3.digitaloceanspaces.com${urlPath}`
)
).data
},
async asyncData(data) {
const config = {
headers: {
@@ -42,6 +37,10 @@ export default {
)
).data
if (mod.body_url && !mod.body) {
mod.body = (await axios.get(mod.body_url)).data
}
const [members, versions] = (
await Promise.all([
axios.get(`https://api.modrinth.com/api/v1/team/${mod.team}/members`),
@@ -90,11 +89,6 @@ export default {
})
}
},
data() {
return {
body: '',
}
},
head() {
return {
title: this.mod.title + ' - Modrinth',

View File

@@ -0,0 +1,339 @@
<template>
<ModPage
:mod="mod"
:versions="versions"
:members="members"
:current-member="currentMember"
>
<div class="new-version">
<div class="controls">
<button
class="brand-button"
title="Create version"
@click="createVersion"
>
Create version
</button>
</div>
<div class="main">
<h3>Name</h3>
<label>
<span>
This is what users will see first. Will default to version number
</span>
<input
v-model="createdVersion.version_title"
type="text"
placeholder="Enter the name"
/>
</label>
<h3>Number</h3>
<label>
<span>
That's how your version will appear in mod lists and in URLs
</span>
<input
v-model="createdVersion.version_number"
type="text"
placeholder="Enter the number"
/>
</label>
<h3>Channel</h3>
<label>
<span>
It is important to notify players and pack makers if the version is
stable
</span>
<multiselect
v-model="createdVersion.release_channel"
placeholder="Select one"
:options="['release', 'beta', 'alpha']"
:searchable="false"
:close-on-select="true"
:show-labels="false"
:allow-empty="false"
/>
</label>
<h3>Loaders</h3>
<label>
<span>
Mark all loaders this version works with. It is essential for search
</span>
<multiselect
v-model="createdVersion.loaders"
:options="selectableLoaders"
:loading="selectableLoaders.length === 0"
:multiple="true"
:searchable="false"
:show-no-results="false"
:close-on-select="true"
:clear-on-select="false"
:show-labels="false"
:limit="6"
:hide-selected="true"
placeholder="Choose loaders..."
/>
</label>
<h3>Game versions</h3>
<label>
<span>
Mark all game version this version supports. It is essential for
search
</span>
<multiselect
v-model="createdVersion.game_versions"
:options="selectableVersions"
:loading="selectableVersions.length === 0"
:multiple="true"
:searchable="true"
:show-no-results="false"
:close-on-select="false"
:clear-on-select="false"
:show-labels="false"
:limit="6"
:hide-selected="true"
placeholder="Choose versions..."
/>
</label>
<h3>Files</h3>
<label>
<span>
You should upload a single JAR file. However, you are allowed to
upload multiple
</span>
<FileInput
accept="application/*"
multiple
prompt="Choose files or drag them here"
@change="updateVersionFiles"
/>
</label>
</div>
<div class="changelog">
<h3>Changelog</h3>
<span>
Tell players and modpack makers what's new. It supports the same
markdown as description, but it is advisable not to be too creative
with it in changelogs
</span>
<div class="textarea-wrapper">
<textarea v-model="createdVersion.version_body"></textarea>
</div>
</div>
</div>
</ModPage>
</template>
<script>
import axios from 'axios'
import Multiselect from 'vue-multiselect'
import ModPage from '~/components/ModPage'
export default {
components: {
ModPage,
Multiselect,
},
async asyncData(data) {
const config = {
headers: {
Authorization: data.$auth.getToken('local')
? data.$auth.getToken('local')
: '',
},
}
try {
const mod = (
await axios.get(
`https://api.modrinth.com/api/v1/mod/${data.params.id}`,
config
)
).data
const [members, versions, selectableLoaders, selectableVersions] = (
await Promise.all([
axios.get(`https://api.modrinth.com/api/v1/team/${mod.team}/members`),
axios.get(
`https://api.modrinth.com/api/v1/versions?ids=${JSON.stringify(
mod.versions
)}`,
config
),
axios.get(`https://api.modrinth.com/api/v1/tag/loader`),
axios.get(`https://api.modrinth.com/api/v1/tag/game_version`),
])
).map((it) => it.data)
const users = (
await axios.get(
`https://api.modrinth.com/api/v1/users?ids=${JSON.stringify(
members.map((it) => it.user_id)
)}`,
config
)
).data
users.forEach((it) => {
const index = members.findIndex((x) => x.user_id === it.id)
members[index].avatar_url = it.avatar_url
members[index].name = it.username
})
const currentMember = data.$auth.loggedIn
? members.find((x) => x.user_id === data.$auth.user.id)
: null
return {
mod,
versions: versions.sort(
(a, b) =>
new Date(b.date_published).getTime() -
new Date(a.date_published).getTime()
),
members,
selectableLoaders,
selectableVersions,
currentMember,
}
} catch {
data.error({
statusCode: 404,
message: 'Mod not found',
})
}
},
data() {
return {
createdVersion: {},
}
},
methods: {
async createVersion() {
this.$nuxt.$loading.start()
const formData = new FormData()
if (!this.createdVersion.version_title) {
this.createdVersion.version_title = this.createdVersion.version_number
}
this.createdVersion.mod_id = this.mod.id
this.createdVersion.dependencies = []
this.createdVersion.featured = false
formData.append('data', JSON.stringify(this.createdVersion))
if (this.createdVersion.raw_files) {
for (let i = 0; i < this.createdVersion.raw_files.length; i++) {
formData.append(
this.createdVersion.file_parts[i],
new Blob([this.createdVersion.raw_files[i]]),
this.createdVersion.raw_files[i].name
)
}
}
try {
await axios({
url: 'https://api.modrinth.com/api/v1/version',
method: 'POST',
data: formData,
headers: {
'Content-Type': 'multipart/form-data',
Authorization: this.$auth.getToken('local'),
},
})
await this.$router.go(null)
} catch (err) {
this.$notify({
group: 'main',
title: 'An Error Occurred',
text: err.response.data.description,
type: 'error',
})
window.scrollTo({ top: 0, behavior: 'smooth' })
}
this.$nuxt.$loading.finish()
},
updateVersionFiles(files) {
this.createdVersion.raw_files = files
const newFileParts = []
for (let i = 0; i < files.length; i++) {
newFileParts.push(files[i].name.concat('-' + i))
}
this.createdVersion.file_parts = newFileParts
},
async downloadFile(hash, url) {
await axios.get(
`https://api.modrinth.com/api/v1/version_file/${hash}/download`
)
const elem = document.createElement('a')
elem.download = hash
elem.href = url
elem.click()
},
},
}
</script>
<style lang="scss" scoped>
.textarea-wrapper {
display: flex;
flex-direction: column;
align-items: stretch;
textarea {
flex: 1;
overflow-y: auto;
resize: none;
max-width: 100%;
}
}
.new-version {
@extend %card;
padding: var(--spacing-card-md) var(--spacing-card-lg);
display: grid;
grid-template:
'controls controls' auto
'main changelog' auto
/ 5fr 4fr;
column-gap: var(--spacing-card-md);
.controls {
grid-area: controls;
display: flex;
flex-direction: row-reverse;
}
.main {
grid-area: main;
}
.changelog {
grid-area: changelog;
display: flex;
flex-direction: column;
.textarea-wrapper {
flex: 1;
}
}
}
label {
display: flex;
span {
flex: 2;
padding-right: var(--spacing-card-lg);
}
input,
.multiselect,
.input-group {
flex: 3;
height: fit-content;
}
}
</style>

View File

@@ -5,6 +5,39 @@
:members="members.filter((it) => it.accepted)"
:current-member="currentMember"
>
<div class="section-header columns">
<h3 class="column-grow-1">General</h3>
</div>
<section>
<h3>Edit Mod</h3>
<label>
<span> This leads you to a page where you can edit your mod. </span>
<nuxt-link class="button" to="edit">Edit</nuxt-link>
</label>
<h3>Create Version</h3>
<label>
<span>
This leads to a page where you can create a version for your mod.
</span>
<nuxt-link class="button" to="newversion">Create Version</nuxt-link>
</label>
<h3>Delete Mod</h3>
<label>
<span>
Clicking on this WILL delete your mod. Do not click on this unless you
want your mod deleted. If you delete your mod, all versions and any
attatched data will be removed from our servers. This may break other
projects, so be careful!
</span>
<div
class="button"
:disabled="(currentMember.permissions & DELETE_MOD) !== DELETE_MOD"
@click="deleteMod"
>
Delete Mod
</div>
</label>
</section>
<div class="section-header columns">
<h3 class="column-grow-1">Team members</h3>
<div class="column">
@@ -392,6 +425,18 @@ export default {
this.$nuxt.$loading.finish()
},
async deleteMod() {
const config = {
headers: {
Authorization: this.$auth.getToken('local'),
},
}
await axios.delete(
`https://api.modrinth.com/api/v1/mod/${this.mod.id}`,
config
)
},
},
}
</script>
@@ -486,4 +531,34 @@ button {
cursor: not-allowed !important;
}
}
section {
@extend %card;
padding: var(--spacing-card-md) var(--spacing-card-lg);
margin-bottom: var(--spacing-card-md);
label {
display: flex;
span {
flex: 2;
padding-right: var(--spacing-card-lg);
}
input {
flex: 3;
height: fit-content;
}
div,
a {
text-align: center;
height: fit-content;
flex: 1;
}
div:hover {
cursor: pointer;
}
}
}
</style>

View File

@@ -0,0 +1,303 @@
<template>
<ModPage
:mod="mod"
:versions="versions"
:members="members"
:current-member="currentMember"
>
<div class="new-version">
<div class="controls">
<button class="brand-button" title="Save version" @click="saveVersion">
Save version
</button>
</div>
<div class="main">
<h3>Name</h3>
<label>
<span>
This is what users will see first. Will default to version number
</span>
<input
v-model="version.name"
type="text"
placeholder="Enter the name"
/>
</label>
<h3>Number</h3>
<label>
<span>
That's how your version will appear in mod lists and in URLs
</span>
<input
v-model="version.version_number"
type="text"
placeholder="Enter the number"
/>
</label>
<h3>Channel</h3>
<label>
<span>
It is important to notify players and pack makers if the version is
stable
</span>
<multiselect
v-model="version.version_type"
placeholder="Select one"
:options="['release', 'beta', 'alpha']"
:searchable="false"
:close-on-select="true"
:show-labels="false"
:allow-empty="false"
/>
</label>
<h3>Loaders</h3>
<label>
<span>
Mark all loaders this version works with. It is essential for search
</span>
<multiselect
v-model="version.loaders"
:options="selectableLoaders"
:loading="selectableLoaders.length === 0"
:multiple="true"
:searchable="false"
:show-no-results="false"
:close-on-select="true"
:clear-on-select="false"
:show-labels="false"
:limit="6"
:hide-selected="true"
placeholder="Choose loaders..."
/>
</label>
<h3>Game versions</h3>
<label>
<span>
Mark all game version this version supports. It is essential for
search
</span>
<multiselect
v-model="version.game_versions"
:options="selectableVersions"
:loading="selectableVersions.length === 0"
:multiple="true"
:searchable="true"
:show-no-results="false"
:close-on-select="false"
:clear-on-select="false"
:show-labels="false"
:limit="6"
:hide-selected="true"
placeholder="Choose versions..."
/>
</label>
</div>
<div class="changelog">
<h3>Changelog</h3>
<span>
Tell players and modpack makers what's new. It supports the same
markdown as description, but it is advisable not to be too creative
with it in changelogs
</span>
<div class="textarea-wrapper">
<textarea v-model="version.changelog"></textarea>
</div>
</div>
</div>
</ModPage>
</template>
<script>
import axios from 'axios'
import ModPage from '@/components/ModPage'
import Multiselect from 'vue-multiselect'
export default {
components: {
ModPage,
Multiselect,
},
auth: false,
async asyncData(data) {
const config = {
headers: {
Authorization: data.$auth.getToken('local')
? data.$auth.getToken('local')
: '',
},
}
try {
const mod = (
await axios.get(
`https://api.modrinth.com/api/v1/mod/${data.params.id}`,
config
)
).data
const [members, versions, selectableLoaders, selectableVersions] = (
await Promise.all([
axios.get(`https://api.modrinth.com/api/v1/team/${mod.team}/members`),
axios.get(
`https://api.modrinth.com/api/v1/versions?ids=${JSON.stringify(
mod.versions
)}`,
config
),
axios.get(`https://api.modrinth.com/api/v1/tag/loader`),
axios.get(`https://api.modrinth.com/api/v1/tag/game_version`),
])
).map((it) => it.data)
const users = (
await axios.get(
`https://api.modrinth.com/api/v1/users?ids=${JSON.stringify(
members.map((it) => it.user_id)
)}`,
config
)
).data
users.forEach((it) => {
const index = members.findIndex((x) => x.user_id === it.id)
members[index].avatar_url = it.avatar_url
members[index].name = it.username
})
const version = versions.find((x) => x.id === data.params.version)
version.author = members.find((x) => x.user_id === version.author_id)
let primaryFile = version.files.find((file) => file.primary)
if (!primaryFile) {
primaryFile = version.files[0]
}
const currentMember = data.$auth.loggedIn
? members.find((x) => x.user_id === data.$auth.user.id)
: null
if (!version.changelog && version.changelog_url) {
version.changelog = (await axios.get(version.changelog_url)).data
}
return {
mod,
versions: versions.sort(
(a, b) =>
new Date(b.date_published).getTime() -
new Date(a.date_published).getTime()
),
members,
version,
primaryFile,
currentMember,
selectableLoaders,
selectableVersions,
}
} catch {
data.error({
statusCode: 404,
message: 'Version not found',
})
}
},
methods: {
async saveVersion() {
const config = {
headers: {
Authorization: this.$auth.getToken('local')
? this.$auth.getToken('local')
: '',
},
}
this.$nuxt.$loading.start()
try {
await axios.patch(
`https://api.modrinth.com/api/v1/version/${this.version.id}`,
this.version,
config
)
await this.$router.replace(
`/mod/${this.mod.id}/version/${this.version.id}`
)
} catch (err) {
this.$notify({
group: 'main',
title: 'An Error Occurred',
text: err.response.data.description,
type: 'error',
})
window.scrollTo({ top: 0, behavior: 'smooth' })
}
this.$nuxt.$loading.finish()
},
},
}
</script>
<style lang="scss" scoped>
.textarea-wrapper {
display: flex;
flex-direction: column;
align-items: stretch;
textarea {
flex: 1;
overflow-y: auto;
resize: none;
max-width: 100%;
}
}
.new-version {
@extend %card;
padding: var(--spacing-card-md) var(--spacing-card-lg);
display: grid;
grid-template:
'controls controls' auto
'main changelog' auto
/ 5fr 4fr;
column-gap: var(--spacing-card-md);
.controls {
grid-area: controls;
display: flex;
flex-direction: row-reverse;
}
.main {
grid-area: main;
}
.changelog {
grid-area: changelog;
display: flex;
flex-direction: column;
.textarea-wrapper {
flex: 1;
}
}
}
label {
display: flex;
span {
flex: 2;
padding-right: var(--spacing-card-lg);
}
input,
.multiselect,
.input-group {
flex: 3;
height: fit-content;
}
}
</style>

View File

@@ -21,17 +21,31 @@
{{ version.version_number }}
</span>
<Categories :categories="version.loaders" />
<a
v-if="primaryFile"
:href="primaryFile.url"
class="download-button"
@click.prevent="
downloadFile(primaryFile.hashes.sha1, primaryFile.url)
"
>
<DownloadIcon />
Download
</a>
<div class="buttons">
<button class="action" @click="deleteVersion">
<TrashIcon />
Delete
</button>
<nuxt-link
v-if="currentMember"
class="action"
:to="version.id + '/edit'"
>
<EditIcon />
Edit
</nuxt-link>
<a
v-if="primaryFile"
:href="primaryFile.url"
class="action"
@click.prevent="
downloadFile(primaryFile.hashes.sha1, primaryFile.url)
"
>
<DownloadIcon />
Download
</a>
</div>
</div>
<div class="stats">
<div class="stat">
@@ -67,7 +81,10 @@
</div>
</div>
</div>
<div v-compiled-markdown="changelog" class="markdown-body"></div>
<div
v-compiled-markdown="version.changelog ? version.changelog : ''"
class="markdown-body"
></div>
<div class="files">
<div v-for="file in version.files" :key="file.hashes.sha1" class="file">
<div class="text-wrapper">
@@ -98,6 +115,8 @@ import ModPage from '@/components/ModPage'
import Categories from '@/components/Categories'
import FileInput from '@/components/FileInput'
import TrashIcon from '~/assets/images/utils/trash.svg?inline'
import EditIcon from '~/assets/images/utils/edit.svg?inline'
import DownloadIcon from '~/assets/images/utils/download.svg?inline'
import CalendarIcon from '~/assets/images/utils/calendar.svg?inline'
import TagIcon from '~/assets/images/utils/tag.svg?inline'
@@ -110,13 +129,10 @@ export default {
DownloadIcon,
CalendarIcon,
TagIcon,
TrashIcon,
EditIcon,
},
auth: false,
async fetch() {
if (this.version.changelog_url) {
this.changelog = (await axios.get(this.version.changelog_url)).data
}
},
async asyncData(data) {
const config = {
headers: {
@@ -175,6 +191,10 @@ export default {
? members.find((x) => x.user_id === data.$auth.user.id)
: null
if (!version.changelog && version.changelog_url) {
version.changelog = (await axios.get(version.changelog_url)).data
}
return {
mod,
versions: versions.sort(
@@ -196,7 +216,6 @@ export default {
},
data() {
return {
changelog: '',
filesToUpload: [],
}
},
@@ -212,6 +231,8 @@ export default {
elem.click()
},
async deleteFile(hash) {
this.$nuxt.$loading.start()
const config = {
headers: {
Authorization: this.$auth.getToken('local'),
@@ -222,8 +243,13 @@ export default {
`https://api.modrinth.com/api/v1/version_file/${hash}`,
config
)
await this.$router.go(null)
this.$nuxt.$loading.finish()
},
async makePrimary(hash) {
this.$nuxt.$loading.start()
const config = {
headers: {
Authorization: this.$auth.getToken('local'),
@@ -233,12 +259,13 @@ export default {
await axios.patch(
`https://api.modrinth.com/api/v1/version/${this.version.id}`,
{
primary_file: {
sha1: hash,
},
primary_file: ['sha1', hash],
},
config
)
await this.$router.go(null)
this.$nuxt.$loading.finish()
},
async addFiles(e) {
this.filesToUpload = e.target.files
@@ -287,6 +314,23 @@ export default {
this.$nuxt.$loading.finish()
},
async deleteVersion() {
this.$nuxt.$loading.start()
const config = {
headers: {
Authorization: this.$auth.getToken('local'),
},
}
await axios.delete(
`https://api.modrinth.com/api/v1/version/${this.version.id}`,
config
)
await this.$router.replace(`/mod/${this.mod.id}`)
this.$nuxt.$loading.finish()
},
},
head() {
return {
@@ -350,23 +394,27 @@ export default {
margin: auto 0.5rem auto 0;
}
.download-button {
margin-left: auto;
padding: 0.5rem;
color: var(--color-button-text);
background-color: var(--color-button-bg);
justify-self: flex-end;
.buttons {
display: flex;
align-items: center;
border-radius: var(--size-rounded-sm);
margin-left: auto;
&:hover,
&:focus {
background-color: var(--color-button-bg-hover);
}
.action {
padding: 0.5rem;
color: var(--color-button-text);
background-color: var(--color-button-bg);
display: flex;
align-items: center;
border-radius: var(--size-rounded-sm);
margin: 0 0 0 0.5rem;
svg {
margin-right: 0.25rem;
&:hover,
&:focus {
background-color: var(--color-button-bg-hover);
}
svg {
margin-right: 0.25rem;
}
}
}
}

View File

@@ -68,159 +68,26 @@
</tr>
</tbody>
</table>
<Popup
v-if="currentMember"
:show-popup="showPopup"
class="create-version-popup-body"
>
<h3>New Version</h3>
<label
for="version-title"
class="required"
title="The title of your version"
>
Version Title
</label>
<input
id="version-title"
v-model="createdVersion.version_title"
required
type="text"
placeholder="Combat Update"
/>
<label
for="version-number"
class="required"
title="The version number of this version. Preferably following semantic versioning"
>
Version Number
</label>
<input
id="version-number"
v-model="createdVersion.version_number"
required
type="text"
placeholder="v1.9"
/>
<label class="required" title="The release channel of this version.">
Release Channel
</label>
<Multiselect
v-model="createdVersion.release_channel"
class="categories-input"
placeholder="Select one"
:options="['release', 'beta', 'alpha']"
:searchable="false"
:close-on-select="true"
:show-labels="false"
:allow-empty="false"
/>
<label
title="The version number of this version. Preferably following semantic versioning"
>
Mod Loaders
</label>
<multiselect
v-model="createdVersion.loaders"
class="categories-input"
:options="selectableLoaders"
:loading="selectableLoaders.length === 0"
:multiple="true"
:searchable="false"
:show-no-results="false"
:close-on-select="true"
:clear-on-select="false"
:show-labels="false"
:limit="6"
:hide-selected="true"
placeholder="Choose loaders..."
/>
<label title="The versions of minecraft that this mod version supports">
Minecraft Versions
</label>
<multiselect
v-model="createdVersion.game_versions"
class="categories-input"
:options="selectableVersions"
:loading="selectableVersions.length === 0"
:multiple="true"
:searchable="true"
:show-no-results="false"
:close-on-select="false"
:clear-on-select="false"
:show-labels="false"
:limit="6"
:hide-selected="true"
placeholder="Choose versions..."
/>
<label for="version-body" title="A list of changes for this version">
Changelog
</label>
<textarea
id="version-body"
v-model="createdVersion.version_body"
class="changelog-editor"
/>
<FileInput
input-id="version-files"
accept="application/java-archive,application/zip"
default-text="Upload Files"
:input-multiple="true"
@change="updateVersionFiles"
>
<label class="required" title="The files associated with the version">
Version Files
</label>
</FileInput>
<div class="popup-buttons">
<button
class="trash-button"
@click="
showPopup = false
createdVersion = {}
"
>
<TrashIcon />
</button>
<button class="default-button" @click="createVersion">
Create Version
</button>
</div>
</Popup>
<button
v-if="currentMember"
class="default-button"
@click="showPopup = !showPopup"
>
<nuxt-link v-if="currentMember" to="newversion" class="button">
New Version
</button>
</nuxt-link>
</ModPage>
</template>
<script>
import axios from 'axios'
import Multiselect from 'vue-multiselect'
import ModPage from '@/components/ModPage'
import Popup from '@/components/Popup'
import FileInput from '@/components/FileInput'
import TrashIcon from '~/assets/images/utils/trash.svg?inline'
import DownloadIcon from '~/assets/images/utils/download.svg?inline'
import ForgeIcon from '~/assets/images/categories/forge.svg?inline'
import FabricIcon from '~/assets/images/categories/fabric.svg?inline'
export default {
components: {
Multiselect,
FileInput,
Popup,
ModPage,
ForgeIcon,
FabricIcon,
DownloadIcon,
TrashIcon,
},
auth: false,
async asyncData(data) {
@@ -292,68 +159,7 @@ export default {
})
}
},
data() {
return {
showPopup: false,
createdVersion: {},
}
},
methods: {
updateVersionFiles(files) {
this.createdVersion.raw_files = files
const newFileParts = []
for (let i = 0; i < files.length; i++) {
newFileParts.push(files[i].name.concat('-' + i))
}
this.createdVersion.file_parts = newFileParts
},
async createVersion() {
this.$nuxt.$loading.start()
const formData = new FormData()
this.createdVersion.mod_id = this.mod.id
this.createdVersion.dependencies = []
this.createdVersion.featured = false
formData.append('data', JSON.stringify(this.createdVersion))
if (this.createdVersion.raw_files) {
for (let i = 0; i < this.createdVersion.raw_files.length; i++) {
formData.append(
this.createdVersion.file_parts[i],
new Blob([this.createdVersion.raw_files[i]]),
this.createdVersion.raw_files[i].name
)
}
}
try {
await axios({
url: 'https://api.modrinth.com/api/v1/version',
method: 'POST',
data: formData,
headers: {
'Content-Type': 'multipart/form-data',
Authorization: this.$auth.getToken('local'),
},
})
await this.$router.go(null)
} catch (err) {
this.$notify({
group: 'main',
title: 'An Error Occurred',
text: err.response.data.description,
type: 'error',
})
window.scrollTo({ top: 0, behavior: 'smooth' })
}
this.$nuxt.$loading.finish()
},
findPrimary(version) {
let file = version.files.find((x) => x.primary)
@@ -487,65 +293,9 @@ table {
}
}
}
.multiselect {
margin-bottom: 20px;
}
input {
width: calc(100% - 15px);
padding: 0.5rem 5px;
margin-bottom: 20px;
}
.changelog-editor {
padding: 20px;
width: calc(100% - 40px);
height: 200px;
resize: none;
outline: none;
border: none;
margin: 10px 0 30px;
background-color: var(--color-button-bg);
color: var(--color-text);
font-family: monospace;
}
.popup-buttons {
margin-top: 20px;
display: flex;
justify-content: right;
align-items: center;
.default-button {
float: none;
margin-top: 0;
}
.trash-button {
cursor: pointer;
margin-right: 10px;
padding: 5px;
border: none;
border-radius: var(--size-rounded-sm);
color: #9b2c2c;
background-color: var(--color-bg);
}
}
.default-button {
.button {
float: right;
margin-top: 20px;
border-radius: var(--size-rounded-sm);
cursor: pointer;
border: none;
padding: 10px;
background-color: var(--color-button-bg);
color: var(--color-button-text);
&:hover,
&:focus {
background-color: var(--color-button-bg-hover);
}
}
@media screen and (max-width: 400px) {

View File

@@ -556,6 +556,12 @@ export default {
async createMod() {
this.$nuxt.$loading.start()
for (const version of this.versions) {
if (!version.version_title) {
version.version_title = version.version_number
}
}
const formData = new FormData()
formData.append(