Add user settings page (#58)

* Several fixes

* User edit page
This commit is contained in:
Geometrically 2020-12-16 13:24:17 -07:00 committed by GitHub
parent 7934f65ae8
commit a38458c9be
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 322 additions and 219 deletions

View File

Before

Width:  |  Height:  |  Size: 921 B

After

Width:  |  Height:  |  Size: 921 B

View File

@ -0,0 +1,62 @@
<template>
<div class="page-container">
<div class="page-contents">
<div class="sidebar-l">
<div class="card page-nav">
<nuxt-link :to="'/dashboard/projects'" class="tab last">
<ModIcon />
My mods
</nuxt-link>
<nuxt-link
v-if="
$auth.user.role === 'admin' || $auth.user.role === 'moderator'
"
:to="'/dashboard/moderation'"
class="tab last"
>
<ModerationIcon />
Moderation
</nuxt-link>
<nuxt-link :to="'/dashboard/settings'" class="tab last">
<SettingsIcon />
Settings
</nuxt-link>
</div>
<m-footer class="footer" />
<client-only>
<EthicalAd type="image" />
</client-only>
</div>
<div class="content">
<slot />
</div>
</div>
</div>
</template>
<script>
import ModIcon from '~/assets/images/sidebar/mod.svg?inline'
import ModerationIcon from '~/assets/images/sidebar/admin.svg?inline'
import SettingsIcon from '~/assets/images/sidebar/settings.svg?inline'
export default {
name: 'DashboardPage',
components: {
ModIcon,
ModerationIcon,
SettingsIcon,
},
}
</script>
<style lang="scss" scoped>
.section-header {
@extend %card;
padding: var(--spacing-card-md) var(--spacing-card-lg);
margin-bottom: var(--spacing-card-md);
h3 {
margin: auto 0;
color: var(--color-text-dark);
font-weight: var(--font-weight-extrabold);
}
}
</style>

View File

@ -63,12 +63,6 @@
<span v-else>Light Mode</span>
</button>
</li>
<li v-tooltip="'Not implemented yet'" class="hidden">
<NuxtLink to="/settings" disabled>
<SettingsIcon />
<span>Settings</span>
</NuxtLink>
</li>
<hr />
<li>
<button @click="logout">
@ -100,13 +94,6 @@
<AnalyticsIcon />
<span>Analytics</span>
</NuxtLink>
<!-- <NuxtLink
v-if="this.$auth.user.role === 'admin'"
to="/dashboard/admin"
>
<AdminIcon />
<span>Admin</span>
</NuxtLink> -->
</section>
<div>
<button class="hamburger" @click="toggleNav">
@ -170,7 +157,6 @@ import ModrinthLogoWhite from '~/assets/images/text-logo-white.svg?inline'
import ModpackIcon from '~/assets/images/sidebar/modpack.svg?inline'
import ProjectsIcon from '~/assets/images/sidebar/projects.svg?inline'
import AnalyticsIcon from '~/assets/images/sidebar/analytics.svg?inline'
// import AdminIcon from '~/assets/images/sidebar/admin.svg?inline'
import DropdownIcon from '~/assets/images/utils/dropdown.svg?inline'
import HamburgerIcon from '~/assets/images/utils/hamburger.svg?inline'
@ -180,7 +166,6 @@ import SunIcon from '~/assets/images/utils/sun.svg?inline'
import UserIcon from '~/assets/images/utils/user.svg?inline'
import UsersIcon from '~/assets/images/utils/users.svg?inline'
import SettingsIcon from '~/assets/images/utils/settings.svg?inline'
import LogOutIcon from '~/assets/images/utils/log-out.svg?inline'
import GitHubIcon from '~/assets/images/utils/github.svg?inline'
@ -191,7 +176,6 @@ export default {
ModpackIcon,
ProjectsIcon,
AnalyticsIcon,
// AdminIcon,
DropdownIcon,
HamburgerIcon,
ExitIcon,
@ -199,7 +183,6 @@ export default {
SunIcon,
UserIcon,
UsersIcon,
SettingsIcon,
LogOutIcon,
GitHubIcon,
},

View File

@ -1,86 +1,56 @@
<template>
<div class="page-container">
<div class="page-contents">
<div class="sidebar-l">
<div class="card page-nav">
<nuxt-link :to="'/dashboard/projects'" class="tab last">
<ModIcon />
My mods
</nuxt-link>
<nuxt-link
v-if="
$auth.user.role === 'admin' || $auth.user.role === 'moderator'
"
:to="'/dashboard/moderation'"
class="tab last"
>
<ModerationIcon />
Moderation
</nuxt-link>
</div>
<m-footer class="footer" />
<client-only>
<EthicalAd type="image" />
</client-only>
</div>
<div class="content">
<div class="section-header">
<h3 class="column-grow-1">Mods</h3>
</div>
<ModCard
v-for="mod in mods"
:id="mod.id"
:key="mod.id"
:author="mod.author"
:name="mod.title"
:description="mod.description"
:latest-version="mod.latest_version"
:created-at="mod.published"
:updated-at="mod.updated"
:downloads="mod.downloads.toString()"
:icon-url="mod.icon_url"
:author-url="mod.author_url"
:page-url="mod.page_url"
:categories="mod.categories"
:edit-mode="true"
:status="mod.status"
:is-modrinth="true"
>
<button
class="button column approve"
@click="changeModStatus(mod.id, 'approved')"
>
Approve
</button>
<button
class="button column reject"
@click="changeModStatus(mod.id, 'rejected')"
>
Reject
</button>
</ModCard>
<div class="section-header">
<h3 class="column-grow-1">Versions</h3>
</div>
</div>
<DashboardPage>
<div class="section-header">
<h3 class="column-grow-1">Mods</h3>
</div>
</div>
<ModCard
v-for="mod in mods"
:id="mod.id"
:key="mod.id"
:author="mod.author"
:name="mod.title"
:description="mod.description"
:latest-version="mod.latest_version"
:created-at="mod.published"
:updated-at="mod.updated"
:downloads="mod.downloads.toString()"
:icon-url="mod.icon_url"
:author-url="mod.author_url"
:page-url="mod.page_url"
:categories="mod.categories"
:edit-mode="true"
:status="mod.status"
:is-modrinth="true"
>
<button
class="button column approve"
@click="changeModStatus(mod.id, 'approved')"
>
Approve
</button>
<button
class="button column reject"
@click="changeModStatus(mod.id, 'rejected')"
>
Reject
</button>
</ModCard>
<div class="section-header">
<h3 class="column-grow-1">Versions</h3>
</div>
</DashboardPage>
</template>
<script>
import axios from 'axios'
import EthicalAd from '@/components/EthicalAd'
import ModCard from '@/components/ProjectCard'
import ModIcon from '~/assets/images/sidebar/mod.svg?inline'
import ModerationIcon from '~/assets/images/sidebar/admin.svg?inline'
import ModCard from '@/components/ProjectCard'
import DashboardPage from '@/components/DashboardPage'
export default {
components: {
EthicalAd,
DashboardPage,
ModCard,
ModIcon,
ModerationIcon,
},
async asyncData(data) {
const config = {
@ -133,17 +103,6 @@ export default {
</script>
<style lang="scss" scoped>
.section-header {
@extend %card;
padding: var(--spacing-card-md) var(--spacing-card-lg);
margin-bottom: var(--spacing-card-md);
h3 {
margin: auto 0;
color: var(--color-text-dark);
font-weight: var(--font-weight-extrabold);
}
}
.button {
margin: 0.25rem 0;
}

View File

@ -1,79 +1,46 @@
<template>
<div class="page-container">
<div class="page-contents">
<div class="sidebar-l">
<div class="card page-nav">
<nuxt-link :to="'/dashboard/projects'" class="tab last">
<ModIcon />
My mods
</nuxt-link>
<nuxt-link
v-if="
$auth.user.role === 'admin' || $auth.user.role === 'moderator'
"
:to="'/dashboard/moderation'"
class="tab last"
>
<ModerationIcon />
Moderation
</nuxt-link>
</div>
<m-footer class="footer" />
<client-only>
<EthicalAd type="image" />
</client-only>
</div>
<div class="content">
<div class="section-header columns">
<h3 class="column-grow-1">My mods</h3>
<nuxt-link class="brand-button column" to="/mod/create">
Create a mod
</nuxt-link>
</div>
<ModCard
v-for="mod in mods"
:id="mod.id"
:key="mod.id"
:author="mod.author"
:name="mod.title"
:description="mod.description"
:latest-version="mod.latest_version"
:created-at="mod.published"
:updated-at="mod.updated"
:downloads="mod.downloads.toString()"
:icon-url="mod.icon_url"
:author-url="mod.author_url"
:page-url="mod.page_url"
:categories="mod.categories"
:edit-mode="true"
:status="mod.status"
:is-modrinth="true"
>
<nuxt-link class="button column" :to="'/mod/' + mod.id + '/edit'">
Edit
</nuxt-link>
</ModCard>
</div>
<DashboardPage>
<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>
<ModCard
v-for="mod in mods"
:id="mod.id"
:key="mod.id"
:author="mod.author"
:name="mod.title"
:description="mod.description"
:latest-version="mod.latest_version"
:created-at="mod.published"
:updated-at="mod.updated"
:downloads="mod.downloads.toString()"
:icon-url="mod.icon_url"
:author-url="mod.author_url"
:page-url="mod.page_url"
:categories="mod.categories"
:edit-mode="true"
:status="mod.status"
:is-modrinth="true"
>
<nuxt-link class="button column" :to="'/mod/' + mod.id + '/edit'">
Edit
</nuxt-link>
</ModCard>
</DashboardPage>
</template>
<script>
import axios from 'axios'
import EthicalAd from '@/components/EthicalAd'
import ModCard from '@/components/ProjectCard'
import MFooter from '@/components/MFooter'
import ModIcon from '~/assets/images/sidebar/mod.svg?inline'
import ModerationIcon from '~/assets/images/sidebar/admin.svg?inline'
import DashboardPage from '@/components/DashboardPage'
export default {
components: {
EthicalAd,
DashboardPage,
ModCard,
ModIcon,
ModerationIcon,
MFooter,
},
async asyncData(data) {
const config = {
@ -102,17 +69,6 @@ export default {
</script>
<style lang="scss" scoped>
.section-header {
@extend %card;
padding: var(--spacing-card-md) var(--spacing-card-lg);
margin-bottom: var(--spacing-card-md);
h3 {
margin: auto 0;
color: var(--color-text-dark);
font-weight: var(--font-weight-extrabold);
}
}
.mod-name {
font-weight: bold;
}

View File

@ -0,0 +1,127 @@
<template>
<DashboardPage>
<div class="section-header columns">
<h3 class="column-grow-1">Settings</h3>
<button class="brand-button column" @click="editProfile">Save</button>
</div>
<section class="essentials">
<h3>Username</h3>
<label>
<span>
The username used on the modrinth site 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
displayed in any API routes or your profile. It is also optional.
</span>
<input v-model="email" type="email" placeholder="Enter your email" />
</label>
<h3>Bio</h3>
<label>
<span>
Give a quick description to your mod. It will appear in the search
</span>
<input v-model="bio" type="text" placeholder="Enter your bio" />
</label>
</section>
</DashboardPage>
</template>
<script>
import DashboardPage from '@/components/DashboardPage'
import axios from 'axios'
export default {
components: {
DashboardPage,
},
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
},
data() {
return {
username: '',
name: '',
email: '',
bio: '',
}
},
methods: {
async editProfile() {
const config = {
headers: {
Authorization: this.$auth.getToken('local'),
},
}
this.$nuxt.$loading.start()
try {
const data = {
username: this.username,
name: this.name,
email: this.email,
bio: this.bio,
}
await axios.patch(
`https://api.modrinth.com/api/v1/user/${this.$auth.user.id}`,
data,
config
)
} catch (err) {
this.$notify({
group: 'main',
title: 'An Error Occurred',
text: err.response.data.description,
type: 'error',
})
}
this.$nuxt.$loading.finish()
},
},
}
</script>
<style lang="scss" scoped>
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;
}
}
</style>

View File

@ -11,6 +11,9 @@ import ModPage from '@/components/ModPage'
export default {
components: { ModPage },
auth: false,
async fetch() {
this.body = (await axios.get(this.mod.body_url)).data
},
async asyncData(data) {
const config = {
headers: {
@ -27,38 +30,43 @@ export default {
)
).data
const [members, versions, body] = (
const [members, versions] = (
await Promise.all([
axios.get(
`https://api.modrinth.com/api/v1/team/${mod.team}/members`,
config
),
axios.get(`https://api.modrinth.com/api/v1/team/${mod.team}/members`),
axios.get(
`https://api.modrinth.com/api/v1/versions?ids=${JSON.stringify(
mod.versions
)}`,
config
),
axios.get(mod.body_url),
])
).map((it) => it.data)
const users = await Promise.all(
members.map((it) =>
axios.get(`https://api.modrinth.com/api/v1/user/${it.user_id}`, config)
const users = (
await axios.get(
`https://api.modrinth.com/api/v1/users?ids=${JSON.stringify(
members.map((it) => it.user_id)
)}`,
config
)
)
users.forEach(
(it, index) => (members[index].avatar_url = it.data.avatar_url)
)
).data
users.forEach((it, index) => {
members[index].avatar_url = it.avatar_url
members[index].name = it.username
})
return {
mod,
body,
versions: versions.reverse(),
members,
}
},
data() {
return {
body: '',
}
},
head() {
return {
title: this.mod.title + ' - Modrinth',

View File

@ -118,6 +118,11 @@ export default {
TagIcon,
},
auth: false,
async fetch() {
if (this.version.changelog_url) {
this.changelog = (await axios.get(this.version.changelog_url)).data
}
},
async asyncData(data) {
const config = {
headers: {
@ -136,10 +141,7 @@ export default {
const [members, versions] = (
await Promise.all([
axios.get(
`https://api.modrinth.com/api/v1/team/${mod.team}/members`,
config
),
axios.get(`https://api.modrinth.com/api/v1/team/${mod.team}/members`),
axios.get(
`https://api.modrinth.com/api/v1/versions?ids=${JSON.stringify(
mod.versions
@ -149,24 +151,24 @@ export default {
])
).map((it) => it.data)
const users = await Promise.all(
members.map((it) =>
axios.get(`https://api.modrinth.com/api/v1/user/${it.user_id}`, config)
const users = (
await axios.get(
`https://api.modrinth.com/api/v1/users?ids=${JSON.stringify(
members.map((it) => it.user_id)
)}`,
config
)
)
users.forEach(
(it, index) => (members[index].avatar_url = it.data.avatar_url)
)
).data
users.forEach((it, index) => {
members[index].avatar_url = it.avatar_url
members[index].name = it.username
})
const version = versions.find((x) => x.id === data.params.version)
version.author = members.find((x) => x.user_id === version.author_id)
let changelog = ''
if (version.changelog_url) {
changelog = (await axios.get(version.changelog_url)).data
}
let primaryFile = version.files.find((file) => file.primary)
if (!primaryFile) {
@ -178,12 +180,12 @@ export default {
versions,
members,
version,
changelog,
primaryFile,
}
},
data() {
return {
changelog: '',
filesToUpload: [],
}
},
@ -358,6 +360,10 @@ export default {
}
}
.markdown-body {
margin: 1rem 0;
}
.files {
display: flex;
@ -434,6 +440,6 @@ export default {
}
.file-input {
margin-top: 2rem;
margin-top: 1rem;
}
</style>

View File

@ -235,10 +235,7 @@ export default {
const [members, versions, selectableLoaders, selectableVersions] = (
await Promise.all([
axios.get(
`https://api.modrinth.com/api/v1/team/${mod.team}/members`,
config
),
axios.get(`https://api.modrinth.com/api/v1/team/${mod.team}/members`),
axios.get(
`https://api.modrinth.com/api/v1/versions?ids=${JSON.stringify(
mod.versions
@ -250,14 +247,19 @@ export default {
])
).map((it) => it.data)
const users = await Promise.all(
members.map((it) =>
axios.get(`https://api.modrinth.com/api/v1/user/${it.user_id}`, config)
const users = (
await axios.get(
`https://api.modrinth.com/api/v1/users?ids=${JSON.stringify(
members.map((it) => it.user_id)
)}`,
config
)
)
users.forEach(
(it, index) => (members[index].avatar_url = it.data.avatar_url)
)
).data
users.forEach((it, index) => {
members[index].avatar_url = it.avatar_url
members[index].name = it.username
})
return {
mod,