feat: replace seed phrase input with animated spinner

This commit is contained in:
Danny Morabito 2025-04-24 17:01:17 +02:00
parent 740921132a
commit 0bde5a4ef3
2 changed files with 163 additions and 16 deletions

View file

@ -20,6 +20,7 @@ import '@components/LoadingView';
import '@components/ProgressSteps';
import '@components/RelayLogs';
import '@components/Setup/CCNList';
import '@components/Setup/SeedPhraseSpinner';
import { NDKEvent, NDKKind, NDKPrivateKeySigner, NDKRelay, NDKRelaySet } from '@nostr-dev-kit/ndk';
import { nip19 } from '@nostr/tools';
@ -300,7 +301,7 @@ export class InitialSetup extends LitElement {
<main ${animate()}>
<section>
<h1>Welcome to Eve</h1>
<h2>Your Private Community Network</h2>
<h2>Your Closed Community Network</h2>
<p>
Connect, share, and engage with your community in a secure,
members-only space designed just for you.
@ -539,21 +540,7 @@ export class InitialSetup extends LitElement {
Security) to enable simpler invitation-based community access, as
well as improve your community's security.
</p>
<div class="input-group">
<arx-input
@change=${this.onSeedPhraseInput}
.value=${this.seedPhrase}
id="seed-input"
type="text"
placeholder="Enter seed phrase..."
></arx-input>
<arx-button
@click=${() => this.generateSeedPhrase()}
label="Generate"
variant="secondary"
>
</arx-button>
</div>
<arx-seed-phrase-spinner @change=${this.onSeedPhraseInput}></arx-seed-phrase-spinner>
</section>
<section>
<h3>Community Name</h3>

View file

@ -0,0 +1,160 @@
import { ArxInputChangeEvent } from '@/components/General/Input';
import * as nip06 from '@nostr/tools/nip06';
import { wordlist } from '@scure/bip39/wordlists/english';
import { LitElement, css, html } from 'lit';
import { customElement, state } from 'lit/decorators.js';
import { map } from 'lit/directives/map.js';
import { when } from 'lit/directives/when.js';
import '@components/General/Button';
@customElement('arx-seed-phrase-spinner')
export class SeedPhraseSpinner extends LitElement {
@state()
private words: (string | null)[] = [null, null, null, null, null, null, null, null, null, null, null, null];
@state()
private animating = false;
@state()
private tempWords: (string | null)[] = [null, null, null, null, null, null, null, null, null, null, null, null];
private getRandomWord() {
return wordlist[Math.floor(Math.random() * wordlist.length)];
}
static override styles = css`
.words-list {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 0.8rem;
width: 100%;
margin-bottom: 1rem;
}
.word {
width: 100%;
height: 3.5rem;
overflow: hidden;
position: relative;
border-radius: 0.5rem;
background: rgba(0, 0, 0, 0.05);
box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.1);
}
.word::before, .word::after {
content: '';
position: absolute;
left: 0;
right: 0;
height: 0.5rem;
z-index: 1;
pointer-events: none;
}
.word::before {
top: 0;
background: linear-gradient(to bottom, rgba(255, 255, 255, 0.7), transparent);
}
.word::after {
bottom: 0;
background: linear-gradient(to top, rgba(255, 255, 255, 0.7), transparent);
}
.word-inner {
display: flex;
flex-direction: column;
position: absolute;
width: 100%;
transition: transform 0.8s cubic-bezier(0.4, 0, 0.2, 1);
will-change: transform;
}
.word-item {
height: 3.5rem;
display: flex;
align-items: center;
justify-content: center;
font-size: 1.5rem;
font-weight: bold;
text-align: center;
padding: 0 0.25rem;
}
.controls {
display: flex;
justify-content: center;
margin-top: 1rem;
}
@keyframes spin {
0% { transform: translateY(0); }
100% { transform: translateY(-3.5rem); }
}
.spinning .word-inner {
animation: spin var(--animation-duration) var(--animation-curve) infinite;
}
.placeholder {
opacity: 0.5;
}
`;
override render() {
return html`
<div class="words-list ${this.animating ? 'spinning' : ''}">
${map(
this.tempWords,
(word) =>
html`
<div class="word">
<div class="word-inner">
${when(
this.animating,
() => html`
<div class="word-item placeholder">${word}</div>
<div class="word-item placeholder">${word}</div>
`,
() =>
html`
<div class="word-item ${!word ? 'placeholder' : ''}">${word || 'tap generate'}</div>
`,
)}
</div>
</div>
`,
)}
</div>
<div class="controls">
<arx-button
variant="primary"
label="Generate Seed Phrase"
?disabled=${this.animating}
?loading=${this.animating}
@click=${this.generateSeedPhrase}
></arx-button>
</div>
`;
}
private generateSeedPhrase() {
if (this.animating) return;
this.animating = true;
const intervalId = window.setInterval(() => {
this.tempWords = this.tempWords.map(() => this.getRandomWord());
}, 50);
setTimeout(() => {
const newWords = nip06.generateSeedWords().split(' ');
clearInterval(intervalId);
this.tempWords = newWords;
this.words = newWords;
this.animating = false;
this.dispatchEvent(new ArxInputChangeEvent(this.words.join(' ')));
}, 1500);
}
}