Monorepo missing features (#1273)

* fix tauri config

* fix package patch

* regen pnpm lock

* use new workflow

* New GH actions

* Update lockfile

* update scripts

* Fix build script

* Fix missing deps

* Fix assets eslint

* Update libraries lint

* Fix all lint configs

* update lockfile

* add fmt + clippy fails

* Separate App Tauri portion

* fix app features

* Fix lints

* install tauri cli

* update lockfile

* corepack, fix lints

* add store path

* fix unused import

* Fix tests

* Issue templates + port over tauri release

* fix actions

* fix before build command

* Add X86 target

* Update build matrix

* finalize actions

* make debug build smaller

* Use debug build to make cache smaller

* dummy commit

* change proj name

* update file name

* Use release builds for less space use

* Remove rust cache

* Readd for app build

* add merge queue trigger
This commit is contained in:
Geometrically
2024-07-09 15:17:38 -07:00
committed by GitHub
parent dab284f339
commit d1bc65c266
265 changed files with 1810 additions and 1871 deletions

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,140 @@
<script setup>
import { LogInIcon } from '@modrinth/assets'
import { Button, Card } from '@modrinth/ui'
import { login as login_flow, set_default_user } from '@/helpers/auth.js'
import { handleError } from '@/store/notifications.js'
import mixpanel from 'mixpanel-browser'
import { ref } from 'vue'
import { handleSevereError } from '@/store/error.js'
const loading = ref(false)
const props = defineProps({
nextPage: {
type: Function,
required: true,
},
prevPage: {
type: Function,
required: true,
},
})
async function login() {
try {
loading.value = true
const loggedIn = await login_flow()
if (loggedIn) {
await set_default_user(loggedIn.id).catch(handleError)
}
await mixpanel.track('AccountLogIn')
loading.value = false
props.nextPage()
} catch (err) {
loading.value = false
handleSevereError(err)
}
}
</script>
<template>
<div class="login-card">
<img
src="https://launcher-files.modrinth.com/assets/default_profile.png"
class="logo"
alt="Minecraft art"
/>
<Card class="logging-in">
<h2>Sign into Minecraft</h2>
<p>
Sign in with your Microsoft account to launch Minecraft with your mods and modpacks. If you
don't have a Minecraft account, you can purchase the game on the
<a
href="https://www.minecraft.net/en-us/store/minecraft-java-bedrock-edition-pc"
class="link"
>
Minecraft website
</a>
</p>
<div class="action-row">
<Button class="transparent" large @click="prevPage"> Back </Button>
<div class="sign-in-pair">
<Button color="primary" large :disabled="loading" @click="login">
<LogInIcon />
{{ loading ? 'Loading...' : 'Sign in' }}
</Button>
</div>
<Button class="transparent" large @click="nextPage()"> Finish</Button>
</div>
</Card>
</div>
</template>
<style scoped lang="scss">
.login-card {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
margin: auto;
padding: var(--gap-lg);
width: 30rem;
img {
width: 100%;
border-radius: var(--radius-lg) var(--radius-lg) 0 0;
}
}
.logging-in {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
vertical-align: center;
gap: var(--gap-md);
background-color: var(--color-raised-bg);
width: 100%;
border-radius: 0 0 var(--radius-lg) var(--radius-lg);
h2,
p {
margin: 0;
}
p {
text-align: center;
}
}
.link {
color: var(--color-blue);
text-decoration: underline;
}
.button-row {
display: flex;
flex-direction: row;
}
.action-row {
width: 100%;
display: flex;
flex-direction: row;
justify-content: space-between;
gap: var(--gap-md);
margin-top: var(--gap-md);
.transparent {
padding: 0 var(--gap-md);
}
}
.sign-in-pair {
display: flex;
flex-direction: column;
gap: var(--gap-sm);
align-items: center;
}
</style>

View File

@@ -0,0 +1,326 @@
<script setup>
import { UserIcon, LockIcon, MailIcon } from '@modrinth/assets'
import { Button, Card, Checkbox } from '@modrinth/ui'
import {
DiscordIcon,
GithubIcon,
MicrosoftIcon,
GoogleIcon,
SteamIcon,
GitLabIcon,
} from '@/assets/external'
import {
authenticate_begin_flow,
authenticate_await_completion,
login_2fa,
create_account,
login_pass,
} from '@/helpers/mr_auth.js'
import { handleError, useNotifications } from '@/store/state.js'
import { onMounted, ref } from 'vue'
const props = defineProps({
nextPage: {
type: Function,
required: true,
},
prevPage: {
type: Function,
required: true,
},
modal: {
type: Boolean,
required: true,
},
})
const loggingIn = ref(true)
const twoFactorFlow = ref(null)
const twoFactorCode = ref('')
const email = ref('')
const username = ref('')
const password = ref('')
const confirmPassword = ref('')
const subscribe = ref(true)
async function signInOauth(provider) {
const url = await authenticate_begin_flow(provider).catch(handleError)
await window.__TAURI_INVOKE__('tauri', {
__tauriModule: 'Shell',
message: {
cmd: 'open',
path: url,
},
})
const creds = await authenticate_await_completion().catch(handleError)
if (creds && creds.type === 'two_factor_required') {
twoFactorFlow.value = creds.flow
} else if (creds && creds.session) {
props.nextPage()
}
}
async function signIn2fa() {
const creds = await login_2fa(twoFactorCode.value, twoFactorFlow.value).catch(handleError)
if (creds && creds.session) {
props.nextPage()
}
}
async function signIn() {
const creds = await login_pass(
username.value,
password.value,
window.turnstile.getResponse(),
).catch(handleError)
window.turnstile.reset()
if (creds && creds.type === 'two_factor_required') {
twoFactorFlow.value = creds.flow
} else if (creds && creds.session) {
props.nextPage()
}
}
async function createAccount() {
if (password.value !== confirmPassword.value) {
const notifs = useNotifications()
notifs.addNotification({
title: 'An error occurred',
text: 'Passwords do not match!',
type: 'error',
})
return
}
const creds = await create_account(
username.value,
email.value,
password.value,
window.turnstile.getResponse(),
subscribe.value,
).catch(handleError)
window.turnstile.reset()
if (creds && creds.session) {
props.nextPage()
}
}
async function goToNextPage() {
props.nextPage()
}
onMounted(() => {
if (window.turnstile === null || !window.turnstile) {
const script = document.createElement('script')
script.src = 'https://challenges.cloudflare.com/turnstile/v0/api.js'
script.async = true
script.defer = true
document.head.appendChild(script)
}
})
</script>
<template>
<Card>
<div class="cf-turnstile" data-sitekey="0x4AAAAAAAHWfmKCm7cUG869"></div>
<template v-if="twoFactorFlow">
<h1>Enter two-factor code</h1>
<p>Please enter a two-factor code to proceed.</p>
<input v-model="twoFactorCode" maxlength="11" type="text" placeholder="Enter code..." />
</template>
<template v-else>
<h1 v-if="loggingIn">Login to Modrinth</h1>
<h1 v-else>Create an account</h1>
<div class="button-grid">
<Button class="discord" large @click="signInOauth('discord')">
<DiscordIcon />
Discord
</Button>
<Button class="github" large @click="signInOauth('github')">
<GithubIcon />
Github
</Button>
<Button class="white" large @click="signInOauth('microsoft')">
<MicrosoftIcon />
Microsoft
</Button>
<Button class="google" large @click="signInOauth('google')">
<GoogleIcon />
Google
</Button>
<Button class="white" large @click="signInOauth('steam')">
<SteamIcon />
Steam
</Button>
<Button class="gitlab" large @click="signInOauth('gitlab')">
<GitLabIcon />
GitLab
</Button>
</div>
<div class="divider">
<hr />
<p>Or</p>
</div>
<div v-if="!loggingIn" class="iconified-input username">
<MailIcon />
<input v-model="email" type="text" placeholder="Email" />
</div>
<div class="iconified-input username">
<UserIcon />
<input
v-model="username"
type="text"
:placeholder="loggingIn ? 'Email or username' : 'Username'"
/>
</div>
<div class="iconified-input" :class="{ username: !loggingIn }">
<LockIcon />
<input v-model="password" type="password" placeholder="Password" />
</div>
<div v-if="!loggingIn" class="iconified-input username">
<LockIcon />
<input v-model="confirmPassword" type="password" placeholder="Confirm password" />
</div>
<Checkbox
v-if="!loggingIn"
v-model="subscribe"
class="subscribe-btn"
label="Subscribe to updates about Modrinth"
/>
<div class="link-row">
<a v-if="loggingIn" class="button-base" @click="loggingIn = false"> Create account </a>
<a v-else class="button-base" @click="loggingIn = true">Sign in</a>
<a class="button-base" href="https://modrinth.com/auth/reset-password">
Forgot password?
</a>
</div>
</template>
<div class="button-row">
<Button class="transparent" large @click="prevPage"> {{ modal ? 'Close' : 'Back' }} </Button>
<Button v-if="twoFactorCode" color="primary" large @click="signIn2fa"> Login </Button>
<Button v-else-if="loggingIn" color="primary" large @click="signIn"> Login </Button>
<Button v-else color="primary" large @click="createAccount"> Create account </Button>
<Button v-if="!modal" class="transparent" large @click="goToNextPage"> Next </Button>
</div>
</Card>
</template>
<style scoped lang="scss">
.card {
width: 25rem;
}
.button-grid {
display: grid;
grid-template-columns: 1fr 1fr;
grid-gap: var(--gap-md);
.btn {
width: 100%;
justify-content: center;
}
.discord {
background-color: #5865f2;
color: var(--color-contrast);
}
.github {
background-color: #8740f1;
color: var(--color-contrast);
}
.white {
background-color: var(--color-contrast);
color: var(--color-accent-contrast);
}
.google {
background-color: #4285f4;
color: var(--color-contrast);
}
.gitlab {
background-color: #fc6d26;
color: var(--color-contrast);
}
}
.divider {
position: relative;
display: flex;
align-items: center;
justify-content: center;
margin: var(--gap-md) 0;
p {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background-color: var(--color-raised-bg);
padding: 0 1rem;
margin: 0;
}
hr {
border: none;
width: 100%;
border-top: 2px solid var(--color-button-bg);
}
}
.iconified-input {
width: 100%;
input {
width: 100%;
flex-basis: auto;
}
}
.username {
margin-bottom: var(--gap-sm);
}
.link-row {
display: flex;
justify-content: space-between;
margin: var(--gap-md) 0;
a {
color: var(--color-blue);
text-decoration: underline;
&:hover {
cursor: pointer;
}
}
}
.button-row {
display: flex;
justify-content: space-between;
.btn {
flex-basis: auto;
}
.transparent {
padding: var(--gap-md) 0;
}
}
:deep {
.checkbox {
border: none;
}
}
</style>

View File

@@ -0,0 +1,284 @@
<script setup>
import { Button } from '@modrinth/ui'
import { ref } from 'vue'
import { get, set } from '@/helpers/settings.js'
import mixpanel from 'mixpanel-browser'
import GalleryImage from '@/components/ui/tutorial/GalleryImage.vue'
import LoginCard from '@/components/ui/tutorial/LoginCard.vue'
import StickyTitleBar from '@/components/ui/tutorial/StickyTitleBar.vue'
const page = ref(1)
const props = defineProps({
finish: {
type: Function,
default: () => {},
},
})
const flow = ref('')
const nextPage = (newFlow) => {
page.value++
mixpanel.track('OnboardingPage', { page: page.value })
if (newFlow) {
flow.value = newFlow
}
}
const prevPage = () => {
page.value--
}
const finishOnboarding = async () => {
mixpanel.track('OnboardingFinish')
const settings = await get()
settings.fully_onboarded = true
await set(settings)
props.finish()
}
</script>
<template>
<div class="onboarding">
<StickyTitleBar />
<GalleryImage
v-if="page === 1"
:gallery="[
{
url: 'https://launcher-files.modrinth.com/onboarding/home.png',
title: 'Discovery',
subtitle: 'See the latest and greatest mods and modpacks to play with from Modrinth',
},
{
url: 'https://launcher-files.modrinth.com/onboarding/discover.png',
title: 'Profile Management',
subtitle:
'Play, manage and search through all the amazing profiles downloaded on your computer at any time, even offline!',
},
]"
logo
>
<Button color="primary" @click="nextPage"> Get started </Button>
</GalleryImage>
<LoginCard v-else-if="page === 2" :next-page="finishOnboarding" :prev-page="prevPage" />
</div>
</template>
<style scoped lang="scss">
.sleek-primary {
background-color: var(--color-brand-highlight);
transition: all ease-in-out 0.1s;
}
.navigation-controls {
flex-grow: 1;
width: min-content;
}
.window-controls {
z-index: 20;
display: none;
flex-direction: row;
align-items: center;
gap: 0.25rem;
.titlebar-button {
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: all ease-in-out 0.1s;
background-color: var(--color-raised-bg);
color: var(--color-base);
&.close {
&:hover,
&:active {
background-color: var(--color-red);
color: var(--color-accent-contrast);
}
}
&:hover,
&:active {
background-color: var(--color-button-bg);
color: var(--color-contrast);
}
}
}
.container {
--appbar-height: 3.25rem;
--sidebar-width: 4.5rem;
height: 100vh;
display: flex;
flex-direction: row;
overflow: hidden;
.view {
width: calc(100% - var(--sidebar-width));
.appbar {
display: flex;
align-items: center;
background: var(--color-raised-bg);
box-shadow: var(--shadow-inset-sm), var(--shadow-floating);
padding: var(--gap-md);
height: 3.25rem;
gap: var(--gap-sm);
user-select: none;
-webkit-user-select: none;
}
.router-view {
width: 100%;
height: calc(100% - 3.125rem);
overflow: auto;
overflow-x: hidden;
background-color: var(--color-bg);
}
}
}
.nav-container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: space-between;
height: 100%;
background-color: var(--color-raised-bg);
box-shadow: var(--shadow-inset-sm), var(--shadow-floating);
padding: var(--gap-md);
width: var(--sidebar-width);
max-width: var(--sidebar-width);
min-width: var(--sidebar-width);
--sidebar-width: 4.5rem;
}
.pages-list {
display: flex;
flex-direction: column;
align-items: flex-start;
justify-content: flex-start;
width: 100%;
gap: 0.5rem;
.btn {
background-color: var(--color-raised-bg);
height: 3rem !important;
width: 3rem !important;
padding: 0.75rem;
border-radius: var(--radius-md);
box-shadow: none;
svg {
width: 1.5rem !important;
height: 1.5rem !important;
max-width: 1.5rem !important;
max-height: 1.5rem !important;
}
&.active {
background-color: var(--color-button-bg);
box-shadow: var(--shadow-floating);
}
&.sleek-primary {
background-color: var(--color-brand-highlight);
transition: all ease-in-out 0.1s;
}
}
}
.nav-section {
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: center;
width: 100%;
height: 100%;
gap: 1rem;
}
.sticky-tip {
position: absolute;
bottom: 1rem;
right: 1rem;
z-index: 10;
}
.intro-card {
display: flex;
flex-direction: column;
padding: var(--gap-xl);
.app-logo {
width: 100%;
height: auto;
display: block;
margin: auto;
}
p {
color: var(--color-contrast);
text-align: left;
width: 100%;
}
.actions {
display: flex;
flex-direction: row;
align-items: center;
justify-content: flex-end;
gap: var(--gap-sm);
}
}
.final-tip {
position: absolute;
bottom: 50%;
right: 50%;
transform: translate(50%, 50%);
z-index: 10;
}
.onboarding {
background:
top linear-gradient(0deg, #31375f, rgba(8, 14, 55, 0)),
url(https://cdn.modrinth.com/landing-new/landing-lower.webp);
background-size: cover;
height: 100vh;
min-height: 100vh;
max-height: 100vh;
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: var(--gap-xl);
padding-top: calc(2.5rem + var(--gap-lg));
}
.first-tip {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
z-index: 10;
}
.whole-page-shadow {
position: absolute;
top: 0;
left: 0;
width: 100vw;
height: 100%;
backdrop-filter: brightness(0.5);
-webkit-backdrop-filter: brightness(0.5);
z-index: 9;
}
</style>

View File

@@ -0,0 +1,81 @@
<script setup>
import { Button } from '@modrinth/ui'
import { XIcon } from '@modrinth/assets'
import { appWindow } from '@tauri-apps/api/window'
import { saveWindowState, StateFlags } from 'tauri-plugin-window-state-api'
import { window } from '@tauri-apps/api'
import { MinimizeIcon, MaximizeIcon } from '@/assets/icons'
</script>
<template>
<div data-tauri-drag-region class="fake-appbar">
<section class="window-controls">
<Button class="titlebar-button" icon-only @click="() => appWindow.minimize()">
<MinimizeIcon />
</Button>
<Button class="titlebar-button" icon-only @click="() => appWindow.toggleMaximize()">
<MaximizeIcon />
</Button>
<Button
class="titlebar-button close"
icon-only
@click="
() => {
saveWindowState(StateFlags.ALL)
window.getCurrent().close()
}
"
>
<XIcon />
</Button>
</section>
</div>
</template>
<style scoped lang="scss">
.fake-appbar {
position: absolute;
width: 100vw;
top: 0;
display: flex;
flex-direction: row;
justify-content: flex-end;
align-items: center;
height: 2.25rem;
background-color: var(--color-raised-bg);
-webkit-app-region: drag;
z-index: 10000;
}
.window-controls {
display: none;
flex-direction: row;
align-items: center;
.titlebar-button {
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: all ease-in-out 0.1s;
background-color: var(--color-raised-bg);
color: var(--color-base);
border-radius: 0;
height: 2.25rem;
&.close {
&:hover,
&:active {
background-color: var(--color-red);
color: var(--color-accent-contrast);
}
}
&:hover,
&:active {
background-color: var(--color-button-bg);
color: var(--color-contrast);
}
}
}
</style>