You've already forked AstralRinth
forked from didirus/AstralRinth
Version editing, user, mod, version deletion
This commit is contained in:
@@ -79,6 +79,7 @@
|
|||||||
|
|
||||||
img {
|
img {
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
|
height: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
pre {
|
pre {
|
||||||
|
|||||||
@@ -59,13 +59,6 @@
|
|||||||
<ExternalIcon />
|
<ExternalIcon />
|
||||||
Source
|
Source
|
||||||
</a>
|
</a>
|
||||||
<nuxt-link
|
|
||||||
v-if="currentMember"
|
|
||||||
:to="'/mod/' + mod.id + '/edit'"
|
|
||||||
class="tab"
|
|
||||||
>
|
|
||||||
Edit
|
|
||||||
</nuxt-link>
|
|
||||||
<nuxt-link
|
<nuxt-link
|
||||||
v-if="currentMember"
|
v-if="currentMember"
|
||||||
:to="'/mod/' + mod.id + '/settings'"
|
:to="'/mod/' + mod.id + '/settings'"
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ export default {
|
|||||||
},
|
},
|
||||||
{ hid: 'twitter:card', name: 'twitter:card', content: 'summary' },
|
{ hid: 'twitter:card', name: 'twitter:card', content: 'summary' },
|
||||||
{ hid: 'twitter:site', name: 'twitter:site', content: '@modrinth' },
|
{ hid: 'twitter:site', name: 'twitter:site', content: '@modrinth' },
|
||||||
|
{ name: 'propeller', content: '9542802736cdc223303e7a97b071550c' },
|
||||||
],
|
],
|
||||||
link: [
|
link: [
|
||||||
{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' },
|
{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' },
|
||||||
|
|||||||
@@ -26,10 +26,10 @@
|
|||||||
:is-modrinth="true"
|
:is-modrinth="true"
|
||||||
>
|
>
|
||||||
<nuxt-link
|
<nuxt-link
|
||||||
class="button buttonse column"
|
class="button buttons column"
|
||||||
:to="'/mod/' + mod.id + '/edit'"
|
:to="'/mod/' + mod.id + '/settings'"
|
||||||
>
|
>
|
||||||
Edit
|
Settings
|
||||||
</nuxt-link>
|
</nuxt-link>
|
||||||
</ModCard>
|
</ModCard>
|
||||||
</DashboardPage>
|
</DashboardPage>
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
<h3 class="column-grow-1">Settings</h3>
|
<h3 class="column-grow-1">Settings</h3>
|
||||||
<button class="brand-button column" @click="editProfile">Save</button>
|
<button class="brand-button column" @click="editProfile">Save</button>
|
||||||
</div>
|
</div>
|
||||||
<section class="essentials">
|
<section>
|
||||||
<h3>Username</h3>
|
<h3>Username</h3>
|
||||||
<label>
|
<label>
|
||||||
<span>
|
<span>
|
||||||
@@ -42,7 +42,7 @@
|
|||||||
<input v-model="bio" type="text" placeholder="Enter your bio" />
|
<input v-model="bio" type="text" placeholder="Enter your bio" />
|
||||||
</label>
|
</label>
|
||||||
</section>
|
</section>
|
||||||
<section class="essentials pad-maker">
|
<section class="pad-maker">
|
||||||
<h3>Theme</h3>
|
<h3>Theme</h3>
|
||||||
<label>
|
<label>
|
||||||
<span
|
<span
|
||||||
@@ -58,7 +58,7 @@
|
|||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
</section>
|
</section>
|
||||||
<section class="essentials pad-maker">
|
<section class="pad-maker">
|
||||||
<h3>Authorization token</h3>
|
<h3>Authorization token</h3>
|
||||||
<label>
|
<label>
|
||||||
<span>
|
<span>
|
||||||
@@ -68,7 +68,7 @@
|
|||||||
<input
|
<input
|
||||||
type="button"
|
type="button"
|
||||||
class="button pad-rem"
|
class="button pad-rem"
|
||||||
value="Copy to clipboard"
|
value="Copy to Clipboard"
|
||||||
@click="copyToken"
|
@click="copyToken"
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
@@ -81,10 +81,25 @@
|
|||||||
<input
|
<input
|
||||||
type="button"
|
type="button"
|
||||||
class="button"
|
class="button"
|
||||||
value="Revoke token"
|
value="Revoke Token"
|
||||||
@click="gotoRevoke"
|
@click="gotoRevoke"
|
||||||
/>
|
/>
|
||||||
</label>
|
</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>
|
</section>
|
||||||
</DashboardPage>
|
</DashboardPage>
|
||||||
</template>
|
</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()
|
this.$nuxt.$loading.finish()
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -3,6 +3,12 @@
|
|||||||
<div class="page-contents">
|
<div class="page-contents">
|
||||||
<header class="columns">
|
<header class="columns">
|
||||||
<h2 class="column-grow-1">Edit Mod</h2>
|
<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
|
<button
|
||||||
v-if="mod.status === 'rejected' || mod.status === 'draft'"
|
v-if="mod.status === 'rejected' || mod.status === 'draft'"
|
||||||
title="Submit for Review"
|
title="Submit for Review"
|
||||||
@@ -174,9 +180,9 @@
|
|||||||
</span>
|
</span>
|
||||||
<div class="columns">
|
<div class="columns">
|
||||||
<div class="textarea-wrapper">
|
<div class="textarea-wrapper">
|
||||||
<textarea id="body" v-model="body"></textarea>
|
<textarea id="body" v-model="mod.body"></textarea>
|
||||||
</div>
|
</div>
|
||||||
<div v-compiled-markdown="body" class="markdown-body"></div>
|
<div v-compiled-markdown="mod.body" class="markdown-body"></div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<section class="extra-links">
|
<section class="extra-links">
|
||||||
@@ -309,15 +315,12 @@ export default {
|
|||||||
name: mod.license.name,
|
name: mod.license.name,
|
||||||
}
|
}
|
||||||
|
|
||||||
const reg = /.+?:\/\/.+?(\/.+?)(?:#|\?|$)/
|
if (mod.body_url && !mod.body) {
|
||||||
const urlPath = reg.exec(mod.body_url)[1]
|
mod.body = (await axios.get(mod.body_url)).data
|
||||||
const res = await axios.get(
|
}
|
||||||
`https://modrinth-cdn.nyc3.digitaloceanspaces.com${urlPath}`
|
|
||||||
)
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
mod,
|
mod,
|
||||||
body: res.data,
|
|
||||||
clientSideType: {
|
clientSideType: {
|
||||||
label: mod.client_side,
|
label: mod.client_side,
|
||||||
id: mod.client_side,
|
id: mod.client_side,
|
||||||
|
|||||||
@@ -5,7 +5,11 @@
|
|||||||
:members="members"
|
:members="members"
|
||||||
:current-member="currentMember"
|
: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>
|
</ModPage>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -16,15 +20,6 @@ import ModPage from '@/components/ModPage'
|
|||||||
export default {
|
export default {
|
||||||
components: { ModPage },
|
components: { ModPage },
|
||||||
auth: false,
|
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) {
|
async asyncData(data) {
|
||||||
const config = {
|
const config = {
|
||||||
headers: {
|
headers: {
|
||||||
@@ -42,6 +37,10 @@ export default {
|
|||||||
)
|
)
|
||||||
).data
|
).data
|
||||||
|
|
||||||
|
if (mod.body_url && !mod.body) {
|
||||||
|
mod.body = (await axios.get(mod.body_url)).data
|
||||||
|
}
|
||||||
|
|
||||||
const [members, versions] = (
|
const [members, versions] = (
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
axios.get(`https://api.modrinth.com/api/v1/team/${mod.team}/members`),
|
axios.get(`https://api.modrinth.com/api/v1/team/${mod.team}/members`),
|
||||||
@@ -90,11 +89,6 @@ export default {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
body: '',
|
|
||||||
}
|
|
||||||
},
|
|
||||||
head() {
|
head() {
|
||||||
return {
|
return {
|
||||||
title: this.mod.title + ' - Modrinth',
|
title: this.mod.title + ' - Modrinth',
|
||||||
|
|||||||
339
pages/mod/_id/newversion.vue
Normal file
339
pages/mod/_id/newversion.vue
Normal 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>
|
||||||
@@ -5,6 +5,39 @@
|
|||||||
:members="members.filter((it) => it.accepted)"
|
:members="members.filter((it) => it.accepted)"
|
||||||
:current-member="currentMember"
|
: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">
|
<div class="section-header columns">
|
||||||
<h3 class="column-grow-1">Team members</h3>
|
<h3 class="column-grow-1">Team members</h3>
|
||||||
<div class="column">
|
<div class="column">
|
||||||
@@ -392,6 +425,18 @@ export default {
|
|||||||
|
|
||||||
this.$nuxt.$loading.finish()
|
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>
|
</script>
|
||||||
@@ -486,4 +531,34 @@ button {
|
|||||||
cursor: not-allowed !important;
|
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>
|
</style>
|
||||||
|
|||||||
303
pages/mod/_id/version/_version/edit.vue
Normal file
303
pages/mod/_id/version/_version/edit.vue
Normal 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>
|
||||||
@@ -21,17 +21,31 @@
|
|||||||
{{ version.version_number }}
|
{{ version.version_number }}
|
||||||
</span>
|
</span>
|
||||||
<Categories :categories="version.loaders" />
|
<Categories :categories="version.loaders" />
|
||||||
<a
|
<div class="buttons">
|
||||||
v-if="primaryFile"
|
<button class="action" @click="deleteVersion">
|
||||||
:href="primaryFile.url"
|
<TrashIcon />
|
||||||
class="download-button"
|
Delete
|
||||||
@click.prevent="
|
</button>
|
||||||
downloadFile(primaryFile.hashes.sha1, primaryFile.url)
|
<nuxt-link
|
||||||
"
|
v-if="currentMember"
|
||||||
>
|
class="action"
|
||||||
<DownloadIcon />
|
:to="version.id + '/edit'"
|
||||||
Download
|
>
|
||||||
</a>
|
<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>
|
||||||
<div class="stats">
|
<div class="stats">
|
||||||
<div class="stat">
|
<div class="stat">
|
||||||
@@ -67,7 +81,10 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</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 class="files">
|
||||||
<div v-for="file in version.files" :key="file.hashes.sha1" class="file">
|
<div v-for="file in version.files" :key="file.hashes.sha1" class="file">
|
||||||
<div class="text-wrapper">
|
<div class="text-wrapper">
|
||||||
@@ -98,6 +115,8 @@ import ModPage from '@/components/ModPage'
|
|||||||
|
|
||||||
import Categories from '@/components/Categories'
|
import Categories from '@/components/Categories'
|
||||||
import FileInput from '@/components/FileInput'
|
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 DownloadIcon from '~/assets/images/utils/download.svg?inline'
|
||||||
import CalendarIcon from '~/assets/images/utils/calendar.svg?inline'
|
import CalendarIcon from '~/assets/images/utils/calendar.svg?inline'
|
||||||
import TagIcon from '~/assets/images/utils/tag.svg?inline'
|
import TagIcon from '~/assets/images/utils/tag.svg?inline'
|
||||||
@@ -110,13 +129,10 @@ export default {
|
|||||||
DownloadIcon,
|
DownloadIcon,
|
||||||
CalendarIcon,
|
CalendarIcon,
|
||||||
TagIcon,
|
TagIcon,
|
||||||
|
TrashIcon,
|
||||||
|
EditIcon,
|
||||||
},
|
},
|
||||||
auth: false,
|
auth: false,
|
||||||
async fetch() {
|
|
||||||
if (this.version.changelog_url) {
|
|
||||||
this.changelog = (await axios.get(this.version.changelog_url)).data
|
|
||||||
}
|
|
||||||
},
|
|
||||||
async asyncData(data) {
|
async asyncData(data) {
|
||||||
const config = {
|
const config = {
|
||||||
headers: {
|
headers: {
|
||||||
@@ -175,6 +191,10 @@ export default {
|
|||||||
? members.find((x) => x.user_id === data.$auth.user.id)
|
? members.find((x) => x.user_id === data.$auth.user.id)
|
||||||
: null
|
: null
|
||||||
|
|
||||||
|
if (!version.changelog && version.changelog_url) {
|
||||||
|
version.changelog = (await axios.get(version.changelog_url)).data
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
mod,
|
mod,
|
||||||
versions: versions.sort(
|
versions: versions.sort(
|
||||||
@@ -196,7 +216,6 @@ export default {
|
|||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
changelog: '',
|
|
||||||
filesToUpload: [],
|
filesToUpload: [],
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -212,6 +231,8 @@ export default {
|
|||||||
elem.click()
|
elem.click()
|
||||||
},
|
},
|
||||||
async deleteFile(hash) {
|
async deleteFile(hash) {
|
||||||
|
this.$nuxt.$loading.start()
|
||||||
|
|
||||||
const config = {
|
const config = {
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: this.$auth.getToken('local'),
|
Authorization: this.$auth.getToken('local'),
|
||||||
@@ -222,8 +243,13 @@ export default {
|
|||||||
`https://api.modrinth.com/api/v1/version_file/${hash}`,
|
`https://api.modrinth.com/api/v1/version_file/${hash}`,
|
||||||
config
|
config
|
||||||
)
|
)
|
||||||
|
|
||||||
|
await this.$router.go(null)
|
||||||
|
this.$nuxt.$loading.finish()
|
||||||
},
|
},
|
||||||
async makePrimary(hash) {
|
async makePrimary(hash) {
|
||||||
|
this.$nuxt.$loading.start()
|
||||||
|
|
||||||
const config = {
|
const config = {
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: this.$auth.getToken('local'),
|
Authorization: this.$auth.getToken('local'),
|
||||||
@@ -233,12 +259,13 @@ export default {
|
|||||||
await axios.patch(
|
await axios.patch(
|
||||||
`https://api.modrinth.com/api/v1/version/${this.version.id}`,
|
`https://api.modrinth.com/api/v1/version/${this.version.id}`,
|
||||||
{
|
{
|
||||||
primary_file: {
|
primary_file: ['sha1', hash],
|
||||||
sha1: hash,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
config
|
config
|
||||||
)
|
)
|
||||||
|
|
||||||
|
await this.$router.go(null)
|
||||||
|
this.$nuxt.$loading.finish()
|
||||||
},
|
},
|
||||||
async addFiles(e) {
|
async addFiles(e) {
|
||||||
this.filesToUpload = e.target.files
|
this.filesToUpload = e.target.files
|
||||||
@@ -287,6 +314,23 @@ export default {
|
|||||||
|
|
||||||
this.$nuxt.$loading.finish()
|
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() {
|
head() {
|
||||||
return {
|
return {
|
||||||
@@ -350,23 +394,27 @@ export default {
|
|||||||
margin: auto 0.5rem auto 0;
|
margin: auto 0.5rem auto 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.download-button {
|
.buttons {
|
||||||
margin-left: auto;
|
|
||||||
padding: 0.5rem;
|
|
||||||
color: var(--color-button-text);
|
|
||||||
background-color: var(--color-button-bg);
|
|
||||||
justify-self: flex-end;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
margin-left: auto;
|
||||||
border-radius: var(--size-rounded-sm);
|
|
||||||
|
|
||||||
&:hover,
|
.action {
|
||||||
&:focus {
|
padding: 0.5rem;
|
||||||
background-color: var(--color-button-bg-hover);
|
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 {
|
&:hover,
|
||||||
margin-right: 0.25rem;
|
&:focus {
|
||||||
|
background-color: var(--color-button-bg-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
svg {
|
||||||
|
margin-right: 0.25rem;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -68,159 +68,26 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<Popup
|
<nuxt-link v-if="currentMember" to="newversion" class="button">
|
||||||
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"
|
|
||||||
>
|
|
||||||
New Version
|
New Version
|
||||||
</button>
|
</nuxt-link>
|
||||||
</ModPage>
|
</ModPage>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
|
|
||||||
import Multiselect from 'vue-multiselect'
|
|
||||||
|
|
||||||
import ModPage from '@/components/ModPage'
|
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 DownloadIcon from '~/assets/images/utils/download.svg?inline'
|
||||||
import ForgeIcon from '~/assets/images/categories/forge.svg?inline'
|
import ForgeIcon from '~/assets/images/categories/forge.svg?inline'
|
||||||
import FabricIcon from '~/assets/images/categories/fabric.svg?inline'
|
import FabricIcon from '~/assets/images/categories/fabric.svg?inline'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
Multiselect,
|
|
||||||
FileInput,
|
|
||||||
Popup,
|
|
||||||
ModPage,
|
ModPage,
|
||||||
ForgeIcon,
|
ForgeIcon,
|
||||||
FabricIcon,
|
FabricIcon,
|
||||||
DownloadIcon,
|
DownloadIcon,
|
||||||
TrashIcon,
|
|
||||||
},
|
},
|
||||||
auth: false,
|
auth: false,
|
||||||
async asyncData(data) {
|
async asyncData(data) {
|
||||||
@@ -292,68 +159,7 @@ export default {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
showPopup: false,
|
|
||||||
createdVersion: {},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
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) {
|
findPrimary(version) {
|
||||||
let file = version.files.find((x) => x.primary)
|
let file = version.files.find((x) => x.primary)
|
||||||
|
|
||||||
@@ -487,65 +293,9 @@ table {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.multiselect {
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
input {
|
.button {
|
||||||
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 {
|
|
||||||
float: right;
|
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) {
|
@media screen and (max-width: 400px) {
|
||||||
|
|||||||
@@ -556,6 +556,12 @@ export default {
|
|||||||
async createMod() {
|
async createMod() {
|
||||||
this.$nuxt.$loading.start()
|
this.$nuxt.$loading.start()
|
||||||
|
|
||||||
|
for (const version of this.versions) {
|
||||||
|
if (!version.version_title) {
|
||||||
|
version.version_title = version.version_number
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const formData = new FormData()
|
const formData = new FormData()
|
||||||
|
|
||||||
formData.append(
|
formData.append(
|
||||||
|
|||||||
Reference in New Issue
Block a user