🧹 Migrate Yarn registry URLs to npm registry: update yarn.lock dependencies for consistency and clean up unused entries.

This commit is contained in:
HolgerHatGarKeineNode
2026-01-23 20:02:21 +01:00
parent a2bce07520
commit 578e4f13fc
6 changed files with 508 additions and 668 deletions

1
.gitignore vendored
View File

@@ -23,3 +23,4 @@ yarn-error.log
/.sisyphus /.sisyphus
/.opencode /.opencode
.switch-omo-config* .switch-omo-config*
/videos

View File

@@ -2,6 +2,8 @@
namespace App\Livewire\Forms; namespace App\Livewire\Forms;
use App\Enums\AssociationStatus;
use App\Models\EinundzwanzigPleb;
use Livewire\Attributes\Validate; use Livewire\Attributes\Validate;
use Livewire\Form; use Livewire\Form;
@@ -10,6 +12,26 @@ class ApplicationForm extends Form
#[Validate('nullable|string')] #[Validate('nullable|string')]
public $reason = ''; public $reason = '';
#[Validate('boolean')] #[Validate('accepted')]
public $check = false; public $check = false;
public ?EinundzwanzigPleb $currentPleb = null;
public function setPleb(EinundzwanzigPleb $pleb): void
{
$this->currentPleb = $pleb;
}
public function apply(AssociationStatus|int $status): void
{
$this->validate();
$status = $status instanceof AssociationStatus ? $status : AssociationStatus::from($status);
$this->currentPleb->update([
'association_status' => $status,
]);
$this->reset('check');
}
} }

View File

@@ -0,0 +1,64 @@
<?php
namespace App\Livewire\Forms;
use App\Models\EinundzwanzigPleb;
use Flux\Flux;
use Livewire\Attributes\Validate;
use Livewire\Form;
class ProfileForm extends Form
{
#[Validate('nullable|email')]
public ?string $email = '';
#[Validate('nullable|string|max:255|regex:/^[a-z0-9_-]+$/|unique:einundzwanzig_plebs,nip05_handle')]
public ?string $nip05Handle = '';
public ?EinundzwanzigPleb $currentPleb;
public function setEmail(EinundzwanzigPleb $currentPleb): void
{
$this->currentPleb = $currentPleb;
$this->email = $currentPleb->email;
}
public function setNip05Handle(EinundzwanzigPleb $currentPleb): void
{
$this->currentPleb = $currentPleb;
$this->nip05Handle = $currentPleb->nip05_handle;
}
public function setPleb(EinundzwanzigPleb $currentPleb): void
{
$this->currentPleb = $currentPleb;
$this->email = $currentPleb->email;
$this->nip05Handle = $currentPleb->nip05_handle;
}
public function saveEmail(): void
{
$this->validateOnly('email');
$this->currentPleb->update([
'email' => $this->email,
]);
Flux::toast('E-Mail Adresse gespeichert.');
}
public function saveNip05Handle(): void
{
$this->validateOnly('nip05Handle');
$nip05Handle = $this->nip05Handle ? strtolower($this->nip05Handle) : null;
$this->currentPleb->update([
'nip05_handle' => $nip05Handle,
]);
$this->nip05Handle = $nip05Handle;
Flux::toast('NIP-05 Handle gespeichert.');
}
}

View File

@@ -1,6 +1,7 @@
<?php <?php
use App\Livewire\Forms\ApplicationForm; use App\Livewire\Forms\ApplicationForm;
use App\Livewire\Forms\ProfileForm;
use App\Models\EinundzwanzigPleb; use App\Models\EinundzwanzigPleb;
use App\Support\NostrAuth; use App\Support\NostrAuth;
use App\Traits\NostrFetcherTrait; use App\Traits\NostrFetcherTrait;
@@ -16,22 +17,19 @@ use swentel\nostr\Request\Request;
use swentel\nostr\Sign\Sign; use swentel\nostr\Sign\Sign;
use swentel\nostr\Subscription\Subscription; use swentel\nostr\Subscription\Subscription;
new class extends Component new class extends Component {
{
use NostrFetcherTrait; use NostrFetcherTrait;
public ApplicationForm $form; public ApplicationForm $form;
public ProfileForm $profileForm;
public bool $no = false; public bool $no = false;
public bool $showEmail = true; public bool $showEmail = true;
public string $fax = ''; public string $fax = '';
public ?string $email = '';
public ?string $nip05Handle = '';
public bool $nip05Verified = false; public bool $nip05Verified = false;
public ?string $nip05VerifiedHandle = null; public ?string $nip05VerifiedHandle = null;
@@ -67,15 +65,15 @@ new class extends Component
$this->currentPubkey = NostrAuth::pubkey(); $this->currentPubkey = NostrAuth::pubkey();
$this->currentPleb = EinundzwanzigPleb::query() $this->currentPleb = EinundzwanzigPleb::query()
->with([ ->with([
'paymentEvents' => fn ($query) => $query->where('year', date('Y')), 'paymentEvents' => fn($query) => $query->where('year', date('Y')),
'profile', 'profile',
]) ])
->where('pubkey', $this->currentPubkey)->first(); ->where('pubkey', $this->currentPubkey)->first();
if ($this->currentPleb) { if ($this->currentPleb) {
$this->email = $this->currentPleb->email; $this->profileForm->setPleb($this->currentPleb);
if ($this->currentPleb->nip05_handle) { $this->form->setPleb($this->currentPleb);
$this->nip05Handle = $this->currentPleb->nip05_handle;
if ($this->currentPleb->nip05_handle) {
// Get all NIP-05 handles for the current pubkey // Get all NIP-05 handles for the current pubkey
$this->nip05VerifiedHandles = $this->getNip05HandlesForPubkey($this->currentPubkey); $this->nip05VerifiedHandles = $this->getNip05HandlesForPubkey($this->currentPubkey);
@@ -84,13 +82,13 @@ new class extends Component
$this->nip05VerifiedHandle = $this->nip05VerifiedHandles[0]; $this->nip05VerifiedHandle = $this->nip05VerifiedHandles[0];
// Check if verified handle differs from database handle // Check if verified handle differs from database handle
if (! in_array($this->nip05Handle, $this->nip05VerifiedHandles, true)) { if (!in_array($this->profileForm->nip05Handle, $this->nip05VerifiedHandles, true)) {
$this->nip05HandleMismatch = true; $this->nip05HandleMismatch = true;
} }
} }
} }
$this->no = $this->currentPleb->no_email; $this->no = $this->currentPleb->no_email;
$this->showEmail = ! $this->no; $this->showEmail = !$this->no;
$this->amountToPay = config('app.env') === 'production' ? 21000 : 1; $this->amountToPay = config('app.env') === 'production' ? 21000 : 1;
if ($this->currentPleb->paymentEvents->count() < 1) { if ($this->currentPleb->paymentEvents->count() < 1) {
$this->createPaymentEvent(); $this->createPaymentEvent();
@@ -104,7 +102,7 @@ new class extends Component
public function updatedNo(): void public function updatedNo(): void
{ {
$this->showEmail = ! $this->no; $this->showEmail = !$this->no;
$this->currentPleb->update([ $this->currentPleb->update([
'no_email' => $this->no, 'no_email' => $this->no,
]); ]);
@@ -115,34 +113,59 @@ new class extends Component
$this->js('alert("Markus Turm wird sich per Fax melden!")'); $this->js('alert("Markus Turm wird sich per Fax melden!")');
} }
public function updatedNip05Handle(): void public function updatedProfileFormNip05Handle(): void
{ {
$this->nip05Handle = strtolower($this->nip05Handle); $this->profileForm->nip05Handle = strtolower($this->profileForm->nip05Handle);
}
public function handleNostrLoggedIn(string $pubkey): void
{
$this->currentPubkey = $pubkey;
$this->currentPleb = EinundzwanzigPleb::query()
->with([
'paymentEvents' => fn($query) => $query->where('year', date('Y')),
'profile',
])
->where('pubkey', $pubkey)->first();
if ($this->currentPleb) {
$this->profileForm->setPleb($this->currentPleb);
$this->form->setPleb($this->currentPleb);
$this->no = $this->currentPleb->no_email;
$this->showEmail = !$this->no;
}
}
public function handleNostrLoggedOut(): void
{
$this->currentPubkey = null;
$this->currentPleb = null;
} }
public function saveEmail(): void public function saveEmail(): void
{ {
$this->validate([ $this->profileForm->saveEmail();
'email' => 'required|email',
]);
$this->currentPleb->update([
'email' => $this->email,
]);
Flux::toast('E-Mail Adresse gespeichert.');
} }
public function saveNip05Handle(): void public function saveNip05Handle(): void
{ {
$this->validate([ $this->profileForm->saveNip05Handle();
'nip05Handle' => 'required|string|max:255|regex:/^[a-z0-9_-]+$/|unique:einundzwanzig_plebs,nip05_handle',
]);
$nip05Handle = strtolower($this->nip05Handle); // Refresh NIP-05 verification status after saving
if ($this->currentPleb->nip05_handle) {
$this->nip05VerifiedHandles = $this->getNip05HandlesForPubkey($this->currentPubkey);
$this->currentPleb->update([ if (count($this->nip05VerifiedHandles) > 0) {
'nip05_handle' => $nip05Handle, $this->nip05Verified = true;
]); $this->nip05VerifiedHandle = $this->nip05VerifiedHandles[0];
Flux::toast('NIP-05 Handle gespeichert.');
if (!in_array($this->profileForm->nip05Handle, $this->nip05VerifiedHandles, true)) {
$this->nip05HandleMismatch = true;
} else {
$this->nip05HandleMismatch = false;
}
}
}
} }
public function pay($comment): mixed public function pay($comment): mixed
@@ -229,17 +252,15 @@ new class extends Component
public function save($type): void public function save($type): void
{ {
$this->form->validate(); try {
if (! $this->form->check) { $this->form->apply($type);
$this->js('alert("Du musst den Statuten zustimmen.")'); Flux::toast('Mitgliedschaft erfolgreich beantragt!', variant: 'success');
} catch (\Illuminate\Validation\ValidationException $e) {
return; if (!$this->form->check) {
$this->js('alert("Du musst den Statuten zustimmen.")');
}
throw $e;
} }
$this->currentPleb
->update([
'association_status' => $type,
]);
} }
public function createPaymentEvent(): void public function createPaymentEvent(): void
@@ -295,7 +316,7 @@ new class extends Component
$this->events = collect($response[config('services.relay')]) $this->events = collect($response[config('services.relay')])
->map(function ($event) { ->map(function ($event) {
if (! isset($event->event)) { if (!isset($event->event)) {
return false; return false;
} }
@@ -367,12 +388,15 @@ new class extends Component
@if($currentPleb && $currentPleb->association_status->value > 1 && $currentYearIsPaid) @if($currentPleb && $currentPleb->association_status->value > 1 && $currentYearIsPaid)
<div class="mt-3 space-y-2"> <div class="mt-3 space-y-2">
<p class="text-xs text-zinc-600 dark:text-zinc-400 leading-relaxed"> <p class="text-xs text-zinc-600 dark:text-zinc-400 leading-relaxed">
Ein Outbox-Relay ist wie ein Postbote für deine Nostr-Nachrichten. Es speichert und Ein Outbox-Relay ist wie ein Postbote für deine Nostr-Nachrichten. Es
verteilt deine Posts. Um unser Relay nutzen zu können, musst du es in deinem speichert und
verteilt deine Posts. Um unser Relay nutzen zu können, musst du es in
deinem
Nostr-Client hinzufügen. Nostr-Client hinzufügen.
</p> </p>
<p class="text-xs text-zinc-600 dark:text-zinc-400 leading-relaxed"> <p class="text-xs text-zinc-600 dark:text-zinc-400 leading-relaxed">
Gehe in deinem Nostr-Client zu den Einstellungen (meistens "Settings" oder Gehe in deinem Nostr-Client zu den Einstellungen (meistens "Settings"
oder
"Relays") und füge folgende Outbox-Relay-Adresse hinzu: "Relays") und füge folgende Outbox-Relay-Adresse hinzu:
</p> </p>
<div class="flex items-center gap-2 mt-2"> <div class="flex items-center gap-2 mt-2">
@@ -383,8 +407,10 @@ new class extends Component
</code> </code>
</div> </div>
<p class="text-xs text-zinc-600 dark:text-zinc-400 leading-relaxed"> <p class="text-xs text-zinc-600 dark:text-zinc-400 leading-relaxed">
<strong>Wichtige Hinweise:</strong> Du kannst deine Posts auf mehreren Relays gleichzeitig <strong>Wichtige Hinweise:</strong> Du kannst deine Posts auf mehreren
veröffentlichen. So stellst du sicher, dass deine Inhalte auch über unser Relay erreichbar sind. Relays gleichzeitig
veröffentlichen. So stellst du sicher, dass deine Inhalte auch über
unser Relay erreichbar sind.
</p> </p>
</div> </div>
@endif @endif
@@ -419,12 +445,16 @@ new class extends Component
<flux:label>Dein NIP-05 Handle</flux:label> <flux:label>Dein NIP-05 Handle</flux:label>
<flux:input.group> <flux:input.group>
<flux:input <flux:input
wire:model.live.debounce="nip05Handle" wire:model.live.debounce="profileForm.nip05Handle"
placeholder="dein-name" placeholder="dein-name"
wire:dirty.class="border-amber-500 dark:border-amber-400"
/> />
<flux:input.group.suffix>@einundzwanzig.space</flux:input.group.suffix> <flux:input.group.suffix>@einundzwanzig.space</flux:input.group.suffix>
</flux:input.group> </flux:input.group>
<flux:error name="nip05Handle"/> <flux:error name="profileForm.nip05Handle"/>
<div wire:dirty wire:target="profileForm.nip05Handle" class="text-xs text-amber-600 dark:text-amber-400 mt-1">
Nicht gespeichert...
</div>
</flux:field> </flux:field>
<div class="flex gap-3"> <div class="flex gap-3">
@@ -433,7 +463,8 @@ new class extends Component
wire:loading.attr="disabled" wire:loading.attr="disabled"
size="sm" size="sm"
variant="primary"> variant="primary">
Speichern <span wire:loading.remove wire:target="saveNip05Handle">Speichern</span>
<span wire:loading wire:target="saveNip05Handle">Speichert...</span>
</flux:button> </flux:button>
</div> </div>
@@ -465,24 +496,30 @@ new class extends Component
@if($nip05Verified) @if($nip05Verified)
<flux:callout variant="success" icon="check-circle" class="mt-4"> <flux:callout variant="success" icon="check-circle" class="mt-4">
<p class="font-medium text-zinc-800 dark:text-zinc-100"> <p class="font-medium text-zinc-800 dark:text-zinc-100">
Du hast {{ count($nip05VerifiedHandles) }} aktive Handles für deinen Pubkey! Du hast {{ count($nip05VerifiedHandles) }} aktive Handles für deinen
Pubkey!
</p> </p>
@if($nip05HandleMismatch) @if($nip05HandleMismatch)
<p class="text-sm text-zinc-600 dark:text-zinc-400 mt-1"> <p class="text-sm text-zinc-600 dark:text-zinc-400 mt-1">
Die Synchronisation zu <strong class="break-all">{{ $nip05Handle }}@einundzwanzig.space</strong> wird automatisch im Hintergrund durchgeführt. Die Synchronisation zu <strong
class="break-all">{{ $profileForm->nip05Handle }}
@einundzwanzig.space</strong> wird automatisch im Hintergrund
durchgeführt.
</p> </p>
@endif @endif
</flux:callout> </flux:callout>
<!-- List of all active handles --> <!-- List of all active handles -->
<div class="mt-4 p-4 bg-white/50 dark:bg-zinc-800/50 rounded border border-zinc-200 dark:border-zinc-600"> <div
class="mt-4 p-4 bg-white/50 dark:bg-zinc-800/50 rounded border border-zinc-200 dark:border-zinc-600">
<p class="text-sm font-medium text-zinc-700 dark:text-zinc-300 mb-2"> <p class="text-sm font-medium text-zinc-700 dark:text-zinc-300 mb-2">
Deine aktivierten Handles: Deine aktivierten Handles:
</p> </p>
<ul class="space-y-2"> <ul class="space-y-2">
@foreach($nip05VerifiedHandles as $handle) @foreach($nip05VerifiedHandles as $handle)
<li class="flex items-center gap-2 text-sm"> <li class="flex items-center gap-2 text-sm">
<span class="break-all text-zinc-800 dark:text-zinc-200 font-mono"> <span
class="break-all text-zinc-800 dark:text-zinc-200 font-mono">
{{ $handle }}@einundzwanzig.space {{ $handle }}@einundzwanzig.space
</span> </span>
<flux:badge color="green" size="xs">OK</flux:badge> <flux:badge color="green" size="xs">OK</flux:badge>
@@ -490,13 +527,15 @@ new class extends Component
@endforeach @endforeach
</ul> </ul>
</div> </div>
@elseif($nip05Handle) @elseif($profileForm->nip05Handle)
<flux:callout variant="secondary" icon="information-circle" class="mt-4"> <flux:callout variant="secondary" icon="information-circle" class="mt-4">
<p class="font-medium text-zinc-800 dark:text-zinc-100"> <p class="font-medium text-zinc-800 dark:text-zinc-100">
Dein Handle <strong class="break-all">{{ $nip05Handle }}@einundzwanzig.space</strong> ist noch nicht aktiv. Dein Handle <strong class="break-all">{{ $profileForm->nip05Handle }}
@einundzwanzig.space</strong> ist noch nicht aktiv.
</p> </p>
<p class="text-sm text-zinc-600 dark:text-zinc-400 mt-1"> <p class="text-sm text-zinc-600 dark:text-zinc-400 mt-1">
Das Handle ist gespeichert, aber noch nicht in der NIP-05 Konfiguration veröffentlicht. Das Handle ist gespeichert, aber noch nicht in der NIP-05 Konfiguration
veröffentlicht.
Der Vorstand wird dies bald aktivieren. Der Vorstand wird dies bald aktivieren.
</p> </p>
</flux:callout> </flux:callout>
@@ -532,8 +571,10 @@ new class extends Component
@if($currentPleb && $currentPleb->association_status->value > 1 && $currentYearIsPaid) @if($currentPleb && $currentPleb->association_status->value > 1 && $currentYearIsPaid)
<div class="space-y-3"> <div class="space-y-3">
<p class="text-xs text-zinc-600 dark:text-zinc-400 leading-relaxed"> <p class="text-xs text-zinc-600 dark:text-zinc-400 leading-relaxed">
Ein Watchtower überwacht deine Lightning Channel und schützt sie, falls deine Node Ein Watchtower überwacht deine Lightning Channel und schützt sie, falls deine
offline ist. Wenn du die Zahlung von Channel-Closing-Transaktionen verpasst, kümmert sich Node
offline ist. Wenn du die Zahlung von Channel-Closing-Transaktionen verpasst,
kümmert sich
der Watchtower darum und verhindert den Verlust deiner Sats. der Watchtower darum und verhindert den Verlust deiner Sats.
</p> </p>
@@ -556,14 +597,26 @@ new class extends Component
Einrichtung für gängige Lightning Clients: Einrichtung für gängige Lightning Clients:
</p> </p>
<ul class="text-xs text-zinc-600 dark:text-zinc-400 leading-relaxed space-y-1 list-disc list-inside"> <ul class="text-xs text-zinc-600 dark:text-zinc-400 leading-relaxed space-y-1 list-disc list-inside">
<li><strong>LND:</strong> <flux:link href="https://docs.lightning.engineering/lightning-network-tools/lnd/watchtower" target="_blank">https://docs.lightning.engineering/lightning-network-tools/lnd/watchtower</flux:link></li> <li><strong>LND:</strong>
<li><strong>Core Lightning:</strong> Nutze den <code class="bg-zinc-200 dark:bg-zinc-700 px-1 rounded">watchtower-client</code> Plugin mit der URI</li> <flux:link
<li><strong>Eclair:</strong> Füge die URI zu den Watchtower-Einstellungen in deiner eclair.conf hinzu</li> href="https://docs.lightning.engineering/lightning-network-tools/lnd/watchtower"
target="_blank">
https://docs.lightning.engineering/lightning-network-tools/lnd/watchtower
</flux:link>
</li>
<li><strong>Core Lightning:</strong> Nutze den <code
class="bg-zinc-200 dark:bg-zinc-700 px-1 rounded">watchtower-client</code>
Plugin mit der URI
</li>
<li><strong>Eclair:</strong> Füge die URI zu den Watchtower-Einstellungen in
deiner eclair.conf hinzu
</li>
</ul> </ul>
</div> </div>
<p class="text-xs text-zinc-600 dark:text-zinc-400 leading-relaxed"> <p class="text-xs text-zinc-600 dark:text-zinc-400 leading-relaxed">
<strong>Wichtig:</strong> Der Watchtower überwacht deine Channel passiv. Er hat keinen Zugriff auf <strong>Wichtig:</strong> Der Watchtower überwacht deine Channel passiv. Er hat
keinen Zugriff auf
deine privaten Schlüssel oder dein Guthaben. deine privaten Schlüssel oder dein Guthaben.
</p> </p>
</div> </div>
@@ -726,12 +779,14 @@ new class extends Component
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
class="group block"> class="group block">
<div class="relative overflow-hidden rounded-lg border border-zinc-200 dark:border-zinc-600 bg-white dark:bg-zinc-800 shadow-sm hover:shadow-md hover:shadow-amber-500/10 transition-all duration-300"> <div
class="relative overflow-hidden rounded-lg border border-zinc-200 dark:border-zinc-600 bg-white dark:bg-zinc-800 shadow-sm hover:shadow-md hover:shadow-amber-500/10 transition-all duration-300">
<img src="{{ asset('img/1.png') }}" <img src="{{ asset('img/1.png') }}"
alt="Amber App Screenshot 1" alt="Amber App Screenshot 1"
loading="lazy" loading="lazy"
class="w-full h-auto object-cover aspect-9/16 md:aspect-9/18 group-hover:scale-105 transition-transform duration-300"> class="w-full h-auto object-cover aspect-9/16 md:aspect-9/18 group-hover:scale-105 transition-transform duration-300">
<div class="absolute inset-0 bg-linear-to-t from-zinc-900/60 via-transparent to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-300"> <div
class="absolute inset-0 bg-linear-to-t from-zinc-900/60 via-transparent to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-300">
<div class="absolute top-3 left-3 right-3"> <div class="absolute top-3 left-3 right-3">
<h4 class="text-black text-sm md:text-base font-semibold drop-shadow-lg"> <h4 class="text-black text-sm md:text-base font-semibold drop-shadow-lg">
Startseite Startseite
@@ -752,12 +807,14 @@ new class extends Component
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
class="group block"> class="group block">
<div class="relative overflow-hidden rounded-lg border border-zinc-200 dark:border-zinc-600 bg-white dark:bg-zinc-800 shadow-sm hover:shadow-md hover:shadow-amber-500/10 transition-all duration-300"> <div
class="relative overflow-hidden rounded-lg border border-zinc-200 dark:border-zinc-600 bg-white dark:bg-zinc-800 shadow-sm hover:shadow-md hover:shadow-amber-500/10 transition-all duration-300">
<img src="{{ asset('img/2.png') }}" <img src="{{ asset('img/2.png') }}"
alt="Amber App Screenshot 2" alt="Amber App Screenshot 2"
loading="lazy" loading="lazy"
class="w-full h-auto object-cover aspect-9/16 md:aspect-9/18 group-hover:scale-105 transition-transform duration-300"> class="w-full h-auto object-cover aspect-9/16 md:aspect-9/18 group-hover:scale-105 transition-transform duration-300">
<div class="absolute inset-0 bg-linear-to-t from-zinc-900/60 via-transparent to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-300"> <div
class="absolute inset-0 bg-linear-to-t from-zinc-900/60 via-transparent to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-300">
<div class="absolute top-3 left-3 right-3"> <div class="absolute top-3 left-3 right-3">
<h4 class="text-black text-sm md:text-base font-semibold drop-shadow-lg"> <h4 class="text-black text-sm md:text-base font-semibold drop-shadow-lg">
Profileinstellungen Profileinstellungen
@@ -778,12 +835,14 @@ new class extends Component
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
class="group block"> class="group block">
<div class="relative overflow-hidden rounded-lg border border-zinc-200 dark:border-zinc-600 bg-white dark:bg-zinc-800 shadow-sm hover:shadow-md hover:shadow-amber-500/10 transition-all duration-300"> <div
class="relative overflow-hidden rounded-lg border border-zinc-200 dark:border-zinc-600 bg-white dark:bg-zinc-800 shadow-sm hover:shadow-md hover:shadow-amber-500/10 transition-all duration-300">
<img src="{{ asset('img/3.png') }}" <img src="{{ asset('img/3.png') }}"
alt="Amber App Screenshot 3" alt="Amber App Screenshot 3"
loading="lazy" loading="lazy"
class="w-full h-auto object-cover aspect-9/16 md:aspect-9/18 group-hover:scale-105 transition-transform duration-300"> class="w-full h-auto object-cover aspect-9/16 md:aspect-9/18 group-hover:scale-105 transition-transform duration-300">
<div class="absolute inset-0 bg-linear-to-t from-zinc-900/60 via-transparent to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-300"> <div
class="absolute inset-0 bg-linear-to-t from-zinc-900/60 via-transparent to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-300">
<div class="absolute top-3 left-3 right-3"> <div class="absolute top-3 left-3 right-3">
<h4 class="text-black text-sm md:text-base font-semibold drop-shadow-lg"> <h4 class="text-black text-sm md:text-base font-semibold drop-shadow-lg">
Keyverwaltung Keyverwaltung
@@ -804,12 +863,14 @@ new class extends Component
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
class="group block"> class="group block">
<div class="relative overflow-hidden rounded-lg border border-zinc-200 dark:border-zinc-600 bg-white dark:bg-zinc-800 shadow-sm hover:shadow-md hover:shadow-amber-500/10 transition-all duration-300"> <div
class="relative overflow-hidden rounded-lg border border-zinc-200 dark:border-zinc-600 bg-white dark:bg-zinc-800 shadow-sm hover:shadow-md hover:shadow-amber-500/10 transition-all duration-300">
<img src="{{ asset('img/4.png') }}" <img src="{{ asset('img/4.png') }}"
alt="Amber App Screenshot 4" alt="Amber App Screenshot 4"
loading="lazy" loading="lazy"
class="w-full h-auto object-cover aspect-9/16 md:aspect-9/18 group-hover:scale-105 transition-transform duration-300"> class="w-full h-auto object-cover aspect-9/16 md:aspect-9/18 group-hover:scale-105 transition-transform duration-300">
<div class="absolute inset-0 bg-linear-to-t from-zinc-900/60 via-transparent to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-300"> <div
class="absolute inset-0 bg-linear-to-t from-zinc-900/60 via-transparent to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-300">
<div class="absolute top-3 left-3 right-3"> <div class="absolute top-3 left-3 right-3">
<h4 class="text-black text-sm md:text-base font-semibold drop-shadow-lg"> <h4 class="text-black text-sm md:text-base font-semibold drop-shadow-lg">
Connect/nsec-Bunker Connect/nsec-Bunker
@@ -882,7 +943,8 @@ new class extends Component
<flux:error name="form.check"/> <flux:error name="form.check"/>
</flux:field> </flux:field>
<div class="flex flex-col sm:flex-row gap-3"> <div class="flex flex-col sm:flex-row gap-3">
<flux:button wire:click="save({{ \App\Enums\AssociationStatus::PASSIVE() }})" variant="primary"> <flux:button wire:click="save({{ \App\Enums\AssociationStatus::PASSIVE() }})"
variant="primary">
Mit deinem aktuellen Nostr-Profil Mitglied werden Mit deinem aktuellen Nostr-Profil Mitglied werden
</flux:button> </flux:button>
<flux:button href="https://einundzwanzig.space/verein/" target="_blank" <flux:button href="https://einundzwanzig.space/verein/" target="_blank"
@@ -929,15 +991,22 @@ new class extends Component
<flux:field> <flux:field>
<flux:label>E-Mail Adresse</flux:label> <flux:label>E-Mail Adresse</flux:label>
<flux:input type="email" wire:model.live.debounce="email" <flux:input
placeholder="E-Mail Adresse"/> type="email"
<flux:error name="email"/> wire:model.live.debounce="profileForm.email"
placeholder="E-Mail Adresse"
wire:dirty.class="border-amber-500 dark:border-amber-400"/>
<flux:error name="profileForm.email"/>
<div wire:dirty wire:target="profileForm.email" class="text-xs text-amber-600 dark:text-amber-400 mt-1">
Nicht gespeichert...
</div>
</flux:field> </flux:field>
</div> </div>
<div wire:key="showSave"> <div wire:key="showSave">
<flux:button wire:click="saveEmail" wire:loading.attr="disabled"> <flux:button wire:click="saveEmail" wire:loading.attr="disabled">
Speichern <span wire:loading.remove wire:target="saveEmail">Speichern</span>
<span wire:loading wire:target="saveEmail">Speichert...</span>
</flux:button> </flux:button>
</div> </div>
@endif @endif

View File

@@ -31,7 +31,7 @@ it('can save email address', function () {
NostrAuth::login($pleb->pubkey); NostrAuth::login($pleb->pubkey);
Livewire::test('association.profile') Livewire::test('association.profile')
->set('email', 'test@example.com') ->set('profileForm.email', 'test@example.com')
->call('saveEmail') ->call('saveEmail')
->assertHasNoErrors(); ->assertHasNoErrors();
@@ -44,9 +44,9 @@ it('validates email format', function () {
NostrAuth::login($pleb->pubkey); NostrAuth::login($pleb->pubkey);
Livewire::test('association.profile') Livewire::test('association.profile')
->set('email', 'invalid-email') ->set('profileForm.email', 'invalid-email')
->call('saveEmail') ->call('saveEmail')
->assertHasErrors(['email']); ->assertHasErrors(['profileForm.email']);
}); });
it('can save nip05 handle', function () { it('can save nip05 handle', function () {
@@ -55,11 +55,11 @@ it('can save nip05 handle', function () {
NostrAuth::login($pleb->pubkey); NostrAuth::login($pleb->pubkey);
Livewire::test('association.profile') Livewire::test('association.profile')
->set('nip05Handle', 'user@example.com') ->set('profileForm.nip05Handle', 'test-handle')
->call('saveNip05Handle') ->call('saveNip05Handle')
->assertHasNoErrors(); ->assertHasNoErrors();
expect($pleb->fresh()->nip05_handle)->toBe('user@example.com'); expect($pleb->fresh()->nip05_handle)->toBe('test-handle');
}); });
it('validates nip05 handle format', function () { it('validates nip05 handle format', function () {
@@ -68,14 +68,14 @@ it('validates nip05 handle format', function () {
NostrAuth::login($pleb->pubkey); NostrAuth::login($pleb->pubkey);
Livewire::test('association.profile') Livewire::test('association.profile')
->set('nip05Handle', 'not-an-email') ->set('profileForm.nip05Handle', 'invalid handle with spaces')
->call('saveNip05Handle') ->call('saveNip05Handle')
->assertHasErrors(['nip05Handle']); ->assertHasErrors(['profileForm.nip05Handle']);
}); });
it('validates nip05 handle uniqueness', function () { it('validates nip05 handle uniqueness', function () {
$pleb1 = EinundzwanzigPleb::factory()->active()->create([ $pleb1 = EinundzwanzigPleb::factory()->active()->create([
'nip05_handle' => 'taken@example.com', 'nip05_handle' => 'taken-handle',
]); ]);
$pleb2 = EinundzwanzigPleb::factory()->active()->create(); $pleb2 = EinundzwanzigPleb::factory()->active()->create();
@@ -83,20 +83,20 @@ it('validates nip05 handle uniqueness', function () {
NostrAuth::login($pleb2->pubkey); NostrAuth::login($pleb2->pubkey);
Livewire::test('association.profile') Livewire::test('association.profile')
->set('nip05Handle', 'taken@example.com') ->set('profileForm.nip05Handle', 'taken-handle')
->call('saveNip05Handle') ->call('saveNip05Handle')
->assertHasErrors(['nip05Handle']); ->assertHasErrors(['profileForm.nip05Handle']);
}); });
it('can save null nip05 handle', function () { it('can save null nip05 handle', function () {
$pleb = EinundzwanzigPleb::factory()->active()->create([ $pleb = EinundzwanzigPleb::factory()->active()->create([
'nip05_handle' => 'old@example.com', 'nip05_handle' => 'old-handle',
]); ]);
NostrAuth::login($pleb->pubkey); NostrAuth::login($pleb->pubkey);
Livewire::test('association.profile') Livewire::test('association.profile')
->set('nip05Handle', null) ->set('profileForm.nip05Handle', null)
->call('saveNip05Handle') ->call('saveNip05Handle')
->assertHasNoErrors(); ->assertHasNoErrors();

844
yarn.lock

File diff suppressed because it is too large Load Diff