refactor: settings page + ui components

This commit is contained in:
Liyas Thomas
2021-07-04 16:59:37 +00:00
committed by GitHub
parent 3e3da2f27b
commit 5e21210962
36 changed files with 449 additions and 566 deletions

View File

@@ -63,7 +63,7 @@ body {
.page-leave-active,
.layout-enter-active,
.layout-leave-active {
@apply transition-opacity;
@apply transition;
}
.page-enter,
@@ -96,7 +96,6 @@ a {
&.link {
@apply items-center;
@apply text-accent;
@apply hover:text-accent;
@apply focus:text-accent;
}
@@ -153,22 +152,17 @@ button {
@apply items-stretch;
}
hr {
@apply border-b;
@apply border-divider;
}
.heading {
@apply font-bold;
@apply text-secondaryDark;
@apply text-lg;
}
.info:not(.toasted) {
@apply m-4;
@apply text-secondaryLight;
.material-icons {
@apply align-middle;
@apply mr-2;
}
}
input {
@apply truncate;
}

View File

@@ -7,30 +7,28 @@
</div>
</template>
<template #body>
<p class="info">
{{ $t("extensions_info1") }}
</p>
<div class="px-2">
<ButtonSecondary
v-tippy="{ theme: 'tooltip' }"
<div class="flex flex-col px-2 space-y-2">
<SmartItem
to="https://addons.mozilla.org/en-US/firefox/addon/hoppscotch"
blank
:title="{ hasFirefoxExtInstalled: $t('installed') }"
svg="firefox"
label="Firefox"
:info-icon="hasFirefoxExtInstalled ? 'check_circle' : ''"
/>
</div>
<div class="px-2">
<ButtonSecondary
v-tippy="{ theme: 'tooltip' }"
<SmartItem
to="https://chrome.google.com/webstore/detail/hoppscotch-browser-extens/amknoiejhlmhancpahfcfcfhllgkpbld"
blank
:title="{ hasChromeExtInstalled: $t('installed') }"
svg="chrome"
label="Chrome"
:info-icon="hasChromeExtInstalled ? 'check_circle' : ''"
/>
</div>
</template>
<template #footer>
<div class="px-2 text-secondaryLight text-xs">
{{ $t("extensions_info1") }}
</div>
</template>
</SmartModal>
</template>

View File

@@ -12,10 +12,11 @@
@click.native="showInstallPrompt()"
/>
<span tabindex="-1">
<span v-if="currentUser === null">
<ButtonSecondary label="Sign in" @click.native="showLogin = true" />
<ButtonPrimary label="Sign up" @click.native="showLogin = true" />
</span>
<ButtonPrimary
v-if="currentUser === null"
label="Get Started"
@click.native="showLogin = true"
/>
<tippy v-else tabindex="-1" trigger="click" theme="popover" arrow>
<template #trigger>
<ProfilePicture
@@ -84,13 +85,12 @@
</tippy>
</span>
</div>
<AppLogin :show="showLogin" @hide-modal="showLogin = false" />
<FirebaseLogin :show="showLogin" @hide-modal="showLogin = false" />
<AppExtensions
:show="showExtensions"
@hide-modal="showExtensions = false"
/>
<AppShortcuts :show="showShortcuts" @hide-modal="showShortcuts = false" />
<FirebaseEmail :show="showEmail" @hide-modal="showEmail = false" />
</header>
</template>
@@ -110,7 +110,6 @@ export default {
showLogin: false,
showExtensions: false,
showShortcuts: false,
showEmail: false,
navigatorShare: navigator.share,
}
},
@@ -222,31 +221,3 @@ export default {
},
}
</script>
<style scoped lang="scss">
.logo {
@apply text-xl;
@apply transition-colors;
@apply ease-in-out;
@apply duration-150;
@apply hover:text-accent;
}
@keyframes slideIn {
0% {
@apply opacity-0;
@apply -left-4;
}
100% {
@apply opacity-100;
@apply left-0;
}
}
.slide-in {
@apply relative;
animation: slideIn 0.2s forwards ease-in-out;
}
</style>

View File

@@ -1,26 +0,0 @@
<template>
<SmartModal v-if="show" @close="hideModal">
<template #header>
<h3 class="heading">{{ $t("login") }}</h3>
<div>
<ButtonSecondary icon="close" @click.native="hideModal" />
</div>
</template>
<template #body>
<FirebaseLogin />
</template>
</SmartModal>
</template>
<script>
export default {
props: {
show: Boolean,
},
methods: {
hideModal() {
this.$emit("hide-modal")
},
},
}
</script>

View File

@@ -7,15 +7,15 @@
:to="localePath(navigation.target)"
class="
p-4
text-xs
flex-col flex-1
hover:bg-primaryLight
hover:bg-primaryDark hover:text-secondaryDark
items-center
justify-center
transition
"
>
<i class="material-icons">{{ navigation.icon }}</i>
<span class="mt-2">{{ navigation.title }}</span>
<i class="material-icons opacity-75">{{ navigation.icon }}</i>
<span class="mt-2 text-xs font-semibold">{{ navigation.title }}</span>
</nuxt-link>
</nav>
</aside>
@@ -36,67 +36,11 @@ export default {
],
}
},
computed: {
currentNavigation() {
return this.navigation.filter((item) =>
this.$route.name.includes(item.primary)
)
},
},
mounted() {
window.addEventListener("scroll", () => {
const mainNavLinks = document.querySelectorAll("nav.secondary-nav a")
const fromTop = window.scrollY
mainNavLinks.forEach(({ hash, classList }) => {
const section = document.querySelector(hash)
if (
section &&
section.offsetTop <= fromTop &&
section.offsetTop + section.offsetHeight > fromTop
) {
classList.add("current")
} else {
classList.remove("current")
}
})
})
},
}
</script>
<style scoped lang="scss">
nav.secondary-nav {
@apply flex flex-col flex-nowrap;
@apply items-center;
@apply justify-center;
@apply border-t-2 border-dashed border-divider;
@apply pt-2;
@apply space-y-2;
a {
@apply inline-flex;
@apply items-center;
@apply justify-center;
@apply flex-shrink-0;
@apply p-4;
@apply rounded-full;
@apply bg-primaryDark;
@apply text-secondaryLight;
@apply fill-current;
@apply outline-none;
@apply ease-in-out;
@apply duration-150;
&:hover {
@apply text-secondary;
@apply fill-current;
}
&.current {
@apply text-accent;
@apply fill-current;
}
}
.active {
@apply text-accent;
}
</style>

View File

@@ -1,7 +1,7 @@
<template>
<div>
<div
:class="['row-wrapper ease-in-out', { 'bg-primaryDark': dragging }]"
:class="[{ 'bg-primaryDark': dragging }]"
@dragover.prevent
@drop.prevent="dropEvent"
@dragover="dragging = true"

View File

@@ -1,7 +1,7 @@
<template>
<div>
<div
:class="['row-wrapper ease-in-out', { 'bg-primaryDark': dragging }]"
:class="[{ 'bg-primaryDark': dragging }]"
@dragover.prevent
@drop.prevent="dropEvent"
@dragover="dragging = true"

View File

@@ -1,7 +1,7 @@
<template>
<div>
<div
:class="['row-wrapper ease-in-out', { 'bg-primaryDark': dragging }]"
:class="[{ 'bg-primaryDark': dragging }]"
draggable="true"
@dragstart="dragStart"
@dragover.stop

View File

@@ -1,7 +1,7 @@
<template>
<div>
<div
:class="['row-wrapper ease-in-out', { 'bg-primaryDark': dragging }]"
:class="[{ 'bg-primaryDark': dragging }]"
@dragover.prevent
@drop.prevent="dropEvent"
@dragover="dragging = true"

View File

@@ -1,7 +1,7 @@
<template>
<div>
<div
:class="['row-wrapper ease-in-out', { 'bg-primaryDark': dragging }]"
:class="[{ 'bg-primaryDark': dragging }]"
@dragover.prevent
@drop.prevent="dropEvent"
@dragover="dragging = true"

View File

@@ -1,7 +1,7 @@
<template>
<div>
<div
:class="['row-wrapper ease-in-out', { 'bg-primaryDark': dragging }]"
:class="[{ 'bg-primaryDark': dragging }]"
draggable="true"
@dragstart="dragStart"
@dragover.stop

View File

@@ -1,6 +1,6 @@
<template>
<div>
<div class="ease-in-out row-wrapper">
<div>
<ButtonSecondary @click.native="toggleShowChildren" />
<i v-show="!showChildren && !isFiltered" class="material-icons"
>arrow_right</i

View File

@@ -1,6 +1,6 @@
<template>
<div>
<div class="ease-in-out row-wrapper">
<div>
<div>
<ButtonSecondary @click.native="toggleShowChildren" />
<i v-show="!showChildren && !isFiltered" class="material-icons"

View File

@@ -1,6 +1,6 @@
<template>
<div>
<div class="ease-in-out row-wrapper">
<div>
<div>
<ButtonSecondary
v-tippy="{ theme: 'tooltip' }"

View File

@@ -1,114 +0,0 @@
<template>
<SmartModal v-if="show" @close="hideModal">
<template #header>
<h3 class="heading">{{ $t("login_with") }} {{ $t("email") }}</h3>
<ButtonSecondary icon="close" @click.native="hideModal" />
</template>
<template v-if="mode === 'sign-in'" #body>
<label for="email"> E-mail </label>
<input
id="email"
v-model="form.email"
class="input"
type="email"
name="email"
placeholder="you@mail.com"
autocomplete="email"
required
spellcheck="false"
autofocus
@keyup.enter="signInWithEmail"
/>
</template>
<template v-else #body>
<div class="flex flex-col items-center">
<div class="flex justify-center max-w-md p-4 items-center flex-col">
<i class="material-icons text-accent" style="font-size: 64px">
verified
</i>
<h3 class="font-bold my-2 text-center text-lg">
{{ $t("we_sent_magic_link") }}
</h3>
<p class="text-center">
{{ $t("we_sent_magic_link_description", { email: form.email }) }}
</p>
<p class="info">{{ $t("check_your_inbox") }}</p>
</div>
</div>
</template>
<template v-if="mode === 'sign-in'" #footer>
<span></span>
<span>
<ButtonSPrimary
:loading="signingInWithEmail"
class="rounded-md button"
:disabled="
form.email.length !== 0
? emailRegex.test(form.email)
? false
: true
: true
"
type="button"
tabindex="-1"
:label="$t('send_magic_link')"
@click.native="signInWithEmail"
/></span>
</template>
</SmartModal>
</template>
<script>
import { currentUser$, signInWithEmail } from "~/helpers/fb/auth"
import { setLocalConfig } from "~/newstore/localpersistence"
export default {
props: {
show: Boolean,
},
data() {
return {
form: {
email: "",
},
signingInWithEmail: false,
emailRegex:
/^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/,
mode: "sign-in",
}
},
mounted() {
this.$subscribeTo(currentUser$, (user) => {
if (user) this.hideModal()
})
},
methods: {
async signInWithEmail() {
this.signingInWithEmail = true
const actionCodeSettings = {
url: `${process.env.BASE_URL}/enter`,
handleCodeInApp: true,
}
await signInWithEmail(this.form.email, actionCodeSettings)
.then(() => {
this.mode = "email"
setLocalConfig("emailForSignIn", this.form.email)
})
.catch((error) => {
this.$toast.error(error.message, {
icon: "error",
})
this.signingInWithEmail = false
})
.finally(() => {
this.signingInWithEmail = false
})
},
hideModal() {
this.mode = "sign-in"
this.$toast.clear()
this.$emit("hide-modal")
},
},
}
</script>

View File

@@ -1,49 +1,52 @@
<template>
<div>
<div v-if="mode === 'sign-in'" class="flex flex-col space-y-2">
<SmartItem
svg="google"
label="Continue with Google"
@click.native="signInWithGoogle"
/>
<SmartItem
svg="github"
label="Continue with GitHub"
@click.native="signInWithGithub"
/>
<SmartItem
icon="mail"
label="Continue with Email"
@click.native="mode = 'email'"
/>
</div>
<p v-if="mode === 'sign-in'" class="mx-4 mt-8 text-secondaryLight text-xs">
By signing in, you are agreeing to our
<SmartAnchor class="link" to="/index" label="Terms of Service" />
and
<SmartAnchor class="link" to="/index" label="Privacy Policy" />.
</p>
<div v-if="mode === 'email'" class="flex items-center px-4">
<label for="email"> Email </label>
<input
id="email"
v-model="form.email"
class="flex flex-1 ml-4 rounded px-4 py-2 outline-none"
type="email"
name="email"
placeholder="you@mail.com"
autocomplete="email"
required
spellcheck="false"
autofocus
@keyup.enter="signInWithEmail"
/>
</div>
<div v-if="mode === 'email'">
<div class="flex flex-col">
<SmartModal v-if="show" @close="hideModal">
<template #header>
<h3 class="heading">{{ $t("login_to_hoppscotch") }}</h3>
<div>
<ButtonSecondary icon="close" @click.native="hideModal" />
</div>
</template>
<template #body>
<div v-if="mode === 'sign-in'" class="flex flex-col space-y-2">
<SmartItem
:loading="signingInWithGoogle"
svg="google"
label="Continue with Google"
@click.native="signInWithGoogle"
/>
<SmartItem
:loading="signingInWithGitHub"
svg="github"
label="Continue with GitHub"
@click.native="signInWithGithub"
/>
<SmartItem
icon="mail"
label="Continue with Email"
@click.native="mode = 'email'"
/>
</div>
<div v-if="mode === 'email'" class="flex flex-col space-y-2">
<div class="flex items-center">
<label for="email" class="flex items-center px-4">
<i class="material-icons opacity-75">mail</i>
</label>
<input
id="email"
v-model="form.email"
class="flex flex-1 rounded px-4 py-2 outline-none"
type="email"
name="email"
placeholder="enter your email"
autocomplete="email"
required
spellcheck="false"
autofocus
@keyup.enter="signInWithEmail"
/>
</div>
<ButtonPrimary
:loading="signingInWithEmail"
class="mx-4 mt-4"
:disabled="
form.email.length !== 0
? emailRegex.test(form.email)
@@ -57,33 +60,45 @@
@click.native="signInWithEmail"
/>
</div>
<p class="mx-4 mt-8 text-secondaryLight text-xs">
Back to
<SmartAnchor
class="link"
to="#"
label="all sign in options"
@click.native="mode = 'sign-in'"
/>.
</p>
</div>
<div v-if="mode === 'email-sent'">
<div class="flex flex-col items-center">
<div class="flex justify-center max-w-md p-4 items-center flex-col">
<i class="material-icons text-accent" style="font-size: 64px">
verified
</i>
<h3 class="font-bold my-2 text-center text-xl">
<div v-if="mode === 'email-sent'" class="flex flex-col px-4">
<div class="flex justify-center max-w-md items-center flex-col">
<i class="material-icons text-accent text-4xl"> verified </i>
<h3 class="font-bold my-2 text-center text-lg">
{{ $t("we_sent_magic_link") }}
</h3>
<p class="text-center">
{{ $t("we_sent_magic_link_description", { email: form.email }) }}
</p>
<p class="mt-4 text-secondaryLight">{{ $t("check_your_inbox") }}</p>
</div>
</div>
</div>
</div>
</template>
<template #footer>
<p v-if="mode === 'sign-in'" class="text-secondaryLight text-xs">
By signing in, you are agreeing to our
<SmartAnchor class="link" to="/index" label="Terms of Service" />
and
<SmartAnchor class="link" to="/index" label="Privacy Policy" />.
</p>
<p v-if="mode === 'email'" class="text-secondaryLight text-xs">
<SmartAnchor
class="link"
label="← All sign in options"
@click.native="mode = 'sign-in'"
/>
</p>
<p
v-if="mode === 'email-sent'"
class="flex flex-1 justify-between text-secondaryLight text-xs"
>
<SmartAnchor
class="link"
label="← Re-enter email"
@click.native="mode = 'email'"
/>
<SmartAnchor class="link" label="Dismiss" @click.native="hideModal" />
</p>
</template>
</SmartModal>
</template>
<script>
@@ -100,11 +115,16 @@ import {
import { setLocalConfig } from "~/newstore/localpersistence"
export default {
props: {
show: Boolean,
},
data() {
return {
form: {
email: "",
},
signingInWithGoogle: false,
signingInWithGitHub: false,
signingInWithEmail: false,
emailRegex:
/^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/,
@@ -123,6 +143,8 @@ export default {
})
},
async signInWithGoogle() {
this.signingInWithGoogle = true
try {
const { additionalUserInfo } = await signInUserWithGoogle()
@@ -191,8 +213,12 @@ export default {
})
}
}
this.signingInWithGoogle = false
},
async signInWithGithub() {
this.signingInWithGitHub = true
try {
const { credential, additionalUserInfo } = await signInUserWithGithub()
@@ -264,9 +290,12 @@ export default {
})
}
}
this.signingInWithGitHub = false
},
async signInWithEmail() {
this.signingInWithEmail = true
const actionCodeSettings = {
url: `${process.env.BASE_URL}/enter`,
handleCodeInApp: true,

View File

@@ -5,6 +5,7 @@
<script>
import ThreeGlobe from "three-globe"
import * as THREE from "three"
import TrackballControls from "three-trackballcontrols"
import geojson from "~/assets/geojson/ne_110m_admin_0_countries.geojson"
import texture from "~/assets/images/texture.png"
@@ -15,6 +16,7 @@ export default {
renderer: null,
scene: null,
camera: null,
tbControls: null,
arcsData: [...Array(20).keys()].map(() => ({
startLat: (Math.random() - 0.5) * 180,
startLng: (Math.random() - 0.5) * 360,
@@ -32,26 +34,26 @@ export default {
}
},
// computed: {
// labelsData() {
// const labelsData = []
// this.arcsData.forEach(
// ({ startLat, startLng, endLat, endLng, color }, linkIdx) =>
// [
// [startLat, startLng],
// [endLat, endLng],
// ].forEach(([lat, lng], edgeIdx) =>
// labelsData.push({
// lat,
// lng,
// color: color[edgeIdx],
// text: `${linkIdx} ${edgeIdx ? "tgt" : "src"}`,
// })
// )
// )
// return labelsData
// },
// },
computed: {
labelsData() {
const labelsData = []
this.arcsData.forEach(
({ startLat, startLng, endLat, endLng, color }, linkIdx) =>
[
[startLat, startLng],
[endLat, endLng],
].forEach(([lat, lng], edgeIdx) =>
labelsData.push({
lat,
lng,
color: color[edgeIdx],
text: `${linkIdx} ${edgeIdx ? "tgt" : "src"}`,
})
)
)
return labelsData
},
},
mounted() {
this.init()
@@ -76,28 +78,32 @@ export default {
this.globe = new ThreeGlobe()
.globeImageUrl(texture)
.atmosphereColor("#aaaaaa")
// arcs layer
.arcsData(this.arcsData)
.arcColor("color")
.arcDashLength(1)
.arcDashGap(() => Math.random())
.arcStroke(1)
.arcStroke(0.6)
.arcDashInitialGap(() => Math.random() * 5)
.arcDashAnimateTime(2000)
// hex layer
.hexPolygonsData(geojson.features)
.hexPolygonResolution(3)
.hexPolygonMargin(0.5)
.hexPolygonColor(() => `#aaaaaa`)
// labels layer
// .labelsData(this.labelsData)
// .labelLat("lat")
// .labelLng("lng")
// .labelText("text")
// .labelColor("color")
// .labelSize(1.5)
// .labelDotRadius(0.5)
// labels layer
.labelsData(this.labelsData)
.labelLat("lat")
.labelLng("lng")
.labelText("text")
.labelColor("color")
.labelSize(1.2)
.labelDotRadius(0.8)
// Setup renderer
this.renderer = new THREE.WebGLRenderer({
alpha: true,
})
@@ -105,26 +111,37 @@ export default {
this.$refs.globe.clientWidth,
this.$refs.globe.clientHeight
)
this.$refs.globe.appendChild(this.renderer.domElement)
// Setup scene
this.scene = new THREE.Scene()
this.scene.background = null
this.scene.add(this.globe)
this.scene.add(new THREE.AmbientLight(0xffffff))
this.scene.add(new THREE.DirectionalLight(0xffffff, 0.8))
this.scene.add(new THREE.DirectionalLight(0xffffff, 0.6))
// Setup camera
this.camera = new THREE.PerspectiveCamera()
this.camera.aspect =
this.$refs.globe.clientWidth / this.$refs.globe.clientHeight
this.camera.updateProjectionMatrix()
this.camera.position.z = 300
// Add camera controls
this.tbControls = new TrackballControls(
this.camera,
this.renderer.domElement
)
this.tbControls.rotateSpeed = 5
this.tbControls.noZoom = true
this.tbControls.noPan = true
},
animate() {
this.renderer.render(this.scene, this.camera)
this.tbControls.update()
requestAnimationFrame(this.animate)
this.globe.rotation.y -= 0.005
this.globe.rotation.y -= 0.0025
},
},
}

View File

@@ -1,26 +1,23 @@
<template>
<div class="flex flex-col">
<label>{{ $t("color") }}: {{ capitalized(active) }}</label>
<div>
<!-- text-blue-400 -->
<!-- text-green-400 -->
<!-- text-teal-400 -->
<!-- text-indigo-400 -->
<!-- text-purple-400 -->
<!-- text-orange-400 -->
<!-- text-pink-400 -->
<!-- text-red-400 -->
<!-- text-yellow-400 -->
<ButtonSecondary
v-for="(color, index) of accentColors"
:key="`color-${index}`"
v-tippy="{ theme: 'tooltip' }"
:title="capitalized(color)"
:class="[`text-${color}-400`, { 'bg-primary': color === active }]"
icon="lens"
@click.native="setActiveColor(color)"
/>
</div>
<div class="flex">
<!-- text-blue-400 -->
<!-- text-green-400 -->
<!-- text-teal-400 -->
<!-- text-indigo-400 -->
<!-- text-purple-400 -->
<!-- text-orange-400 -->
<!-- text-pink-400 -->
<!-- text-red-400 -->
<!-- text-yellow-400 -->
<ButtonSecondary
v-for="(color, index) of accentColors"
:key="`color-${index}`"
v-tippy="{ theme: 'tooltip' }"
:title="`${color.charAt(0).toUpperCase()}${color.slice(1)}`"
:class="[`text-${color}-400`, { 'bg-primary': color === active }]"
icon="lens"
@click.native="setActiveColor(color)"
/>
</div>
</template>
@@ -49,15 +46,11 @@ export default {
setLocalConfig("THEME_COLOR", color)
},
},
methods: {
setActiveColor(color) {
document.documentElement.setAttribute("data-accent", color)
this.active = color
},
capitalized(color) {
return `${color.charAt(0).toUpperCase()}${color.slice(1)}`
},
},
}
</script>

View File

@@ -277,9 +277,6 @@ export default {
.label {
@apply p-2;
@apply ease-in-out;
@apply duration-150;
}
.siblings {

View File

@@ -200,9 +200,6 @@ export default {
@apply mx-2;
@apply left-0;
@apply z-50;
@apply ease-in-out;
@apply duration-150;
@apply shadow-lg;
top: calc(100% - 8px);

View File

@@ -6,44 +6,26 @@
trigger="click"
theme="popover"
arrow
interactive
:animate-fill="false"
>
<template #trigger>
<ButtonSecondary
<SmartItem
v-tippy="{ theme: 'tooltip' }"
:title="$t('choose_language')"
:label="$i18n.locales.find(({ code }) => code == $i18n.locale).name"
/>
{{
$i18n.locales.find(({ code }) => code == $i18n.locale).country
:label="`${
$i18n.locales.find(({ code }) => code == $i18n.locale).country
| formatCountry
}}
} ${$i18n.locales.find(({ code }) => code == $i18n.locale).name}`"
/>
</template>
<NuxtLink
<SmartItem
v-for="locale in $i18n.locales.filter(
({ code }) => code !== $i18n.locale
)"
:key="locale.code"
class="
inline-flex
items-center
px-4
py-2
transition
rounded-lg
hover:bg-accentLight hover:text-secondaryDark
focus:bg-accentLight focus:text-secondaryDark focus:outline-none
"
:to="switchLocalePath(locale.code)"
>
<span class="mr-2 text-lg">
{{ locale.country | formatCountry }}
</span>
<span class="font-semibold">
{{ locale.name }}
</span>
</NuxtLink>
:to="switchLocalePath(locale.code).toString()"
:label="`${locale.country | formatCountry} ${locale.name}`"
/>
</tippy>
</span>
</template>

View File

@@ -1,31 +1,17 @@
<template>
<div class="flex flex-col">
<label>
<ColorScheme placeholder="..." tag="span">
{{ $t("background") }}:
{{
$colorMode.preference.charAt(0).toUpperCase() +
$colorMode.preference.slice(1)
}}
<span v-if="$colorMode.preference === 'system'">
({{ $colorMode.value }} mode detected)
</span>
</ColorScheme>
</label>
<div>
<ButtonSecondary
v-for="(color, index) of colors"
:key="`color-${index}`"
v-tippy="{ theme: 'tooltip' }"
:title="`${color.charAt(0).toUpperCase()}${color.slice(1)}`"
:class="[
{ 'bg-primary': color === $colorMode.preference },
{ 'text-accent hover:text-accent': color === $colorMode.value },
]"
:icon="getIcon(color)"
@click.native="$colorMode.preference = color"
/>
</div>
<div class="flex">
<ButtonSecondary
v-for="(color, index) of colors"
:key="`color-${index}`"
v-tippy="{ theme: 'tooltip' }"
:title="`${color.charAt(0).toUpperCase()}${color.slice(1)}`"
:class="[
{ 'bg-primary': color === $colorMode.preference },
{ 'text-accent hover:text-accent': color === $colorMode.value },
]"
:icon="getIcon(color)"
@click.native="$colorMode.preference = color"
/>
</div>
</template>

View File

@@ -21,8 +21,6 @@
@apply text-secondary;
@apply font-mono;
@apply ease-in-out;
@apply duration-150;
@apply border border-divider;
}

View File

@@ -16,24 +16,29 @@
"
:class="[
{ 'opacity-50 cursor-not-allowed': disabled },
{ 'pointer-events-none': loading },
{ 'flex-1': label },
{ 'flex-row-reverse justify-end': reverse },
]"
:disabled="disabled"
:tabindex="loading ? '-1' : '0'"
>
<i
v-if="icon"
:class="label ? (reverse ? 'ml-4 opacity-75' : 'mr-4 opacity-75') : ''"
class="material-icons"
>
{{ icon }}
</i>
<SmartIcon
v-if="svg"
:name="svg"
:class="label ? (reverse ? 'ml-4 opacity-75' : 'mr-4 opacity-75') : ''"
class="svg-icons"
/>
<span v-if="!loading" class="inline-flex items-center">
<i
v-if="icon"
:class="label ? (reverse ? 'ml-4 opacity-75' : 'mr-4 opacity-75') : ''"
class="material-icons"
>
{{ icon }}
</i>
<SmartIcon
v-if="svg"
:name="svg"
:class="label ? (reverse ? 'ml-4 opacity-75' : 'mr-4 opacity-75') : ''"
class="svg-icons"
/>
</span>
<SmartSpinner v-else class="mr-4" />
<div class="inline-flex items-start" :class="{ 'flex-col': description }">
<div class="font-semibold">
{{ label }}
@@ -42,6 +47,9 @@
{{ description }}
</p>
</div>
<i v-if="infoIcon" class="ml-4 text-accent material-icons">
{{ infoIcon }}
</i>
</SmartLink>
</template>
@@ -80,14 +88,18 @@ export default {
type: Boolean,
default: false,
},
infoIcon: {
type: String,
default: "",
loading: {
type: Boolean,
default: false,
},
reverse: {
type: Boolean,
default: false,
},
infoIcon: {
type: String,
default: "",
},
},
}
</script>

View File

@@ -123,8 +123,8 @@ export default {
@apply flex;
@apply items-center;
@apply justify-center;
@apply ease-in-out;
@apply duration-150;
@apply transition;
@apply bg-primaryLight;
}
@@ -133,13 +133,11 @@ export default {
@apply flex flex-1 flex-col;
@apply m-2;
@apply p-4;
@apply ease-in-out;
@apply duration-150;
@apply transition;
@apply bg-primary;
@apply rounded-lg;
@apply shadow-xl;
@apply max-w-md;
@apply max-h-xl;
}
.modal-header {
@@ -149,6 +147,7 @@ export default {
.modal-body {
@apply my-4;
@apply overflow-auto;
@apply max-h-xl;
}
.modal-footer {
@@ -164,7 +163,6 @@ export default {
.modal-leave-active .modal-container {
@apply transform;
@apply scale-90;
@apply ease-in-out;
@apply duration-150;
@apply transition;
}
</style>

View File

@@ -1,7 +1,6 @@
<template>
<svg
class="animate-spin"
:class="`w-${size} h-${size}`"
class="animate-spin w-5 h-5"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
@@ -21,14 +20,3 @@
/>
</svg>
</template>
<script>
export default {
props: {
size: {
type: Number,
default: 6,
},
},
}
</script>

View File

@@ -45,7 +45,7 @@ $transition: all 0.2s ease-in-out;
@apply align-middle;
@apply rounded-full;
@apply p-0;
@apply m-4;
@apply mr-4;
@apply cursor-pointer;
@apply flex-shrink-0;

View File

@@ -6,7 +6,11 @@
<div v-else>
<label>{{ $t("login_with") }}</label>
<p>
<FirebaseLogin @show-email="showEmail = true" />
<ButtonPrimary
v-if="currentUser"
label="Get Started"
@click.native="showLogin = true"
/>
</p>
</div>
</div>
@@ -45,7 +49,7 @@
</li>
</ul>
</div>
<FirebaseEmail :show="showEmail" @hide-modal="showEmail = false" />
<FirebaseLogin :show="showLogin" @hide-modal="showLogin = false" />
</AppSection>
</template>
@@ -62,7 +66,7 @@ export default {
editingteamID: "",
me: {},
myTeams: [],
showEmail: false,
showLogin: false,
}
},
subscriptions() {

View File

@@ -2,10 +2,10 @@
<transition
:appear="appear"
name="translate-fade"
enter-active-class="duration-500"
enter-active-class="transition"
enter-class="opacity-0"
enter-to-class="opacity-100"
leave-active-class="duration-500"
leave-active-class="transition"
leave-class="opacity-100"
leave-to-class="opacity-0"
>

View File

@@ -2,10 +2,10 @@
<transition
:appear="appear"
name="translate-slide-right"
enter-active-class="duration-500 transform"
enter-active-class="transition transform"
enter-class="-translate-x-full"
enter-to-class="translate-x-0"
leave-active-class="duration-500 transform"
leave-active-class="transition transform"
leave-class="translate-x-0"
leave-to-class="-translate-x-full"
>

View File

@@ -251,11 +251,12 @@
"extensions_use_toggle": "Use the browser extension to send requests (if present)",
"extension_version": "Extension Version",
"extension_ver_not_reported": "Not Reported",
"extensions_info1": "Browser extension that simplifies access to Hoppscotch",
"extensions_info1": "Browser extension simplifies access to Hoppscotch, fix CORS issues, etc.",
"extensions_info2": "Get Hoppscotch browser extension!",
"installed": "Installed",
"login_with": "Login with",
"login": "Login",
"login_to_hoppscotch": "Login to Hoppscotch",
"logged_out": "Logged out",
"login_success": "Successfully logged in",
"logout": "Logout",
@@ -333,10 +334,9 @@
"import_from_my_collections": "Import from My Collections",
"export_as_json": "Export as JSON",
"send_magic_link": "Send a magic link",
"check_your_inbox": "Check your inbox.",
"you_can_dismiss_this_modal": "You can dismiss this ",
"we_sent_magic_link": "We sent you a magic link!",
"we_sent_magic_link_description": "We sent an email to {email}. It contains a magic link thatll log you in.",
"we_sent_magic_link_description": "Check your inbox - we sent an email to {email}. It contains a magic link that will log you in.",
"hide_sidebar": "Hide sidebar",
"show_sidebar": "Show sidebar",
"protocols": "Protocols",

View File

@@ -443,6 +443,6 @@ export default {
// Router configuration (https://nuxtjs.org/api/configuration-router)
router: {
linkExactActiveClass: "text-accent",
linkExactActiveClass: "active",
},
}

15
package-lock.json generated
View File

@@ -35,6 +35,7 @@
"tern": "^0.24.3",
"three": "^0.130.0",
"three-globe": "^2.18.5",
"three-trackballcontrols": "^0.9.0",
"vue-apollo": "^3.0.7",
"vue-cli-plugin-apollo": "^0.22.2",
"vue-functional-data-merge": "^3.1.0",
@@ -28000,6 +28001,14 @@
"three": ">=0.88.0"
}
},
"node_modules/three-trackballcontrols": {
"version": "0.9.0",
"resolved": "https://registry.npmjs.org/three-trackballcontrols/-/three-trackballcontrols-0.9.0.tgz",
"integrity": "sha512-Z6HmIJnP70r5uONvcPCdLEF0SsG1kbGzNb7qQYj3c7b6v2E3XTlbNpZsgTjt36oKm0Z2tU11D6EbW4i8KIHuqA==",
"peerDependencies": {
"three": ">= 0.86 <= 1.0"
}
},
"node_modules/throat": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/throat/-/throat-6.0.1.tgz",
@@ -55000,6 +55009,12 @@
"tinycolor2": "^1.4"
}
},
"three-trackballcontrols": {
"version": "0.9.0",
"resolved": "https://registry.npmjs.org/three-trackballcontrols/-/three-trackballcontrols-0.9.0.tgz",
"integrity": "sha512-Z6HmIJnP70r5uONvcPCdLEF0SsG1kbGzNb7qQYj3c7b6v2E3XTlbNpZsgTjt36oKm0Z2tU11D6EbW4i8KIHuqA==",
"requires": {}
},
"throat": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/throat/-/throat-6.0.1.tgz",

View File

@@ -51,6 +51,7 @@
"tern": "^0.24.3",
"three": "^0.130.0",
"three-globe": "^2.18.5",
"three-trackballcontrols": "^0.9.0",
"vue-apollo": "^3.0.7",
"vue-cli-plugin-apollo": "^0.22.2",
"vue-functional-data-merge": "^3.1.0",

View File

@@ -1,5 +1,27 @@
<template>
<div>
<FirebaseLogin />
<ButtonPrimary
v-if="currentUser === null"
label="Get Started"
@click.native="showLogin = true"
/>
<FirebaseLogin :show="showLogin" @hide-modal="showLogin = false" />
</div>
</template>
<script>
import { currentUser$ } from "~/helpers/fb/auth"
export default {
data() {
return {
showLogin: false,
}
},
subscriptions() {
return {
currentUser: currentUser$,
}
},
}
</script>

View File

@@ -4,74 +4,148 @@
<Teams />
</div>
<AppSection label="account">
<div class="flex flex-col">
<label>{{ $t("account") }}</label>
<div v-if="currentUser">
<ButtonSecondary />
<img
v-if="currentUser.photoURL"
:src="currentUser.photoURL"
class="w-6 h-6 rounded-full material-icons"
/>
<i v-else class="material-icons">account_circle</i>
<span>
{{ currentUser.displayName || $t("nothing_found") }}
</span>
<br />
<ButtonSecondary />
<i class="material-icons">email</i>
<span>
{{ currentUser.email || $t("nothing_found") }}
</span>
<br />
<FirebaseLogout />
<p>
<SmartToggle
:on="SYNC_COLLECTIONS"
@change="toggleSettings('syncCollections', !SYNC_COLLECTIONS)"
>
{{ $t("syncCollections") + " " + $t("sync") }}
{{ SYNC_COLLECTIONS ? $t("enabled") : $t("disabled") }}
</SmartToggle>
</p>
<p>
<SmartToggle
:on="SYNC_ENVIRONMENTS"
@change="toggleSettings('syncEnvironments', !SYNC_ENVIRONMENTS)"
>
{{ $t("syncEnvironments") + " " + $t("sync") }}
{{ SYNC_ENVIRONMENTS ? $t("enabled") : $t("disabled") }}
</SmartToggle>
</p>
<p>
<SmartToggle
:on="SYNC_HISTORY"
@change="toggleSettings('syncHistory', !SYNC_HISTORY)"
>
{{ $t("syncHistory") + " " + $t("sync") }}
{{ SYNC_HISTORY ? $t("enabled") : $t("disabled") }}
</SmartToggle>
<div class="space-y-8">
<div class="md:grid md:grid-cols-3 md:gap-4">
<div class="md:col-span-1 p-8">
<h3 class="heading">
{{ $t("account") }}
</h3>
<p class="mt-1 text-xs text-secondaryLight">
Customize your account settings.
</p>
</div>
<div v-else>
<label>{{ $t("login_with") }}</label>
<p>
<FirebaseLogin @show-email="showEmail = true" />
</p>
<div class="md:col-span-2 border border-divider p-8 rounded-lg">
<div v-if="currentUser === null">
<ButtonPrimary label="Log in" @click.native="showLogin = true" />
<div class="mt-4 text-xs text-secondaryLight">
Log in to access.
</div>
</div>
<div v-else class="space-y-8">
<fieldset>
<legend class="font-bold text-secondaryDark">User</legend>
<div class="mt-4 space-y-4">
<div class="flex items-start">
<div class="flex items-center">
<img
v-if="currentUser.photoURL"
:src="currentUser.photoURL"
class="w-5 h-5 rounded-full material-icons"
/>
<i v-else class="material-icons">account_circle</i>
</div>
<div class="ml-4">
<label>
{{ currentUser.displayName || $t("nothing_found") }}
</label>
<p class="mt-1 text-xs text-secondaryLight">
This is your display name.
</p>
</div>
</div>
<div class="flex items-start">
<div class="flex items-center">
<i class="material-icons">email</i>
</div>
<div class="ml-4">
<label>
{{ currentUser.email || $t("nothing_found") }}
</label>
<p class="mt-1 text-xs text-secondaryLight">
Your primary email address.
</p>
</div>
</div>
</div>
</fieldset>
<fieldset>
<legend class="font-bold text-secondaryDark">Sync</legend>
<div class="mt-1 text-xs text-secondaryLight">
These settings are synced to cloud.
</div>
<div class="mt-4 space-y-4">
<div class="flex items-center">
<SmartToggle
:on="SYNC_COLLECTIONS"
@change="
toggleSettings('syncCollections', !SYNC_COLLECTIONS)
"
>
{{ $t("syncCollections") }}
</SmartToggle>
</div>
<div class="flex items-center">
<SmartToggle
:on="SYNC_ENVIRONMENTS"
@change="
toggleSettings('syncEnvironments', !SYNC_ENVIRONMENTS)
"
>
{{ $t("syncEnvironments") }}
</SmartToggle>
</div>
<div class="flex items-center">
<SmartToggle
:on="SYNC_HISTORY"
@change="toggleSettings('syncHistory', !SYNC_HISTORY)"
>
{{ $t("syncHistory") }}
</SmartToggle>
</div>
</div>
</fieldset>
</div>
</div>
</div>
</AppSection>
<div class="md:grid md:grid-cols-3 md:gap-4">
<div class="md:col-span-1 p-8">
<h3 class="heading">
{{ $t("theme") }}
</h3>
<p class="mt-1 text-xs text-secondaryLight">
Customize your application theme.
</p>
</div>
<div
class="md:col-span-2 border border-divider p-8 rounded-lg space-y-8"
>
<fieldset>
<legend class="font-bold text-secondaryDark">
{{ $t("background") }}
</legend>
<div class="mt-1 text-xs text-secondaryLight">
<ColorScheme placeholder="..." tag="span">
{{
$colorMode.preference.charAt(0).toUpperCase() +
$colorMode.preference.slice(1)
}}
<span v-if="$colorMode.preference === 'system'">
({{ $colorMode.value }} mode detected)
</span>
</ColorScheme>
</div>
<div class="mt-4">
<SmartColorModePicker />
</div>
</fieldset>
<fieldset>
<legend class="font-bold text-secondaryDark">
{{ $t("color") }}
</legend>
<div class="mt-1 text-xs text-secondaryLight">
{{ active.charAt(0).toUpperCase() + active.slice(1) }}
</div>
<div class="mt-4">
<SmartAccentModePicker />
</div>
</fieldset>
</div>
</div>
</div>
<AppSection label="theme">
<div class="flex flex-col">
<label>{{ $t("theme") }}</label>
<SmartColorModePicker />
<SmartAccentModePicker />
<span>
<SmartToggle
:on="SCROLL_INTO_ENABLED"
@@ -199,7 +273,7 @@
</div>
</div>
</AppSection>
<FirebaseEmail :show="showEmail" @hide-modal="showEmail = false" />
<FirebaseLogin :show="showLogin" @hide-modal="showLogin = false" />
</div>
</template>
@@ -215,6 +289,7 @@ import {
import type { KeysMatching } from "~/types/ts-utils"
import { currentUserInfo$ } from "~/helpers/teams/BackendUserInfo"
import { currentUser$ } from "~/helpers/fb/auth"
import { getLocalConfig } from "~/newstore/localpersistence"
type SettingsType = typeof defaultSettings
@@ -237,10 +312,12 @@ export default Vue.extend({
EXTENSIONS_ENABLED: true,
PROXY_ENABLED: true,
showEmail: false,
currentBackendUser: null,
currentUser: null,
showLogin: false,
active: getLocalConfig("THEME_COLOR") || "green",
}
},
subscriptions() {