You've already forked AstralRinth
forked from didirus/AstralRinth
[WIP] Rework design (#34)
* WIP: Redesign the default layout * Merge old & new default layouts * Fix login logic; add proper user controls dropdown * Fix latest version listing (#31) (#32) Co-authored-by: Aeledfyr <45501007+Aeledfyr@users.noreply.github.com> * First pass of design cleanup * Improve ad integration and fix light theme * Begin splitting up variables, change some styling to new mockup * Continue redesign progress * Work on some more pages * Add missing dark theme variables for text * Continue working on modularizing * Continue progress, redo pagination * Fix auth buttons in navbar layout * Continue progress * Continue progress more * Redo ModResult * Scope ModPage :irritater: * Continue Dashboard * Continue progress on Dashboard and cleanup * Add missing variables for dark theme * Small tweaks, cleanup, and continue mod page progress * Fix user not being able to see hidden mods that they own * Start reworking mod creation * Continue revamp of mod creation page * Yank v-html out * Hotfix markdown rendering and some spacing issues * Move legal; continue with mod creation; create reusable footer * Create README.md * Update README.md * Update README.md * Add in basic usage instructions * Fix some stuff * Continue with mod creation; fix some CSS errors * Start user page * Start transition to vue-select; fix a few bugs * Continue mod creation page * Finish mod pages * Add very raw version editing * Mod editing + creation * Fixed versions that were in processing causing a 404 (#39) Co-authored-by: Mikhail Oleynikov <falseresync@gmail.com> Co-authored-by: Aeledfyr <45501007+Aeledfyr@users.noreply.github.com> Co-authored-by: Jai A <jai.a@tuta.io> Co-authored-by: MulverineX <mulverin3@gmail.com> Co-authored-by: diabolical17 <calumproh28@gmail.com> Co-authored-by: Geometrically <18202329+Geometrically@users.noreply.github.com>
This commit is contained in:
@@ -1,28 +1,35 @@
|
||||
<template>
|
||||
<div class="main">
|
||||
<h1>About</h1>
|
||||
<p>
|
||||
Founded in 2020, Modrinth was created to provide modders with an open and
|
||||
intuitive platform to publish their mods on.
|
||||
</p>
|
||||
<div class="container">
|
||||
<h1>About</h1>
|
||||
<p>
|
||||
Founded in 2020, Modrinth was created to provide modders with an open
|
||||
and intuitive platform to publish their mods on.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Our primary goal is to be as open as possible, with all our code being
|
||||
Open Source, while giving back to the modding community as much as
|
||||
possible.
|
||||
</p>
|
||||
<p>
|
||||
Our primary goal is to be as open as possible, with all our code being
|
||||
Open Source, while giving back to the modding community as much as
|
||||
possible.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
While we still are in early alpha, we hope we can soon be a major modding
|
||||
platform for all modders :)
|
||||
</p>
|
||||
<p>
|
||||
While we still are in early alpha, we hope we can soon be a major
|
||||
modding platform for all modders :)
|
||||
</p>
|
||||
</div>
|
||||
<m-footer class="footer" centered />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import MFooter from '@/components/MFooter'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
MFooter,
|
||||
},
|
||||
auth: false,
|
||||
layout: 'home',
|
||||
head: {
|
||||
title: 'About - Modrinth',
|
||||
meta: [
|
||||
@@ -54,7 +61,12 @@ export default {
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.main {
|
||||
margin: 0 auto;
|
||||
margin: var(--spacing-card-sm) auto;
|
||||
max-width: 800px;
|
||||
}
|
||||
|
||||
.container {
|
||||
@extend %card;
|
||||
padding: var(--spacing-card-sm) var(--spacing-card-lg);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,80 +1,80 @@
|
||||
<template>
|
||||
<div class="content">
|
||||
<h2>My projects</h2>
|
||||
<div class="section-header">
|
||||
<h3>Mods</h3>
|
||||
<nuxt-link class="create-button" to="/mod/create"
|
||||
>Create a new mod</nuxt-link
|
||||
>
|
||||
<div class="page-container">
|
||||
<div class="page-contents">
|
||||
<div class="sidebar-l">
|
||||
<div class="card page-nav">
|
||||
<nuxt-link :to="'/dashboard/projects'" class="tab last">
|
||||
<ModIcon />
|
||||
My mods
|
||||
</nuxt-link>
|
||||
</div>
|
||||
<client-only>
|
||||
<EthicalAd type="image" />
|
||||
</client-only>
|
||||
</div>
|
||||
<div class="content">
|
||||
<div class="section-header columns">
|
||||
<h3 class="column-grow-1">My mods</h3>
|
||||
<nuxt-link class="brand-button column" to="/mod/create">
|
||||
Create a mod
|
||||
</nuxt-link>
|
||||
</div>
|
||||
<ModCard
|
||||
v-for="mod in mods"
|
||||
:id="mod.id"
|
||||
:key="mod.id"
|
||||
:author="mod.author"
|
||||
:name="mod.title"
|
||||
:description="mod.description"
|
||||
:latest-version="mod.latest_version"
|
||||
:created-at="mod.published"
|
||||
:updated-at="mod.updated"
|
||||
:downloads="mod.downloads.toString()"
|
||||
:icon-url="mod.icon_url"
|
||||
:author-url="mod.author_url"
|
||||
:page-url="mod.page_url"
|
||||
:categories="mod.categories"
|
||||
:edit-mode="true"
|
||||
:status="mod.status"
|
||||
:is-modrinth="true"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th>Name</th>
|
||||
<th>Role</th>
|
||||
<th>Status</th>
|
||||
<th>Downloads</th>
|
||||
<th>Last updated</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="mod in mods" :key="mod.id">
|
||||
<td>
|
||||
<img class="rounded-md" :src="mod.icon_url" />
|
||||
</td>
|
||||
<td>
|
||||
<nuxt-link :to="'/mod/' + mod.id">
|
||||
{{ mod.title }}
|
||||
</nuxt-link>
|
||||
</td>
|
||||
<td>Owner</td>
|
||||
<td>
|
||||
<span v-if="mod.status === 'approved'" class="badge green">
|
||||
Approved
|
||||
</span>
|
||||
<span v-if="mod.status === 'rejected'" class="badge red">
|
||||
Rejected
|
||||
</span>
|
||||
<span v-if="mod.status === 'draft'" class="badge yellow">
|
||||
Draft
|
||||
</span>
|
||||
<span v-if="mod.status === 'processing'" class="badge yellow">
|
||||
Processing
|
||||
</span>
|
||||
<span v-if="mod.status === 'unlisted'" class="badge gray">
|
||||
Unlisted
|
||||
</span>
|
||||
<span v-if="mod.status === 'unknown'" class="badge gray">
|
||||
Unknown
|
||||
</span>
|
||||
</td>
|
||||
<td>{{ mod.downloads }}</td>
|
||||
<td>{{ $dayjs(mod.published).format('YYYY-MM-DD') }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<client-only>
|
||||
<EthicalAd />
|
||||
</client-only>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import axios from 'axios'
|
||||
import EthicalAd from '@/components/EthicalAd'
|
||||
import ModCard from '@/components/ProjectCard'
|
||||
|
||||
import ModIcon from '~/assets/images/sidebar/mod.svg?inline'
|
||||
|
||||
export default {
|
||||
components: { EthicalAd },
|
||||
components: {
|
||||
EthicalAd,
|
||||
ModCard,
|
||||
ModIcon,
|
||||
},
|
||||
async fetch() {
|
||||
const config = {
|
||||
headers: {
|
||||
Authorization: this.$auth.getToken('local'),
|
||||
},
|
||||
}
|
||||
|
||||
try {
|
||||
let res = await axios.get(
|
||||
`https://api.modrinth.com/api/v1/user/${this.$auth.user.id}/mods`
|
||||
`https://api.modrinth.com/api/v1/user/${this.$auth.user.id}/mods`,
|
||||
config
|
||||
)
|
||||
|
||||
if (res.data) {
|
||||
res = await axios.get(
|
||||
`https://api.modrinth.com/api/v1/mods?ids=${JSON.stringify(res.data)}`
|
||||
`https://api.modrinth.com/api/v1/mods?ids=${JSON.stringify(
|
||||
res.data
|
||||
)}`,
|
||||
config
|
||||
)
|
||||
this.mods = res.data
|
||||
}
|
||||
@@ -90,29 +90,23 @@ export default {
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.section-header {
|
||||
display: flex;
|
||||
margin-bottom: 1rem;
|
||||
|
||||
& > * {
|
||||
margin-right: 1rem;
|
||||
@extend %card;
|
||||
padding: var(--spacing-card-md) var(--spacing-card-lg);
|
||||
margin-bottom: var(--spacing-card-md);
|
||||
h3 {
|
||||
margin: auto 0;
|
||||
color: var(--color-text-dark);
|
||||
font-weight: var(--font-weight-extrabold);
|
||||
}
|
||||
}
|
||||
|
||||
.create-button {
|
||||
margin: auto 0;
|
||||
padding: 4px 20px;
|
||||
border-radius: 5px;
|
||||
color: var(--color-grey-5);
|
||||
background-color: var(--color-grey-1);
|
||||
}
|
||||
|
||||
table {
|
||||
background: var(--color-bg);
|
||||
border-collapse: collapse;
|
||||
border-radius: 0.5rem;
|
||||
box-shadow: 0 2px 3px 1px var(--color-grey-2);
|
||||
table-layout: fixed;
|
||||
width: 100%;
|
||||
margin-bottom: var(--spacing-card-md);
|
||||
background: var(--color-raised-bg);
|
||||
border-radius: var(--size-rounded-card);
|
||||
|
||||
* {
|
||||
text-align: left;
|
||||
@@ -122,7 +116,7 @@ table {
|
||||
tr:first-child {
|
||||
th,
|
||||
td {
|
||||
border-bottom: 1px solid var(--color-grey-2);
|
||||
border-bottom: 1px solid var(--color-divider);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,7 +124,7 @@ table {
|
||||
td {
|
||||
&:first-child {
|
||||
text-align: center;
|
||||
width: 5%;
|
||||
width: 7%;
|
||||
}
|
||||
|
||||
&:nth-child(2) {
|
||||
@@ -140,7 +134,7 @@ table {
|
||||
}
|
||||
|
||||
th {
|
||||
color: #718096;
|
||||
color: var(--color-heading);
|
||||
font-size: 0.8rem;
|
||||
letter-spacing: 0.02rem;
|
||||
margin-bottom: 0.5rem;
|
||||
@@ -150,7 +144,7 @@ table {
|
||||
}
|
||||
|
||||
td {
|
||||
padding: 0.25rem 1rem;
|
||||
padding: 0.75rem;
|
||||
|
||||
img {
|
||||
height: 3rem;
|
||||
@@ -185,4 +179,8 @@ table {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.mod-name {
|
||||
font-weight: bold;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
<template>
|
||||
<div class="content">
|
||||
<h2>Documentation</h2>
|
||||
<p>Coming Soon!</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
auth: false,
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.main {
|
||||
margin: 0 auto;
|
||||
max-width: 800px;
|
||||
}
|
||||
</style>
|
||||
@@ -2,7 +2,9 @@
|
||||
<div>
|
||||
<div class="main-hero columns">
|
||||
<div class="left">
|
||||
<h1 class="typewriter">{{ currentText }}</h1>
|
||||
<h1 class="typewriter">
|
||||
{{ currentText }}<span aria-hidden="true"></span>
|
||||
</h1>
|
||||
<h1>modding platform</h1>
|
||||
</div>
|
||||
<div class="right columns">
|
||||
@@ -159,13 +161,18 @@ fetch('https://api.modrinth.com/api/v1/mod').then(res => res.json()).then(data =
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
<m-footer class="footer" centered />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import MFooter from '@/components/MFooter'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
MFooter,
|
||||
},
|
||||
auth: false,
|
||||
layout: 'home',
|
||||
data() {
|
||||
return {
|
||||
currentText: 'Open source',
|
||||
@@ -208,6 +215,7 @@ export default {
|
||||
}
|
||||
|
||||
.main-hero {
|
||||
margin-top: 100px;
|
||||
height: 600px;
|
||||
|
||||
.left {
|
||||
@@ -217,12 +225,15 @@ export default {
|
||||
.typewriter {
|
||||
display: inline-block;
|
||||
color: var(--color-brand);
|
||||
border-right: 0.15em solid var(--color-brand);
|
||||
animation: caret 1s steps(1) infinite;
|
||||
|
||||
@keyframes caret {
|
||||
50% {
|
||||
border-color: transparent;
|
||||
span {
|
||||
border-right: 0.15em solid var(--color-brand);
|
||||
animation: caret 1s steps(1) infinite;
|
||||
|
||||
@keyframes caret {
|
||||
50% {
|
||||
border-color: transparent;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -256,7 +267,7 @@ export default {
|
||||
p {
|
||||
line-height: 25px;
|
||||
letter-spacing: 0.2px;
|
||||
color: var(--color-grey-7);
|
||||
color: var(--color-text);
|
||||
|
||||
span {
|
||||
color: var(--color-brand);
|
||||
@@ -276,7 +287,7 @@ export default {
|
||||
}
|
||||
|
||||
.slanted-hero {
|
||||
background: var(--color-grey-1);
|
||||
background: var(--color-raised-bg);
|
||||
height: 500px;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
@@ -336,6 +347,10 @@ export default {
|
||||
}
|
||||
}
|
||||
|
||||
.footer {
|
||||
margin-top: 150px;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 500px) {
|
||||
.hero {
|
||||
margin-top: 0 !important;
|
||||
|
||||
191
pages/legal/privacy.vue
Normal file
191
pages/legal/privacy.vue
Normal file
@@ -0,0 +1,191 @@
|
||||
<template>
|
||||
<div class="main">
|
||||
<div class="container">
|
||||
<h1>Privacy Policy</h1>
|
||||
|
||||
<p>
|
||||
At Modrinth, accessible from https://modrinth.com, one of our main
|
||||
priorities is the privacy of our visitors. This Privacy Policy document
|
||||
contains types of information that is collected and recorded by Modrinth
|
||||
and how we use it.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
If you have additional questions or require more information about our
|
||||
Privacy Policy, do not hesitate to contact us.
|
||||
</p>
|
||||
|
||||
<h2>Log Files</h2>
|
||||
|
||||
<p>
|
||||
Modrinth follows a standard procedure of using log files. These files
|
||||
log visitors when they visit websites. All hosting companies do this and
|
||||
a part of hosting services' analytics. The information collected by log
|
||||
files include internet protocol (IP) addresses, browser type, Internet
|
||||
Service Provider (ISP), date and time stamp, referring/exit pages, and
|
||||
possibly the number of clicks. These are not linked to any information
|
||||
that is personally identifiable. The purpose of the information is for
|
||||
analyzing trends, administering the site, tracking users' movement on
|
||||
the website, and gathering demographic information.
|
||||
</p>
|
||||
|
||||
<h2>Cookies and Web Beacons</h2>
|
||||
|
||||
<p>
|
||||
Like any other website, Modrinth uses 'cookies'. These cookies are used
|
||||
to store information including visitors' preferences, and the pages on
|
||||
the website that the visitor accessed or visited. The information is
|
||||
used to optimize the users' experience by customizing our web page
|
||||
content based on visitors' browser type and/or other information.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
For more general information on cookies, please read
|
||||
<a href="https://www.privacypolicies.com/blog/cookies/"
|
||||
>"What Are Cookies"</a
|
||||
>.
|
||||
</p>
|
||||
|
||||
<h2>Our Advertising Partners</h2>
|
||||
|
||||
<p>
|
||||
Some of advertisers on our site may use cookies and web beacons. Our
|
||||
advertising partners are listed below. Each of our advertising partners
|
||||
has their own Privacy Policy for their policies on user data. For easier
|
||||
access, we hyperlinked to their Privacy Policies below.
|
||||
</p>
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
<p>EthicalAds</p>
|
||||
<p>
|
||||
<a href="https://www.ethicalads.io/privacy-policy/"
|
||||
>https://www.ethicalads.io/privacy-policy/</a
|
||||
>
|
||||
</p>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h2>Privacy Policies</h2>
|
||||
|
||||
<P
|
||||
>You may consult this list to find the Privacy Policy for each of the
|
||||
advertising partners of Modrinth.</P
|
||||
>
|
||||
|
||||
<p>
|
||||
Third-party ad servers or ad networks uses technologies like cookies,
|
||||
JavaScript, or Web Beacons that are used in their respective
|
||||
advertisements and links that appear on Modrinth, which are sent
|
||||
directly to users' browser. They automatically receive your IP address
|
||||
when this occurs. These technologies are used to measure the
|
||||
effectiveness of their advertising campaigns and/or to personalize the
|
||||
advertising content that you see on websites that you visit.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Note that Modrinth has no access to or control over these cookies that
|
||||
are used by third-party advertisers.
|
||||
</p>
|
||||
|
||||
<h2>Third Party Privacy Policies</h2>
|
||||
|
||||
<p>
|
||||
Modrinth's Privacy Policy does not apply to other advertisers or
|
||||
websites. Thus, we are advising you to consult the respective Privacy
|
||||
Policies of these third-party ad servers for more detailed information.
|
||||
It may include their practices and instructions about how to opt-out of
|
||||
certain options.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
You can choose to disable cookies through your individual browser
|
||||
options. To know more detailed information about cookie management with
|
||||
specific web browsers, it can be found at the browsers' respective
|
||||
websites. What Are Cookies?
|
||||
</p>
|
||||
|
||||
<h2>Children's Information</h2>
|
||||
|
||||
<p>
|
||||
Another part of our priority is adding protection for children while
|
||||
using the internet. We encourage parents and guardians to observe,
|
||||
participate in, and/or monitor and guide their online activity.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Modrinth does not knowingly collect any Personal Identifiable
|
||||
Information from children under the age of 13. If you think that your
|
||||
child provided this kind of information on our website, we strongly
|
||||
encourage you to contact us immediately and we will do our best efforts
|
||||
to promptly remove such information from our records.
|
||||
</p>
|
||||
|
||||
<h2>Online Privacy Policy Only</h2>
|
||||
|
||||
<p>
|
||||
This Privacy Policy applies only to our online activities and is valid
|
||||
for visitors to our website with regards to the information that they
|
||||
shared and/or collect in Modrinth. This policy is not applicable to any
|
||||
information collected offline or via channels other than this website.
|
||||
</p>
|
||||
|
||||
<h2>Consent</h2>
|
||||
|
||||
<p>
|
||||
By using our website, you hereby consent to our Privacy Policy and agree
|
||||
to its Terms and Conditions.
|
||||
</p>
|
||||
</div>
|
||||
<m-footer class="footer" centered />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import MFooter from '@/components/MFooter'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
MFooter,
|
||||
},
|
||||
auth: false,
|
||||
head: {
|
||||
title: 'Privacy - Modrinth',
|
||||
meta: [
|
||||
{
|
||||
hid: 'description',
|
||||
name: 'description',
|
||||
content:
|
||||
'The privacy policy of Modrinth, an open source modding platform. Modrinth currently supports Minecraft, including the forge and fabric mod loaders.',
|
||||
},
|
||||
{
|
||||
hid: 'apple-mobile-web-app-title',
|
||||
name: 'apple-mobile-web-app-title',
|
||||
content: 'Privacy Policy',
|
||||
},
|
||||
{
|
||||
hid: 'og:title',
|
||||
name: 'og:title',
|
||||
content: 'Privacy Policy',
|
||||
},
|
||||
{
|
||||
hid: 'og:url',
|
||||
name: 'og:url',
|
||||
content: `https://modrinth.com/privacy`,
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.main {
|
||||
margin: var(--spacing-card-sm) auto;
|
||||
max-width: 800px;
|
||||
}
|
||||
|
||||
.container {
|
||||
@extend %card;
|
||||
padding: var(--spacing-card-sm) var(--spacing-card-lg);
|
||||
}
|
||||
</style>
|
||||
203
pages/legal/terms.vue
Normal file
203
pages/legal/terms.vue
Normal file
@@ -0,0 +1,203 @@
|
||||
<template>
|
||||
<div class="main">
|
||||
<div class="container">
|
||||
<h1>Terms and Conditions</h1>
|
||||
|
||||
<h2>1. Terms</h2>
|
||||
|
||||
<p>
|
||||
By accessing this Website, accessible from https://modrinth.com, you are
|
||||
agreeing to be bound by these Website Terms and Conditions of Use and
|
||||
agree that you are responsible for the agreement with any applicable
|
||||
local laws. If you disagree with any of these terms, you are prohibited
|
||||
from accessing this site. The materials contained in this Website are
|
||||
protected by copyright and trade mark law.
|
||||
</p>
|
||||
|
||||
<h2>2. Use License</h2>
|
||||
|
||||
<p>
|
||||
Permission is granted to temporarily download one copy of the materials
|
||||
on Guavy LLC's Website for personal, non-commercial transitory viewing
|
||||
only. This is the grant of a license, not a transfer of title, and under
|
||||
this license you may not:
|
||||
</p>
|
||||
|
||||
<ul>
|
||||
<li>modify or copy the materials;</li>
|
||||
<li>
|
||||
use the materials for any commercial purpose or for any public
|
||||
display;
|
||||
</li>
|
||||
<li>
|
||||
attempt to reverse engineer any software contained on Guavy LLC's
|
||||
Website;
|
||||
</li>
|
||||
<li>
|
||||
remove any copyright or other proprietary notations from the
|
||||
materials; or
|
||||
</li>
|
||||
<li>
|
||||
transferring the materials to another person or "mirror" the materials
|
||||
on any other server.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<p>
|
||||
This will let Guavy LLC to terminate upon violations of any of these
|
||||
restrictions. Upon termination, your viewing right will also be
|
||||
terminated and you should destroy any downloaded materials in your
|
||||
possession whether it is printed or electronic format.
|
||||
</p>
|
||||
|
||||
<h2>3. Disclaimer</h2>
|
||||
|
||||
<p>
|
||||
All the materials on Guavy LLC’s Website are provided "as is". Guavy LLC
|
||||
makes no warranties, may it be expressed or implied, therefore negates
|
||||
all other warranties. Furthermore, Guavy LLC does not make any
|
||||
representations concerning the accuracy or reliability of the use of the
|
||||
materials on its Website or otherwise relating to such materials or any
|
||||
sites linked to this Website.
|
||||
</p>
|
||||
|
||||
<h2>4. Limitations</h2>
|
||||
|
||||
<p>
|
||||
Guavy LLC or its suppliers will not be hold accountable for any damages
|
||||
that will arise with the use or inability to use the materials on Guavy
|
||||
LLC’s Website, even if Guavy LLC or an authorize representative of this
|
||||
Website has been notified, orally or written, of the possibility of such
|
||||
damage. Some jurisdiction does not allow limitations on implied
|
||||
warranties or limitations of liability for incidental damages, these
|
||||
limitations may not apply to you.
|
||||
</p>
|
||||
|
||||
<h2>5. Revisions and Errata</h2>
|
||||
|
||||
<p>
|
||||
The materials appearing on Guavy LLC’s Website may include technical,
|
||||
typographical, or photographic errors. Guavy LLC will not promise that
|
||||
any of the materials in this Website are accurate, complete, or current.
|
||||
Guavy LLC may change the materials contained on its Website at any time
|
||||
without notice. Guavy LLC does not make any commitment to update the
|
||||
materials.
|
||||
</p>
|
||||
|
||||
<h2>6. Links</h2>
|
||||
|
||||
<p>
|
||||
Guavy LLC has not reviewed all of the sites linked to its Website and is
|
||||
not responsible for the contents of any such linked site. The presence
|
||||
of any link does not imply endorsement by Guavy LLC of the site. The use
|
||||
of any linked website is at the user’s own risk.
|
||||
</p>
|
||||
|
||||
<h2>7. Site Terms of Use Modifications</h2>
|
||||
|
||||
<p>
|
||||
Guavy LLC may revise these Terms of Use for its Website at any time
|
||||
without prior notice. By using this Website, you are agreeing to be
|
||||
bound by the current version of these Terms and Conditions of Use.
|
||||
</p>
|
||||
|
||||
<h2>8. Your Privacy</h2>
|
||||
|
||||
<p>
|
||||
Please read our <nuxt-link to="/privacy"> Privacy Policy</nuxt-link>.
|
||||
</p>
|
||||
|
||||
<h2>9. Governing Law</h2>
|
||||
|
||||
<p>
|
||||
Any claim related to Guavy LLC's Website shall be governed by the laws
|
||||
of us without regards to its conflict of law provisions.
|
||||
</p>
|
||||
|
||||
<h2>10. Content</h2>
|
||||
|
||||
<p>
|
||||
When you upload text, software, mods, scripts, graphics, photos, audio,
|
||||
videos, links, interactive features and other materials that may be
|
||||
viewed on, or accessed through Modrinth, we refer to it as “Content”.
|
||||
</p>
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
You must own or have the necessary licenses, rights, consents, and
|
||||
permissions to store, share or distribute the Content that is uploaded
|
||||
under your Modrinth account.
|
||||
</li>
|
||||
<li>
|
||||
You are responsible for all activity and Content that is uploaded
|
||||
under your Modrinth account.
|
||||
</li>
|
||||
<li>
|
||||
You must not transmit any viruses, worms, malware, or any other code
|
||||
of a destructive nature through Modrinth.
|
||||
</li>
|
||||
<li>
|
||||
You retain all of your ownership rights to your Content. We do not
|
||||
claim any ownership in or to any of your Content.
|
||||
</li>
|
||||
<li>
|
||||
To enable us to provide the services of Modrinth, you hereby grant us
|
||||
a worldwide, non-exclusive, royalty-free, and unrestricted license to
|
||||
use, reproduce, distribute copies, prepare derivative works of, or
|
||||
display Content in connection with Modrinth in any medium and for any
|
||||
purpose (including commercial purposes), which is irrevocable.
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<m-footer class="footer" centered />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import MFooter from '@/components/MFooter'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
MFooter,
|
||||
},
|
||||
auth: false,
|
||||
head: {
|
||||
title: 'Terms - Modrinth',
|
||||
meta: [
|
||||
{
|
||||
hid: 'description',
|
||||
name: 'description',
|
||||
content:
|
||||
'The Terms of Service of Modrinth, an open source modding platform. Modrinth currently supports Minecraft, including the forge and fabric mod loaders.',
|
||||
},
|
||||
{
|
||||
hid: 'apple-mobile-web-app-title',
|
||||
name: 'apple-mobile-web-app-title',
|
||||
content: 'Terms of Service',
|
||||
},
|
||||
{
|
||||
hid: 'og:title',
|
||||
name: 'og:title',
|
||||
content: 'Terms of Service',
|
||||
},
|
||||
{
|
||||
hid: 'og:url',
|
||||
name: 'og:url',
|
||||
content: `https://modrinth.com/tos`,
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.main {
|
||||
margin: var(--spacing-card-sm) auto;
|
||||
max-width: 800px;
|
||||
}
|
||||
|
||||
.container {
|
||||
@extend %card;
|
||||
padding: var(--spacing-card-sm) var(--spacing-card-lg);
|
||||
}
|
||||
</style>
|
||||
571
pages/mod/_id/edit.vue
Normal file
571
pages/mod/_id/edit.vue
Normal file
@@ -0,0 +1,571 @@
|
||||
<template>
|
||||
<div class="page-container">
|
||||
<div class="page-contents">
|
||||
<header class="columns">
|
||||
<h2 class="column-grow-1">Edit Mod</h2>
|
||||
<button
|
||||
title="Save"
|
||||
class="brand-button column"
|
||||
:disabled="!this.$nuxt.$loading"
|
||||
@click="saveMod"
|
||||
>
|
||||
Save
|
||||
</button>
|
||||
</header>
|
||||
<EthicalAd class="advert" />
|
||||
<section class="essentials">
|
||||
<h3>Name</h3>
|
||||
<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"
|
||||
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 and GIF</li>
|
||||
</ul>
|
||||
<button
|
||||
class="transparent-button"
|
||||
@click="
|
||||
icon = null
|
||||
previewImage = null
|
||||
"
|
||||
>
|
||||
Reset icon
|
||||
</button>
|
||||
</div>
|
||||
<img
|
||||
:src="
|
||||
mod.icon_url
|
||||
? 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"
|
||||
track-by="id"
|
||||
label="label"
|
||||
: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"
|
||||
track-by="id"
|
||||
label="label"
|
||||
: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="body"></textarea>
|
||||
</div>
|
||||
<div v-compiled-markdown="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.wiki_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="mod.license"
|
||||
placeholder="Select one"
|
||||
track-by="short"
|
||||
label="name"
|
||||
:options="availableLicenses"
|
||||
:searchable="true"
|
||||
:close-on-select="true"
|
||||
:show-labels="false"
|
||||
/>
|
||||
<input
|
||||
v-model="mod.license.url"
|
||||
type="url"
|
||||
placeholder="License URL"
|
||||
/>
|
||||
</div>
|
||||
</label>
|
||||
</section>
|
||||
<!--
|
||||
<section class="donations">
|
||||
<div class="title">
|
||||
<h3>Donation links</h3>
|
||||
<i>— this section is optional</i>
|
||||
</div>
|
||||
</section>
|
||||
-->
|
||||
<m-footer class="footer" centered />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import axios from 'axios'
|
||||
import Multiselect from 'vue-multiselect'
|
||||
|
||||
import MFooter from '@/components/MFooter'
|
||||
import FileInput from '@/components/FileInput'
|
||||
import EthicalAd from '@/components/EthicalAd'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
MFooter,
|
||||
FileInput,
|
||||
EthicalAd,
|
||||
Multiselect,
|
||||
},
|
||||
async asyncData(data) {
|
||||
const [
|
||||
mod,
|
||||
availableCategories,
|
||||
availableLoaders,
|
||||
availableGameVersions,
|
||||
availableLicenses,
|
||||
// availableDonationPlatforms,
|
||||
] = (
|
||||
await Promise.all([
|
||||
axios.get(`https://api.modrinth.com/api/v1/mod/${data.params.id}`),
|
||||
axios.get(`https://api.modrinth.com/api/v1/tag/category`),
|
||||
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/license`),
|
||||
// axios.get(`https://api.modrinth.com/api/v1/tag/donation_platform`),
|
||||
])
|
||||
).map((it) => it.data)
|
||||
|
||||
mod.license = {
|
||||
short: mod.license.id,
|
||||
name: mod.license.name,
|
||||
}
|
||||
|
||||
const res = await axios.get(mod.body_url)
|
||||
|
||||
return {
|
||||
mod,
|
||||
body: res.data,
|
||||
clientSideType: {
|
||||
label: mod.client_side,
|
||||
id: mod.client_side,
|
||||
},
|
||||
serverSideType: {
|
||||
label: mod.server_side,
|
||||
id: mod.server_side,
|
||||
},
|
||||
availableCategories,
|
||||
availableLoaders,
|
||||
availableGameVersions,
|
||||
availableLicenses,
|
||||
// availableDonationPlatforms,
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
previewImage: null,
|
||||
compiledBody: '',
|
||||
|
||||
icon: null,
|
||||
|
||||
sideTypes: [
|
||||
{ label: 'Required', id: 'required' },
|
||||
{ label: 'No functionality', id: 'no-functionality' },
|
||||
{ label: 'Unsupported', id: 'unsupported' },
|
||||
],
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
license(newValue, oldValue) {
|
||||
if (newValue == null) {
|
||||
this.license_url = ''
|
||||
return
|
||||
}
|
||||
|
||||
switch (newValue.short) {
|
||||
case 'custom':
|
||||
this.license_url = ''
|
||||
break
|
||||
default:
|
||||
this.license_url = `https://cdn.modrinth.com/licenses/${newValue.short}.txt`
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
async saveMod() {
|
||||
this.$nuxt.$loading.start()
|
||||
|
||||
try {
|
||||
await axios.patch(
|
||||
`https://api.modrinth.com/api/v1/mod/${this.mod.id}`,
|
||||
{
|
||||
title: this.mod.title,
|
||||
description: this.mod.description,
|
||||
body: this.body,
|
||||
categories: this.mod.categories,
|
||||
issues_url: this.mod.issues_url,
|
||||
source_url: this.mod.source_url,
|
||||
wiki_url: this.mod.wiki_url,
|
||||
license_url: this.mod.license_url,
|
||||
discord_url: this.mod.discord_url,
|
||||
license_id: this.mod.license.short,
|
||||
client_side: this.clientSideType.id,
|
||||
server_side: this.serverSideType.id,
|
||||
slug: this.mod.mod_slug,
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
Authorization: this.$auth.getToken('local'),
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
await this.$router.replace(`/mod/${this.mod.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()
|
||||
},
|
||||
|
||||
showPreviewImage(e) {
|
||||
const reader = new FileReader()
|
||||
this.icon = e.target.files[0]
|
||||
reader.readAsDataURL(this.icon)
|
||||
|
||||
reader.onload = (event) => {
|
||||
this.previewImage = event.target.result
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.title {
|
||||
* {
|
||||
display: inline;
|
||||
}
|
||||
}
|
||||
|
||||
label {
|
||||
display: flex;
|
||||
|
||||
span {
|
||||
flex: 2;
|
||||
padding-right: var(--spacing-card-lg);
|
||||
}
|
||||
|
||||
input,
|
||||
.multiselect,
|
||||
.input-group {
|
||||
flex: 3;
|
||||
height: fit-content;
|
||||
}
|
||||
}
|
||||
|
||||
.input-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
* {
|
||||
margin-bottom: var(--spacing-card-sm);
|
||||
}
|
||||
}
|
||||
|
||||
.textarea-wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
|
||||
textarea {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
resize: none;
|
||||
max-width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.page-contents {
|
||||
display: grid;
|
||||
grid-template:
|
||||
'header header header' auto
|
||||
'advert advert advert' auto
|
||||
'essentials essentials mod-icon' auto
|
||||
'game-sides game-sides game-sides' auto
|
||||
'description description description' auto
|
||||
'versions versions versions' auto
|
||||
'extra-links license license' auto
|
||||
'donations donations .' auto
|
||||
'footer footer footer' auto
|
||||
/ 4fr 1fr 4fr;
|
||||
column-gap: var(--spacing-card-md);
|
||||
row-gap: var(--spacing-card-md);
|
||||
}
|
||||
|
||||
header {
|
||||
@extend %card;
|
||||
|
||||
grid-area: header;
|
||||
padding: var(--spacing-card-md) var(--spacing-card-lg);
|
||||
|
||||
h2 {
|
||||
margin: auto 0;
|
||||
color: var(--color-text-dark);
|
||||
font-weight: var(--font-weight-extrabold);
|
||||
}
|
||||
|
||||
button {
|
||||
margin-left: 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.advert {
|
||||
grid-area: advert;
|
||||
}
|
||||
|
||||
section {
|
||||
@extend %card;
|
||||
|
||||
padding: var(--spacing-card-md) var(--spacing-card-lg);
|
||||
}
|
||||
|
||||
section.essentials {
|
||||
grid-area: essentials;
|
||||
}
|
||||
|
||||
section.mod-icon {
|
||||
grid-area: mod-icon;
|
||||
|
||||
img {
|
||||
align-self: flex-start;
|
||||
max-width: 50%;
|
||||
margin-left: var(--spacing-card-lg);
|
||||
}
|
||||
}
|
||||
|
||||
section.game-sides {
|
||||
grid-area: game-sides;
|
||||
|
||||
.columns {
|
||||
flex-wrap: wrap;
|
||||
|
||||
span {
|
||||
flex: 2;
|
||||
}
|
||||
|
||||
.labeled-control {
|
||||
flex: 2;
|
||||
margin-left: var(--spacing-card-lg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
section.description {
|
||||
grid-area: description;
|
||||
|
||||
& > .columns {
|
||||
align-items: stretch;
|
||||
min-height: 10rem;
|
||||
max-height: 40rem;
|
||||
|
||||
& > * {
|
||||
flex: 1;
|
||||
max-width: 50%;
|
||||
}
|
||||
}
|
||||
|
||||
.markdown-body {
|
||||
overflow-y: auto;
|
||||
padding: 0 var(--spacing-card-sm);
|
||||
}
|
||||
}
|
||||
|
||||
section.extra-links {
|
||||
grid-area: extra-links;
|
||||
|
||||
label {
|
||||
align-items: center;
|
||||
margin-top: var(--spacing-card-sm);
|
||||
|
||||
span {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
section.license {
|
||||
grid-area: license;
|
||||
|
||||
label {
|
||||
margin-top: var(--spacing-card-sm);
|
||||
}
|
||||
}
|
||||
|
||||
section.donations {
|
||||
grid-area: donations;
|
||||
}
|
||||
|
||||
.footer {
|
||||
grid-area: footer;
|
||||
}
|
||||
|
||||
.choose-image {
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
@@ -1,14 +1,11 @@
|
||||
<template>
|
||||
<ModPage :mod="mod" :versions="versions" :members="members">
|
||||
<div class="markdown-body" v-html="body"></div>
|
||||
<div v-compiled-markdown="body" class="markdown-body"></div>
|
||||
</ModPage>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import axios from 'axios'
|
||||
|
||||
import xss from 'xss'
|
||||
import marked from 'marked'
|
||||
import ModPage from '@/components/ModPage'
|
||||
|
||||
export default {
|
||||
@@ -31,17 +28,20 @@ export default {
|
||||
members[i].avatar_url = res.data.avatar_url
|
||||
}
|
||||
|
||||
res = await axios.get(mod.body_url)
|
||||
const body = xss(marked(res.data))
|
||||
const body = (await axios.get(mod.body_url)).data
|
||||
|
||||
const versions = []
|
||||
|
||||
for (const version of mod.versions) {
|
||||
res = await axios.get(
|
||||
`https://api.modrinth.com/api/v1/version/${version}`
|
||||
)
|
||||
|
||||
versions.push(res.data)
|
||||
try {
|
||||
res = await axios.get(
|
||||
`https://api.modrinth.com/api/v1/version/${version}`
|
||||
)
|
||||
versions.push(res.data)
|
||||
} catch {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('Some versions may be missing...')
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -102,9 +102,9 @@ export default {
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.markdown-body {
|
||||
padding: 20px;
|
||||
box-shadow: 0 2px 3px 1px var(--color-grey-2);
|
||||
background: var(--color-bg);
|
||||
border-radius: 0 0 var(--size-rounded-sm) var(--size-rounded-sm);
|
||||
padding: 1rem;
|
||||
margin-bottom: var(--spacing-card-md);
|
||||
background: var(--color-raised-bg);
|
||||
border-radius: var(--size-rounded-card);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -31,11 +31,15 @@ export default {
|
||||
const versions = []
|
||||
|
||||
for (const version of mod.versions) {
|
||||
res = await axios.get(
|
||||
`https://api.modrinth.com/api/v1/version/${version}`
|
||||
)
|
||||
|
||||
versions.push(res.data)
|
||||
try {
|
||||
res = await axios.get(
|
||||
`https://api.modrinth.com/api/v1/version/${version}`
|
||||
)
|
||||
versions.push(res.data)
|
||||
} catch {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('Some versions may be missing...')
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
@@ -1,109 +1,115 @@
|
||||
<template>
|
||||
<ModPage :mod="mod" :versions="versions" :members="members">
|
||||
<div class="version">
|
||||
<div class="header">
|
||||
<h3>{{ version.name }}</h3>
|
||||
<div
|
||||
v-if="
|
||||
this.$auth.loggedIn &&
|
||||
members.find((x) => x.user_id === this.$auth.user.id)
|
||||
<div class="version-header">
|
||||
<h4>{{ version.name }}</h4>
|
||||
<span v-if="version.version_type === 'release'" class="badge green">
|
||||
Release
|
||||
</span>
|
||||
<span v-if="version.version_type === 'beta'" class="badge yellow">
|
||||
Beta
|
||||
</span>
|
||||
<span v-if="version.version_type === 'alpha'" class="badge red">
|
||||
Alpha
|
||||
</span>
|
||||
<span>
|
||||
{{ version.version_number }}
|
||||
</span>
|
||||
<Categories :categories="version.loaders" />
|
||||
<a
|
||||
:href="primaryFile.url"
|
||||
class="download-button"
|
||||
@click.prevent="
|
||||
downloadFile(primaryFile.hashes.sha1, primaryFile.url)
|
||||
"
|
||||
class="user-actions"
|
||||
>
|
||||
<button class="trash red">
|
||||
<TrashIcon />
|
||||
</button>
|
||||
<button class="upload" @click="showPopup = !showPopup">
|
||||
<UploadIcon />
|
||||
</button>
|
||||
<DownloadIcon />
|
||||
Download
|
||||
</a>
|
||||
</div>
|
||||
<div class="stats">
|
||||
<div class="stat">
|
||||
<DownloadIcon />
|
||||
<div class="info">
|
||||
<h4>Downloads</h4>
|
||||
<p class="value">{{ version.downloads }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="stat">
|
||||
<CalendarIcon />
|
||||
<div class="info">
|
||||
<h4>Created</h4>
|
||||
<p
|
||||
v-tooltip="
|
||||
$dayjs(version.published).format(
|
||||
'[Created on] YYYY-MM-DD [at] HH:mm A'
|
||||
)
|
||||
"
|
||||
class="value"
|
||||
>
|
||||
{{ $dayjs(version.published).fromNow() }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="stat">
|
||||
<TagIcon />
|
||||
<div class="info">
|
||||
<h4>Available For</h4>
|
||||
<p class="value">
|
||||
{{ version.game_versions.join(', ') }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="markdown-body" v-html="changelog"></div>
|
||||
<hr />
|
||||
<div class="columns metadata">
|
||||
<div class="author">
|
||||
<img :src="version.author.avatar_url" />
|
||||
<p>{{ version.author.name }}</p>
|
||||
</div>
|
||||
<p>{{ version.downloads }} Downloads</p>
|
||||
<div>
|
||||
<FabricIcon
|
||||
v-if="version.loaders.includes('fabric')"
|
||||
stroke="#AC6C3A"
|
||||
/>
|
||||
<ForgeIcon
|
||||
v-if="version.loaders.includes('forge')"
|
||||
stroke="#8B81E6"
|
||||
/>
|
||||
</div>
|
||||
<div class="game-versions">
|
||||
<p v-for="gameVersion in version.game_versions" :key="gameVersion">
|
||||
{{ gameVersion }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<hr />
|
||||
<div v-compiled-markdown="changelog" class="markdown-body"></div>
|
||||
<div class="files">
|
||||
<div v-for="file in version.files" :key="file.hashes.sha1">
|
||||
<p>{{ file.filename }}</p>
|
||||
<a :href="file.url" download>
|
||||
<div class="text-wrapper">
|
||||
<p>{{ file.filename }}</p>
|
||||
<div
|
||||
v-if="
|
||||
$auth.loggedIn &&
|
||||
members.find((x) => x.user_id === $auth.user.id)
|
||||
"
|
||||
class="actions"
|
||||
>
|
||||
<button @click="deleteFile(file.hashes.sha1)">Delete File</button>
|
||||
<button @click="makePrimary(file.hashes.sha1)">
|
||||
Make Primary
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<a
|
||||
:href="file.url"
|
||||
@click.prevent="downloadFile(file.hash, file.url)"
|
||||
>
|
||||
<DownloadIcon />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<FileInput class="file-input" @change="addFiles" />
|
||||
</div>
|
||||
<Popup :show-popup="showPopup">
|
||||
<h3 class="popup-title">Upload Files</h3>
|
||||
<FileInput
|
||||
input-id="version-files"
|
||||
input-accept="application/*"
|
||||
default-text="Upload Files"
|
||||
:input-multiple="true"
|
||||
@change="addFiles"
|
||||
>
|
||||
<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
|
||||
filesToUpload = []
|
||||
"
|
||||
>
|
||||
<TrashIcon />
|
||||
</button>
|
||||
<button class="default-button" @click="uploadFiles">Upload</button>
|
||||
</div>
|
||||
</Popup>
|
||||
</ModPage>
|
||||
</template>
|
||||
<script>
|
||||
import axios from 'axios'
|
||||
|
||||
import ModPage from '@/components/ModPage'
|
||||
import xss from 'xss'
|
||||
import marked from 'marked'
|
||||
|
||||
import Popup from '@/components/Popup'
|
||||
import Categories from '@/components/Categories'
|
||||
import FileInput from '@/components/FileInput'
|
||||
import DownloadIcon from '~/assets/images/utils/download.svg?inline'
|
||||
import UploadIcon from '~/assets/images/utils/upload.svg?inline'
|
||||
import TrashIcon from '~/assets/images/utils/trash.svg?inline'
|
||||
|
||||
import ForgeIcon from '~/assets/images/categories/forge.svg?inline'
|
||||
import FabricIcon from '~/assets/images/categories/fabric.svg?inline'
|
||||
import CalendarIcon from '~/assets/images/utils/calendar.svg?inline'
|
||||
import TagIcon from '~/assets/images/utils/tag.svg?inline'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Popup,
|
||||
FileInput,
|
||||
Categories,
|
||||
ModPage,
|
||||
ForgeIcon,
|
||||
FabricIcon,
|
||||
DownloadIcon,
|
||||
UploadIcon,
|
||||
TrashIcon,
|
||||
CalendarIcon,
|
||||
TagIcon,
|
||||
},
|
||||
auth: false,
|
||||
async asyncData(data) {
|
||||
@@ -125,11 +131,15 @@ export default {
|
||||
|
||||
const versions = []
|
||||
for (const version of mod.versions) {
|
||||
res = await axios.get(
|
||||
`https://api.modrinth.com/api/v1/version/${version}`
|
||||
)
|
||||
|
||||
versions.push(res.data)
|
||||
try {
|
||||
res = await axios.get(
|
||||
`https://api.modrinth.com/api/v1/version/${version}`
|
||||
)
|
||||
versions.push(res.data)
|
||||
} catch {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('Some versions may be missing...')
|
||||
}
|
||||
}
|
||||
|
||||
const version = versions.find((x) => x.id === data.params.version)
|
||||
@@ -138,8 +148,13 @@ export default {
|
||||
|
||||
let changelog = ''
|
||||
if (version.changelog_url) {
|
||||
res = await axios.get(version.changelog_url)
|
||||
changelog = xss(marked(res.data))
|
||||
changelog = (await axios.get(version.changelog_url)).data
|
||||
}
|
||||
|
||||
let primaryFile = version.files.find((file) => file.primary)
|
||||
|
||||
if (!primaryFile) {
|
||||
primaryFile = version.files[0]
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -148,16 +163,55 @@ export default {
|
||||
members,
|
||||
version,
|
||||
changelog,
|
||||
primaryFile,
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
showPopup: false,
|
||||
filesToUpload: [],
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
addFiles(e) {
|
||||
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()
|
||||
},
|
||||
async deleteFile(hash) {
|
||||
const config = {
|
||||
headers: {
|
||||
Authorization: this.$auth.getToken('local'),
|
||||
},
|
||||
}
|
||||
|
||||
await axios.delete(
|
||||
`https://api.modrinth.com/api/v1/version_file/${hash}`,
|
||||
config
|
||||
)
|
||||
},
|
||||
async makePrimary(hash) {
|
||||
const config = {
|
||||
headers: {
|
||||
Authorization: this.$auth.getToken('local'),
|
||||
},
|
||||
}
|
||||
|
||||
await axios.patch(
|
||||
`https://api.modrinth.com/api/v1/version/${this.version.id}`,
|
||||
{
|
||||
primary_file: {
|
||||
sha1: hash,
|
||||
},
|
||||
},
|
||||
config
|
||||
)
|
||||
},
|
||||
async addFiles(e) {
|
||||
this.filesToUpload = e.target.files
|
||||
|
||||
for (let i = 0; i < e.target.files.length; i++) {
|
||||
@@ -165,8 +219,7 @@ export default {
|
||||
'-' + i
|
||||
)
|
||||
}
|
||||
},
|
||||
async uploadFiles() {
|
||||
|
||||
this.$nuxt.$loading.start()
|
||||
|
||||
const formData = new FormData()
|
||||
@@ -254,70 +307,38 @@ export default {
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.version {
|
||||
background: var(--color-bg);
|
||||
border-radius: 0 0 0.5rem 0.5rem;
|
||||
box-shadow: 0 2px 3px 1px var(--color-grey-2);
|
||||
padding: 1em;
|
||||
margin-bottom: var(--spacing-card-md);
|
||||
background: var(--color-raised-bg);
|
||||
border-radius: var(--size-rounded-card);
|
||||
padding: 1rem;
|
||||
|
||||
.header {
|
||||
h3 {
|
||||
display: inline-block;
|
||||
}
|
||||
.user-actions {
|
||||
float: right;
|
||||
|
||||
button {
|
||||
cursor: pointer;
|
||||
margin-right: 10px;
|
||||
padding: 5px;
|
||||
border: none;
|
||||
border-radius: var(--size-rounded-sm);
|
||||
}
|
||||
|
||||
.trash {
|
||||
color: #9b2c2c;
|
||||
background-color: var(--color-bg);
|
||||
}
|
||||
|
||||
.upload {
|
||||
color: var(--color-text);
|
||||
background-color: var(--color-grey-1);
|
||||
* {
|
||||
margin: auto 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
hr {
|
||||
margin: 20px 0;
|
||||
color: var(--color-grey-1);
|
||||
}
|
||||
|
||||
.metadata {
|
||||
.version-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
.author {
|
||||
h4,
|
||||
span {
|
||||
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;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
img {
|
||||
height: 50px;
|
||||
width: 50px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
border-radius: var(--size-rounded-sm);
|
||||
|
||||
.game-versions {
|
||||
max-width: 200px;
|
||||
p {
|
||||
margin: 0 0 0 10px;
|
||||
padding: 4px;
|
||||
font-size: 15px;
|
||||
color: var(--color-text);
|
||||
background-color: var(--color-grey-1);
|
||||
display: inline-block;
|
||||
&:hover,
|
||||
&:focus {
|
||||
background-color: var(--color-button-bg-hover);
|
||||
}
|
||||
|
||||
svg {
|
||||
margin-right: 0.25rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -326,71 +347,77 @@ export default {
|
||||
|
||||
div {
|
||||
display: flex;
|
||||
margin-right: 10px;
|
||||
border: 1px solid var(--color-grey-1);
|
||||
border-radius: var(--size-rounded-sm);
|
||||
margin-right: 0.5rem;
|
||||
background: var(--color-bg);
|
||||
border-radius: var(--size-rounded-control);
|
||||
|
||||
p {
|
||||
margin-left: 10px;
|
||||
margin-right: 10px;
|
||||
.text-wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 0.5rem;
|
||||
|
||||
.actions {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
max-height: 3rem;
|
||||
font-size: var(--font-size-sm);
|
||||
|
||||
button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
svg {
|
||||
margin-left: 0.25rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
a {
|
||||
display: table-cell;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-left: auto;
|
||||
width: 40px;
|
||||
height: 60px;
|
||||
background-color: var(--color-grey-1);
|
||||
color: var(--color-grey-3);
|
||||
width: 2.5rem;
|
||||
height: auto;
|
||||
background-color: var(--color-button-bg);
|
||||
color: var(--color-button-text);
|
||||
border-radius: 0 var(--size-rounded-control) var(--size-rounded-control)
|
||||
0;
|
||||
|
||||
svg {
|
||||
margin-top: 15px;
|
||||
vertical-align: center;
|
||||
height: 30px;
|
||||
width: 40px;
|
||||
}
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
background-color: var(--color-grey-3);
|
||||
color: var(--color-grey-4);
|
||||
background-color: var(--color-button-bg-hover);
|
||||
color: var(--color-button-text-hover);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.popup-title {
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
|
||||
.popup-buttons {
|
||||
margin-top: 40px;
|
||||
.stats {
|
||||
display: flex;
|
||||
justify-content: left;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
margin: 0.5rem 0;
|
||||
.stat {
|
||||
margin-right: 0.75rem;
|
||||
@extend %stat;
|
||||
|
||||
.default-button {
|
||||
border-radius: var(--size-rounded-sm);
|
||||
cursor: pointer;
|
||||
border: none;
|
||||
padding: 10px;
|
||||
background-color: var(--color-grey-1);
|
||||
color: var(--color-grey-5);
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
color: var(--color-grey-4);
|
||||
svg {
|
||||
padding: 0.25rem;
|
||||
border-radius: 50%;
|
||||
background-color: var(--color-button-bg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.trash-button {
|
||||
cursor: pointer;
|
||||
margin-right: 10px;
|
||||
padding: 5px;
|
||||
border: none;
|
||||
border-radius: var(--size-rounded-sm);
|
||||
color: #9b2c2c;
|
||||
background-color: var(--color-bg);
|
||||
}
|
||||
.file-input {
|
||||
margin-top: 2rem;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -5,20 +5,29 @@
|
||||
<tr>
|
||||
<th></th>
|
||||
<th>Name</th>
|
||||
<th>Number</th>
|
||||
<th>Loaders</th>
|
||||
<th>Game Versions</th>
|
||||
<th>Version</th>
|
||||
<th>Mod Loader</th>
|
||||
<th>Minecraft Version</th>
|
||||
<th>Status</th>
|
||||
<th>Downloads</th>
|
||||
<th>Published</th>
|
||||
<th>Date Published</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="version in versions" :key="version.id">
|
||||
<td>
|
||||
<nuxt-link :to="'/mod/' + mod.id + '/version/' + version.id">
|
||||
<a
|
||||
:href="findPrimary(version).url"
|
||||
class="download"
|
||||
@click.prevent="
|
||||
downloadFile(
|
||||
findPrimary(version).hashes.sha1,
|
||||
findPrimary(version).url
|
||||
)
|
||||
"
|
||||
>
|
||||
<DownloadIcon />
|
||||
</nuxt-link>
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<nuxt-link :to="'/mod/' + mod.id + '/version/' + version.id">
|
||||
@@ -27,14 +36,8 @@
|
||||
</td>
|
||||
<td>{{ version.version_number }}</td>
|
||||
<td>
|
||||
<FabricIcon
|
||||
v-if="version.loaders.includes('fabric')"
|
||||
stroke="#AC6C3A"
|
||||
/>
|
||||
<ForgeIcon
|
||||
v-if="version.loaders.includes('forge')"
|
||||
stroke="#8B81E6"
|
||||
/>
|
||||
<FabricIcon v-if="version.loaders.includes('fabric')" />
|
||||
<ForgeIcon v-if="version.loaders.includes('forge')" />
|
||||
</td>
|
||||
<td>{{ version.game_versions.join(', ') }}</td>
|
||||
<td>
|
||||
@@ -62,10 +65,6 @@
|
||||
class="create-version-popup-body"
|
||||
>
|
||||
<h3>New Version</h3>
|
||||
<div v-if="currentError" class="error">
|
||||
<h4>Error</h4>
|
||||
<p>{{ currentError }}</p>
|
||||
</div>
|
||||
<label
|
||||
for="version-title"
|
||||
class="required"
|
||||
@@ -110,7 +109,7 @@
|
||||
<label
|
||||
title="The version number of this version. Preferably following semantic versioning"
|
||||
>
|
||||
Loaders
|
||||
Mod Loaders
|
||||
</label>
|
||||
<multiselect
|
||||
v-model="createdVersion.loaders"
|
||||
@@ -128,7 +127,7 @@
|
||||
placeholder="Choose loaders..."
|
||||
/>
|
||||
<label title="The versions of minecraft that this mod version supports">
|
||||
Game Versions
|
||||
Minecraft Versions
|
||||
</label>
|
||||
<multiselect
|
||||
v-model="createdVersion.game_versions"
|
||||
@@ -155,7 +154,7 @@
|
||||
/>
|
||||
<FileInput
|
||||
input-id="version-files"
|
||||
input-accept="application/java-archive,application/zip"
|
||||
accept="application/java-archive,application/zip"
|
||||
default-text="Upload Files"
|
||||
:input-multiple="true"
|
||||
@change="updateVersionFiles"
|
||||
@@ -237,11 +236,15 @@ export default {
|
||||
|
||||
const versions = []
|
||||
for (const version of mod.versions) {
|
||||
res = await axios.get(
|
||||
`https://api.modrinth.com/api/v1/version/${version}`
|
||||
)
|
||||
|
||||
versions.push(res.data)
|
||||
try {
|
||||
res = await axios.get(
|
||||
`https://api.modrinth.com/api/v1/version/${version}`
|
||||
)
|
||||
versions.push(res.data)
|
||||
} catch {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('Some versions may be missing...')
|
||||
}
|
||||
}
|
||||
|
||||
res = await axios.get(`https://api.modrinth.com/api/v1/tag/loader`)
|
||||
@@ -320,6 +323,29 @@ export default {
|
||||
|
||||
this.$nuxt.$loading.finish()
|
||||
},
|
||||
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) {
|
||||
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()
|
||||
},
|
||||
},
|
||||
head() {
|
||||
return {
|
||||
@@ -368,10 +394,10 @@ export default {
|
||||
|
||||
<style lang="scss" scoped>
|
||||
table {
|
||||
background: var(--color-bg);
|
||||
border-collapse: collapse;
|
||||
border-radius: 0 0 0.5rem 0.5rem;
|
||||
box-shadow: 0 2px 3px 1px var(--color-grey-2);
|
||||
margin-bottom: var(--spacing-card-md);
|
||||
background: var(--color-raised-bg);
|
||||
border-radius: var(--size-rounded-card);
|
||||
table-layout: fixed;
|
||||
width: 100%;
|
||||
|
||||
@@ -383,7 +409,7 @@ table {
|
||||
tr:first-child {
|
||||
th,
|
||||
td {
|
||||
border-bottom: 1px solid var(--color-grey-2);
|
||||
border-bottom: 1px solid var(--color-divider);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -391,14 +417,14 @@ table {
|
||||
td {
|
||||
&:first-child {
|
||||
text-align: center;
|
||||
width: 5%;
|
||||
width: 7%;
|
||||
|
||||
svg {
|
||||
color: var(--color-grey-3);
|
||||
color: var(--color-text);
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
color: var(--color-grey-5);
|
||||
color: var(--color-text-hover);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -406,23 +432,23 @@ table {
|
||||
&:nth-child(2),
|
||||
&:nth-child(5) {
|
||||
padding-left: 0;
|
||||
width: 15%;
|
||||
width: 12%;
|
||||
}
|
||||
}
|
||||
|
||||
th {
|
||||
color: #718096;
|
||||
color: var(--color-heading);
|
||||
font-size: 0.8rem;
|
||||
letter-spacing: 0.02rem;
|
||||
margin-bottom: 0.5rem;
|
||||
margin-top: 1.5rem;
|
||||
padding: 1rem 1rem;
|
||||
padding: 0.75rem 1rem;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
td {
|
||||
overflow: hidden;
|
||||
padding: 0.25rem 1rem;
|
||||
padding: 0.75rem 1rem;
|
||||
|
||||
img {
|
||||
height: 3rem;
|
||||
@@ -448,7 +474,7 @@ input {
|
||||
outline: none;
|
||||
border: none;
|
||||
margin: 10px 0 30px;
|
||||
background-color: var(--color-grey-1);
|
||||
background-color: var(--color-button-bg);
|
||||
color: var(--color-text);
|
||||
font-family: monospace;
|
||||
}
|
||||
@@ -482,21 +508,15 @@ input {
|
||||
cursor: pointer;
|
||||
border: none;
|
||||
padding: 10px;
|
||||
background-color: var(--color-grey-1);
|
||||
color: var(--color-grey-5);
|
||||
background-color: var(--color-button-bg);
|
||||
color: var(--color-button-text);
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
color: var(--color-grey-4);
|
||||
background-color: var(--color-button-bg-hover);
|
||||
}
|
||||
}
|
||||
|
||||
.error {
|
||||
margin: 20px 0;
|
||||
border-left: #e04e3e 7px solid;
|
||||
padding: 5px 20px 20px 20px;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 400px) {
|
||||
th,
|
||||
td {
|
||||
|
||||
1159
pages/mod/create.vue
1159
pages/mod/create.vue
File diff suppressed because it is too large
Load Diff
755
pages/mods.vue
755
pages/mods.vue
@@ -1,276 +1,294 @@
|
||||
<template>
|
||||
<div class="columns">
|
||||
<div class="content column-grow-5">
|
||||
<h2>Mods</h2>
|
||||
<section class="search-bar">
|
||||
<div class="iconified-input column-grow-2">
|
||||
<label class="hidden" for="search">Search Mods</label>
|
||||
<input
|
||||
id="search"
|
||||
v-model="query"
|
||||
type="search"
|
||||
name="search"
|
||||
placeholder="Search mods"
|
||||
@input="onSearchChange(1)"
|
||||
<div class="page-container">
|
||||
<div class="page-contents">
|
||||
<div class="content">
|
||||
<section class="search-nav">
|
||||
<div class="iconified-input column-grow-2">
|
||||
<label class="hidden" for="search">Search Mods</label>
|
||||
<input
|
||||
id="search"
|
||||
v-model="query"
|
||||
type="search"
|
||||
name="search"
|
||||
placeholder="Search..."
|
||||
autocomplete="off"
|
||||
@input="onSearchChange(1)"
|
||||
/>
|
||||
<SearchIcon />
|
||||
</div>
|
||||
<div class="sort-paginate">
|
||||
<div class="labeled-control">
|
||||
<h3>Sort By</h3>
|
||||
<Multiselect
|
||||
v-model="sortType"
|
||||
class="sort-types"
|
||||
placeholder="Select one"
|
||||
track-by="display"
|
||||
label="display"
|
||||
:options="sortTypes"
|
||||
:searchable="false"
|
||||
:close-on-select="true"
|
||||
:show-labels="false"
|
||||
:allow-empty="false"
|
||||
@input="onSearchChange(1)"
|
||||
>
|
||||
<template slot="singleLabel" slot-scope="{ option }">{{
|
||||
option.display
|
||||
}}</template>
|
||||
</Multiselect>
|
||||
</div>
|
||||
<div class="labeled-control per-page">
|
||||
<h3>Per Page</h3>
|
||||
<Multiselect
|
||||
v-model="maxResults"
|
||||
class="max-results"
|
||||
placeholder="Select one"
|
||||
:options="[5, 10, 15, 20, 50, 100]"
|
||||
:searchable="false"
|
||||
:close-on-select="true"
|
||||
:show-labels="false"
|
||||
:allow-empty="false"
|
||||
@input="onSearchChange(currentPage)"
|
||||
>
|
||||
</Multiselect>
|
||||
</div>
|
||||
<div class="mobile-filters-button">
|
||||
<button @click="toggleFiltersMenu">Filter</button>
|
||||
</div>
|
||||
</div>
|
||||
<pagination
|
||||
:current-page="currentPage"
|
||||
:pages="pages"
|
||||
@switch-page="onSearchChange"
|
||||
></pagination>
|
||||
</section>
|
||||
<div class="results column-grow-4">
|
||||
<client-only>
|
||||
<EthicalAd type="text" />
|
||||
</client-only>
|
||||
<SearchResult
|
||||
v-for="(result, index) in results"
|
||||
:id="result.mod_id.split('-')[1]"
|
||||
:key="result.mod_id"
|
||||
:author="result.author"
|
||||
:name="result.title"
|
||||
:description="result.description"
|
||||
:latest-version="result.latest_version"
|
||||
:created-at="result.date_created"
|
||||
:updated-at="result.date_modified"
|
||||
:downloads="result.downloads.toString()"
|
||||
:icon-url="result.icon_url"
|
||||
:author-url="result.author_url"
|
||||
:page-url="result.page_url"
|
||||
:categories="result.categories"
|
||||
:is-ad="index === -1"
|
||||
:is-modrinth="result.host === 'modrinth'"
|
||||
/>
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<circle cx="11" cy="11" r="8" />
|
||||
<line x1="21" y1="21" x2="16.65" y2="16.65" />
|
||||
</svg>
|
||||
</div>
|
||||
<div class="sort-paginate">
|
||||
<Multiselect
|
||||
v-model="sortType"
|
||||
class="sort-types"
|
||||
placeholder="Select one"
|
||||
track-by="display"
|
||||
label="display"
|
||||
:options="sortTypes"
|
||||
:searchable="false"
|
||||
:close-on-select="true"
|
||||
:show-labels="false"
|
||||
:allow-empty="false"
|
||||
@input="onSearchChange(1)"
|
||||
>
|
||||
<template slot="singleLabel" slot-scope="{ option }">{{
|
||||
option.display
|
||||
}}</template>
|
||||
</Multiselect>
|
||||
<div class="mobile-filters-button">
|
||||
<button @click="toggleFiltersMenu">Filter...</button>
|
||||
<div v-if="results.length === 0" class="no-results">
|
||||
<p>No results found for your query!</p>
|
||||
</div>
|
||||
</div>
|
||||
<pagination
|
||||
:current-page="currentPage"
|
||||
:pages="pages"
|
||||
@switch-page="onSearchChange"
|
||||
></pagination>
|
||||
</section>
|
||||
<div class="results column-grow-4">
|
||||
<client-only>
|
||||
<EthicalAd type="text" />
|
||||
</client-only>
|
||||
<SearchResult
|
||||
v-for="(result, index) in results"
|
||||
:id="result.mod_id"
|
||||
:key="result.mod_id"
|
||||
:author="result.author"
|
||||
:name="result.title"
|
||||
:description="result.description"
|
||||
:latest-version="result.latest_version"
|
||||
:created-at="result.date_created"
|
||||
:updated-at="result.date_modified"
|
||||
:downloads="result.downloads.toString()"
|
||||
:icon-url="result.icon_url"
|
||||
:author-url="result.author_url"
|
||||
:page-url="result.page_url"
|
||||
:categories="result.categories"
|
||||
:is-ad="index === -1"
|
||||
/>
|
||||
<div v-if="results.length === 0" class="no-results">
|
||||
<p>No results found for your query!</p>
|
||||
</div>
|
||||
<section v-if="pages.length > 1" class="search-bottom">
|
||||
<div class="labeled-control">
|
||||
<h3>Per Page</h3>
|
||||
<Multiselect
|
||||
v-model="maxResults"
|
||||
class="max-results"
|
||||
placeholder="Select one"
|
||||
:options="[5, 10, 15, 20, 50, 100]"
|
||||
:searchable="false"
|
||||
:close-on-select="true"
|
||||
:show-labels="false"
|
||||
:allow-empty="false"
|
||||
@input="onSearchChange(currentPage)"
|
||||
>
|
||||
</Multiselect>
|
||||
</div>
|
||||
<pagination
|
||||
:current-page="currentPage"
|
||||
:pages="pages"
|
||||
@switch-page="onSearchChangeToTop"
|
||||
></pagination>
|
||||
</section>
|
||||
</div>
|
||||
<section v-if="pages.length > 1" class="search-bottom">
|
||||
<Multiselect
|
||||
v-model="maxResults"
|
||||
class="max-results"
|
||||
placeholder="Select one"
|
||||
:options="[5, 10, 15, 20, 50, 100]"
|
||||
:searchable="false"
|
||||
:close-on-select="true"
|
||||
:show-labels="false"
|
||||
:allow-empty="false"
|
||||
@input="onSearchChange(currentPage)"
|
||||
>
|
||||
</Multiselect>
|
||||
<pagination
|
||||
:current-page="currentPage"
|
||||
:pages="pages"
|
||||
@switch-page="onSearchChangeToTop"
|
||||
></pagination>
|
||||
<section id="filters" class="filters">
|
||||
<div class="filters-wrapper">
|
||||
<section class="filter-group">
|
||||
<button class="filter-button-done" @click="toggleFiltersMenu">
|
||||
Done
|
||||
</button>
|
||||
<button @click="clearFilters">Reset filters</button>
|
||||
<h3>Categories</h3>
|
||||
<SearchFilter
|
||||
:active-filters="facets"
|
||||
display-name="Technology"
|
||||
facet-name="categories:technology"
|
||||
@toggle="toggleFacet"
|
||||
>
|
||||
<TechCategory />
|
||||
</SearchFilter>
|
||||
<SearchFilter
|
||||
:active-filters="facets"
|
||||
display-name="Adventure"
|
||||
facet-name="categories:adventure"
|
||||
@toggle="toggleFacet"
|
||||
>
|
||||
<AdventureCategory />
|
||||
</SearchFilter>
|
||||
<SearchFilter
|
||||
:active-filters="facets"
|
||||
display-name="Magic"
|
||||
facet-name="categories:magic"
|
||||
@toggle="toggleFacet"
|
||||
>
|
||||
<MagicCategory />
|
||||
</SearchFilter>
|
||||
<SearchFilter
|
||||
:active-filters="facets"
|
||||
display-name="Utility"
|
||||
facet-name="categories:utility"
|
||||
@toggle="toggleFacet"
|
||||
>
|
||||
<UtilityCategory />
|
||||
</SearchFilter>
|
||||
<SearchFilter
|
||||
:active-filters="facets"
|
||||
display-name="Decoration"
|
||||
facet-name="categories:decoration"
|
||||
@toggle="toggleFacet"
|
||||
>
|
||||
<DecorationCategory />
|
||||
</SearchFilter>
|
||||
<SearchFilter
|
||||
:active-filters="facets"
|
||||
display-name="Library"
|
||||
facet-name="categories:library"
|
||||
@toggle="toggleFacet"
|
||||
>
|
||||
<LibraryCategory />
|
||||
</SearchFilter>
|
||||
<SearchFilter
|
||||
:active-filters="facets"
|
||||
display-name="Cursed"
|
||||
facet-name="categories:cursed"
|
||||
@toggle="toggleFacet"
|
||||
>
|
||||
<CursedCategory />
|
||||
</SearchFilter>
|
||||
<SearchFilter
|
||||
:active-filters="facets"
|
||||
display-name="World Generation"
|
||||
facet-name="categories:worldgen"
|
||||
@toggle="toggleFacet"
|
||||
>
|
||||
<WorldGenCategory />
|
||||
</SearchFilter>
|
||||
<SearchFilter
|
||||
:active-filters="facets"
|
||||
display-name="Storage"
|
||||
facet-name="categories:storage"
|
||||
@toggle="toggleFacet"
|
||||
>
|
||||
<StorageCategory />
|
||||
</SearchFilter>
|
||||
<SearchFilter
|
||||
:active-filters="facets"
|
||||
display-name="Food"
|
||||
facet-name="categories:food"
|
||||
@toggle="toggleFacet"
|
||||
>
|
||||
<FoodCategory />
|
||||
</SearchFilter>
|
||||
<SearchFilter
|
||||
:active-filters="facets"
|
||||
display-name="Equipment"
|
||||
facet-name="categories:equipment"
|
||||
@toggle="toggleFacet"
|
||||
>
|
||||
<EquipmentCategory />
|
||||
</SearchFilter>
|
||||
<SearchFilter
|
||||
:active-filters="facets"
|
||||
display-name="Miscellaneous"
|
||||
facet-name="categories:misc"
|
||||
@toggle="toggleFacet"
|
||||
>
|
||||
<MiscCategory />
|
||||
</SearchFilter>
|
||||
<h3>Mod Loaders</h3>
|
||||
<SearchFilter
|
||||
:active-filters="facets"
|
||||
display-name="Fabric"
|
||||
facet-name="categories:fabric"
|
||||
@toggle="toggleFacet"
|
||||
>
|
||||
<FabricLoader />
|
||||
</SearchFilter>
|
||||
<SearchFilter
|
||||
:active-filters="facets"
|
||||
display-name="Forge"
|
||||
facet-name="categories:forge"
|
||||
@toggle="toggleFacet"
|
||||
>
|
||||
<ForgeLoader />
|
||||
</SearchFilter>
|
||||
<h3>Host</h3>
|
||||
<SearchFilter
|
||||
:active-filters="facets"
|
||||
display-name="Modrinth"
|
||||
facet-name="host:modrinth"
|
||||
@toggle="toggleFacet"
|
||||
>
|
||||
<Modrinth />
|
||||
</SearchFilter>
|
||||
<SearchFilter
|
||||
:active-filters="facets"
|
||||
display-name="CurseForge"
|
||||
facet-name="host:curseforge"
|
||||
@toggle="toggleFacet"
|
||||
>
|
||||
<FlameAnvil />
|
||||
</SearchFilter>
|
||||
<h3>Versions</h3>
|
||||
<SearchFilter
|
||||
:active-filters="showVersions"
|
||||
display-name="Include snapshots"
|
||||
facet-name="snapshots"
|
||||
style="margin-bottom: 10px"
|
||||
@toggle="fillInitialVersions"
|
||||
/>
|
||||
</section>
|
||||
<multiselect
|
||||
v-model="selectedVersions"
|
||||
:options="versions"
|
||||
:loading="versions.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..."
|
||||
@input="onSearchChange(1)"
|
||||
></multiselect>
|
||||
</div>
|
||||
<m-footer class="footer" />
|
||||
</section>
|
||||
</div>
|
||||
<section id="filters" class="filters">
|
||||
<div class="filters-wrapper">
|
||||
<section class="filter-group">
|
||||
<button class="filter-button-done" @click="toggleFiltersMenu">
|
||||
Done
|
||||
</button>
|
||||
<button @click="clearFilters">Clear Filters</button>
|
||||
<h3>Categories</h3>
|
||||
<SearchFilter
|
||||
:active-filters="facets"
|
||||
display-name="Technology"
|
||||
facet-name="categories:technology"
|
||||
@toggle="toggleFacet"
|
||||
>
|
||||
<TechCategory />
|
||||
</SearchFilter>
|
||||
<SearchFilter
|
||||
:active-filters="facets"
|
||||
display-name="Adventure"
|
||||
facet-name="categories:adventure"
|
||||
@toggle="toggleFacet"
|
||||
>
|
||||
<AdventureCategory />
|
||||
</SearchFilter>
|
||||
<SearchFilter
|
||||
:active-filters="facets"
|
||||
display-name="Magic"
|
||||
facet-name="categories:magic"
|
||||
@toggle="toggleFacet"
|
||||
>
|
||||
<MagicCategory />
|
||||
</SearchFilter>
|
||||
<SearchFilter
|
||||
:active-filters="facets"
|
||||
display-name="Utility"
|
||||
facet-name="categories:utility"
|
||||
@toggle="toggleFacet"
|
||||
>
|
||||
<UtilityCategory />
|
||||
</SearchFilter>
|
||||
<SearchFilter
|
||||
:active-filters="facets"
|
||||
display-name="Decoration"
|
||||
facet-name="categories:decoration"
|
||||
@toggle="toggleFacet"
|
||||
>
|
||||
<DecorationCategory />
|
||||
</SearchFilter>
|
||||
<SearchFilter
|
||||
:active-filters="facets"
|
||||
display-name="Library"
|
||||
facet-name="categories:library"
|
||||
@toggle="toggleFacet"
|
||||
>
|
||||
<LibraryCategory />
|
||||
</SearchFilter>
|
||||
<SearchFilter
|
||||
:active-filters="facets"
|
||||
display-name="Cursed"
|
||||
facet-name="categories:cursed"
|
||||
@toggle="toggleFacet"
|
||||
>
|
||||
<CursedCategory />
|
||||
</SearchFilter>
|
||||
<SearchFilter
|
||||
:active-filters="facets"
|
||||
display-name="Worldgen"
|
||||
facet-name="categories:worldgen"
|
||||
@toggle="toggleFacet"
|
||||
>
|
||||
<WorldGenCategory />
|
||||
</SearchFilter>
|
||||
<SearchFilter
|
||||
:active-filters="facets"
|
||||
display-name="Storage"
|
||||
facet-name="categories:storage"
|
||||
@toggle="toggleFacet"
|
||||
>
|
||||
<StorageCategory />
|
||||
</SearchFilter>
|
||||
<SearchFilter
|
||||
:active-filters="facets"
|
||||
display-name="Food"
|
||||
facet-name="categories:food"
|
||||
@toggle="toggleFacet"
|
||||
>
|
||||
<FoodCategory />
|
||||
</SearchFilter>
|
||||
<SearchFilter
|
||||
:active-filters="facets"
|
||||
display-name="Equipment"
|
||||
facet-name="categories:equipment"
|
||||
@toggle="toggleFacet"
|
||||
>
|
||||
<EquipmentCategory />
|
||||
</SearchFilter>
|
||||
<SearchFilter
|
||||
:active-filters="facets"
|
||||
display-name="Misc"
|
||||
facet-name="categories:misc"
|
||||
@toggle="toggleFacet"
|
||||
>
|
||||
<MiscCategory />
|
||||
</SearchFilter>
|
||||
<h3>Loaders</h3>
|
||||
<SearchFilter
|
||||
:active-filters="facets"
|
||||
display-name="Forge"
|
||||
facet-name="categories:forge"
|
||||
@toggle="toggleFacet"
|
||||
>
|
||||
<ForgeLoader />
|
||||
</SearchFilter>
|
||||
<SearchFilter
|
||||
:active-filters="facets"
|
||||
display-name="Fabric"
|
||||
facet-name="categories:fabric"
|
||||
@toggle="toggleFacet"
|
||||
>
|
||||
<FabricLoader />
|
||||
</SearchFilter>
|
||||
<h3>Platforms</h3>
|
||||
<SearchFilter
|
||||
:active-filters="facets"
|
||||
display-name="Modrinth"
|
||||
facet-name="host:modrinth"
|
||||
@toggle="toggleFacet"
|
||||
>
|
||||
</SearchFilter>
|
||||
<SearchFilter
|
||||
:active-filters="facets"
|
||||
display-name="Curseforge"
|
||||
facet-name="host:curseforge"
|
||||
@toggle="toggleFacet"
|
||||
>
|
||||
</SearchFilter>
|
||||
<h3>Versions</h3>
|
||||
<SearchFilter
|
||||
:active-filters="showVersions"
|
||||
display-name="Snapshots"
|
||||
facet-name="snapshots"
|
||||
style="margin-bottom: 10px"
|
||||
@toggle="fillInitialVersions"
|
||||
/>
|
||||
</section>
|
||||
<multiselect
|
||||
v-model="selectedVersions"
|
||||
:options="versions"
|
||||
:loading="versions.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..."
|
||||
@input="onSearchChange(1)"
|
||||
></multiselect>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Multiselect from 'vue-multiselect'
|
||||
import axios from 'axios'
|
||||
import SearchResult from '@/components/ModResult'
|
||||
import SearchResult from '@/components/ProjectCard'
|
||||
import Pagination from '@/components/Pagination'
|
||||
import SearchFilter from '@/components/SearchFilter'
|
||||
|
||||
import EthicalAd from '@/components/EthicalAd'
|
||||
import MFooter from '@/components/MFooter'
|
||||
import TechCategory from '~/assets/images/categories/tech.svg?inline'
|
||||
import AdventureCategory from '~/assets/images/categories/adventure.svg?inline'
|
||||
import CursedCategory from '~/assets/images/categories/cursed.svg?inline'
|
||||
@@ -287,9 +305,15 @@ import WorldGenCategory from '~/assets/images/categories/worldgen.svg?inline'
|
||||
import ForgeLoader from '~/assets/images/categories/forge.svg?inline'
|
||||
import FabricLoader from '~/assets/images/categories/fabric.svg?inline'
|
||||
|
||||
import Modrinth from '~/assets/images/categories/modrinth.svg?inline'
|
||||
import FlameAnvil from '~/assets/images/categories/flameanvil.svg?inline'
|
||||
|
||||
import SearchIcon from '~/assets/images/utils/search.svg?inline'
|
||||
|
||||
export default {
|
||||
auth: false,
|
||||
components: {
|
||||
MFooter,
|
||||
EthicalAd,
|
||||
SearchResult,
|
||||
Pagination,
|
||||
@@ -309,6 +333,9 @@ export default {
|
||||
WorldGenCategory,
|
||||
ForgeLoader,
|
||||
FabricLoader,
|
||||
Modrinth,
|
||||
FlameAnvil,
|
||||
SearchIcon,
|
||||
},
|
||||
async fetch() {
|
||||
if (this.$route.query.q) this.query = this.$route.query.q
|
||||
@@ -345,12 +372,12 @@ export default {
|
||||
currentPage: 1,
|
||||
sortTypes: [
|
||||
{ display: 'Relevance', name: 'relevance' },
|
||||
{ display: 'Total Downloads', name: 'downloads' },
|
||||
{ display: 'Newest', name: 'newest' },
|
||||
{ display: 'Updated', name: 'updated' },
|
||||
{ display: 'Download count', name: 'downloads' },
|
||||
{ display: 'Recently created', name: 'newest' },
|
||||
{ display: 'Recently updated', name: 'updated' },
|
||||
],
|
||||
sortType: { display: 'Relevance', name: 'relevance' },
|
||||
maxResults: 5,
|
||||
maxResults: 20,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
@@ -482,7 +509,7 @@ export default {
|
||||
url += `&v=${encodeURIComponent(this.selectedVersions)}`
|
||||
if (this.sortType.name !== 'relevance')
|
||||
url += `&s=${encodeURIComponent(this.sortType.name)}`
|
||||
if (this.maxResults > 5)
|
||||
if (this.maxResults > 20)
|
||||
url += `&m=${encodeURIComponent(this.maxResults)}`
|
||||
|
||||
window.history.pushState(new Date(), 'Mods', url)
|
||||
@@ -525,17 +552,32 @@ export default {
|
||||
|
||||
<style src="vue-multiselect/dist/vue-multiselect.min.css"></style>
|
||||
<style lang="scss">
|
||||
.search-bar {
|
||||
.search-nav {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
flex-flow: column;
|
||||
background: var(--color-raised-bg);
|
||||
border-radius: var(--size-rounded-card);
|
||||
padding: 0.25rem 1rem 0.25rem 1rem;
|
||||
margin-bottom: var(--spacing-card-md);
|
||||
input {
|
||||
border: none;
|
||||
background: transparent;
|
||||
min-width: 200px;
|
||||
}
|
||||
.iconified-input {
|
||||
width: 100%;
|
||||
}
|
||||
.sort-paginate {
|
||||
margin-left: 0.5rem;
|
||||
margin-right: 0.5rem;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
.per-page {
|
||||
margin-left: 0.5rem;
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
@media screen and (min-width: 900px) {
|
||||
flex-flow: row;
|
||||
@@ -543,34 +585,45 @@ export default {
|
||||
width: auto;
|
||||
}
|
||||
.sort-paginate {
|
||||
display: block;
|
||||
width: auto;
|
||||
}
|
||||
}
|
||||
@media screen and (min-width: 1024px) {
|
||||
.sort-paginate {
|
||||
.per-page {
|
||||
display: unset;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.search-bottom {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
background: var(--color-raised-bg);
|
||||
border-radius: var(--size-rounded-card);
|
||||
padding: 0.25rem 1rem 0.25rem 1rem;
|
||||
select {
|
||||
width: 100px;
|
||||
margin-right: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.content {
|
||||
min-height: 96vh;
|
||||
.labeled-control {
|
||||
h3 {
|
||||
@extend %small-label;
|
||||
margin-left: 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.mobile-filters-button {
|
||||
display: inline-block;
|
||||
button {
|
||||
background: var(--color-bg);
|
||||
color: var(--color-text);
|
||||
border: 2px solid var(--color-grey-3);
|
||||
border-radius: var(--size-rounded-sm);
|
||||
padding: 0.5rem;
|
||||
margin-top: 0;
|
||||
height: 2.5rem;
|
||||
padding-left: 1rem;
|
||||
padding-right: 1rem;
|
||||
}
|
||||
|
||||
// Hide button on larger screens where it's not needed
|
||||
@@ -581,48 +634,47 @@ export default {
|
||||
|
||||
.filters {
|
||||
overflow-y: auto;
|
||||
background-color: var(--color-bg);
|
||||
border-left: 1px solid var(--color-grey-2);
|
||||
position: fixed;
|
||||
width: 100vw;
|
||||
right: -100vw;
|
||||
max-height: 100vh;
|
||||
min-width: 15%;
|
||||
top: 3.5rem;
|
||||
height: calc(100vh - 3.5rem);
|
||||
top: var(--size-navbar-height);
|
||||
height: calc(100vh - var(--size-navbar-height));
|
||||
transition: right 150ms;
|
||||
background-color: var(--color-raised-bg);
|
||||
flex-shrink: 0; // Stop shrinking when page contents change
|
||||
|
||||
.filters-wrapper {
|
||||
padding: 0 0.75rem;
|
||||
padding: 0.25rem 0.75rem 0.75rem 0.75rem;
|
||||
}
|
||||
|
||||
h3 {
|
||||
color: #718096;
|
||||
font-size: 0.8rem;
|
||||
letter-spacing: 0.02rem;
|
||||
margin-bottom: 0.5rem;
|
||||
margin-top: 1.5rem;
|
||||
text-transform: uppercase;
|
||||
@extend %large-label;
|
||||
margin-top: 1.25em;
|
||||
}
|
||||
|
||||
// Larger screens that don't need to collapse
|
||||
@media screen and (min-width: 900px) {
|
||||
position: sticky;
|
||||
width: 215px;
|
||||
padding-right: 1rem;
|
||||
transition: none;
|
||||
}
|
||||
|
||||
// Desktop
|
||||
@media screen and (min-width: 1145px) {
|
||||
top: 0;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
&.active {
|
||||
right: 0;
|
||||
}
|
||||
// Larger screens that don't need to collapse
|
||||
@media screen and (min-width: 900px) {
|
||||
top: 0;
|
||||
right: auto;
|
||||
position: unset;
|
||||
height: unset;
|
||||
max-height: unset;
|
||||
transition: none;
|
||||
margin-left: var(--spacing-card-lg);
|
||||
overflow-y: unset;
|
||||
padding-right: 1rem;
|
||||
width: 18vw;
|
||||
background-color: transparent;
|
||||
.filters-wrapper {
|
||||
background-color: var(--color-raised-bg);
|
||||
border-radius: var(--size-rounded-card);
|
||||
}
|
||||
}
|
||||
@media screen and (min-width: 1024px) {
|
||||
width: 300px;
|
||||
}
|
||||
}
|
||||
|
||||
.filter-group {
|
||||
@@ -631,17 +683,6 @@ export default {
|
||||
button {
|
||||
cursor: pointer;
|
||||
width: 100%;
|
||||
padding: 5px 0;
|
||||
outline: none;
|
||||
color: var(--color-grey-5);
|
||||
background-color: var(--color-grey-1);
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--color-grey-2);
|
||||
color: var(--color-text);
|
||||
}
|
||||
}
|
||||
|
||||
.filter-button-done {
|
||||
@@ -654,65 +695,12 @@ export default {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
// Desktop
|
||||
@media screen and (min-width: 1145px) {
|
||||
margin-top: 2em;
|
||||
}
|
||||
}
|
||||
|
||||
.iconified-select {
|
||||
margin-left: 1em;
|
||||
align-items: center;
|
||||
display: inline-flex;
|
||||
flex-direction: row-reverse;
|
||||
|
||||
select {
|
||||
padding-left: 2.5rem;
|
||||
|
||||
&:hover {
|
||||
& + svg {
|
||||
color: var(--color-grey-6);
|
||||
}
|
||||
}
|
||||
|
||||
&:focus {
|
||||
& + svg {
|
||||
color: var(--color-text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
svg {
|
||||
color: var(--color-grey-5);
|
||||
margin-right: -2rem;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
select {
|
||||
width: 220px;
|
||||
padding: 0.5rem 1rem;
|
||||
background: var(--color-bg);
|
||||
border: 2px solid var(--color-grey-3);
|
||||
border-radius: var(--size-rounded-sm);
|
||||
color: var(--color-grey-9);
|
||||
font-size: 1rem;
|
||||
-webkit-appearance: none;
|
||||
-moz-appearance: none;
|
||||
appearance: none;
|
||||
|
||||
&:hover {
|
||||
border-color: var(--color-grey-4);
|
||||
color: var(--color-text);
|
||||
}
|
||||
}
|
||||
|
||||
.sort-types {
|
||||
min-width: 200px;
|
||||
border: 2px solid var(--color-grey-3);
|
||||
border-radius: var(--size-rounded-sm);
|
||||
border: none;
|
||||
border-radius: var(--size-rounded-control);
|
||||
|
||||
.multiselect__tags {
|
||||
padding: 10px 50px 0 8px;
|
||||
@@ -723,45 +711,14 @@ select {
|
||||
.no-results {
|
||||
text-align: center;
|
||||
padding: 20px 0;
|
||||
font-size: 30px;
|
||||
font-size: 1.25rem;
|
||||
color: var(--color-text);
|
||||
background-color: var(--color-grey-1);
|
||||
margin-bottom: var(--spacing-card-md);
|
||||
background: var(--color-raised-bg);
|
||||
border-radius: var(--size-rounded-card);
|
||||
}
|
||||
|
||||
.max-results {
|
||||
max-width: 80px;
|
||||
}
|
||||
|
||||
.multiselect__content-wrapper {
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.multiselect__tags {
|
||||
border: 2px solid var(--color-grey-3);
|
||||
border-radius: var(--size-rounded-sm);
|
||||
}
|
||||
|
||||
.multiselect__tags,
|
||||
.multiselect__spinner {
|
||||
background: var(--color-bg);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.multiselect__spinner::before,
|
||||
.multiselect__spinner::after {
|
||||
border-top-color: var(--color-brand);
|
||||
}
|
||||
|
||||
.multiselect__option--selected.multiselect__option--highlight,
|
||||
.multiselect__option,
|
||||
.multiselect__single,
|
||||
.multiselect__input {
|
||||
color: var(--color-text);
|
||||
background: var(--color-bg);
|
||||
}
|
||||
|
||||
.multiselect__option--highlight,
|
||||
.multiselect__tag {
|
||||
background: var(--color-brand);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,178 +0,0 @@
|
||||
<template>
|
||||
<div class="main">
|
||||
<h1>Privacy Policy</h1>
|
||||
|
||||
<p>
|
||||
At Modrinth, accessible from https://modrinth.com, one of our main
|
||||
priorities is the privacy of our visitors. This Privacy Policy document
|
||||
contains types of information that is collected and recorded by Modrinth
|
||||
and how we use it.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
If you have additional questions or require more information about our
|
||||
Privacy Policy, do not hesitate to contact us.
|
||||
</p>
|
||||
|
||||
<h2>Log Files</h2>
|
||||
|
||||
<p>
|
||||
Modrinth follows a standard procedure of using log files. These files log
|
||||
visitors when they visit websites. All hosting companies do this and a
|
||||
part of hosting services' analytics. The information collected by log
|
||||
files include internet protocol (IP) addresses, browser type, Internet
|
||||
Service Provider (ISP), date and time stamp, referring/exit pages, and
|
||||
possibly the number of clicks. These are not linked to any information
|
||||
that is personally identifiable. The purpose of the information is for
|
||||
analyzing trends, administering the site, tracking users' movement on the
|
||||
website, and gathering demographic information.
|
||||
</p>
|
||||
|
||||
<h2>Cookies and Web Beacons</h2>
|
||||
|
||||
<p>
|
||||
Like any other website, Modrinth uses 'cookies'. These cookies are used to
|
||||
store information including visitors' preferences, and the pages on the
|
||||
website that the visitor accessed or visited. The information is used to
|
||||
optimize the users' experience by customizing our web page content based
|
||||
on visitors' browser type and/or other information.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
For more general information on cookies, please read
|
||||
<a href="https://www.privacypolicies.com/blog/cookies/"
|
||||
>"What Are Cookies"</a
|
||||
>.
|
||||
</p>
|
||||
|
||||
<h2>Our Advertising Partners</h2>
|
||||
|
||||
<p>
|
||||
Some of advertisers on our site may use cookies and web beacons. Our
|
||||
advertising partners are listed below. Each of our advertising partners
|
||||
has their own Privacy Policy for their policies on user data. For easier
|
||||
access, we hyperlinked to their Privacy Policies below.
|
||||
</p>
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
<p>EthicalAds</p>
|
||||
<p>
|
||||
<a href="https://www.ethicalads.io/privacy-policy/"
|
||||
>https://www.ethicalads.io/privacy-policy/</a
|
||||
>
|
||||
</p>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h2>Privacy Policies</h2>
|
||||
|
||||
<P
|
||||
>You may consult this list to find the Privacy Policy for each of the
|
||||
advertising partners of Modrinth.</P
|
||||
>
|
||||
|
||||
<p>
|
||||
Third-party ad servers or ad networks uses technologies like cookies,
|
||||
JavaScript, or Web Beacons that are used in their respective
|
||||
advertisements and links that appear on Modrinth, which are sent directly
|
||||
to users' browser. They automatically receive your IP address when this
|
||||
occurs. These technologies are used to measure the effectiveness of their
|
||||
advertising campaigns and/or to personalize the advertising content that
|
||||
you see on websites that you visit.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Note that Modrinth has no access to or control over these cookies that are
|
||||
used by third-party advertisers.
|
||||
</p>
|
||||
|
||||
<h2>Third Party Privacy Policies</h2>
|
||||
|
||||
<p>
|
||||
Modrinth's Privacy Policy does not apply to other advertisers or websites.
|
||||
Thus, we are advising you to consult the respective Privacy Policies of
|
||||
these third-party ad servers for more detailed information. It may include
|
||||
their practices and instructions about how to opt-out of certain options.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
You can choose to disable cookies through your individual browser options.
|
||||
To know more detailed information about cookie management with specific
|
||||
web browsers, it can be found at the browsers' respective websites. What
|
||||
Are Cookies?
|
||||
</p>
|
||||
|
||||
<h2>Children's Information</h2>
|
||||
|
||||
<p>
|
||||
Another part of our priority is adding protection for children while using
|
||||
the internet. We encourage parents and guardians to observe, participate
|
||||
in, and/or monitor and guide their online activity.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Modrinth does not knowingly collect any Personal Identifiable Information
|
||||
from children under the age of 13. If you think that your child provided
|
||||
this kind of information on our website, we strongly encourage you to
|
||||
contact us immediately and we will do our best efforts to promptly remove
|
||||
such information from our records.
|
||||
</p>
|
||||
|
||||
<h2>Online Privacy Policy Only</h2>
|
||||
|
||||
<p>
|
||||
This Privacy Policy applies only to our online activities and is valid for
|
||||
visitors to our website with regards to the information that they shared
|
||||
and/or collect in Modrinth. This policy is not applicable to any
|
||||
information collected offline or via channels other than this website.
|
||||
</p>
|
||||
|
||||
<h2>Consent</h2>
|
||||
|
||||
<p>
|
||||
By using our website, you hereby consent to our Privacy Policy and agree
|
||||
to its Terms and Conditions.
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
auth: false,
|
||||
layout: 'home',
|
||||
head: {
|
||||
title: 'Privacy - Modrinth',
|
||||
meta: [
|
||||
{
|
||||
hid: 'description',
|
||||
name: 'description',
|
||||
content:
|
||||
'The privacy policy of Modrinth, an open source modding platform. Modrinth currently supports Minecraft, including the forge and fabric mod loaders.',
|
||||
},
|
||||
{
|
||||
hid: 'apple-mobile-web-app-title',
|
||||
name: 'apple-mobile-web-app-title',
|
||||
content: 'Privacy Policy',
|
||||
},
|
||||
{
|
||||
hid: 'og:title',
|
||||
name: 'og:title',
|
||||
content: 'Privacy Policy',
|
||||
},
|
||||
{
|
||||
hid: 'og:url',
|
||||
name: 'og:url',
|
||||
content: `https://modrinth.com/privacy`,
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.main {
|
||||
margin: 0 auto;
|
||||
max-width: 800px;
|
||||
}
|
||||
</style>
|
||||
187
pages/tos.vue
187
pages/tos.vue
@@ -1,187 +0,0 @@
|
||||
<template>
|
||||
<div class="main">
|
||||
<h1>Terms and Conditions</h1>
|
||||
|
||||
<h2>1. Terms</h2>
|
||||
|
||||
<p>
|
||||
By accessing this Website, accessible from https://modrinth.com, you are
|
||||
agreeing to be bound by these Website Terms and Conditions of Use and
|
||||
agree that you are responsible for the agreement with any applicable local
|
||||
laws. If you disagree with any of these terms, you are prohibited from
|
||||
accessing this site. The materials contained in this Website are protected
|
||||
by copyright and trade mark law.
|
||||
</p>
|
||||
|
||||
<h2>2. Use License</h2>
|
||||
|
||||
<p>
|
||||
Permission is granted to temporarily download one copy of the materials on
|
||||
Guavy LLC's Website for personal, non-commercial transitory viewing only.
|
||||
This is the grant of a license, not a transfer of title, and under this
|
||||
license you may not:
|
||||
</p>
|
||||
|
||||
<ul>
|
||||
<li>modify or copy the materials;</li>
|
||||
<li>
|
||||
use the materials for any commercial purpose or for any public display;
|
||||
</li>
|
||||
<li>
|
||||
attempt to reverse engineer any software contained on Guavy LLC's
|
||||
Website;
|
||||
</li>
|
||||
<li>
|
||||
remove any copyright or other proprietary notations from the materials;
|
||||
or
|
||||
</li>
|
||||
<li>
|
||||
transferring the materials to another person or "mirror" the materials
|
||||
on any other server.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<p>
|
||||
This will let Guavy LLC to terminate upon violations of any of these
|
||||
restrictions. Upon termination, your viewing right will also be terminated
|
||||
and you should destroy any downloaded materials in your possession whether
|
||||
it is printed or electronic format.
|
||||
</p>
|
||||
|
||||
<h2>3. Disclaimer</h2>
|
||||
|
||||
<p>
|
||||
All the materials on Guavy LLC’s Website are provided "as is". Guavy LLC
|
||||
makes no warranties, may it be expressed or implied, therefore negates all
|
||||
other warranties. Furthermore, Guavy LLC does not make any representations
|
||||
concerning the accuracy or reliability of the use of the materials on its
|
||||
Website or otherwise relating to such materials or any sites linked to
|
||||
this Website.
|
||||
</p>
|
||||
|
||||
<h2>4. Limitations</h2>
|
||||
|
||||
<p>
|
||||
Guavy LLC or its suppliers will not be hold accountable for any damages
|
||||
that will arise with the use or inability to use the materials on Guavy
|
||||
LLC’s Website, even if Guavy LLC or an authorize representative of this
|
||||
Website has been notified, orally or written, of the possibility of such
|
||||
damage. Some jurisdiction does not allow limitations on implied warranties
|
||||
or limitations of liability for incidental damages, these limitations may
|
||||
not apply to you.
|
||||
</p>
|
||||
|
||||
<h2>5. Revisions and Errata</h2>
|
||||
|
||||
<p>
|
||||
The materials appearing on Guavy LLC’s Website may include technical,
|
||||
typographical, or photographic errors. Guavy LLC will not promise that any
|
||||
of the materials in this Website are accurate, complete, or current. Guavy
|
||||
LLC may change the materials contained on its Website at any time without
|
||||
notice. Guavy LLC does not make any commitment to update the materials.
|
||||
</p>
|
||||
|
||||
<h2>6. Links</h2>
|
||||
|
||||
<p>
|
||||
Guavy LLC has not reviewed all of the sites linked to its Website and is
|
||||
not responsible for the contents of any such linked site. The presence of
|
||||
any link does not imply endorsement by Guavy LLC of the site. The use of
|
||||
any linked website is at the user’s own risk.
|
||||
</p>
|
||||
|
||||
<h2>7. Site Terms of Use Modifications</h2>
|
||||
|
||||
<p>
|
||||
Guavy LLC may revise these Terms of Use for its Website at any time
|
||||
without prior notice. By using this Website, you are agreeing to be bound
|
||||
by the current version of these Terms and Conditions of Use.
|
||||
</p>
|
||||
|
||||
<h2>8. Your Privacy</h2>
|
||||
|
||||
<p>Please read our <nuxt-link to="/privacy"> Privacy Policy</nuxt-link>.</p>
|
||||
|
||||
<h2>9. Governing Law</h2>
|
||||
|
||||
<p>
|
||||
Any claim related to Guavy LLC's Website shall be governed by the laws of
|
||||
us without regards to its conflict of law provisions.
|
||||
</p>
|
||||
|
||||
<h2>10. Content</h2>
|
||||
|
||||
<p>
|
||||
When you upload text, software, mods, scripts, graphics, photos, audio,
|
||||
videos, links, interactive features and other materials that may be viewed
|
||||
on, or accessed through Modrinth, we refer to it as “Content”.
|
||||
</p>
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
You must own or have the necessary licenses, rights, consents, and
|
||||
permissions to store, share or distribute the Content that is uploaded
|
||||
under your Modrinth account.
|
||||
</li>
|
||||
<li>
|
||||
You are responsible for all activity and Content that is uploaded under
|
||||
your Modrinth account.
|
||||
</li>
|
||||
<li>
|
||||
You must not transmit any viruses, worms, malware, or any other code of
|
||||
a destructive nature through Modrinth.
|
||||
</li>
|
||||
<li>
|
||||
You retain all of your ownership rights to your Content. We do not claim
|
||||
any ownership in or to any of your Content.
|
||||
</li>
|
||||
<li>
|
||||
To enable us to provide the services of Modrinth, you hereby grant us a
|
||||
worldwide, non-exclusive, royalty-free, and unrestricted license to use,
|
||||
reproduce, distribute copies, prepare derivative works of, or display
|
||||
Content in connection with Modrinth in any medium and for any purpose
|
||||
(including commercial purposes), which is irrevocable.
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
auth: false,
|
||||
layout: 'home',
|
||||
head: {
|
||||
title: 'TOS - Modrinth',
|
||||
meta: [
|
||||
{
|
||||
hid: 'description',
|
||||
name: 'description',
|
||||
content:
|
||||
'The Terms of Service of Modrinth, an open source modding platform. Modrinth currently supports Minecraft, including the forge and fabric mod loaders.',
|
||||
},
|
||||
{
|
||||
hid: 'apple-mobile-web-app-title',
|
||||
name: 'apple-mobile-web-app-title',
|
||||
content: 'Terms of Service',
|
||||
},
|
||||
{
|
||||
hid: 'og:title',
|
||||
name: 'og:title',
|
||||
content: 'Terms of Service',
|
||||
},
|
||||
{
|
||||
hid: 'og:url',
|
||||
name: 'og:url',
|
||||
content: `https://modrinth.com/tos`,
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.main {
|
||||
margin: 0 auto;
|
||||
max-width: 800px;
|
||||
}
|
||||
</style>
|
||||
@@ -1,49 +1,94 @@
|
||||
<template>
|
||||
<div class="content">
|
||||
<div class="user-profile">
|
||||
<img :src="user.avatar_url" :alt="user.username" />
|
||||
<div class="info">
|
||||
<h1>{{ user.username }}</h1>
|
||||
<p class="joined-text">Joined {{ $dayjs(user.created).fromNow() }}</p>
|
||||
<p v-if="user.bio" class="bio">{{ user.bio }}</p>
|
||||
<p v-if="user.role === 'admin'" class="badge red">Admin</p>
|
||||
<p v-if="user.role === 'moderator'" class="badge yellow">Moderator</p>
|
||||
<p v-if="user.role === 'developer'" class="badge green">Developer</p>
|
||||
<div class="page-container">
|
||||
<div class="page-contents">
|
||||
<div class="sidebar-l">
|
||||
<div class="card">
|
||||
<div class="user-info">
|
||||
<img :src="user.avatar_url" :alt="user.username" />
|
||||
<div class="text">
|
||||
<h2>{{ user.username }}</h2>
|
||||
<p v-if="user.role === 'admin'" class="badge red">Admin</p>
|
||||
<p v-if="user.role === 'moderator'" class="badge yellow">
|
||||
Moderator
|
||||
</p>
|
||||
<p v-if="user.role === 'developer'" class="badge green">
|
||||
Developer
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<p v-if="user.bio" class="bio">{{ user.bio }}</p>
|
||||
</div>
|
||||
<div class="card stats">
|
||||
<div class="stat">
|
||||
<CalendarIcon />
|
||||
<div class="info">
|
||||
<h4>Created</h4>
|
||||
<p
|
||||
v-tooltip="
|
||||
$dayjs(user.created).format(
|
||||
'[Created on] YYYY-MM-DD [at] HH:mm A'
|
||||
)
|
||||
"
|
||||
class="value"
|
||||
>
|
||||
{{ $dayjs(user.created).fromNow() }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="stat">
|
||||
<DownloadIcon />
|
||||
<div class="info">
|
||||
<h4>Downloads</h4>
|
||||
<p class="value">
|
||||
{{ sumDownloads() }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<m-footer class="footer" />
|
||||
</div>
|
||||
<div class="content">
|
||||
<client-only>
|
||||
<EthicalAd />
|
||||
</client-only>
|
||||
<div class="mods">
|
||||
<SearchResult
|
||||
v-for="result in mods"
|
||||
:id="result.mod_id"
|
||||
:key="result.mod_id"
|
||||
:name="result.title"
|
||||
:description="result.description"
|
||||
:created-at="result.published"
|
||||
:updated-at="result.updated"
|
||||
:downloads="result.downloads.toString()"
|
||||
:icon-url="result.icon_url"
|
||||
:author-url="result.author_url"
|
||||
:categories="result.categories"
|
||||
:is-modrinth="true"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<client-only>
|
||||
<EthicalAd />
|
||||
</client-only>
|
||||
<div class="user-mods">
|
||||
<SearchResult
|
||||
v-for="result in mods"
|
||||
:id="result.mod_id"
|
||||
:key="result.mod_id"
|
||||
:name="result.title"
|
||||
:description="result.description"
|
||||
:latest-version="result.versions[0]"
|
||||
:created-at="result.published"
|
||||
:updated-at="result.updated"
|
||||
:downloads="result.downloads.toString()"
|
||||
:icon-url="result.icon_url"
|
||||
:author-url="result.author_url"
|
||||
:page-url="result.page_url"
|
||||
:categories="result.categories"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import axios from 'axios'
|
||||
import SearchResult from '@/components/ModResult'
|
||||
import SearchResult from '@/components/ProjectCard'
|
||||
import EthicalAd from '@/components/EthicalAd'
|
||||
import MFooter from '@/components/MFooter'
|
||||
|
||||
import CalendarIcon from '~/assets/images/utils/calendar.svg?inline'
|
||||
import DownloadIcon from '~/assets/images/utils/download.svg?inline'
|
||||
|
||||
export default {
|
||||
auth: false,
|
||||
components: {
|
||||
EthicalAd,
|
||||
SearchResult,
|
||||
CalendarIcon,
|
||||
DownloadIcon,
|
||||
MFooter,
|
||||
},
|
||||
async asyncData(data) {
|
||||
let res = await axios.get(
|
||||
@@ -67,56 +112,61 @@ export default {
|
||||
user,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
formatNumber(x) {
|
||||
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',')
|
||||
},
|
||||
sumDownloads() {
|
||||
let sum = 0
|
||||
|
||||
for (const mod of this.mods) {
|
||||
sum += mod.downloads
|
||||
}
|
||||
|
||||
return this.formatNumber(sum)
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.user-profile {
|
||||
@media screen and (min-width: 900px) {
|
||||
display: inline-flex;
|
||||
text-align: left;
|
||||
.sidebar-l {
|
||||
min-width: 21rem;
|
||||
|
||||
.user-info {
|
||||
@extend %row;
|
||||
img {
|
||||
width: 6rem;
|
||||
height: 6rem;
|
||||
margin-right: var(--spacing-card-md);
|
||||
border-radius: var(--size-rounded-icon);
|
||||
}
|
||||
.text {
|
||||
h2 {
|
||||
margin: 0;
|
||||
color: var(--color-text-dark);
|
||||
font-size: var(--font-size-lg);
|
||||
}
|
||||
.badge {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
}
|
||||
text-align: center;
|
||||
margin-bottom: 20px;
|
||||
margin-left: 15px;
|
||||
.stats {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
.stat {
|
||||
@extend %stat;
|
||||
|
||||
img {
|
||||
border-radius: var(--size-rounded-md);
|
||||
width: 250px;
|
||||
height: 250px;
|
||||
}
|
||||
|
||||
.info {
|
||||
@media screen and (min-width: 900px) {
|
||||
margin-left: 15px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.joined-text {
|
||||
margin-top: 5px;
|
||||
color: var(--color-grey-4);
|
||||
}
|
||||
|
||||
.bio {
|
||||
margin-top: 5px;
|
||||
font-size: 16pt;
|
||||
}
|
||||
|
||||
.badge {
|
||||
display: inline-block;
|
||||
svg {
|
||||
padding: 0.25rem;
|
||||
border-radius: 50%;
|
||||
background-color: var(--color-button-bg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.user-mods {
|
||||
border-top: 1px solid var(--color-grey-1);
|
||||
padding-top: 10px;
|
||||
margin: 10px;
|
||||
* {
|
||||
margin-left: 0;
|
||||
}
|
||||
.mods {
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user