silvester-24/src/lib/components/Questions.svelte

372 lines
9.0 KiB
Svelte

<script lang="ts">
import { persisted } from '$lib/helpers/localStore';
import { fade, slide } from 'svelte/transition';
import TextSplit from './TextSplit.svelte';
import ImageFrame from './ImageFrame.svelte';
import Button from './button.svelte';
import InputRange from './InputRange.svelte';
import { goto } from '$app/navigation';
import Loader from './Loader.svelte';
let data = persisted<{
name: string;
nameAccepted?: boolean;
provideAdelsTitel?: boolean;
adelsTitel?: string;
adelsTitelAccepted?: boolean;
adelsTitelSuggestions?: string[];
providePortrait?: boolean;
portraitUrl?: string;
portraitHairType?: 'straight' | 'curly' | 'bald';
portraitHairColor?: 'red' | 'brown' | 'blonde' | 'black' | 'grey' | 'white';
portraitHairLength?: 'short' | 'medium' | 'long';
portraitAccepted?: boolean;
portraitPublic?: boolean;
confidence?: number;
confidenceAccepted?: boolean;
createPersonality?: boolean;
}>('data', {
name: '',
nameAccepted: false,
adelsTitel: undefined,
adelsTitelSuggestions: [],
provideAdelsTitel: undefined,
portraitPublic: true,
confidence: undefined,
confidenceAccepted: false,
createPersonality: undefined
});
let loadingAdelsTitel = false;
async function fetchAdelsTitel() {
if (loadingAdelsTitel) return;
loadingAdelsTitel = true;
const res = await fetch('/api/ai/name/' + $data.name);
const json = await res.json();
$data.adelsTitelSuggestions = json;
loadingAdelsTitel = false;
}
let loadingPortrait = false;
async function fetchPortrait() {
if (loadingPortrait) return;
loadingPortrait = true;
const res = await fetch(`/api/ai/image/${$data.adelsTitel || $data.name}`, {
method: 'POST',
body: JSON.stringify({
hairType: $data.portraitHairType,
hairColor: $data.portraitHairColor,
hairLength: $data.portraitHairLength
})
});
const json = await res.json();
$data.portraitUrl = json.url;
loadingPortrait = false;
}
let isSubmitting = false;
async function submit() {
if (isSubmitting) return;
isSubmitting = true;
await fetch(`/api/invites`, {
method: 'POST',
body: JSON.stringify($data)
});
localStorage.clear();
localStorage.setItem('last-name', $data?.adelsTitel || '');
isSubmitting = false;
goto('/gallery');
}
</script>
<div class="wrapper">
<section in:slide={{ delay: 500 }}>
<TextSplit content="Wie lauten Euer Vor- und Nachname, edler Gast?" />
<input placeholder="Name" type="text" bind:value={$data.name} />
{#if !$data.nameAccepted}
<button
on:click={() => {
$data.nameAccepted = true;
}}>Name akzeptieren</button
>
{/if}
</section>
{#if ($data.name && $data.nameAccepted) || $data.confidence !== undefined}
<section in:slide={{ delay: 0, duration: 500 }}>
<TextSplit
content="Wie sicher dürfen wir mit Eurem glanzvollen Erscheinen rechnen?"
delay={0}
/>
<div in:fade={{ delay: 1200 }}>
<InputRange bind:value={$data.confidence} />
</div>
{#if !$data.confidenceAccepted}
<button
on:click={() => {
$data.confidenceAccepted = true;
}}>ok</button
>
{/if}
</section>
{/if}
{#if $data.confidence !== undefined && $data.confidenceAccepted && $data.createPersonality === undefined}
<section in:slide out:slide>
<TextSplit
content="Möchtet Ihr, dass wir eine alternative Persönlichkeit für Euch erschaffen?"
/>
<div>
<button
on:click={() => {
$data.createPersonality = true;
}}>Ja</button
>
<button
on:click={() => {
$data.createPersonality = false;
}}>Nein</button
>
</div>
{#if $data.adelsTitel}
{$data.adelsTitel}
{/if}
</section>
{/if}
{#if $data.createPersonality === true}
<section in:fade out:slide>
{#if $data.provideAdelsTitel === undefined}
<TextSplit content="Möchtet Ihr einen würdigen Adelstitel für Euch in Erwägung ziehen?" />
<div>
<button
on:click={() => {
fetchAdelsTitel();
$data.provideAdelsTitel = true;
}}
>
ja</button
>
<button
on:click={() => {
$data.adelsTitel = '';
$data.provideAdelsTitel = false;
}}
>
nein</button
>
</div>
{/if}
{#if loadingAdelsTitel}
<div style="display: flex; gap: 20px; align-items: center;">
<Loader />
<p>Adelstitel werden geschmiedet</p>
</div>
{:else if typeof $data.adelsTitel === 'string'}
<TextSplit content="Euer Adelstitel" />
<input placeholder="Name" type="text" bind:value={$data.adelsTitel} />
{#if !$data.adelsTitelAccepted}
<button
on:click={() => {
$data.adelsTitelAccepted = true;
}}>akzeptieren</button
>
{/if}
{:else if $data.adelsTitelSuggestions?.length}
<p>Adelstitel Vorschläge</p>
<hr />
{#each $data.adelsTitelSuggestions as suggestion}
<button
on:click={() => {
$data.adelsTitel = suggestion;
}}>{suggestion}</button
>
{/each}
<hr />
<button
on:click={() => {
fetchAdelsTitel();
}}>neue vorschläge</button
>
{/if}
</section>
{/if}
{#if $data.adelsTitel && $data.adelsTitelAccepted && $data.providePortrait !== false}
<section transition:slide class="portrait-frame" class:loaded={!!$data.portraitUrl}>
{#if $data.providePortrait === undefined}
<TextSplit
content="Sollten unsere begabten Künstler ein majestätisches Porträt von Euch anfertigen?"
/>
<div>
<button
on:click={() => {
$data.providePortrait = true;
}}>ja</button
>
<button
on:click={() => {
$data.providePortrait = true;
}}>false</button
>
</div>
{/if}
{#if $data.providePortrait && !loadingPortrait && !$data.portraitUrl}
<p>
Wir werden {$data.adelsTitel || $data.name} mit
<select placeholder="Typ" bind:value={$data.portraitHairType}>
<option value="straight">glatten</option>
<option value="curly">lockigen</option>
<option value="wavy">welligen</option>
</select>
<select placeholder="Länge" bind:value={$data.portraitHairLength}>
<option value="long">langen</option>
<option value="medium">medium-langen</option>
<option value="short">kurzen</option>
</select>,
<select placeholder="Farbe" bind:value={$data.portraitHairColor}>
<option value="red">roten</option>
<option value="black">schwarzen</option>
<option value="blond">blonden</option>
<option value="braunem">braunen</option>
</select> Haaren zeichnen
</p>
<button on:click={() => fetchPortrait()}> porträt malen (~15 Sekunden)</button>
{:else if loadingPortrait}
<div style="display: flex; gap: 20px; align-items: center;">
<Loader />
<p>Euer edles Antlitz wird gemalt</p>
</div>
{:else if $data.portraitUrl}
<div in:slide={{ duration: 2000 }} class="portrait">
<ImageFrame src={$data.portraitUrl} alt="portrait" />
{#if !$data.portraitAccepted}
<button
on:click={() => {
$data.portraitAccepted = true;
}}>Porträt annehmen</button
>
<button
on:click={() => {
$data.portraitUrl = undefined;
}}>Neues Porträt anfordern</button
>
{/if}
</div>
{/if}
</section>
{/if}
{#if $data.portraitAccepted}
{#if isSubmitting}
<section in:slide style="border: none;">
<div style="display: flex; gap: 20px; align-items: center;">
<Loader />
<p>Einladung wird abgeschickt</p>
</div>
</section>
{:else}
<div class="button-wrapper">
<Button on:click={() => submit()}>Möchtet Ihr diese königliche Einladung annehmen?</Button>
</div>
{/if}
{/if}
</div>
<style>
.button-wrapper {
display: flex;
justify-content: center;
padding-block: 20px;
margin: 0 auto;
width: 99%;
max-width: 500px;
padding-block: 50px;
font-size: 2em;
margin-bottom: 200px;
}
.portrait-frame {
margin-top: 100px;
}
.portrait-frame.loaded {
border: none;
}
button {
border: none;
border-radius: 5px;
padding: 5px 9px;
margin-right: 10px;
background: #866831;
box-shadow: 3px 3px 8px #e1b45f inset;
cursor: pointer;
}
.wrapper {
display: flex;
flex-direction: column;
gap: 50px;
padding-bottom: 200px;
}
.portrait {
width: 100%;
}
input[type='text'] {
font-family: 'Parisienne', cursive;
background: transparent;
border: none;
color: white;
width: 100%;
font-size: 1em;
margin: 0 auto;
border-bottom: 1px solid white;
box-sizing: border-box;
}
input[type='text']::after {
content: '';
background: white;
height: 1px;
width: 100%;
}
select {
background: transparent;
color: white;
border: none;
padding: 5px;
border-bottom: 1px solid white;
}
select > option {
color: black;
}
section {
color: white;
font-family: 'Parisienne', cursive;
max-width: 500px;
padding: 20px;
margin: 0 auto;
font-size: 2em;
width: 70%;
border-width: 30px;
border-style: solid;
border-image: url(/border_b.svg);
border-image-repeat: stretch;
border-image-slice: 100%;
border-image-slice: 24% 23%;
}
</style>