Corresponding frontend PR to modrinth/labrinth#449 (#763)

Co-authored-by: Prospector <prospectordev@gmail.com>
Fixes https://github.com/modrinth/knossos/issues/597
Fixes https://github.com/modrinth/knossos/issues/593
Fixes https://github.com/modrinth/knossos/issues/768
This commit is contained in:
triphora
2022-12-14 19:36:28 -05:00
committed by GitHub
parent 03a6b0311f
commit 520f9801be
10 changed files with 298 additions and 127 deletions

View File

@@ -16,5 +16,6 @@ module.exports = {
plugins: ['prettier'],
rules: {
'no-console': 'off',
'vue/no-v-html': 'off',
},
}

1
.gitignore vendored
View File

@@ -95,3 +95,4 @@ sw.*
# pnpm files
pnpm-lock.yaml
/.npmrc

View File

@@ -34,6 +34,7 @@
&:focus-visible,
&:hover {
color: var(--color-link-hover);
cursor: pointer;
}
&:active {
@@ -1001,6 +1002,11 @@ h1 {
.label__description {
display: block;
margin-block-end: var(--spacing-card-sm);
.label__subdescription {
display: block;
margin-block-start: var(--spacing-card-md);
}
}
}
}
@@ -1061,8 +1067,12 @@ h1 {
width: 15rem;
}
input + *, .input-group + * {
margin-block-start: var(--spacing-card-md);
>,
.extend-styling> {
input + *,
.input-group + * {
margin-block-start: var(--spacing-card-md);
}
}
button, .button, .iconified-button {

View File

@@ -181,9 +181,7 @@ Questions? [Join the Modrinth Discord for support!](https://discord.gg/EUHuJHt)`
categories: [],
client_side: this.getClientSide(),
server_side: this.getServerSide(),
license_id: this.$tag.licenses.map((it) => it.short).includes('arr')
? 'arr'
: this.$tag.licenses[0].short,
license_id: 'LicenseRef-All-Rights-Reserved',
is_draft: true,
})
)

View File

@@ -346,7 +346,6 @@ export default {
categories,
loaders,
gameVersions,
licenses,
donationPlatforms,
reportTypes,
] = (
@@ -354,7 +353,6 @@ export default {
axios.get(`${API_URL}tag/category`, headers),
axios.get(`${API_URL}tag/loader`, headers),
axios.get(`${API_URL}tag/game_version`, headers),
axios.get(`${API_URL}tag/license`, headers),
axios.get(`${API_URL}tag/donation_platform`, headers),
axios.get(`${API_URL}tag/report_type`, headers),
])
@@ -363,7 +361,6 @@ export default {
state.categories = categories
state.loaders = loaders
state.gameVersions = gameVersions
state.licenses = licenses
state.donationPlatforms = donationPlatforms
state.reportTypes = reportTypes

View File

@@ -1,5 +1,13 @@
<template>
<div>
<Modal
ref="modal_license"
:header="project.license.name ? project.license.name : 'License'"
>
<div class="modal-license">
<div class="markdown-body" v-html="$xss($md.render(licenseText))" />
</div>
</Modal>
<ModalReport
ref="modal_project_report"
:item-id="project.id"
@@ -471,15 +479,25 @@
<div class="infos">
<div class="info">
<div class="key">License</div>
<div class="value uppercase">
<div class="value lowercase">
<a
v-if="project.license.url"
class="text-link"
:href="project.license.url"
>
{{ project.license.id }}
{{ licenseIdDisplay }}
</a>
<span v-else>{{ project.license.id }}</span>
<a
v-else-if="
project.license.id === 'LicenseRef-All-Rights-Reserved' ||
!project.license.id.includes('LicenseRef')
"
class="text-link"
@click="getLicenseData()"
>
{{ licenseIdDisplay }}
</a>
<span v-else>{{ licenseIdDisplay }}</span>
</div>
</div>
<div
@@ -812,15 +830,25 @@
<div class="infos">
<div class="info">
<div class="key">License</div>
<div class="value uppercase">
<div class="value lowercase">
<a
v-if="project.license.url"
class="text-link"
:href="project.license.url"
>
{{ project.license.id }}
{{ licenseIdDisplay }}
</a>
<span v-else>{{ project.license.id }}</span>
<a
v-else-if="
project.license.id === 'LicenseRef-All-Rights-Reserved' ||
!project.license.id.includes('LicenseRef')
"
class="text-link"
@click="getLicenseData()"
>
{{ licenseIdDisplay }}
</a>
<span v-else>{{ licenseIdDisplay }}</span>
</div>
</div>
<div
@@ -883,6 +911,7 @@ import ChevronRightIcon from '~/assets/images/utils/chevron-right.svg?inline'
import Advertisement from '~/components/ads/Advertisement'
import Badge from '~/components/ui/Badge'
import Categories from '~/components/ui/search/Categories'
import Modal from '~/components/ui/Modal'
import ModalReport from '~/components/ui/ModalReport'
import NavRow from '~/components/ui/NavRow'
import CopyCode from '~/components/ui/CopyCode'
@@ -895,6 +924,7 @@ export default {
NavRow,
Badge,
Advertisement,
Modal,
ModalReport,
IssuesIcon,
DownloadIcon,
@@ -1052,6 +1082,7 @@ export default {
data() {
return {
showKnownErrors: false,
licenseText: '',
}
},
fetch() {
@@ -1105,7 +1136,7 @@ export default {
this.project.status === 'approved' ||
this.project.status === 'archived'
? 'all'
: 'noindeex',
: 'noindex',
},
],
}
@@ -1120,6 +1151,17 @@ export default {
this.loaders
)
},
licenseIdDisplay() {
const id = this.project.license.id
if (id === 'LicenseRef-All-Rights-Reserved') {
return 'ARR'
} else if (id.includes('LicenseRef')) {
return id.replaceAll('LicenseRef-', '').replaceAll('-', ' ')
} else {
return id
}
},
},
methods: {
findPrimary(version) {
@@ -1193,6 +1235,18 @@ export default {
this.$nuxt.$loading.finish()
}
},
async getLicenseData() {
try {
const text = await this.$axios.get(
`tag/license/${this.project.license.id}`
)
this.licenseText = text.data.body
} catch {
this.licenseText = 'License text could not be retrieved.'
}
this.$refs.modal_license.show()
},
},
}
</script>
@@ -1442,4 +1496,8 @@ export default {
margin-bottom: var(--spacing-card-xs);
font-size: 1.125rem;
}
.modal-license {
padding: var(--spacing-card-bg);
}
</style>

View File

@@ -53,13 +53,7 @@
<li v-if="!savingAsDraft && project.versions.length < 1">
Your project must have at least one version to submit for review.
</li>
<li
v-if="
license === null || license_url === null || license_url === ''
"
>
Your project must have a license.
</li>
<li v-if="license.short === ''">Your project must have a license.</li>
</ul>
</div>
</header>
@@ -350,6 +344,9 @@
type="url"
maxlength="2048"
placeholder="Enter a valid URL"
:disabled="
(currentMember.permissions & EDIT_DETAILS) !== EDIT_DETAILS
"
/>
</label>
<label
@@ -382,55 +379,106 @@
/>
</label>
</section>
<section class="card license">
<div class="title">
<h3>License<span class="required">*</span></h3>
</div>
<label>
<span>
It is very important to choose a proper license for your mod. You may
choose one from our list or provide a URL to a custom license.
<br />
Confused? See our
<a
href="https://blog.modrinth.com/licensing-guide/"
target="_blank"
rel="noopener noreferrer"
class="text-link"
>
licensing guide</a
>
for more information.
</span>
<section class="universal-card license">
<h3>License<span class="required">*</span></h3>
<div class="adjacent-input">
<label for="license-multiselect">
<span class="label__description">
It is very important to choose a proper license for your mod. You
may choose one from our list or provide a custom license. You may
also provide a custom URL to your chosen license; otherwise, the
license text will be displayed.
<span
v-if="license && license.friendly === 'Custom'"
class="label__subdescription"
>
Enter a valid
<a
href="https://spdx.org/licenses/"
target="_blank"
rel="noopener noreferrer"
class="text-link"
>SPDX license identifier</a
>
in the marked area. If your license does not have a SPDX
identifier (for example, if you created the license yourself or if
the license is Minecraft-specific), simply check the box and enter
the name of the license instead.
</span>
<span class="label__subdescription">
Confused? See our
<a
href="https://blog.modrinth.com/licensing-guide/"
target="_blank"
rel="noopener noreferrer"
class="text-link"
>
licensing guide</a
>
for more information.
</span>
</span>
</label>
<div class="legacy-input-group">
<Multiselect
id="license-multiselect"
v-model="license"
placeholder="Choose license..."
placeholder="Select license..."
track-by="short"
label="short"
:options="$tag.licenses"
:custom-label="(value) => value.short.toUpperCase()"
label="friendly"
:options="defaultLicenses"
:searchable="true"
:close-on-select="true"
:show-labels="false"
:disabled="
(currentMember.permissions & EDIT_DETAILS) !== EDIT_DETAILS
"
/>
<input
v-model="license_url"
type="url"
maxlength="2048"
placeholder="License URL"
:class="{
'known-error': newProject.license_url === '' && showKnownErrors,
'known-error': license.short === '' && showKnownErrors,
}"
:disabled="
(currentMember.permissions & EDIT_DETAILS) !== EDIT_DETAILS
"
/>
<Checkbox
v-if="license.requiresOnlyOrLater"
v-model="allowOrLater"
:disabled="
(currentMember.permissions & EDIT_DETAILS) !== EDIT_DETAILS
"
>
Allow later editions of this license
</Checkbox>
<Checkbox
v-if="license.friendly === 'Custom'"
v-model="nonSpdxLicense"
:disabled="
(currentMember.permissions & EDIT_DETAILS) !== EDIT_DETAILS
"
>
License does not have a SPDX identifier
</Checkbox>
<input
v-if="license.friendly === 'Custom'"
v-model="license.short"
type="text"
maxlength="2048"
:placeholder="nonSpdxLicense ? 'License name' : 'SPDX identifier'"
:class="{
'known-error': license.short === '' && showKnownErrors,
}"
:disabled="
(currentMember.permissions & EDIT_DETAILS) !== EDIT_DETAILS
"
/>
<input
v-model="newProject.license.url"
type="url"
maxlength="2048"
placeholder="License URL (optional)"
:disabled="
(currentMember.permissions & EDIT_DETAILS) !== EDIT_DETAILS
"
/>
</div>
</label>
</div>
</section>
<section class="header-card donations">
<div class="header__row">
@@ -507,9 +555,11 @@ import RevertIcon from '~/assets/images/utils/undo.svg?inline'
import Chips from '~/components/ui/Chips'
import FileInput from '~/components/ui/FileInput'
import Avatar from '~/components/ui/Avatar'
import Checkbox from '~/components/ui/Checkbox'
export default {
components: {
Checkbox,
Avatar,
FileInput,
Chips,
@@ -542,8 +592,79 @@ export default {
clientSideType: '',
serverSideType: '',
license: { short: '', name: '' },
license_url: '',
defaultLicenses: [
{ friendly: 'Custom', short: '' },
{
friendly: 'All Rights Reserved/No License',
short: 'All-Rights-Reserved',
},
{ friendly: 'Apache License 2.0', short: 'Apache-2.0' },
{
friendly: 'BSD 2-Clause "Simplified" License',
short: 'BSD-2-Clause',
},
{
friendly: 'BSD 3-Clause "New" or "Revised" License',
short: 'BSD-3-Clause',
},
{
friendly: 'CC Zero (Public Domain equivalent)',
short: 'CC0-1.0',
},
{ friendly: 'CC-BY 4.0', short: 'CC-BY-4.0' },
{
friendly: 'CC-BY-SA 4.0',
short: 'CC-BY-SA-4.0',
},
{
friendly: 'CC-BY-NC 4.0',
short: 'CC-BY-NC-4.0',
},
{
friendly: 'CC-BY-NC-SA 4.0',
short: 'CC-BY-NC-SA-4.0',
},
{
friendly: 'CC-BY-ND 4.0',
short: 'CC-BY-ND-4.0',
},
{
friendly: 'CC-BY-NC-ND 4.0',
short: 'CC-BY-NC-ND-4.0',
},
{
friendly: 'GNU Affero General Public License v3',
short: 'AGPL-3.0',
requiresOnlyOrLater: true,
},
{
friendly: 'GNU Lesser General Public License v2.1',
short: 'LGPL-2.1',
requiresOnlyOrLater: true,
},
{
friendly: 'GNU Lesser General Public License v3',
short: 'LGPL-3.0',
requiresOnlyOrLater: true,
},
{
friendly: 'GNU General Public License v2',
short: 'GPL-2.0',
requiresOnlyOrLater: true,
},
{
friendly: 'GNU General Public License v3',
short: 'GPL-3.0',
requiresOnlyOrLater: true,
},
{ friendly: 'ISC License', short: 'ISC' },
{ friendly: 'MIT License', short: 'MIT' },
{ friendly: 'Mozilla Public License 2.0', short: 'MPL-2.0' },
{ friendly: 'zlib License', short: 'Zlib' },
],
license: { friendly: '', short: '', requiresOnlyOrLater: false },
allowOrLater: false,
nonSpdxLicense: false,
donationPlatforms: [],
donationLinks: [],
@@ -571,8 +692,6 @@ export default {
fetch() {
this.newProject = this.project
this.newProject.license.short = this.newProject.license.id
if (this.newProject.donation_urls) {
for (const platform of this.newProject.donation_urls) {
this.donationPlatforms.push({
@@ -583,12 +702,16 @@ export default {
}
}
this.license = {
short: this.newProject.license.id,
name: this.newProject.license.name,
}
this.license_url = this.newProject.license.url
const licenseId = this.newProject.license.id
const trimmedLicenseId = licenseId
.replaceAll('-only', '')
.replaceAll('-or-later', '')
.replaceAll('LicenseRef-', '')
this.license = this.defaultLicenses.find(
(x) => x.short === trimmedLicenseId
) ?? { friendly: 'Custom', short: licenseId.replaceAll('LicenseRef-', '') }
this.allowOrLater = licenseId.includes('-or-later')
this.nonSpdxLicense = licenseId.includes('LicenseRef-')
this.clientSideType =
this.newProject.client_side.charAt(0) +
@@ -599,22 +722,21 @@ export default {
this.setCategories()
},
watch: {
license(newValue, oldValue) {
if (newValue == null) {
this.license_url = ''
return
}
computed: {
licenseId() {
let id = ''
switch (newValue.short) {
case 'custom':
if (oldValue === null || oldValue.short !== '') {
this.license_url = ''
}
break
default:
this.license_url = `https://cdn.modrinth.com/licenses/${newValue.short}.txt`
}
if (this.nonSpdxLicense || this.license.short === 'All-Rights-Reserved')
id += 'LicenseRef-'
id += this.license.short
if (this.license.requiresOnlyOrLater)
id += this.allowOrLater ? 'or-later' : '-only'
if (this.nonSpdxLicense) id.replaceAll(' ', '-')
return id
},
},
created() {
@@ -652,9 +774,7 @@ export default {
this.newProject.title !== '' &&
this.newProject.description !== '' &&
this.newProject.slug !== '' &&
this.license.short !== null &&
this.license_url !== null &&
this.license_url !== ''
this.license.short !== ''
) {
if (this.savingAsDraft) {
return true
@@ -695,15 +815,16 @@ export default {
? this.newProject.source_url
: null,
wiki_url: this.newProject.wiki_url ? this.newProject.wiki_url : null,
license_url: this.license_url,
license_url: this.newProject.license.url
? this.newProject.license.url
: null,
discord_url: this.newProject.discord_url
? this.newProject.discord_url
: null,
license_id: this.license.short,
license_id: this.licenseId,
client_side: this.clientSideType.toLowerCase(),
server_side: this.serverSideType.toLowerCase(),
slug: this.newProject.slug,
license: this.license.short,
donation_urls: this.donationPlatforms.map((it, index) => {
return {
id: it.short,
@@ -733,10 +854,11 @@ export default {
)
}
this.newProject.license = {
id: this.newProject.license.short,
url: this.newProject.license.url,
}
// While the emit below will take care of most changes,
// some items require manually updating
this.newProject.license.id = this.licenseId
this.newProject.client_side = this.clientSideType.toLowerCase()
this.newProject.server_side = this.serverSideType.toLowerCase()
this.$emit('update:project', this.newProject)
@@ -795,6 +917,7 @@ label {
input,
.multiselect,
.checkbox,
.legacy-input-group {
flex: 3;
height: fit-content;
@@ -808,6 +931,11 @@ label {
* {
margin-bottom: var(--spacing-card-sm);
}
.multiselect {
width: unset;
height: inherit;
}
}
.resizable-textarea-wrapper textarea {
@@ -966,15 +1094,6 @@ section.donations {
}
}
.legacy-input-group {
display: flex;
flex-direction: column;
* {
margin-bottom: var(--spacing-card-sm);
}
}
.text-input-wrapper {
width: 100%;
display: flex;

View File

@@ -20,7 +20,7 @@
>
<button
:disabled="
selectedLicenses.length === 0 &&
onlyOpenSource === false &&
selectedEnvironments.length === 0 &&
selectedVersions.length === 0 &&
facets.length === 0 &&
@@ -195,17 +195,12 @@
placeholder="Choose versions..."
@input="onSearchChange(1)"
></multiselect>
<h3 class="sidebar-menu-heading">Licenses</h3>
<Multiselect
v-model="selectedLicenses"
placeholder="Choose licenses..."
:loading="$tag.licenses.length === 0"
:options="$tag.licenses.map((x) => x.short.toUpperCase())"
:multiple="true"
:searchable="true"
:close-on-select="false"
:show-labels="false"
:allow-empty="true"
<h3 class="sidebar-menu-heading">Open source</h3>
<Checkbox
v-model="onlyOpenSource"
label="Open source only"
style="margin-bottom: 0.5rem"
:border="false"
@input="onSearchChange(1)"
/>
</div>
@@ -429,7 +424,7 @@ export default {
return {
query: '',
selectedLicenses: [],
onlyOpenSource: false,
showSnapshots: false,
selectedVersions: [],
@@ -478,7 +473,7 @@ export default {
if (this.$route.query.v)
this.selectedVersions = this.$route.query.v.split(',')
if (this.$route.query.l)
this.selectedLicenses = this.$route.query.l.split(',')
this.onlyOpenSource = this.$route.query.l === 'true'
if (this.$route.query.h) this.showSnapshots = this.$route.query.h === 'true'
if (this.$route.query.e)
this.selectedEnvironments = this.$route.query.e.split(',')
@@ -608,7 +603,7 @@ export default {
for (const facet of [...this.orFacets])
await this.toggleOrFacet(facet, true)
this.selectedLicenses = []
this.onlyOpenSource = false
this.selectedVersions = []
this.selectedEnvironments = []
await this.onSearchChange(1)
@@ -719,13 +714,7 @@ export default {
formattedFacets.push(versionFacets)
}
if (this.selectedLicenses.length > 0) {
const licenseFacets = []
for (const facet of this.selectedLicenses) {
licenseFacets.push('license:' + facet.toLowerCase())
}
formattedFacets.push(licenseFacets)
}
if (this.onlyOpenSource) formattedFacets.push(['open_source:true'])
if (this.selectedEnvironments.length > 0) {
let environmentFacets = []
@@ -800,8 +789,7 @@ export default {
queryItems.push(`g=${encodeURIComponent(this.orFacets)}`)
if (this.selectedVersions.length > 0)
queryItems.push(`v=${encodeURIComponent(this.selectedVersions)}`)
if (this.selectedLicenses.length > 0)
queryItems.push(`l=${encodeURIComponent(this.selectedLicenses)}`)
if (this.onlyOpenSource) queryItems.push(`l=true`)
if (this.showSnapshots) queryItems.push(`h=true`)
if (this.selectedEnvironments.length > 0)
queryItems.push(`e=${encodeURIComponent(this.selectedEnvironments)}`)

View File

@@ -25,7 +25,7 @@
Program. Setup a method of receiving payments below to enable
monetization.
</p>
<div class="enroll">
<div class="enroll extend-styling">
<Chips
v-model="selectedWallet"
:starting-value="selectedWallet"

View File

@@ -4,7 +4,6 @@ export const state = () => ({
categories: tags.categories,
loaders: tags.loaders,
gameVersions: tags.gameVersions,
licenses: tags.licenses,
donationPlatforms: tags.donationPlatforms,
reportTypes: tags.reportTypes,
projectTypes: [