You've already forked AstralRinth
forked from didirus/AstralRinth
Nuxt Season Finale (#531)
Co-authored-by: Emma Cypress Pointer-Null <emmaffle@modrinth.com>
This commit is contained in:
@@ -1,10 +1,3 @@
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
|
||||
stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg">
|
||||
<circle cx="12" cy="12" r="10"></circle>
|
||||
<line x1="14.31" y1="8" x2="20.05" y2="17.94"></line>
|
||||
<line x1="9.69" y1="8" x2="21.17" y2="8"></line>
|
||||
<line x1="7.38" y1="12" x2="13.12" y2="2.06"></line>
|
||||
<line x1="9.69" y1="16" x2="3.95" y2="6.06"></line>
|
||||
<line x1="14.31" y1="16" x2="2.83" y2="16"></line>
|
||||
<line x1="16.62" y1="12" x2="10.88" y2="21.94"></line>
|
||||
</svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M3 6l3 1m0 0l-3 9a5.002 5.002 0 006.001 0M6 7l3 9M6 7l6-2m6 2l3-1m-3 1l-3 9a5.002 5.002 0 006.001 0M18 7l3 9m-3-9l-6-2m0-2v2m0 16V5m0 16H9m3 0h3" />
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 552 B After Width: | Height: | Size: 342 B |
4
assets/images/utils/check-circle.svg
Normal file
4
assets/images/utils/check-circle.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M12 22c5.523 0 10-4.477 10-10S17.523 2 12 2 2 6.477 2 12s4.477 10 10 10z"></path>
|
||||
<path d="m9 12 2 2 4-4"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 315 B |
5
assets/images/utils/clear.svg
Normal file
5
assets/images/utils/clear.svg
Normal file
@@ -0,0 +1,5 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="12" cy="12" r="10"></circle>
|
||||
<line x1="15" y1="9" x2="9" y2="15"></line>
|
||||
<line x1="9" y1="9" x2="15" y2="15"></line>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 323 B |
@@ -454,6 +454,15 @@
|
||||
border-top-right-radius: var(--size-rounded-card) !important;
|
||||
}
|
||||
|
||||
.known-error .multiselect__tags {
|
||||
border-color: var(--color-badge-red-bg) !important;
|
||||
background-color: var(--color-warning-bg) !important;
|
||||
|
||||
&::placeholder {
|
||||
color: var(--color-warning-text);
|
||||
}
|
||||
}
|
||||
|
||||
.multiselect {
|
||||
color: var(--color-text) !important;
|
||||
|
||||
@@ -554,6 +563,7 @@ label {
|
||||
|
||||
@media screen and (min-width: 1024px) {
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
span {
|
||||
@@ -814,6 +824,26 @@ label {
|
||||
}
|
||||
}
|
||||
|
||||
.vue-notification {
|
||||
background: #44A4FC;
|
||||
border-left: 5px solid #44A4FC;
|
||||
|
||||
&.success {
|
||||
background: #68CD86;
|
||||
border-left-color: #68CD86;
|
||||
}
|
||||
|
||||
&.warn {
|
||||
background: #ffb648;
|
||||
border-left-color: #ffb648;
|
||||
}
|
||||
|
||||
&.error {
|
||||
background: #E54D42;
|
||||
border-left-color: #E54D42;
|
||||
}
|
||||
}
|
||||
|
||||
.vue-notification-group {
|
||||
right: 25px !important;
|
||||
|
||||
@@ -839,3 +869,17 @@ label {
|
||||
height: 1px;
|
||||
margin: var(--spacing-card-bg) 0;
|
||||
}
|
||||
|
||||
input.known-error,
|
||||
textarea.known-error {
|
||||
border-color: var(--color-badge-red-bg) !important;
|
||||
background-color: var(--color-warning-bg) !important;
|
||||
|
||||
&::placeholder {
|
||||
color: var(--color-warning-text);
|
||||
}
|
||||
}
|
||||
|
||||
.known-errors {
|
||||
color: var(--color-badge-red-bg);
|
||||
}
|
||||
|
||||
@@ -247,7 +247,7 @@ h2 {
|
||||
|
||||
h3 {
|
||||
margin-top: 0.5rem;
|
||||
margin-bottom: 0;
|
||||
margin-bottom: 0.25rem;
|
||||
color: var(--color-text-dark);
|
||||
}
|
||||
|
||||
@@ -258,6 +258,7 @@ button {
|
||||
|
||||
input {
|
||||
border-radius: 2rem;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
pre {
|
||||
|
||||
@@ -1,88 +0,0 @@
|
||||
<template>
|
||||
<label class="button" @drop.prevent="addFile" @dragover.prevent>
|
||||
<span>
|
||||
{{ text }}
|
||||
</span>
|
||||
<input
|
||||
type="file"
|
||||
:multiple="multiple"
|
||||
:accept="accept"
|
||||
@change="onChange"
|
||||
/>
|
||||
</label>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'FileInput',
|
||||
props: {
|
||||
prompt: {
|
||||
type: String,
|
||||
default: 'Select file',
|
||||
},
|
||||
multiple: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
accept: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
text: this.prompt,
|
||||
files: [],
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onChange(files, shouldNotReset) {
|
||||
if (!shouldNotReset) this.files = files.target.files
|
||||
|
||||
const length = this.files.length
|
||||
if (length === 0) {
|
||||
this.text = this.prompt
|
||||
} else if (length === 1) {
|
||||
this.text = '1 file selected'
|
||||
} else if (length > 1) {
|
||||
this.text = length + ' files selected'
|
||||
}
|
||||
this.$emit('change', this.files)
|
||||
},
|
||||
addFile(e) {
|
||||
const droppedFiles = e.dataTransfer.files
|
||||
|
||||
if (!this.multiple) this.files = []
|
||||
|
||||
if (!droppedFiles) return
|
||||
;[...droppedFiles].forEach((f) => {
|
||||
this.files.push(f)
|
||||
})
|
||||
|
||||
if (!this.multiple && this.files.length > 0) this.files = [this.files[0]]
|
||||
|
||||
if (this.files.length > 0) this.onChange(null, true)
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
padding: var(--spacing-card-sm) var(--spacing-card-md);
|
||||
}
|
||||
|
||||
span {
|
||||
border: 2px dashed var(--color-divider-dark);
|
||||
border-radius: var(--size-rounded-control);
|
||||
padding: var(--spacing-card-md) var(--spacing-card-lg);
|
||||
}
|
||||
|
||||
input {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
@@ -2,7 +2,7 @@
|
||||
<div class="columns">
|
||||
<label class="button" @drop.prevent="addFile" @dragover.prevent>
|
||||
<span>
|
||||
<UploadIcon />
|
||||
<UploadIcon v-if="showIcon" />
|
||||
{{ prompt }}
|
||||
</span>
|
||||
<input
|
||||
@@ -36,6 +36,14 @@ export default {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
maxSize: {
|
||||
type: Number,
|
||||
default: null,
|
||||
},
|
||||
showIcon: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@@ -46,7 +54,26 @@ export default {
|
||||
onChange(files, shouldNotReset) {
|
||||
if (!shouldNotReset) this.files = files.target.files
|
||||
|
||||
this.$emit('change', this.files)
|
||||
this.files = [...this.files].filter((file) => {
|
||||
if (this.maxSize === null) {
|
||||
return true
|
||||
} else if (file.size > this.maxSize) {
|
||||
console.log('File size: ' + file.size + ', max size: ' + this.maxSize)
|
||||
alert(
|
||||
'File ' +
|
||||
file.name +
|
||||
' is too big! Must be less than ' +
|
||||
this.$formatBytes(this.maxSize)
|
||||
)
|
||||
return false
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
})
|
||||
|
||||
if (this.files.length > 0) {
|
||||
this.$emit('change', this.files)
|
||||
}
|
||||
},
|
||||
addFile(e) {
|
||||
const droppedFiles = e.dataTransfer.files
|
||||
@@ -74,7 +101,6 @@ label {
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
padding: var(--spacing-card-sm) var(--spacing-card-md);
|
||||
margin-bottom: var(--spacing-card-sm);
|
||||
}
|
||||
|
||||
span {
|
||||
@@ -95,4 +121,17 @@ span {
|
||||
input {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.known-error label {
|
||||
border-color: var(--color-badge-red-bg) !important;
|
||||
background-color: var(--color-warning-bg) !important;
|
||||
|
||||
span {
|
||||
border-color: var(--color-badge-red-bg);
|
||||
}
|
||||
|
||||
&::placeholder {
|
||||
color: var(--color-warning-text);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -6,9 +6,8 @@
|
||||
<Multiselect
|
||||
v-if="getValidLoaders().length > 1"
|
||||
v-model="selectedLoader"
|
||||
:options="
|
||||
getValidLoaders().map((x) => x.charAt(0).toUpperCase() + x.slice(1))
|
||||
"
|
||||
:options="getValidLoaders()"
|
||||
:custom-label="(value) => value.charAt(0).toUpperCase() + value.slice(1)"
|
||||
:multiple="false"
|
||||
:searchable="false"
|
||||
:show-no-results="false"
|
||||
@@ -60,7 +59,7 @@
|
||||
updateVersionFilters()
|
||||
"
|
||||
>
|
||||
<CrossIcon />
|
||||
<ClearIcon />
|
||||
Clear filters
|
||||
</button>
|
||||
</div>
|
||||
@@ -69,14 +68,14 @@
|
||||
<script>
|
||||
import Multiselect from 'vue-multiselect'
|
||||
import Checkbox from '~/components/ui/Checkbox'
|
||||
import CrossIcon from '~/assets/images/utils/x.svg?inline'
|
||||
import ClearIcon from '~/assets/images/utils/clear.svg?inline'
|
||||
|
||||
export default {
|
||||
name: 'VersionFilterControl',
|
||||
components: {
|
||||
Multiselect,
|
||||
Checkbox,
|
||||
CrossIcon,
|
||||
ClearIcon,
|
||||
},
|
||||
props: {
|
||||
versions: {
|
||||
@@ -123,9 +122,10 @@ export default {
|
||||
(projectVersion) =>
|
||||
(this.selectedGameVersions.length === 0 ||
|
||||
this.selectedGameVersions.some((gameVersion) =>
|
||||
projectVersion.game_versions.includes(gameVersion.toLowerCase())
|
||||
projectVersion.game_versions.includes(gameVersion)
|
||||
)) &&
|
||||
projectVersion.loaders.includes(this.selectedLoader.toLowerCase())
|
||||
(this.selectedLoader === null ||
|
||||
projectVersion.loaders.includes(this.selectedLoader))
|
||||
)
|
||||
this.$emit('updateVersions', temp)
|
||||
},
|
||||
|
||||
@@ -56,6 +56,21 @@
|
||||
{{ $user.notifications.length }}
|
||||
</div>
|
||||
</nuxt-link>
|
||||
<nuxt-link
|
||||
v-if="
|
||||
$auth.user &&
|
||||
($auth.user.role === 'moderator' ||
|
||||
$auth.user.role === 'admin')
|
||||
"
|
||||
to="/moderation"
|
||||
class="control-button"
|
||||
title="Moderation"
|
||||
>
|
||||
<ModerationIcon aria-hidden="true" />
|
||||
<div v-if="moderationNotifications > 0" class="bubble">
|
||||
{{ moderationNotifications }}
|
||||
</div>
|
||||
</nuxt-link>
|
||||
<div v-if="$auth.user" ref="mobileMenu" class="dropdown">
|
||||
<button class="control" value="Profile Dropdown">
|
||||
<img
|
||||
@@ -230,6 +245,7 @@
|
||||
position="bottom right"
|
||||
:max="5"
|
||||
:ignore-duplicates="true"
|
||||
:duration="10000"
|
||||
/>
|
||||
<Nuxt id="main" />
|
||||
</main>
|
||||
@@ -237,16 +253,23 @@
|
||||
<div class="logo-info" role="region" aria-label="Modrinth information">
|
||||
<ModrinthLogo aria-hidden="true" class="text-logo" />
|
||||
<p>
|
||||
Modrinth is open source software. You may view the source code at
|
||||
Modrinth is
|
||||
<a
|
||||
target="_blank"
|
||||
href="https://github.com/modrinth/knossos"
|
||||
href="https://github.com/modrinth"
|
||||
class="text-link"
|
||||
>
|
||||
our GitHub page</a
|
||||
open source</a
|
||||
>.
|
||||
</p>
|
||||
<p>
|
||||
{{ owner }}/{{ slug }} {{ branch }}@<a
|
||||
target="_blank"
|
||||
:href="'https://github.com/' + owner + '/' + slug + '/tree/' + hash"
|
||||
class="text-link"
|
||||
>{{ hash.substring(0, 7) }}</a
|
||||
>
|
||||
</p>
|
||||
<p>{{ owner }}/{{ slug }} {{ branch }}@{{ hash.substring(0, 7) }}</p>
|
||||
<p>© Rinth, Inc.</p>
|
||||
</div>
|
||||
<div class="links links-1" role="region" aria-label="Legal">
|
||||
@@ -343,6 +366,7 @@ export default {
|
||||
hash: process.env.hash || 'unknown',
|
||||
isMobileMenuOpen: false,
|
||||
registeredSkipLink: null,
|
||||
moderationNotifications: 0,
|
||||
}
|
||||
},
|
||||
async fetch() {
|
||||
@@ -351,6 +375,19 @@ export default {
|
||||
this.$store.dispatch('tag/fetchAllTags'),
|
||||
this.$store.dispatch('cosmetics/fetchCosmetics', this.$cookies),
|
||||
])
|
||||
if (
|
||||
(this.$auth.user && this.$auth.user.role === 'moderator') ||
|
||||
this.$auth.user.role === 'admin'
|
||||
) {
|
||||
const [projects, reports] = (
|
||||
await Promise.all([
|
||||
this.$axios.get(`moderation/projects`, this.$auth.headers),
|
||||
this.$axios.get(`report`, this.$auth.headers),
|
||||
])
|
||||
).map((it) => it.data)
|
||||
|
||||
this.moderationNotifications = projects.length + reports.length
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
authUrl() {
|
||||
@@ -416,6 +453,16 @@ export default {
|
||||
removeFocus() {
|
||||
document.activeElement.blur() // This doesn't work, sadly. Help
|
||||
},
|
||||
async getModerationCount() {
|
||||
const [projects, reports] = (
|
||||
await Promise.all([
|
||||
this.$axios.get(`moderation/projects`, this.$auth.headers),
|
||||
this.$axios.get(`report`, this.$auth.headers),
|
||||
])
|
||||
).map((it) => it.data)
|
||||
|
||||
return projects.length + reports.length
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -271,7 +271,7 @@ export default {
|
||||
breaks: false,
|
||||
},
|
||||
loading: {
|
||||
color: 'green',
|
||||
color: '#1bd96a',
|
||||
height: '2px',
|
||||
},
|
||||
env: {
|
||||
|
||||
@@ -139,8 +139,11 @@
|
||||
</div>
|
||||
<div
|
||||
v-if="
|
||||
currentMember &&
|
||||
(project.status === 'processing' ||
|
||||
(currentMember ||
|
||||
($auth.user &&
|
||||
($auth.user.role === 'moderator' ||
|
||||
$auth.user.role === 'admin'))) &&
|
||||
(project.status !== 'approved' ||
|
||||
(project.moderator_message &&
|
||||
(project.moderator_message.message ||
|
||||
project.moderator_message.body)))
|
||||
@@ -193,22 +196,44 @@
|
||||
</div>
|
||||
<div class="buttons">
|
||||
<button
|
||||
v-if="
|
||||
project.status !== 'processing' && project.status !== 'approved'
|
||||
"
|
||||
class="iconified-button"
|
||||
v-if="project.status === 'rejected'"
|
||||
class="iconified-button brand-button-colors"
|
||||
@click="submitForReview"
|
||||
>
|
||||
Resubmit for approval
|
||||
<CheckIcon />
|
||||
Resubmit for review
|
||||
</button>
|
||||
<button
|
||||
v-if="project.status === 'draft'"
|
||||
class="iconified-button brand-button-colors"
|
||||
@click="submitForReview"
|
||||
>
|
||||
<CheckIcon />
|
||||
Submit for review
|
||||
</button>
|
||||
<button
|
||||
v-if="project.status === 'approved'"
|
||||
class="iconified-button"
|
||||
@click="clearMessage"
|
||||
>
|
||||
<ClearIcon />
|
||||
Clear message
|
||||
</button>
|
||||
</div>
|
||||
<div v-if="showKnownErrors" class="known-errors">
|
||||
<ul>
|
||||
<li v-if="project.body === ''">
|
||||
Your project must have a body to submit for review.
|
||||
</li>
|
||||
<li v-if="project.versions.length < 1">
|
||||
Your project must have at least one version to submit for review.
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<p v-if="project.status === 'rejected'">
|
||||
Do not resubmit for review until you've addressed the moderator
|
||||
message!
|
||||
</p>
|
||||
</div>
|
||||
<div class="extra-info card">
|
||||
<template
|
||||
@@ -344,6 +369,12 @@
|
||||
class="featured-version"
|
||||
>
|
||||
<a
|
||||
v-tooltip="
|
||||
findPrimary(version).filename +
|
||||
' (' +
|
||||
$formatBytes(findPrimary(version).size) +
|
||||
')'
|
||||
"
|
||||
:href="findPrimary(version).url"
|
||||
class="download"
|
||||
:title="`Download ${version.name}`"
|
||||
@@ -365,7 +396,11 @@
|
||||
>
|
||||
{{
|
||||
version.loaders
|
||||
.map((x) => x.charAt(0).toUpperCase() + x.slice(1))
|
||||
.map((x) =>
|
||||
x.toLowerCase() === 'modloader'
|
||||
? 'ModLoader'
|
||||
: x.charAt(0).toUpperCase() + x.slice(1)
|
||||
)
|
||||
.join(', ')
|
||||
}}
|
||||
{{ $formatVersion(version.game_versions) }}
|
||||
@@ -485,7 +520,7 @@
|
||||
<span>Changelog</span>
|
||||
</nuxt-link>
|
||||
<nuxt-link
|
||||
v-if="project.versions.length > 0"
|
||||
v-if="project.versions.length > 0 || currentMember"
|
||||
:to="`/${project.project_type}/${
|
||||
project.slug ? project.slug : project.id
|
||||
}/versions`"
|
||||
@@ -531,6 +566,8 @@
|
||||
|
||||
<script>
|
||||
import CalendarIcon from '~/assets/images/utils/calendar.svg?inline'
|
||||
import CheckIcon from '~/assets/images/utils/check.svg?inline'
|
||||
import ClearIcon from '~/assets/images/utils/clear.svg?inline'
|
||||
import DownloadIcon from '~/assets/images/utils/download.svg?inline'
|
||||
import UpdateIcon from '~/assets/images/utils/updated.svg?inline'
|
||||
import CodeIcon from '~/assets/images/sidebar/mod.svg?inline'
|
||||
@@ -556,6 +593,8 @@ export default {
|
||||
IssuesIcon,
|
||||
DownloadIcon,
|
||||
CalendarIcon,
|
||||
CheckIcon,
|
||||
ClearIcon,
|
||||
UpdateIcon,
|
||||
CodeIcon,
|
||||
ReportIcon,
|
||||
@@ -645,6 +684,11 @@ export default {
|
||||
})
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
showKnownErrors: false,
|
||||
}
|
||||
},
|
||||
head() {
|
||||
return {
|
||||
title: `${this.project.title} - ${
|
||||
@@ -737,28 +781,32 @@ export default {
|
||||
this.$nuxt.$loading.finish()
|
||||
},
|
||||
async submitForReview() {
|
||||
this.$nuxt.$loading.start()
|
||||
if (this.project.body === '' || this.project.versions.length < 1) {
|
||||
this.showKnownErrors = true
|
||||
} else {
|
||||
this.$nuxt.$loading.start()
|
||||
|
||||
try {
|
||||
await this.$axios.patch(
|
||||
`project/${this.project.id}`,
|
||||
{
|
||||
status: 'processing',
|
||||
},
|
||||
this.$auth.headers
|
||||
)
|
||||
try {
|
||||
await this.$axios.patch(
|
||||
`project/${this.project.id}`,
|
||||
{
|
||||
status: 'processing',
|
||||
},
|
||||
this.$auth.headers
|
||||
)
|
||||
|
||||
this.project.status = 'processing'
|
||||
} catch (err) {
|
||||
this.$notify({
|
||||
group: 'main',
|
||||
title: 'An error occurred',
|
||||
text: err.response.data.description,
|
||||
type: 'error',
|
||||
})
|
||||
this.project.status = 'processing'
|
||||
} catch (err) {
|
||||
this.$notify({
|
||||
group: 'main',
|
||||
title: 'An error occurred',
|
||||
text: err.response.data.description,
|
||||
type: 'error',
|
||||
})
|
||||
}
|
||||
|
||||
this.$nuxt.$loading.finish()
|
||||
}
|
||||
|
||||
this.$nuxt.$loading.finish()
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,46 +1,77 @@
|
||||
<template>
|
||||
<div class="page-contents">
|
||||
<header class="card columns">
|
||||
<h3 class="column-grow-1">Edit project</h3>
|
||||
<nuxt-link
|
||||
:to="`/${project.project_type}/${
|
||||
project.slug ? project.slug : project.id
|
||||
}/settings`"
|
||||
class="iconified-button column"
|
||||
>
|
||||
Back
|
||||
</nuxt-link>
|
||||
<button
|
||||
v-if="
|
||||
project.status === 'rejected' ||
|
||||
project.status === 'draft' ||
|
||||
project.status === 'unlisted'
|
||||
"
|
||||
title="Submit for approval"
|
||||
class="iconified-button column"
|
||||
:disabled="!$nuxt.$loading"
|
||||
@click="saveProjectReview"
|
||||
>
|
||||
Submit for approval
|
||||
</button>
|
||||
<button
|
||||
title="Save"
|
||||
class="iconified-button brand-button-colors column"
|
||||
:disabled="!$nuxt.$loading"
|
||||
@click="saveProject"
|
||||
>
|
||||
<CheckIcon />
|
||||
Save
|
||||
</button>
|
||||
<header class="card">
|
||||
<div class="columns">
|
||||
<h3 class="column-grow-1">Edit project</h3>
|
||||
<nuxt-link
|
||||
:to="`/${project.project_type}/${
|
||||
project.slug ? project.slug : project.id
|
||||
}/settings`"
|
||||
class="iconified-button column"
|
||||
>
|
||||
<CrossIcon />
|
||||
Cancel
|
||||
</nuxt-link>
|
||||
<button
|
||||
v-if="
|
||||
project.status === 'rejected' ||
|
||||
project.status === 'draft' ||
|
||||
project.status === 'unlisted'
|
||||
"
|
||||
title="Submit for review"
|
||||
class="iconified-button column"
|
||||
:disabled="!$nuxt.$loading"
|
||||
@click="saveProjectReview"
|
||||
>
|
||||
<CheckIcon />
|
||||
Submit for review
|
||||
</button>
|
||||
<button
|
||||
title="Save"
|
||||
class="iconified-button brand-button-colors column"
|
||||
:disabled="!$nuxt.$loading"
|
||||
@click="saveProjectNotForReview"
|
||||
>
|
||||
<SaveIcon />
|
||||
Save changes
|
||||
</button>
|
||||
</div>
|
||||
<div v-if="showKnownErrors" class="known-errors">
|
||||
<ul>
|
||||
<li v-if="newProject.title === ''">Your project must have a name.</li>
|
||||
<li v-if="newProject.description === ''">
|
||||
Your project must have a summary.
|
||||
</li>
|
||||
<li v-if="newProject.slug === ''">
|
||||
Your project must have a vanity URL.
|
||||
</li>
|
||||
<li v-if="!savingAsDraft && newProject.body === ''">
|
||||
Your project must have a body to submit for review.
|
||||
</li>
|
||||
<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>
|
||||
</ul>
|
||||
</div>
|
||||
</header>
|
||||
<section class="card essentials">
|
||||
<h3>Name<span class="required">*</span></h3>
|
||||
<label>
|
||||
<span>
|
||||
Be creative! Generic project names will be harder to search for.
|
||||
<h3>Name<span class="required">*</span></h3>
|
||||
<span>
|
||||
Be creative! Generic project names will be harder to search for.
|
||||
</span>
|
||||
</span>
|
||||
<input
|
||||
v-model="newProject.title"
|
||||
:class="{ 'known-error': newProject.title === '' && showKnownErrors }"
|
||||
type="text"
|
||||
placeholder="Enter the name"
|
||||
:disabled="
|
||||
@@ -48,14 +79,19 @@
|
||||
"
|
||||
/>
|
||||
</label>
|
||||
<h3>Summary<span class="required">*</span></h3>
|
||||
<label>
|
||||
<span>
|
||||
Give a short description of your project that will appear on search
|
||||
pages.
|
||||
<h3>Summary<span class="required">*</span></h3>
|
||||
<span>
|
||||
Give a short description of your project that will appear on search
|
||||
pages.
|
||||
</span>
|
||||
</span>
|
||||
<input
|
||||
v-model="newProject.description"
|
||||
:class="{
|
||||
'known-error': newProject.description === '' && showKnownErrors,
|
||||
}"
|
||||
type="text"
|
||||
placeholder="Enter the summary"
|
||||
:disabled="
|
||||
@@ -63,11 +99,13 @@
|
||||
"
|
||||
/>
|
||||
</label>
|
||||
<h3>Categories</h3>
|
||||
<label>
|
||||
<span class="no-padding">
|
||||
Select up to 3 categories that will help others <br />
|
||||
find your project.
|
||||
<span>
|
||||
<h3>Categories</h3>
|
||||
<span class="no-padding">
|
||||
Select up to 3 categories that will help others <br />
|
||||
find your project.
|
||||
</span>
|
||||
</span>
|
||||
<Multiselect
|
||||
id="categories"
|
||||
@@ -77,6 +115,9 @@
|
||||
.filter((x) => x.project_type === project.project_type)
|
||||
.map((it) => it.name)
|
||||
"
|
||||
:custom-label="
|
||||
(value) => value.charAt(0).toUpperCase() + value.slice(1)
|
||||
"
|
||||
:loading="$tag.categories.length === 0"
|
||||
:multiple="true"
|
||||
:searchable="false"
|
||||
@@ -93,23 +134,28 @@
|
||||
"
|
||||
/>
|
||||
</label>
|
||||
<h3>Vanity URL (slug)<span class="required">*</span></h3>
|
||||
<label>
|
||||
<label class="vertical-input">
|
||||
<span>
|
||||
Set this to something that will looks nice in your project's URL.
|
||||
<h3>Vanity URL (slug)<span class="required">*</span></h3>
|
||||
<span class="slug-description"
|
||||
>https://modrinth.com/{{ newProject.project_type.toLowerCase() }}/{{
|
||||
newProject.slug ? newProject.slug : 'your-slug'
|
||||
}}
|
||||
</span>
|
||||
</span>
|
||||
<input
|
||||
id="name"
|
||||
v-model="newProject.slug"
|
||||
:class="{ 'known-error': newProject.slug === '' && showKnownErrors }"
|
||||
type="text"
|
||||
placeholder="Enter the vanity URL slug"
|
||||
placeholder="Enter the vanity URL"
|
||||
:disabled="
|
||||
(currentMember.permissions & EDIT_DETAILS) !== EDIT_DETAILS
|
||||
"
|
||||
/>
|
||||
</label>
|
||||
</section>
|
||||
<section class="card project-icon rows">
|
||||
<section class="card project-icon">
|
||||
<h3>Icon</h3>
|
||||
<img
|
||||
:src="
|
||||
@@ -121,7 +167,9 @@
|
||||
"
|
||||
alt="preview-image"
|
||||
/>
|
||||
<file-input
|
||||
<SmartFileInput
|
||||
:max-size="262144"
|
||||
:show-icon="false"
|
||||
accept="image/png,image/jpeg,image/gif,image/webp"
|
||||
class="choose-image"
|
||||
prompt="Choose image or drag it here"
|
||||
@@ -142,15 +190,22 @@
|
||||
</button>
|
||||
</section>
|
||||
<section class="card game-sides">
|
||||
<h3>Supported environments</h3>
|
||||
<div class="columns">
|
||||
<span> Let others know what environments your project supports. </span>
|
||||
<div>
|
||||
<h3>Supported environments</h3>
|
||||
<span>
|
||||
Let others know what environments your project supports.
|
||||
</span>
|
||||
</div>
|
||||
<div class="labeled-control">
|
||||
<h3>Client<span class="required">*</span></h3>
|
||||
<Multiselect
|
||||
v-model="clientSideType"
|
||||
placeholder="Select one"
|
||||
:options="sideTypes"
|
||||
:custom-label="
|
||||
(value) => value.charAt(0).toUpperCase() + value.slice(1)
|
||||
"
|
||||
:searchable="false"
|
||||
:close-on-select="true"
|
||||
:show-labels="false"
|
||||
@@ -166,6 +221,9 @@
|
||||
v-model="serverSideType"
|
||||
placeholder="Select one"
|
||||
:options="sideTypes"
|
||||
:custom-label="
|
||||
(value) => value.charAt(0).toUpperCase() + value.slice(1)
|
||||
"
|
||||
:searchable="false"
|
||||
:close-on-select="true"
|
||||
:show-labels="false"
|
||||
@@ -183,19 +241,20 @@
|
||||
for="body"
|
||||
title="You can type an extended description of your project here."
|
||||
>
|
||||
Description
|
||||
Body<span class="required">*</span>
|
||||
</label>
|
||||
</h3>
|
||||
<span>
|
||||
You can type an extended description of your mod here. This editor
|
||||
supports Markdown. Its syntax can be found
|
||||
supports
|
||||
<a
|
||||
class="text-link"
|
||||
href="https://guides.github.com/features/mastering-markdown/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="text-link"
|
||||
>here</a
|
||||
>.
|
||||
>Markdown</a
|
||||
>. HTML can also be used inside your description, not including styles,
|
||||
scripts, and iframes (though YouTube iframes are allowed).
|
||||
</span>
|
||||
<ThisOrThat
|
||||
v-model="bodyViewMode"
|
||||
@@ -207,6 +266,9 @@
|
||||
<textarea
|
||||
id="body"
|
||||
v-model="newProject.body"
|
||||
:class="{
|
||||
'known-error': newProject.body === '' && showKnownErrors,
|
||||
}"
|
||||
:disabled="(currentMember.permissions & EDIT_BODY) !== EDIT_BODY"
|
||||
/>
|
||||
</div>
|
||||
@@ -297,10 +359,11 @@
|
||||
<div class="input-group">
|
||||
<Multiselect
|
||||
v-model="license"
|
||||
placeholder="Select one"
|
||||
placeholder="Choose license..."
|
||||
track-by="short"
|
||||
label="short"
|
||||
:options="$tag.licenses"
|
||||
:custom-label="(value) => value.short.toUpperCase()"
|
||||
:searchable="true"
|
||||
:close-on-select="true"
|
||||
:show-labels="false"
|
||||
@@ -365,7 +428,7 @@
|
||||
"
|
||||
>
|
||||
<TrashIcon />
|
||||
Remove Link
|
||||
Remove link
|
||||
</button>
|
||||
<hr
|
||||
v-if="
|
||||
@@ -381,21 +444,25 @@
|
||||
<script>
|
||||
import Multiselect from 'vue-multiselect'
|
||||
|
||||
import TrashIcon from '~/assets/images/utils/trash.svg?inline'
|
||||
import CrossIcon from '~/assets/images/utils/x.svg?inline'
|
||||
import CheckIcon from '~/assets/images/utils/check.svg?inline'
|
||||
import PlusIcon from '~/assets/images/utils/plus.svg?inline'
|
||||
import SaveIcon from '~/assets/images/utils/save.svg?inline'
|
||||
import TrashIcon from '~/assets/images/utils/trash.svg?inline'
|
||||
|
||||
import FileInput from '~/components/ui/FileInput'
|
||||
import ThisOrThat from '~/components/ui/ThisOrThat'
|
||||
import SmartFileInput from '~/components/ui/SmartFileInput'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
FileInput,
|
||||
SmartFileInput,
|
||||
ThisOrThat,
|
||||
Multiselect,
|
||||
TrashIcon,
|
||||
CrossIcon,
|
||||
CheckIcon,
|
||||
PlusIcon,
|
||||
SaveIcon,
|
||||
TrashIcon,
|
||||
},
|
||||
beforeRouteLeave(to, from, next) {
|
||||
if (
|
||||
@@ -444,6 +511,9 @@ export default {
|
||||
|
||||
isEditing: true,
|
||||
bodyViewMode: 'source',
|
||||
|
||||
showKnownErrors: false,
|
||||
savingAsDraft: false,
|
||||
}
|
||||
},
|
||||
fetch() {
|
||||
@@ -514,9 +584,38 @@ export default {
|
||||
this.DELETE_PROJECT = 1 << 7
|
||||
},
|
||||
methods: {
|
||||
checkFields() {
|
||||
const reviewConditions =
|
||||
this.newProject.body !== '' && this.newProject.versions.length > 0
|
||||
if (
|
||||
this.newProject.name !== '' &&
|
||||
this.newProject.description !== '' &&
|
||||
this.newProject.slug !== '' &&
|
||||
this.license.short !== null &&
|
||||
this.license_url !== null &&
|
||||
this.license_url !== ''
|
||||
) {
|
||||
if (this.savingAsDraft) {
|
||||
return true
|
||||
} else if (reviewConditions) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
this.showKnownErrors = true
|
||||
return false
|
||||
},
|
||||
async saveProjectReview() {
|
||||
this.isProcessing = true
|
||||
await this.saveProject()
|
||||
this.savingAsDraft = false
|
||||
if (this.checkFields()) {
|
||||
this.isProcessing = true
|
||||
await this.saveProject()
|
||||
}
|
||||
},
|
||||
async saveProjectNotForReview() {
|
||||
this.savingAsDraft = true
|
||||
if (this.checkFields()) {
|
||||
await this.saveProject()
|
||||
}
|
||||
},
|
||||
async saveProject() {
|
||||
this.$nuxt.$loading.start()
|
||||
@@ -709,6 +808,15 @@ header {
|
||||
|
||||
section.essentials {
|
||||
grid-area: essentials;
|
||||
label {
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 1024px) {
|
||||
input {
|
||||
margin-left: 1.5rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
section.project-icon {
|
||||
@@ -716,12 +824,11 @@ section.project-icon {
|
||||
|
||||
img {
|
||||
max-width: 100%;
|
||||
margin-bottom: 1rem;
|
||||
margin-bottom: 0.25rem;
|
||||
border-radius: var(--size-rounded-lg);
|
||||
}
|
||||
|
||||
.iconified-button {
|
||||
width: 9rem;
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
}
|
||||
@@ -732,12 +839,11 @@ section.game-sides {
|
||||
.columns {
|
||||
flex-wrap: wrap;
|
||||
|
||||
span {
|
||||
div {
|
||||
flex: 2;
|
||||
}
|
||||
|
||||
.labeled-control {
|
||||
flex: 2;
|
||||
margin-left: var(--spacing-card-lg);
|
||||
|
||||
h3 {
|
||||
@@ -818,4 +924,15 @@ section.donations {
|
||||
.required {
|
||||
color: var(--color-badge-red-bg);
|
||||
}
|
||||
|
||||
.vertical-input {
|
||||
flex-direction: column;
|
||||
justify-content: left;
|
||||
align-items: unset;
|
||||
gap: 0.5rem;
|
||||
|
||||
input {
|
||||
margin-left: 0 !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -65,20 +65,18 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="currentMember" class="card buttons">
|
||||
<div v-if="currentMember" class="card buttons header-buttons">
|
||||
<button
|
||||
class="iconified-button"
|
||||
@click="
|
||||
newGalleryItems.push({
|
||||
title: '',
|
||||
description: '',
|
||||
featured: false,
|
||||
url: '',
|
||||
})
|
||||
v-if="
|
||||
newGalleryItems.length > 0 ||
|
||||
editGalleryIndexes.length > 0 ||
|
||||
deleteGalleryUrls.length > 0
|
||||
"
|
||||
class="action iconified-button"
|
||||
@click="resetGallery"
|
||||
>
|
||||
<UploadIcon />
|
||||
Upload
|
||||
<CrossIcon />
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
v-if="
|
||||
@@ -90,19 +88,21 @@
|
||||
@click="saveGallery"
|
||||
>
|
||||
<CheckIcon />
|
||||
Save
|
||||
Save changes
|
||||
</button>
|
||||
<button
|
||||
v-if="
|
||||
newGalleryItems.length > 0 ||
|
||||
editGalleryIndexes.length > 0 ||
|
||||
deleteGalleryUrls.length > 0
|
||||
class="iconified-button"
|
||||
@click="
|
||||
newGalleryItems.push({
|
||||
title: '',
|
||||
description: '',
|
||||
featured: false,
|
||||
url: '',
|
||||
})
|
||||
"
|
||||
class="action iconified-button"
|
||||
@click="resetGallery"
|
||||
>
|
||||
<TrashIcon />
|
||||
Discard Changes
|
||||
<PlusIcon />
|
||||
Add an image
|
||||
</button>
|
||||
</div>
|
||||
<div class="items">
|
||||
@@ -177,7 +177,7 @@
|
||||
"
|
||||
>
|
||||
<TrashIcon />
|
||||
Delete
|
||||
Remove
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -214,6 +214,7 @@
|
||||
</div>
|
||||
<div class="gallery-bottom">
|
||||
<SmartFileInput
|
||||
:max-size="5242880"
|
||||
accept="image/png,image/jpeg,image/gif,image/webp,.png,.jpeg,.gif,.webp"
|
||||
prompt="Choose image or drag it here"
|
||||
@change="(files) => showPreviewImage(files, index)"
|
||||
@@ -225,7 +226,7 @@
|
||||
@click="newGalleryItems.splice(index, 1)"
|
||||
>
|
||||
<TrashIcon />
|
||||
Delete
|
||||
Remove
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -236,7 +237,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import UploadIcon from '~/assets/images/utils/upload.svg?inline'
|
||||
import PlusIcon from '~/assets/images/utils/plus.svg?inline'
|
||||
import CalendarIcon from '~/assets/images/utils/calendar.svg?inline'
|
||||
import TrashIcon from '~/assets/images/utils/trash.svg?inline'
|
||||
import CrossIcon from '~/assets/images/utils/x.svg?inline'
|
||||
@@ -254,7 +255,7 @@ import Checkbox from '~/components/ui/Checkbox'
|
||||
export default {
|
||||
components: {
|
||||
CalendarIcon,
|
||||
UploadIcon,
|
||||
PlusIcon,
|
||||
Checkbox,
|
||||
EditIcon,
|
||||
TrashIcon,
|
||||
@@ -615,7 +616,7 @@ export default {
|
||||
}
|
||||
|
||||
input {
|
||||
width: calc(100% - 2rem - 4px);
|
||||
width: 100%;
|
||||
margin: 0 0 0.25rem;
|
||||
}
|
||||
|
||||
@@ -663,6 +664,15 @@ export default {
|
||||
.gallery-buttons {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.columns {
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.header-buttons {
|
||||
display: flex;
|
||||
justify-content: right;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -12,31 +12,46 @@
|
||||
<div class="card">
|
||||
<h3>General</h3>
|
||||
</div>
|
||||
<section class="card">
|
||||
<h3>Edit project</h3>
|
||||
<label>
|
||||
<span> This leads you to a page where you can edit your project. </span>
|
||||
<nuxt-link class="iconified-button" to="edit">Edit</nuxt-link>
|
||||
</label>
|
||||
<h3>Create version</h3>
|
||||
<section class="card main-settings">
|
||||
<label>
|
||||
<span>
|
||||
This leads to a page where you can create a version for your project.
|
||||
<h3>Edit project</h3>
|
||||
<span>
|
||||
This leads you to a page where you can edit your project.
|
||||
</span>
|
||||
</span>
|
||||
<nuxt-link
|
||||
class="iconified-button"
|
||||
to="version/create"
|
||||
:disabled="
|
||||
(currentMember.permissions & UPLOAD_VERSION) !== UPLOAD_VERSION
|
||||
"
|
||||
>Create version</nuxt-link
|
||||
>
|
||||
<div>
|
||||
<nuxt-link class="iconified-button" to="edit"
|
||||
><EditIcon />Edit</nuxt-link
|
||||
>
|
||||
</div>
|
||||
</label>
|
||||
<h3>Delete project</h3>
|
||||
<label>
|
||||
<span>
|
||||
Removes your project from Modrinth's servers and search. Clicking on
|
||||
this will delete your project, so be extra careful!
|
||||
<h3>Create a version</h3>
|
||||
<span>
|
||||
This leads to a page where you can create a version for your
|
||||
project.
|
||||
</span>
|
||||
</span>
|
||||
<div>
|
||||
<nuxt-link
|
||||
class="iconified-button"
|
||||
to="version/create"
|
||||
:disabled="
|
||||
(currentMember.permissions & UPLOAD_VERSION) !== UPLOAD_VERSION
|
||||
"
|
||||
><PlusIcon />Create a version</nuxt-link
|
||||
>
|
||||
</div>
|
||||
</label>
|
||||
<label>
|
||||
<span>
|
||||
<h3>Delete project</h3>
|
||||
<span>
|
||||
Removes your project from Modrinth's servers and search. Clicking on
|
||||
this will delete your project, so be extra careful!
|
||||
</span>
|
||||
</span>
|
||||
<div
|
||||
class="iconified-button"
|
||||
@@ -45,7 +60,7 @@
|
||||
"
|
||||
@click="showPopup"
|
||||
>
|
||||
Delete project
|
||||
<TrashIcon />Delete project
|
||||
</div>
|
||||
</label>
|
||||
</section>
|
||||
@@ -81,14 +96,17 @@
|
||||
<div class="info">
|
||||
<img :src="member.avatar_url" :alt="member.name" />
|
||||
<div class="text">
|
||||
<h4>{{ member.name }}</h4>
|
||||
<h3>{{ member.role }}</h3>
|
||||
<nuxt-link :to="'/user/' + member.user.username" class="name">
|
||||
<p class="title-link">{{ member.name }}</p>
|
||||
</nuxt-link>
|
||||
<p>{{ member.role }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="side-buttons">
|
||||
<Badge v-if="member.accepted" type="accepted" color="green" />
|
||||
<Badge v-else type="pending" color="yellow" />
|
||||
<button
|
||||
v-if="member.role !== 'Owner'"
|
||||
class="dropdown-icon"
|
||||
@click="
|
||||
openTeamMembers.indexOf(member.user.id) === -1
|
||||
@@ -109,22 +127,21 @@
|
||||
<input
|
||||
v-model="allTeamMembers[index].role"
|
||||
type="text"
|
||||
:class="{ 'known-error': member.role === 'Owner' }"
|
||||
:disabled="
|
||||
member.role === 'Owner' ||
|
||||
(currentMember.permissions & EDIT_MEMBER) !== EDIT_MEMBER
|
||||
"
|
||||
/>
|
||||
</label>
|
||||
<ul v-if="member.role === 'Owner'" class="known-errors">
|
||||
<li>A project can only have one 'Owner'.</li>
|
||||
</ul>
|
||||
</div>
|
||||
<h3>Permissions</h3>
|
||||
<div class="permissions">
|
||||
<Checkbox
|
||||
:value="
|
||||
(member.permissions & UPLOAD_VERSION) === UPLOAD_VERSION ||
|
||||
member.role === 'Owner'
|
||||
"
|
||||
:value="(member.permissions & UPLOAD_VERSION) === UPLOAD_VERSION"
|
||||
:disabled="
|
||||
member.role === 'Owner' ||
|
||||
(currentMember.permissions & EDIT_MEMBER) !== EDIT_MEMBER ||
|
||||
(currentMember.permissions & UPLOAD_VERSION) !== UPLOAD_VERSION
|
||||
"
|
||||
@@ -132,12 +149,8 @@
|
||||
@input="allTeamMembers[index].permissions ^= UPLOAD_VERSION"
|
||||
/>
|
||||
<Checkbox
|
||||
:value="
|
||||
(member.permissions & DELETE_VERSION) === DELETE_VERSION ||
|
||||
member.role === 'Owner'
|
||||
"
|
||||
:value="(member.permissions & DELETE_VERSION) === DELETE_VERSION"
|
||||
:disabled="
|
||||
member.role === 'Owner' ||
|
||||
(currentMember.permissions & EDIT_MEMBER) !== EDIT_MEMBER ||
|
||||
(currentMember.permissions & DELETE_VERSION) !== DELETE_VERSION
|
||||
"
|
||||
@@ -145,12 +158,8 @@
|
||||
@input="allTeamMembers[index].permissions ^= DELETE_VERSION"
|
||||
/>
|
||||
<Checkbox
|
||||
:value="
|
||||
(member.permissions & EDIT_DETAILS) === EDIT_DETAILS ||
|
||||
member.role === 'Owner'
|
||||
"
|
||||
:value="(member.permissions & EDIT_DETAILS) === EDIT_DETAILS"
|
||||
:disabled="
|
||||
member.role === 'Owner' ||
|
||||
(currentMember.permissions & EDIT_MEMBER) !== EDIT_MEMBER ||
|
||||
(currentMember.permissions & EDIT_DETAILS) !== EDIT_DETAILS
|
||||
"
|
||||
@@ -158,12 +167,8 @@
|
||||
@input="allTeamMembers[index].permissions ^= EDIT_DETAILS"
|
||||
/>
|
||||
<Checkbox
|
||||
:value="
|
||||
(member.permissions & EDIT_BODY) === EDIT_BODY ||
|
||||
member.role === 'Owner'
|
||||
"
|
||||
:value="(member.permissions & EDIT_BODY) === EDIT_BODY"
|
||||
:disabled="
|
||||
member.role === 'Owner' ||
|
||||
(currentMember.permissions & EDIT_MEMBER) !== EDIT_MEMBER ||
|
||||
(currentMember.permissions & EDIT_BODY) !== EDIT_BODY
|
||||
"
|
||||
@@ -171,12 +176,8 @@
|
||||
@input="allTeamMembers[index].permissions ^= EDIT_BODY"
|
||||
/>
|
||||
<Checkbox
|
||||
:value="
|
||||
(member.permissions & MANAGE_INVITES) === MANAGE_INVITES ||
|
||||
member.role === 'Owner'
|
||||
"
|
||||
:value="(member.permissions & MANAGE_INVITES) === MANAGE_INVITES"
|
||||
:disabled="
|
||||
member.role === 'Owner' ||
|
||||
(currentMember.permissions & EDIT_MEMBER) !== EDIT_MEMBER ||
|
||||
(currentMember.permissions & MANAGE_INVITES) !== MANAGE_INVITES
|
||||
"
|
||||
@@ -184,12 +185,8 @@
|
||||
@input="allTeamMembers[index].permissions ^= MANAGE_INVITES"
|
||||
/>
|
||||
<Checkbox
|
||||
:value="
|
||||
(member.permissions & REMOVE_MEMBER) === REMOVE_MEMBER ||
|
||||
member.role === 'Owner'
|
||||
"
|
||||
:value="(member.permissions & REMOVE_MEMBER) === REMOVE_MEMBER"
|
||||
:disabled="
|
||||
member.role === 'Owner' ||
|
||||
(currentMember.permissions & EDIT_MEMBER) !== EDIT_MEMBER ||
|
||||
(currentMember.permissions & REMOVE_MEMBER) !== REMOVE_MEMBER
|
||||
"
|
||||
@@ -197,24 +194,16 @@
|
||||
@input="allTeamMembers[index].permissions ^= REMOVE_MEMBER"
|
||||
/>
|
||||
<Checkbox
|
||||
:value="
|
||||
(member.permissions & EDIT_MEMBER) === EDIT_MEMBER ||
|
||||
member.role === 'Owner'
|
||||
"
|
||||
:value="(member.permissions & EDIT_MEMBER) === EDIT_MEMBER"
|
||||
:disabled="
|
||||
member.role === 'Owner' ||
|
||||
(currentMember.permissions & EDIT_MEMBER) !== EDIT_MEMBER
|
||||
"
|
||||
label="Edit member"
|
||||
@input="allTeamMembers[index].permissions ^= EDIT_MEMBER"
|
||||
/>
|
||||
<Checkbox
|
||||
:value="
|
||||
(member.permissions & DELETE_PROJECT) === DELETE_PROJECT ||
|
||||
member.role === 'Owner'
|
||||
"
|
||||
:value="(member.permissions & DELETE_PROJECT) === DELETE_PROJECT"
|
||||
:disabled="
|
||||
member.role === 'Owner' ||
|
||||
(currentMember.permissions & EDIT_MEMBER) !== EDIT_MEMBER ||
|
||||
(currentMember.permissions & DELETE_PROJECT) !== DELETE_PROJECT
|
||||
"
|
||||
@@ -226,7 +215,6 @@
|
||||
<button
|
||||
class="iconified-button"
|
||||
:disabled="
|
||||
member.role === 'Owner' ||
|
||||
(currentMember.permissions & EDIT_MEMBER) !== EDIT_MEMBER
|
||||
"
|
||||
@click="removeTeamMember(index)"
|
||||
@@ -249,7 +237,8 @@
|
||||
<button
|
||||
class="iconified-button brand-button-colors"
|
||||
:disabled="
|
||||
(currentMember.permissions & EDIT_MEMBER) !== EDIT_MEMBER
|
||||
(currentMember.permissions & EDIT_MEMBER) !== EDIT_MEMBER ||
|
||||
member.role === 'Owner'
|
||||
"
|
||||
@click="updateTeamMember(index)"
|
||||
>
|
||||
@@ -270,6 +259,7 @@ import Badge from '~/components/ui/Badge'
|
||||
import DropdownIcon from '~/assets/images/utils/dropdown.svg?inline'
|
||||
import PlusIcon from '~/assets/images/utils/plus.svg?inline'
|
||||
import CheckIcon from '~/assets/images/utils/check.svg?inline'
|
||||
import EditIcon from '~/assets/images/utils/edit.svg?inline'
|
||||
import TrashIcon from '~/assets/images/utils/trash.svg?inline'
|
||||
import UserIcon from '~/assets/images/utils/user.svg?inline'
|
||||
|
||||
@@ -281,6 +271,7 @@ export default {
|
||||
Badge,
|
||||
PlusIcon,
|
||||
CheckIcon,
|
||||
EditIcon,
|
||||
TrashIcon,
|
||||
UserIcon,
|
||||
},
|
||||
@@ -480,17 +471,12 @@ export default {
|
||||
}
|
||||
.text {
|
||||
margin: auto 0 auto 0.5rem;
|
||||
h4 {
|
||||
font-weight: normal;
|
||||
margin: 0;
|
||||
font-size: var(--font-size-sm);
|
||||
.name {
|
||||
font-weight: bold;
|
||||
}
|
||||
h3 {
|
||||
text-transform: uppercase;
|
||||
margin-top: 0.1rem;
|
||||
margin-bottom: 0;
|
||||
font-size: var(--font-size-sm);
|
||||
font-weight: var(--font-weight-extrabold);
|
||||
letter-spacing: 0.02rem;
|
||||
p {
|
||||
margin: 0.2rem 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -571,28 +557,19 @@ section {
|
||||
padding-right: var(--spacing-card-lg);
|
||||
}
|
||||
|
||||
div {
|
||||
flex: none;
|
||||
}
|
||||
|
||||
input {
|
||||
flex: 3;
|
||||
height: fit-content;
|
||||
}
|
||||
|
||||
div,
|
||||
a {
|
||||
display: block;
|
||||
text-align: center;
|
||||
height: fit-content;
|
||||
flex: 1;
|
||||
@media screen and (max-width: 1024px) {
|
||||
margin: 0.5rem 0 1rem 0;
|
||||
}
|
||||
}
|
||||
div:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.team-invite {
|
||||
gap: 0.5rem;
|
||||
@media screen and (max-width: 1024px) {
|
||||
flex-direction: column;
|
||||
h3 {
|
||||
@@ -638,4 +615,8 @@ section {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.main-settings span {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -42,49 +42,52 @@
|
||||
Auto-featured
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="mode === 'edit'" class="buttons">
|
||||
<button
|
||||
class="action iconified-button brand-button-colors"
|
||||
@click="saveEditedVersion"
|
||||
>
|
||||
<CheckIcon aria-hidden="true" />
|
||||
Save
|
||||
</button>
|
||||
<div v-if="mode === 'edit'" class="header-buttons buttons">
|
||||
<nuxt-link
|
||||
v-if="$auth.user"
|
||||
:to="`/${project.project_type}/${
|
||||
project.slug ? project.slug : project.id
|
||||
}/version/${encodeURIComponent(version.version_number)}`"
|
||||
class="action iconified-button"
|
||||
class="iconified-button"
|
||||
>
|
||||
<TrashIcon aria-hidden="true" />
|
||||
Discard changes
|
||||
<CrossIcon aria-hidden="true" />
|
||||
Cancel
|
||||
</nuxt-link>
|
||||
</div>
|
||||
<div v-else-if="mode === 'create'" class="buttons">
|
||||
<button
|
||||
class="action iconified-button brand-button-colors"
|
||||
@click="createVersion"
|
||||
class="iconified-button brand-button-colors"
|
||||
@click="saveEditedVersion"
|
||||
>
|
||||
<CheckIcon aria-hidden="true" />
|
||||
<SaveIcon aria-hidden="true" />
|
||||
Save
|
||||
</button>
|
||||
</div>
|
||||
<div v-else-if="mode === 'create'" class="header-buttons buttons">
|
||||
<nuxt-link
|
||||
v-if="$auth.user"
|
||||
:to="`/${project.project_type}/${
|
||||
project.slug ? project.slug : project.id
|
||||
}/versions`"
|
||||
class="action iconified-button"
|
||||
class="iconified-button"
|
||||
>
|
||||
<TrashIcon aria-hidden="true" />
|
||||
Discard version
|
||||
<CrossIcon aria-hidden="true" />
|
||||
Cancel
|
||||
</nuxt-link>
|
||||
<button
|
||||
class="iconified-button brand-button-colors"
|
||||
@click="createVersion"
|
||||
>
|
||||
<CheckIcon aria-hidden="true" />
|
||||
Create
|
||||
</button>
|
||||
</div>
|
||||
<div v-else class="buttons">
|
||||
<a
|
||||
v-if="primaryFile"
|
||||
v-tooltip="
|
||||
primaryFile.filename + ' (' + $formatBytes(primaryFile.size) + ')'
|
||||
"
|
||||
:href="primaryFile.url"
|
||||
class="action iconified-button brand-button-colors"
|
||||
class="bold-button iconified-button brand-button-colors"
|
||||
:title="`Download ${primaryFile.filename}`"
|
||||
>
|
||||
<DownloadIcon aria-hidden="true" />
|
||||
@@ -124,6 +127,7 @@
|
||||
>
|
||||
<input
|
||||
v-model="version.name"
|
||||
class="full-width-input"
|
||||
type="text"
|
||||
placeholder="Enter the version name..."
|
||||
/>
|
||||
@@ -179,6 +183,9 @@
|
||||
class="input"
|
||||
placeholder="Select one"
|
||||
:options="['release', 'beta', 'alpha']"
|
||||
:custom-label="
|
||||
(value) => value.charAt(0).toUpperCase() + value.slice(1)
|
||||
"
|
||||
:searchable="false"
|
||||
:close-on-select="true"
|
||||
:show-labels="false"
|
||||
@@ -217,6 +224,12 @@
|
||||
)
|
||||
.map((it) => it.name)
|
||||
"
|
||||
:custom-label="
|
||||
(value) =>
|
||||
value === 'modloader'
|
||||
? 'Risugami\'s ModLoader'
|
||||
: value.charAt(0).toUpperCase() + value.slice(1)
|
||||
"
|
||||
:loading="$tag.loaders.length === 0"
|
||||
:multiple="true"
|
||||
:searchable="false"
|
||||
@@ -231,7 +244,11 @@
|
||||
<p v-else class="value">
|
||||
{{
|
||||
version.loaders
|
||||
.map((x) => x.charAt(0).toUpperCase() + x.slice(1))
|
||||
.map((x) =>
|
||||
x.toLowerCase() === 'modloader'
|
||||
? "Risugami's ModLoader"
|
||||
: x.charAt(0).toUpperCase() + x.slice(1)
|
||||
)
|
||||
.join(', ')
|
||||
}}
|
||||
</p>
|
||||
@@ -381,7 +398,7 @@
|
||||
class="iconified-button"
|
||||
@click="version.dependencies.splice(index, 1)"
|
||||
>
|
||||
<TrashIcon /> Delete
|
||||
<TrashIcon /> Remove
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -401,13 +418,19 @@
|
||||
v-model="newDependencyId"
|
||||
type="text"
|
||||
oninput="this.value = this.value.replace(' ', '')"
|
||||
:placeholder="`Enter the ${dependencyAddMode} ID...`"
|
||||
:placeholder="`Enter the ${dependencyAddMode} ID${
|
||||
dependencyAddMode === 'project' ? '/slug' : ''
|
||||
}`"
|
||||
@keyup.enter="addDependency"
|
||||
/>
|
||||
<Multiselect
|
||||
v-model="newDependencyType"
|
||||
class="input"
|
||||
placeholder="Select one"
|
||||
:options="['required', 'optional', 'incompatible']"
|
||||
:custom-label="
|
||||
(value) => value.charAt(0).toUpperCase() + value.slice(1)
|
||||
"
|
||||
:searchable="false"
|
||||
:close-on-select="true"
|
||||
:show-labels="false"
|
||||
@@ -415,7 +438,7 @@
|
||||
/>
|
||||
<button class="iconified-button" @click="addDependency">
|
||||
<PlusIcon />
|
||||
Add dependency
|
||||
Add
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -456,7 +479,9 @@
|
||||
:key="file.hashes.sha1"
|
||||
class="file"
|
||||
>
|
||||
<p class="filename">{{ file.filename }}</p>
|
||||
<p class="filename">
|
||||
{{ file.filename }}
|
||||
</p>
|
||||
<div
|
||||
v-if="primaryFile.hashes.sha1 === file.hashes.sha1"
|
||||
class="featured"
|
||||
@@ -473,6 +498,7 @@
|
||||
<DownloadIcon aria-hidden="true" />
|
||||
Download
|
||||
</a>
|
||||
<p v-if="mode === 'version'">({{ $formatBytes(file.size) }})</p>
|
||||
<button
|
||||
v-if="mode === 'edit'"
|
||||
class="action iconified-button"
|
||||
@@ -482,7 +508,7 @@
|
||||
"
|
||||
>
|
||||
<TrashIcon aria-hidden="true" />
|
||||
Delete
|
||||
Remove
|
||||
</button>
|
||||
<button
|
||||
v-if="
|
||||
@@ -507,7 +533,7 @@
|
||||
@click="newFiles.splice(index, 1)"
|
||||
>
|
||||
<TrashIcon aria-hidden="true" />
|
||||
Delete
|
||||
Remove
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -517,6 +543,7 @@
|
||||
class="choose-files"
|
||||
accept=".jar,application/java-archive,.zip,application/zip,.mrpack"
|
||||
prompt="Choose files or drag them here"
|
||||
:max-size="524288000"
|
||||
@change="(x) => x.forEach((y) => newFiles.push(y))"
|
||||
/>
|
||||
</section>
|
||||
@@ -531,7 +558,9 @@ import StatelessFileInput from '~/components/ui/StatelessFileInput'
|
||||
|
||||
import InfoIcon from '~/assets/images/utils/info.svg?inline'
|
||||
import TrashIcon from '~/assets/images/utils/trash.svg?inline'
|
||||
import SaveIcon from '~/assets/images/utils/save.svg?inline'
|
||||
import PlusIcon from '~/assets/images/utils/plus.svg?inline'
|
||||
import CrossIcon from '~/assets/images/utils/x.svg?inline'
|
||||
import EditIcon from '~/assets/images/utils/edit.svg?inline'
|
||||
import DownloadIcon from '~/assets/images/utils/download.svg?inline'
|
||||
import ReportIcon from '~/assets/images/utils/report.svg?inline'
|
||||
@@ -556,7 +585,9 @@ export default {
|
||||
StarIcon,
|
||||
CheckIcon,
|
||||
Multiselect,
|
||||
SaveIcon,
|
||||
PlusIcon,
|
||||
CrossIcon,
|
||||
StatelessFileInput,
|
||||
InfoIcon,
|
||||
},
|
||||
@@ -951,22 +982,22 @@ section {
|
||||
}
|
||||
}
|
||||
|
||||
.header-buttons {
|
||||
justify-content: right;
|
||||
}
|
||||
|
||||
.buttons {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
row-gap: 0.5rem;
|
||||
|
||||
.brand-button-colors {
|
||||
.bold-button {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 1024px) {
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.action:not(:first-child) {
|
||||
margin: 0 0 0 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.version-data-inputs {
|
||||
@@ -1005,8 +1036,7 @@ section {
|
||||
|
||||
input {
|
||||
margin: 0;
|
||||
height: 1.25rem;
|
||||
width: calc(100% - 2.25rem);
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1129,4 +1159,9 @@ section {
|
||||
min-height: 10rem;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.full-width-input {
|
||||
width: 100%;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
<template>
|
||||
<div class="content">
|
||||
<div class="card" v-if="currentMember">
|
||||
<nuxt-link to="version/create" class="iconified-button new-version">
|
||||
<UploadIcon />
|
||||
Upload
|
||||
<div v-if="currentMember" class="card header-buttons">
|
||||
<nuxt-link
|
||||
to="version/create"
|
||||
class="brand-button-colors iconified-button"
|
||||
>
|
||||
<PlusIcon />
|
||||
Create a version
|
||||
</nuxt-link>
|
||||
</div>
|
||||
<VersionFilterControl
|
||||
@@ -11,7 +14,7 @@
|
||||
:versions="versions"
|
||||
@updateVersions="updateVersions"
|
||||
/>
|
||||
<div class="card">
|
||||
<div v-if="versions.length > 0" class="card">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
@@ -25,6 +28,12 @@
|
||||
<tr v-for="version in filteredVersions" :key="version.id">
|
||||
<td>
|
||||
<a
|
||||
v-tooltip="
|
||||
$parent.findPrimary(version).filename +
|
||||
' (' +
|
||||
$formatBytes($parent.findPrimary(version).size) +
|
||||
')'
|
||||
"
|
||||
:href="$parent.findPrimary(version).url"
|
||||
class="download-button"
|
||||
:class="version.version_type"
|
||||
@@ -69,7 +78,11 @@
|
||||
<p>
|
||||
{{
|
||||
version.loaders
|
||||
.map((x) => x.charAt(0).toUpperCase() + x.slice(1))
|
||||
.map((x) =>
|
||||
x.toLowerCase() === 'modloader'
|
||||
? 'ModLoader'
|
||||
: x.charAt(0).toUpperCase() + x.slice(1)
|
||||
)
|
||||
.join(', ') +
|
||||
' ' +
|
||||
$formatVersion(version.game_versions)
|
||||
@@ -93,7 +106,11 @@
|
||||
<p>
|
||||
{{
|
||||
version.loaders
|
||||
.map((x) => x.charAt(0).toUpperCase() + x.slice(1))
|
||||
.map((x) =>
|
||||
x.toLowerCase() === 'modloader'
|
||||
? 'ModLoader'
|
||||
: x.charAt(0).toUpperCase() + x.slice(1)
|
||||
)
|
||||
.join(', ')
|
||||
}}
|
||||
</p>
|
||||
@@ -118,14 +135,14 @@
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import UploadIcon from '~/assets/images/utils/upload.svg?inline'
|
||||
import PlusIcon from '~/assets/images/utils/plus.svg?inline'
|
||||
import DownloadIcon from '~/assets/images/utils/download.svg?inline'
|
||||
import VersionBadge from '~/components/ui/Badge'
|
||||
import VersionFilterControl from '~/components/ui/VersionFilterControl'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
UploadIcon,
|
||||
PlusIcon,
|
||||
DownloadIcon,
|
||||
VersionBadge,
|
||||
VersionFilterControl,
|
||||
@@ -165,10 +182,6 @@ export default {
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.new-version {
|
||||
max-width: 5.25rem;
|
||||
}
|
||||
|
||||
table {
|
||||
border-collapse: separate;
|
||||
border-spacing: 0 0.75rem;
|
||||
@@ -249,4 +262,9 @@ table {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.header-buttons {
|
||||
display: flex;
|
||||
justify-content: right;
|
||||
}
|
||||
</style>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -9,29 +9,36 @@
|
||||
:disabled="!$nuxt.$loading"
|
||||
@click="createReport"
|
||||
>
|
||||
<PlusIcon />
|
||||
Create
|
||||
<CheckIcon />
|
||||
Submit
|
||||
</button>
|
||||
</header>
|
||||
<section class="card info">
|
||||
<h3>Item ID</h3>
|
||||
<label>
|
||||
<span>
|
||||
The ID of the item you are reporting. For example, the item ID of a
|
||||
project would be its project ID, found on the right side of that
|
||||
project's page under "Project ID".
|
||||
<h3>Item ID</h3>
|
||||
<span>
|
||||
The ID of the item you are reporting. For example, the item ID of
|
||||
a project would be its project ID, found on the right side of that
|
||||
project's page under "Project ID".
|
||||
</span>
|
||||
</span>
|
||||
<input v-model="itemId" type="text" placeholder="Enter the item ID" />
|
||||
</label>
|
||||
<h3>Item type</h3>
|
||||
<label>
|
||||
<span class="no-padding"
|
||||
>The type of the item that is being reported.</span
|
||||
>
|
||||
<span>
|
||||
<h3>Item type</h3>
|
||||
<span class="no-padding"
|
||||
>The type of the item that is being reported.</span
|
||||
>
|
||||
</span>
|
||||
<multiselect
|
||||
id="item-type"
|
||||
v-model="itemType"
|
||||
:options="['project', 'version', 'user']"
|
||||
:custom-label="
|
||||
(value) => value.charAt(0).toUpperCase() + value.slice(1)
|
||||
"
|
||||
:multiple="false"
|
||||
:searchable="false"
|
||||
:show-no-results="false"
|
||||
@@ -39,16 +46,21 @@
|
||||
placeholder="Choose item type"
|
||||
/>
|
||||
</label>
|
||||
<h3>Report type</h3>
|
||||
<label>
|
||||
<span class="no-padding">
|
||||
The type of report. This is the category that this report falls
|
||||
under.
|
||||
<span>
|
||||
<h3>Report type</h3>
|
||||
<span class="no-padding">
|
||||
The type of report. This is the category that this report falls
|
||||
under.
|
||||
</span>
|
||||
</span>
|
||||
<multiselect
|
||||
id="report-type"
|
||||
v-model="reportType"
|
||||
:options="reportTypes"
|
||||
:custom-label="
|
||||
(value) => value.charAt(0).toUpperCase() + value.slice(1)
|
||||
"
|
||||
:multiple="false"
|
||||
:searchable="false"
|
||||
:show-no-results="false"
|
||||
@@ -68,13 +80,13 @@
|
||||
</h3>
|
||||
<span>
|
||||
You can type the of the long form of your description here. This
|
||||
editor supports markdown. You can find the syntax
|
||||
editor supports
|
||||
<a
|
||||
href="https://guides.github.com/features/mastering-markdown/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="text-link"
|
||||
>here</a
|
||||
>Markdown</a
|
||||
>.
|
||||
</span>
|
||||
<ThisOrThat
|
||||
@@ -102,13 +114,13 @@
|
||||
import Multiselect from 'vue-multiselect'
|
||||
import ThisOrThat from '~/components/ui/ThisOrThat'
|
||||
|
||||
import PlusIcon from '~/assets/images/utils/plus.svg?inline'
|
||||
import CheckIcon from '~/assets/images/utils/check.svg?inline'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Multiselect,
|
||||
ThisOrThat,
|
||||
PlusIcon,
|
||||
CheckIcon,
|
||||
},
|
||||
async asyncData(data) {
|
||||
const reportTypes = (await data.$axios.get(`tag/report_type`)).data
|
||||
@@ -261,4 +273,12 @@ section.description {
|
||||
.card {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.card span {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
label {
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -69,7 +69,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ClearIcon from '~/assets/images/utils/trash.svg?inline'
|
||||
import ClearIcon from '~/assets/images/utils/clear.svg?inline'
|
||||
import UpdateIcon from '~/assets/images/utils/updated.svg?inline'
|
||||
import UsersIcon from '~/assets/images/utils/users.svg?inline'
|
||||
import UpToDate from '~/assets/images/illustrations/up_to_date.svg?inline'
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
}"
|
||||
>
|
||||
<aside class="normal-page__sidebar" aria-label="Filters">
|
||||
<section class="card" role="presentation">
|
||||
<section class="card filters-card" role="presentation">
|
||||
<button
|
||||
class="iconified-button sidebar-menu-close-button"
|
||||
@click="sidebarMenuOpen = !sidebarMenuOpen"
|
||||
@@ -30,7 +30,7 @@
|
||||
class="iconified-button"
|
||||
@click="clearFilters"
|
||||
>
|
||||
<ExitIcon aria-hidden="true" />
|
||||
<ClearIcon aria-hidden="true" />
|
||||
Clear filters
|
||||
</button>
|
||||
<section aria-label="Category filters">
|
||||
@@ -291,7 +291,7 @@ import ClientSide from '~/assets/images/categories/client.svg?inline'
|
||||
import ServerSide from '~/assets/images/categories/server.svg?inline'
|
||||
|
||||
import SearchIcon from '~/assets/images/utils/search.svg?inline'
|
||||
import ExitIcon from '~/assets/images/utils/exit.svg?inline'
|
||||
import ClearIcon from '~/assets/images/utils/clear.svg?inline'
|
||||
import EyeIcon from '~/assets/images/utils/eye.svg?inline'
|
||||
import EyeOffIcon from '~/assets/images/utils/eye-off.svg?inline'
|
||||
|
||||
@@ -309,7 +309,7 @@ export default {
|
||||
ClientSide,
|
||||
ServerSide,
|
||||
SearchIcon,
|
||||
ExitIcon,
|
||||
ClearIcon,
|
||||
EyeIcon,
|
||||
EyeOffIcon,
|
||||
LogoAnimated,
|
||||
@@ -649,6 +649,10 @@ export default {
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.filters-card {
|
||||
padding: var(--spacing-card-lg);
|
||||
}
|
||||
|
||||
.sidebar-menu {
|
||||
display: none;
|
||||
margin-top: 1rem;
|
||||
|
||||
@@ -17,61 +17,16 @@
|
||||
<nuxt-link class="tab" to="/settings/privacy">
|
||||
<span>Privacy</span>
|
||||
</nuxt-link>
|
||||
<button
|
||||
v-if="actionButton"
|
||||
class="iconified-button brand-button-colors right"
|
||||
@click="actionButtonCallback"
|
||||
>
|
||||
<CheckIcon />
|
||||
{{ actionButton }}
|
||||
</button>
|
||||
</div>
|
||||
<NuxtChild
|
||||
:action-button.sync="actionButton"
|
||||
:action-button-callback.sync="actionButtonCallback"
|
||||
/>
|
||||
<NuxtChild />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import CheckIcon from '~/assets/images/utils/check.svg?inline'
|
||||
|
||||
export default {
|
||||
name: 'Settings',
|
||||
components: {
|
||||
CheckIcon,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
actionButton: '',
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
'$route.path': {
|
||||
handler() {
|
||||
this.actionButton = ''
|
||||
this.actionButtonCallback = () => {}
|
||||
},
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
actionButtonCallback() {},
|
||||
changeTheme() {
|
||||
const shift = event.shiftKey
|
||||
switch (this.$colorMode.preference) {
|
||||
case 'dark':
|
||||
this.$colorMode.preference = shift ? 'light' : 'oled'
|
||||
break
|
||||
case 'oled':
|
||||
this.$colorMode.preference = shift ? 'dark' : 'light'
|
||||
break
|
||||
default:
|
||||
this.$colorMode.preference = shift ? 'oled' : 'dark'
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -1,180 +1,144 @@
|
||||
<template>
|
||||
<div class="edit-page">
|
||||
<div class="left-side">
|
||||
<div class="profile-picture card">
|
||||
<h3>Profile picture</h3>
|
||||
<div class="uploader">
|
||||
<img
|
||||
:src="previewImage ? previewImage : $auth.user.avatar_url"
|
||||
@click="developerMode++"
|
||||
/>
|
||||
<file-input
|
||||
accept="image/png,image/jpeg,image/gif,image/webp"
|
||||
class="choose-image"
|
||||
prompt="Choose image or drag it here"
|
||||
@change="showPreviewImage"
|
||||
/>
|
||||
<section class="card account-settings">
|
||||
<div class="header">
|
||||
<h2 class="title">Account settings</h2>
|
||||
<div class="controls">
|
||||
<button
|
||||
class="brand-button-colors iconified-button"
|
||||
title="Save account settings changes"
|
||||
@click="saveChanges()"
|
||||
>
|
||||
<SaveIcon />
|
||||
Save changes
|
||||
</button>
|
||||
</div>
|
||||
<button
|
||||
class="iconified-button"
|
||||
@click="
|
||||
icon = null
|
||||
previewImage = null
|
||||
"
|
||||
>
|
||||
<TrashIcon />
|
||||
Reset
|
||||
</button>
|
||||
</div>
|
||||
<div class="recap card">
|
||||
<section>
|
||||
<h2>Profile Recap</h2>
|
||||
<div>
|
||||
<Badge
|
||||
v-if="$auth.user.role === 'admin'"
|
||||
type="Admin"
|
||||
color="red"
|
||||
<div class="left-side">
|
||||
<h3>Profile picture</h3>
|
||||
<div class="profile-picture">
|
||||
<img :src="previewImage ? previewImage : $auth.user.avatar_url" />
|
||||
<div class="uploader">
|
||||
<SmartFileInput
|
||||
:show-icon="false"
|
||||
:max-size="2097152"
|
||||
accept="image/png,image/jpeg,image/gif,image/webp"
|
||||
class="choose-image"
|
||||
prompt="Choose image or drag it here"
|
||||
@change="showPreviewImage"
|
||||
/>
|
||||
<Badge
|
||||
v-else-if="$auth.user.role === 'moderator'"
|
||||
type="Moderator"
|
||||
color="yellow"
|
||||
/>
|
||||
<Badge v-else type="Developer" color="green" />
|
||||
<div class="stat">
|
||||
<SunriseIcon />
|
||||
<span>Joined {{ $dayjs($auth.user.created).fromNow() }}</span>
|
||||
</div>
|
||||
<button
|
||||
class="iconified-button"
|
||||
@click="
|
||||
icon = null
|
||||
previewImage = null
|
||||
"
|
||||
>
|
||||
<TrashIcon />
|
||||
Reset
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
<section>
|
||||
<div class="stat">
|
||||
<DownloadIcon />
|
||||
<span>
|
||||
<strong>{{ sumDownloads() }}</strong> downloads
|
||||
</span>
|
||||
</div>
|
||||
<div class="stat">
|
||||
<HeartIcon />
|
||||
<span>
|
||||
<strong>{{ sumFollows() }}</strong> followers of projects
|
||||
</span>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="right-side">
|
||||
<section class="card">
|
||||
<h3>Username</h3>
|
||||
<div class="right-side">
|
||||
<label>
|
||||
<span>
|
||||
The username used on Modrinth to identify yourself. This must be
|
||||
unique.
|
||||
<h3>Username</h3>
|
||||
<span>This must be unique.</span>
|
||||
</span>
|
||||
|
||||
<input
|
||||
v-model="username"
|
||||
type="text"
|
||||
placeholder="Enter your username"
|
||||
/>
|
||||
</label>
|
||||
<h3>Email</h3>
|
||||
<label>
|
||||
<span>
|
||||
The email for your account. This is private information which is not
|
||||
exposed in any API routes or on your profile. It is also optional.
|
||||
<h3>Email (optional)</h3>
|
||||
<span>This is kept private.</span>
|
||||
</span>
|
||||
<input v-model="email" type="email" placeholder="Enter your email" />
|
||||
</label>
|
||||
<h3>Bio</h3>
|
||||
<label>
|
||||
<span>
|
||||
A description of yourself which other users can see on your profile.
|
||||
<h3>Bio</h3>
|
||||
<span>Describe yourself to other users!</span>
|
||||
</span>
|
||||
<input v-model="bio" type="text" placeholder="Enter your bio" />
|
||||
</label>
|
||||
<h3>Theme</h3>
|
||||
<label>
|
||||
</div>
|
||||
</section>
|
||||
<section class="card">
|
||||
<div class="header">
|
||||
<h2 class="title">Display settings</h2>
|
||||
</div>
|
||||
<label>
|
||||
<span>
|
||||
<h3>Theme</h3>
|
||||
<span>Change the global site theme.</span>
|
||||
</span>
|
||||
<Multiselect
|
||||
v-model="$colorMode.preference"
|
||||
:options="['system', 'light', 'dark', 'oled']"
|
||||
:custom-label="
|
||||
(value) =>
|
||||
value === 'oled'
|
||||
? 'OLED'
|
||||
: value.charAt(0).toUpperCase() + value.slice(1)
|
||||
"
|
||||
:searchable="false"
|
||||
:close-on-select="true"
|
||||
:show-labels="false"
|
||||
:allow-empty="false"
|
||||
/>
|
||||
</label>
|
||||
<label>
|
||||
<span>
|
||||
<h3>Search sidebar on the right</h3>
|
||||
<span>
|
||||
Change the global site theme. It can also be changed between light
|
||||
and dark in the navigation bar.
|
||||
Enabling this will put the search page's filters sidebar on the
|
||||
right side.
|
||||
</span>
|
||||
<Multiselect
|
||||
v-model="$colorMode.preference"
|
||||
:options="['system', 'light', 'dark', 'oled']"
|
||||
:searchable="false"
|
||||
:close-on-select="true"
|
||||
:show-labels="false"
|
||||
:allow-empty="false"
|
||||
/>
|
||||
</label>
|
||||
<h3>Search sidebar on right side</h3>
|
||||
<label>
|
||||
</span>
|
||||
<input
|
||||
v-model="searchLayout"
|
||||
class="switch stylized-toggle"
|
||||
type="checkbox"
|
||||
@change="changeLayout"
|
||||
/>
|
||||
</label>
|
||||
<label>
|
||||
<span>
|
||||
<h3>Project sidebar on the right</h3>
|
||||
|
||||
<span>
|
||||
Sets the sidebar direction for search pages. Enabling this will put
|
||||
the search bar on the right side.
|
||||
Enabling this will put the project pages' info sidebars on the right
|
||||
side.
|
||||
</span>
|
||||
<input
|
||||
v-model="searchLayout"
|
||||
class="switch stylized-toggle"
|
||||
type="checkbox"
|
||||
@change="changeLayout"
|
||||
/>
|
||||
</label>
|
||||
<h3>Project sidebar on right side</h3>
|
||||
<label>
|
||||
<span>
|
||||
Sets the sidebar direction for project pages. Enabling this will
|
||||
make projects look closer to the legacy layout, with project
|
||||
information on the right side.
|
||||
</span>
|
||||
<input
|
||||
v-model="projectLayout"
|
||||
class="switch stylized-toggle"
|
||||
type="checkbox"
|
||||
@change="changeLayout"
|
||||
/>
|
||||
</label>
|
||||
<section v-if="developerMode > 6">
|
||||
<h3>Developer options</h3>
|
||||
<label>
|
||||
<span>
|
||||
Set the API endpoint. This value is not stored, and is intended
|
||||
for temporary usage.</span
|
||||
>
|
||||
<Multiselect
|
||||
v-model="apiEndpoint"
|
||||
:options="['production', 'staging']"
|
||||
:searchable="false"
|
||||
:close-on-select="true"
|
||||
:show-labels="false"
|
||||
:allow-empty="false"
|
||||
@input="changeApiEndpoint()"
|
||||
/>
|
||||
</label>
|
||||
</section>
|
||||
</section>
|
||||
</div>
|
||||
</span>
|
||||
<input
|
||||
v-model="projectLayout"
|
||||
class="switch stylized-toggle"
|
||||
type="checkbox"
|
||||
@change="changeLayout"
|
||||
/>
|
||||
</label>
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Multiselect from 'vue-multiselect'
|
||||
import FileInput from '~/components/ui/FileInput'
|
||||
import Badge from '~/components/ui/Badge'
|
||||
|
||||
import HeartIcon from '~/assets/images/utils/heart.svg?inline'
|
||||
import SmartFileInput from '~/components/ui/SmartFileInput'
|
||||
import TrashIcon from '~/assets/images/utils/trash.svg?inline'
|
||||
import SunriseIcon from '~/assets/images/utils/sunrise.svg?inline'
|
||||
import DownloadIcon from '~/assets/images/utils/download.svg?inline'
|
||||
import SaveIcon from '~/assets/images/utils/save.svg?inline'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
TrashIcon,
|
||||
SunriseIcon,
|
||||
DownloadIcon,
|
||||
HeartIcon,
|
||||
Badge,
|
||||
FileInput,
|
||||
SaveIcon,
|
||||
SmartFileInput,
|
||||
Multiselect,
|
||||
},
|
||||
asyncData(ctx) {
|
||||
@@ -190,24 +154,15 @@ export default {
|
||||
previewImage: null,
|
||||
searchLayout: false,
|
||||
projectLayout: false,
|
||||
apiEndpoint: this.getApiEndpoint(),
|
||||
developerMode: 0,
|
||||
}
|
||||
},
|
||||
fetch() {
|
||||
this.searchLayout = this.$store.state.cosmetics.searchLayout
|
||||
this.projectLayout = this.$store.state.cosmetics.projectLayout
|
||||
|
||||
this.$emit('update:action-button', 'Save')
|
||||
this.$emit('update:action-button-callback', this.saveChanges)
|
||||
},
|
||||
head: {
|
||||
title: 'Settings - Modrinth',
|
||||
},
|
||||
created() {
|
||||
this.$emit('update:action-button', 'Save')
|
||||
this.$emit('update:action-button-callback', this.saveChanges)
|
||||
},
|
||||
methods: {
|
||||
changeTheme() {
|
||||
const shift = event.shiftKey
|
||||
@@ -222,17 +177,6 @@ export default {
|
||||
this.$colorMode.preference = shift ? 'oled' : 'dark'
|
||||
}
|
||||
},
|
||||
changeApiEndpoint() {
|
||||
const subdomain =
|
||||
this.apiEndpoint === 'production' ? 'api' : 'staging-api'
|
||||
this.$axios.defaults.baseURL =
|
||||
'https://' + subdomain + '.modrinth.com/v2/'
|
||||
},
|
||||
getApiEndpoint() {
|
||||
return this.$axios.defaults.baseURL === 'https://api.modrinth.com/v2/'
|
||||
? 'production'
|
||||
: 'staging'
|
||||
},
|
||||
showPreviewImage(files) {
|
||||
const reader = new FileReader()
|
||||
this.icon = files[0]
|
||||
@@ -311,76 +255,72 @@ export default {
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.edit-page {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
.account-settings {
|
||||
display: grid;
|
||||
grid-template: 'header header' auto 'left-side left-side' auto 'right-side right-side' auto;
|
||||
|
||||
@media screen and (min-width: 1024px) {
|
||||
flex-direction: row;
|
||||
|
||||
.left-side {
|
||||
margin-right: var(--spacing-card-bg);
|
||||
}
|
||||
grid-template:
|
||||
'header header' auto
|
||||
'left-side right-side' auto;
|
||||
}
|
||||
}
|
||||
|
||||
.left-side {
|
||||
min-width: 20rem;
|
||||
.left-side {
|
||||
grid-area: left-side;
|
||||
min-width: 20rem;
|
||||
|
||||
.profile-picture {
|
||||
h3 {
|
||||
font-size: var(--font-size-lg);
|
||||
}
|
||||
|
||||
.uploader {
|
||||
margin: 1rem 0;
|
||||
text-align: center;
|
||||
.profile-picture {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 0.5rem;
|
||||
align-items: center;
|
||||
|
||||
img {
|
||||
box-shadow: var(--shadow-card);
|
||||
|
||||
border-radius: var(--size-rounded-md);
|
||||
width: 8rem;
|
||||
width: 10rem;
|
||||
height: 10rem;
|
||||
object-fit: contain;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.recap {
|
||||
section {
|
||||
h2 {
|
||||
font-size: var(--font-size-lg);
|
||||
margin: 0 0 0.5rem 0;
|
||||
}
|
||||
|
||||
.version-badge {
|
||||
text-transform: none;
|
||||
margin-bottom: 0.25rem;
|
||||
|
||||
&::first-letter {
|
||||
text-transform: uppercase;
|
||||
.uploader {
|
||||
text-align: center;
|
||||
.iconified-button {
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.right-side {
|
||||
grid-area: right-side;
|
||||
margin-left: var(--spacing-card-lg);
|
||||
}
|
||||
}
|
||||
|
||||
.stat {
|
||||
.card span {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
label {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin: 0.5rem 0;
|
||||
padding-bottom: 1rem;
|
||||
grid-area: header;
|
||||
|
||||
svg {
|
||||
width: auto;
|
||||
height: 1.25rem;
|
||||
|
||||
margin-right: 0.25rem;
|
||||
.title {
|
||||
flex-grow: 1;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
span {
|
||||
strong {
|
||||
font-weight: bolder;
|
||||
}
|
||||
.controls {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,5 +1,26 @@
|
||||
<template>
|
||||
<div class="rows card">
|
||||
<div class="header">
|
||||
<h2 class="title">Privacy settings</h2>
|
||||
<div class="controls">
|
||||
<button class="iconified-button" @click="toggleAll(false)">
|
||||
<DenyIcon />
|
||||
Deny all
|
||||
</button>
|
||||
<button class="iconified-button" @click="toggleAll(true)">
|
||||
<AllowIcon />
|
||||
Allow all
|
||||
</button>
|
||||
<button
|
||||
class="brand-button-colors iconified-button"
|
||||
title="Confirm privacy settings"
|
||||
@click="confirm()"
|
||||
>
|
||||
<SaveIcon />
|
||||
Save changes
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="privacy-settings-container">
|
||||
<div>
|
||||
Modrinth relies on different providers and in-house tools to allow us to
|
||||
@@ -30,23 +51,23 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="actions">
|
||||
<button class="iconified-button" @click="toggleAll(false)">
|
||||
Select none
|
||||
</button>
|
||||
<button class="iconified-button" @click="toggleAll(true)">
|
||||
Select all
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import toggles from '@/privacy-toggles'
|
||||
import DenyIcon from '~/assets/images/utils/clear.svg?inline'
|
||||
import AllowIcon from '~/assets/images/utils/check-circle.svg?inline'
|
||||
import SaveIcon from '~/assets/images/utils/save.svg?inline'
|
||||
|
||||
export default {
|
||||
auth: false,
|
||||
name: 'Privacy',
|
||||
components: {
|
||||
DenyIcon,
|
||||
AllowIcon,
|
||||
SaveIcon,
|
||||
},
|
||||
data: () => {
|
||||
const settings = toggles.settings
|
||||
return {
|
||||
@@ -54,9 +75,6 @@ export default {
|
||||
}
|
||||
},
|
||||
fetch() {
|
||||
this.$emit('update:action-button', 'Confirm')
|
||||
this.$emit('update:action-button-callback', this.confirm)
|
||||
|
||||
this.$store.dispatch('consent/loadFromCookies', this.$cookies)
|
||||
if (this.$store.state.consent.is_consent_given) {
|
||||
Object.keys(toggles.settings).forEach((key) => {
|
||||
@@ -76,10 +94,6 @@ export default {
|
||||
head: {
|
||||
title: 'Privacy Settings - Modrinth',
|
||||
},
|
||||
created() {
|
||||
this.$emit('update:action-button', 'Confirm')
|
||||
this.$emit('update:action-button-callback', this.confirm)
|
||||
},
|
||||
options: {
|
||||
auth: false,
|
||||
},
|
||||
@@ -91,6 +105,7 @@ export default {
|
||||
}
|
||||
|
||||
this.$forceUpdate()
|
||||
this.confirm()
|
||||
},
|
||||
confirm() {
|
||||
this.$store.commit('consent/set_consent', true)
|
||||
@@ -173,4 +188,22 @@ export default {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-bottom: 1rem;
|
||||
grid-area: header;
|
||||
|
||||
.title {
|
||||
flex-grow: 1;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.controls {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -12,12 +12,17 @@
|
||||
/>
|
||||
|
||||
<section class="card">
|
||||
<h3>Authorization token</h3>
|
||||
<div class="header">
|
||||
<h2 class="title">Security settings</h2>
|
||||
</div>
|
||||
<label>
|
||||
<span>
|
||||
Your authorization token can be used with the Modrinth API, the
|
||||
Minotaur Gradle plugin, and other applications that interact with
|
||||
Modrinth's API. Be sure to keep this secret!
|
||||
<h3>Authorization token</h3>
|
||||
<span>
|
||||
Your authorization token can be used with the Modrinth API, the
|
||||
Minotaur Gradle plugin, and other applications that interact with
|
||||
Modrinth's API. Be sure to keep this secret!
|
||||
</span>
|
||||
</span>
|
||||
<input
|
||||
type="button"
|
||||
@@ -26,12 +31,14 @@
|
||||
@click="copyToken"
|
||||
/>
|
||||
</label>
|
||||
<h3>Revoke your token</h3>
|
||||
<label>
|
||||
<span
|
||||
>This will log you out of Modrinth, and you will have to log in again
|
||||
to access Modrinth with a new token.</span
|
||||
>
|
||||
<span>
|
||||
<h3>Revoke your token</h3>
|
||||
<span
|
||||
>This will log you out of Modrinth, and you will have to log in
|
||||
again to access Modrinth with a new token.</span
|
||||
>
|
||||
</span>
|
||||
<input
|
||||
type="button"
|
||||
class="iconified-button"
|
||||
@@ -39,14 +46,16 @@
|
||||
@click="$router.replace('/settings/revoke-token')"
|
||||
/>
|
||||
</label>
|
||||
<h3>Delete your account</h3>
|
||||
<label>
|
||||
<span
|
||||
>Clicking on this WILL delete your account. Do not click on this
|
||||
unless you want your account deleted. If you delete your account, all
|
||||
attached data, including projects, will be removed from our servers.
|
||||
This cannot be reversed, so be careful!</span
|
||||
>
|
||||
<span>
|
||||
<h3>Delete your account</h3>
|
||||
<span
|
||||
>Clicking on this WILL delete your account. Do not click on this
|
||||
unless you want your account deleted. If you delete your account,
|
||||
all attached data, including projects, will be removed from our
|
||||
servers. This cannot be reversed, so be careful!</span
|
||||
>
|
||||
</span>
|
||||
<input
|
||||
value="Delete account"
|
||||
type="button"
|
||||
@@ -96,3 +105,26 @@ export default {
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.card span {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-bottom: 1rem;
|
||||
grid-area: header;
|
||||
|
||||
.title {
|
||||
flex-grow: 1;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.controls {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -20,6 +20,14 @@
|
||||
<hr class="card-divider" />
|
||||
<h3 class="sidebar__item">About me</h3>
|
||||
<span v-if="user.bio" class="sidebar__item bio">{{ user.bio }}</span>
|
||||
<a
|
||||
:href="githubUrl"
|
||||
target="_blank"
|
||||
class="sidebar__item report-button iconified-button"
|
||||
>
|
||||
<GitHubIcon aria-hidden="true" />
|
||||
View GitHub profile
|
||||
</a>
|
||||
<div class="sidebar__item stats-block">
|
||||
<div class="stats-block__item secondary-stat">
|
||||
<SunriseIcon class="secondary-stat__icon" aria-hidden="true" />
|
||||
@@ -125,6 +133,7 @@ import ThisOrThat from '~/components/ui/ThisOrThat'
|
||||
import Badge from '~/components/ui/Badge'
|
||||
import Advertisement from '~/components/ads/Advertisement'
|
||||
|
||||
import GitHubIcon from '~/assets/images/utils/github.svg?inline'
|
||||
import ReportIcon from '~/assets/images/utils/report.svg?inline'
|
||||
import SunriseIcon from '~/assets/images/utils/sunrise.svg?inline'
|
||||
import DownloadIcon from '~/assets/images/utils/download.svg?inline'
|
||||
@@ -139,6 +148,7 @@ export default {
|
||||
ProjectCard,
|
||||
SunriseIcon,
|
||||
DownloadIcon,
|
||||
GitHubIcon,
|
||||
ReportIcon,
|
||||
Badge,
|
||||
SettingsIcon,
|
||||
@@ -160,10 +170,17 @@ export default {
|
||||
])
|
||||
).map((it) => it.data)
|
||||
|
||||
const githubUrl = (
|
||||
await (
|
||||
await fetch(`https://api.github.com/user/` + user.github_id)
|
||||
).json()
|
||||
).html_url
|
||||
|
||||
return {
|
||||
selectedProjectType: 'all',
|
||||
user,
|
||||
projects,
|
||||
githubUrl,
|
||||
}
|
||||
} catch {
|
||||
data.error({
|
||||
|
||||
@@ -98,4 +98,15 @@ export default ({ store }, inject) => {
|
||||
|
||||
return output.join(', ')
|
||||
})
|
||||
inject('formatBytes', (bytes, decimals = 2) => {
|
||||
if (bytes === 0) return '0 Bytes'
|
||||
|
||||
const k = 1024
|
||||
const dm = decimals < 0 ? 0 : decimals
|
||||
const sizes = ['Bytes', 'KiB', 'MiB', 'GiB']
|
||||
|
||||
const i = Math.floor(Math.log(bytes) / Math.log(k))
|
||||
|
||||
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i]
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user