✨ feat: replace seed phrase input with animated spinner
This commit is contained in:
parent
740921132a
commit
0bde5a4ef3
2 changed files with 163 additions and 16 deletions
|
@ -20,6 +20,7 @@ import '@components/LoadingView';
|
||||||
import '@components/ProgressSteps';
|
import '@components/ProgressSteps';
|
||||||
import '@components/RelayLogs';
|
import '@components/RelayLogs';
|
||||||
import '@components/Setup/CCNList';
|
import '@components/Setup/CCNList';
|
||||||
|
import '@components/Setup/SeedPhraseSpinner';
|
||||||
import { NDKEvent, NDKKind, NDKPrivateKeySigner, NDKRelay, NDKRelaySet } from '@nostr-dev-kit/ndk';
|
import { NDKEvent, NDKKind, NDKPrivateKeySigner, NDKRelay, NDKRelaySet } from '@nostr-dev-kit/ndk';
|
||||||
import { nip19 } from '@nostr/tools';
|
import { nip19 } from '@nostr/tools';
|
||||||
|
|
||||||
|
@ -300,7 +301,7 @@ export class InitialSetup extends LitElement {
|
||||||
<main ${animate()}>
|
<main ${animate()}>
|
||||||
<section>
|
<section>
|
||||||
<h1>Welcome to Eve</h1>
|
<h1>Welcome to Eve</h1>
|
||||||
<h2>Your Private Community Network</h2>
|
<h2>Your Closed Community Network</h2>
|
||||||
<p>
|
<p>
|
||||||
Connect, share, and engage with your community in a secure,
|
Connect, share, and engage with your community in a secure,
|
||||||
members-only space designed just for you.
|
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
|
Security) to enable simpler invitation-based community access, as
|
||||||
well as improve your community's security.
|
well as improve your community's security.
|
||||||
</p>
|
</p>
|
||||||
<div class="input-group">
|
<arx-seed-phrase-spinner @change=${this.onSeedPhraseInput}></arx-seed-phrase-spinner>
|
||||||
<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>
|
|
||||||
</section>
|
</section>
|
||||||
<section>
|
<section>
|
||||||
<h3>Community Name</h3>
|
<h3>Community Name</h3>
|
||||||
|
|
160
src/components/Setup/SeedPhraseSpinner.ts
Normal file
160
src/components/Setup/SeedPhraseSpinner.ts
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue