diff --git a/resources/views/livewire/association/profile.blade.php b/resources/views/livewire/association/profile.blade.php index 765cec6..cbe13d5 100644 --- a/resources/views/livewire/association/profile.blade.php +++ b/resources/views/livewire/association/profile.blade.php @@ -29,6 +29,12 @@ new class extends Component { public ?string $nip05Handle = ''; + public bool $nip05Verified = false; + + public ?string $nip05VerifiedHandle = null; + + public bool $nip05HandleMismatch = false; + public array $yearsPaid = []; public array $events = []; @@ -63,8 +69,30 @@ new class extends Component { if ($this->currentPleb) { $this->email = $this->currentPleb->email; if ($this->currentPleb->nip05_handle) { - $this->nip05Handle = strtolower(str_replace('@einundzwanzig.space', '', - $this->currentPleb->nip05_handle)); + $this->nip05Handle = $this->currentPleb->nip05_handle; + + // Verify NIP-05 handle against nostr.json + try { + $response = \Illuminate\Support\Facades\Http::get( + 'https://einundzwanzig.space/.well-known/nostr.json', + ); + $data = $response->json(); + if (isset($data['names'])) { + foreach ($data['names'] as $handle => $pubkey) { + if ($pubkey === $this->currentPubkey) { + $this->nip05Verified = true; + $this->nip05VerifiedHandle = $handle; + // Check if verified handle differs from database handle + if ($handle !== $this->nip05Handle) { + $this->nip05HandleMismatch = true; + } + break; + } + } + } + } catch (\Exception) { + // Silently fail if nostr.json is not accessible + } } $this->no = $this->currentPleb->no_email; $this->showEmail = !$this->no; @@ -114,7 +142,7 @@ new class extends Component { 'nip05Handle' => 'required|string|max:255|regex:/^[a-z0-9_-]+$/|unique:einundzwanzig_plebs,nip05_handle', ]); - $nip05Handle = strtolower($this->nip05Handle).'@einundzwanzig.space'; + $nip05Handle = strtolower($this->nip05Handle); $this->currentPleb->update([ 'nip05_handle' => $nip05Handle, @@ -399,6 +427,39 @@ new class extends Component { vertrauenswürdiger.

+ + + @if($nip05Verified) + @if($nip05HandleMismatch) + +

+ Dein aktiviertes Handle ist {{ $nip05VerifiedHandle }}@einundzwanzig.space +

+

+ Die Synchronisation zu {{ $nip05Handle }}@einundzwanzig.space wird automatisch im Hintergrund durchgeführt. +

+
+ @else + +

+ Dein Handle {{ $nip05VerifiedHandle }}@einundzwanzig.space ist aktiv verifiziert! +

+

+ Dein Handle ist in der NIP-05 Konfiguration eingetragen und bereit für die Verwendung. +

+
+ @endif + @elseif($nip05Handle) + +

+ Dein Handle {{ $nip05Handle }}@einundzwanzig.space ist noch nicht aktiv. +

+

+ Das Handle ist gespeichert, aber noch nicht in der NIP-05 Konfiguration veröffentlicht. + Der Vorstand wird dies bald aktivieren. +

+
+ @endif @else
diff --git a/resources/views/livewire/association/project-support/form/create.blade.php b/resources/views/livewire/association/project-support/form/create.blade.php index 4d350e1..6f6290c 100644 --- a/resources/views/livewire/association/project-support/form/create.blade.php +++ b/resources/views/livewire/association/project-support/form/create.blade.php @@ -5,12 +5,15 @@ use App\Support\NostrAuth; use Livewire\Attributes\Layout; use Livewire\Attributes\Title; use Livewire\Component; +use Livewire\WithFileUploads; new #[Layout('layouts.app')] #[Title('Projektförderung anlegen')] class extends Component { + use WithFileUploads; + public array $form = [ 'name' => '', 'description' => '', @@ -20,6 +23,8 @@ class extends Component 'sats_paid' => 0, ]; + public $file = null; + public bool $isAllowed = false; public bool $isAdmin = false; @@ -40,6 +45,14 @@ class extends Component } } + public function removeFile(): void + { + if ($this->file) { + $this->file->delete(); + $this->file = null; + } + } + public function save(): void { $this->validate([ @@ -47,9 +60,10 @@ class extends Component 'form.description' => 'required|string', 'form.support_in_sats' => 'required|integer|min:0', 'form.website' => 'required|url|max:255', + 'file' => 'nullable|file|mimes:jpeg,png,jpg,gif,webp|max:10240', ]); - ProjectProposal::query()->create([ + $projectProposal = ProjectProposal::query()->create([ 'name' => $this->form['name'], 'description' => $this->form['description'], 'support_in_sats' => (int) $this->form['support_in_sats'], @@ -59,6 +73,10 @@ class extends Component 'einundzwanzig_pleb_id' => \App\Models\EinundzwanzigPleb::query()->where('pubkey', NostrAuth::pubkey())->first()->id, ]); + if ($this->file) { + $projectProposal->addMedia($this->file)->toMediaCollection('main'); + } + $this->redirectRoute('association.projectSupport'); } }; @@ -70,8 +88,11 @@ class extends Component class="flex flex-col md:flex-row items-center mb-6 space-y-4 md:space-y-0 md:space-x-4">

- Projektförderung anlegen + Projektförderungs-Antrag anlegen

+ + Zurück +
@@ -97,6 +118,30 @@ class extends Component + + Bild + + + + + + @if($file) +
+ + + + + +
+ @endif +
diff --git a/resources/views/livewire/association/project-support/form/edit.blade.php b/resources/views/livewire/association/project-support/form/edit.blade.php index 4818a0c..bed98d1 100644 --- a/resources/views/livewire/association/project-support/form/edit.blade.php +++ b/resources/views/livewire/association/project-support/form/edit.blade.php @@ -5,12 +5,15 @@ use App\Support\NostrAuth; use Livewire\Attributes\Layout; use Livewire\Attributes\Title; use Livewire\Component; +use Livewire\WithFileUploads; new #[Layout('layouts.app')] #[Title('Projektförderung bearbeiten')] class extends Component { + use WithFileUploads; + public ProjectProposal $project; public array $form = [ @@ -22,6 +25,8 @@ class extends Component 'sats_paid' => 0, ]; + public $file = null; + public bool $isAllowed = false; public bool $isAdmin = false; @@ -58,6 +63,21 @@ class extends Component } } + public function deleteMainImage(): void + { + if ($this->project->getFirstMedia('main')) { + $this->project->getFirstMedia('main')->delete(); + } + } + + public function removeFile(): void + { + if ($this->file) { + $this->file->delete(); + $this->file = null; + } + } + public function update(): void { $this->validate([ @@ -65,6 +85,7 @@ class extends Component 'form.description' => 'required|string', 'form.support_in_sats' => 'required|integer|min:0', 'form.website' => 'required|url|max:255', + 'file' => 'nullable|file|mimes:jpeg,png,jpg,gif,webp|max:10240', ]); $this->project->update([ @@ -76,6 +97,10 @@ class extends Component 'sats_paid' => $this->isAdmin ? $this->form['sats_paid'] : $this->project->sats_paid, ]); + if ($this->file) { + $this->project->addMedia($this->file)->toMediaCollection('main'); + } + $this->redirectRoute('association.projectSupport.item', $this->project); } }; @@ -88,8 +113,11 @@ class extends Component class="flex flex-col md:flex-row items-center mb-6 space-y-4 md:space-y-0 md:space-x-4">

- Projektförderung bearbeiten + Projektförderungs-Antrag bearbeiten

+ + Zurück +
@@ -121,6 +149,46 @@ class extends Component +
+ + Bild + + + + + + @if($file) +
+ + + + + +
+ @endif +
+ + @if($project->getFirstMedia('main')) +
+ + + + + +
+ @endif +
diff --git a/resources/views/livewire/association/project-support/index.blade.php b/resources/views/livewire/association/project-support/index.blade.php index 3bbea4d..bf1c674 100644 --- a/resources/views/livewire/association/project-support/index.blade.php +++ b/resources/views/livewire/association/project-support/index.blade.php @@ -1,5 +1,6 @@ loadProjects(); - if (NostrAuth::check()) { - $this->currentPubkey = NostrAuth::pubkey(); - $this->currentPleb = EinundzwanzigPleb::query()->where('pubkey', $this->currentPubkey)->first(); - $this->isAllowed = true; - } } public function updatedSearch(): void @@ -134,44 +130,46 @@ new class extends Component {
  • - + Unterstützt
  • - + Abgelehnt
  • -
    {{ $projects->count() }} Projekte
    +
    {{ $projects->count() }} Projekte
    - -
    - @foreach($this->projects as $project) - - @endforeach -
    + +
    + @foreach($this->projects as $project) + + @endforeach +
    - - -
    -
    - Projektunterstützung löschen? - -

    Du bist dabei, diese Projektunterstützung zu löschen.

    -

    Diese Aktion kann nicht rückgängig gemacht werden.

    -
    -
    -
    - - - Abbrechen - - Löschen -
    -
    -
    - + + +
    +
    + Projektunterstützung löschen? + +

    Du bist dabei, diese Projektunterstützung zu löschen.

    +

    Diese Aktion kann nicht rückgängig gemacht werden.

    +
    +
    +
    + + + Abbrechen + + Löschen +
    +
    +
    + diff --git a/resources/views/livewire/association/project-support/show.blade.php b/resources/views/livewire/association/project-support/show.blade.php index 2064539..f8816a1 100644 --- a/resources/views/livewire/association/project-support/show.blade.php +++ b/resources/views/livewire/association/project-support/show.blade.php @@ -1,6 +1,7 @@ projectProposal = \App\Models\ProjectProposal::query()->where('slug', $projectProposal)->firstOrFail(); + $this->projectProposal = ProjectProposal::query()->where('slug', $projectProposal)->firstOrFail(); if (NostrAuth::check()) { $this->currentPubkey = NostrAuth::pubkey(); $this->isAllowed = true; + $this->mountWithNostrAuth(); + $this->ownVoteExists = Vote::query() + ->where('project_proposal_id', $this->projectProposal->id) + ->where('einundzwanzig_pleb_id', $this->currentPleb->id) + ->exists(); } } @@ -54,6 +60,10 @@ new class extends Component { ], [ 'value' => true, ]); + $this->ownVoteExists = Vote::query() + ->where('project_proposal_id', $this->projectProposal->id) + ->where('einundzwanzig_pleb_id', $this->currentPleb->id) + ->exists(); } public function handleNotApprove(): void @@ -64,6 +74,10 @@ new class extends Component { ], [ 'value' => false, ]); + $this->ownVoteExists = Vote::query() + ->where('project_proposal_id', $this->projectProposal->id) + ->where('einundzwanzig_pleb_id', $this->currentPleb->id) + ->exists(); } } ?> diff --git a/tests/Feature/Livewire/Association/ProjectSupportTest.php b/tests/Feature/Livewire/Association/ProjectSupportTest.php index 9593dd6..99043bb 100644 --- a/tests/Feature/Livewire/Association/ProjectSupportTest.php +++ b/tests/Feature/Livewire/Association/ProjectSupportTest.php @@ -226,3 +226,81 @@ it('displays project details', function () { ->assertSee('Test Project Name') ->assertSee('Test Project Description'); }); + +it('initializes currentPleb when authenticated', function () { + $pleb = EinundzwanzigPleb::factory()->create(); + $project = ProjectProposal::factory()->create(); + + NostrAuth::login($pleb->pubkey); + + Livewire::test('association.project-support.show', ['project' => $project]) + ->assertSet('currentPleb.id', $pleb->id); +}); + +it('initializes ownVoteExists to false when no vote exists', function () { + $pleb = EinundzwanzigPleb::factory()->create(); + $project = ProjectProposal::factory()->create(); + + NostrAuth::login($pleb->pubkey); + + Livewire::test('association.project-support.show', ['project' => $project]) + ->assertSet('ownVoteExists', false) + ->assertSee('Zustimmen') + ->assertSee('Ablehnen'); +}); + +it('initializes ownVoteExists to true when vote exists', function () { + $pleb = EinundzwanzigPleb::factory()->create(); + $project = ProjectProposal::factory()->create(); + \App\Models\Vote::create([ + 'project_proposal_id' => $project->id, + 'einundzwanzig_pleb_id' => $pleb->id, + 'value' => true, + ]); + + NostrAuth::login($pleb->pubkey); + + Livewire::test('association.project-support.show', ['project' => $project]) + ->assertSet('ownVoteExists', true) + ->assertDontSee('Zustimmen') + ->assertDontSee('Ablehnen') + ->assertSee('Du hast bereits abgestimmt.'); +}); + +it('can handle approve vote', function () { + $pleb = EinundzwanzigPleb::factory()->create(); + $project = ProjectProposal::factory()->create(); + + NostrAuth::login($pleb->pubkey); + + Livewire::test('association.project-support.show', ['project' => $project]) + ->call('handleApprove') + ->assertHasNoErrors(); + + $vote = \App\Models\Vote::query() + ->where('project_proposal_id', $project->id) + ->where('einundzwanzig_pleb_id', $pleb->id) + ->first(); + + expect($vote)->not->toBeNull() + ->and($vote->value)->toBeTrue(); +}); + +it('can handle not approve vote', function () { + $pleb = EinundzwanzigPleb::factory()->create(); + $project = ProjectProposal::factory()->create(); + + NostrAuth::login($pleb->pubkey); + + Livewire::test('association.project-support.show', ['project' => $project]) + ->call('handleNotApprove') + ->assertHasNoErrors(); + + $vote = \App\Models\Vote::query() + ->where('project_proposal_id', $project->id) + ->where('einundzwanzig_pleb_id', $pleb->id) + ->first(); + + expect($vote)->not->toBeNull() + ->and($vote->value)->toBeFalse(); +});