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
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 1132 additions and 741 deletions

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 }
}
}

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;
}

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;

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;
}
}

View File

@ -6,6 +6,7 @@
width: 100%;
}
html {
body {
overflow-y: scroll;
overflow-x: hidden;
}

View File

@ -198,8 +198,15 @@ export default {
},
},
methods: {
formatNumber(x) {
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',')
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;

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 {

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',
],
/*

File diff suppressed because it is too large Load Diff

View File

@ -120,7 +120,7 @@ export default {
}
.filters {
margin-bottom: 0.5rem;
margin-bottom: 1rem;
}
.version-header {

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,
@ -652,15 +652,29 @@ label {
.page-contents {
display: grid;
grid-template:
'header header header' auto
'essentials essentials project-icon' auto
'game-sides game-sides game-sides' auto
'description description description' auto
'extra-links extra-links extra-links' auto
'license license license' auto
'donations donations donations' auto
'footer footer footer' auto
/ 4fr 1fr 2fr;
'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
'game-sides game-sides game-sides' auto
'description description description' auto
'extra-links extra-links extra-links' auto
'license license license' auto
'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 {

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 {

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);

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>

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 {

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;

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) {

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));
width: auto;
margin-right: 1rem;
.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,13 +213,18 @@ 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;
strong {
color: var(--color-brand);
p {
margin: 0;
strong {
color: var(--color-brand);
}
}
}
}
@ -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>

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 {

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"

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);
}
}
}

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;

View File

@ -1,3 +0,0 @@
export default ({ store }, inject) => {
inject('auth', store.state.auth)
}

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
store/cosmetics.js Normal file
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)
},
}