From 7b68f863219780126fb09b3d6eda089f53263e5b Mon Sep 17 00:00:00 2001 From: brufdev Date: Tue, 25 Feb 2025 20:26:21 +0000 Subject: [PATCH] Move the "Insert template" action to the Markdown editor toolbar --- app/Livewire/Modals/AddNode.php | 5 +- .../Modals/MarkdownEditorTemplate.php | 91 ++++++++++++++ app/Livewire/Vault/Show.php | 69 +---------- .../markdownEditor/toolbar.blade.php | 9 +- .../modals/markdownEditorTemplate.blade.php | 24 ++++ resources/views/livewire/vault/show.blade.php | 50 +------- .../Modals/MarkdownEditorTemplateTest.php | 114 ++++++++++++++++++ tests/Feature/Vault/ShowTest.php | 95 --------------- 8 files changed, 242 insertions(+), 215 deletions(-) create mode 100644 app/Livewire/Modals/MarkdownEditorTemplate.php create mode 100644 resources/views/livewire/modals/markdownEditorTemplate.blade.php create mode 100644 tests/Feature/Modals/MarkdownEditorTemplateTest.php diff --git a/app/Livewire/Modals/AddNode.php b/app/Livewire/Modals/AddNode.php index b1b9dbc..a2a7403 100644 --- a/app/Livewire/Modals/AddNode.php +++ b/app/Livewire/Modals/AddNode.php @@ -39,12 +39,9 @@ final class AddNode extends Component public function add(): void { - $node = $this->form->create(); + $this->form->create(); $this->closeModal(); $this->dispatch('node-updated'); - if ($node->parent_id === $this->form->vault->templates_node_id) { - $this->dispatch('templates-refresh'); - } $message = $this->form->is_file ? __('File created') : __('Folder created'); $this->dispatch('toast', message: $message, type: 'success'); } diff --git a/app/Livewire/Modals/MarkdownEditorTemplate.php b/app/Livewire/Modals/MarkdownEditorTemplate.php new file mode 100644 index 0000000..2a1e0ce --- /dev/null +++ b/app/Livewire/Modals/MarkdownEditorTemplate.php @@ -0,0 +1,91 @@ + */ + public ?Collection $templates = null; + + public function mount(Vault $vault): void + { + $this->authorize('view', $vault); + } + + #[On('open-modal')] + public function open(VaultNode $selectedFile): void + { + $this->openModal(); + $this->getTemplates(); + $this->selectedFile = $selectedFile; + } + + public function insertTemplate(VaultNode $node): void + { + $this->authorize('update', $this->vault); + $sameVault = $node->vault && $node->vault->is($this->vault); + $isNote = $node->is_file && in_array($node->extension, Note::extensions()); + $isTemplate = $node->parent_id && $node->parent_id === $this->vault->templates_node_id; + $fileSelected = $this->selectedFile && $this->selectedFile->exists(); + + if (!$sameVault || !$isNote || !$isTemplate || !$fileSelected) { + $this->dispatch('toast', message: __('Something went wrong'), type: 'error'); + + return; + } + + /** @var VaultNode $selectedFile */ + $selectedFile = $this->selectedFile; + $now = now(); + $content = str_replace( + ['{{date}}', '{{time}}'], + [$now->format('Y-m-d'), $now->format('H:i')], + (string) $node->content, + ); + $content = str_contains($content, '{{content}}') + ? str_replace('{{content}}', (string) $selectedFile->content, $content) + : $content . PHP_EOL . $selectedFile->content; + $selectedFile->update(['content' => $content]); + $this->dispatch('file-refresh', node: $selectedFile); + $this->dispatch('toast', message: __('Template inserted'), type: 'success'); + } + + public function render(): Factory|View + { + return view('livewire.modals.markdownEditorTemplate'); + } + + private function getTemplates(): void + { + if (!$this->vault->templatesNode) { + $this->templates = null; + + return; + } + + $this->templates = $this->vault + ->templatesNode + ->childs() + ->where('is_file', true) + ->where('extension', 'LIKE', 'md') + ->orderBy('name') + ->get(); + } +} diff --git a/app/Livewire/Vault/Show.php b/app/Livewire/Vault/Show.php index 8d80add..99261be 100644 --- a/app/Livewire/Vault/Show.php +++ b/app/Livewire/Vault/Show.php @@ -12,10 +12,8 @@ use App\Actions\UpdateVault; use App\Livewire\Forms\VaultNodeForm; use App\Models\Vault; use App\Models\VaultNode; -use App\Services\VaultFiles\Note; use Illuminate\Contracts\View\Factory; use Illuminate\Contracts\View\View; -use Illuminate\Database\Eloquent\Collection; use Livewire\Attributes\Locked; use Livewire\Attributes\On; use Livewire\Attributes\Url; @@ -28,9 +26,6 @@ final class Show extends Component public VaultNodeForm $nodeForm; - /** @var Collection */ - public Collection $templates; - #[Locked] #[Url(as: 'file')] public ?int $selectedFile = null; @@ -49,9 +44,7 @@ final class Show extends Component new UpdateVault()->handle($vault, [ 'opened_at' => now(), ]); - $this->vault = $vault; $this->nodeForm->setVault($this->vault); - $this->getTemplates(); if ((int) $this->selectedFile > 0) { $selectedFile = $this->vault->nodes() @@ -134,10 +127,6 @@ final class Show extends Component if ($node->wasChanged(['parent_id', 'name'])) { $this->dispatch('node-updated'); - - if ($node->parent_id === $this->vault->templates_node_id) { - $this->getTemplates(); - } } } @@ -154,56 +143,9 @@ final class Show extends Component new UpdateVault()->handle($this->vault, [ 'templates_node_id' => $node->id, ]); - $this->getTemplates(); $this->dispatch('toast', message: __('Template folder updated'), type: 'success'); } - public function insertTemplate(VaultNode $node): void - { - $this->authorize('update', $this->vault); - $sameVault = $node->vault && $this->vault->id === $node->vault->id; - $isNote = $node->is_file && in_array($node->extension, Note::extensions()); - $isTemplate = $node->parent_id === $this->vault->templates_node_id; - $fileSelected = (int) $this->selectedFile > 0; - - if (!$sameVault || !$isNote || !$isTemplate || !$fileSelected || !$this->isEditMode) { - $this->dispatch('toast', message: __('Something went wrong'), type: 'error'); - - return; - } - - $now = now(); - /** @var VaultNode $selectedNode */ - $selectedNode = $this->nodeForm->node; - $content = str_replace( - ['{{date}}', '{{time}}'], - [$now->format('Y-m-d'), $now->format('H:i')], - (string) $node->content, - ); - $content = str_contains($content, '{{content}}') - ? str_replace('{{content}}', (string) $selectedNode->content, $content) - : $content . PHP_EOL . $selectedNode->content; - $selectedNode->update(['content' => $content]); - $this->nodeForm->setNode($selectedNode); - $this->dispatch('toast', message: __('Template inserted'), type: 'success'); - } - - #[On('templates-refresh')] - public function getTemplates(): void - { - if (!$this->vault->templatesNode) { - return; - } - - $this->templates = $this->vault - ->templatesNode - ->childs() - ->where('is_file', true) - ->where('extension', 'LIKE', 'md') - ->orderBy('name') - ->get(); - } - public function deleteNode(VaultNode $node): void { $this->authorize('delete', $node->vault); @@ -218,20 +160,11 @@ final class Show extends Component fn (VaultNode $node): bool => $node->id === $this->selectedFile, ) ); + if ($openFileDeleted) { $this->closeFile(); } - $templateDeleted = !is_null( - array_find( - $deletedNodes, - fn (VaultNode $node): bool => $node->parent_id === $this->vault->templates_node_id, - ) - ); - if ($templateDeleted) { - $this->getTemplates(); - } - $message = $node->is_file ? __('File deleted') : __('Folder deleted'); $this->dispatch('toast', message: $message, type: 'success'); } catch (Throwable $e) { diff --git a/resources/views/components/markdownEditor/toolbar.blade.php b/resources/views/components/markdownEditor/toolbar.blade.php index 6ec13fe..24c7aa2 100644 --- a/resources/views/components/markdownEditor/toolbar.blade.php +++ b/resources/views/components/markdownEditor/toolbar.blade.php @@ -43,12 +43,17 @@ Link + @click="$wire.dispatchTo('modals.markdown-editor-search', 'open-modal')" + >Link External link Image + @click="$wire.dispatchTo('modals.markdown-editor-search', 'open-modal', { type: 'image' })" + >Image External image Table + Template diff --git a/resources/views/livewire/modals/markdownEditorTemplate.blade.php b/resources/views/livewire/modals/markdownEditorTemplate.blade.php new file mode 100644 index 0000000..978ce28 --- /dev/null +++ b/resources/views/livewire/modals/markdownEditorTemplate.blade.php @@ -0,0 +1,24 @@ + + + @if ($templates && count($templates)) +
    + @foreach ($templates as $template) +
  • + +
  • + @endforeach +
+ @else +

{{ __('No templates found') }}

+ @endif +
+
diff --git a/resources/views/livewire/vault/show.blade.php b/resources/views/livewire/vault/show.blade.php index 856d059..1bd484f 100644 --- a/resources/views/livewire/vault/show.blade.php +++ b/resources/views/livewire/vault/show.blade.php @@ -63,52 +63,9 @@
- - - - - - - - - - - {{ __('Insert template') }} - - - - - @if ($templates && count($templates)) -
    - @foreach ($templates as $template) -
  • - -
  • - @endforeach -
- @else -

{{ __('No templates found') }}

- @endif -
-
- - - - - {{ __('Close file') }} - - -
-
+
@@ -211,6 +168,7 @@ + @script diff --git a/tests/Feature/Modals/MarkdownEditorTemplateTest.php b/tests/Feature/Modals/MarkdownEditorTemplateTest.php new file mode 100644 index 0000000..27626e2 --- /dev/null +++ b/tests/Feature/Modals/MarkdownEditorTemplateTest.php @@ -0,0 +1,114 @@ +create()->first(); + $vault = new CreateVault()->handle($user, [ + 'name' => fake()->words(3, true), + ]); + + Livewire::actingAs($user) + ->test(MarkdownEditorTemplate::class, ['vault' => $vault]) + ->assertSet('show', false) + ->call('open') + ->assertSet('show', true); +}); + +it('inserts a template', function (): void { + $user = User::factory()->create()->first(); + $vault = new CreateVault()->handle($user, [ + 'name' => fake()->words(3, true), + ]); + $templateFolderNode = new CreateVaultNode()->handle($vault, [ + 'is_file' => false, + 'name' => fake()->words(3, true), + ]); + $templateNode = new CreateVaultNode()->handle($vault, [ + 'is_file' => true, + 'parent_id' => $templateFolderNode->id, + 'name' => fake()->words(3, true), + 'extension' => 'md', + 'content' => 'content: {{content}}', + ]); + $content = fake()->paragraph(); + $node = new CreateVaultNode()->handle($vault, [ + 'is_file' => true, + 'name' => fake()->words(3, true), + 'extension' => 'md', + 'content' => $content, + ]); + $vault->update(['templates_node_id' => $templateFolderNode->id]); + + Livewire::actingAs($user) + ->test(MarkdownEditorTemplate::class, ['vault' => $vault]) + ->call('open', $node) + ->call('insertTemplate', $templateNode); + + expect($node->refresh()->content)->toBe('content: ' . $content); +}); + +it('does not insert a template from a non-template node', function (): void { + $user = User::factory()->create()->first(); + $vault = new CreateVault()->handle($user, [ + 'name' => fake()->words(3, true), + ]); + $firstNode = new CreateVaultNode()->handle($vault, [ + 'is_file' => true, + 'name' => fake()->words(3, true), + 'extension' => 'md', + 'content' => 'content: {{content}}', + ]); + $secondNodeContent = fake()->paragraph(); + $secondNode = new CreateVaultNode()->handle($vault, [ + 'is_file' => true, + 'name' => fake()->words(3, true), + 'extension' => 'md', + 'content' => $secondNodeContent, + ]); + + Livewire::actingAs($user) + ->test(MarkdownEditorTemplate::class, ['vault' => $vault]) + ->call('open', $secondNode) + ->call('insertTemplate', $firstNode); + + expect($secondNode->refresh()->content)->toBe($secondNodeContent); +}); + +it('inserts a template without {{content}} variable', function (): void { + $user = User::factory()->create()->first(); + $vault = new CreateVault()->handle($user, [ + 'name' => fake()->words(3, true), + ]); + $templateFolderNode = new CreateVaultNode()->handle($vault, [ + 'is_file' => false, + 'name' => fake()->words(3, true), + ]); + $templateNode = new CreateVaultNode()->handle($vault, [ + 'is_file' => true, + 'parent_id' => $templateFolderNode->id, + 'name' => fake()->words(3, true), + 'extension' => 'md', + 'content' => 'Daily note', + ]); + $nodeContent = fake()->paragraph(); + $node = new CreateVaultNode()->handle($vault, [ + 'is_file' => true, + 'name' => fake()->words(3, true), + 'extension' => 'md', + 'content' => $nodeContent, + ]); + $vault->update(['templates_node_id' => $templateFolderNode->id]); + + Livewire::actingAs($user) + ->test(MarkdownEditorTemplate::class, ['vault' => $vault]) + ->call('open', $node) + ->call('insertTemplate', $templateNode); + + expect($node->refresh()->content)->toBe('Daily note' . PHP_EOL . $nodeContent); +}); diff --git a/tests/Feature/Vault/ShowTest.php b/tests/Feature/Vault/ShowTest.php index 1054b80..6f55382 100644 --- a/tests/Feature/Vault/ShowTest.php +++ b/tests/Feature/Vault/ShowTest.php @@ -234,101 +234,6 @@ it('does not set the template folder if it is a file', function (): void { ->assertSet('vault.templates_node_id', null); }); -it('inserts a template', function (): void { - $user = User::factory()->create()->first(); - $vault = new CreateVault()->handle($user, [ - 'name' => fake()->words(3, true), - ]); - $templateFolderNode = new CreateVaultNode()->handle($vault, [ - 'is_file' => false, - 'name' => fake()->words(3, true), - ]); - $templateNode = new CreateVaultNode()->handle($vault, [ - 'is_file' => true, - 'parent_id' => $templateFolderNode->id, - 'name' => fake()->words(3, true), - 'extension' => 'md', - 'content' => 'content: {{content}}', - ]); - $node = new CreateVaultNode()->handle($vault, [ - 'is_file' => true, - 'name' => fake()->words(3, true), - 'extension' => 'md', - 'content' => fake()->paragraph(), - ]); - - Livewire::actingAs($user) - ->withQueryParams(['file' => $node->id]) - ->test(Show::class, ['vault' => $vault]) - ->assertSet('nodeForm.content', $node->content) - ->call('setTemplateFolder', $templateFolderNode) - ->call('insertTemplate', $templateNode) - ->assertSet('nodeForm.content', 'content: ' . $node->content); -}); - -it('does not insert a template from a non-template node', function (): void { - $user = User::factory()->create()->first(); - $vault = new CreateVault()->handle($user, [ - 'name' => fake()->words(3, true), - ]); - $folderNode = new CreateVaultNode()->handle($vault, [ - 'is_file' => false, - 'name' => fake()->words(3, true), - ]); - $firstNode = new CreateVaultNode()->handle($vault, [ - 'is_file' => true, - 'parent_id' => $folderNode->id, - 'name' => fake()->words(3, true), - 'extension' => 'md', - 'content' => 'content: {{content}}', - ]); - $secondNode = new CreateVaultNode()->handle($vault, [ - 'is_file' => true, - 'name' => fake()->words(3, true), - 'extension' => 'md', - 'content' => fake()->paragraph(), - ]); - - Livewire::actingAs($user) - ->withQueryParams(['file' => $secondNode->id]) - ->test(Show::class, ['vault' => $vault]) - ->assertSet('nodeForm.content', $secondNode->content) - ->call('insertTemplate', $firstNode) - ->assertSet('nodeForm.content', $secondNode->content); -}); - -it('inserts a template without {{content}} variable', function (): void { - $user = User::factory()->create()->first(); - $vault = new CreateVault()->handle($user, [ - 'name' => fake()->words(3, true), - ]); - $templateFolderNode = new CreateVaultNode()->handle($vault, [ - 'is_file' => false, - 'name' => fake()->words(3, true), - ]); - $templateNode = new CreateVaultNode()->handle($vault, [ - 'is_file' => true, - 'parent_id' => $templateFolderNode->id, - 'name' => fake()->words(3, true), - 'extension' => 'md', - 'content' => 'Daily note', - ]); - $node = new CreateVaultNode()->handle($vault, [ - 'is_file' => true, - 'name' => fake()->words(3, true), - 'extension' => 'md', - 'content' => fake()->paragraph(), - ]); - - Livewire::actingAs($user) - ->withQueryParams(['file' => $node->id]) - ->test(Show::class, ['vault' => $vault]) - ->assertSet('nodeForm.content', $node->content) - ->call('setTemplateFolder', $templateFolderNode) - ->call('insertTemplate', $templateNode) - ->assertSet('nodeForm.content', 'Daily note' . PHP_EOL . $node->content); -}); - it('updates the node', function (): void { $user = User::factory()->create()->first(); $vault = new CreateVault()->handle($user, [