Lots of fixes - see trello (#347)

* A ton of fixes

* Fix project deletion message
This commit is contained in:
Geometrically
2022-01-28 18:11:34 -07:00
committed by GitHub
parent 643cd87706
commit 86f37863a7
25 changed files with 1132 additions and 741 deletions
+3 -3
View File
@@ -1,7 +1,7 @@
export default function (to, from, savedPosition) {
if (to.name.startsWith('type-id') && !from.name.startsWith('type-id')) {
return { x: 0, y: 0 }
} else {
if (to.name.startsWith('type-id') && from.name.startsWith('type-id')) {
return savedPosition
} else {
return { x: 0, y: 0 }
}
}
+43 -9
View File
@@ -31,8 +31,6 @@
box-shadow: inset 0px -1px 1px rgba(17, 24, 39, 0.1);
max-height: 2rem;
color: var(--color-button-text);
background-color: var(--color-button-bg);
text-decoration: none;
@@ -385,23 +383,19 @@
.multiselect {
color: var(--color-text) !important;
max-height: 40px;
input {
background: transparent;
}
&.top-margin {
.multiselect__tags {
padding-top: 10px;
}
}
.multiselect__tags {
border-radius: 1.25rem;
background: var(--color-dropdown-bg);
border: none;
cursor: pointer;
padding-left: 1rem;
padding-top: 10px;
&:active,
&:hover {
@@ -436,6 +430,8 @@
background: var(--color-dropdown-bg);
border: none;
overflow-x: hidden;
border-bottom-left-radius: var(--size-rounded-card);
border-bottom-right-radius: var(--size-rounded-card);
.multiselect__element {
.multiselect__option--highlight {
@@ -470,6 +466,10 @@ label {
span {
flex: 2;
padding-right: var(--spacing-card-lg);
&.no-padding {
padding-right: 0;
}
}
input,
@@ -518,8 +518,10 @@ label {
}
.stylized-toggle {
min-height: 32px;
height: 32px;
width: 52px;
max-width: 52px;
border-radius: 16px;
display: inline-block;
position: relative;
@@ -563,8 +565,14 @@ label {
height: 1.75rem;
width: 1.75rem;
border-radius: 1.5rem;
background-color: var(--color-button-bg);
color: var(--color-brand-inverted);
background-color: var(--color-brand);
margin-right: var(--spacing-card-sm);
&:hover {
background-color: var(--color-brand-hover);
}
svg {
width: 1.25rem;
margin: auto;
@@ -682,3 +690,29 @@ label {
// box-shadow: var(--shadow-card);
}
.vue-notification-group {
right: 25px !important;
.vue-notification-template {
border-radius: var(--size-rounded-card);
margin: 0 0 25px 0;
.notification-title {
font-size: var(--font-size-lg);
margin-right: auto;
}
.notification-content {
font-size: var(--font-size-md);
}
}
}
.card-divider {
background-color: var(--color-divider);
border: none;
color: var(--color-divider);
height: 1px;
margin: var(--spacing-card-bg) 0;
}
+2 -16
View File
@@ -2,10 +2,6 @@ html {
@extend .light-mode;
}
body {
overflow-y: scroll;
}
.light-mode {
--color-icon: #6b7280;
--color-text: hsl(221, 39%, 11%);
@@ -30,7 +26,7 @@ body {
--color-brand-3: #30b27b;
--color-brand-disabled: #e2e8f0;
--color-button-bg: #e6e7eb;
--color-button-bg: #e0e0e5;
--color-button-text: var(--color-text-dark);
--color-button-bg-hover: #d9dce0;
--color-button-text-hover: #1b1e24;
@@ -259,15 +255,9 @@ textarea {
padding: 0.5rem 1rem;
border: 2px solid transparent;
&:focus,
&:hover:not([disabled]) {
&:hover:not([disabled]):not(:focus) {
background: var(--color-button-bg-hover);
color: var(--color-text);
//outline: none; Bad for accessibility
&::placeholder {
color: var(--color-text);
}
}
&:focus {
@@ -275,10 +265,6 @@ textarea {
border-color: var(--color-divider-dark);
}
&::placeholder {
color: var(--color-color-text);
}
&:disabled,
&[disabled] {
opacity: 0.6;
+5 -1
View File
@@ -48,6 +48,11 @@
flex-direction: row;
margin: 0 auto;
max-width: 80rem;
column-gap: 0.75rem;
&.alt-layout {
flex-direction: row-reverse;
}
}
.normal-page__sidebar {
@@ -55,7 +60,6 @@
}
.normal-page__content {
padding-left: 0.75rem;
width: 60rem;
}
}
+2 -1
View File
@@ -6,6 +6,7 @@
width: 100%;
}
html {
body {
overflow-y: scroll;
overflow-x: hidden;
}
+19 -7
View File
@@ -198,8 +198,15 @@ export default {
},
},
methods: {
formatNumber(x) {
formatNumber(y) {
const x = +y
if (x >= 1000000) {
return (x / 1000000).toFixed(2).toString() + 'M'
} else if (x >= 10000) {
return (x / 1000).toFixed(1).toString() + 'K'
} else {
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',')
}
},
},
}
@@ -213,7 +220,6 @@ export default {
.project-card {
display: flex;
flex-direction: row;
flex-direction: column;
padding: var(--spacing-card-bg);
width: calc(100% - 2 * var(--spacing-card-bg));
@@ -300,8 +306,9 @@ export default {
margin-right: 2rem;
svg {
width: 1.25rem;
height: 1.25rem;
margin-right: 0.125rem;
margin-right: 0.25rem;
}
}
}
@@ -359,6 +366,15 @@ export default {
@media screen and (max-width: 560px) {
.card-content {
flex-direction: column;
.info {
.dates {
.date {
margin-bottom: 0.5rem;
}
}
}
.right-side {
padding-top: var(--spacing-card-sm);
@@ -368,10 +384,6 @@ export default {
margin-left: 0;
}
.buttons {
flex-direction: row;
}
.buttons button,
a {
margin-left: unset;
+24 -23
View File
@@ -226,7 +226,7 @@
<p>modrinth/knossos {{ version }}</p>
<p>© Guavy LLC</p>
</div>
<div class="links" role="region" aria-label="Legal">
<div class="links links-1" role="region" aria-label="Legal">
<h4 aria-hidden="true">Legal</h4>
<nuxt-link to="/legal/terms">Terms</nuxt-link>
<nuxt-link to="/legal/privacy">Privacy</nuxt-link>
@@ -238,7 +238,7 @@
License
</a>
</div>
<div class="links" role="region" aria-label="Resources">
<div class="links links-2" role="region" aria-label="Resources">
<h4 aria-hidden="true">Resources</h4>
<a target="_blank" href="https://blog.modrinth.com">Blog</a>
<a target="_blank" href="https://discord.gg/EUHuJHt">Discord</a>
@@ -275,26 +275,22 @@ import HomeIcon from '~/assets/images/sidebar/home.svg?inline'
import ModIcon from '~/assets/images/sidebar/mod.svg?inline'
import ModpackIcon from '~/assets/images/sidebar/modpack.svg?inline'
// import DropdownIcon from '~/assets/images/utils/dropdown.svg?inline'
import MoonIcon from '~/assets/images/utils/moon.svg?inline'
import SunIcon from '~/assets/images/utils/sun.svg?inline'
import PlusIcon from '~/assets/images/utils/plus.svg?inline'
// import UserIcon from '~/assets/images/utils/user.svg?inline'
import LogOutIcon from '~/assets/images/utils/log-out.svg?inline'
import GitHubIcon from '~/assets/images/utils/github.svg?inline'
import CookieConsent from '~/components/ads/CookieConsent'
const overflowStyle = 'overlay'
const overflowStyle = 'scroll'
export default {
components: {
ModrinthLogo,
// DropdownIcon,
MoonIcon,
SunIcon,
// UserIcon,
LogOutIcon,
GitHubIcon,
NotificationIcon,
@@ -322,6 +318,7 @@ export default {
await Promise.all([
this.$store.dispatch('user/fetchAll', { force: true }),
this.$store.dispatch('tag/fetchAllTags'),
this.$store.dispatch('cosmetics/fetchCosmetics', this.$cookies),
])
},
computed: {
@@ -335,8 +332,7 @@ export default {
this.isMobileMenuOpen =
this.$refs.mobileMenu.className === 'mobile-menu active'
document.documentElement.style.overflow = overflowStyle
document.body.style.overflow = overflowStyle
document.body.style.overflowY = overflowStyle
this.$store.dispatch('user/fetchAll')
},
@@ -356,12 +352,8 @@ export default {
}`
document.body.scrollTop = 0
document.documentElement.style.overflow =
document.documentElement.style.overflow !== 'hidden'
? 'hidden'
: overflowStyle
document.body.style.overflow =
document.body.style.overflow !== 'hidden' ? 'hidden' : overflowStyle
document.body.style.overflowY =
document.body.style.overflowY !== 'hidden' ? 'hidden' : overflowStyle
this.isMobileMenuOpen = !currentlyActive
},
@@ -393,12 +385,6 @@ export default {
</script>
<style lang="scss">
html {
overflow: auto;
//noinspection CssInvalidPropertyValue
overflow: overlay;
}
.layout {
min-height: 100vh;
background-color: var(--color-bg);
@@ -850,14 +836,20 @@ html {
footer {
margin: 6rem 0 2rem 0;
flex-wrap: wrap;
text-align: center;
display: grid;
grid-template:
'logo-info logo-info' auto
'links-1 links-2' auto
'buttons buttons' auto
/ 1fr 1fr;
.logo-info {
margin-left: auto;
margin-right: auto;
max-width: 22rem;
max-width: 20rem;
margin-bottom: 1rem;
grid-area: logo-info;
.text-logo {
width: 10rem;
@@ -878,11 +870,20 @@ html {
a {
margin: 0 0 1rem 0;
}
&.links-1 {
grid-area: links-1;
}
&.links-2 {
grid-area: links-2;
}
}
.buttons {
margin-left: auto;
margin-right: auto;
grid-area: buttons;
button,
a {
-1
View File
@@ -174,7 +174,6 @@ export default {
'~/plugins/vue-notification.js',
'~/plugins/xss.js',
'~/plugins/vue-syntax.js',
'~/plugins/auth.js',
'~/plugins/shorthands.js',
],
/*
+84 -49
View File
@@ -1,7 +1,11 @@
<template>
<div class="page-container">
<div class="page-contents">
<section class="project-info">
<div
:class="{
'page-contents': true,
'alt-layout': $store.state.cosmetics.projectLayout,
}"
>
<div class="header card">
<nuxt-link
:to="
@@ -68,7 +72,7 @@
{{ project.description }}
</p>
<Categories :categories="project.categories" class="categories" />
<hr />
<hr class="card-divider" />
<div class="stats">
<span class="stat">{{ formatNumber(project.downloads) }}</span>
<span class="label"
@@ -83,9 +87,7 @@
<div class="date">
<CalendarIcon aria-hidden="true" />
<span class="label">Created</span>
<span class="value">{{
$dayjs(project.published).fromNow()
}}</span>
<span class="value">{{ $dayjs(project.published).fromNow() }}</span>
</div>
<div class="date">
<UpdateIcon aria-hidden="true" />
@@ -93,7 +95,7 @@
<span class="value">{{ $dayjs(project.updated).fromNow() }}</span>
</div>
</div>
<hr v-if="$auth.user" />
<hr v-if="$auth.user" class="card-divider" />
<div class="buttons">
<nuxt-link
v-if="$auth.user"
@@ -104,9 +106,7 @@
Report
</nuxt-link>
<button
v-if="
$auth.user && !$user.follows.find((x) => x.id === project.id)
"
v-if="$auth.user && !$user.follows.find((x) => x.id === project.id)"
class="iconified-button"
@click="$store.dispatch('user/followProject', project)"
>
@@ -114,9 +114,7 @@
Follow
</button>
<button
v-if="
$auth.user && $user.follows.find((x) => x.id === project.id)
"
v-if="$auth.user && $user.follows.find((x) => x.id === project.id)"
class="iconified-button"
@click="$store.dispatch('user/unfollowProject', project)"
>
@@ -133,9 +131,9 @@
(project.moderator_message.message ||
project.moderator_message.body)))
"
class="card"
class="project-status card"
>
<h3>Project status</h3>
<h3 class="card-header">Project status</h3>
<div class="status-info"></div>
<p>
Your project is currently:
@@ -160,14 +158,14 @@
</p>
<div class="message">
<p v-if="project.status === 'processing'">
Your project is currently not viewable by people who are not part
of your team. Please wait for our moderators to manually review
your project to see if it abides by our project rules!
Your project is currently not viewable by people who are not part of
your team. Please wait for our moderators to manually review your
project to see if it abides by our project rules!
</p>
<p v-if="project.status === 'draft'">
Your project is currently not viewable by people who are not part
of your team. If your project is ready for review, click the
button below to make your mod public!
Your project is currently not viewable by people who are not part of
your team. If your project is ready for review, click the button
below to make your mod public!
</p>
<p v-if="project.moderator_message">
{{ project.moderator_message.message }}
@@ -198,7 +196,7 @@
</button>
</div>
</div>
<div class="card">
<div class="extra-info card">
<template
v-if="
project.issues_url ||
@@ -207,7 +205,7 @@
project.discord_url
"
>
<h3>External resources</h3>
<h3 class="card-header">External resources</h3>
<div class="links">
<a
v-if="project.issues_url"
@@ -317,16 +315,14 @@
<span v-else-if="donation.id === 'patreon'">Patreon</span>
<span v-else-if="donation.id === 'paypal'">PayPal</span>
<span v-else-if="donation.id === 'ko-fi'">Ko-fi</span>
<span v-else-if="donation.id === 'github'"
>GitHub Sponsors</span
>
<span v-else-if="donation.id === 'github'">GitHub Sponsors</span>
<span v-else>Donate</span>
</a>
</div>
<hr />
<hr class="card-divider" />
</template>
<template v-if="featuredVersions.length > 0">
<h3>Featured versions</h3>
<h3 class="card-header">Featured versions</h3>
<div
v-for="version in featuredVersions"
:key="version.id"
@@ -363,7 +359,7 @@
.map((x) => x.charAt(0).toUpperCase() + x.slice(1))
.join(', ')
}}
{{ version.game_versions[version.game_versions.length - 1] }}
{{ $formatVersion(version.game_versions) }}
</div>
<VersionBadge
v-if="version.version_type === 'release'"
@@ -382,9 +378,9 @@
/>
</div>
</div>
<hr />
<hr class="card-divider" />
</template>
<h3>Project members</h3>
<h3 class="card-header">Project members</h3>
<div
v-for="member in members"
:key="member.user.id"
@@ -398,8 +394,8 @@
<p class="role">{{ member.role }}</p>
</div>
</div>
<hr />
<h3>Technical information</h3>
<hr class="card-divider" />
<h3 class="card-header">Technical information</h3>
<div class="infos">
<div class="info">
<div class="key">License</div>
@@ -431,10 +427,10 @@
</div>
<Advertisement
v-if="project.status === 'approved' || project.status === 'unlisted'"
class="small-advertisement"
type="square"
small-screen="destroy"
/>
</section>
<div class="content">
<div class="project-main">
<div class="card styled-tabs">
@@ -699,7 +695,7 @@ export default {
try {
await this.$axios.patch(
`project/${this.currentProject.id}`,
`project/${this.project.id}`,
{
moderation_message: null,
moderation_message_body: null,
@@ -747,14 +743,43 @@ export default {
}
</script>
<style lang="scss" scoped>
hr {
background-color: var(--color-divider);
border: none;
color: var(--color-divider);
height: 1px;
margin: var(--spacing-card-bg) 0;
.page-contents {
display: grid;
grid-template:
'header'
'project-status'
'content'
'extra-info'
'small-advert'
/ 100%;
@media screen and (min-width: 1024px) {
grid-template:
'header content' auto
'project-status content' auto
'extra-info content' auto
'small-advert content' auto
'dummy content' 1fr
/ 20rem calc(100% - 20rem);
&.alt-layout {
grid-template:
'content header' auto
'content project-status' auto
'content extra-info' auto
'content small-advert' auto
'content dummy' 1fr
/ 1fr calc(100% - 20rem);
}
}
column-gap: var(--spacing-card-md);
}
.header {
grid-area: header;
.icon {
width: 6rem;
height: 6rem;
@@ -836,17 +861,28 @@ hr {
}
}
.project-status {
grid-area: project-status;
}
.extra-info {
grid-area: extra-info;
}
.small-advertisement {
grid-area: small-advert;
}
.content {
grid-area: content;
}
.project-info {
height: auto;
overflow: hidden;
@media screen and (min-width: 1024px) {
min-width: 21rem;
max-width: 21rem;
margin-right: var(--spacing-card-md);
}
h3 {
.card-header {
font-weight: bold;
color: var(--color-heading);
margin-bottom: 0.3rem;
@@ -914,7 +950,7 @@ hr {
&:not(:last-child)::after {
content: '•';
margin: 0 0.5rem;
margin: 0 0.25rem;
}
}
}
@@ -960,7 +996,6 @@ hr {
}
}
}
}
@media screen and (max-width: 550px) {
.title a {
+1 -1
View File
@@ -120,7 +120,7 @@ export default {
}
.filters {
margin-bottom: 0.5rem;
margin-bottom: 1rem;
}
.version-header {
+27 -5
View File
@@ -5,7 +5,7 @@
<nuxt-link
:to="`/${project.project_type}/${
project.slug ? project.slug : project.id
}`"
}/settings`"
class="iconified-button column"
>
Back
@@ -65,8 +65,9 @@
</label>
<h3>Categories</h3>
<label>
<span>
Select up to 3 categories that will help others find your project.
<span class="no-padding">
Select up to 3 categories that will help others <br />
find your project.
</span>
<Multiselect
id="categories"
@@ -298,7 +299,7 @@
v-model="license"
placeholder="Select one"
track-by="short"
label="name"
label="short"
:options="$tag.licenses"
:searchable="true"
:close-on-select="true"
@@ -616,7 +617,6 @@ label {
span {
flex: 2;
padding-right: var(--spacing-card-lg);
}
input,
@@ -651,6 +651,19 @@ label {
.page-contents {
display: grid;
grid-template:
'header' auto
'essentials' auto
'project-icon' auto
'game-sides' auto
'description' auto
'extra-links' auto
'license' auto
'donations' auto
'footer' auto
/ 1fr;
@media screen and (min-width: 1024px) {
grid-template:
'header header header' auto
'essentials essentials project-icon' auto
@@ -661,6 +674,7 @@ label {
'donations donations donations' auto
'footer footer footer' auto
/ 4fr 1fr 2fr;
}
column-gap: var(--spacing-card-md);
row-gap: var(--spacing-card-md);
}
@@ -712,6 +726,10 @@ section.game-sides {
.labeled-control {
flex: 2;
margin-left: var(--spacing-card-lg);
h3 {
margin-bottom: var(--spacing-card-sm);
}
}
}
}
@@ -766,6 +784,10 @@ section.donations {
flex: 1;
}
}
button {
margin: 0.5rem 0;
}
}
.footer {
+9 -2
View File
@@ -437,7 +437,7 @@ export default {
this.$notify({
group: 'main',
title: 'Action Success',
text: 'Your _type has been successfully deleted.',
text: 'Your project has been successfully deleted.',
type: 'success',
})
},
@@ -458,6 +458,13 @@ export default {
</script>
<style lang="scss" scoped>
.card {
h3 {
margin-top: 0.5rem;
margin-bottom: 0.5rem;
}
}
.member {
margin-bottom: var(--spacing-card-md);
@@ -594,7 +601,7 @@ section {
}
h3 {
margin-right: auto;
margin: auto auto auto 0;
}
> div {
+21 -19
View File
@@ -14,7 +14,13 @@
class="iconified-button back-button"
:to="`/${project.project_type}/${
project.slug ? project.slug : project.id
}/versions`"
}/${
$nuxt.context.from
? $nuxt.context.from.name === 'type-id-changelog'
? 'changelog'
: 'versions'
: 'versions'
}`"
>
<BackIcon aria-hidden="true" />
Back to list
@@ -125,7 +131,7 @@
placeholder="Enter the version name..."
/>
<Checkbox v-model="version.featured" label="Featured" />
<hr />
<hr class="card-divider" />
</div>
<section v-if="mode === 'edit' || mode === 'create'">
<h3>Changelog</h3>
@@ -158,7 +164,7 @@
: 'No changelog specified.'
"
></div>
<hr />
<hr class="card-divider" />
</section>
<section>
<h3>Metadata</h3>
@@ -187,7 +193,8 @@
class="value"
type="beta"
color="yellow"
/><VersionBadge
/>
<VersionBadge
v-else-if="version.version_type === 'alpha'"
class="value"
type="alpha"
@@ -259,11 +266,7 @@
placeholder="Choose versions..."
/>
<p v-else class="value">
{{
version.game_versions
.map((x) => x.charAt(0).toUpperCase() + x.slice(1))
.join(', ')
}}
{{ $formatVersion(version.game_versions) }}
</p>
</div>
<div v-if="mode === 'version'" class="data">
@@ -292,7 +295,7 @@
<p class="value">{{ version.id }}</p>
</div>
</div>
<hr />
<hr class="card-divider" />
</section>
<section
v-if="
@@ -394,7 +397,7 @@
</button>
</div>
</div>
<hr />
<hr class="card-divider" />
</section>
<section
v-if="version.files.length > 0 || mode === 'edit' || mode === 'create'"
@@ -819,6 +822,8 @@ export default {
})
).data
this.$emit('update:project', this.versions.concat([data]))
await this.$router.push(
`/${this.project.project_type}/${
this.project.slug ? this.project.slug : data.project_id
@@ -850,14 +855,6 @@ export default {
</script>
<style lang="scss" scoped>
hr {
background-color: var(--color-divider);
border: none;
color: var(--color-divider);
height: 1px;
margin: var(--spacing-card-bg) 0;
}
.content {
max-width: calc(100% - (2 * var(--spacing-card-lg)));
}
@@ -906,6 +903,11 @@ section {
.data-wrapper {
display: flex;
flex-wrap: wrap;
flex-direction: column;
@media screen and (min-width: 800px) {
flex-direction: row;
}
.data {
flex-basis: calc(33.333333% - 0.5rem);
+2 -2
View File
@@ -71,7 +71,7 @@
.map((x) => x.charAt(0).toUpperCase() + x.slice(1))
.join(', ') +
' ' +
version.game_versions[version.game_versions.length - 1]
$formatVersion(version.game_versions)
}}
</p>
<p></p>
@@ -96,7 +96,7 @@
.join(', ')
}}
</p>
<p>{{ version.game_versions[version.game_versions.length - 1] }}</p>
<p>{{ $formatVersion(version.game_versions) }}</p>
</td>
<td>
<p>
+101 -37
View File
@@ -43,8 +43,9 @@
</label>
<h3>Categories</h3>
<label>
<span>
Select up to 3 categories that will help others find your project.
<span class="no-padding">
Select up to 3 categories that will help others <br />
find your project.
</span>
<multiselect
id="categories"
@@ -81,7 +82,7 @@
</label>
<h3>Project type</h3>
<label>
<span>The project type of your project.</span>
<span class="no-padding">The project type of your project.</span>
<Multiselect
v-model="projectType"
placeholder="Select one"
@@ -379,19 +380,6 @@
placeholder="Choose versions..."
/>
</label>
<h3>Files</h3>
<label>
<span>
You must upload at least one file, however, you are allowed to
upload multiple files.
</span>
<FileInput
accept=".jar,application/java-archive,.zip,application/zip"
multiple
prompt="Choose files or drag them here"
@change="updateVersionFiles"
/>
</label>
</div>
<div class="dependencies">
<h3>Dependencies</h3>
@@ -470,6 +458,35 @@
</div>
</div>
</div>
<div class="files">
<h3>Files</h3>
<SmartFileInput
class="file-input"
multiple
accept=".jar,application/java-archive,.zip,application/zip,.mrpack"
prompt="Upload files"
@change="
(x) =>
x.forEach((y) => versions[currentVersionIndex].files.push(y))
"
/>
<div class="uploaded-files">
<div
v-for="(file, index) in versions[currentVersionIndex].files"
:key="index + 'new'"
class="file"
>
<p class="filename">{{ file.name }}</p>
<button
class="action iconified-button"
@click="versions[currentVersionIndex].files.splice(index, 1)"
>
<TrashIcon aria-hidden="true" />
Delete
</button>
</div>
</div>
</div>
<div class="changelog">
<h3>Changes</h3>
<span>
@@ -630,7 +647,6 @@
<div class="title">
<div class="text">
<h3>License</h3>
<i> this section is optional</i>
</div>
</div>
<label>
@@ -837,10 +853,18 @@ export default {
async createProject() {
this.$nuxt.$loading.start()
for (const version of this.versions) {
for (let i = 0; i < this.versions.length; i++) {
const version = this.versions[i]
if (!version.version_title) {
version.version_title = version.version_number
}
const newFileParts = []
for (let j = 0; j < version.files.length; j++) {
newFileParts.push(`version-${i}-${j}`)
}
version.file_parts = newFileParts
}
const formData = new FormData()
@@ -903,11 +927,11 @@ export default {
for (let i = 0; i < this.versions.length; i++) {
const version = this.versions[i]
for (let j = 0; j < version.raw_files.length; j++) {
for (let j = 0; j < version.files.length; j++) {
formData.append(
`version-${i}-${j}`,
new Blob([version.raw_files[j]]),
version.raw_files[j].name
new Blob([version.files[j]]),
version.files[j].name
)
}
}
@@ -939,6 +963,7 @@ export default {
title: 'An error occurred',
text: description,
type: 'error',
duration: 10000,
})
window.scrollTo({ top: 0, behavior: 'smooth' })
@@ -976,21 +1001,9 @@ export default {
}
},
updateVersionFiles(files) {
this.versions[this.currentVersionIndex].raw_files = files
const newFileParts = []
for (let i = 0; i < files.length; i++) {
newFileParts.push(`version-${this.currentVersionIndex}-${i}`)
}
this.versions[this.currentVersionIndex].file_parts = newFileParts
},
createVersion() {
this.versions.push({
raw_files: [],
file_parts: [],
files: [],
version_number: '',
version_title: '',
version_body: '',
@@ -1125,7 +1138,6 @@ section.project-icon {
}
.iconified-button {
width: 9rem;
margin-top: 0.5rem;
}
}
@@ -1143,6 +1155,10 @@ section.game-sides {
.labeled-control {
flex: 2;
margin-left: var(--spacing-card-lg);
h3 {
margin-bottom: var(--spacing-card-sm);
}
}
}
}
@@ -1213,6 +1229,23 @@ section.versions {
&:last-child {
display: flex;
}
@media screen and (max-width: 800px) {
+ &:nth-child(4),
&:nth-child(3) {
display: none;
}
&:first-child,
&:nth-child(5) {
width: unset;
}
}
@media screen and (max-width: 1024px) {
&:nth-child(2) {
display: none;
}
}
}
th {
@@ -1248,19 +1281,20 @@ section.versions {
'controls controls' auto
'main main' auto
'dependencies dependencies' auto
'files files'
'changelog changelog'
/ 5fr 4fr;
column-gap: var(--spacing-card-md);
@media screen and (min-width: 1024px) {
grid-template:
'controls controls' auto
'main dependencies' auto
'main files' 1fr
'changelog changelog'
/ 5fr 4fr;
}
column-gap: var(--spacing-card-sm);
.controls {
grid-area: controls;
display: flex;
@@ -1342,6 +1376,32 @@ section.versions {
}
}
.files {
grid-area: files;
.file-input {
margin-top: 1rem;
}
.uploaded-files {
.file {
display: flex;
align-items: center;
margin-bottom: 0.25rem;
flex-wrap: wrap;
row-gap: 0.25rem;
* {
margin-left: 0.25rem;
}
.filename {
margin: 0;
font-weight: bold;
}
}
}
}
.changelog {
grid-area: changelog;
display: flex;
@@ -1464,6 +1524,10 @@ section.donations {
flex: 1;
}
}
button {
margin: 0.5rem 0;
}
}
.footer {
+4 -18
View File
@@ -25,7 +25,9 @@
</label>
<h3>Item type</h3>
<label>
<span>The type of the item that is being reported.</span>
<span class="no-padding"
>The type of the item that is being reported.</span
>
<multiselect
id="item-type"
v-model="itemType"
@@ -39,7 +41,7 @@
</label>
<h3>Report type</h3>
<label>
<span>
<span class="no-padding">
The type of report. This is the category that this report falls
under.
</span>
@@ -195,22 +197,6 @@ export default {
}
}
label {
display: flex;
span {
flex: 2;
padding-right: var(--spacing-card-lg);
}
input,
.multiselect,
.input-group {
flex: 3;
height: fit-content;
}
}
.textarea-wrapper {
display: flex;
flex-direction: column;
-4
View File
@@ -112,10 +112,6 @@ export default {
input {
box-sizing: content-box;
}
.iconified-button {
padding: 1.25rem 1rem;
}
}
@media screen and (max-width: 750px) {
+79 -12
View File
@@ -5,6 +5,10 @@
<h1>Notifications</h1>
<div class="divider card">
<ThisOrThat
v-model="selectedNotificationType"
:items="notificationTypes"
/>
<button class="iconified-button" @click="clearNotifications">
<ClearIcon />
Clear all
@@ -12,17 +16,21 @@
</div>
<div class="notifications">
<div
v-for="notification in $user.notifications"
v-for="notification in selectedNotificationType !== 'all'
? $user.notifications.filter(
(x) => x.type === NOTIFICATION_TYPES[selectedNotificationType]
)
: $user.notifications"
:key="notification.id"
class="card notification"
>
<div class="icon">
<UpdateIcon v-if="notification.type === 'project-update'" />
<UpdateIcon v-if="notification.type === 'project_update'" />
<UsersIcon v-else-if="notification.type === 'team_invite'" />
</div>
<div class="text">
<nuxt-link :to="notification.link" class="top">
<h3>{{ notification.title }}</h3>
<h3 v-html="$xss($md.render(notification.title))" />
<span>
Notified {{ $dayjs(notification.created).fromNow() }}</span
>
@@ -41,7 +49,7 @@
{{ action.title }}
</button>
<button
v-if="$user.notifications.length === 0"
v-if="notification.actions.length === 0"
class="iconified-button"
@click="performAction(notification, notificationIndex, null)"
>
@@ -65,21 +73,51 @@ import ClearIcon from '~/assets/images/utils/trash.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'
import ThisOrThat from '~/components/ui/ThisOrThat'
const NOTIFICATION_TYPES = {
'Team Invites': 'team_invite',
'Project Updates': 'project_update',
}
export default {
name: 'Notifications',
components: {
ThisOrThat,
ClearIcon,
UpdateIcon,
UsersIcon,
UpToDate,
},
data() {
return {
selectedNotificationType: 'all',
}
},
async fetch() {
await this.$store.dispatch('user/fetchNotifications')
},
head: {
title: 'Notifications - Modrinth',
},
computed: {
notificationTypes() {
const obj = { all: true }
for (const notification of this.$user.notifications) {
obj[
Object.keys(NOTIFICATION_TYPES).find(
(key) => NOTIFICATION_TYPES[key] === notification.type
)
] = true
}
return Object.keys(obj)
},
},
created() {
this.NOTIFICATION_TYPES = NOTIFICATION_TYPES
},
methods: {
async clearNotifications() {
try {
@@ -142,25 +180,32 @@ h1 {
}
.divider {
button {
margin-left: auto;
}
align-items: center;
display: flex;
justify-content: space-between;
flex-wrap: wrap;
row-gap: 0.5rem;
}
.notifications {
.notification {
display: flex;
max-height: 4rem;
flex-wrap: wrap;
padding: var(--spacing-card-sm) var(--spacing-card-lg);
.icon svg {
height: calc(4rem - var(--spacing-card-sm));
.icon {
display: flex;
flex-direction: column;
justify-content: center;
svg {
height: calc(3rem - var(--spacing-card-sm));
width: auto;
margin-right: 1rem;
}
}
.text {
max-height: calc(4rem - var(--spacing-card-sm));
display: flex;
flex-direction: column;
justify-content: space-between;
@@ -168,16 +213,21 @@ h1 {
.top {
display: flex;
align-items: baseline;
flex-direction: column;
h3 {
h3 ::v-deep {
font-size: var(--font-size-lg);
margin: 0 0.5rem 0 0;
p {
margin: 0;
strong {
color: var(--color-brand);
}
}
}
}
p {
padding: 0;
@@ -186,6 +236,9 @@ h1 {
}
.buttons {
display: flex;
flex-direction: column;
justify-content: center;
margin-left: auto;
text-align: right;
@@ -201,5 +254,19 @@ h1 {
.page-contents {
max-width: calc(1280px - 20rem) !important;
}
.notifications {
.notification {
flex-wrap: nowrap;
.text {
flex-direction: column;
.top {
flex-direction: row;
}
}
}
}
}
</style>
+19 -3
View File
@@ -1,5 +1,10 @@
<template>
<div class="normal-page">
<div
:class="{
'normal-page': true,
'alt-layout': $store.state.cosmetics.searchLayout,
}"
>
<aside class="normal-page__sidebar" aria-label="Filters">
<section class="card" role="presentation">
<button
@@ -133,7 +138,6 @@
/>
</div>
</section>
<Advertisement type="square" small-screen="destroy" />
</aside>
<section class="normal-page__content">
<div class="card search-controls">
@@ -309,6 +313,8 @@ export default {
}
if (this.$route.query.v)
this.selectedVersions = this.$route.query.v.split(',')
if (this.$route.query.l)
this.selectedLicenses = this.$route.query.l.split(',')
if (this.$route.query.h) this.showSnapshots = this.$route.query.h === 'true'
if (this.$route.query.e)
this.selectedEnvironments = this.$route.query.e.split(',')
@@ -529,6 +535,8 @@ export default {
queryItems.push(`f=${encodeURIComponent(this.facets)}`)
if (this.selectedVersions.length > 0)
queryItems.push(`v=${encodeURIComponent(this.selectedVersions)}`)
if (this.selectedLicenses.length > 0)
queryItems.push(`l=${encodeURIComponent(this.selectedLicenses)}`)
if (this.showSnapshots) url += `h=true`
if (this.selectedEnvironments.length > 0)
queryItems.push(
@@ -560,7 +568,7 @@ export default {
}
</script>
<style scoped>
<style lang="scss" scoped>
.sidebar-menu {
display: none;
margin-top: 1rem;
@@ -577,6 +585,14 @@ export default {
.search-controls {
display: flex;
flex-direction: column;
.iconified-input {
margin-left: 6px;
input {
width: calc(100% + 8px);
}
}
}
.search-controls__sorting {
+1 -1
View File
@@ -9,7 +9,7 @@
:created-at="project.published"
:updated-at="project.updated"
:description="project.description"
:downloads="project.downloads.toString()"
:downloads="project.downloads ? project.downloads.toString() : '0'"
:icon-url="project.icon_url"
:name="project.title"
:client-side="project.client_side"
+50 -17
View File
@@ -23,29 +23,28 @@
Reset
</button>
</div>
<div class="recap">
<div class="recap card">
<section>
<h2>Quick recap of you</h2>
<h2>Profile Recap</h2>
<div>
<Badge
v-if="$auth.user.role === 'admin'"
type="You are an admin"
type="Admin"
color="red"
/>
<Badge
v-else-if="$auth.user.role === 'moderator'"
type="You are a moderator"
type="Moderator"
color="yellow"
/>
<Badge v-else type="You are a developer" color="green" />
<Badge v-else type="Developer" color="green" />
<div class="stat">
<SunriseIcon />
<span>You joined {{ $dayjs($auth.user.created).fromNow() }}</span>
<span>Joined {{ $dayjs($auth.user.created).fromNow() }}</span>
</div>
</div>
</section>
<section>
<h2>You have</h2>
<div class="stat">
<DownloadIcon />
<span>
@@ -105,6 +104,33 @@
:allow-empty="false"
/>
</label>
<h3>Search Layout</h3>
<label>
<span>
Sets the sidebar direction for the search page. Enabling this will
put the search bar on the right side
</span>
<input
v-model="projectLayout"
class="switch stylized-toggle"
type="checkbox"
@change="changeLayout"
/>
</label>
<h3>Project Layout</h3>
<label>
<span>
Sets the sidebar direction for project pages. Enabling this will be
close to the legacy layout with project information on the right
side
</span>
<input
v-model="searchLayout"
class="switch stylized-toggle"
type="checkbox"
@change="changeLayout"
/>
</label>
</section>
</div>
</div>
@@ -141,9 +167,14 @@ export default {
return {
icon: null,
previewImage: null,
searchLayout: false,
projectLayout: false,
}
},
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)
},
@@ -198,6 +229,13 @@ export default {
return this.formatNumber(sum)
},
async changeLayout() {
await this.$store.dispatch('cosmetics/save', {
searchLayout: this.searchLayout,
projectLayout: this.projectLayout,
$cookies: this.$cookies,
})
},
async saveChanges() {
this.$nuxt.$loading.start()
try {
@@ -248,6 +286,10 @@ export default {
@media screen and (min-width: 1024px) {
flex-direction: row;
.left-side {
margin-right: var(--spacing-card-bg);
}
}
}
@@ -255,8 +297,6 @@ export default {
min-width: 20rem;
.profile-picture {
margin-right: var(--spacing-card-bg);
h3 {
font-size: var(--font-size-lg);
}
@@ -277,13 +317,6 @@ export default {
.recap {
section {
padding: var(--spacing-card-md) var(--spacing-card-lg);
margin-bottom: 1rem;
@media screen and (min-width: 1024px) {
padding: 0;
}
h2 {
font-size: var(--font-size-lg);
margin: 0 0 0.5rem 0;
@@ -304,6 +337,7 @@ export default {
.stat {
display: flex;
align-items: center;
margin: 0.5rem 0;
svg {
width: auto;
@@ -315,7 +349,6 @@ export default {
span {
strong {
font-weight: bolder;
font-size: var(--font-size-xl);
}
}
}
+19 -12
View File
@@ -1,21 +1,13 @@
<template>
<div class="normal-page">
<div>
<aside class="card sidebar normal-page__sidebar">
<div class="normal-page__sidebar">
<aside class="card sidebar">
<img
class="sidebar__item profile-picture"
:src="user.avatar_url"
:alt="user.username"
/>
<h1 class="sidebar__item username">{{ user.username }}</h1>
<nuxt-link
v-if="$auth.user && $auth.user.id !== user.id"
:to="`/create/report?id=${user.id}&t=user`"
class="sidebar__item report-button iconified-button"
>
<ReportIcon aria-hidden="true" />
Report
</nuxt-link>
<div class="sidebar__item">
<Badge v-if="user.role === 'admin'" type="admin" color="red" />
<Badge
@@ -25,6 +17,7 @@
/>
<Badge v-else type="developer" color="green" />
</div>
<hr class="card-divider" />
<h3 class="sidebar__item">About me</h3>
<span v-if="user.bio" class="sidebar__item bio">{{ user.bio }}</span>
<div class="sidebar__item stats-block">
@@ -48,6 +41,16 @@
</div>
</div>
</div>
<template v-if="$auth.user && $auth.user.id !== user.id">
<hr class="card-divider" />
<nuxt-link
:to="`/create/report?id=${user.id}&t=user`"
class="sidebar__item report-button iconified-button"
>
<ReportIcon aria-hidden="true" />
Report
</nuxt-link>
</template>
</aside>
</div>
<div class="normal-page__content">
@@ -71,7 +74,9 @@
<div v-if="projects.length > 0">
<ProjectCard
v-for="project in selectedProjectType !== 'all'
? projects.filter((x) => x.project_type === selectedProjectType)
? projects.filter(
(x) => x.project_type === selectedProjectType.slice(0, -1)
)
: projects"
:id="project.slug || project.id"
:key="project.id"
@@ -217,7 +222,7 @@ export default {
const obj = { all: true }
for (const project of this.projects) {
obj[project.project_type] = true
obj[project.project_type + 's'] = true
}
return Object.keys(obj)
@@ -245,6 +250,8 @@ export default {
align-items: center;
display: flex;
justify-content: space-between;
flex-wrap: wrap;
row-gap: 0.5rem;
}
.sidebar__item:not(:last-child) {
margin: 0 0 0.75rem 0;
-3
View File
@@ -1,3 +0,0 @@
export default ({ store }, inject) => {
inject('auth', store.state.auth)
}
+87
View File
@@ -1,4 +1,91 @@
export default ({ store }, inject) => {
inject('user', store.state.user)
inject('tag', store.state.tag)
inject('auth', store.state.auth)
inject('formatVersion', (versionArray) => {
const allVersions = store.state.tag.gameVersions.slice().reverse()
const allReleases = allVersions.filter((x) => x.version_type === 'release')
const intervals = []
let currentInterval = 0
for (let i = 0; i < versionArray.length; i++) {
const index = allVersions.findIndex((x) => x.version === versionArray[i])
const releaseIndex = allReleases.findIndex(
(x) => x.version === versionArray[i]
)
if (i === 0) {
intervals.push([[versionArray[i], index, releaseIndex]])
} else {
const intervalBase = intervals[currentInterval]
if (
(index - intervalBase[intervalBase.length - 1][1] === 1 ||
releaseIndex - intervalBase[intervalBase.length - 1][2] === 1) &&
(allVersions[intervalBase[0][1]].version_type === 'release' ||
allVersions[index].version_type !== 'release')
) {
intervalBase[1] = [versionArray[i], index, releaseIndex]
} else {
currentInterval += 1
intervals[currentInterval] = [[versionArray[i], index, releaseIndex]]
}
}
}
const newIntervals = []
for (let i = 0; i < intervals.length; i++) {
const interval = intervals[i]
if (
interval.length === 2 &&
interval[0][2] !== -1 &&
interval[1][2] === -1
) {
let lastSnapshot = null
for (let j = interval[1][1]; j > interval[0][1]; j--) {
if (allVersions[j].version_type === 'release') {
newIntervals.push([
interval[0],
[
allVersions[j].version,
j,
allReleases.findIndex(
(x) => x.version === allVersions[j].version
),
],
])
if (lastSnapshot !== null && lastSnapshot !== j + 1) {
newIntervals.push([
[allVersions[lastSnapshot].version, lastSnapshot, -1],
interval[1],
])
} else {
newIntervals.push([interval[1]])
}
break
} else {
lastSnapshot = j
}
}
} else {
newIntervals.push(interval)
}
}
const output = []
for (const interval of newIntervals) {
if (interval.length === 2) {
output.push(`${interval[0][0]}${interval[1][0]}`)
} else {
output.push(interval[0][0])
}
}
return output.join(', ')
})
}
+35
View File
@@ -0,0 +1,35 @@
const parameters = {
maxAge: 60 * 60 * 24 * 365 * 10, // Ten years
sameSite: 'Strict',
secure: true,
httpOnly: false,
path: '/',
}
export const state = () => ({
searchLayout: false,
projectLayout: false,
})
export const mutations = {
SET_SEARCH_LAYOUT(state, searchLayout) {
state.searchLayout = searchLayout
},
SET_PROJECT_LAYOUT(state, projectLayout) {
state.projectLayout = projectLayout
},
}
export const actions = {
fetchCosmetics({ commit }, $cookies) {
commit('SET_PROJECT_LAYOUT', $cookies.get('project-layout'))
commit('SET_SEARCH_LAYOUT', $cookies.get('search-layout'))
},
save({ commit }, { projectLayout, searchLayout, $cookies }) {
commit('SET_PROJECT_LAYOUT', projectLayout)
commit('SET_SEARCH_LAYOUT', searchLayout)
$cookies.set('project-layout', projectLayout, parameters)
$cookies.set('search-layout', searchLayout, parameters)
},
}