You've already forked AstralRinth
forked from didirus/AstralRinth
Add initial support for the v2 of the API (Still WIP) (#250)
* Functionally implement modpacks * Add LogoAnimated to logo license * Fix eslint errors * Add `z-index: 20` to user dropdown (#287) * Fix pages not working, add changelog page, redesign versions page * Update theme colors, add OLED theme, update some project creation text. (#292) * Update theme colors, add OLED theme, update some project creation text. * Make summary normal text color * Update favicons, update logos to use dynamic colors, updated filters panel a bit * Update wording from #250 * Version page rework * Manually apply some commits from master, other minor v2 fixes (#296) * Homepage styling improvements (#285) * Add border radius to video + example code colors * Change color + allow overflow scroll * Minor v2 fixes - Makes multiple loaders display correctly (used to be `Fabric,Forge` is now `Fabric, Forge` - Fix oopses in #292 - Allow .jar and .zip in file prompt - Apply 30cbd3a6c372940d1e86cc8134d0dfc7e8e5ee9c to pages/create/project.vue - Display `fabric, forge` instead of broken icons on pages/create/project.vue * Markdown styling fixes (#268) * Add table color variables (+ prettier fixes) * Add details and table styling to .markdown-body * Add indexing meta value depending on the status of the mod. (#261) * General UI Improvement (again) (#255) * Add and fix some stuff * Add warning when leaving to `mod/create` * Fix mods/create not working * Fix a bug & add improvements to a couple moderation aspects (#278) This PR fixes reports on the moderation dashboard going to `/dashboard/mod/_id` instead of to `/mod/_id`. It also allows the ability for moderators to unlist mods in the queue from the frontend instead of having to do it via the backend.  Unlisted mods should have the ability to resubmit for approval, so I've also changed "Submit for Review" to "Submit for approval", allowing unlisted mods to do that as well.  * Add project guidelines to Terms page (#275) * Add project guidelines to Terms page This adds the project guidelines as outlined [here](https://discord.com/channels/734077874708938864/734077874708938867/806556531491471368). NOTE: I've made a few tweaks in wording to accommodate this format, so this is not an exact copy. * Move rules to its own page * Allow users to login from search page when it is rendered serverside (#272) * Change `this.$route.fullPath` → `this.$route.path` * Closes modrinth/knossos#256 * Wrap mod icon and title in link (#273) * Wrap mod icon and title in link * Fixes #218 * Editor's note Skipped #249 (search was rewritten), #266 (couldn't figure out how to apply it), #270 (didn't seem to apply properly), #252 (manually merged in with #292), #262 (superceded by #270), #282, #271, #277, #283, and #281 (those five didn't get wiped) Co-authored-by: venashial <venashial.levo@aleeas.com> Co-authored-by: Redblueflame <contact@redblueflame.com> Co-authored-by: Johan Novak <wickedtree@wickedtree.codes> * SSR descriptions, version edit page * Working version editing + dependency management (besides files) * Version create page, file functionality * Fix some issues with the version page * More versions page fixes * Project gallery * Box shadows, user profile page, WIP header * Finish user dashboard * Finish search and fix minor issues * Moderator page + messages, notifications page * Fix dropdown menu, fix XSS, fix team members page * Change doc url on main page (#309) * Re-Fix docs url (#313) * Clean up. Part 1: Fix immediate problems (#316) * Clean up tabs and cards CSS a little * Fix project page; Remove bad styles from search * Yeet and flatten lots of styles; fix font sizes * Restyle search; fix moderation * Fix profile page * Remove injected SCSS entirely * Fix a mobile layout overflowing * Apiv2-support fixes (#320) * Fix member user_id -> user.id * Fix incorrect report redirect * Change theme switcher from button to multiselect * Fix remaining items Co-authored-by: Jai A <jaiagr+gpg@pm.me> * Fix bugs * Full mobile support, update create project page, fix various bugs * New Dark Mode brand colors (#325) * Use "color-brand-hover" for auth-prompt when hover over * New dark mode brand colors * Fix new version featured bug * Remove old home page, other fixes * Fix error when merging * Fix prettier error :( Co-authored-by: Jai A <jaiagr+gpg@pm.me> Co-authored-by: venashial <venashial.levo@aleeas.com> Co-authored-by: Prospector <6166773+Prospector@users.noreply.github.com> Co-authored-by: Emma <emmaffle@modrinth.com> Co-authored-by: Johan Novak <wickedtree@wickedtree.codes> Co-authored-by: Jai A <jaiagr@pm.me> Co-authored-by: Mysterious_Dev <40738104+Mysterious-Dev@users.noreply.github.com> Co-authored-by: Mikhail Oleynikov <contact@falseresync.ru> Co-authored-by: Christian Popov <30723811+Xrey274@users.noreply.github.com>
This commit is contained in:
@@ -1,129 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="section-header">
|
||||
<h3 class="column-grow-1">Followed mods</h3>
|
||||
</div>
|
||||
<div v-if="mods.length !== 0">
|
||||
<ModCard
|
||||
v-for="(mod, index) in mods"
|
||||
:id="mod.id"
|
||||
:key="mod.id"
|
||||
:author="mod.author"
|
||||
:author-url="mod.author_url"
|
||||
:categories="mod.categories"
|
||||
:created-at="mod.published"
|
||||
:description="mod.description"
|
||||
:downloads="mod.downloads.toString()"
|
||||
:edit-mode="true"
|
||||
:icon-url="mod.icon_url"
|
||||
:is-modrinth="true"
|
||||
:latest-version="mod.latest_version"
|
||||
:name="mod.title"
|
||||
:page-url="mod.page_url"
|
||||
:updated-at="mod.updated"
|
||||
>
|
||||
<div class="buttons">
|
||||
<button
|
||||
class="button column unfav-button iconified-button"
|
||||
@click="unfavMod(index)"
|
||||
>
|
||||
<FollowIcon />
|
||||
Unfollow
|
||||
</button>
|
||||
</div>
|
||||
</ModCard>
|
||||
</div>
|
||||
<div v-else class="error">
|
||||
<FollowIllustration class="icon"></FollowIllustration>
|
||||
<br />
|
||||
<span class="text"
|
||||
>You don't have any followed mods. <br />
|
||||
Why don't you <nuxt-link class="link" to="/mods">search</nuxt-link> for
|
||||
new ones?</span
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ModCard from '~/components/ui/ProjectCard'
|
||||
import FollowIcon from '~/assets/images/utils/heart.svg?inline'
|
||||
import FollowIllustration from '~/assets/images/illustrations/follow_illustration.svg?inline'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
ModCard,
|
||||
FollowIcon,
|
||||
FollowIllustration,
|
||||
},
|
||||
async asyncData(data) {
|
||||
const res = await data.$axios.get(
|
||||
`user/${data.$auth.user.id}/follows`,
|
||||
data.$auth.headers
|
||||
)
|
||||
|
||||
const mods = (
|
||||
await data.$axios.get(`mods?ids=${JSON.stringify(res.data)}`)
|
||||
).data.sort((a, b) => a.title > b.title)
|
||||
|
||||
return {
|
||||
mods,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async unfavMod(index) {
|
||||
await this.$axios.delete(
|
||||
`mod/${this.mods[index].id}/follow`,
|
||||
this.$auth.headers
|
||||
)
|
||||
|
||||
this.mods.splice(index, 1)
|
||||
},
|
||||
},
|
||||
head: {
|
||||
title: 'Followed Mods - Modrinth',
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.button {
|
||||
margin: 0.25rem 2rem 0.25rem 0;
|
||||
}
|
||||
|
||||
.buttons {
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.unfav-button {
|
||||
margin-left: auto;
|
||||
padding: 0.5rem;
|
||||
}
|
||||
|
||||
.error {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
.icon {
|
||||
width: 8rem;
|
||||
height: 8rem;
|
||||
margin: 1.5rem 0;
|
||||
}
|
||||
|
||||
.text {
|
||||
margin-bottom: 2rem;
|
||||
font-size: 1.25rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.link {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,114 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="section-header columns">
|
||||
<h3 class="column-grow-1">Revoke your Modrinth token</h3>
|
||||
</div>
|
||||
<section class="essentials pad-maker">
|
||||
<p>
|
||||
Revoking your Modrinth token can have unintended consequences. Please be
|
||||
aware that the following could break:
|
||||
</p>
|
||||
<ul>
|
||||
<li>Any application that uses your token to access the API.</li>
|
||||
<li>
|
||||
Gradle - if Minotaur is given a incorrect token, your Gradle builds
|
||||
could fail.
|
||||
</li>
|
||||
<li>
|
||||
GitHub - if you use a GitHub action that uses the Modrinth API, it
|
||||
will cause errors.
|
||||
</li>
|
||||
</ul>
|
||||
<p>If you are willing to continue, complete the following steps:</p>
|
||||
<ol>
|
||||
<li>
|
||||
<a
|
||||
href="https://github.com/settings/connections/applications/3acffb2e808d16d4b226"
|
||||
target="_blank"
|
||||
>
|
||||
Head to the Modrinth Application page on GitHub.
|
||||
</a>
|
||||
Make sure to be logged into the GitHub account you used for Modrinth!
|
||||
</li>
|
||||
<li>
|
||||
Press the big red "Revoke Access" button next to the "Permissions"
|
||||
header.
|
||||
</li>
|
||||
</ol>
|
||||
<p>
|
||||
Once you have completed those steps, press the continue button below.
|
||||
</p>
|
||||
<p>
|
||||
<strong>
|
||||
This will log you out of Modrinth, however, when you log back in, your
|
||||
token will be regenerated.
|
||||
</strong>
|
||||
</p>
|
||||
<button @click="logout">Continue</button>
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
components: {},
|
||||
methods: {
|
||||
logout() {
|
||||
this.$cookies.set('auth-token-reset', true)
|
||||
window.location.href = '/'
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.pad-rem {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.pad-maker {
|
||||
margin-top: var(--spacing-card-md);
|
||||
}
|
||||
|
||||
.save-btn-div {
|
||||
overflow: hidden;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.save-btn {
|
||||
float: right;
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--color-link);
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
section {
|
||||
@extend %card;
|
||||
padding: var(--spacing-card-md) var(--spacing-card-lg);
|
||||
}
|
||||
|
||||
label {
|
||||
display: flex;
|
||||
|
||||
span {
|
||||
flex: 2;
|
||||
padding-right: var(--spacing-card-lg);
|
||||
}
|
||||
|
||||
input {
|
||||
flex: 3;
|
||||
height: fit-content;
|
||||
}
|
||||
|
||||
button {
|
||||
:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
height: fit-content;
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,192 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="section-header">
|
||||
<h3 class="column-grow-1">Mods</h3>
|
||||
</div>
|
||||
<div v-if="mods.length !== 0">
|
||||
<ModCard
|
||||
v-for="(mod, index) in mods"
|
||||
:id="mod.id"
|
||||
:key="mod.id"
|
||||
:author="mod.author"
|
||||
:author-url="mod.author_url"
|
||||
:categories="mod.categories"
|
||||
:created-at="mod.published"
|
||||
:description="mod.description"
|
||||
:downloads="mod.downloads.toString()"
|
||||
:edit-mode="true"
|
||||
:icon-url="mod.icon_url"
|
||||
:is-modrinth="true"
|
||||
:latest-version="mod.latest_version"
|
||||
:name="mod.title"
|
||||
:page-url="mod.page_url"
|
||||
:status="mod.status"
|
||||
:updated-at="mod.updated"
|
||||
>
|
||||
<div class="buttons">
|
||||
<button
|
||||
class="button column approve"
|
||||
@click="changeModStatus(mod.id, 'approved', index)"
|
||||
>
|
||||
Approve
|
||||
</button>
|
||||
<button
|
||||
class="button column unlist"
|
||||
@click="changeModStatus(mod.id, 'unlisted', index)"
|
||||
>
|
||||
Unlist
|
||||
</button>
|
||||
<button
|
||||
class="button column reject"
|
||||
@click="changeModStatus(mod.id, 'rejected', index)"
|
||||
>
|
||||
Reject
|
||||
</button>
|
||||
</div>
|
||||
</ModCard>
|
||||
</div>
|
||||
<div v-else class="error">
|
||||
<Security class="icon"></Security>
|
||||
<br />
|
||||
<span class="text">You are up-to-date!</span>
|
||||
</div>
|
||||
<div class="section-header">
|
||||
<h3 class="column-grow-1">Reports</h3>
|
||||
</div>
|
||||
<div v-if="reports.length !== 0">
|
||||
<div v-for="(report, index) in reports" :key="report.id" class="report">
|
||||
<div class="header">
|
||||
<h5 class="title">
|
||||
Report for {{ report.item_type }}
|
||||
<nuxt-link
|
||||
:to="
|
||||
'/' + report.item_type + '/' + report.item_id.replace(/\W/g, '')
|
||||
"
|
||||
>{{ report.item_id }}
|
||||
</nuxt-link>
|
||||
</h5>
|
||||
<p
|
||||
v-tooltip="
|
||||
$dayjs(report.created).format(
|
||||
'[Created at] YYYY-MM-DD [at] HH:mm A'
|
||||
)
|
||||
"
|
||||
class="date"
|
||||
>
|
||||
Created {{ $dayjs(report.created).fromNow() }}
|
||||
</p>
|
||||
<button class="delete iconified-button" @click="deleteReport(index)">
|
||||
Delete
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
v-compiled-markdown="report.body"
|
||||
v-highlightjs
|
||||
class="markdown-body"
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="error">
|
||||
<Security class="icon"></Security>
|
||||
<br />
|
||||
<span class="text">You are up-to-date!</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ModCard from '~/components/ui/ProjectCard'
|
||||
import Security from '~/assets/images/illustrations/security.svg?inline'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
ModCard,
|
||||
Security,
|
||||
},
|
||||
async asyncData(data) {
|
||||
const mods = (await data.$axios.get(`moderation/mods`, data.$auth.headers))
|
||||
.data
|
||||
|
||||
const reports = (await data.$axios.get(`report`, data.$auth.headers)).data
|
||||
|
||||
return {
|
||||
mods,
|
||||
reports,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async changeModStatus(id, status, index) {
|
||||
await this.$axios.patch(
|
||||
`mod/${id}`,
|
||||
{
|
||||
status,
|
||||
},
|
||||
this.$auth.headers
|
||||
)
|
||||
|
||||
this.mods.splice(index, 1)
|
||||
},
|
||||
async deleteReport(index) {
|
||||
await this.$axios.delete(
|
||||
`report/${this.reports[index].id}`,
|
||||
this.$auth.headers
|
||||
)
|
||||
|
||||
this.reports.splice(index, 1)
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.button {
|
||||
margin: 0 5rem 0.5rem auto;
|
||||
}
|
||||
|
||||
.buttons {
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.report {
|
||||
@extend %card-spaced-b;
|
||||
padding: var(--spacing-card-sm) var(--spacing-card-lg);
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
|
||||
.title {
|
||||
font-size: var(--font-size-lg);
|
||||
margin: 0 0.5rem 0 0;
|
||||
}
|
||||
|
||||
.iconified-button {
|
||||
margin-left: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.error {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
.icon {
|
||||
width: 8rem;
|
||||
height: 8rem;
|
||||
margin: 1.5rem 0;
|
||||
}
|
||||
|
||||
.text {
|
||||
margin-bottom: 2rem;
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,167 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="section-header columns">
|
||||
<h3 class="column-grow-1">My notifications</h3>
|
||||
</div>
|
||||
<div v-if="notifications.length !== 0">
|
||||
<div
|
||||
v-for="(notification, notificationIndex) in notifications"
|
||||
:key="notification.id"
|
||||
class="notification columns"
|
||||
>
|
||||
<div class="text">
|
||||
<nuxt-link :to="'/' + notification.link" class="top-wrapper">
|
||||
<h3 class="title">
|
||||
{{ notification.title }}
|
||||
</h3>
|
||||
<p
|
||||
v-tooltip="
|
||||
$dayjs(notification.created).format(
|
||||
'[Created at] YYYY-MM-DD [at] HH:mm A'
|
||||
)
|
||||
"
|
||||
class="date"
|
||||
>
|
||||
Notified {{ $dayjs(notification.created).fromNow() }}
|
||||
</p>
|
||||
</nuxt-link>
|
||||
<p class="description">
|
||||
{{ notification.text }}
|
||||
</p>
|
||||
</div>
|
||||
<div v-if="notification.actions.length > 0" class="actions">
|
||||
<button
|
||||
v-for="(action, actionIndex) in notification.actions"
|
||||
:key="actionIndex"
|
||||
@click="performAction(notification, notificationIndex, actionIndex)"
|
||||
>
|
||||
{{ action.title }}
|
||||
</button>
|
||||
</div>
|
||||
<div v-else class="actions">
|
||||
<button @click="performAction(notification, notificationIndex, null)">
|
||||
Dismiss
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="error">
|
||||
<UpToDate class="icon"></UpToDate>
|
||||
<br />
|
||||
<span class="text">You are up-to-date!</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import UpToDate from '~/assets/images/illustrations/up_to_date.svg?inline'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
UpToDate,
|
||||
},
|
||||
async asyncData(data) {
|
||||
const notifications = (
|
||||
await data.$axios.get(
|
||||
`user/${data.$auth.user.id}/notifications`,
|
||||
data.$auth.headers
|
||||
)
|
||||
).data.sort((a, b) => new Date(b.created) - new Date(a.created))
|
||||
|
||||
return {
|
||||
notifications,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async performAction(notification, notificationIndex, actionIndex) {
|
||||
this.$nuxt.$loading.start()
|
||||
|
||||
try {
|
||||
if (actionIndex !== null) {
|
||||
const config = {
|
||||
method: notification.actions[
|
||||
actionIndex
|
||||
].action_route[0].toLowerCase(),
|
||||
url: `${notification.actions[actionIndex].action_route[1]}`,
|
||||
headers: {
|
||||
Authorization: this.$auth.token,
|
||||
},
|
||||
}
|
||||
|
||||
await this.$axios(config)
|
||||
}
|
||||
|
||||
await this.$axios.delete(
|
||||
`notification/${notification.id}`,
|
||||
this.$auth.headers
|
||||
)
|
||||
|
||||
this.notifications.splice(notificationIndex, 1)
|
||||
this.$store.dispatch('user/fetchNotifications', { force: true })
|
||||
} catch (err) {
|
||||
this.$notify({
|
||||
group: 'main',
|
||||
title: 'An error occurred',
|
||||
text: err.response.data.description,
|
||||
type: 'error',
|
||||
})
|
||||
}
|
||||
|
||||
this.$nuxt.$loading.finish()
|
||||
},
|
||||
},
|
||||
head: {
|
||||
title: 'Notifications - Modrinth',
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.notification {
|
||||
@extend %card;
|
||||
padding: var(--spacing-card-sm) var(--spacing-card-lg);
|
||||
margin-bottom: var(--spacing-card-sm);
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
.text {
|
||||
.top-wrapper {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: baseline;
|
||||
|
||||
.title {
|
||||
font-size: var(--font-size-lg);
|
||||
margin: 0 0.5rem 0 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.error {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
.icon {
|
||||
width: 8rem;
|
||||
height: 8rem;
|
||||
margin: 1.5rem 0;
|
||||
}
|
||||
|
||||
.text {
|
||||
margin-bottom: 2rem;
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
|
||||
.link {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,177 +0,0 @@
|
||||
<!--suppress HtmlFormInputWithoutLabel -->
|
||||
<template>
|
||||
<div class="popup card">
|
||||
<div class="consent-container">
|
||||
<div class="h1">Privacy settings</div>
|
||||
<div>
|
||||
Modrinth relies on different providers and in-house tools to allow us to
|
||||
provide custom-tailored experiences and personalized advertising. You
|
||||
can change your privacy settings at any time by going to this settings
|
||||
page, via the dashboard or via the footer of any page.
|
||||
</div>
|
||||
<br class="divider" />
|
||||
<div class="toggles">
|
||||
<div v-for="(scope, id) in scopes" :key="id" class="toggle">
|
||||
<div class="toggle-text">
|
||||
<div class="title">{{ scope.title }}</div>
|
||||
<div class="contents">
|
||||
{{ scope.description }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="spacer"></div>
|
||||
<div class="toggle-action">
|
||||
<label :for="id"></label>
|
||||
<input
|
||||
:id="id"
|
||||
ref="toggles"
|
||||
v-model="scopes[id].value"
|
||||
type="checkbox"
|
||||
class="switch stylized-toggle"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="actions">
|
||||
<button class="btn button" @click="toggleOff">Refuse All</button>
|
||||
<button class="btn button" @click="toggleOn">Accept All</button>
|
||||
<button class="btn brand-button" @click="confirm">
|
||||
Confirm my choices
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/* eslint-disable require-await */
|
||||
import scopes from '@/privacy-toggles'
|
||||
export default {
|
||||
name: 'Privacy',
|
||||
fetch() {
|
||||
this.$store.dispatch('consent/loadFromCookies', this.$cookies)
|
||||
|
||||
if (this.$store.state.consent.is_consent_given) {
|
||||
Object.keys(scopes.settings).forEach((key) => {
|
||||
scopes.settings[key].value = false
|
||||
})
|
||||
|
||||
// Load the allowed scopes from the store
|
||||
this.$store.state.consent.scopes_allowed.forEach((scope) => {
|
||||
if (this.scopes[scope] != null)
|
||||
this.$set(this.scopes[scope], 'value', true)
|
||||
})
|
||||
} else {
|
||||
Object.keys(scopes.settings).forEach((key) => {
|
||||
scopes.settings[key].value = scopes.settings[key].default
|
||||
})
|
||||
}
|
||||
},
|
||||
data: () => {
|
||||
const settings = scopes.settings
|
||||
return {
|
||||
scopes: settings,
|
||||
}
|
||||
},
|
||||
options: {
|
||||
auth: false,
|
||||
},
|
||||
methods: {
|
||||
toggleOff() {
|
||||
for (const elem in this.scopes) {
|
||||
this.$set(this.scopes[elem], 'value', false)
|
||||
}
|
||||
},
|
||||
toggleOn() {
|
||||
for (const elem in this.scopes) {
|
||||
this.$set(this.scopes[elem], 'value', true)
|
||||
}
|
||||
},
|
||||
confirm() {
|
||||
this.$store.commit('consent/set_consent', true)
|
||||
for (const elem in this.scopes) {
|
||||
if (this.scopes[elem].value === true) {
|
||||
this.$store.commit('consent/add_scope', elem)
|
||||
} else {
|
||||
this.$store.commit('consent/remove_scope', elem)
|
||||
}
|
||||
}
|
||||
this.$store.dispatch('consent/save', this.$cookies)
|
||||
this.$notify({
|
||||
group: 'main',
|
||||
title: 'Saved',
|
||||
text: 'Your preferences have been saved successfully.',
|
||||
type: 'success',
|
||||
})
|
||||
},
|
||||
},
|
||||
head: {
|
||||
title: 'Privacy Settings - Modrinth',
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.card {
|
||||
@extend %card;
|
||||
padding: var(--spacing-card-lg);
|
||||
}
|
||||
.popup {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.spacer {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
.actions {
|
||||
margin-top: 1rem;
|
||||
margin-right: -0.5rem;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: flex-end;
|
||||
flex-wrap: wrap;
|
||||
.btn {
|
||||
margin-top: 0.5rem;
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
}
|
||||
.consent-container {
|
||||
.h1 {
|
||||
font-size: 2rem;
|
||||
font-weight: bold;
|
||||
margin-bottom: 0.6rem;
|
||||
}
|
||||
.divider {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
.toggles {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
.toggle {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
margin-bottom: 1rem;
|
||||
.toggle-text {
|
||||
.title {
|
||||
color: var(--color-text-dark);
|
||||
font-weight: bold;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
.contents {
|
||||
color: var(--color-text);
|
||||
}
|
||||
}
|
||||
.spacer {
|
||||
flex-grow: 1;
|
||||
}
|
||||
.toggle-action {
|
||||
margin-left: 1rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
margin-right: 1rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,116 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="section-header columns">
|
||||
<h3 class="column-grow-1">My mods</h3>
|
||||
<nuxt-link class="brand-button column" to="/mod/create">
|
||||
Create a mod
|
||||
</nuxt-link>
|
||||
</div>
|
||||
<div v-if="mods.length !== 0">
|
||||
<ModCard
|
||||
v-for="mod in mods"
|
||||
:id="mod.slug ? mod.slug : mod.id"
|
||||
:key="mod.id"
|
||||
:author="mod.author"
|
||||
:name="mod.title"
|
||||
:description="mod.description"
|
||||
:latest-version="mod.latest_version"
|
||||
:created-at="mod.published"
|
||||
:updated-at="mod.updated"
|
||||
:downloads="mod.downloads.toString()"
|
||||
:icon-url="mod.icon_url"
|
||||
:author-url="mod.author_url"
|
||||
:page-url="mod.page_url"
|
||||
:categories="mod.categories"
|
||||
:edit-mode="true"
|
||||
:status="mod.status"
|
||||
:is-modrinth="true"
|
||||
>
|
||||
<nuxt-link
|
||||
class="button column edit-button"
|
||||
:to="'/mod/' + mod.id + '/settings'"
|
||||
>
|
||||
Settings
|
||||
</nuxt-link>
|
||||
</ModCard>
|
||||
</div>
|
||||
<div v-else class="error">
|
||||
<UpToDate class="icon"></UpToDate><br />
|
||||
<span class="text"
|
||||
>You don't have any mods.<br />
|
||||
Would you like to
|
||||
<nuxt-link class="link" to="/mod/create">create one</nuxt-link>?</span
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ModCard from '~/components/ui/ProjectCard'
|
||||
import UpToDate from '~/assets/images/illustrations/up_to_date.svg?inline'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
ModCard,
|
||||
UpToDate,
|
||||
},
|
||||
async asyncData(data) {
|
||||
let res = await data.$axios.get(
|
||||
`user/${data.$auth.user.id}/mods`,
|
||||
data.$auth.headers
|
||||
)
|
||||
|
||||
res = await data.$axios.get(
|
||||
`mods?ids=${JSON.stringify(res.data)}`,
|
||||
data.$auth.headers
|
||||
)
|
||||
|
||||
return {
|
||||
mods: res.data,
|
||||
}
|
||||
},
|
||||
head: {
|
||||
title: 'My Mods - Modrinth',
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.mod-name {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.edit-button {
|
||||
align-self: flex-end;
|
||||
margin-right: 2rem;
|
||||
}
|
||||
|
||||
.error {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
.icon {
|
||||
width: 8rem;
|
||||
height: 8rem;
|
||||
margin: 1.5rem 0;
|
||||
}
|
||||
|
||||
.text {
|
||||
text-align: center;
|
||||
margin-bottom: 2rem;
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
|
||||
.link {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
// .buttonse {
|
||||
// margin-left: 4.5rem;
|
||||
// padding: 0.5rem 2rem 0.5rem 2rem;
|
||||
// }
|
||||
</style>
|
||||
@@ -1,242 +0,0 @@
|
||||
/* eslint-disable vue/attribute-hyphenation */
|
||||
<template>
|
||||
<div>
|
||||
<ConfirmPopup
|
||||
ref="delete_popup"
|
||||
title="Are you sure you want to delete your account?"
|
||||
description="If you proceed, your user and all attached data will be removed from our
|
||||
servers. This cannot be reversed, so be careful!"
|
||||
proceed-label="Delete account"
|
||||
:confirmation-text="username"
|
||||
:has-to-type="true"
|
||||
@proceed="deleteAccount"
|
||||
/>
|
||||
<div class="section-header columns">
|
||||
<h3 class="column-grow-1">Settings</h3>
|
||||
<button class="brand-button column" @click="editProfile">Save</button>
|
||||
</div>
|
||||
<section>
|
||||
<h3>Username</h3>
|
||||
<label>
|
||||
<span>
|
||||
The username used on Modrinth to identify yourself. This must be
|
||||
unique.
|
||||
</span>
|
||||
<input
|
||||
v-model="username"
|
||||
type="text"
|
||||
placeholder="Enter your username"
|
||||
/>
|
||||
</label>
|
||||
<h3>Name</h3>
|
||||
<label>
|
||||
<span>
|
||||
Your display name on your Modrinth profile. This does not have to be
|
||||
unique, can be set to anything, and is optional.
|
||||
</span>
|
||||
<input v-model="name" type="text" placeholder="Enter your name" />
|
||||
</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.
|
||||
</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.
|
||||
</span>
|
||||
<input v-model="bio" type="text" placeholder="Enter your bio" />
|
||||
</label>
|
||||
</section>
|
||||
<section class="pad-maker">
|
||||
<h3>Theme</h3>
|
||||
<label>
|
||||
<span
|
||||
>Change the global site theme of Modrinth. You can choose between
|
||||
light mode and dark mode. You can switch it using this button or
|
||||
anywhere by accessing the theme switcher in the navigation bar
|
||||
dropdown.</span
|
||||
>
|
||||
<input
|
||||
type="button"
|
||||
class="button pad-rem"
|
||||
value="Change theme"
|
||||
@click="changeTheme"
|
||||
/>
|
||||
</label>
|
||||
</section>
|
||||
<section class="pad-maker">
|
||||
<h3>Authorization token</h3>
|
||||
<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!
|
||||
</span>
|
||||
<input
|
||||
type="button"
|
||||
class="button pad-rem"
|
||||
value="Copy to clipboard"
|
||||
@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
|
||||
>
|
||||
<input
|
||||
type="button"
|
||||
class="button"
|
||||
value="Revoke token"
|
||||
@click="gotoRevoke"
|
||||
/>
|
||||
</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
|
||||
>
|
||||
<input
|
||||
value="Delete Account"
|
||||
type="button"
|
||||
class="button"
|
||||
@click="showPopup"
|
||||
/>
|
||||
</label>
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ConfirmPopup from '~/components/ui/ConfirmPopup'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
ConfirmPopup,
|
||||
},
|
||||
fetch() {
|
||||
this.username = this.$auth.user.username
|
||||
this.name = this.$auth.user.name
|
||||
this.email = this.$auth.user.email
|
||||
this.bio = this.$auth.user.bio
|
||||
this.token = this.$auth.token
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
username: '',
|
||||
name: '',
|
||||
email: '',
|
||||
bio: '',
|
||||
token: '',
|
||||
confirm_delete: false,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
changeTheme() {
|
||||
this.$colorMode.preference =
|
||||
this.$colorMode.value === 'dark' ? 'light' : 'dark'
|
||||
},
|
||||
gotoRevoke() {
|
||||
this.$router.replace('/dashboard/misc/revoke-token')
|
||||
},
|
||||
async copyToken() {
|
||||
await navigator.clipboard.writeText(this.token)
|
||||
this.$notify({
|
||||
group: 'main',
|
||||
title: 'Copied to clipboard.',
|
||||
text: 'Copied your Modrinth token to the clipboard.',
|
||||
type: 'success',
|
||||
})
|
||||
},
|
||||
async editProfile() {
|
||||
this.$nuxt.$loading.start()
|
||||
|
||||
try {
|
||||
const data = {
|
||||
username: this.username,
|
||||
name: this.name,
|
||||
email: this.email,
|
||||
bio: this.bio,
|
||||
}
|
||||
|
||||
await this.$axios.patch(
|
||||
`user/${this.$auth.user.id}`,
|
||||
data,
|
||||
this.$auth.headers
|
||||
)
|
||||
|
||||
await this.$store.dispatch('auth/fetchUser', {
|
||||
token: this.$auth.token,
|
||||
})
|
||||
} catch (err) {
|
||||
this.$notify({
|
||||
group: 'main',
|
||||
title: 'An error occurred',
|
||||
text: err.response.data.description,
|
||||
type: 'error',
|
||||
})
|
||||
}
|
||||
|
||||
this.$nuxt.$loading.finish()
|
||||
},
|
||||
async deleteAccount() {
|
||||
this.$nuxt.$loading.start()
|
||||
|
||||
try {
|
||||
await this.$axios.delete(
|
||||
`user/${this.$auth.user.id}`,
|
||||
this.$auth.headers
|
||||
)
|
||||
} catch (err) {
|
||||
this.$notify({
|
||||
group: 'main',
|
||||
title: 'An error occurred',
|
||||
text: err.response.data.description,
|
||||
type: 'error',
|
||||
})
|
||||
}
|
||||
|
||||
this.$nuxt.$loading.finish()
|
||||
},
|
||||
showPopup() {
|
||||
this.$refs.delete_popup.show()
|
||||
},
|
||||
},
|
||||
head: {
|
||||
title: 'Settings - Modrinth',
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.pad-rem {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.pad-maker {
|
||||
margin-top: var(--spacing-card-md);
|
||||
}
|
||||
|
||||
.save-btn-div {
|
||||
overflow: hidden;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.save-btn {
|
||||
float: right;
|
||||
}
|
||||
|
||||
section {
|
||||
@extend %card;
|
||||
padding: var(--spacing-card-md) var(--spacing-card-lg);
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user