feat(frontend): refactor and modernize welcome page (#3614)
* feat(frontend): refactor and modernize welcome page - also fixes navbar issue. Closes: #1533 * fix(frontend): lint issues & use standard variables instead of the constants from error.vue * fix(frontend): remove creator count as it's not a count of all users * fix(frontend): lang reshuffle * feat: rinthbot * fix: lint issues * fix: sizing of bot on mobile & scss cleanup for error.vue * fix: lint issues * fix: ui lint
@ -5,12 +5,7 @@
|
|||||||
<Logo404 />
|
<Logo404 />
|
||||||
</div>
|
</div>
|
||||||
<div class="error-box" :class="{ 'has-bot': !is404 }">
|
<div class="error-box" :class="{ 'has-bot': !is404 }">
|
||||||
<img
|
<img v-if="!is404" :src="SadRinthbot" alt="Sad Modrinth bot" class="error-box__sad-bot" />
|
||||||
v-if="!is404"
|
|
||||||
src="https://cdn-raw.modrinth.com/sad-bot.webp"
|
|
||||||
alt="Sad Modrinth bot"
|
|
||||||
class="error-box__sad-bot"
|
|
||||||
/>
|
|
||||||
<div v-if="!is404" class="error-box__top-glow" />
|
<div v-if="!is404" class="error-box__top-glow" />
|
||||||
<div class="error-box__body">
|
<div class="error-box__body">
|
||||||
<h1 class="error-box__title">{{ formatMessage(errorMessages.title) }}</h1>
|
<h1 class="error-box__title">{{ formatMessage(errorMessages.title) }}</h1>
|
||||||
@ -55,6 +50,7 @@
|
|||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { defineMessage, useVIntl } from "@vintl/vintl";
|
import { defineMessage, useVIntl } from "@vintl/vintl";
|
||||||
|
import { SadRinthbot } from "@modrinth/assets";
|
||||||
import Logo404 from "~/assets/images/404.svg";
|
import Logo404 from "~/assets/images/404.svg";
|
||||||
|
|
||||||
const { formatMessage } = useVIntl();
|
const { formatMessage } = useVIntl();
|
||||||
@ -272,6 +268,19 @@ const routeMessages = [
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.error-graphic {
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
svg {
|
||||||
|
fill: var(--color-text);
|
||||||
|
color: var(--color-text);
|
||||||
|
width: min(15rem, 100%);
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.error-box {
|
.error-box {
|
||||||
background-color: var(--color-raised-bg);
|
background-color: var(--color-raised-bg);
|
||||||
border-radius: 1.25rem;
|
border-radius: 1.25rem;
|
||||||
@ -281,51 +290,41 @@ const routeMessages = [
|
|||||||
gap: 1.25rem;
|
gap: 1.25rem;
|
||||||
box-shadow: var(--shadow-card);
|
box-shadow: var(--shadow-card);
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
|
||||||
|
|
||||||
.error-box.has-bot {
|
&.has-bot {
|
||||||
margin-block: 120px;
|
margin-block: 120px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.error-box p {
|
p {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.error-box a {
|
a {
|
||||||
color: var(--color-brand);
|
color: var(--color-brand);
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
|
||||||
|
|
||||||
.error-box a:hover,
|
&:hover,
|
||||||
.error-box a:focus {
|
&:focus {
|
||||||
filter: brightness(1.125);
|
filter: brightness(1.125);
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
|
|
||||||
.error-graphic {
|
|
||||||
margin-bottom: 2rem;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.error-graphic svg {
|
&__sad-bot {
|
||||||
fill: var(--color-text);
|
|
||||||
color: var(--color-text);
|
|
||||||
|
|
||||||
width: min(15rem, 100%);
|
|
||||||
height: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.error-box__sad-bot {
|
|
||||||
--_bot-height: 112px;
|
--_bot-height: 112px;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: calc(-1 * var(--_bot-height));
|
top: calc(-1 * var(--_bot-height));
|
||||||
right: 5rem;
|
right: 5rem;
|
||||||
width: auto;
|
width: auto;
|
||||||
height: var(--_bot-height);
|
height: var(--_bot-height);
|
||||||
|
|
||||||
|
@media screen and (max-width: 768px) {
|
||||||
|
--_bot-height: 70px;
|
||||||
|
right: 2rem;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.error-box__top-glow {
|
&__top-glow {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 1px;
|
height: 1px;
|
||||||
background: linear-gradient(
|
background: linear-gradient(
|
||||||
@ -341,28 +340,28 @@ const routeMessages = [
|
|||||||
opacity: 0.4;
|
opacity: 0.4;
|
||||||
}
|
}
|
||||||
|
|
||||||
.error-box__title {
|
&__title {
|
||||||
font-size: 2rem;
|
font-size: 2rem;
|
||||||
font-weight: 900;
|
font-weight: 900;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.error-box__subtitle {
|
&__subtitle {
|
||||||
font-size: 1.25rem;
|
font-size: 1.25rem;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
.error-box__body {
|
&__body {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 0.75rem;
|
gap: 0.75rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.error-box__list-title {
|
&__list-title {
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
.error-box__list {
|
&__list {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@ -370,11 +369,11 @@ const routeMessages = [
|
|||||||
padding-left: 1.25rem;
|
padding-left: 1.25rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.error-box li {
|
li {
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
.error-box__details {
|
&__details {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
color: var(--color-secondary);
|
color: var(--color-secondary);
|
||||||
@ -382,4 +381,5 @@ const routeMessages = [
|
|||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
font-size: 0.875rem;
|
font-size: 0.875rem;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -159,7 +159,7 @@
|
|||||||
"message": "Subscribe to updates about Modrinth"
|
"message": "Subscribe to updates about Modrinth"
|
||||||
},
|
},
|
||||||
"auth.welcome.description": {
|
"auth.welcome.description": {
|
||||||
"message": "Thank you for creating an account. You can now follow and create projects, receive updates about your favorite projects, and more!"
|
"message": "You’re now part of the awesome community of creators & explorers already building, downloading, and staying up-to-date with amazing mods."
|
||||||
},
|
},
|
||||||
"auth.welcome.label.tos": {
|
"auth.welcome.label.tos": {
|
||||||
"message": "By creating an account, you have agreed to Modrinth's <terms-link>Terms</terms-link> and <privacy-policy-link>Privacy Policy</privacy-policy-link>."
|
"message": "By creating an account, you have agreed to Modrinth's <terms-link>Terms</terms-link> and <privacy-policy-link>Privacy Policy</privacy-policy-link>."
|
||||||
|
|||||||
@ -1,10 +1,20 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div class="welcome-box has-bot">
|
||||||
<h1>{{ formatMessage(messages.welcomeLongTitle) }}</h1>
|
<img :src="WavingRinthbot" alt="Waving Modrinth Bot" class="welcome-box__waving-bot" />
|
||||||
|
<div class="welcome-box__top-glow" />
|
||||||
|
<div class="welcome-box__body">
|
||||||
|
<h1 class="welcome-box__title">
|
||||||
|
{{ formatMessage(messages.welcomeLongTitle) }}
|
||||||
|
</h1>
|
||||||
|
|
||||||
<section class="auth-form">
|
<p class="welcome-box__subtitle">
|
||||||
<p>
|
<IntlFormatted :message-id="messages.welcomeDescription">
|
||||||
{{ formatMessage(messages.welcomeDescription) }}
|
<template #bold="{ children }">
|
||||||
|
<strong>
|
||||||
|
<component :is="() => normalizeChildren(children)" />
|
||||||
|
</strong>
|
||||||
|
</template>
|
||||||
|
</IntlFormatted>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<Checkbox
|
<Checkbox
|
||||||
@ -14,11 +24,12 @@
|
|||||||
:description="formatMessage(messages.subscribeCheckbox)"
|
:description="formatMessage(messages.subscribeCheckbox)"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<button class="btn btn-primary continue-btn centered-btn" @click="continueSignUp">
|
<button class="btn btn-primary centered-btn" @click="continueSignUp">
|
||||||
{{ formatMessage(commonMessages.continueButton) }} <RightArrowIcon />
|
{{ formatMessage(commonMessages.continueButton) }}
|
||||||
|
<RightArrowIcon />
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<p>
|
<p class="tos-text">
|
||||||
<IntlFormatted :message-id="messages.tosLabel">
|
<IntlFormatted :message-id="messages.tosLabel">
|
||||||
<template #terms-link="{ children }">
|
<template #terms-link="{ children }">
|
||||||
<NuxtLink to="/legal/terms" class="text-link">
|
<NuxtLink to="/legal/terms" class="text-link">
|
||||||
@ -32,12 +43,15 @@
|
|||||||
</template>
|
</template>
|
||||||
</IntlFormatted>
|
</IntlFormatted>
|
||||||
</p>
|
</p>
|
||||||
</section>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { Checkbox, commonMessages } from "@modrinth/ui";
|
import { Checkbox, commonMessages } from "@modrinth/ui";
|
||||||
import { RightArrowIcon } from "@modrinth/assets";
|
import { RightArrowIcon, WavingRinthbot } from "@modrinth/assets";
|
||||||
|
|
||||||
|
const route = useRoute();
|
||||||
|
|
||||||
const { formatMessage } = useVIntl();
|
const { formatMessage } = useVIntl();
|
||||||
|
|
||||||
@ -54,7 +68,7 @@ const messages = defineMessages({
|
|||||||
welcomeDescription: {
|
welcomeDescription: {
|
||||||
id: "auth.welcome.description",
|
id: "auth.welcome.description",
|
||||||
defaultMessage:
|
defaultMessage:
|
||||||
"Thank you for creating an account. You can now follow and create projects, receive updates about your favorite projects, and more!",
|
"You’re now part of the awesome community of creators & explorers already building, downloading, and staying up-to-date with awazing mods.",
|
||||||
},
|
},
|
||||||
welcomeLongTitle: {
|
welcomeLongTitle: {
|
||||||
id: "auth.welcome.long-title",
|
id: "auth.welcome.long-title",
|
||||||
@ -72,20 +86,18 @@ useHead({
|
|||||||
|
|
||||||
const subscribe = ref(true);
|
const subscribe = ref(true);
|
||||||
|
|
||||||
async function continueSignUp() {
|
onMounted(async () => {
|
||||||
const route = useRoute();
|
|
||||||
|
|
||||||
await useAuth(route.query.authToken);
|
await useAuth(route.query.authToken);
|
||||||
await useUser();
|
await useUser();
|
||||||
|
});
|
||||||
|
|
||||||
|
async function continueSignUp() {
|
||||||
if (subscribe.value) {
|
if (subscribe.value) {
|
||||||
try {
|
try {
|
||||||
await useBaseFetch("auth/email/subscribe", {
|
await useBaseFetch("auth/email/subscribe", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
});
|
});
|
||||||
} catch {
|
} catch {}
|
||||||
/* empty */
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (route.query.redirect) {
|
if (route.query.redirect) {
|
||||||
@ -95,3 +107,84 @@ async function continueSignUp() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.welcome-box {
|
||||||
|
background-color: var(--color-raised-bg);
|
||||||
|
border-radius: var(--size-rounded-lg);
|
||||||
|
padding: 1.75rem 2rem;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 1.25rem;
|
||||||
|
box-shadow: var(--shadow-card);
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&.has-bot {
|
||||||
|
margin-block: 120px;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
a {
|
||||||
|
color: var(--color-brand);
|
||||||
|
font-weight: var(--weight-bold);
|
||||||
|
&:hover,
|
||||||
|
&:focus {
|
||||||
|
filter: brightness(1.125);
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__waving-bot {
|
||||||
|
--bot-height: 112px;
|
||||||
|
position: absolute;
|
||||||
|
top: calc(-1 * var(--bot-height));
|
||||||
|
right: 5rem;
|
||||||
|
height: var(--bot-height);
|
||||||
|
width: auto;
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
--bot-height: 70px;
|
||||||
|
right: 2rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__top-glow {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 1px;
|
||||||
|
opacity: 0.4;
|
||||||
|
background: linear-gradient(
|
||||||
|
to right,
|
||||||
|
transparent 2rem,
|
||||||
|
var(--color-green) calc(100% - 13rem),
|
||||||
|
var(--color-green) calc(100% - 5rem),
|
||||||
|
transparent calc(100% - 2rem)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
&__body {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 1.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__title {
|
||||||
|
font-size: var(--text-32);
|
||||||
|
font-weight: var(--weight-extrabold);
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__subtitle {
|
||||||
|
font-size: var(--text-18);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tos-text {
|
||||||
|
font-size: var(--text-14);
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
BIN
packages/assets/branding/rinthbot/angry.webp
Normal file
|
After Width: | Height: | Size: 219 KiB |
BIN
packages/assets/branding/rinthbot/annoyed.webp
Normal file
|
After Width: | Height: | Size: 238 KiB |
BIN
packages/assets/branding/rinthbot/confused.webp
Normal file
|
After Width: | Height: | Size: 347 KiB |
BIN
packages/assets/branding/rinthbot/excited.webp
Normal file
|
After Width: | Height: | Size: 276 KiB |
BIN
packages/assets/branding/rinthbot/laughing.webp
Normal file
|
After Width: | Height: | Size: 290 KiB |
BIN
packages/assets/branding/rinthbot/sad.webp
Normal file
|
After Width: | Height: | Size: 234 KiB |
BIN
packages/assets/branding/rinthbot/sleeping.webp
Normal file
|
After Width: | Height: | Size: 376 KiB |
BIN
packages/assets/branding/rinthbot/sobbing.webp
Normal file
|
After Width: | Height: | Size: 192 KiB |
BIN
packages/assets/branding/rinthbot/thinking.webp
Normal file
|
After Width: | Height: | Size: 247 KiB |
BIN
packages/assets/branding/rinthbot/waving.webp
Normal file
|
After Width: | Height: | Size: 241 KiB |
5
packages/assets/icons.d.ts
vendored
@ -4,3 +4,8 @@ declare module '*.svg?component' {
|
|||||||
const src: FunctionalComponent<SVGAttributes>
|
const src: FunctionalComponent<SVGAttributes>
|
||||||
export default src
|
export default src
|
||||||
}
|
}
|
||||||
|
|
||||||
|
declare module '*.webp' {
|
||||||
|
const src: string
|
||||||
|
export default src
|
||||||
|
}
|
||||||
|
|||||||
@ -4,6 +4,16 @@
|
|||||||
import _ModrinthIcon from './branding/logo.svg?component'
|
import _ModrinthIcon from './branding/logo.svg?component'
|
||||||
import _FourOhFourNotFound from './branding/404.svg?component'
|
import _FourOhFourNotFound from './branding/404.svg?component'
|
||||||
import _ModrinthPlusIcon from './branding/modrinth-plus.svg?component'
|
import _ModrinthPlusIcon from './branding/modrinth-plus.svg?component'
|
||||||
|
import _AngryRinthbot from './branding/rinthbot/angry.webp'
|
||||||
|
import _AnnoyedRinthbot from './branding/rinthbot/annoyed.webp'
|
||||||
|
import _ConfusedRinthbot from './branding/rinthbot/confused.webp'
|
||||||
|
import _ExcitedRinthbot from './branding/rinthbot/excited.webp'
|
||||||
|
import _LaughingRinthbot from './branding/rinthbot/laughing.webp'
|
||||||
|
import _SadRinthbot from './branding/rinthbot/sad.webp'
|
||||||
|
import _SleepingRinthbot from './branding/rinthbot/sleeping.webp'
|
||||||
|
import _SobbingRinthbot from './branding/rinthbot/sobbing.webp'
|
||||||
|
import _ThinkingRinthbot from './branding/rinthbot/thinking.webp'
|
||||||
|
import _WavingRinthbot from './branding/rinthbot/waving.webp'
|
||||||
|
|
||||||
// External Icons
|
// External Icons
|
||||||
import _SSODiscordIcon from './external/sso/discord.svg?component'
|
import _SSODiscordIcon from './external/sso/discord.svg?component'
|
||||||
@ -217,6 +227,16 @@ import './omorphia.scss'
|
|||||||
export const ModrinthIcon = _ModrinthIcon
|
export const ModrinthIcon = _ModrinthIcon
|
||||||
export const FourOhFourNotFound = _FourOhFourNotFound
|
export const FourOhFourNotFound = _FourOhFourNotFound
|
||||||
export const ModrinthPlusIcon = _ModrinthPlusIcon
|
export const ModrinthPlusIcon = _ModrinthPlusIcon
|
||||||
|
export const AngryRinthbot = _AngryRinthbot
|
||||||
|
export const AnnoyedRinthbot = _AnnoyedRinthbot
|
||||||
|
export const ConfusedRinthbot = _ConfusedRinthbot
|
||||||
|
export const ExcitedRinthbot = _ExcitedRinthbot
|
||||||
|
export const LaughingRinthbot = _LaughingRinthbot
|
||||||
|
export const SadRinthbot = _SadRinthbot
|
||||||
|
export const SleepingRinthbot = _SleepingRinthbot
|
||||||
|
export const SobbingRinthbot = _SobbingRinthbot
|
||||||
|
export const ThinkingRinthbot = _ThinkingRinthbot
|
||||||
|
export const WavingRinthbot = _WavingRinthbot
|
||||||
export const SSODiscordIcon = _SSODiscordIcon
|
export const SSODiscordIcon = _SSODiscordIcon
|
||||||
export const SSOGitHubIcon = _SSOGitHubIcon
|
export const SSOGitHubIcon = _SSOGitHubIcon
|
||||||
export const SSOGitLabIcon = _SSOGitLabIcon
|
export const SSOGitLabIcon = _SSOGitLabIcon
|
||||||
|
|||||||