Move mod pages to nuxt child (#201)

* Move mod pages to nuxt child

* Minor fixes

* Fix lockfile
This commit is contained in:
Geometrically
2021-05-10 21:35:03 -07:00
committed by GitHub
parent d914b38f9f
commit 0e338ef453
10 changed files with 1070 additions and 1415 deletions

899
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -22,7 +22,7 @@
"cookie-universal-nuxt": "^2.1.4", "cookie-universal-nuxt": "^2.1.4",
"highlight.js": "^10.3.2", "highlight.js": "^10.3.2",
"marked": "^2.0.0", "marked": "^2.0.0",
"nuxt": "^2.14.7", "nuxt": "^2.15.5",
"v-tooltip": "^2.0.3", "v-tooltip": "^2.0.3",
"vue-click-outside": "^1.1.0", "vue-click-outside": "^1.1.0",
"vue-highlightjs": "^1.3.3", "vue-highlightjs": "^1.3.3",
@@ -31,7 +31,7 @@
"xss": "^1.0.8" "xss": "^1.0.8"
}, },
"devDependencies": { "devDependencies": {
"@nuxt/types": "^2.14.12", "@nuxt/types": "^2.15.5",
"@nuxtjs/color-mode": "^1.1.1", "@nuxtjs/color-mode": "^1.1.1",
"@nuxtjs/eslint-config": "^3.1.0", "@nuxtjs/eslint-config": "^3.1.0",
"@nuxtjs/eslint-module": "^2.0.0", "@nuxtjs/eslint-module": "^2.0.0",
@@ -43,6 +43,6 @@
"eslint-plugin-prettier": "^3.1.4", "eslint-plugin-prettier": "^3.1.4",
"node-sass": "^4.14.1", "node-sass": "^4.14.1",
"prettier": "^2.1.2", "prettier": "^2.1.2",
"sass-loader": "^9.0.3" "sass-loader": "^10.1.1"
} }
} }

View File

@@ -84,7 +84,7 @@
</nuxt-link> </nuxt-link>
<nuxt-link <nuxt-link
v-if="currentMember" v-if="currentMember"
:to="'/mod/' + mod.id + '/settings'" :to="'/mod/' + (mod.slug ? mod.slug : mod.id) + '/settings'"
class="tab" class="tab"
> >
<span>Settings</span> <span>Settings</span>
@@ -129,7 +129,14 @@
</div> </div>
</div> </div>
<div class="mod-content"> <div class="mod-content">
<slot /> <NuxtChild
:mod="mod"
:versions="versions"
:featured-versions="featuredVersions"
:members="members"
:current-member="currentMember"
:link-bar.sync="linkBar"
/>
</div> </div>
</div> </div>
<section class="mod-info"> <section class="mod-info">
@@ -280,7 +287,14 @@
Alpha Alpha
</span> </span>
<h4 class="title"> <h4 class="title">
<nuxt-link :to="'/mod/' + mod.id + '/version/' + version.id"> <nuxt-link
:to="
'/mod/' +
(mod.slug ? mod.slug : mod.id) +
'/version/' +
version.id
"
>
{{ version.name }} {{ version.name }}
</nuxt-link> </nuxt-link>
</h4> </h4>
@@ -377,49 +391,65 @@ export default {
ReportIcon, ReportIcon,
FollowIcon, FollowIcon,
}, },
props: { async asyncData(data) {
mod: { try {
type: Object, const mod = (
default() { await axios.get(
return {} `https://api.modrinth.com/api/v1/mod/${data.params.id}`,
}, data.$auth.headers
}, )
featuredVersions: { ).data
type: Array,
default() { const [members, versions, featuredVersions, userFollows] = (
return [] await Promise.all([
}, axios.get(`https://api.modrinth.com/api/v1/team/${mod.team}/members`),
}, axios.get(`https://api.modrinth.com/api/v1/mod/${mod.id}/version`),
versions: { axios.get(
type: Array, `https://api.modrinth.com/api/v1/mod/${mod.id}/version?featured=true`
default() { ),
return [] axios.get(
}, data.$auth.user
}, ? `https://api.modrinth.com/api/v1/user/${data.$auth.user.id}/follows`
members: { : `https://api.modrinth.com`,
type: Array, data.$auth.headers
default() { ),
return [] ])
}, ).map((it) => it.data)
},
currentMember: { const users = (
type: Object, await axios.get(
default() { `https://api.modrinth.com/api/v1/users?ids=${JSON.stringify(
return null members.map((it) => it.user_id)
}, )}`,
}, data.$auth.headers
linkBar: { )
type: Array, ).data
default() {
return [] users.forEach((it) => {
}, const index = members.findIndex((x) => x.user_id === it.id)
}, members[index].avatar_url = it.avatar_url
userFollows: { members[index].name = it.username
type: Array, })
default() {
return null const currentMember = data.$auth.user
}, ? members.find((x) => x.user_id === data.$auth.user.id)
}, : null
return {
mod,
versions,
featuredVersions,
members,
currentMember,
userFollows: userFollows.name ? null : userFollows,
linkBar: [],
}
} catch {
data.error({
statusCode: 404,
message: 'Mod not found',
})
}
}, },
methods: { methods: {
formatNumber(x) { formatNumber(x) {
@@ -466,6 +496,52 @@ export default {
this.userFollows.splice(this.userFollows.indexOf(this.mod.id), 1) this.userFollows.splice(this.userFollows.indexOf(this.mod.id), 1)
}, },
}, },
head() {
return {
title: this.mod.title + ' - Modrinth',
meta: [
{
hid: 'og:type',
name: 'og:type',
content: 'website',
},
{
hid: 'og:title',
name: 'og:title',
content: this.mod.title,
},
{
hid: 'apple-mobile-web-app-title',
name: 'apple-mobile-web-app-title',
content: this.mod.title,
},
{
hid: 'og:description',
name: 'og:description',
content: this.mod.description,
},
{
hid: 'description',
name: 'description',
content:
this.mod.description +
' View other minecraft mods on Modrinth today! Modrinth is a new and modern Minecraft modding platform supporting both the Forge and Fabric mod loaders.',
},
{
hid: 'og:url',
name: 'og:url',
content: `https://modrinth.com/mod/${this.mod.id}`,
},
{
hid: 'og:image',
name: 'og:image',
content: this.mod.icon_url
? this.mod.icon_url
: 'https://cdn.modrinth.com/placeholder.png',
},
],
}
},
} }
</script> </script>

View File

@@ -1,302 +1,298 @@
<template> <template>
<div class="page-container"> <div class="page-contents">
<div class="page-contents"> <header class="columns">
<header class="columns"> <h3 class="column-grow-1">Edit Mod</h3>
<h3 class="column-grow-1">Edit Mod</h3> <nuxt-link
<nuxt-link :to="'/mod/' + (mod.slug ? mod.slug : mod.id)"
:to="'/mod/' + (mod.slug ? mod.slug : mod.id)" class="button column"
class="button column" >
> Back
Back </nuxt-link>
</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" class="button column"
class="button column" :disabled="!this.$nuxt.$loading"
:disabled="!this.$nuxt.$loading" @click="saveModReview"
@click="saveModReview" >
> Submit for Review
Submit for Review </button>
</button> <button
<button title="Save"
title="Save" class="brand-button column"
class="brand-button column" :disabled="!this.$nuxt.$loading"
:disabled="!this.$nuxt.$loading" @click="saveMod"
@click="saveMod" >
> Save
Save </button>
</button> </header>
</header> <section class="essentials">
<section class="essentials"> <h3>Name</h3>
<h3>Name</h3> <label>
<label>
<span>
Be creative. TechCraft v7 won't be searchable and won't be clicked
on
</span>
<input v-model="mod.title" type="text" placeholder="Enter the name" />
</label>
<h3>Summary</h3>
<label>
<span>
Give a quick description to your mod. It will appear in the search
</span>
<input
v-model="mod.description"
type="text"
placeholder="Enter the summary"
/>
</label>
<h3>Categories</h3>
<label>
<span>
Select up to 3 categories. They will help to find your mod
</span>
<multiselect
id="categories"
v-model="mod.categories"
:options="availableCategories"
:loading="availableCategories.length === 0"
:multiple="true"
:searchable="false"
:show-no-results="false"
:close-on-select="false"
:clear-on-select="false"
:show-labels="false"
:max="3"
:limit="6"
:hide-selected="true"
placeholder="Choose categories"
/>
</label>
<h3>Vanity URL (slug)</h3>
<label>
<span>
Set this to something pretty, so URLs to your mod are more readable
</span>
<input
id="name"
v-model="mod.slug"
type="text"
placeholder="Enter the vanity URL's last bit"
/>
</label>
</section>
<section class="mod-icon rows">
<h3>Icon</h3>
<div class="columns row-grow-1">
<div class="column-grow-1 rows">
<file-input
accept="image/png,image/jpeg,image/gif,image/webp"
class="choose-image"
prompt="Choose image or drag it here"
@change="showPreviewImage"
/>
<ul class="row-grow-1">
<li>Must be a square</li>
<li>Minimum size is 100x100</li>
<li>Acceptable formats are PNG, JPEG, GIF and WEBP</li>
</ul>
<button
class="transparent-button"
@click="
icon = null
previewImage = null
iconChanged = true
"
>
Reset icon
</button>
</div>
<img
:src="
previewImage
? previewImage
: mod.icon_url && !iconChanged
? mod.icon_url
: 'https://cdn.modrinth.com/placeholder.svg'
"
alt="preview-image"
/>
</div>
</section>
<section class="game-sides">
<h3>Supported environments</h3>
<div class="columns">
<span>
Let others know if your mod is for clients, servers or universal.
For example, IC2 will be required + required, while OptiFine will be
required + no functionality
</span>
<div class="labeled-control">
<h3>Client</h3>
<Multiselect
v-model="clientSideType"
placeholder="Select one"
:options="sideTypes"
:searchable="false"
:close-on-select="true"
:show-labels="false"
:allow-empty="false"
/>
</div>
<div class="labeled-control">
<h3>Server</h3>
<Multiselect
v-model="serverSideType"
placeholder="Select one"
:options="sideTypes"
:searchable="false"
:close-on-select="true"
:show-labels="false"
:allow-empty="false"
/>
</div>
</div>
</section>
<section class="description">
<h3>
<label
for="body"
title="You can type the of the long form of your description here."
>
Description
</label>
</h3>
<span> <span>
You can type the of the long form of your description here. This Be creative. TechCraft v7 won't be searchable and won't be clicked on
editor supports markdown. You can find the syntax
<a
href="https://guides.github.com/features/mastering-markdown/"
target="_blank"
rel="noopener noreferrer"
>here</a
>.
</span> </span>
<div class="columns"> <input v-model="mod.title" type="text" placeholder="Enter the name" />
<div class="textarea-wrapper"> </label>
<textarea id="body" v-model="mod.body"></textarea> <h3>Summary</h3>
</div> <label>
<div v-compiled-markdown="mod.body" class="markdown-body"></div> <span>
</div> Give a quick description to your mod. It will appear in the search
</section> </span>
<section class="extra-links"> <input
<div class="title"> v-model="mod.description"
<h3>External links</h3> type="text"
</div> placeholder="Enter the summary"
<label />
title="A place for users to report bugs, issues, and concerns about your mod." </label>
> <h3>Categories</h3>
<span>Issue tracker</span> <label>
<input <span>
v-model="mod.issues_url" Select up to 3 categories. They will help to find your mod
type="url" </span>
placeholder="Enter a valid URL" <multiselect
id="categories"
v-model="mod.categories"
:options="availableCategories"
:loading="availableCategories.length === 0"
:multiple="true"
:searchable="false"
:show-no-results="false"
:close-on-select="false"
:clear-on-select="false"
:show-labels="false"
:max="3"
:limit="6"
:hide-selected="true"
placeholder="Choose categories"
/>
</label>
<h3>Vanity URL (slug)</h3>
<label>
<span>
Set this to something pretty, so URLs to your mod are more readable
</span>
<input
id="name"
v-model="mod.slug"
type="text"
placeholder="Enter the vanity URL's last bit"
/>
</label>
</section>
<section class="mod-icon rows">
<h3>Icon</h3>
<div class="columns row-grow-1">
<div class="column-grow-1 rows">
<file-input
accept="image/png,image/jpeg,image/gif,image/webp"
class="choose-image"
prompt="Choose image or drag it here"
@change="showPreviewImage"
/> />
</label> <ul class="row-grow-1">
<label title="A page/repository containing the source code"> <li>Must be a square</li>
<span>Source code</span> <li>Minimum size is 100x100</li>
<input <li>Acceptable formats are PNG, JPEG, GIF and WEBP</li>
v-model="mod.source_url" </ul>
type="url"
placeholder="Enter a valid URL"
/>
</label>
<label
title="A page containing information, documentation, and help for the mod."
>
<span>Wiki page</span>
<input
v-model="mod.wiki_url"
type="url"
placeholder="Enter a valid URL"
/>
</label>
<label title="An inivitation link to your Discord server.">
<span>Discord invite</span>
<input
v-model="mod.discord_url"
type="url"
placeholder="Enter a valid URL"
/>
</label>
</section>
<section class="license">
<div class="title">
<h3>License</h3>
</div>
<label>
<span>
It is really important to choose a proper license for your mod. You
may choose one from our list or provide a URL to your own license.
URL field will be filled automatically for provided licenses
</span>
<div class="input-group">
<Multiselect
v-model="license"
placeholder="Select one"
track-by="short"
label="name"
:options="availableLicenses"
:searchable="true"
:close-on-select="true"
:show-labels="false"
/>
<input v-model="license_url" type="url" placeholder="License URL" />
</div>
</label>
</section>
<section class="donations">
<div class="title">
<h3>Donation links</h3>
<button <button
title="Add a link" class="transparent-button"
class="button"
:disabled="false"
@click=" @click="
donationPlatforms.push({}) icon = null
donationLinks.push('') previewImage = null
iconChanged = true
" "
> >
Add a link Reset icon
</button> </button>
</div> </div>
<div v-for="(item, index) in donationPlatforms" :key="index"> <img
<label title="The donation link."> :src="
<span>Donation Link</span> previewImage
<input ? previewImage
v-model="donationLinks[index]" : mod.icon_url && !iconChanged
type="url" ? mod.icon_url
placeholder="Enter a valid URL" : 'https://cdn.modrinth.com/placeholder.svg'
/> "
</label> alt="preview-image"
<label title="The donation platform of the link."> />
<span>Donation Platform</span> </div>
<Multiselect </section>
v-model="donationPlatforms[index]" <section class="game-sides">
placeholder="Select one" <h3>Supported environments</h3>
track-by="short" <div class="columns">
label="name" <span>
:options="availableDonationPlatforms" Let others know if your mod is for clients, servers or universal. For
:searchable="false" example, IC2 will be required + required, while OptiFine will be
:close-on-select="true" required + no functionality
:show-labels="false" </span>
/> <div class="labeled-control">
</label> <h3>Client</h3>
<button <Multiselect
class="button" v-model="clientSideType"
@click=" placeholder="Select one"
donationPlatforms.splice(index, 1) :options="sideTypes"
donationLinks.splice(index, 1) :searchable="false"
" :close-on-select="true"
> :show-labels="false"
Remove Link :allow-empty="false"
</button> />
<hr />
</div> </div>
</section> <div class="labeled-control">
<m-footer class="footer" centered /> <h3>Server</h3>
</div> <Multiselect
v-model="serverSideType"
placeholder="Select one"
:options="sideTypes"
:searchable="false"
:close-on-select="true"
:show-labels="false"
:allow-empty="false"
/>
</div>
</div>
</section>
<section class="description">
<h3>
<label
for="body"
title="You can type the of the long form of your description here."
>
Description
</label>
</h3>
<span>
You can type the of the long form of your description here. This editor
supports markdown. You can find the syntax
<a
href="https://guides.github.com/features/mastering-markdown/"
target="_blank"
rel="noopener noreferrer"
>here</a
>.
</span>
<div class="columns">
<div class="textarea-wrapper">
<textarea id="body" v-model="mod.body"></textarea>
</div>
<div v-compiled-markdown="mod.body" class="markdown-body"></div>
</div>
</section>
<section class="extra-links">
<div class="title">
<h3>External links</h3>
</div>
<label
title="A place for users to report bugs, issues, and concerns about your mod."
>
<span>Issue tracker</span>
<input
v-model="mod.issues_url"
type="url"
placeholder="Enter a valid URL"
/>
</label>
<label title="A page/repository containing the source code">
<span>Source code</span>
<input
v-model="mod.source_url"
type="url"
placeholder="Enter a valid URL"
/>
</label>
<label
title="A page containing information, documentation, and help for the mod."
>
<span>Wiki page</span>
<input
v-model="mod.wiki_url"
type="url"
placeholder="Enter a valid URL"
/>
</label>
<label title="An inivitation link to your Discord server.">
<span>Discord invite</span>
<input
v-model="mod.discord_url"
type="url"
placeholder="Enter a valid URL"
/>
</label>
</section>
<section class="license">
<div class="title">
<h3>License</h3>
</div>
<label>
<span>
It is really important to choose a proper license for your mod. You
may choose one from our list or provide a URL to your own license. URL
field will be filled automatically for provided licenses
</span>
<div class="input-group">
<Multiselect
v-model="license"
placeholder="Select one"
track-by="short"
label="name"
:options="availableLicenses"
:searchable="true"
:close-on-select="true"
:show-labels="false"
/>
<input v-model="license_url" type="url" placeholder="License URL" />
</div>
</label>
</section>
<section class="donations">
<div class="title">
<h3>Donation links</h3>
<button
title="Add a link"
class="button"
:disabled="false"
@click="
donationPlatforms.push({})
donationLinks.push('')
"
>
Add a link
</button>
</div>
<div v-for="(item, index) in donationPlatforms" :key="index">
<label title="The donation link.">
<span>Donation Link</span>
<input
v-model="donationLinks[index]"
type="url"
placeholder="Enter a valid URL"
/>
</label>
<label title="The donation platform of the link.">
<span>Donation Platform</span>
<Multiselect
v-model="donationPlatforms[index]"
placeholder="Select one"
track-by="short"
label="name"
:options="availableDonationPlatforms"
:searchable="false"
:close-on-select="true"
:show-labels="false"
/>
</label>
<button
class="button"
@click="
donationPlatforms.splice(index, 1)
donationLinks.splice(index, 1)
"
>
Remove Link
</button>
<hr />
</div>
</section>
</div> </div>
</template> </template>
@@ -305,11 +301,9 @@ import axios from 'axios'
import Multiselect from 'vue-multiselect' import Multiselect from 'vue-multiselect'
import FileInput from '~/components/ui/FileInput' import FileInput from '~/components/ui/FileInput'
import MFooter from '~/components/layout/MFooter'
export default { export default {
components: { components: {
MFooter,
FileInput, FileInput,
Multiselect, Multiselect,
}, },
@@ -411,6 +405,9 @@ export default {
} }
}, },
}, },
created() {
this.$emit('update:link-bar', [['Edit', 'edit']])
},
methods: { methods: {
async saveModReview() { async saveModReview() {
this.isProcessing = true this.isProcessing = true
@@ -464,7 +461,9 @@ export default {
) )
} }
await this.$router.replace(`/mod/${this.mod.id}`) await this.$router.replace(
`/mod/${this.mod.slug ? this.mod.slug : this.mod.id}`
)
} catch (err) { } catch (err) {
this.$notify({ this.$notify({
group: 'main', group: 'main',
@@ -545,12 +544,14 @@ label {
grid-template: grid-template:
'header header header' auto 'header header header' auto
'advert advert advert' auto 'advert advert advert' auto
'essentials essentials mod-icon' auto 'essentials essentials essentials' auto
'mod-icon mod-icon mod-icon' auto
'game-sides game-sides game-sides' auto 'game-sides game-sides game-sides' auto
'description description description' auto 'description description description' auto
'versions versions versions' auto 'versions versions versions' auto
'extra-links license license' auto 'extra-links extra-links extra-links' auto
'donations donations .' auto 'license license license' auto
'donations donations donations' auto
'footer footer footer' auto 'footer footer footer' auto
/ 4fr 1fr 4fr; / 4fr 1fr 4fr;
column-gap: var(--spacing-card-md); column-gap: var(--spacing-card-md);

View File

@@ -1,136 +1,20 @@
<template> <template>
<ModPage <div v-compiled-markdown="mod.body" v-highlightjs class="markdown-body"></div>
:mod="mod"
:versions="versions"
:featured-versions="featuredVersions"
:members="members"
:current-member="currentMember"
:link-bar="[['Description', '']]"
:user-follows="userFollows"
>
<div
v-compiled-markdown="mod.body"
v-highlightjs
class="markdown-body"
></div>
</ModPage>
</template> </template>
<script> <script>
import axios from 'axios'
import ModPage from '~/components/layout/ModPage'
export default { export default {
components: { ModPage },
auth: false, auth: false,
async asyncData(data) { props: {
try { mod: {
const mod = ( type: Object,
await axios.get( default() {
`https://api.modrinth.com/api/v1/mod/${data.params.id}`, return {}
data.$auth.headers },
) },
).data
if (mod.body_url && !mod.body) {
mod.body = (await axios.get(mod.body_url)).data
}
const [members, versions, featuredVersions, userFollows] = (
await Promise.all([
axios.get(`https://api.modrinth.com/api/v1/team/${mod.team}/members`),
axios.get(`https://api.modrinth.com/api/v1/mod/${mod.id}/version`),
axios.get(
`https://api.modrinth.com/api/v1/mod/${mod.id}/version?featured=true`
),
axios.get(
data.$auth.user
? `https://api.modrinth.com/api/v1/user/${data.$auth.user.id}/follows`
: `https://api.modrinth.com`,
data.$auth.headers
),
])
).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)
)}`,
data.$auth.headers
)
).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.user
? members.find((x) => x.user_id === data.$auth.user.id)
: null
return {
mod,
versions,
featuredVersions,
members,
currentMember,
userFollows: userFollows.name ? null : userFollows,
}
} catch {
data.error({
statusCode: 404,
message: 'Mod not found',
})
}
}, },
head() { created() {
return { this.$emit('update:link-bar', [['Description', '']])
title: this.mod.title + ' - Modrinth',
meta: [
{
hid: 'og:type',
name: 'og:type',
content: 'website',
},
{
hid: 'og:title',
name: 'og:title',
content: this.mod.title,
},
{
hid: 'apple-mobile-web-app-title',
name: 'apple-mobile-web-app-title',
content: this.mod.title,
},
{
hid: 'og:description',
name: 'og:description',
content: this.mod.description,
},
{
hid: 'description',
name: 'description',
content:
this.mod.description +
' View other minecraft mods on Modrinth today! Modrinth is a new and modern Minecraft modding platform that is compatible with CurseForge too!',
},
{
hid: 'og:url',
name: 'og:url',
content: `https://modrinth.com/mod/${this.mod.id}`,
},
{
hid: 'og:image',
name: 'og:image',
content: this.mod.icon_url
? this.mod.icon_url
: 'https://cdn.modrinth.com/placeholder.png',
},
],
}
}, },
} }
</script> </script>

View File

@@ -1,13 +1,5 @@
<template> <template>
<ModPage <div>
:mod="mod"
:versions="versions"
:featured-versions="featuredVersions"
:members="members"
:current-member="currentMember"
:link-bar="[['New Version', 'newversion']]"
:user-follows="userFollows"
>
<div class="new-version"> <div class="new-version">
<div class="controls"> <div class="controls">
<button <button
@@ -124,88 +116,44 @@
</div> </div>
</div> </div>
</div> </div>
</ModPage> </div>
</template> </template>
<script> <script>
import axios from 'axios' import axios from 'axios'
import Multiselect from 'vue-multiselect' import Multiselect from 'vue-multiselect'
import ModPage from '~/components/layout/ModPage'
import FileInput from '~/components/ui/FileInput' import FileInput from '~/components/ui/FileInput'
export default { export default {
components: { components: {
ModPage,
Multiselect, Multiselect,
FileInput, FileInput,
}, },
props: {
mod: {
type: Object,
default() {
return {}
},
},
},
async asyncData(data) { async asyncData(data) {
try { try {
const mod = ( const [selectableLoaders, selectableVersions] = (
await axios.get(
`https://api.modrinth.com/api/v1/mod/${data.params.id}`,
data.$auth.headers
)
).data
const [
members,
versions,
featuredVersions,
selectableLoaders,
selectableVersions,
userFollows,
] = (
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/mod/${mod.id}/version`),
axios.get(
`https://api.modrinth.com/api/v1/mod/${mod.id}/version?featured=true`
),
axios.get(`https://api.modrinth.com/api/v1/tag/loader`), axios.get(`https://api.modrinth.com/api/v1/tag/loader`),
axios.get(`https://api.modrinth.com/api/v1/tag/game_version`), axios.get(`https://api.modrinth.com/api/v1/tag/game_version`),
axios.get(
data.$auth.user
? `https://api.modrinth.com/api/v1/user/${data.$auth.user.id}/follows`
: `https://api.modrinth.com`,
data.$auth.headers
),
]) ])
).map((it) => it.data) ).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)
)}`,
data.$auth.headers
)
).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.user
? members.find((x) => x.user_id === data.$auth.user.id)
: null
return { return {
mod,
versions,
featuredVersions,
members,
selectableLoaders, selectableLoaders,
selectableVersions, selectableVersions,
currentMember,
userFollows: userFollows.name ? null : userFollows,
} }
} catch { } catch {
data.error({ data.error({
statusCode: 404, statusCode: 404,
message: 'Mod not found', message: 'Unable to fetch versions and loaders',
}) })
} }
}, },
@@ -214,6 +162,9 @@ export default {
createdVersion: {}, createdVersion: {},
} }
}, },
created() {
this.$emit('update:link-bar', [['New Version', 'newversion']])
},
methods: { methods: {
async createVersion() { async createVersion() {
this.$nuxt.$loading.start() this.$nuxt.$loading.start()
@@ -247,7 +198,11 @@ export default {
}, },
}) })
).data ).data
await this.$router.push(`/mod/${data.mod_id}/version/${data.id}`) await this.$router.push(
`/mod/${this.mod.slug ? this.mod.slug : data.mod_id}/version/${
data.id
}`
)
} catch (err) { } catch (err) {
this.$notify({ this.$notify({
group: 'main', group: 'main',

View File

@@ -1,13 +1,5 @@
<template> <template>
<ModPage <div>
:mod="mod"
:versions="versions"
:members="members.filter((it) => it.accepted)"
:current-member="currentMember"
:featured-versions="featuredVersions"
:link-bar="[['Settings', 'settings']]"
:user-follows="userFollows"
>
<ConfirmPopup <ConfirmPopup
ref="delete_popup" ref="delete_popup"
title="Are you sure you want to delete this mod?" title="Are you sure you want to delete this mod?"
@@ -244,82 +236,37 @@
</div> </div>
</div> </div>
</div> </div>
</ModPage> </div>
</template> </template>
<script> <script>
import axios from 'axios' import axios from 'axios'
import ModPage from '~/components/layout/ModPage'
import ConfirmPopup from '~/components/ui/ConfirmPopup' import ConfirmPopup from '~/components/ui/ConfirmPopup'
import DropdownIcon from '~/assets/images/utils/dropdown.svg?inline' import DropdownIcon from '~/assets/images/utils/dropdown.svg?inline'
export default { export default {
components: { ModPage, DropdownIcon, ConfirmPopup }, components: { DropdownIcon, ConfirmPopup },
async asyncData(data) { props: {
try { mod: {
const mod = ( type: Object,
await axios.get( default() {
`https://api.modrinth.com/api/v1/mod/${data.params.id}`, return {}
data.$auth.headers },
) },
).data members: {
type: Array,
const [members, versions, featuredVersions, userFollows] = ( default() {
await Promise.all([ return []
axios.get( },
`https://api.modrinth.com/api/v1/team/${mod.team}/members`, },
data.$auth.headers currentMember: {
), type: Object,
axios.get(`https://api.modrinth.com/api/v1/mod/${mod.id}/version`), default() {
axios.get( return null
`https://api.modrinth.com/api/v1/mod/${mod.id}/version?featured=true` },
), },
axios.get(
data.$auth.user
? `https://api.modrinth.com/api/v1/user/${data.$auth.user.id}/follows`
: `https://api.modrinth.com`,
data.$auth.headers
),
])
).map((it) => it.data)
const [users] = (
await Promise.all([
axios.get(
`https://api.modrinth.com/api/v1/users?ids=${JSON.stringify(
members.map((it) => it.user_id)
)}`,
data.$auth.headers
),
])
).map((it) => it.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.user
? members.find((x) => x.user_id === data.$auth.user.id)
: null
return {
mod,
versions,
featuredVersions,
members,
currentMember,
userFollows: userFollows.name ? null : userFollows,
}
} catch {
data.error({
statusCode: 404,
message: 'Mod not found',
})
}
}, },
data() { data() {
return { return {
@@ -328,6 +275,8 @@ export default {
} }
}, },
created() { created() {
this.$emit('update:link-bar', [['Settings', 'settings']])
this.UPLOAD_VERSION = 1 << 0 this.UPLOAD_VERSION = 1 << 0
this.DELETE_VERSION = 1 << 1 this.DELETE_VERSION = 1 << 1
this.EDIT_DETAILS = 1 << 2 this.EDIT_DETAILS = 1 << 2

View File

@@ -1,17 +1,5 @@
<template> <template>
<ModPage <div>
:mod="mod"
:versions="versions"
:featured-versions="featuredVersions"
:members="members"
:current-member="currentMember"
:link-bar="[
['Versions', 'versions'],
[version.name, 'versions/' + version.id],
['Edit Version', 'versions/' + version.id + '/edit'],
]"
:user-follows="userFollows"
>
<div class="new-version"> <div class="new-version">
<div class="controls"> <div class="controls">
<button class="brand-button" title="Save version" @click="saveVersion"> <button class="brand-button" title="Save version" @click="saveVersion">
@@ -111,106 +99,87 @@
</div> </div>
</div> </div>
</div> </div>
</ModPage> </div>
</template> </template>
<script> <script>
import axios from 'axios' import axios from 'axios'
import Multiselect from 'vue-multiselect' import Multiselect from 'vue-multiselect'
import ModPage from '~/components/layout/ModPage'
export default { export default {
components: { components: {
ModPage,
Multiselect, Multiselect,
}, },
auth: false, auth: false,
props: {
mod: {
type: Object,
default() {
return {}
},
},
versions: {
type: Array,
default() {
return []
},
},
members: {
type: Array,
default() {
return [{}]
},
},
currentMember: {
type: Object,
default() {
return null
},
},
},
async fetch() {
this.version = this.versions.find(
(x) => x.id === this.$route.params.version
)
if (!this.version.changelog && this.version.changelog_url) {
this.version.changelog = (
await axios.get(this.version.changelog_url)
).data
}
},
async asyncData(data) { async asyncData(data) {
try { try {
const mod = ( const [selectableLoaders, selectableVersions] = (
await axios.get(
`https://api.modrinth.com/api/v1/mod/${data.params.id}`,
data.$auth.headers
)
).data
const [
members,
versions,
featuredVersions,
selectableLoaders,
selectableVersions,
userFollows,
] = (
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/mod/${mod.id}/version`),
axios.get(
`https://api.modrinth.com/api/v1/mod/${mod.id}/version?featured=true`
),
axios.get(`https://api.modrinth.com/api/v1/tag/loader`), axios.get(`https://api.modrinth.com/api/v1/tag/loader`),
axios.get(`https://api.modrinth.com/api/v1/tag/game_version`), axios.get(`https://api.modrinth.com/api/v1/tag/game_version`),
axios.get(
data.$auth.user
? `https://api.modrinth.com/api/v1/user/${data.$auth.user.id}/follows`
: `https://api.modrinth.com`,
data.$auth.headers
),
]) ])
).map((it) => it.data) ).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)
)}`,
data.$auth.headers
)
).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.user
? 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 { return {
mod,
versions,
featuredVersions,
members,
version,
primaryFile,
currentMember,
selectableLoaders, selectableLoaders,
selectableVersions, selectableVersions,
userFollows: userFollows.name ? null : userFollows,
} }
} catch { } catch {
data.error({ data.error({
statusCode: 404, statusCode: 404,
message: 'Version not found', message: 'Unable to fetch versions or loaders',
}) })
} }
}, },
data() {
return {
version: {},
}
},
mounted() {
this.$emit('update:link-bar', [
['Versions', 'versions'],
[this.version.name, 'versions/' + this.version.id],
['Edit Version', 'versions/' + this.version.id + '/edit'],
])
},
methods: { methods: {
async saveVersion() { async saveVersion() {
this.$nuxt.$loading.start() this.$nuxt.$loading.start()
@@ -222,7 +191,9 @@ export default {
this.$auth.headers this.$auth.headers
) )
await this.$router.replace( await this.$router.replace(
`/mod/${this.mod.id}/version/${this.version.id}` `/mod/${this.mod.slug ? this.mod.slug : this.mod.id}/version/${
this.version.id
}`
) )
} catch (err) { } catch (err) {
this.$notify({ this.$notify({

View File

@@ -1,16 +1,5 @@
<template> <template>
<ModPage <div>
:mod="mod"
:versions="versions"
:members="members"
:current-member="currentMember"
:featured-versions="featuredVersions"
:link-bar="[
['Versions', 'versions'],
[version.name, 'versions/' + version.id],
]"
:user-follows="userFollows"
>
<ConfirmPopup <ConfirmPopup
ref="delete_file_popup" ref="delete_file_popup"
title="Are you sure you want to delete this file?" title="Are you sure you want to delete this file?"
@@ -73,7 +62,7 @@
:href="primaryFile.url" :href="primaryFile.url"
class="action iconified-button" class="action iconified-button"
@click.prevent=" @click.prevent="
downloadFile(primaryFile.hashes.sha1, primaryFile.url) $parent.downloadFile(primaryFile.hashes.sha1, primaryFile.url)
" "
> >
<DownloadIcon /> <DownloadIcon />
@@ -110,7 +99,9 @@
<div class="info"> <div class="info">
<h4>Available For</h4> <h4>Available For</h4>
<p class="value"> <p class="value">
{{ version.game_versions.join(', ') }} {{
version.game_versions ? version.game_versions.join(', ') : ''
}}
</p> </p>
</div> </div>
</div> </div>
@@ -142,12 +133,11 @@
</div> </div>
<FileInput v-if="currentMember" class="file-input" @change="addFiles" /> <FileInput v-if="currentMember" class="file-input" @change="addFiles" />
</div> </div>
</ModPage> </div>
</template> </template>
<script> <script>
import axios from 'axios' import axios from 'axios'
import ModPage from '~/components/layout/ModPage'
import ConfirmPopup from '~/components/ui/ConfirmPopup' import ConfirmPopup from '~/components/ui/ConfirmPopup'
import Categories from '~/components/ui/search/Categories' import Categories from '~/components/ui/search/Categories'
@@ -163,7 +153,6 @@ export default {
components: { components: {
FileInput, FileInput,
Categories, Categories,
ModPage,
DownloadIcon, DownloadIcon,
CalendarIcon, CalendarIcon,
TagIcon, TagIcon,
@@ -173,98 +162,66 @@ export default {
ConfirmPopup, ConfirmPopup,
}, },
auth: false, auth: false,
async asyncData(data) { props: {
try { mod: {
const mod = ( type: Object,
await axios.get( default() {
`https://api.modrinth.com/api/v1/mod/${data.params.id}`, return {}
data.$auth.headers },
) },
).data versions: {
type: Array,
default() {
return []
},
},
members: {
type: Array,
default() {
return [{}]
},
},
currentMember: {
type: Object,
default() {
return null
},
},
},
async fetch() {
this.version = this.versions.find(
(x) => x.id === this.$route.params.version
)
const [members, versions, featuredVersions, userFollows] = ( this.primaryFile = this.version.files.find((file) => file.primary)
await Promise.all([
axios.get(`https://api.modrinth.com/api/v1/team/${mod.team}/members`),
axios.get(`https://api.modrinth.com/api/v1/mod/${mod.id}/version`),
axios.get(
`https://api.modrinth.com/api/v1/mod/${mod.id}/version?featured=true`
),
axios.get(
data.$auth.user
? `https://api.modrinth.com/api/v1/user/${data.$auth.user.id}/follows`
: `https://api.modrinth.com`,
data.$auth.headers
),
])
).map((it) => it.data)
const users = ( if (!this.primaryFile) {
await axios.get( this.primaryFile = this.version.files[0]
`https://api.modrinth.com/api/v1/users?ids=${JSON.stringify(
members.map((it) => it.user_id)
)}`,
data.$auth.headers
)
).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.user
? 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,
featuredVersions,
members,
version,
primaryFile,
currentMember,
userFollows: userFollows.name ? null : userFollows,
}
} catch {
data.error({
statusCode: 404,
message: 'Version not found',
})
} }
if (!this.version.changelog && this.version.changelog_url) {
this.version.changelog = (
await axios.get(this.version.changelog_url)
).data
}
console.log(this.version)
}, },
data() { data() {
return { return {
primaryFile: {},
version: {},
filesToUpload: [], filesToUpload: [],
popup_data: null, popup_data: null,
} }
}, },
mounted() {
this.$emit('update:link-bar', [
['Versions', 'versions'],
[this.version.name, 'versions/' + this.version.id],
])
},
methods: { methods: {
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()
},
deleteFilePopup(hash) { deleteFilePopup(hash) {
this.popup_data = hash this.popup_data = hash
this.$refs.delete_file_popup.show() this.$refs.delete_file_popup.show()
@@ -354,49 +311,6 @@ export default {
this.$nuxt.$loading.finish() this.$nuxt.$loading.finish()
}, },
}, },
head() {
return {
title: this.mod.title + ' - Modrinth - Files',
meta: [
{
hid: 'description',
name: 'description',
content:
this.mod.description +
' View other minecraft mods on Modrinth today! Modrinth is a new and modern Minecraft modding platform that is compatible with CurseForge too!',
},
{
hid: 'apple-mobile-web-app-title',
name: 'apple-mobile-web-app-title',
content: this.mod.title,
},
{
hid: 'og:title',
name: 'og:title',
content: this.mod.title,
},
{
hid: 'og:url',
name: 'og:url',
content: `https://modrinth.com/mod/${this.mod.id}`,
},
{
hid: 'og:description',
name: 'og:description',
content: this.mod.description,
},
{ hid: 'og:type', name: 'og:type', content: 'website' },
{
hid: 'og:image',
name: 'og:image',
content: this.mod.icon_url
? this.mod.icon_url
: 'https://cdn.modrinth.com/placeholder.png',
},
],
}
},
} }
</script> </script>

View File

@@ -1,13 +1,5 @@
<template> <template>
<ModPage <div>
:mod="mod"
:versions="versions"
:featured-versions="featuredVersions"
:members="members"
:current-member="currentMember"
:link-bar="[['Versions', 'versions']]"
:user-follows="userFollows"
>
<table> <table>
<thead> <thead>
<tr> <tr>
@@ -25,12 +17,12 @@
<tr v-for="version in versions" :key="version.id"> <tr v-for="version in versions" :key="version.id">
<td> <td>
<a <a
:href="findPrimary(version).url" :href="$parent.findPrimary(version).url"
class="download" class="download"
@click.prevent=" @click.prevent="
downloadFile( $parent.downloadFile(
findPrimary(version).hashes.sha1, $parent.findPrimary(version).hashes.sha1,
findPrimary(version).url $parent.findPrimary(version).url
) )
" "
> >
@@ -87,150 +79,42 @@
New Version New Version
</nuxt-link> </nuxt-link>
</div> </div>
</ModPage> </div>
</template> </template>
<script> <script>
import axios from 'axios'
import ModPage from '~/components/layout/ModPage'
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: {
ModPage,
ForgeIcon, ForgeIcon,
FabricIcon, FabricIcon,
DownloadIcon, DownloadIcon,
}, },
auth: false, auth: false,
async asyncData(data) { props: {
try { mod: {
const mod = ( type: Object,
await axios.get( default() {
`https://api.modrinth.com/api/v1/mod/${data.params.id}`, return {}
data.$auth.headers },
)
).data
const [members, versions, featuredVersions, userFollows] = (
await Promise.all([
axios.get(`https://api.modrinth.com/api/v1/team/${mod.team}/members`),
axios.get(`https://api.modrinth.com/api/v1/mod/${mod.id}/version`),
axios.get(
`https://api.modrinth.com/api/v1/mod/${mod.id}/version?featured=true`
),
axios.get(
data.$auth.user
? `https://api.modrinth.com/api/v1/user/${data.$auth.user.id}/follows`
: `https://api.modrinth.com`,
data.$auth.headers
),
])
).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)
)}`,
data.$auth.headers
)
).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.user
? members.find((x) => x.user_id === data.$auth.user.id)
: null
return {
mod,
versions,
featuredVersions,
members,
currentMember,
userFollows: userFollows.name ? null : userFollows,
}
} catch {
data.error({
statusCode: 404,
message: 'Mod not found',
})
}
},
methods: {
findPrimary(version) {
let file = version.files.find((x) => x.primary)
if (!file) {
file = version.files[0]
}
if (!file) {
file = { url: `/mod/${this.mod.id}/version/${version.id}` }
}
return file
}, },
async downloadFile(hash, url) { versions: {
await axios.get( type: Array,
`https://api.modrinth.com/api/v1/version_file/${hash}/download` default() {
) return []
},
const elem = document.createElement('a') },
elem.download = hash currentMember: {
elem.href = url type: Object,
elem.click() default() {
return null
},
}, },
}, },
head() { created() {
return { this.$emit('update:link-bar', [['Versions', 'versions']])
title: this.mod.title + ' - Modrinth',
meta: [
{
hid: 'description',
name: 'description',
content:
this.mod.description +
' View other minecraft mods on Modrinth today! Modrinth is a new and modern Minecraft modding platform that is compatible with CurseForge too!',
},
{
hid: 'apple-mobile-web-app-title',
name: 'apple-mobile-web-app-title',
content: this.mod.title,
},
{
hid: 'og:title',
name: 'og:title',
content: this.mod.title,
},
{
hid: 'og:url',
name: 'og:url',
content: `https://modrinth.com/mod/${this.mod.id}`,
},
{
hid: 'og:description',
name: 'og:description',
content: this.mod.description,
},
{ hid: 'og:type', name: 'og:type', content: 'website' },
{
hid: 'og:image',
name: 'og:image',
content: this.mod.icon_url
? this.mod.icon_url
: 'https://cdn.modrinth.com/placeholder.png',
},
],
}
}, },
} }
</script> </script>