mirror of
https://github.com/BetterSEQTA/BetterSEQTA-Plus.git
synced 2026-06-06 03:34:40 +00:00
Merge pull request #417 from StroepWafel/updated-sign-in
feat: Unified portaled sign-in overlay
This commit is contained in:
@@ -1,29 +1,32 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { fade } from "svelte/transition";
|
import { fade } from "svelte/transition";
|
||||||
import { animate } from "motion";
|
import { animate } from "motion";
|
||||||
import { closeExtensionPopup } from "@/seqta/utils/Closers/closeExtensionPopup";
|
import { onMount } from "svelte";
|
||||||
|
import { cloudAuth } from "@/seqta/utils/CloudAuth";
|
||||||
|
import CloudLoginForm from "@/interface/components/store/CloudLoginForm.svelte";
|
||||||
|
|
||||||
let { onClose } = $props<{ onClose: () => void }>();
|
let { onClose } = $props<{ onClose: () => void }>();
|
||||||
let modalElement: HTMLElement;
|
let modalElement: HTMLElement;
|
||||||
|
|
||||||
$effect(() => {
|
onMount(() => {
|
||||||
if (modalElement) {
|
return cloudAuth.subscribe((s) => {
|
||||||
animate(modalElement, { scale: [0.9, 1], opacity: [0, 1] }, { type: "spring", stiffness: 300, damping: 25 });
|
if (s.isLoggedIn) onClose();
|
||||||
}
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
function handleSignIn() {
|
$effect(() => {
|
||||||
onClose();
|
if (modalElement) {
|
||||||
if (document.getElementById("ExtensionPopup")) {
|
animate(
|
||||||
closeExtensionPopup();
|
modalElement,
|
||||||
} else {
|
{ scale: [0.9, 1], opacity: [0, 1] },
|
||||||
window.close();
|
{ type: "spring", stiffness: 300, damping: 25 },
|
||||||
}
|
);
|
||||||
}
|
}
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="flex fixed inset-0 z-[10000] justify-center items-center bg-black/50"
|
class="flex fixed inset-0 z-[99999] justify-center items-center bg-black/50"
|
||||||
onclick={(e) => {
|
onclick={(e) => {
|
||||||
if (e.target === e.currentTarget) onClose();
|
if (e.target === e.currentTarget) onClose();
|
||||||
}}
|
}}
|
||||||
@@ -37,7 +40,7 @@
|
|||||||
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
||||||
<div
|
<div
|
||||||
bind:this={modalElement}
|
bind:this={modalElement}
|
||||||
class="p-4 mx-4 w-full max-w-md bg-white rounded-2xl shadow-2xl dark:bg-zinc-800 dark:text-white"
|
class="p-4 mx-4 w-full max-w-md max-h-[90vh] overflow-y-auto bg-white rounded-2xl shadow-2xl dark:bg-zinc-800 dark:text-white"
|
||||||
onclick={(e) => e.stopPropagation()}
|
onclick={(e) => e.stopPropagation()}
|
||||||
onkeydown={(e) => e.stopPropagation()}
|
onkeydown={(e) => e.stopPropagation()}
|
||||||
>
|
>
|
||||||
@@ -45,32 +48,19 @@
|
|||||||
Sign in to favorite themes
|
Sign in to favorite themes
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
<p class="mb-6 text-zinc-600 dark:text-zinc-400">
|
<p class="mb-4 text-sm text-zinc-600 dark:text-zinc-400">
|
||||||
Sign in in the Theme Store to save favorites across devices, or create an account to get started.
|
Sign in to the Theme Store to save favorites across devices, or create an account to get started.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div class="flex flex-wrap gap-2 justify-end">
|
<CloudLoginForm compact onSuccess={onClose} />
|
||||||
|
|
||||||
|
<div class="flex justify-end mt-4">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onclick={onClose}
|
onclick={onClose}
|
||||||
class="px-4 py-2 text-sm font-medium rounded-lg bg-zinc-200 dark:bg-zinc-700 text-zinc-700 dark:text-zinc-200 hover:bg-zinc-300 dark:hover:bg-zinc-600 transition-colors duration-200"
|
class="px-4 py-2 text-sm font-medium rounded-lg bg-zinc-200 dark:bg-zinc-700 text-zinc-700 dark:text-zinc-200 hover:bg-zinc-300 dark:hover:bg-zinc-600 transition-colors duration-200"
|
||||||
>
|
>
|
||||||
OK
|
Close
|
||||||
</button>
|
|
||||||
<a
|
|
||||||
href="https://accounts.betterseqta.org/register"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
class="inline-flex items-center gap-2 px-4 py-2 text-sm font-medium rounded-lg border border-zinc-200 dark:border-zinc-600 text-zinc-700 dark:text-zinc-300 hover:bg-zinc-100 dark:hover:bg-zinc-800 transition-all duration-200"
|
|
||||||
>
|
|
||||||
Create account
|
|
||||||
</a>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
onclick={handleSignIn}
|
|
||||||
class="px-4 py-2 text-sm font-medium rounded-lg bg-zinc-800 dark:bg-zinc-200 text-white dark:text-zinc-900 hover:bg-zinc-700 dark:hover:bg-zinc-300 transition-colors duration-200"
|
|
||||||
>
|
|
||||||
Sign in
|
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,11 +1,8 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { onMount } from "svelte";
|
import { onMount } from "svelte";
|
||||||
import { cloudAuth } from "@/seqta/utils/CloudAuth";
|
import { cloudAuth } from "@/seqta/utils/CloudAuth";
|
||||||
|
import CloudLoginForm from "./CloudLoginForm.svelte";
|
||||||
|
|
||||||
let username = $state("");
|
|
||||||
let password = $state("");
|
|
||||||
let loading = $state(false);
|
|
||||||
let error = $state<string | null>(null);
|
|
||||||
let cloudState = $state(cloudAuth.state);
|
let cloudState = $state(cloudAuth.state);
|
||||||
let open = $state(false);
|
let open = $state(false);
|
||||||
let dropdownEl: HTMLElement;
|
let dropdownEl: HTMLElement;
|
||||||
@@ -35,27 +32,6 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
async function handleLogin() {
|
|
||||||
if (loading) return;
|
|
||||||
error = null;
|
|
||||||
if (!username.trim() || !password) {
|
|
||||||
error = "Please enter username and password";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
loading = true;
|
|
||||||
try {
|
|
||||||
const result = await cloudAuth.login(username.trim(), password);
|
|
||||||
if (result.success) {
|
|
||||||
password = "";
|
|
||||||
open = false;
|
|
||||||
} else {
|
|
||||||
error = result.error ?? "Login failed";
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
loading = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function handleLogout() {
|
async function handleLogout() {
|
||||||
await cloudAuth.logout();
|
await cloudAuth.logout();
|
||||||
open = false;
|
open = false;
|
||||||
@@ -142,58 +118,11 @@
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
{:else}
|
{:else}
|
||||||
<p class="mb-4 text-base text-zinc-600 dark:text-zinc-400">
|
<CloudLoginForm
|
||||||
Sign in to favorite themes. Your favorites sync across devices when logged in.
|
onSuccess={() => {
|
||||||
</p>
|
open = false;
|
||||||
<form
|
|
||||||
class="flex flex-col gap-3"
|
|
||||||
autocomplete="off"
|
|
||||||
onsubmit={(e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
handleLogin();
|
|
||||||
}}
|
}}
|
||||||
>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
name="betterseqta-cloud-username"
|
|
||||||
autocomplete="off"
|
|
||||||
placeholder="Email or username"
|
|
||||||
bind:value={username}
|
|
||||||
disabled={loading}
|
|
||||||
readonly
|
|
||||||
onfocus={(e) => e.currentTarget.removeAttribute('readonly')}
|
|
||||||
class="w-full px-4 py-3 text-base rounded-lg bg-zinc-100 dark:bg-zinc-800 dark:text-white border border-zinc-200 dark:border-zinc-600 focus:outline-none focus:ring-2 focus:ring-accent-ring focus:border-transparent transition-colors duration-200"
|
|
||||||
/>
|
/>
|
||||||
<input
|
|
||||||
type="password"
|
|
||||||
name="betterseqta-cloud-password"
|
|
||||||
autocomplete="new-password"
|
|
||||||
placeholder="Password"
|
|
||||||
bind:value={password}
|
|
||||||
disabled={loading}
|
|
||||||
readonly
|
|
||||||
onfocus={(e) => e.currentTarget.removeAttribute('readonly')}
|
|
||||||
class="w-full px-4 py-3 text-base rounded-lg bg-zinc-100 dark:bg-zinc-800 dark:text-white border border-zinc-200 dark:border-zinc-600 focus:outline-none focus:ring-2 focus:ring-accent-ring focus:border-transparent transition-colors duration-200"
|
|
||||||
/>
|
|
||||||
{#if error}
|
|
||||||
<p class="text-base text-red-600 dark:text-red-400">{error}</p>
|
|
||||||
{/if}
|
|
||||||
<button
|
|
||||||
type="submit"
|
|
||||||
disabled={loading}
|
|
||||||
class="w-full px-4 py-3 text-base font-medium rounded-lg bg-zinc-800 dark:bg-zinc-200 text-white dark:text-zinc-900 hover:bg-zinc-700 dark:hover:bg-zinc-300 disabled:opacity-50 transition-colors duration-200"
|
|
||||||
>
|
|
||||||
{loading ? "Signing in..." : "Sign in"}
|
|
||||||
</button>
|
|
||||||
<a
|
|
||||||
href="https://accounts.betterseqta.org/register"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
class="inline-flex items-center justify-center gap-2 px-4 py-3 text-base font-medium rounded-lg border border-zinc-200 dark:border-zinc-600 text-zinc-700 dark:text-zinc-300 hover:bg-zinc-100 dark:hover:bg-zinc-800 transition-all duration-200"
|
|
||||||
>
|
|
||||||
Create account
|
|
||||||
</a>
|
|
||||||
</form>
|
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -0,0 +1,111 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { cloudAuth } from "@/seqta/utils/CloudAuth";
|
||||||
|
|
||||||
|
let {
|
||||||
|
introText,
|
||||||
|
onSuccess,
|
||||||
|
compact = false,
|
||||||
|
} = $props<{
|
||||||
|
introText?: string;
|
||||||
|
onSuccess?: () => void;
|
||||||
|
/** Smaller padding/text for overlays (e.g. SignInToFavoriteModal) */
|
||||||
|
compact?: boolean;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
let username = $state("");
|
||||||
|
let password = $state("");
|
||||||
|
let loading = $state(false);
|
||||||
|
let error = $state<string | null>(null);
|
||||||
|
|
||||||
|
const inputClass = $derived(
|
||||||
|
compact
|
||||||
|
? "w-full px-4 py-2 text-sm rounded-lg bg-zinc-100 dark:bg-zinc-800 dark:text-white border border-zinc-200 dark:border-zinc-600 focus:outline-none focus:ring-2 focus:ring-accent-ring focus:border-transparent transition-colors duration-200"
|
||||||
|
: "w-full px-4 py-3 text-base rounded-lg bg-zinc-100 dark:bg-zinc-800 dark:text-white border border-zinc-200 dark:border-zinc-600 focus:outline-none focus:ring-2 focus:ring-accent-ring focus:border-transparent transition-colors duration-200",
|
||||||
|
);
|
||||||
|
|
||||||
|
const btnClass = $derived(
|
||||||
|
compact
|
||||||
|
? "w-full px-4 py-2 text-sm font-medium rounded-lg bg-zinc-800 dark:bg-zinc-200 text-white dark:text-zinc-900 hover:bg-zinc-700 dark:hover:bg-zinc-300 disabled:opacity-50 transition-colors duration-200"
|
||||||
|
: "w-full px-4 py-3 text-base font-medium rounded-lg bg-zinc-800 dark:bg-zinc-200 text-white dark:text-zinc-900 hover:bg-zinc-700 dark:hover:bg-zinc-300 disabled:opacity-50 transition-colors duration-200",
|
||||||
|
);
|
||||||
|
|
||||||
|
const linkClass = $derived(
|
||||||
|
compact
|
||||||
|
? "inline-flex items-center justify-center gap-2 px-4 py-2 text-sm font-medium rounded-lg border border-zinc-200 dark:border-zinc-600 text-zinc-700 dark:text-zinc-300 hover:bg-zinc-100 dark:hover:bg-zinc-800 transition-all duration-200"
|
||||||
|
: "inline-flex items-center justify-center gap-2 px-4 py-3 text-base font-medium rounded-lg border border-zinc-200 dark:border-zinc-600 text-zinc-700 dark:text-zinc-300 hover:bg-zinc-100 dark:hover:bg-zinc-800 transition-all duration-200",
|
||||||
|
);
|
||||||
|
|
||||||
|
async function handleLogin() {
|
||||||
|
if (loading) return;
|
||||||
|
error = null;
|
||||||
|
if (!username.trim() || !password) {
|
||||||
|
error = "Please enter username and password";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
loading = true;
|
||||||
|
try {
|
||||||
|
const result = await cloudAuth.login(username.trim(), password);
|
||||||
|
if (result.success) {
|
||||||
|
password = "";
|
||||||
|
onSuccess?.();
|
||||||
|
} else {
|
||||||
|
error = result.error ?? "Login failed";
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
loading = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#if introText}
|
||||||
|
<p
|
||||||
|
class="mb-4 text-zinc-600 dark:text-zinc-400 {compact ? 'text-sm' : 'text-base'}"
|
||||||
|
>
|
||||||
|
{introText}
|
||||||
|
</p>
|
||||||
|
{/if}
|
||||||
|
<form
|
||||||
|
class="flex flex-col gap-3"
|
||||||
|
autocomplete="off"
|
||||||
|
onsubmit={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
handleLogin();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
name="betterseqta-cloud-username"
|
||||||
|
autocomplete="off"
|
||||||
|
placeholder="Email or username"
|
||||||
|
bind:value={username}
|
||||||
|
disabled={loading}
|
||||||
|
readonly
|
||||||
|
onfocus={(e) => e.currentTarget.removeAttribute("readonly")}
|
||||||
|
class={inputClass}
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
type="password"
|
||||||
|
name="betterseqta-cloud-password"
|
||||||
|
autocomplete="new-password"
|
||||||
|
placeholder="Password"
|
||||||
|
bind:value={password}
|
||||||
|
disabled={loading}
|
||||||
|
readonly
|
||||||
|
onfocus={(e) => e.currentTarget.removeAttribute("readonly")}
|
||||||
|
class={inputClass}
|
||||||
|
/>
|
||||||
|
{#if error}
|
||||||
|
<p class="text-red-600 dark:text-red-400 {compact ? 'text-sm' : 'text-base'}">{error}</p>
|
||||||
|
{/if}
|
||||||
|
<button type="submit" disabled={loading} class={btnClass}>
|
||||||
|
{loading ? "Signing in..." : "Sign in"}
|
||||||
|
</button>
|
||||||
|
<a
|
||||||
|
href="https://accounts.betterseqta.org/register"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
class={linkClass}
|
||||||
|
>
|
||||||
|
Create account
|
||||||
|
</a>
|
||||||
|
</form>
|
||||||
@@ -2,16 +2,14 @@
|
|||||||
import type { Theme } from '@/interface/types/Theme'
|
import type { Theme } from '@/interface/types/Theme'
|
||||||
import { fade } from 'svelte/transition';
|
import { fade } from 'svelte/transition';
|
||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
import SignInToFavoriteModal from '@/interface/components/SignInToFavoriteModal.svelte';
|
let { theme, onClick, toggleFavorite, isLoggedIn, onRequestSignIn } = $props<{
|
||||||
|
|
||||||
let { theme, onClick, toggleFavorite, isLoggedIn } = $props<{
|
|
||||||
theme: Theme;
|
theme: Theme;
|
||||||
onClick: () => void;
|
onClick: () => void;
|
||||||
toggleFavorite: (theme: Theme) => void;
|
toggleFavorite: (theme: Theme) => void;
|
||||||
isLoggedIn: boolean;
|
isLoggedIn: boolean;
|
||||||
|
onRequestSignIn?: () => void;
|
||||||
}>();
|
}>();
|
||||||
let menuOpen = $state(false);
|
let menuOpen = $state(false);
|
||||||
let showSignInModal = $state(false);
|
|
||||||
let menuRef: HTMLDivElement;
|
let menuRef: HTMLDivElement;
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
@@ -34,14 +32,23 @@
|
|||||||
if (isLoggedIn) {
|
if (isLoggedIn) {
|
||||||
toggleFavorite(theme);
|
toggleFavorite(theme);
|
||||||
} else {
|
} else {
|
||||||
showSignInModal = true;
|
onRequestSignIn?.();
|
||||||
}
|
}
|
||||||
menuOpen = false;
|
menuOpen = false;
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="w-full cursor-pointer" role="button" tabindex="-1" onkeydown={onClick} onclick={handleCardClick}>
|
<div
|
||||||
<div class="bg-gray-50 w-full transition-all hover:scale-105 duration-500 relative group flex flex-col hover:shadow-2xl dark:hover:shadow-white/[0.1] dark:hover:shadow-white/[0.8] dark:bg-zinc-800 dark:border-white/[0.1] h-auto rounded-xl overflow-clip border" transition:fade>
|
class="relative z-0 hover:z-20 w-full cursor-pointer"
|
||||||
|
role="button"
|
||||||
|
tabindex="-1"
|
||||||
|
onkeydown={onClick}
|
||||||
|
onclick={handleCardClick}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="bg-gray-50 w-full transition-all duration-500 ease-out relative group flex flex-col rounded-xl overflow-clip border hover:scale-105 hover:shadow-2xl dark:hover:shadow-white/[0.1] dark:hover:shadow-white/[0.8] dark:bg-zinc-800 dark:border-white/[0.1] h-auto"
|
||||||
|
transition:fade
|
||||||
|
>
|
||||||
<!-- Menu dropdown -->
|
<!-- Menu dropdown -->
|
||||||
<div class="absolute top-2 right-2 z-20" data-theme-menu bind:this={menuRef}>
|
<div class="absolute top-2 right-2 z-20" data-theme-menu bind:this={menuRef}>
|
||||||
<button
|
<button
|
||||||
@@ -104,7 +111,3 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{#if showSignInModal}
|
|
||||||
<SignInToFavoriteModal onClose={() => (showSignInModal = false)} />
|
|
||||||
{/if}
|
|
||||||
|
|||||||
@@ -2,12 +2,13 @@
|
|||||||
import type { Theme } from '@/interface/types/Theme'
|
import type { Theme } from '@/interface/types/Theme'
|
||||||
import ThemeCard from './ThemeCard.svelte';
|
import ThemeCard from './ThemeCard.svelte';
|
||||||
|
|
||||||
let { themes, searchTerm, setDisplayTheme, toggleFavorite, isLoggedIn } = $props<{
|
let { themes, searchTerm, setDisplayTheme, toggleFavorite, isLoggedIn, onRequestSignIn } = $props<{
|
||||||
themes: Theme[];
|
themes: Theme[];
|
||||||
searchTerm: string;
|
searchTerm: string;
|
||||||
setDisplayTheme: (theme: Theme) => void;
|
setDisplayTheme: (theme: Theme) => void;
|
||||||
toggleFavorite: (theme: Theme) => void;
|
toggleFavorite: (theme: Theme) => void;
|
||||||
isLoggedIn: boolean;
|
isLoggedIn: boolean;
|
||||||
|
onRequestSignIn?: () => void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
let filteredThemes = $derived(themes.filter((theme: Theme) =>
|
let filteredThemes = $derived(themes.filter((theme: Theme) =>
|
||||||
@@ -23,12 +24,13 @@
|
|||||||
onClick={() => setDisplayTheme(theme)}
|
onClick={() => setDisplayTheme(theme)}
|
||||||
{toggleFavorite}
|
{toggleFavorite}
|
||||||
{isLoggedIn}
|
{isLoggedIn}
|
||||||
|
{onRequestSignIn}
|
||||||
/>
|
/>
|
||||||
{/each}
|
{/each}
|
||||||
|
|
||||||
{#if filteredThemes.length !== 0}
|
{#if filteredThemes.length !== 0}
|
||||||
<a href="https://betterseqta.gitbook.io/betterseqta-docs" class='w-full cursor-pointer'>
|
<a href="https://betterseqta.gitbook.io/betterseqta-docs" class="block relative z-0 hover:z-20 w-full cursor-pointer">
|
||||||
<div class="bg-zinc-50 h-48 w-full transition-all hover:scale-105 duration-500 relative justify-center items-center group group/card flex flex-col hover:shadow-2xl dark:hover:shadow-white/[0.1] hover:shadow-white/[0.8] dark:bg-zinc-800 dark:border-white/[0.1] rounded-xl overflow-clip border">
|
<div class="bg-zinc-50 h-48 w-full transition-all duration-500 ease-out relative overflow-clip rounded-xl border group group/card flex flex-col justify-center items-center hover:scale-105 hover:shadow-2xl dark:hover:shadow-white/[0.1] hover:shadow-white/[0.8] dark:bg-zinc-800 dark:border-white/[0.1]">
|
||||||
<div class="text-2xl font-IconFamily">{'\uecb3'}</div>
|
<div class="text-2xl font-IconFamily">{'\uecb3'}</div>
|
||||||
<div class="text-xl font-bold text-center transition-all duration-500 dark:text-white">
|
<div class="text-xl font-bold text-center transition-all duration-500 dark:text-white">
|
||||||
Got a Theme Idea?
|
Got a Theme Idea?
|
||||||
|
|||||||
@@ -2,9 +2,7 @@
|
|||||||
import type { Theme } from '@/interface/types/Theme'
|
import type { Theme } from '@/interface/types/Theme'
|
||||||
import { fade } from 'svelte/transition';
|
import { fade } from 'svelte/transition';
|
||||||
import { animate } from 'motion';
|
import { animate } from 'motion';
|
||||||
import SignInToFavoriteModal from '@/interface/components/SignInToFavoriteModal.svelte';
|
let { theme, currentThemes, setDisplayTheme, onInstall, onRemove, allThemes, displayTheme, toggleFavorite, isLoggedIn, onRequestSignIn } = $props<{
|
||||||
|
|
||||||
let { theme, currentThemes, setDisplayTheme, onInstall, onRemove, allThemes, displayTheme, toggleFavorite, isLoggedIn } = $props<{
|
|
||||||
theme: Theme | null;
|
theme: Theme | null;
|
||||||
currentThemes: string[];
|
currentThemes: string[];
|
||||||
setDisplayTheme: (theme: Theme | null) => void;
|
setDisplayTheme: (theme: Theme | null) => void;
|
||||||
@@ -14,16 +12,16 @@
|
|||||||
displayTheme: Theme | null;
|
displayTheme: Theme | null;
|
||||||
toggleFavorite?: (theme: Theme) => void;
|
toggleFavorite?: (theme: Theme) => void;
|
||||||
isLoggedIn?: boolean;
|
isLoggedIn?: boolean;
|
||||||
|
onRequestSignIn?: () => void;
|
||||||
}>();
|
}>();
|
||||||
let installing = $state(false);
|
let installing = $state(false);
|
||||||
let showSignInModal = $state(false);
|
|
||||||
let modalElement: HTMLElement;
|
let modalElement: HTMLElement;
|
||||||
|
|
||||||
function handleFavoriteClick() {
|
function handleFavoriteClick() {
|
||||||
if (isLoggedIn && toggleFavorite && theme) {
|
if (isLoggedIn && toggleFavorite && theme) {
|
||||||
toggleFavorite(theme);
|
toggleFavorite(theme);
|
||||||
} else {
|
} else {
|
||||||
showSignInModal = true;
|
onRequestSignIn?.();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -159,8 +157,8 @@
|
|||||||
</h3>
|
</h3>
|
||||||
<div class="grid grid-cols-1 gap-4 sm:grid-cols-2">
|
<div class="grid grid-cols-1 gap-4 sm:grid-cols-2">
|
||||||
{#each getRelatedThemes() as relatedTheme (relatedTheme.id)}
|
{#each getRelatedThemes() as relatedTheme (relatedTheme.id)}
|
||||||
<button onclick={() => { hideModal(relatedTheme) }} class="w-full cursor-pointer">
|
<button onclick={() => { hideModal(relatedTheme) }} class="relative z-0 hover:z-20 w-full cursor-pointer">
|
||||||
<div class="bg-gray-50 w-full transition-all hover:scale-105 duration-500 relative group group/card flex flex-col hover:shadow-2xl dark:hover:shadow-white/[0.1] hover:shadow-white/[0.8] dark:bg-zinc-800 dark:border-white/[0.1] h-auto rounded-xl overflow-clip border">
|
<div class="bg-gray-50 w-full transition-all duration-500 ease-out relative group group/card flex flex-col hover:scale-105 hover:shadow-2xl dark:hover:shadow-white/[0.1] hover:shadow-white/[0.8] dark:bg-zinc-800 dark:border-white/[0.1] h-auto rounded-xl overflow-clip border">
|
||||||
<div class="absolute bottom-1 left-3 z-10 mb-1 text-xl font-bold text-white transition-all duration-500 group-hover:-translate-y-0.5">
|
<div class="absolute bottom-1 left-3 z-10 mb-1 text-xl font-bold text-white transition-all duration-500 group-hover:-translate-y-0.5">
|
||||||
{relatedTheme.name}
|
{relatedTheme.name}
|
||||||
</div>
|
</div>
|
||||||
@@ -180,7 +178,3 @@
|
|||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{#if showSignInModal}
|
|
||||||
<SignInToFavoriteModal onClose={() => (showSignInModal = false)} />
|
|
||||||
{/if}
|
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
import { loadBackground } from '@/seqta/ui/ImageBackgrounds'
|
import { loadBackground } from '@/seqta/ui/ImageBackgrounds'
|
||||||
import Backgrounds from '../components/store/Backgrounds.svelte'
|
import Backgrounds from '../components/store/Backgrounds.svelte'
|
||||||
import { cloudAuth } from '@/seqta/utils/CloudAuth'
|
import { cloudAuth } from '@/seqta/utils/CloudAuth'
|
||||||
|
import SignInToFavoriteModal from '../components/SignInToFavoriteModal.svelte'
|
||||||
|
|
||||||
const themeManager = ThemeManager.getInstance();
|
const themeManager = ThemeManager.getInstance();
|
||||||
let cloudLoggedIn = $state(cloudAuth.state.isLoggedIn);
|
let cloudLoggedIn = $state(cloudAuth.state.isLoggedIn);
|
||||||
@@ -34,6 +35,7 @@
|
|||||||
|
|
||||||
let error = $state<string | null>(null);
|
let error = $state<string | null>(null);
|
||||||
let selectedBackground = $state<string | null>(null);
|
let selectedBackground = $state<string | null>(null);
|
||||||
|
let showSignInOverlay = $state(false);
|
||||||
|
|
||||||
const fetchCurrentThemes = async () => {
|
const fetchCurrentThemes = async () => {
|
||||||
const themes = await themeManager.getAvailableThemes();
|
const themes = await themeManager.getAvailableThemes();
|
||||||
@@ -169,6 +171,7 @@
|
|||||||
{setDisplayTheme}
|
{setDisplayTheme}
|
||||||
{toggleFavorite}
|
{toggleFavorite}
|
||||||
isLoggedIn={cloudLoggedIn}
|
isLoggedIn={cloudLoggedIn}
|
||||||
|
onRequestSignIn={() => (showSignInOverlay = true)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{#if displayTheme}
|
{#if displayTheme}
|
||||||
@@ -180,6 +183,7 @@
|
|||||||
{setDisplayTheme}
|
{setDisplayTheme}
|
||||||
{toggleFavorite}
|
{toggleFavorite}
|
||||||
isLoggedIn={cloudLoggedIn}
|
isLoggedIn={cloudLoggedIn}
|
||||||
|
onRequestSignIn={() => (showSignInOverlay = true)}
|
||||||
onInstall={async () => {
|
onInstall={async () => {
|
||||||
if (displayTheme) {
|
if (displayTheme) {
|
||||||
await themeManager.downloadTheme(displayTheme);
|
await themeManager.downloadTheme(displayTheme);
|
||||||
@@ -204,4 +208,8 @@
|
|||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{#if showSignInOverlay}
|
||||||
|
<SignInToFavoriteModal onClose={() => (showSignInOverlay = false)} />
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
@@ -18,12 +18,9 @@ export class SettingsResizer {
|
|||||||
if (!iframePopup) return;
|
if (!iframePopup) return;
|
||||||
|
|
||||||
const viewportHeight = window.innerHeight;
|
const viewportHeight = window.innerHeight;
|
||||||
const idealHeight = viewportHeight - 80 - 15; // -80px for the top of the popup
|
const rawIdeal = viewportHeight - 80 - 15; // room below top chrome
|
||||||
|
const idealHeight = Math.min(Math.max(rawIdeal, 280), 600);
|
||||||
|
|
||||||
if (idealHeight > 600) {
|
|
||||||
iframePopup.style.height = "600px";
|
|
||||||
} else {
|
|
||||||
iframePopup.style.height = `${idealHeight}px`;
|
iframePopup.style.height = `${idealHeight}px`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user